概述
OpenCV在使用卷積進行圖像處理過程種,如何處理邊緣像素與錨定輸出兩個技術細節一直是很多人求而不得的疑惑。其實OpenCV在做卷積濾波時會對圖像進行邊界填充,實現對邊緣像素的卷積計算的支持,不同填充方式與不同錨定點會得到圖像卷積輸出不同的結果。
邊界填充
我們首先來看一下OpenCV種支持標準卷積邊緣填充做法,OpenCV支持的有如下幾種卷積邊緣填充算法:
常量邊界
BORDER_CONSTANT
iiiiii|abcdefgh|iiiiiii
邊界復制
BORDER_REPLICATE
aaaaaa|abcdefgh|hhhhhhh
邊界反射
BORDER_REFLECT
fedcba|abcdefgh|hgfedcb
邊界換行
BORDER_WRAP
cdefgh|abcdefgh|abcdefg
邊界反射101
BORDER_REFLECT_101
gfedcb|abcdefgh|gfedcba
邊界透明-很不幸運的是OpenCV4已經不支持啦!
BORDER_TRANSPARENT
uvwxyz|abcdefgh|ijklmno
默認填充方式
OpenCV中 filter2D, blur, GaussianBlur等卷積操作默認支持為BORDER_DEFAULT(BORDER_REFLECT_101)
各種不同方式對邊緣的填充效果如下:
上圖背景為紅色,填充上下左右四個像素大小邊緣!右下角為原圖,左上角圖像為常量邊緣填充效果(i=0黑色)。
相關代碼實現如下:
image=cv.imread("D:/images/qxx.png"); ih,iw=image.shape[:2] border=4 #邊界填充 b1=cv.copyMakeBorder(image,border,border,border,border,cv.BORDER_CONSTANT) b2=cv.copyMakeBorder(image,border,border,border,border,cv.BORDER_REPLICATE) b3=cv.copyMakeBorder(image,border,border,border,border,cv.BORDER_REFLECT) b4=cv.copyMakeBorder(image,border,border,border,border,cv.BORDER_WRAP) b5=cv.copyMakeBorder(image,border,border,border,border,cv.BORDER_REFLECT_101) #邊界填充類型說明 cv.putText(image,"input",(20,20),cv.FONT_HERSHEY_PLAIN,1.0,(255,0,0)) cv.putText(b1,"BORDER_CONSTANT",(20,20),cv.FONT_HERSHEY_PLAIN,1.0,(255,0,0)) cv.putText(b2,"BORDER_REPLICATE",(20,20),cv.FONT_HERSHEY_PLAIN,1.0,(255,0,0)) cv.putText(b3,"BORDER_REFLECT",(20,20),cv.FONT_HERSHEY_PLAIN,1.0,(255,0,0)) cv.putText(b4,"BORDER_WRAP",(20,20),cv.FONT_HERSHEY_PLAIN,1.0,(255,0,0)) cv.putText(b5,"BORDER_REFLECT_101",(20,20),cv.FONT_HERSHEY_PLAIN,1.0,(255,0,0)) #拼接結果輸出 h=b1.shape[0]*2+8 w=b1.shape[1]*3+16 bh,bw=b1.shape[:2] result=np.zeros([h,w,3],dtype=np.uint8) result[:,:,:]=(0,0,255) result[0:bh,0:bw,:]=b1; result[0:bh,bw+8:bw+bw+8,:]=b2; result[0:bh,bw+bw+16:bw+bw+bw+16,:]=b3; result[bh+8:bh+bh+8,0:bw,:]=b4; result[bh+8:bh+bh+8,bw+8:bw+bw+8,:]=b5; result[bh+12:bh+12+ih,bw+bw+20:bw+bw+20+iw,:]=image; #顯示 cv.imshow("result",result) cv.imwrite("D:/border_result.png",result) cv.waitKey(0) cv.destroyAllWindows()
錨定位置
在進行卷積處理的時候,卷積mask與對應的像素塊點乘得到輸出,把輸出結果賦值給哪個像素點是由錨定參數anchor決定,以自定義濾波函數filter2D為例說明
voidcv::filter2D( InputArraysrc, OutputArraydst, intddepth, InputArraykernel, Pointanchor=Point(-1,-1), doubledelta=0, intborderType=BORDER_DEFAULT ) 其中 kernel - 表示輸入的自定義卷積核大小 anchor - 表示錨定點位置,默認情況Point(-1,-1)表示是卷積核的中心位置 borderType - 表示邊緣填充的像素大小,ksize/2其中ksize表示卷積核大小
上述函數在卷積核為奇數的時候,卷積核的中心位置很容易確定,比如3x3的卷積核大小,中心位置為Point(1,1),5x5的卷積核大小中心位置為Point(2,2)
但是當卷積核大小為偶數的時候,很多人都搞不清楚中心位置是如何確定的,其實這個時候中心也為(ksize/2), 對2x2的卷積核,中心位置為Point(1,1),4x4的卷積核中心位置為Point(2,2)。
錨定位置對卷積結果的影響
以2x2與4x4的卷積核為與3x3與5x5的像素數據為例
情況一
2x2卷積核對3x3的像素塊
當錨定點為默認(1,1)/(-1,-1)時候:
當錨定點設置為(0,0)時:
可以看到二者的輸出結果全然不同,原因在于當錨定點不同的時候,卷積mask的開始位置也會不不同,圖示如下:
情況二:
4x4卷積核對5x5的像素塊:
使用BORDER_DEFAULT填充方式,填充之后為:
不同錨定位置的均值卷積輸出結果:
三個不同錨定點對應卷積mask的起始位置與錨定像素輸出:
代碼演示如下:
src=np.zeros([3,3],dtype=np.uint8) src[0,0]=16 src[1,1]=8 src[2,2]=4 print(" inputimage: ",src) k1=[[1,0],[0,-1]] print(" kernel: ",k1) result=cv.copyMakeBorder(src,1,1,1,1,cv.BORDER_DEFAULT) print(" BORDER_DEFAULT邊界填充: ",result) dst=cv.filter2D(src,cv.CV_32F,np.asarray(k1),None,anchor=(0,0),borderType=cv.BORDER_DEFAULT) print(" filter2D: ",dst) print(" ") src=np.zeros([5,5],dtype=np.uint8) src[0,0]=32 src[1,1]=16 src[2,2]=8 src[3,3]=4 src[4,4]=2 print(" input: ",src) k2=np.ones([4,4],dtype=np.int32) print(" kernel: ",k2) result=cv.copyMakeBorder(src,3,3,3,3,cv.BORDER_DEFAULT) print(" 邊界填充: ",result) dst=cv.filter2D(src,cv.CV_32F,np.asarray(k2),None,anchor=(-1,-1),borderType=cv.BORDER_DEFAULT) print(" filter2DResult: ",dst)
原文標題:詳解OpenCV卷積濾波之邊緣處理與錨定輸出
文章出處:【微信公眾號:OpenCV學堂】歡迎添加關注!文章轉載請注明出處。
-
函數
+關注
關注
3文章
4338瀏覽量
62774 -
代碼
+關注
關注
30文章
4803瀏覽量
68768 -
OpenCV
+關注
關注
31文章
635瀏覽量
41399
原文標題:詳解OpenCV卷積濾波之邊緣處理與錨定輸出
文章出處:【微信號:CVSCHOOL,微信公眾號:OpenCV學堂】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論