一、原理簡介
邊緣檢測原理 - 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);
}
