OpenCV: Smoothing Images
類似數學上的一維卷積操作,深度學習中的多維卷積運算。
均值濾波用來模糊和平滑圖像,但是圖像失真比較大。
官方API文檔:OpenCV: Image Filtering-blur()
語法格式如下:
cv.blur(src, ksize[, dst[, anchor[, borderType]]]) ->dst
該API的卷積核的任何元素都是1,而卷積核權值就是元素個數的倒數,也即這是一個歸一化的卷積核。
因此,這個API名正言順地不愧為“均值濾波”的實現API之一。
官方API文檔:OpenCV: Image Filtering-boxFilter()
語法格式如下:
cv.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) ->dst
該API的卷積核的任何元素都是1,而卷積核權值默認是求和的倒數;只有normalize設置為False時才是一個非歸一化的卷積核。
中值濾波一般用來去除椒鹽噪聲(salt-and-pepper noise),去噪效果顯著。
官方API文檔:OpenCV: Image Filtering-medianBlur()
語法格式如下。
cv.medianBlur(src, ksize[, dst]) ->dst
中值濾波最有意思的一點,就是不涉及到卷積運算,僅僅是比較運算得到孔隙內中值;所以沒有卷積核的概念,ksize是濾波孔隙尺寸。
高斯濾波相比均值濾波,不僅去噪效果好,而且圖像失真較少。
官方API文檔:OpenCV: Image Filtering-GaussianBlur()
高斯濾波的API-GaussianBlur()的語法如下。
cv.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) ->dst
高斯濾波的卷積核不像均值濾波和方框濾波那樣元素全部相同,顧名思義——卷積核各元素共同組成高斯分布;如下圖所示,就屬於一個3*3的高斯核。
雙邊濾波器可以有效去除噪聲,同時較好地保留邊緣銳度。但是,其速度比絕大多數濾波器都慢。
API中文參考文檔:opencv之bilateralFilter()函數_bilateralfilter函數
語法格式如下。
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) ->dst
sigma參數
雙邊濾波不支持原地處理圖像,也即不支持傳入src和dst同樣的變量。
參見Sobel算子的API文檔。
sobel算子結合了高斯平滑和微分計算,主要用於邊緣檢測,其次還有去除噪點、平滑圖像。但是它檢測邊緣的位置精度不是很高。
語法格式如下。
cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]) ->dst
Sobel算子的3*3卷積核,見本文9.2小節。
為了避免負梯度時小於0被截斷為0(來自利用cv2.Sobel()計算圖像梯度的細節講解),最好將ddepth設置為CV_32F或CV_64F;sobel梯度計算完後,用一個convertScaleAbs將負像素點轉成正數並且深度轉成CV_8U,最後才用imshow()顯示;
或者,直接在讀取圖像後,對圖像對應的多維數組直接除以255,除完後數組的數據類型自動變為CV_64F,范圍在0-1之間,也即歸一化;在進行Sobel梯度運算(ddepth參數設置為-1即可)後再來一個取絕對值(不是用convertScaleAbs,而是用Python內置abs、Numpy中的abs或absolute),最後用imshow()顯示圖像,就可以規避問題;
前後兩種方法類似點在於都需在計算梯度前將圖像位深度轉成浮點型,避免負數梯度被截斷。
Scharr算子可以稱得上是對Sobel算子的優化;Scharr算子在處理邊緣時比Sobel精度高一些。
兩種算子的區別就是他們的卷積核不同;它們的計算耗時和算法復雜度相同。
官方API文檔:OpenCV: Image Filtering-Scharr()
語法格式如下
cv.Scharr(src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]]) ->dst
可以看出!
cv.Scharr(src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]])
和
cv.Sobel(src, ddepth, dx, dy[, dst[, ksize=cv2.FILTER_SCHARR[, scale[, delta[, borderType]]]]]) ->dst
完全相同!
官方API文檔:OpenCV: Image Filtering-filter2D()
語法如下:
cv.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]) ->dst
第三方參考資料:OpenCV每日函數 圖像過濾模塊 (20) sepFilter2D函數
可分離濾波的API官方文檔:OpenCV: Image Filtering-sepFilter2D()
語法格式如下:
cv.sepFilter2D(src, ddepth, kernelX, kernelY[, dst[, anchor[, delta[, borderType]]]]) ->dst
可分離濾波器算法的運算機制:先用kernelX進行卷積運算,然後再將第一次卷積運算的結果用kernelY進行卷積,最後每個元素加上delta偏差值。
可分離濾波器算法的優點:假設圖像面積為A,按照filter2D()來卷積,卷積核是K*K大小,那麼最後運算量與A*K*K成正比;而如果同一圖像按照sepFilter2D()來卷積,橫、縱卷積核長度都是K,最後運算量與2*A*K成正比;當K大於等於3時(普遍情況正如此),明顯前者運算量更大;因此可分離濾波器的運算速度很快!
(1)均值濾波的卷積核
(2)高斯模糊的卷積核
(1)垂直梯度-bottom sobel
(2)垂直梯度-top sobel
(3)水平梯度-left sobel
(4)水平梯度-right sobel
浮雕本質就是在給定方向上的梯度,比如下面這個kernel是135°上的梯度。
如果想要檢測輪廓,可以用下面的核;中間是8,鄰近的所有像素點都是-1;表示如果中間像素與周圍像素點的值相近,最後得到的取值很小,偏向於黑色。
銳化是指加大相鄰像素點之間的色差;下圖即是一個銳化卷積核,假設中央是x,而-1的位置對應y,那麼最後得到的取值是5x-4y;與相鄰像素點的差值由原來的x-y變成了5(x-y),顯然差的絕對值更大了。
一般用來檢測邊緣,當然也可用來檢測圖像中的模糊。
當卷積核的錨點元素為1,而其他元素都為0時,代表卷積得到的結果圖與原圖別無二致。
不僅可以用來生成卷積核,也可用來生成供腐蝕(erode或morphologyEx + cv.MORPH_ERODE)、膨脹(dilate或morphologyEx + cv.MORPH_DILATE)、開運算(morphologyEx + cv.MORPH_OPEN)、閉運算(morphologyEx + cv.MORPH_CLOSE)、形態學梯度(morphologyEx + cv.MORPH_GRADIENT)、頂帽運算(morphologyEx + cv.MORPH_TOPHAT)、黑帽運算(morphologyEx + cv.MORPH_BLACKHAT)所需的結構元素。
cv.getStructuringElement(shape, ksize[, anchor]) ->retval
下面給代碼例程,輔助大家對該API的理解;我們可以看到所有的點取值都是1,只不過形狀構成不同罷了!
import cv2 as cv
def filter_custom():
rect_ker = cv.getStructuringElement(cv.MORPH_RECT, (7, 7))
elli_ker = cv.getStructuringElement(cv.MORPH_ELLIPSE, (7, 7))
cross_ker = cv.getStructuringElement(cv.MORPH_CROSS, (7, 7))
print(rect_ker.dtype)
print(elli_ker.dtype)
print(cross_ker.dtype)
print(rect_ker)
print(elli_ker)
print(cross_ker)
filter_custom()
cv.waitKey(0)
cv.destroyAllWindows()
輸出如下。
uint8
uint8
uint8
[[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]]
[[0 0 0 1 0 0 0]
[0 1 1 1 1 1 0]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[0 1 1 1 1 1 0]
[0 0 0 1 0 0 0]]
[[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]
[1 1 1 1 1 1 1]
[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]]
語法格式如下。
cv.getGaussianKernel(ksize, sigma[, ktype]) ->retval
這個生成高斯核的方式,和GaussianBlur()生成的卷積核不同,這種方法得到的卷積核只能是ksize * 1的形狀。
但是,這兩種方式得到的卷積核的權重值求和都是1,因為如果不為1,往往是錯誤的。
該函數可以用來生成用於計算圖像導數的濾波器系數,
語法格式如下。
cv.getDerivKernels(dx, dy, ksize[, kx[, ky[, normalize[, ktype]]]]) ->kx, ky
比如說因為3*3的sobel算子的如下
取出第一列作為dx,那麼dx=[[-1], [0], [1]];取出第一行作為dy,也即dy=[[-1], [2], [-1]]。
這個卷積核,一般用來提取特征;特別是人臉特征識別,經常用到該函數。
該函數由兩部分組成——高斯函數(對應標准差參數sigma和均值參數theta)和正余弦函數,
參考自Gabor濾波簡介與Opencv中的實現及參數變化實驗
語法格式如下。
cv.getGaborKernel(ksize, sigma, theta, lambd, gamma[, psi[, ktype]]) ->retval