詳解OpenCV卷積濾波之邊緣處理與錨定輸出
點(diǎn)擊上方“小白學(xué)視覺”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時間送達(dá)
本文轉(zhuǎn)自|OpenCV學(xué)堂
OpenCV在使用卷積進(jìn)行圖像處理過程種,如何處理邊緣像素與錨定輸出兩個技術(shù)細(xì)節(jié)一直是很多人求而不得的疑惑。其實OpenCV在做卷積濾波時會對圖像進(jìn)行邊界填充,實現(xiàn)對邊緣像素的卷積計算的支持,不同填充方式與不同錨定點(diǎn)會得到圖像卷積輸出不同的結(jié)果。
我們首先來看一下OpenCV種支持標(biāo)準(zhǔn)卷積邊緣填充做法,OpenCV支持的有如下幾種卷積邊緣填充算法:
常量邊界
BORDER_CONSTANT
iiiiii|abcdefgh|iiiiiii
邊界復(fù)制
BORDER_REPLICATE
aaaaaa|abcdefgh|hhhhhhh
邊界反射
BORDER_REFLECT
fedcba|abcdefgh|hgfedcb
邊界換行
BORDER_WRAP
cdefgh|abcdefgh|abcdefg
邊界反射101
BORDER_REFLECT_101
gfedcb|abcdefgh|gfedcba
邊界透明-很不幸運(yùn)的是OpenCV4已經(jīng)不支持啦!
BORDER_TRANSPARENT
uvwxyz|abcdefgh|ijklmno
默認(rèn)填充方式
OpenCV中 filter2D, blur, GaussianBlur等卷積操作默認(rèn)支持為BORDER_DEFAULT(BORDER_REFLECT_101)
各種不同方式對邊緣的填充效果如下:

上圖背景為紅色,填充上下左右四個像素大小邊緣!右下角為原圖,左上角圖像為常量邊緣填充效果(i=0黑色)。
相關(guān)代碼實現(xiàn)如下:
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))
# 拼接結(jié)果輸出
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()在進(jìn)行卷積處理的時候,卷積mask與對應(yīng)的像素塊點(diǎn)乘得到輸出,把輸出結(jié)果賦值給哪個像素點(diǎn)是由錨定參數(shù)anchor決定,以自定義濾波函數(shù)filter2D為例說明
void cv::filter2D(
InputArray src,
OutputArray dst,
int ddepth,
InputArray kernel,
Point anchor = Point(-1,-1),
double delta = 0,
int borderType = BORDER_DEFAULT
)
其中
kernel - 表示輸入的自定義卷積核大小
anchor - 表示錨定點(diǎn)位置,默認(rèn)情況Point(-1, -1)表示是卷積核的中心位置
borderType - 表示邊緣填充的像素大小,ksize/2其中ksize表示卷積核大小上述函數(shù)在卷積核為奇數(shù)的時候,卷積核的中心位置很容易確定,比如3x3的卷積核大小,中心位置為Point(1,1),5x5的卷積核大小中心位置為Point(2,2)
但是當(dāng)卷積核大小為偶數(shù)的時候,很多人都搞不清楚中心位置是如何確定的,其實這個時候中心也為(ksize/2), 對2x2的卷積核,中心位置為Point(1,1),4x4的卷積核中心位置為Point(2,2)。
以2x2與4x4的卷積核為與3x3與5x5的像素數(shù)據(jù)為例
情況一
2x2卷積核對3x3的像素塊


當(dāng)錨定點(diǎn)為默認(rèn)(1,1)/(-1,-1)時候:

當(dāng)錨定點(diǎn)設(shè)置為(0,0)時:

可以看到二者的輸出結(jié)果全然不同,原因在于當(dāng)錨定點(diǎn)不同的時候,卷積mask的開始位置也會不不同,圖示如下:

情況二:
4x4卷積核對5x5的像素塊:

使用BORDER_DEFAULT填充方式,填充之后為:

不同錨定位置的均值卷積輸出結(jié)果:

三個不同錨定點(diǎn)對應(yīng)卷積mask的起始位置與錨定像素輸出:

代碼演示如下:
src = np.zeros([3, 3], dtype=np.uint8)
src[0, 0] = 16
src[1, 1] = 8
src[2, 2] = 4
print("\n input image: \n",src)
k1 = [[1, 0], [0, -1]]
print("\nkernel : \n", k1)
result = cv.copyMakeBorder(src, 1, 1, 1, 1, cv.BORDER_DEFAULT)
print("\nBORDER_DEFAULT 邊界填充 : \n", result)
dst = cv.filter2D(src, cv.CV_32F, np.asarray(k1), None, anchor=(0, 0), borderType=cv.BORDER_DEFAULT)
print("\nfilter2D : \n", dst)
print("\n")
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("\ninput: \n", src)
k2 = np.ones([4, 4], dtype=np.int32)
print("\nkernel:\n", k2)
result = cv.copyMakeBorder(src, 3, 3, 3, 3, cv.BORDER_DEFAULT)
print("\n邊界填充:\n", result)
dst = cv.filter2D(src, cv.CV_32F, np.asarray(k2), None, anchor=(-1, -1), borderType=cv.BORDER_DEFAULT)
print("\n filter2D Result: \n", dst)交流群
歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學(xué)影像、GAN、算法競賽等微信群(以后會逐漸細(xì)分),請掃描下面微信號加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進(jìn)入相關(guān)微信群。請勿在群內(nèi)發(fā)送廣告,否則會請出群,謝謝理解~

