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