OpenCV-Python教程:平滑處理–均值平滑、中值平滑(blur,medianBlur)


返回Opencv-Python教程

圖像在生成、傳輸或存儲過程中可能因為外界干擾產生噪聲,從而使圖像在視覺上表現為出現一些孤立點或者像素值突然變化的點,圖像平滑處理的目的就是為了消除圖像中的這類噪聲。

在講平滑處理前,先來了解下在OpenCV中平滑處理用到的“滑動窗口”的概念,下面的這個例子中選擇了一個ksize=3x3的滑動窗口(或稱濾波器模板、kernel),如黃色部分所示。用這個ksize=3x3的窗口作用於原始圖像上的每一個像素,如下圖的綠色部分所示,被這個窗口覆蓋的9個像素點都參與計算,這樣在該像素點上就會得到一個新的像素值,當窗口沿着圖像逐個像素進行計算,就會得到一幅新的圖像。

上圖中濾波器模板的不同就構成了濾波算法的差異,比如均值平滑算法中滑動窗口中各個像素點的系數均為1/(窗口高*窗口寬),高斯平滑中系數和中心點的距離滿足高斯分布。

從上圖也可以看到,當滑動窗口作用於圖像邊沿的時候,滑動窗口的某些像素並沒有和圖像重合,這時就需要對邊沿做特殊處理,常用的方法有填0、填1、復制邊沿等方式。

1、均值平滑blur()

均值平滑的滑動窗口所有系數為1/(窗口高*窗口寬),新生成的像素值就是窗口中心點以及周圍所有像素值相加后的平均值。比如選擇一個ksize=5x5的窗口,新圖像的(x,y)點的像素值用numpy表示為 np.sum(i[x-2:i+3,y-2:y+3])/(5*5)。

blur()的接口形式:

dst=cv2.blur(src, ksize[, dst[, anchor[, borderType]]])
  • 參數含義:
  • src:源圖像,通道數不限,數據類型必須為CV_8U, CV_16U, CV_16S, CV_32F or CV_64F;
  • ksize:kernel尺寸、窗口大小,二元組類型,元素值可以是偶數或奇數;
  • anchor:錨點,默認為(-1,-1),作用於滑動窗口的中心點;
  • borderType:邊界處理類型;

下面這個例子是使用不同ksize進行均值平滑的對比:

import matplotlib.pyplot as plt 
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')

img = cv2.imread('..\\lena.jpg')
img_ret1 = cv2.blur(img,(3,3))
img_ret2 = cv2.blur(img,(5,5))
img_ret3 = cv2.blur(img,(11,11))

#顯示圖像
fig,ax = plt.subplots(2,2)
ax[0,0].set_title('VX:桔子code  原圖')
ax[0,0].imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)) #matplotlib顯示圖像為rgb格式
ax[0,1].set_title('blur ksize=3')
ax[0,1].imshow(cv2.cvtColor(img_ret1,cv2.COLOR_BGR2RGB))
ax[1,0].set_title('blur ksize=5')
ax[1,0].imshow(cv2.cvtColor(img_ret2,cv2.COLOR_BGR2RGB))
ax[1,1].set_title('blur ksize=11') 
ax[1,1].imshow(cv2.cvtColor(img_ret3,cv2.COLOR_BGR2RGB))
ax[0,0].axis('off');ax[0,1].axis('off');ax[1,0].axis('off');ax[1,1].axis('off')#關閉坐標軸顯示
plt.show() 

運行結果:

從運行效果可以看到,ksize越大,圖像越模糊,清晰度越低。

通過觀察前面經過處理后的圖像能得到比較直觀的感受,下面將從數值角度驗證下定量的結果。下面這個例子中設置ksize=(5,5),提取新圖像坐標x,y = 10,10處的像素值,並提取原圖像的x,y=10,10為中心高寬為5的子圖,計算子圖的平均值:

import numpy as np 
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
 
img = cv2.imread('..\\lena.jpg')
img_ret1 = cv2.blur(img,(5,5))
x,y = 10,10
b,g,r = cv2.split(img[x-2:x+3,y-2:y+3])#分離出原圖在x,y點的bgr通道
print('img b:\n',b )
print('img g:\n',g )
print('img r:\n',r )
print('img average(b,g,r):',np.sum(b)/25,np.sum(g)/25,np.sum(r)/25)#分別計算每個通道的平均值
print('img_ret1[x,y]',img_ret1[x,y])

 運行結果:

cv2.__version__: 4.5.2
img b:
 [[124 113 113 113 114]
 [117 111 111 110 108]
 [112 110 111 108 106]
 [114 112 111 110 106]
 [112 108 106 109 105]]
img g:
 [[142 131 133 133 136]
 [135 129 131 130 133]
 [132 130 133 130 131]
 [134 132 133 132 131]
 [134 130 131 134 130]]
img r:
 [[235 224 228 228 231]
 [228 222 226 225 227]
 [227 225 228 225 225]
 [229 227 228 227 225]
 [229 225 225 228 224]]
img average(b,g,r): 110.96 132.4 226.84
img_ret1[x,y] [111 132 227]

 從運行結果可以看到新圖像img_ret1像素點x,y=10,10的像素值為[111,132,227]等於原始圖像的平均值:110.96,132.4,226.84取整后的結果。

2、中值平滑medianBlur()

中值平滑和均值平滑一樣也采用滑動窗口的方式,但是它並不是計算滑動窗口中的某種加權和,而是使用原圖像滑動窗口中所有像素值排序后的中值。

medianBlur的接口形式如下:

