1. 程式人生 > >opencv邊緣檢測-拉普拉斯運算元

opencv邊緣檢測-拉普拉斯運算元

sobel運算元一文說了,索貝爾運算元是模擬一階求導,導數越大的地方說明變換越劇烈,越有可能是邊緣.

那如果繼續對f'(t)求導呢?

可以發現"邊緣處"的二階導數=0. 我們可以利用這一特性去尋找影象的邊緣. 注意有一個問題,二階求導為0的位置也可能是無意義的位置

拉普拉斯運算元推導過程

以x方向求解為例:
一階差分:f'(x) = f(x) - f(x - 1)
二階差分:f''(x) = f'(x+1) - f'(x) = (f(x + 1) - f(x)) - (f(x) - f(x - 1))
化簡後:f''(x) = f(x - 1) - 2 f(x)) + f(x + 1)

提取前面的係數:[1, -2, 1]

同理得到y方向的係數[1,-2,1]

這樣的話,疊加起來就得到了拉普拉斯矩陣

opencv實現

Laplacian api

預設的ksize=1,和ksize=3效果是一樣的,都是用的上述拉普拉斯矩陣去卷積原影象

關於filter具體是什麼,可以通過函式getDerivKernels得到

dx,dy代表求導的階數.

def cal_filter(dx,dy,ksize):
    kx, ky=cv.getDerivKernels(dx, dy, ksize)
    print(kx)
    print(ky)
                                                                  
cal_filter(2,2,1)   
cal_filter(2,2,3) 
cal_filter(2,2,5)

輸出為
可以看到ksize=1和ksize=3其實是一樣的.

import cv2 as cv
def test():
    src = cv.imread("/home/sc/disk/keepgoing/opencv_test/sidetest.jpeg")
    src = cv.GaussianBlur(src, (3, 3), 0)
    gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
    
    dst1 = cv.Laplacian(gray, -1,3)
    dst2 = cv.Laplacian(gray, -1,1)
    
    cv.imshow("origin",src)
    cv.imshow("dst1",dst1)
    cv.imshow("dst2",dst2)
    if 27 == cv.waitKey():
        cv.destroyAllWindows()

test()

效果如下:

sobel和laplace都是比較簡單的邊緣檢測演算法,目前比較常用的是canny,後面的博文會寫到.
在搜尋各種邊緣檢測演算法的適用場景時,發現大部分文章都只講了opencv裡如何實現,並且都是互相抄來抄去.下面給出個人認為講的不錯的兩個link
https://blog.csdn.net/xiaojiegege123456/article/details/7714863
https://dsp.stackexchange.com/questions/74/what-factors-should-i-consider-in-choosing-an-edge-detection-algorithm

二階導數還可以說明灰度突變的型別。在有些情況下,如灰度變化均勻的影象,只利用一階導數可能找不到邊界,此時二階導數就能提供很有用的資訊。二階導數對噪聲也比較敏感,解決的方法是先對影象進行平滑濾波,消除部分噪聲,再進行邊緣檢測。不過,利用二階導數資訊的演算法是基於過零檢測的,因此得到的邊緣點數比較少,有利於後繼的處理和識別工作

總結一下就是:拉普拉斯對噪聲更敏感,但是對邊緣灰度變化不大的影象,檢測效果比索貝爾運算元要好一些.比如下圖中牛和樹的灰度變換並不是特別強.

實際使用中最常用的還是canny演算法.後面的博文會再做介紹.