c++版opencv基础学习Day3_图像梯度及边缘检测

图像梯度与边缘检测

是什么以及目的与意义

梯度是什么?梯度本身是个向量,表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模),这点我们在高数都学过。

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。

梯度计算需要求导数。但是图像梯度的计算,是通过计算像素值的差得到梯度的近似值。图像梯度表示的是图像变化的速度,反映了图像的边缘信息。

边缘是像素值快速变化的地方。所以对于图像的边缘部分,其灰度值变化较大,梯度值也较大;对于图像中较平滑的部分,其灰度值变化较小,梯度值也较小。

为了检测边缘,我们需要检测图像中的不连续性,可以使用图像梯度来检测不连续性。但是,图像梯度也会受到噪声的影响,因此建议先对图像进行平滑处理

Sobel算子

关于原理:

我感觉我解释也很难,建议直接看下面的链接,非常通俗易懂,超级推荐!!!一定要看

1.Sobel算子原理解析_哔哩哔哩_bilibili

直接放函数,虽然可能这些情况并不会完全用上,但方便查阅,全放上

#include <opencv2/imgproc.hpp>
函数说明:void cv::Sobel( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize = 3, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
输入参数:
				src					输入图像。
				dst					与src具有相同大小和相同通道数的输出图像。
				ddepth				输出图像深度(使用src.depth()时为-1)。
				dx					x方向的求导阶数。一般为0,1,2。其中,0表示该方向没有求导。
				dy					y方向的求导阶数。一般为0,1,2。其中,0表示该方向没有求导。
				ksize = 3			卷积核大小。一般为1、3、5或7。
				scale = 1			计算导数值的可选比例因子;默认1,即不应用缩放。
				delta = 0			偏移量,卷积结果要加上这个数字。
				borderType = BORDER_DEFAULT		边界类型(即边界填充方式)。默认BORDER_DEFAULT。不支持BORDER_WRAP。
							cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
							cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
							cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
							cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
							cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
							cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
							cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
							cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
							cv::BORDER_ISOLATED = 8 			do not look outside of ROI

 Scharr算子

和Sobel算子类似,懂了Sobel算子,这个算子就很容易理解了

区别在于:Sobel算子比较擅长检测轮廓比较明显的区域,而Scharr算子可以检测出轮廓并不是很明显的区域

照例链接:4.Scharr算子原理_哔哩哔哩_bilibili

(应该是同一个人讲的,很清楚,建议1.5-2倍速)

#include <opencv2/imgproc.hpp>
函数说明:void cv::Scharr( InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
输入参数:
				src					输入图像。
				dst					与src具有相同大小和相同通道数的输出图像。
				ddepth				输出图像深度(使用src.depth()时为-1)。
				dx					x方向的求导阶数。一般为0,1,2。其中,0表示该方向没有求导。
				dy					y方向的求导阶数。一般为0,1,2。其中,0表示该方向没有求导。
				ksize = 3			卷积核大小。它必须是1、3、5或7。
				scale = 1			计算导数值的可选比例因子;默认1,即不应用缩放。
				delta = 0			偏移量,卷积结果要加上这个数字。
				borderType = BORDER_DEFAULT		边界类型(即边界填充方式)。默认BORDER_DEFAULT。不支持BORDER_WRAP。
							cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
							cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
							cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
							cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
							cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
							cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
							cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
							cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
							cv::BORDER_ISOLATED = 8 			do not look outside of ROI

Laplacian算子

和前两个算子一样,但是他更精致,检测性能较好,常用于图像增强领域和边缘提取。

6.拉普拉斯算子_哔哩哔哩_bilibili

(看原理的话看完2分钟就ok了)

#include <opencv2/imgproc.hpp>
函数说明:void cv::Laplacian( InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
输入参数:
				src					输入图像。
				dst					与src具有相同大小和相同通道数的输出图像。
				ddepth				输出图像深度(使用src.depth()时为-1)。
				ksize = 1			卷积核大小。它必须是1、3、5或7。
				scale = 1			计算导数值的可选比例因子;默认1,即不应用缩放。
				delta = 0			偏移量,卷积结果要加上这个数字。
				borderType = BORDER_DEFAULT		边界类型(即边界填充方式)。默认BORDER_DEFAULT。不支持BORDER_WRAP。
							cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
							cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
							cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
							cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
							cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
							cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
							cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
							cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
							cv::BORDER_ISOLATED = 8 			do not look outside of ROI

Canny边缘检测算子

canny边缘检测算法步骤:

    1、使用高斯滤波器对图像进行平滑处理。
    2、利用一阶偏导算子找到灰度图像沿着水平方向Gx和垂直方向Gy的偏导数,并计算梯度的幅值和方向。
    3、对梯度幅值进行NMS非极大值抑制,获取局部梯度的最大值。
    在3X3窗口中,将给定像素P与沿着梯度线方向的两个像素进行比较,若P的梯度幅值小于该两个像素的梯度幅值,则令P=0;否则保留原幅值。
    备注:将梯度方向分为4种来比较梯度幅值的强度:水平方向、垂直方向、正方向、-45°方向。
    4、用双边阈值检测和边缘连接。
    分三种情况:
    (1)若像素值大于高阈值,则该像素一定是边缘像素(强边缘点),置为255;
    (2)若小于低阈值,则一定不是,置为0;
    (3)若像素值大于低阈值但小于高阈值,则观察该像素的(3X3)8邻域像素中是否有大于高阈值的像素点,若有则该像素是边缘像素,并将该点置为255,用以连接强边缘点;否则不是,则该点置为0。

老样子,不如直接看视频:

8.Canny边缘检测原理_哔哩哔哩_bilibili

#include <opencv2/imgproc.hpp>
函数说明:void cv::Canny	( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false );
输入参数:
				image						8位输入图像。
				edges						输出图像。其具有与输入图像相同大小和类型。
				threshold1					第一个阈值(低阈值)。
				threshold2					第二个阈值(高阈值)。
				apertureSize = 3			Sobel算子孔径尺寸 。默认为3。可以是1、3、5、7
				L2gradient = false			选择L1 or L2范数计算图像梯度大小。
									(L2graduation=false)默认L1范数=|dI/dx|+|dI/dy|。
									(L2gradient=true)L2范数= (dI/dx)^2 + (dI/dy)^2。
									
/*注释:	高阈值比较严格,求的边缘很少,一般认为高阈值的边缘都是有效。
		低阈值比较宽松,求的边缘很多(一般包括高阈值求到的边缘),其中不少是无效的边缘。
	
Canny求得的边缘希望是连在一起的(通常是封闭的)
(1)先用高阈值将要提取轮廓的物体与背景区分开来,但可能边缘轮廓不连续或者不够平滑。
(2)然后低阈值平滑边缘的轮廓,将不连续的部分连接起来。*/

栗子

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{

	string img_path1 = "D:/系统默认/桌面/RM/sha.jpeg";
	//string img_path2 = "D:/系统默认/桌面/RM/tou.jpeg";

	Mat src = imread(img_path1, 1);
	//Mat src2 = imread(img_path2, 1);
	
	if (src.empty() ) {
		cout << "can't read image!!" << endl;
		return -1;
	}

	//预处理
	Mat src_Gray, src_Gaus;
	GaussianBlur(src, src_Gaus, Size(3, 3), 0, 0);
	cvtColor(src, src_Gray, COLOR_BGR2GRAY);
	

	//Sobel算子
	Mat Sobel_X, Sobel_Y, Sobel_X_abs, Sobel_Y_abs, Sobel_XY, Sobel_XY1;
	Sobel(src_Gray, Sobel_X, src_Gray.depth(), 1, 0);					//计算 x 轴方向
	Sobel(src_Gray, Sobel_Y, src_Gray.depth(), 0, 1);	               //计算 y 轴方向
	convertScaleAbs(Sobel_X, Sobel_X_abs);								//取绝对值
	convertScaleAbs(Sobel_Y, Sobel_Y_abs);								//取绝对值
    addWeighted(Sobel_X_abs, 0.5, Sobel_Y_abs, 0.5, 0, Sobel_XY);		//图像融合
	
	
	//Scharr算子
	Mat Scharr_X, Scharr_Y, Scharr_X_abs, Scharr_Y_abs, Scharr_XY, Scharr_XY1;
	Scharr(src_Gray, Scharr_X, src_Gray.depth(), 1, 0);					//计算 x 轴方向
	Scharr(src_Gray, Scharr_Y, src_Gray.depth(), 0, 1);					//计算 y 轴方向
	convertScaleAbs(Scharr_X, Scharr_X_abs);							//取绝对值
	convertScaleAbs(Scharr_Y, Scharr_Y_abs);							//取绝对值
	addWeighted(Scharr_X_abs, 0.5, Scharr_Y_abs, 0.5, 0, Scharr_XY);
	
	//拉普拉斯算子
	Mat src_Laplacian;
	Laplacian(src_Gray, src_Laplacian, src_Gray.depth());

	//Canny
	Mat src_Canny;
	Canny(src_Gray, src_Canny, 10, 100);

	imshow("src", src);
	imshow("Sobel_XY", Sobel_XY);
	imshow("Scharr_XY", Scharr_XY);
	imshow("src_Laplacian", src_Laplacian);
	imshow("src_Canny", src_Canny);


	waitKey();
	return 0;

	
}

学习参考

学习的主要参考依旧是:Opencv C++图像处理(全)_c++ opencv 图像处理-CSDN博客

还有一些视频链接在文章里面

另外,今天中秋节,学的比较少,中秋节快乐!!!

//ps:案例图片用的是我莎莎的照片哈哈哈,今天看莎头混双半决赛4:0赢了哦

大家中秋节都要快乐哦!!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值