Opencv(四)卷积及边缘检测sobel、laplacian、canny

自定义线性滤波

卷积概念:
卷积是图像处理中一个操作,是kernel在图像的每个像素上的操作。
Kernel本质上一个固定大小的矩阵数组,其中心点称为锚点(anchor point)
Opencv(四)卷积及边缘检测sobel、laplacian、canny_第1张图片

自定义卷积模糊:
filter2D方法filter2D(
Mat src, //输入图像
Mat dst, // 模糊图像
int depth, // 图像深度32/8
Mat kernel, // 卷积核/模板
Point anchor, // 锚点位置
double delta // 计算出来的像素+delta

)
其中 kernel是可以自定义的卷积核.
例如:
Opencv(四)卷积及边缘检测sobel、laplacian、canny_第2张图片

#include 
#include 
#include 

using namespace cv;
int main(int argc, char** argv) {
     
	Mat src, dst;
	int ksize = 0;

	src = imread("D:/vcprojects/images/test1.png");
	if (!src.data) {
     
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_WIN[] = "input image";
	char OUTPUT_WIN[] = "Custom Blur Filter Result";
	namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);

	imshow(INPUT_WIN, src);
	
	// Sobel X 方向
	// Mat kernel_x = (Mat_(3, 3) << -1, 0, 1, -2,0,2,-1,0,1);
	// filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0.0);

	// Sobel Y 方向
	// Mat yimg;
	// Mat kernel_y = (Mat_(3, 3) << -1, -2, -1, 0,0,0, 1,2,1);
	// filter2D(src, yimg, -1, kernel_y, Point(-1, -1), 0.0);

	// 拉普拉斯算子
	//Mat kernel_y = (Mat_(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
	//filter2D(src, dst, -1, kernel_y, Point(-1, -1), 0.0);
	int c = 0;
	int index = 0;
	while (true) {
     
		c = waitKey(500);
		if ((char)c == 27) {
     // ESC 
			break;
		}
		ksize = 5 + (index % 8) * 2;
		Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);
		filter2D(src, dst, -1, kernel, Point(-1, -1));
		index++;
		imshow(OUTPUT_WIN, dst);	
	}

	// imshow("Sobel Y", yimg);
	return 0;
}

卷积边界问题

图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理。
在卷积开始之前增加边缘像素,填充的像素值为0或者RGB黑色,比如3x3在
四周各填充1个像素的边缘,这样就确保图像的边缘被处理,在卷积处理之
后再去掉这些边缘。openCV中默认的处理方法是: BORDER_DEFAULT,此外
常用的还有如下几种:

  • BORDER_CONSTANT – 填充边缘用指定像素值
  • BORDER_REPLICATE – 填充边缘像素用已知的边缘像素值。
  • BORDER_WRAP – 用另外一边的像素来补偿填充
#include 
#include 
#include 

using namespace cv;
int main(int argc, char** argv) {
     
	Mat src, dst;
	src = imread("D:/vcprojects/images/test.jpg");
	if (!src.data) {
     
		printf("could not load image...\n");
		return -1;
	}
	char INPUT_WIN[] = "input image";
	char OUTPUT_WIN[] = "Border Demo";
	namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_WIN, src);
	/*
	int top = (int)(0.05*src.rows);
	int bottom = (int)(0.05*src.rows);
	int left = (int)(0.05*src.cols);
	int right = (int)(0.05*src.cols);
	RNG rng(12345);
	int borderType = BORDER_DEFAULT;

	int c = 0;
	while (true) {
		c = waitKey(500);
		// ESC
		if ((char)c == 27) {
			break;
		}
		if ((char)c == 'r') {
			borderType = BORDER_REPLICATE;
		} else if((char)c == 'w') {
			borderType = BORDER_WRAP;
		} else if((char)c == 'c') {
			borderType = BORDER_CONSTANT;
		} 
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);
		imshow(OUTPUT_WIN, dst);
	}
	*/

	GaussianBlur(src, dst, Size(5, 5), 0, 0);
	imshow(OUTPUT_WIN, dst);

	waitKey(0);
	return 0;
}

卷积应用-图像边缘提取

Sobel算子:
是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度
Soble算子功能集合高斯平滑和微分求导
又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方法与Y方向梯度图像

在这里插入图片描述
Opencv(四)卷积及边缘检测sobel、laplacian、canny_第3张图片求取导数的近似值,kernel=3时不是很准确,OpenCV使用改进版本Scharr函数,算子如下:
Opencv(四)卷积及边缘检测sobel、laplacian、canny_第4张图片
cv::Sobel (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度.
Int dx. // X方向,几阶导数
int dy // Y方向,几阶导数.
int ksize, SOBEL算子kernel大小,必须是1、3、5、7、
double scale = 1
double delta = 0
int borderType = BORDER_DEFAULT
)