dst=cv2.medianBlur(src, ksize[, dst])
  • 參數含義:
  • src:源圖像,通道數可以是1,3或4,當ksize為3或者5時,數據類型可以是CV_8U, CV_16U, CV_32F,當使用更大的ksize時,數據類型只能是CV_8U;
  • ksize:kernel尺寸、窗口大小,整數型,大於1的奇數值;

下面這個例子是使用不同ksize進行中值平滑的對比:

import matplotlib.pyplot as plt 
import cv2

print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')

img = cv2.imread('..\\lena.jpg')
img_ret1 = cv2.medianBlur(img,3)
img_ret2 = cv2.medianBlur(img,5)
img_ret3 = cv2.medianBlur(img,11)

#顯示圖像
fig,ax = plt.subplots(2,2)
ax[0,0].set_title('VX:桔子code  原圖')
ax[0,0].imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)) #matplotlib顯示圖像為rgb格式
ax[0,1].set_title('medianBlur ksize=3')
ax[0,1].imshow(cv2.cvtColor(img_ret1,cv2.COLOR_BGR2RGB))
ax[1,0].set_title('medianBlur ksize=5')
ax[1,0].imshow(cv2.cvtColor(img_ret2,cv2.COLOR_BGR2RGB))
ax[1,1].set_title('medianBlur ksize=11') 
ax[1,1].imshow(cv2.cvtColor(img_ret3,cv2.COLOR_BGR2RGB))
ax[0,0].axis('off');ax[0,1].axis('off');ax[1,0].axis('off');ax[1,1].axis('off')#關閉坐標軸顯示
plt.show() 

運行結果:

下面仍然從數值角度看下中值平滑的處理,ksize=(5,5),提取新圖像坐標x,y = 10,10處的像素值,並提取原圖像的x,y=10,10為中心高寬為5的子圖,再計算子圖像素值展開成list后的中值:

import numpy as np
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
 
img = cv2.imread('..\\lena.jpg')
img_ret1 = cv2.medianBlur(img,5)

x,y = 10,10
b,g,r = cv2.split(img[x-2:x+3,y-2:y+3])#分離出原圖在x,y點的bgr通道
print('img b:\n',b )
print('img g:\n',g )
print('img r:\n',r )
list_b = list(b.flatten())     #展開得到一個list
list_b.sort()                  #list排序
list_g = list(g.flatten())
list_g.sort()
list_r = list(r.flatten())
list_r.sort()
print('list_b:',list_b )
print('list_g:',list_g )
print('list_r:',list_r )
print('list_b[12]:',list_b[12])#提取中值
print('list_g[12]:',list_g[12])
print('list_r[12]:',list_r[12])
print('img_ret1[x,y]',img_ret1[x,y])#新圖像像素值

運行結果:

list_b: [105, 106, 106, 106, 108, 108, 108, 109, 110, 110, 110, 111, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 117, 124]
list_g: [129, 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 132, 132, 132, 133, 133, 133, 133, 133, 134, 134, 134, 135, 136, 142]
list_r: [222, 224, 224, 225, 225, 225, 225, 225, 225, 225, 226, 227, 227, 227, 227, 228, 228, 228, 228, 228, 228, 229, 229, 231, 235]
list_b[12]: 111 #中值
list_g[12]: 132
list_r[12]: 227
img_ret1[x,y] [111 132 227]

從上面的運行結果可以看到新圖像在坐標x,y = 10,10處的像素值為[111 132 227],等於該處滑動窗口內像素值的中值。

3、像素值對比

通過前面的介紹我們可以看到平滑處理后圖像會變得更“模糊”,這是因為不管均值也好,中值也罷,都會降低圖像變化的程度,下面我們從另外一個角度來驗證下變化效果。

因為圖像在水平和垂直方向都發生了“平滑”,為了方便觀察下面我們僅以水平方向為例,以圖像的水平方向坐標作為繪圖的X軸,在上面這個lena圖片中,X軸的取值范圍就是0~511(圖像寬度為512個像素),提取第10行的像素值作為繪圖的Y軸,因為是uint8(CV_8U)類型的數據,Y軸分布在0~255。分別繪制原圖、均值濾波和中值濾波后的像素值的曲線:

import numpy as np
import matplotlib.pyplot as plt 
import cv2

print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')

img = cv2.imread('..\\lena.jpg')
img_ret1 = cv2.blur(img,(15,15)) 
img_ret2 = cv2.medianBlur(img,15)
 
X = np.arange(img.shape[1])  
Y = img[:,:,0][10,:]         #僅提取b通道第10行
Y1 = img_ret1[:,:,0][10,:]   #提取變化后圖像的第10行
Y2 = img_ret2[:,:,0][10,:]
plt.plot(X,Y,'-g',label='raw' ) #繪圖
plt.plot(X,Y1,'-r',label='blur')
plt.plot(X,Y2,'-b',label='medianBlur')
plt.legend(title='img type(juzicode.com)',fontsize='xx-large',loc='upper center')
plt.show()

運行結果:

從上面的對比可以看到綠色的原始圖像,其像素值變化的非常“劇烈”,有很多波峰或波谷,但是經過平滑處理后的像素值則顯得平滑的多。

小結:平滑處理是圖像濾波的一種,可以看做是低通濾波,它會消除圖像的高頻“信號”,讓圖像看起來更模糊、平滑,通過將變化前后的圖像像素值繪制曲線可以更形象地觀察到這種平滑效果。

 原文鏈接:http://www.juzicode.com/opencv-python-blur-medianblur

擴展閱讀:

  1. OpenCV-Python教程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM