在OpenCV中基於深度學習的邊緣檢測


 
    
    
    
     
     
              
     
 
    

點擊上方AI算法與圖像處理」,選擇加"星標"或「置頂c++

重磅乾貨,第一時間送達git



做者:ANKIT SACHANgithub

編譯:ronghuaiyang
web

導讀

分析了Canny的優劣,並給出了OpenCV使用深度學習作邊緣檢測的流程,文末有代碼連接。算法

在這篇文章中,咱們將學習如何在OpenCV中使用基於深度學習的邊緣檢測,它比目前流行的canny邊緣檢測器更精確。邊緣檢測在許多用例中是有用的,如視覺顯著性檢測,目標檢測,跟蹤和運動分析,結構從運動,3D重建,自動駕駛,圖像到文本分析等等。
編程

什麼是邊緣檢測?

邊緣檢測是計算機視覺中一個很是古老的問題,它涉及到檢測圖像中的邊緣來肯定目標的邊界,從而分離感興趣的目標。最流行的邊緣檢測技術之一是Canny邊緣檢測,它已經成爲大多數計算機視覺研究人員和實踐者的首選方法。讓咱們快速看一下Canny邊緣檢測。微信

Canny邊緣檢測算法

1983年,John Canny在麻省理工學院發明了Canny邊緣檢測。它將邊緣檢測視爲一個信號處理問題。其核心思想是,若是你觀察圖像中每一個像素的強度變化,它在邊緣的時候很是高。網絡

在下面這張簡單的圖片中,強度變化只發生在邊界上。因此,你能夠很容易地經過觀察像素強度的變化來識別邊緣。app

如今,看下這張圖片。強度不是恆定的,但強度的變化率在邊緣處最高。(微積分複習:變化率能夠用一階導數(梯度)來計算。)
框架

Canny邊緣檢測器經過4步來識別邊緣:

  1. 去噪:由於這種方法依賴於強度的忽然變化,若是圖像有不少隨機噪聲,那麼會將噪聲做爲邊緣。因此,使用5×5的高斯濾波器平滑你的圖像是一個很是好的主意。
  2. 梯度計算:下一步,咱們計算圖像中每一個像素的強度的梯度(強度變化率)。咱們也計算梯度的方向。

梯度方向垂直於邊緣,它被映射到四個方向中的一個(水平、垂直和兩個對角線方向)。
  1. 非極大值抑制:如今,咱們想刪除不是邊緣的像素(設置它們的值爲0)。你可能會說,咱們能夠簡單地選取梯度值最高的像素,這些就是咱們的邊。然而,在真實的圖像中,梯度不是簡單地在只一個像素處達到峯值,而是在臨近邊緣的像素處都很是高。所以咱們在梯度方向上取3×3附近的局部最大值。

  1. 遲滯閾值化:在下一步中,咱們須要決定一個梯度的閾值,低於這個閾值全部的像素都將被抑制(設置爲0)。而Canny邊緣檢測器則採用遲滯閾值法。遲滯閾值法是一種很是簡單而有效的方法。咱們使用兩個閾值來代替只用一個閾值:

    高閾值 = 選擇一個很是高的值,這樣任何梯度值高於這個值的像素都確定是一個邊緣。

    低閾值 = 選擇一個很是低的值,任何梯度值低於該值的像素絕對不是邊緣。

    在這兩個閾值之間有梯度的像素會被檢查,若是它們和邊緣相連,就會留下,不然就會去掉。

遲滯閾值化

Canny 邊緣檢測的問題:

因爲Canny邊緣檢測器只關注局部變化,沒有語義(理解圖像的內容)理解,精度有限(不少時候是這樣)。

Canny邊緣檢測器在這種狀況下會失敗,由於沒有理解圖像的上下文

語義理解對於邊緣檢測是相當重要的,這就是爲何使用機器學習或深度學習的基於學習的檢測器比canny邊緣檢測器產生更好的結果。

OpenCV中基於深度學習的邊緣檢測

OpenCV在其全新的DNN模塊中集成了基於深度學習的邊緣檢測技術。你須要OpenCV 3.4.3或更高版本。這種技術被稱爲總體嵌套邊緣檢測或HED,是一種基於學習的端到端邊緣檢測系統,使用修剪過的相似vgg的卷積神經網絡進行圖像到圖像的預測任務。

HED利用了中間層的輸出。以前的層的輸出稱爲side output,將全部5個卷積層的輸出進行融合,生成最終的預測。因爲在每一層生成的特徵圖大小不一樣,它能夠有效地以不一樣的尺度查看圖像。

網絡結構:總體嵌套邊緣檢測

HED方法不只比其餘基於深度學習的方法更準確,並且速度也比其餘方法快得多。這就是爲何OpenCV決定將其集成到新的DNN模塊中。如下是這篇論文的結果:

在OpenCV中訓練深度學習邊緣檢測的代碼

