OpenCV与图像处理学习八——图像边缘提取(Canny检测代码)
OpenCV與圖像處理學習八——圖像邊緣提取(Canny檢測代碼)
- 一、圖像梯度
- 1.1 梯度
- 1.2 圖像梯度
- 二、梯度圖與梯度算子
- 2.1模板卷積
- 2.2 梯度圖
- 2.3 梯度算子
- 2.3.1 Roberts交叉算子
- 2.3.2 Prewitt算子
- 2.3.3 Sobel算子
- 三、Canny邊緣檢測算法(代碼實現)
這次筆記簡單介紹圖像梯度、梯度圖以及梯度算子的概念,并詳細介紹三種基本的梯度算子,然后簡單的介紹Canny檢測的原理與代碼實現(因為Canny檢測中有很重要的一步用到了Sobel算子計算梯度,所以先介紹前面的內容)。
一、圖像梯度
1.1 梯度
先來看梯度的概念:
梯度是一個向量,梯度方向指向函數變化最快的方向,大小就是它的模,也是最大的變化率,對于二元函數z=f(x,y),它在點(x,y)的梯度記為:
或:
梯度的計算公式為:
梯度向量的幅值和方向角為:
有了梯度是最大變化率這么一個認識,下面我們拓展到圖像梯度的概念上來。
1.2 圖像梯度
圖像梯度即圖像中灰度變化的度量,求圖像梯度的過程是二維離散函數求導過程。
因為圖像邊緣上的像素值變化非常劇烈,所以圖像的邊緣其實就是圖像上灰度級變化很快的點的集合。
下圖展示了一個灰度圖的數學化表達,像素點(x,y)的灰度值是f(x,y),它有八個鄰域(有時使用四鄰域):
圖像在點(x,y)的梯度為:
分別對應圖像的水平方向和豎直方向,可見圖像梯度的求法只是像素值之間的差,而無需求導(因為數字圖像是離散的)。
二、梯度圖與梯度算子
2.1模板卷積
要理解梯度圖的生成,就要先了解模板卷積的過程,模板卷積是模板運算的一種方式,其步驟如下:
其實就是現在的卷積運算干的事。
2.2 梯度圖
梯度圖的生成和模板卷積相同,不同的是要生成梯度圖,還需要在模板卷積完成后計算在點(x,y)梯度的幅值,將幅值作為像素值,這樣才算完。
注意: 梯度圖上每個像素點的灰度值就是梯度向量的幅度,生成梯度圖需要兩個模板(求圖像梯度需要兩個方向),右圖為水平和豎直方向最簡單的模板:
所以水平方向和豎直方向上的梯度為:
2.3 梯度算子
梯度算子是一階導數算子,是水平G(x)和豎直G(y)方向對應模板的組合,也有對角線方向,即是上述卷積模板的組合。
常見的一階算子:Roberts交叉算子, Prewitt算子, Sobel算子,下面將分別介紹。
2.3.1 Roberts交叉算子
Roberts交叉算子其本質是一個對角線方向的梯度算子,對應的水平方向和豎直方向的梯度分別為:
優點:邊緣定位較準,適用于邊緣明顯且噪聲較少的圖像。
缺點:
2.3.2 Prewitt算子
Prewitt算子是典型的3*3模板,其模板中心對應要求梯度的原圖像坐標(x,y), (x,y)對應的8-鄰域的像素灰度值如下表所示:
通過Prewitt算子的水平模板M(x)卷積后,對應的水平方向梯度為:
通過Prewitt算子的豎直模板M(y)卷積后,對應的豎直方向梯度為:
輸出梯度圖在(x,y)的灰度值為:
優點:Prewitt算子引入了類似局部平均的運算,對噪聲具有平滑作用,較Roberts算子更能抑制噪聲。
2.3.3 Sobel算子
Sobel算子其實就是是增加了權重系數的Prewitt算子,其模板中心對應要求梯度的原圖像坐標,對應的8-鄰域的像素灰度值如下表所示:
通過Sobel算子的水平模板M(x)卷積后,對應的水平方向梯度為:
通過Sobel算子的豎直模板M(y)卷積后,對應的豎直方向梯度為:
輸出梯度圖在(x,y)的灰度值為:
優點:Sobel算子引入了類似局部加權平均的運算,對邊緣的定位比要比Prewitt算子好。
因為Sobel算子的效果較好,實際使用中相比于另外兩種更多,所以我們只看一下Sobel算子的例子。
函數:
dst = cv2.Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] )參數:
src:輸入圖像。
ddepth:輸出圖像位深度,-1表示采用的是與原圖像相同的深度。目標圖像的深度必須大于等于原圖像的深度;
dx:x導數的階數,0表示這個方向上沒有求導,一般為0、 1、 2;
dy:y導數的階數,0表示這個方向上沒有求導,一般為0、 1、 2;
ksize:Sobel算子的尺寸,必須是1,3,5或7。還可以是一個特殊值,ksize = FILTER_SCHARR (-1),那么將會使用scharr算子,在x方向的算子為:
在y方向上是這個算子的轉置。
scale:(可選)計算的導數值的比例因子;默認情況下,不應用縮放。
delta:(可選)在將結果存儲到dst中之前添加到結果中的增量值。
看個例子:
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('./image/girl2.png', 0) sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)plt.subplot(1, 3, 1), plt.imshow(img, cmap = 'gray') plt.title('Original'), plt.xticks([]), plt.yticks([]) plt.subplot(1, 3, 2), plt.imshow(sobelx, cmap = 'gray') plt.title('Sobel X'), plt.xticks([]), plt.yticks([]) plt.subplot(1, 3, 3),plt.imshow(sobely, cmap = 'gray') plt.title('Sobel Y'), plt.xticks([]), plt.yticks([]) plt.show()得到的就是該圖像在x方向上和y方向上的梯度圖:
從這個效果中我們也可以看出,梯度圖能夠突出圖像中的邊緣或明暗變化劇烈的地方。
三、Canny邊緣檢測算法(代碼實現)
Canny算法是先平滑后求導數的方法。 John Canny研究了最優邊緣檢測方法所需的特性,給出了評價邊緣檢測性能優劣的三個指標:
步驟:
在OpenCV中的函數為:
edges = cv2.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )參數:
看個例子:
# 加載 opencv 和 numpy import cv2 import numpy as np # 以灰度圖形式讀入圖像 img = cv2.imread('./image/canny.png', 0) v1 = cv2.Canny(img, 80, 150, (3, 3)) v2 = cv2.Canny(img, 50, 100, (5, 5))# np.vstack():在豎直方向上堆疊 # np.hstack():在水平方向上平鋪堆疊 ret = np.hstack((v1, v2)) cv2.imshow('img', ret) cv2.waitKey(0) cv2.destroyAllWindows()原圖如下所示:
通過canny檢測得到該圖的邊緣信息,設置了兩組參數,得到的結果分別為:
不同的參數設置可能得到不同的結果,很明顯第一種參數使得檢測得到的邊緣信息更少更干凈,而第二種得到的更多更全面,實際使用中可以自己調節。
Canny檢測作為傳統的圖像邊緣提取算法,雖然效果上不如現在大火的深度學習的各種網絡,但是對于一些邊緣信息較為明顯單一的圖像來說任然有使用價值,我們將輸入圖像換一下:
再用Canny檢測(上述代碼,換一下輸入圖像即可),得到的結果為:
可以看到檢測效果已經非常不錯了,更重要的是,它不需要訓練,所以速度非常的快,這是它很大的一個優點,所以現在還是會使用到Canny檢測。
總結
以上是生活随笔為你收集整理的OpenCV与图像处理学习八——图像边缘提取(Canny检测代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从‘一边拉琴,一边哭’,看什么是真正的兴
- 下一篇: Libcurl安装与HelloWorld