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)
同理得到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演算法.後面的博文會再做介紹.