## OpenCV 學習(幾種基本的低通濾波)
對圖像進行濾波處理是圖像處理中最常見的一種操作類型。而這其中低通濾波(也可以叫做平滑)有事各種濾波處理中最常用的。這里就簡單寫寫 OpenCV 中提供的幾種低通濾波方法。
### 均值濾波
這種濾波方法就是取一個像素的鄰域內各像素的平均值作為濾波結果。比如下面這個例子:
~~~
cv::blur(image, result, cv::Size(7, 7), cv::Point(-1, -1), cv::BORDER_DEFAULT);
~~~
原始圖像是 image, 濾波后的圖像是 result ,鄰域大小為 5 * 5。 cv::Point(-1, -1) 表明鄰域的零位就是鄰域的中心,這個是默認值,如果不改變的話可以不填。
cv::BORDER_DEFAULT 是對邊界的處理辦法,這個一般也不需要改變的。
下面是一副圖像濾波前后的對比。


與 blur 函數相關的還有個 boxFilter 函數。這個濾波器的核的各個元素都為 1。normalize = false 時相當于鄰域內各像素的數值求和。 normalize = true 時,計算結果等效于 blur 函數。
~~~
cv::boxFilter ( InputArray src,
OutputArray dst,
int ddepth,
Size ksize,
Point anchor = Point(-1,-1),
bool normalize = true,
int borderType = BORDER_DEFAULT
)
~~~
### 高斯濾波
均值濾波對鄰域內各個像素采用統一的權值,這種方式對大多數應用來說不是最佳的。高斯濾波采取鄰域內越靠近的值提供越大的權重的方式計算平均值。權重的選取采用高斯函數的形式。高斯函數有個非常好的特點,就是無論在時域還是頻域都是鐘形的。通過控制 σ 可以控制低通濾波的截止頻率。函數原型如下:
~~~
void GaussianBlur( InputArray src,
OutputArray dst, Size ksize,
double sigmaX, double sigmaY=0,
int borderType=BORDER_DEFAULT );
~~~
使用起來很方便,下面是個例子:
~~~
cv::GaussianBlur(image, result, cv::Size(7, 7), 1.5);
~~~
參數image為輸入圖像,result為輸出圖像,Size(5,5)定義了核的大小,最后一個參數是高斯濾波的 σ 。從函數原型上可以看到有 sigmaX 和 sigmaY 兩個參數。通常情況下 sigmaY 取與 sigmaX 相同的值,這時可以不寫出來。也就是用它的默認值 0.
還是剛才的圖像,高斯濾波后的結果如下:

高斯濾波器的大小和 σ 可以只指定一個。另一個會自動選擇合適的值。
比如說我們希望 σ=1.5。那么可以直接寫為:
~~~
cv::GaussianBlur(image, result, cv::Size(0, 0), 1.5);
~~~
或者要求核的大小為 9 * 9 個點。可以這樣寫:
~~~
cv::GaussianBlur(image, result, cv::Size(9, 9), 0);
~~~
如果加大 σ 會濾波的更平滑些,但是也會損失更多的細節,當 σ=4 時的濾波結果如下。

相關的函數還有個 getGaussianKernel。 這個函數可以計算高斯濾波器的系數,但是它計算的是 1 維濾波器的系數。對于高斯濾波器來說, 2 維系數其實就是橫向和豎向兩個 1 維濾波器的系數的乘積。這種性質有個專有名詞,叫做 seperable filter。
下面是這個函數的一個用例:
~~~
cv::Mat kernel = cv::getGaussianKernel(7, 1.5, CV_32F);
~~~
### 中值濾波器
中值濾波是一種非線性濾波器。它是取鄰域內各點的統計中值作為輸出。這種濾波器可以有效的去除椒鹽噪聲。還能保持圖像中各物體的邊界不被模糊掉。是一種最常用的非線性濾波器。這種濾波器只能使用正方形的鄰域。下面是個例子:
~~~
cv::medianBlur(image, result, 7);
~~~
下面是原始圖像。

中值濾波的結果如下。

同樣大小濾波核高斯濾波的結果如下。

對比很明顯,中值濾波對于去除這些細線更有效。
### 通用濾波器 filter2D
利用這個函數我們可以自定義濾波器的核。
比如下面的代碼:
~~~
cv::Mat kernel = (cv::Mat_<float>(3, 3) << 1/9.0, 1/9.0, 1/9.0,
1/9.0, 1/9.0, 1/9.0,
1/9.0, 1/9.0, 1/9.0);
cv::filter2D(image, result, image.depth(), kernel);
~~~
就相當于一個 3 * 3 的均值濾波器。
~~~
cv::blur(image, result, cv::Size(3, 3), cv::Point(-1, -1), cv::BORDER_DEFAULT);
~~~
當然,如果只是搞個均值濾波器,不需要這么麻煩,直接用 blur 函數就可以了。但是如果我們要設計個很特殊的濾波器時,filter2D 就派上用場了。
### 可分離濾波器 sepFilter2D
一個 2 維濾波器,如果可以分離為x 方向和 y 方向兩個獨立的 1 維濾波器。那么這個 2 維濾波器就稱為 可分離濾波器。比如我們上面介紹的高斯濾波器就是一個典型的可分離濾波器。具有這種性質的濾波器有快速算法,可以比不具有這個性質的普通的濾波器更高效的計算。
這個函數的接口如下:
~~~
void sepFilter2D( InputArray src, OutputArray dst, int ddepth,
InputArray kernelX, InputArray kernelY,
Point anchor=Point(-1,-1),
double delta=0, int borderType=BORDER_DEFAULT );
~~~
與其他濾波器最大的區別就是需要傳進 2 個濾波器核,kernelX 和 kernelY。下面舉個例子:
~~~
cv::Mat kernel = cv::getGaussianKernel(7, 1.5, CV_32F);
cv::sepFilter2D(image, result, -1, kernel, kernel);
~~~
相當于對 image 進行了一次高斯濾波,也就是說與下面的代碼等效。
~~~
cv::GaussianBlur(image, result, cv::Size(7, 7), 1.5);
~~~
大家可以試試,兩種方法得到的結果完全相同。