OpenCV使用的預訓練模型已經在Caffe框架中訓練過了,能夠這樣加載:

sh download_pretrained.sh

網絡中有一個crop層,默認是沒有實現的,因此咱們須要本身實現一下。

class CropLayer(object):
    def __init__(self, params, blobs):
        self.xstart = 0
        self.xend = 0
        self.ystart = 0
        self.yend = 0

    # Our layer receives two inputs. We need to crop the first input blob
    # to match a shape of the second one (keeping batch size and number of channels)
    def getMemoryShapes(self, inputs):
        inputShape, targetShape = inputs[0], inputs[1]
        batchSize, numChannels = inputShape[0], inputShape[1]
        height, width = targetShape[2], targetShape[3]

        self.ystart = (inputShape[2] - targetShape[2]) // 2
        self.xstart = (inputShape[3] - targetShape[3]) // 2
        self.yend = self.ystart + height
        self.xend = self.xstart + width

        return [[batchSize, numChannels, height, width]]

    def forward(self, inputs):
        return [inputs[0][:,:,self.ystart:self.yend,self.xstart:self.xend]]

如今,咱們能夠重載這個類,只需用一行代碼註冊該層。

cv.dnn_registerLayer('Crop', CropLayer)

如今,咱們準備構建網絡圖並加載權重,這能夠經過OpenCV的dnn.readNe函數。

net = cv.dnn.readNet(args.prototxt, args.caffemodel)

如今,下一步是批量加載圖像,並經過網絡運行它們。爲此,咱們使用cv2.dnn.blobFromImage方法。該方法從輸入圖像中建立四維blob。

blob = cv.dnn.blobFromImage(image, scalefactor, size, mean, swapRB, crop)

其中:

image:是咱們想要發送給神經網絡進行推理的輸入圖像。

scalefactor:圖像縮放常數,不少時候咱們須要把uint8的圖像除以255,這樣全部的像素都在0到1之間。默認值是1.0,不縮放。

size:輸出圖像的空間大小。它將等於後續神經網絡做爲blobFromImage輸出所需的輸入大小。

swapRB:布爾值,表示咱們是否想在3通道圖像中交換第一個和最後一個通道。OpenCV默認圖像爲BGR格式,但若是咱們想將此順序轉換爲RGB,咱們能夠將此標誌設置爲True,這也是默認值。

mean:爲了進行歸一化,有時咱們計算訓練數據集上的平均像素值,並在訓練過程當中從每幅圖像中減去它。若是咱們在訓練中作均值減法,那麼咱們必須在推理中應用它。這個平均值是一個對應於R, G, B通道的元組。例如Imagenet數據集的均值是R=103.93, G=116.77, B=123.68。若是咱們使用swapRB=False,那麼這個順序將是(B, G, R)。

crop:布爾標誌,表示咱們是否想居中裁剪圖像。若是設置爲True,則從中心裁剪輸入圖像時,較小的尺寸等於相應的尺寸,而其餘尺寸等於或大於該尺寸。然而,若是咱們將其設置爲False,它將保留長寬比,只是將其調整爲固定尺寸大小。

在咱們這個場景下:

inp = cv.dnn.blobFromImage(frame, scalefactor=1.0, size=(args.width, args.height),                 
                           mean=(104.00698793116.66876762122.67891434), swapRB=False,                 
                           crop=False)

如今,咱們只須要調用一下前向方法。

net.setInput(inp)
out = net.forward()
out = out[00]
out = cv.resize(out, (frame.shape[1], frame.shape[0]))
out = 255 * out
out = out.astype(np.uint8)
out=cv.cvtColor(out,cv.COLOR_GRAY2BGR)
con=np.concatenate((frame,out),axis=1)
cv.imshow(kWinName,con)

結果:

中間的圖像是人工標註的圖像,右邊是HED的結果

中間的圖像是人工標註的圖像,右邊是HED的結果

文中的代碼:https://github.com/sankit1/cv-tricks.com/tree/master/OpenCV/Edge_detection


END

英文原文:https://cv-tricks.com/opencv-dnn/edge-detection-hed/

下載1:何愷明頂會分享


AI算法與圖像處理」公衆號後臺回覆:何愷明,便可下載。總共有6份PDF,涉及 ResNet、Mask RCNN等經典工做的總結分析


下載2:終身受益的編程指南:Google編程風格指南


AI算法與圖像處理」公衆號後臺回覆:c++,便可下載。歷經十年考驗,最權威的編程規範!



   
下載3 CVPR2020

AI算法與圖像處公衆號後臺回覆: CVPR2020 便可下載1467篇CVPR 2020論文
 
    
    
    
     
     
              
     
 
    
我的微信(若是沒有備註不拉羣!
請註明: 地區+學校/企業+研究方向+暱稱


以爲不錯就點亮在看吧



本文分享自微信公衆號 - AI算法與圖像處理(AI_study)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索