【OpenCV:从零到一】14:边缘检测|Scharr滤波器|Sobel算子|Laplacian算子|Canny算子

前言
这是我《OpenCV:从零到一》专栏的第十四篇博客,想看跟多请戳这。
本文概要
Scharr
Sobel
Laplacian
Canny
threshold
convertScaleAbs
Scharr滤波器、Sobel算子、Laplacian算子、Canny算子的区别
算子、滤波器、卷积核三者的区别
案例代码
大概内容:Scharr滤波器、Sobel算子、Laplacian算子、Canny算子 。

#include 
#include 
#include 

using namespace cv;

Mat src, gray_src, dst;
int t1_value = 50;
int max_value = 255;
void Canny_Demo(int, void*);

int main(int argc, char** argv) {
	src = imread("D://86186//Documents//opencv//lena.jpg");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	imshow("src", src);

	//-----------------Sobel和Schar------------------------//
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);
	imshow("gray image", gray_src);

	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);
	// Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
	convertScaleAbs(xgrad, xgrad);
	convertScaleAbs(ygrad, ygrad);
	imshow("Soble xgrad", xgrad);
	imshow("Soble ygrad", ygrad);

	Mat xygrad = Mat(xgrad.size(), xgrad.type());
	printf("type : %d\n", xgrad.type());
	int width = xgrad.cols;
	int height = ygrad.rows;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			int xg = xgrad.at<uchar>(row, col);
			int yg = ygrad.at<uchar>(row, col);
			int xy = xg + yg;
			xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
		}
	}
	//addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
	imshow("Soble xygrad", xygrad);


	//-----------------Laplancce---------------------//
	Mat edge_image;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);

	Laplacian(gray_src, edge_image, CV_16S, 3);
	convertScaleAbs(edge_image, edge_image);

	threshold(edge_image, edge_image, 0, 255, THRESH_OTSU | THRESH_BINARY);
	imshow("edge_image", edge_image);

	//-----------------Canny---------------------//

	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	createTrackbar("Threshold Value:", "Canny", &t1_value, max_value, Canny_Demo);
	Canny_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Canny_Demo(int, void*) {
	Mat edge_output;
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);
	//bitwise_not(edge_output, edge_output);
	imshow("Canny", ~edge_output);//波浪号的作用是整个图像取反,相当于bitwise_not()
}

运行效果:

解析及注意事项

  • 算子是一个函数空间到函数空间上的映射,算子包括我们常见的数学函数、泛函、变换等等,滤波器,详细可以见这个回答。滤波器也分两种:线性滤波器和非线性滤波器,而线性滤波器卷积时使用到的模版矩阵叫做卷积核(kernel),很多时候算子也代指卷积核,但是一定要清楚的是卷积核不能代指算子,算子不仅仅包括卷积核,可能还有其他的处理,例如Sobel算子可能卷积同时还会做一个高斯模糊。而因为Scharr除了卷积(求导)也不做其他的操作,所以我们一般直接称Scharr为滤波器,而不是算子。(是算子,但是用滤波器称呼更精确,能够其他形成区别)
  • 边缘检测有几个比较常用的算子:Sobel、Laplacian、Canny。
  • Sobel算子结合了高斯平滑和微分求导(Scharr)(一阶、一个方向上的),用来计算图像的灰度函数的近似梯度,获得对应的梯度矢量或法矢量。(需要注意的是Sobel和Scharr的思想虽然一样,但是他们的卷积核还是有差别的)
  • Laplacian算子是一个二阶微分算子(它内部就是调用的Sobel算子)二阶导数可以用来进行检测边缘,因为图像是二维的,需要在两个方向上进行求导(x和y)。使用Laplacian算子会使求导变得简单,让一幅图片减去Laplacian算子的结果可以提高对比度。
  • Canny算子是目前最优的边缘检测的算法,分四步:高斯平滑降噪(滤波)、Sobel计算梯度赋值(增强)、非极大值抑制(增强)、滞后阈值(检测)。
  • 由此看出其实几个算子的关系是藕断丝连的,Sobel是Scharr的封装,Laplacian和Canny调用了Sobel。这也印证了计算机发展史就是一个封装史,技术是不断迭代出来的,不是凭空出现的!
  • 这些算子说道底都要卷积,所以参数和掩膜的差不多,极个别的另外说明
  • 之前提到的波浪号的作用如下为取反。
    【OpenCV:从零到一】14:边缘检测|Scharr滤波器|Sobel算子|Laplacian算子|Canny算子_第1张图片

全注释代码

#include 
#include 
#include 

using namespace cv;

