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