opencv4 学习 12 边缘检测
1、Sobel 算子和 Scharr 算子
原理:都是先计算当前像素的水平方向梯度和垂直方向梯度,然后取两梯度的平方和,再开方;也可以直接取两梯度的绝对值的和。
#include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <iostream> using namespace cv; int main(int argc, char* argv[]) { Mat image, src, src_gray; Mat grad; const String window_name = "Sobel Demo - Simple Edge Detector"; int ksize = 1; int scale = 1; int delta = 0; int ddepth = CV_16S; String imageName = "lena.jpg"; image = imread(imageName, IMREAD_COLOR); for(;;){ GaussianBlur(image, src, Size(3,3), 0, 0, BORDER_DEFAULT); cvtColor(src, src_gray, COLOR_BGR2GRAY); Mat grad_x, grad_y; Mat abs_grad_x, abs_grad_y; Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT); Sobel(src_gray, grad_y, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT); convertScaleAbs(grad_x, abs_grad_x); convertScaleAbs(grad_y, abs_grad_y); addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad); imshow(window_name, grad); char key = (char) waitKey(0); if(key == 27) return EXIT_SUCCESS; if(key == 'k' || key == 'K') ksize = ksize < 30 ? ksize+2: -1; if(key == 's' || key == 'S') scale++; if(key == 'd' || key == 'D') delta++; if(key == 'r' || key == 'R') {scale=1, ksize=-1, delta = 0;} // when ksize = -1, it equal to Scharr function } return EXIT_SUCCESS; }
2、Laplacian 算子
前面的算子都是计算像素的一阶导数,Laplacian 算子计算像素的二阶导数,其内部实际上也是调用Soble算子实现的。
#include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <iostream> using namespace cv; int main(int argc, char* argv[]) { Mat image, src, src_gray, dst; Mat grad; const String window_name = "Laplacian Demo - Simple Edge Detector"; int kernel_size = 3; int scale = 1; int delta = 0; int ddepth = CV_16S; String imageName = "lena.jpg"; image = imread(imageName, IMREAD_COLOR); GaussianBlur(image, src, Size(3,3), 0, 0, BORDER_DEFAULT); cvtColor(src, src_gray, COLOR_BGR2GRAY); Mat abs_dst; Laplacian(src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_CONSTANT); convertScaleAbs(dst, abs_dst); imshow(window_name, abs_dst); char key = (char) waitKey(0); return EXIT_SUCCESS; }
3、 Canny 算子
步骤:
- 对图像进行高斯模糊
- 分别计算水平和垂直方向的梯度以及方向梯度之间的夹角
- 使用 Non-maximum suppression 去除冗余像素
- 设置两个阈值(upper, lower)过滤像素
- 当像素梯度大于upper时,则为边缘
- 当像素梯度小于lower时,则剔除
- 当像素梯度位于lower和upper之间时,如果该像素和一个梯度大于upper的像素连接,则该像素则为边缘;否则,剔除
- canny 算法推荐的 upper:lower 比率在 2:1 和 3:1 之间
#include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <iostream> using namespace cv; Mat src, src_gray; Mat dst, detected_edges; int lowThreshold = 0; const int max_lowThreshold = 100; const int ratio = 3; const int kernel_size = 3; const char* window_name = "Edge Map"; static void cannyThreshold(int, void*){ blur(src_gray, detected_edges, Size(3,3)); Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size); dst = Scalar::all(0); src.copyTo(dst, detected_edges); imshow(window_name, dst); } int main(int argc, char* argv[]) { src = imread("lena.jpg", IMREAD_COLOR); dst.create(src.size(), src.type()); cvtColor(src,src_gray,COLOR_BGR2GRAY); namedWindow(window_name, WINDOW_AUTOSIZE); createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, cannyThreshold); cannyThreshold(0,0); char key = (char) waitKey(0); return EXIT_SUCCESS; }
参考:
https://docs.opencv.org/3.4/d2/d2c/tutorial_sobel_derivatives.html
https://docs.opencv.org/3.4/d5/db5/tutorial_laplace_operator.html
https://docs.opencv.org/3.4/da/d5c/tutorial_canny_detector.html