【OpenCV学习】第16课:图像边缘提取 - Sobel算子详细剖析(图像梯度)

仅自学做笔记用,后续有错误会更改

理论

  1. 卷积的应用 - 图像边缘提取:
  • 边缘是什么:是像素值发生跃迁的地方, 是图像的显著特征之一, 再图像特征提取丶对象检测丶模式识别等方面都有重要作用
  • 如何捕捉/提取边缘:对图像求它的一阶导数,delta = f(x) - f(x-1), delta值越大, 说明像素在x方向变化越大,边缘信号越强
    在这里插入图片描述
  • 如果你已经忘记了数学求导什么的概念, 也不用担心, 直接用Sobel算子进行卷积操作就可以了!
  1. Sobel算子
  • 是离散微分算子(discrete differentiation operator), 用来计算图像灰度的近似梯度
  • Sobel算子功能集合了 高斯模糊和微分求导
  • 又被称为一阶微分算子,求导算子,在水平和垂直两个方向求导,得到图像X方向与Y方向的梯度图像
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/62e69e82e0e649c9b236b0bae34379f6.png
  • 求取导数的近似值(上图的实际应用公式), kernel=3时不是很准确,容易受到干扰, Opencv使用改进版本Scharr函数, 算子如下:
    在这里插入图片描述
  1. Sobel算子边缘提取步骤:
  • 先把原图像进行高斯模糊操作
  • 再把图像转灰度图
  • 再通过Sobel算子求X梯度和Y梯度
  • 线性混合X与Y的梯度图(可以不使用addweighted接口,而是手动去写, 去掉权重α与1-α的影响,可以使最后的图更明显),得到最终的振幅图像

相关API

cv::Sobel(
InputArray src, //输入图像
OutputArray dst, //输出图像
int depth, //输出图像深度,填-1表示跟输入图像一致, 由于灰度图是CV_8U,所以Sobel一般使用CV_16S/CV_32F, 需要比输入的灰度图的深度更高, 结果才会更明显
int dx, //x方向,几阶导数, sobel取1
int dy, //y方向,几阶导数, sobel取1
int ksize, //算子(kernel)大小,Sobel算子必须是奇数, 常见的是3
double scale = 1, //输出图像放大或缩小倍数
double delta = 0, //偏移量
int borderType = BORDER_DEFAULT
)

cv::Scharr(
InputArray src, //输入图像
OutputArray dst, //输出图像
int depth, //输出图像深度,填-1表示跟输入图像一致, 由于灰度图是CV_8U,所以Sobel一般使用CV_16S/CV_32F, 需要比输入的灰度图的深度更高, 结果才会更明显
int dx, //x方向,几阶导数, sobel取1
int dy, //y方向,几阶导数, sobel取1
int ksize, //算子(kernel)大小,Sobel算子必须是奇数, 常见的是3
double scale = 1, //输出图像放大或缩小倍数
double delta = 0, //偏移量
int borderType = BORDER_DEFAULT
)

代码示例

using namespace cv;
int main(int argc, char** argv){
	Mat src,dst;
	int ksize = 0;
	src = imread(...);
	if( !src.data ){
		return -1;
	}
	//原图
	char INPUT_WIN[] = "input image";
	namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_WIN, src);
	//先高斯模糊, 再转灰度图
	Mat gray_src;
	GaussianBlur(src, dst, Size(3,3), 0, 0);
	cvtColor(dst, gray_src, CV_BGR2GRAY);
	imshow("gray image",gray_src);
	
	/*//cv::Scharr操作,  可以看最后的效果截图, 它的提取效果非常强烈, 根本不怕干扰
	Mat xgrad, ygrad;
	Scharr(gray_src, xgrad, CV_16S, 1, 0);
	Scharr(gray_src, ygrad, CV_16S, 0, 1);*/
	//cv::Sobel操作(输出深度为CV_16S,且有convertScaleAbs转换)
	Mat xgrad, ygrad;
	Scharr(gray_src, xgrad, CV_16S, 1, 0);
	Scharr(gray_src, ygrad, CV_16S, 0, 1);
	Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);	//这里为什么用CV_16S, 因为灰度图是CV_8U,我们设置输出图像的深度比灰度图更大, 就可以容纳更大的特征值, 提取效果也就更明显, 你也可以填-1等同于输入图像的深度,但是反正不能小于输入图像的深度。
	Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
	convertScaleAbs(xgrad, xgrad);			    //这个函数的作用是保证Sobel算子操作过后有些负数结果值不被置为0, 也就是不被截取掉
	convertScaleAbs(ygrad, ygrad);
	imshow("xgrad", xgrad);
	imshow("ygrad", ygrad);
	
	/*//cv::Sobel操作(输出深度填-1,且没有convertScaleAbs转换)
	Mat xgrad, ygrad;
	Sobel(gray_src, xgrad, -1, 1, 0, 3);
	Sobel(gray_src, ygrad, -1, 0, 1, 3);
	imshow("xgrad", xgrad);
	imshow("ygrad", ygrad);*/


	//最终线性混合图:手动写线性混合,去掉权重影响, 可以看最后的效果截图,更明显
	Mat xygrad = Mat(xgrad.size(), xgrad.type());
	int width = xgrad.cols;
	int height = ygrad.rows;
	for(int row = 0; row < weight; row++){
		for(int col = 0;col < width;col++){
			int xg = xgrad.at<uchar>(row, col);
			int yg = ygrad.at<uchar>(row, col);										//因为灰度图是CV_8U,所以用uchar
			int xy = xg + yg;																	//直接相加, 没有权重影响, 更亮更明显
			xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);	//saturate_cast这个东西的作用是保证最终值不超过0~255
		}
	}
	imshow("Final Result", xygrad);
	
	/*//最终线性混合图:直接调用addWeighted接口
	Mat xygrad;
	addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
	imshow("Final Result", xygrad);*/

	waitKey(0);
	return 0;
}

效果截图:
使用cv::Sobel(两个方向的输出深度都填CV_16S且有convertScaleAbs转换) x梯度图y梯度图
在这里插入图片描述
使用cv::Sobel(两个方向的输出深度都填-1且没有convertScaleAbs转换) x梯度图y梯度图
在这里插入图片描述
使用cv::Sobel(两个方向的输出深度都填CV_16S且有convertScaleAbs转换, 直接调用addWeighted接口) 最终线性混合图
在这里插入图片描述
使用cv::Sobel(两个方向的输出深度都填CV_16S且有convertScaleAbs转换, 手动写线性混合) 最终线性混合图
在这里插入图片描述
使用cv::Scharr(两个方向的输出深度都填CV_16S且有convertScaleAbs转换, 且手动写线性混合) 最终线性混合图
在这里插入图片描述

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值