一、原理简介
边缘检测原理 - Sobel, Laplace, Canny算子
X方向Sobel算子
-1 | -2 | -1 |
0 | 0 | 0 |
1 | 2 | 1 |
Y方向Sobel算子
-1 | 0 | 1 |
-2 | 0 | 2 |
-1 | 0 | 1 |
Laplace算子
1 | 1 | 1 |
1 | -8 | 1 |
1 | 1 | 1 |
Canny 边缘检测算子
高斯滤波器平滑图像
一阶差分偏导计算梯度值和方向
对梯度值不是极大值的地方进行抑制
用双阈值连接图上的联通点
通俗说一下,
1.用高斯滤波主要是去掉图像上的噪声。
2.计算一阶差分,OpenCV 源码中也是用 sobel 算子来算的。
3.算出来的梯度值,把不是极值的点,全部置0,去掉了大部分弱的边缘。所以图像边缘会变细。
4.双阈值 t1, t2, 是这样的,t1 <= t2
大于 t2 的点肯定是边缘
小于 t1 的点肯定不是边缘
在 t1, t2 之间的点,通过已确定的边缘点,发起8领域方向的搜索(广搜),图中可达的是边缘,不可达的点不是边缘。
最后得出 canny 边缘图。
二、代码演示
有关函数 convertScaleAbs,文档解释如下,不过这里不使用其放缩功能
1、Sobel 边缘检测算子
由于需要指定横向纵向,所以分两步进行,最后组合即可,
cv::Mat image = cv::imread("test.jpg"); cv::imshow("原图", image); cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); cv::Mat contours; cv::Mat sobelX, sobelY; cv::Sobel( image, sobelX, CV_16S, // 图像depth,输入8U,输出16S防止外溢 1, 0, // xorder, yorder 3, // 内核尺寸 1, 1 // 输出结果乘alpha加beta ); cv::convertScaleAbs(sobelX, sobelX); cv::imshow("Sobel_X", sobelX); cv::Sobel( image, sobelY, CV_8U, 0, 1, 3, 1, 1 ); cv::convertScaleAbs(sobelY, sobelY); cv::imshow("Sobel_Y", sobelY); cv::addWeighted(sobelX, 0.5, sobelY, 0.5, 0, contours); cv::imshow("Sobel", contours);
XY单方向输出如下,
两这合并如下,
2、Laplace 边缘检测算子
// cv::Mat image = cv::imread("skin.jfif"); cv::Mat image = cv::imread("test.jpg"); cv::imshow("原图", image); cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); cv::Mat contours; cv::GaussianBlur(gray, gray, cv::Size(5, 5), 1.5); cv::Laplacian( gray, contours, CV_16S, 3, // 内核尺寸 1, 0 // 放缩因子 ); cv::convertScaleAbs(contours, contours); cv::imshow("Laplacian", contours);
3、Canny 边缘检测算子
// cv::Mat image = cv::imread("skin.jfif"); cv::Mat image = cv::imread("test.jpg"); cv::imshow("原图", image); cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); cv::Mat contours; cv::GaussianBlur(gray, gray, cv::Size(5, 5), 1.5); cv::Canny( gray, contours, 10, // 低阈值 150 // 高阈值 ); cv::imshow("Canny", contours);
高低阈值参数的设定对于检测效果影响很大,一般来说低阈值检测出十分琐碎的边缘,且设置的越低检测出来的越多,而高阈值这决定了保留多少边缘,对于上图,我们将高阈值下调至50查看一下效果,会发现保留细节数目增加了
附录、函数总览
void edge() { // cv::Mat image = cv::imread("skin.jfif"); cv::Mat image = cv::imread("test.jpg"); cv::imshow("原图", image); cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); cv::Mat contours; cv::GaussianBlur(gray, gray, cv::Size(5, 5), 1.5); cv::Canny( gray, contours, 10, // 低阈值 150 // 高阈值 ); cv::imshow("Canny", contours); cv::Laplacian( gray, contours, CV_16S, 3, // 内核尺寸 1 ); cv::Mat abs_dst; cv::convertScaleAbs(contours, contours); cv::imshow("Laplacian", contours); cv::Mat sobelX, sobelY; cv::Sobel( image, sobelX, CV_16S, // 图像depth,输入8U,输出16S防止外溢 1, 0, // xorder, yorder 3, // 内核尺寸 1, 1 // 输出结果乘alpha加beta ); cv::convertScaleAbs(sobelX, sobelX); cv::imshow("Sobel_X", sobelX); cv::Sobel( image, sobelY, CV_8U, 0, 1, 3, 1, 1 ); cv::convertScaleAbs(sobelY, sobelY); cv::imshow("Sobel_Y", sobelY); cv::addWeighted(sobelX, 0.5, sobelY, 0.5, 0, contours); cv::imshow("Sobel", contours); }