Mat src, gray_src, dst;
int t1_value = 50 ;
int max_value = 255;
void Canny_Demo(int, void*);

int main(int argc, char** argv) {
	src = imread("D://86186//Documents//opencv//lena.jpg");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}
	//imshow("src", src);

	//-------------------Schar------------------------//
	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	GaussianBlur(gray_src, dst, Size(3, 3), 0, 0);
	//imshow("gray image", gray_src);

	Mat xgrad, ygrad, xgrad2, ygrad2;
	Scharr(dst, xgrad, CV_16S, 1, 0);//S->signed
	//Scharr(src,dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT)
	Scharr(dst, ygrad, CV_16S, 0, 1);//dx 和 dy 分别是x方向和y方向上的阶数
	convertScaleAbs(xgrad, xgrad);//convertScaleAbs (InputArray src, OutputArray dst, double alpha=1, double beta=0)
	convertScaleAbs(ygrad, ygrad);//On each element of the input array conversion to an unsigned 8-bit type:
	//imshow("Scharr Xgrad", xgrad);
	//imshow("Scharr Ygrad", ygrad);
	//合并两个方向
	Mat xygrad = Mat(xgrad.size(), xgrad.type());
	//printf("type : %d\n", xgrad.type());
	int width = xgrad.cols;
	int height = ygrad.rows;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			int xg = xgrad.at<uchar>(row, col);
			int yg = ygrad.at<uchar>(row, col);
			int xy = xg + yg;
			xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
		}
	}
	imshow("Scharr XYgrad", xygrad);

	//-------------------Sobel------------------------//
	
	Sobel(gray_src, xgrad2, CV_16S, 1, 0,3);
	// Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize = 3, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT)
	Sobel(gray_src, ygrad2, CV_16S, 0, 1,3);
	
	
	convertScaleAbs(xgrad2, xgrad2);
	convertScaleAbs(ygrad2, ygrad2);
	
	//imshow("Soble Xgrad2", xgrad2);
	//imshow("Soble Ygrad2", ygrad2);
	Mat xygrad2 = Mat(xgrad2.size(), xgrad2.type());
	addWeighted(xgrad2, 0.5, ygrad2, 0.5, 0, xygrad2);
	imshow("Sobel XYgrad", xygrad2);

	//-----------------Laplancce---------------------//
	Mat edge_image;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);
	Laplacian(gray_src, edge_image, CV_16S, 3);
	//Laplacian (InputArray src, OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, int borderType=BORDER_DEFAULT)
	convertScaleAbs(edge_image, edge_image);
	/*
	在梯度运算中,很可能会出现负的灰度值,我们不能再采用uint8类型作为像素的数据类型,
	而是采用了CV_64F这种有负数范围的类型,运算结果里的负数我们也不能不处理,否则在显示的时候就会被截断为0,
	这样就会丢失边界信息。convertScaleAbs函数就是把梯度运算后的图像给正值化,也就是取绝对值
	*/

	threshold(edge_image, edge_image, 0, 255, THRESH_OTSU | THRESH_BINARY);
	imshow("edge_image", edge_image);

	//-----------------Canny---------------------//

	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	GaussianBlur(gray_src, gray_src, Size(3, 3), 0, 0);
	namedWindow("Canny");
	createTrackbar("Threshold Value:", "Canny", &t1_value, max_value, Canny_Demo);
	Canny_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Canny_Demo(int, void*) {
	Mat edge_output;
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);
	/*
	Canny有两个重载
	Canny (InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
	Canny (InputArray dx, InputArray dy, OutputArray edges, double threshold1, double threshold2, bool L2gradient=false)
	threshold1	first threshold for the hysteresis procedure.
	threshold2	second threshold for the hysteresis procedure.
	apertureSize	aperture size for the Sobel operator.
	L2gradient	a flag, 
	dx	16-bit x derivative of input image (CV_16SC1 or CV_16SC3).
	dy	16-bit y derivative of input image (same type as dx).
	*/
	//bitwise_not(edge_output, edge_output);
	imshow("Canny", ~edge_output);//波浪号的作用是整个图像取反,相当于bitwise_not()
}

翻译笔记
magnitude n.震级;巨大;重大;重要性;大小;数量;星等
hysteresis n.【物】磁滞;【物】滞后现象[作用];迟滞性;平衡阻碍
accurate adj.正确无误的;精确的;准确的;准确的(掷、射、击等)
gradient n.倾斜度;梯度变化曲线
aperture n.缝隙;小孔;(尤指摄影机等的光圈)孔径

你可能感兴趣的:(OpenCV:从零到一,卷积,opencv,计算机视觉,边缘检测,c++)