圖像模糊,也可以稱為圖像濾波,主要是為了去除圖像中明顯的噪聲點;
這一節主要介紹兩種濾波方式: 均值濾波和高斯濾波;
重點介紹一下兩者的原理,並使用OpenCV提供的API進行測試;
卷積計算
其實,不管是均值濾波,還是高斯濾波,其核心計算是卷積操作;
計算方式如下圖所示,通過一個卷積核在圖像上以滑動窗口的形式進行計算:
相應位置元素相乘后,累加,再取平均;每一次卷積計算的表達式如下:
其中,\(k ,l\)表示卷積核的尺寸;\(h\)表示卷積核;
因此,卷積計算的核心是卷積核的選擇;另外,卷積核的尺寸最好是奇數,因為需要對局部視野中心進行賦值;
均值濾波
均值濾波的卷積核如下所示:
均值濾波的卷積核中所有的元素大小相同,且經過歸一化;
OpenCV中均值濾波API blur
使用的介紹:
void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
src
表示需要被執行均值濾波處理的圖像dst
表示濾波后的圖像ksize
表示卷積核的尺寸- 其他保持默認即可;
舉例, blur
的使用(完整代碼在文章最后):
Mat dst;
blur(src, dst, Size(3, 3), Point(-1, -1)); // filter2D
高斯濾波
高斯濾波的卷積核不同於均值濾波;
高斯濾波卷積核是通過高斯函數計算出來的,計算方式如下:
將各個位置的坐標帶入到高斯函數中,計算得到卷積核,其位置坐標如下所示:
假設,卷積核尺寸選擇\(3\times3\),\(\sigma=0.8\),則計算得到的卷積核為:
上面卷積核的計算方式如下:
- 將位置坐標代碼高斯函數中,計算得到每個位置的結果;
- 將所有位置的值除以所有位置的總和;
這里提供一下python實現的計算方式:
import math
import numpy as np
sigma = 0.8;
def calc_val(x, y, sigma):
val = (1/(2*math.pi*sigma**2))*(math.e**(-(x**2+y**2)/(2*sigma**2)))
return val
a_11 = calc_val(-1, 1, sigma)
a_12 = calc_val(0, 1, sigma)
a_13 = calc_val(1, 1, sigma)
a_21 = calc_val(-1, 0, sigma)
a_22 = calc_val(0, 0, sigma)
a_23 = calc_val(1, 0, sigma)
a_31 = calc_val(-1, -1, sigma)
a_32 = calc_val(0, -1, sigma)
a_33 = calc_val(1, -1, sigma)
res = np.array([[a_11, a_12, a_13],
[a_21, a_22, a_23],
[a_31, a_32, a_33]])
res = res/np.sum(res)
print(res)
那么,由高斯函數計算得到卷積核之后,有兩種使用方式:
1. 小數卷積核
小數卷積核指的是直接使用計算得到卷積核用於后續卷積計算;
2. 整數卷積核
整數卷積核指的是將卷積核進行歸一化處理;
- 首先,先將左上角縮放到1,其他位置上的值再乘以這個縮放系數;
得到:
計算方式,python
實現如下:
# 歸一化, 代碼接着上面的來
scale = 1 / a_11
res = res * scale
print(res)
- 其次, 對計算后的卷積核,取整,再除以和,如下;
從以上的描述過程可以看出,高斯卷積核最重要的參數是高斯分布的標准差\(\sigma\),它代表了數據的離散程度;
- 如果\(\sigma\)較小,得到的高斯核中心系數就越大,周圍系數越小,則對圖像的平滑效果不好;
- 如果\(\sigma\)較大,則高斯核中的各個系數相差不大,則對圖像的平滑效果較好;
上面介紹了,如何得到高斯核,那么接下來,看一下OpenCV中提供的GaussianBlur
的使用:
void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
src
表示需要模糊的圖像;dst
表示模糊的輸出圖像;ksize
表示卷積核的尺寸;sigmaX
表示在X方向的標准偏差;sigmaY
表示在Y方向上的標准偏差;
需要注意的是,如果sigmaY=0
,則將其設為sigmaX
;如果兩者都為0,則兩者由卷積核的尺寸進行計算:
注意:這里提到的sigmaX, sigmaY
如何與上面敘述的高級核的構造過程如何產生關系,還不得知,以后弄明白了再補充;
示例:
Mat dst_gaussian;
int sigma = 3;
GaussianBlur(src, dst_gaussian, Size(3, 3), sigma, sigma);
最后,完整的程序如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(){
// 讀取圖像
Mat src = imread("/home/chen/dataset/lena.jpg");
if (src.empty()){
cout << "cloud not load image." << endl;
return -1;
}
// 均值模糊
Mat dst;
blur(src, dst, Size(3, 3), Point(-1, -1)); // filter2D
// 高斯模糊
Mat dst_gaussian;
int sigma = 3;
GaussianBlur(src, dst_gaussian, Size(3, 3), sigma, sigma);
// 顯示圖像
char input_title[] = "src";
char output_title[] = "src_blur";
char output_title_gaussian[] = "src_gaussian_blur";
namedWindow(input_title, WINDOW_AUTOSIZE);
imshow(input_title, src);
namedWindow(output_title, WINDOW_AUTOSIZE);
imshow(output_title, dst);
namedWindow(output_title_gaussian, WINDOW_AUTOSIZE);
imshow(output_title_gaussian, dst_gaussian);
waitKey(0);
return 0;
}
輸出:
Reference: