邊緣檢測 是影像處理 過程中經常會涉及到的一個環節。而在計算機視覺 和 機器學習領域,邊緣檢測 用於 特徵提取 和 特徵檢測 效果也是特別明顯。而 openCV 中進行邊緣檢測的 演算法 真是五花八門,下面我就選幾個最常用演算法的函式api進行介紹。
本文涉及到的效果請看:邊緣檢測
內容大綱
-
轉換灰度圖
-
自適應閾值處理
-
Sobel運算元
-
Scharr運算元
-
Laplacian運算元
-
Canny邊緣檢測
-
Sobel運算元 結合 Laplacian運算元
轉換灰度圖
openCV 中有個色彩型別轉換函式,其中轉換為灰度圖( cv.COLOR_RGB2GRAY)出現頻率非常高,是其他操作的基礎,色彩型別轉換函式:
cv.cvtColor (src, dst, code, dstCn = 0)
- src: 原始影像
- dst: 輸出影像
- code: 色彩空間轉換碼,灰度圖為cv.COLOR_RGB2GRAY,其他型別可查api文件
- dstcn: 影像通道數
const src = cv.imread(img);//讀取影像資料
const dst = new cv.Mat();//輸出的影像
cv.cvtColor(src, dst, cv.COLOR_RGB2GRAY, 0);//轉換為灰度圖
cv.imshow(canvas, dst);
src.delete();
dst.delete();
自適應閾值處理
自適應閾值處理的方式通過計算每個畫素點周圍鄰近區域的加權平均值獲得閾值,並使用該閾值對當前畫素點進行處理。自適應閾值處理函式:
cv.adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType)
- maxValue:需要處理的最大值
- adaptiveMethod:自適應閾值演算法,可選 cv.ADAPTIVE_THRESH_GAUSSIAN_C (高斯方程) 或 cv.ADAPTIVE_THRESH_MEAN_C(加權平均)
- thresholdType: 閾值處理方法,可選 cv.THRESH_BINARY(二值化) 或 cv.THRESH_BINARY_INV(二值化取反)
const src = cv.imread(img);
const dst = new cv.Mat();
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);//轉換為灰度圖
cv.adaptiveThreshold(src, dst, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 7);//自適應閾值處理
cv.imshow(canvas, dst);//顯示輸出影像
效果如下:
Sobel運算元
Sobel運算元結合了高斯平滑 和微分求導運算,利用區域性差分尋找邊緣,計算所得到的是一個梯度近似值。Sobel運算元比較特殊的地方是要分別橫向計算和縱向計算,然後再把兩者結合,這就需要用到 cv.addWeighted 這個函式按比例顯示輸出影像xy方向的佔比。Sobel運算元函式:
cv.Sobel (src, dst, ddepth, dx, dy, ksize = 3, scale = 1, delta = 0, borderType = cv.BORDER_DEFAULT)
- ddepth:輸出影像的深度,可選 cv.CV_8U,cv.CV_16U,cv.CV_32F,cv.CV_64F
- dx:x方向求導
- dy:y方向求導
- ksize:核大小,可選1,3,5,7
- scale:縮放因子,預設1
- delta:影像dst的值,預設0
- borderType:邊界樣式,具體可檢視api文件
const src = cv.imread(img);
const dstx = new cv.Mat();
const dsty = new cv.Mat();
const dst = new cv.Mat();
cv.cvtColor(src, src, cv.COLOR_RGB2GRAY, 0);
cv.Sobel(src, dstx, cv.CV_8U, 1, 0, 1, 3, 0, cv.BORDER_DEFAULT); //Sobel運算元 x方向
cv.Sobel(src, dsty, cv.CV_8U, 0, 1, 1, 3, 0, cv.BORDER_DEFAULT); //Sobel運算元 y方向
cv.addWeighted(dstx, 0.5, dsty, 0.5, 0, dst); //分別給xy方向分配權重比例
cv.imshow(canvas, dst);
效果如下:
Scharr運算元
Scharr運算元可以看做是對Sobel運算元的改進,它的精度更高,呼叫方式和Sobel運算元基本一致,只是少了ksize 這個引數,下面直接看呼叫程式碼不同的部分:
cv.Scharr(src, dstx, cv.CV_8U, 1, 0, 1, 0, cv.BORDER_DEFAULT); //Scharr運算元 x方向
cv.Scharr(src, dsty, cv.CV_8U, 0, 1, 1, 0, cv.BORDER_DEFAULT); //Scharr運算元 y方向
cv.addWeighted(dstx, 0.5, dsty, 0.5, 0, dst); //分別給xy方向分配權重比例
效果如下:
Laplacian運算元
Laplacian(拉普拉斯)運算元是一種二階導數運算元,可以滿足不同方向的影像邊緣銳化,Laplacian(拉普拉斯)運算元分別進行了兩次橫向和縱向的計算。因此就不用跟 Sobel運算元 和 Scharr運算元 一樣要分別單獨計算xy了。Laplacian運算元函式:
cv.Laplacian (src, dst, ddepth, ksize = 1, scale = 1, delta = 0, borderType = cv.BORDER_DEFAULT)
- ddepth:輸出影像的深度,可選 cv.CV_8U,cv.CV_16U,cv.CV_32F,cv.CV_64F
- ksize:核大小,可選1,3,5,7
- scale:縮放因子,預設1
- delta:影像dst的值,預設0
- borderType:邊界樣式,具體可檢視api文件
const src = cv.imread(img);
const dst = new cv.Mat();
cv.cvtColor(src, src, cv.COLOR_RGB2GRAY, 0);
cv.Laplacian(src, dst, cv.CV_8U, 1, 1, 0, cv.BORDER_DEFAULT); //拉普拉斯運算元
cv.imshow(canvas, dst);
效果如下:
Canny邊緣檢測
Canny邊緣檢測是一種使用多級邊緣檢測演算法的方法。它會經過去噪,計算梯度,非極大值抑制,確定邊緣這幾個步驟,好像很強大的感覺。Canny函式:
cv.Canny(src, dot, threshold1, threshold2, apertureSize = 3, L2gradient = false)
- threshold1: 第一個閾值,值較小時能獲取更多邊緣資訊
- threshold2: 第二個閾值,值較小時能獲取更多邊緣資訊
- apertureSize: 孔徑大小
- L2gradient: 影像梯度幅度,預設False
const src = cv.imread(img);
const dst = new cv.Mat();
cv.cvtColor(src, src, cv.COLOR_RGB2GRAY, 0);
cv.Canny(src, dst, 50, 100, 3, false);
cv.imshow(canvas, dst);
效果如下:
Sobel運算元 結合 Laplacian運算元
openCV 還可以將不同演算法結合起來,達到更好的效果,我們就以 Sobel運算元 結合 Laplacian運算元 為例
const src = cv.imread(img);
const dstx = new cv.Mat();
const dsty = new cv.Mat();
const dst = new cv.Mat();
const dst2 = new cv.Mat();
cv.Sobel(src, dstx, cv.CV_8U, 1, 0, 1, 3, 0, cv.BORDER_DEFAULT); //sobel運算元 x方向
cv.Sobel(src, dsty, cv.CV_8U, 0, 1, 1, 3, 0, cv.BORDER_DEFAULT); //sobel運算元 y方向
cv.addWeighted(dstx, 0.5, dsty, 0.5, 0, dst); //分別給xy方向分配權重比例
cv.Laplacian(src, dst2, cv.CV_8U, 1, 1, 0, cv.BORDER_DEFAULT); //拉普拉斯運算元
const mask = new cv.Mat();
cv.add(dst, dst2, dst2, mask, -1); //影像相加
cv.imshow(canvas, dst2);
效果如下:
總結
openCV中實現邊緣檢測遠不止上面介紹的幾種,還有一種更強大的方式實現邊緣檢測,那就是傅立葉變換,它完全可以實現上面說的演算法,但是比較複雜而已。我們需要做的就是了解清楚每種演算法 效果有什麼差異,以及最適合使用的場景。