Opencv(四)卷积及边缘检测sobel、laplacian、canny_第5张图片]cv::Scharr (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度.
Int dx. // X方向,几阶导数
int dy // Y方向,几阶导数.
double scale = 1
double delta = 0
int borderType = BORDER_DEFAULT
)
其他:
GaussianBlur( src, dst, Size(3,3), 0, 0, BORDER_DEFAULT );
cvtColor( src, gray, COLOR_RGB2GRAY );
addWeighted( A, 0.5,B, 0.5, 0, AB);
convertScaleAbs(A, B)// 计算图像A的像素绝对值,输出到图像B

#include 
#include 
#include 

using namespace cv;
int main(int argc, char** argv) {
     
	Mat src, dst;
	src = imread("D:/vcprojects/images/test.png");
	if (!src.data) {
     
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_TITLE[] = "input image";
	char OUTPUT_TITLE[] = "sobel-demo";
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	Mat gray_src;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_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("xgrad", xgrad);
	imshow("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(OUTPUT_TITLE, xygrad);

	waitKey(0);
	return 0;
}

Laplance算子

二阶导数我不会,别担心 ->拉普拉斯算子(Laplance operator)
Opencv(四)卷积及边缘检测sobel、laplacian、canny_第6张图片

Opencv已经提供了相关API - cv::Laplance

处理流程:
高斯模糊 – 去噪声GaussianBlur()
转换为灰度图像cvtColor()
拉普拉斯 – 二阶导数计算Laplacian()
取绝对值convertScaleAbs()
显示结果

Laplacian(
InputArray src,
OutputArray dst,
int depth, //深度CV_16S
int kisze, // 3
double scale = 1,
double delta =0.0,
int borderType = 4
)

#include 
#include 
#include 

using namespace cv;
int main(int argc, char** argv) {
     
	Mat src, dst;
	src = imread("D:/vcprojects/images/lena.png");
	if (!src.data) {
     
		printf("could not load image");
	}
	char input_title[] = "input image";
	char output_title[] = "Laplaiance Result";
	namedWindow(input_title, CV_WINDOW_AUTOSIZE);
	imshow(input_title, src);

	Mat gray_src, edge_image;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_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);
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow(output_title, edge_image);

	waitKey(0);
	return 0;
}

Opencv(四)卷积及边缘检测sobel、laplacian、canny_第7张图片Canny算法介绍 – 五步 in cv::Canny
高斯模糊 - GaussianBlur
灰度转换 - cvtColor
计算梯度 – Sobel/Scharr
非最大信号抑制
高低阈值输出二值图像

Canny(
InputArray src, // 8-bit的输入图像
OutputArray edges,// 输出边缘图像, 一般都是二值图像,背景是黑色
double threshold1,// 低阈值,常取高阈值的1/2或者1/3
double threshold2,// 高阈值
int aptertureSize,// Soble算子的size,通常3x3,取值3
bool L2gradient // 选择 true表示是L2来归一化,否则用L1归一化

Opencv(四)卷积及边缘检测sobel、laplacian、canny_第8张图片默认情况一般选择是L1,参数设置为false

#include 
#include 
#include 

using namespace cv;
using namespace std;
int main(int argc, char** argv) {
     
	Mat src, src_gray, dst;
	src = imread("D:/vcprojects/images/lines.png");
	if (!src.data) {
     
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_TITLE[] = "input image";
	char OUTPUT_TITLE[] = "hough-line-detection";
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	// extract edge
	Canny(src, src_gray, 150, 200);
	cvtColor(src_gray, dst, CV_GRAY2BGR);
	imshow("edge image", src_gray);

	vector<Vec2f> lines;     
	HoughLines(src_gray, lines, 1, CV_PI / 180, 150, 0, 0);
	for (size_t i = 0; i < lines.size(); i++) {
      
		float rho = lines[i][0]; // 极坐标中的r长度
		float theta = lines[i][1]; // 极坐标中的角度
		Point pt1, pt2;         
		double a = cos(theta), b = sin(theta);         
		double x0 = a*rho, y0 = b*rho;      
		// 转换为平面坐标的四个点
		pt1.x = cvRound(x0 + 1000 * (-b));        
		pt1.y = cvRound(y0 + 1000 * (a));         
		pt2.x = cvRound(x0 - 1000 * (-b));         
		pt2.y = cvRound(y0 - 1000 * (a));         
		line(dst, pt1, pt2, Scalar(0, 0, 255), 1, CV_AA); 
	}

	/*
	vector plines;
	HoughLinesP(src_gray, plines, 1, CV_PI / 180.0, 10, 0, 10);
	Scalar color = Scalar(0, 0, 255);
	for (size_t i = 0; i < plines.size(); i++) {
		Vec4f hline = plines[i];
		line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA);
	}*/
	imshow(OUTPUT_TITLE, dst);

	waitKey(0);
	return 0;
}

你可能感兴趣的:(C++,opencv,opencv,计算机视觉,边缘检测)