介紹
第一件事情還是先做名詞解釋,圖像平滑到底是個啥?
從字面意思理解貌似圖像平滑好像是在說圖像滑動。
emmmmmmmmmmmmmmm。。。。
其實(shí)半毛錢關(guān)系也沒有,圖像平滑技術(shù)通常也被成為圖像濾波技術(shù)(這個名字看到可能大家會有點(diǎn)感覺)。
每一幅圖像都包含某種程度的噪聲,噪聲可以理解為由一種或者多種原因造成的灰度值的隨機(jī)變化,如由光子通量的隨機(jī)性造成的噪聲等等。
而圖像平滑技術(shù)或者是圖像濾波技術(shù)就是用來處理圖像上的噪聲,其中,能夠具備邊緣保持作用的圖像平滑處理,成為了大家關(guān)注的重點(diǎn)。
這不廢話,處理個圖片降噪,結(jié)果把整個圖像搞的跟玻璃上糊上了一層水霧一樣,這種降噪有啥意義。
本文會介紹 OpenCV 中提供的圖像平滑的 4 個算法:
均值濾波
方框?yàn)V波
高斯濾波
中值濾波
下面開始一個一個看吧:)
增加噪聲
import cv2 as cv
import numpy as np
# 讀取圖片
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
rows, cols, chn = img.shape
# 加噪聲
for i in range(5000):
x = np.random.randint(0, rows)
y = np.random.randint(0, cols)
img[x, y, :] = 255
cv.imshow("noise", img)
# 圖像保存
cv.imwrite("maliao_noise.jpg", img)
# 等待顯示
cv.waitKey()
cv.destroyAllWindows()
2D 圖像卷積(圖像過濾)
在介紹濾波之前先簡單介紹下 2D 圖像卷積,圖像卷積其實(shí)就是圖像過濾。
圖像過濾的時候可以使用各種低通濾波器( LPF ),高通濾波器( HPF )等對圖像進(jìn)行過濾。
低通濾波器( LPF )有助于消除噪聲,但是會使圖像模糊。
高通濾波器( HPF )有助于在圖像中找到邊緣。
OpenCV 為我們提供了一個函數(shù) filter2D()
來將內(nèi)核與圖像進(jìn)行卷積。
我們嘗試對圖像進(jìn)行平均濾波, 5 x 5 平均濾波器內(nèi)核如下:
我們保持這個內(nèi)核在一個像素上,將所有低于這個內(nèi)核的 25 個像素相加,取其平均值,然后用新的平均值替換中心像素。它將對圖像中的所有像素繼續(xù)此操作,完整的示例代碼如下:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 讀取圖片
img = cv.imread("maliao_noise.jpg", cv.IMREAD_UNCHANGED)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
kernel = np.ones((5,5),np.float32)/25
dst = cv.filter2D(rgb_img, -1, kernel)
titles = ['Source Image', 'filter2D Image']
images = [rgb_img, dst]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
均值濾波
均值濾波是指任意一點(diǎn)的像素值,都是周圍 N * M 個像素值的均值。
其實(shí)均值濾波和上面的那個圖像卷積的示例,做了同樣的事情,我只是用 filter2D() 這個方法手動完成了均值濾波,實(shí)際上 OpenCV 為我們提供了專門的均值濾波的方法,前面圖像卷積沒有看明白的同學(xué),可以再一遍均值濾波,我盡量把這個事情整的明白的。
還是來畫個圖吧:
中間那個紅色的方框里面的值,是周圍 25 個格子區(qū)域中的像素的和去除以 25 ,這個公式是下面這樣的:
我為了偷懶,所有的格子里面的像素值都寫成 1 ,畢竟 n / n 永遠(yuǎn)都等于 1 ,快夸我機(jī)智。
上面這個 5 * 5 的矩陣稱為核,針對原始圖像內(nèi)的像素點(diǎn),采用核進(jìn)行處理,得到結(jié)果圖像。
這個核我們可以自定義大小,比如 5 * 5 ,3 * 3 , 10 * 10 等等,具體定義多大完全看療效。
OpenCV 為我提供了 blur()
方法用作實(shí)現(xiàn)均值濾波,原函數(shù)如下:
def blur(src, ksize, dst=None, anchor=None, borderType=None)
- kSize: 內(nèi)核參數(shù),其實(shí)就是圖片進(jìn)行卷積的時候相乘的那個矩陣,具體的卷積是如何算的,網(wǎng)上有很多,我這里就不介紹了,所得到的圖像是模糊的,而且圖像其實(shí)是按照原來的比例缺少了(原圖像-內(nèi)核參數(shù)+1)^2 個單元格。
- anchor: Point 類型,即錨點(diǎn),有默認(rèn)值 Point(-1, -1) ,當(dāng)坐標(biāo)為負(fù)值,就表示取核的中心。
- borderType: Int 類型,用于推斷圖像外部像素的某種邊界模式,有默認(rèn)值 BORDER_DEFAULT 。
示例
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取圖片
img = cv.imread("maliao_noise.jpg", cv.IMREAD_UNCHANGED)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 均值濾波
blur_img = cv.blur(rgb_img, (3, 3))
# blur_img = cv.blur(img, (5, 5))
# blur_img = cv.blur(img, (10, 10))
# blur_img = cv.blur(img, (20, 20))
titles = ['Source Image', 'Blur Image']
images = [rgb_img, blur_img]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
這個降噪的效果好像沒有前面 2D 卷積的那個降噪效果好,但是圖像更為清晰,因?yàn)槲以谶@個示例中使用了更小的核 3 * 3 的核,順便我也試了下大核,比如代碼中注釋掉的 10 * 10 的核或者 20 * 20 的核,實(shí)時證明,核越大降噪效果越好,但是相反的是圖像會越模糊。
方框?yàn)V波
方框?yàn)V波和均值濾波核基本一致,其中的區(qū)別是需不需要進(jìn)行歸一化處理。
什么是歸一化處理等下再說,我們先看方框?yàn)V波的原函數(shù):
def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None)
- src: 原始圖像。
- ddepth: Int 類型,目標(biāo)圖像深度,通常用 -1 表示與原始圖像一致。
- kSize: 內(nèi)核參數(shù)。
- dst: 輸出與 src 大小和類型相同的圖像。
- anchor: Point 類型,即錨點(diǎn),有默認(rèn)值 Point(-1, -1) 。
- normalize: Int 類型,表示是否對目標(biāo)圖像進(jìn)行歸一化處理。
當(dāng) normalize 為 true 時,需要執(zhí)行均值化處理。
當(dāng) normalize 為 false 時,不進(jìn)行均值化處理,實(shí)際上是求周圍各像素的和,很容易發(fā)生溢出,溢出時均為白色,對應(yīng)像素值為 255 。
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取圖片
img = cv.imread('maliao_noise.jpg')
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 方框?yàn)V波
result = cv.boxFilter(source, -1, (5, 5), normalize = 1)
# 顯示圖形
titles = ['Source Image', 'BoxFilter Image']
images = [source, result]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
當(dāng)我們把 normalize 的屬性設(shè)為 0 時,不進(jìn)行歸一化處理,結(jié)果就變成了下面這個樣子:
高斯濾波
為了克服簡單局部平均法的弊端(圖像模糊),目前已提出許多保持邊緣、細(xì)節(jié)的局部平滑算法。它們的出發(fā)點(diǎn)都集中在如何選擇鄰域的大小、形狀和方向、參數(shù)加平均及鄰域各店的權(quán)重系數(shù)等。
在高斯濾波的方法中,實(shí)際上是把卷積核換成了高斯核,那么什么是高斯核呢?
簡單來講就是方框還是那個方框,原來每個方框里面的權(quán)是相等的,大家最后取平均,現(xiàn)在變成了高斯分布的,方框中心的那個權(quán)值最大,其余方框根據(jù)距離中心元素的距離遞減,構(gòu)成一個高斯小山包,這樣取到的值就變成了加權(quán)平均。
下圖是所示的是 3 * 3 和 5 * 5 領(lǐng)域的高斯核。
高斯濾波是在 OpenCV 中是由 GaussianBlur()
方法進(jìn)行實(shí)現(xiàn)的,它的原函數(shù)如下:
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
- sigmaX: 表示 X 方向方差。
這里需要注意的是 ksize 核大小,在高斯核當(dāng)中,核 (N, N) 必須是奇數(shù), X 方向方差主要控制權(quán)重。
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取圖片
img = cv.imread('maliao_noise.jpg')
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 方框?yàn)V波
result = cv.GaussianBlur(source, (3, 3), 0)
# 顯示圖形
titles = ['Source Image', 'GaussianBlur Image']
images = [source, result]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
中值濾波
在使用鄰域平均法去噪的同時也使得邊界變得模糊。
而中值濾波是非線性的圖像處理方法,在去噪的同時可以兼顧到邊界信息的保留。
中值濾波具體的做法是選一個含有奇數(shù)點(diǎn)的窗口 W ,將這個窗口在圖像上掃描,把窗口中所含的像素點(diǎn)按灰度級的升或降序排列,取位于中間的灰度值來代替該點(diǎn)的灰度值。
下圖是一個一維的窗口的濾波過程:
在 OpenCV 中,主要是通過調(diào)用 medianBlur()
來實(shí)現(xiàn)中值濾波,它的原函數(shù)如下:
def medianBlur(src, ksize, dst=None)
中值濾波的核心數(shù)和高斯濾波的核心數(shù)一樣,必須要是大于 1 的奇數(shù)。
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取圖片
img = cv.imread('maliao_noise.jpg')
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 方框?yàn)V波
result = cv.medianBlur(source, 3)
# 顯示圖形
titles = ['Source Image', 'medianBlur Image']
images = [source, result]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
可以明顯看到,目前中值濾波是對原圖像降噪后還原度最高的,常用的中值濾波的圖形除了可以使用方框,還有十字形、圓形和環(huán)形,不同形狀的窗口產(chǎn)生不同的濾波效果。
雙邊濾波
import cv2 as cv
source = cv.imread("zhaopian.jpg")
dst = cv.bilateralFilter(src=source, d=0, sigmaColor=30, sigmaSpace=15)
cv.imshow("source", source)
cv.imshow("dst", dst)
cv.waitKey()
cv.destroyAllWindows()
原理解析
上面的圖片美顏效果其實(shí)使用的是 OpenCV 中為我們提供的雙邊濾波器,是一種圖片降噪算法。
前面的文章介紹過,高斯濾波的方式會造成邊緣模糊化,這是沒辦法的事情,這是高斯濾波過濾方式而導(dǎo)致的。
而雙邊濾波就是在高斯濾波的基礎(chǔ)上,對高斯濾波的方式加以改進(jìn),結(jié)合圖像的空間鄰近度和像素值相似度的一種折處理,同時考慮 空域信息(domain) 和 值域信息(range) ,達(dá)到保邊降噪的目的。
說人話就是雙邊濾波在進(jìn)行濾波的過程中,不光要考慮周圍像素值與中點(diǎn)像素值的大小之差,還需要考慮空間上的距離,進(jìn)而確定該點(diǎn)對中間點(diǎn)的影響因子。
比如在一張圖像中,相鄰的像素點(diǎn)的顏色會非常相近,但是如果在邊緣區(qū)域,相鄰元素點(diǎn)的顏色變化會非常的大。
高斯過濾器的過濾過程中就是因?yàn)闆]有考慮邊緣區(qū)域而導(dǎo)致過濾后圖像邊緣模糊,而雙邊濾波由于在過濾的過程中考慮到了周圍像素值與中點(diǎn)像素值的差值大小,從而會確定一個影響因子,從而實(shí)現(xiàn)圖片的保邊降噪。
具體的實(shí)現(xiàn)原理如下:
從這個圖中可以看出,在圖(b)空域核上,每個像素的權(quán)重是符合高斯分布的,而在圖(c)的值域核上,由于像素取值相差過大,不同顏色的權(quán)重系數(shù)相差也很大,雙邊過濾過濾完成后,邊緣兩側(cè)的像素點(diǎn)保留了原有的色彩值。文章來源:http://www.zghlxwxcb.cn/news/detail-441768.html
接下來還是看下雙邊濾波的原函數(shù):文章來源地址http://www.zghlxwxcb.cn/news/detail-441768.html
def bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None)
- src:原圖
- d:像素鄰域的直徑。如果這個值設(shè)為非正數(shù),那么 OpenCV 會從第五個參數(shù) sigmaSpace 來將它計算出來。
- sigmaColor:顏色空間濾波器的 值。這個參數(shù)的值越大,就表明該像素鄰域內(nèi)有更寬廣的顏色會被混合到一起,產(chǎn)生較大的半相等顏色區(qū)域。
- sigmaSpace:坐標(biāo)空間中濾波器的 值,坐標(biāo)空間的標(biāo)注方差。他的數(shù)值越大,意味著越遠(yuǎn)的像素會相互影響,從而使更大的區(qū)域足夠相似的顏色獲取相同的顏色。當(dāng) d > 0 , d 指定了鄰域大小且與 sigmaSpace 無關(guān)。否則, d 正比于 sigmaSpace 。使用過程中我發(fā)現(xiàn)這個值越大,圖像的過渡效果越好。
到了這里,關(guān)于python --opencv圖像處理濾波詳解(均值濾波、2D 圖像卷積、方框?yàn)V波、 高斯濾波、中值濾波、雙邊濾波)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!