圖像平滑從信號處理的角度看就是去除其中的高頻信息,保留低頻信息。因此我們可以對圖像實施低通濾波。低通濾波可以去除圖像中的噪音,模糊圖像(噪音是圖像中變化比較大的區域,也就是高頻信息)。而高通濾波能夠提取圖像的邊緣(邊緣也是高頻信息集中的區域)。
根據濾波器的不同又可以分為均值濾波,高斯加權濾波,中值濾波, 雙邊濾波。
均值濾波
平均濾波是將一個m*n(m, n為奇數)大小的kernel放在圖像上,中間像素的值用kernel覆蓋區域的像素平均值替代。平均濾波對高斯噪聲的表現比較好,對椒鹽噪聲的表現比較差。
$$ g(x,y) = \frac{1}{mn}\sum_{(x,y) \in S_{xy}} f(s,t)$$
其中$S_{xy} 表示中心點在(x, y) 大小為m X n 濾波器窗口。
當濾波器模板的所有系數都相等時,也稱為盒狀濾波器。BoxFilter , BoxFilter可以用來計算圖像像素鄰域的和。
cv2.boxFilter() normalize=False, 此時不使用歸一化卷積窗,當前計算像素值為鄰域像素和。
加權均值濾波器不同於上面均值濾波器的所有像素系數都是相同的,加權的均值濾波器使用的模板系數,會根據像素和窗口中心的像素距離取不同的系數。比如距離中心像素的距離越近,系數越大。
$$\frac{1}{16}
\left [
\begin{array}{ccc}
1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1
\end{array}
\right ]
$$
一般的作用於M*N大小的圖像,窗口大小為m*n的加權平均濾波器計算公式為:
$$g(x, y) = \frac{\sum_{s = -a}^a \sum_{t = -b}^b w(s, t) f(x+s, y+t)}{\sum_{s = -a}^a \sum_{t = -b}^b w(s, t)}$$
高斯加權濾波器
高斯函數是一種正態分布函數,一個二維高斯函數如下:
$$hh(x, y) = \frac{1}{2\pi \sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}$$
$\sigma$為標准差, 如果要得到一個高斯濾波器模板可以對高斯函數進行離散化,得到離散值作為模板系數。簡單來說就是以模板中心位置為坐標原點進行取樣。
高斯加權平均中,最重要是$\sigma$的選取,標准差代表數據離散程度,如果$\sigma$小那么,高斯分布中心區域更加聚集,平滑效果越差;反之,則更離散,平滑效果越明顯。
中值濾波
前面提到的都是線性平滑濾波器(中間像素值是由鄰域像素值線性加權得到),而中值濾波器,使用濾波器窗口包含區域的像素值的中值來得到窗口中心的像素值,本質上是一種非線性平滑濾波器。中值濾波器對於去除impulse noise或者salt-and-pepper noise非常有效, 對於gaussian noise的表現則比較差。
import cv2 import numpy as np from skimage.util import random_noise from matplotlib import pyplot as plt import skimage.io from skimage import img_as_ubyte """ Created on 2018.07.02 @author: vincent """ def salt(img, n): for k in range(n): i = int(np.random.random()*img.shape[1]) j = int(np.random.random()*img.shape[0]) if img.ndim==2: img[j.i] = 0; elif img.ndim==3: img[j,i,0] = 255 img[j,i,1] = 255 img[j,i,2] = 255 def pepper(img, n): for k in range(n): i = int(np.random.random()*img.shape[1]) j = int(np.random.random()*img.shape[0]) if img.ndim==2: img[j.i] = 0; elif img.ndim==3: img[j,i,0] = 0 img[j,i,1] = 0 img[j,i,2] = 0 if __name__ == "__main__": img = skimage.io.imread('/home/vincent/Pictures/work/lena.png') sigma = 0.1 gaussian_img = random_noise(img, var = sigma**2) salt_img = random_noise(img, mode = 'salt') pepper_img = random_noise(img, mode = 'pepper') salt_pepper_img = random_noise(img, mode = 's&p') poisson_img = random_noise(img, mode='poisson') images = [img, gaussian_img, salt_img, pepper_img, salt_pepper_img, poisson_img] titles = ['origin', 'gaussian_noise', 'salt_noise', 'pepper_noise', 'salt_pepper_noise', 'poisson_noise'] for i in range(6): plt.subplot(2,3, i+1) plt.title(titles[i]) plt.imshow(images[i]) # denoise on gaussian noise plt.figure() #gaussian_img = cv2.cvtColor(gaussian_img.astype(np.uint8), cv2.COLOR_RGB2BGR) #gaussian_img = gaussian_img[:,:,::-1] gaussian_img = img_as_ubyte(gaussian_img) print(gaussian_img.shape, gaussian_img.dtype) mean_filter = cv2.blur(gaussian_img, (5,5)) median_filter = cv2.medianBlur(gaussian_img, 5) images = [gaussian_img, mean_filter, median_filter] titles = ['gaussian_img', 'mean_filter', 'median_filter'] for i in range(3): plt.subplot(1,3,i+1) plt.title(titles[i]) plt.imshow(images[i]) # denoise on salt and pepper noise plt.figure() salt_pepper_img = img_as_ubyte(salt_pepper_img) #salt_pepper_img = cv2.cvtColor(salt_pepper_img.astype(np.uint8), cv2.COLOR_RGB2BGR) mean_filter = cv2.blur(salt_pepper_img, (5,5)) median_filter = cv2.medianBlur(salt_pepper_img, 5) images = [salt_pepper_img, mean_filter, median_filter] titles = ['salt_pepper_img', 'mean_filter', 'median_filter'] for i in range(3): plt.subplot(1,3,i+1) plt.title(titles[i]) plt.imshow(images[i]) plt.show() plt.close('all')
雙邊濾波
加權均值濾波和高斯濾波是求中心點區域鄰近區域像素的高斯加權平均值,只考慮了像素之間的空間關系,而沒有考慮像素值之間的關系。而邊界處的灰度值變化比較大,因此這兩種方法也會把邊界模糊掉。而雙邊濾波會同時使用空間高斯權重和灰度值相似性高斯權重,確保邊界處不會被模糊掉。
import cv2 import matplotlib.pyplot as plt img = cv2.imread('/home/vincent/Pictures/work/lena.png') gaussian_img = cv2.GaussianBlur(img, (5,5), 75) bilateral_img = cv2.bilateralFilter(img, 5, 75, 75) images = [img, gaussian_img, bilateral_img] titles = ['origin image', 'gaussian blur', 'bilateral blur'] for i in range(3): plt.subplot(1,3,i+1) plt.imshow(cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB)) plt.title(titles[i]) plt.show() plt.close('all')
由上圖可以看出雙邊濾波能夠較好的保留圖像的邊緣信息。