一、圖像平滑與濾波概念
介紹圖像濾波之前有必要了解一下圖像平滑的概念。
圖像平滑(smoothing)也稱為圖像模糊(blurring),是一種在圖像處理中使用頻率很高的操作,進行圖像平滑的操作原因有很多,在這里重點介紹使用平滑操作降低圖片噪聲。因為在圖像中,噪聲的能量大都集中在幅度譜的低頻和中頻部分,而在較高的頻段,一些重要的細節信息往往被噪聲淹沒。在一幅圖像中,所謂的高頻部分是指圖像中像素值落差很大的部分,而低頻則是指像素值與旁邊的像素值相差不大甚至相同,而圖像的一些細節的部分往往由高頻信息來展現,圖像中摻雜的噪聲往往也處于高頻段,這就造成了一些細節信息被噪聲淹沒,可以根據不同的噪聲類型用不同的濾波器進行處理。
濾波的目的有兩個即:1.抽出對象的特征作為圖像識別的特征模式;2.為適應圖像處理要求,消除數字圖像所混入的噪聲
對圖像濾波有兩個要求:1.不能損壞圖像的輪廓和邊緣等重要信息;2.使圖像清晰視覺效果更好
為了進行圖像平滑操作,通常在圖像上加一個濾波器(filter),最常見的類型是線性的,輸出像素值g(x, y)最終由原像素值和加權值決定。其過程如下:
其中h(x, y)被稱為核(kernel),是加到圖像上濾波器(filter)的系數,它有助于把濾波器進行可視化為一個窗口在圖像上滑動,這些涉及到鄰域的卷積操作。
鄰域算子值利用給定像素周圍像素的值決定此像素的最終輸出。左邊圖像就是原像素的值,中間圖像是濾波器(filter),filter的目的就是將濾波器的加權值進行這樣的可視化窗口,最右邊的圖像是原圖像和濾波器一起卷積生成,圖像中的藍色部分是左圖中紅色和中間濾波器卷積計算得到的結果。所以可以看出圖像像素最終的值不僅與原像素有關也可濾波器選取的kernel窗口大小有關。
二、方框濾波
在有了上述的理論基礎之后,首先介紹一下方框濾波。
這是所有濾波器中最簡單的一種濾波方式。每一個輸出像素的是內核鄰域像素值的平均值得到。
通用的濾波kernel如下:
這里是一個長寬分別為Kwidth和Kheight的窗口函數,在此區域內鄰域中像素值疊加求平均即可求出位于kernel中心點像素的像素值。
在OpenCV中方框濾波的被封裝在boxFilter函數中。
函數原型:
void boxFilter( InputArray src, OutputArray dst, int ddepth,
Size ksize, Point anchor = Point(-1,-1),
bool normalize = true,
int borderType = BORDER_DEFAULT );
各個參數詳解:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該為CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。
- 第三個參數,int類型的ddepth,輸出圖像的深度,-1代表使用原圖深度,即src.depth()。
- 第四個參數,Size類型(對Size類型稍后有講解)的ksize,內核的大小。一般這樣寫Size( w,h )來表示內核的大小( 其中,w 為像素寬度, h為像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- 第五個參數,Point類型的anchor,表示錨點(即被平滑的那個點),注意他有默認值Point(-1,-1)。如果這個點坐標是負值的話,就表示取核的中心為錨點,所以默認值Point(-1,-1)表示這個錨點在核的中心。
- 第六個參數,bool類型的normalize,默認值為true,一個標識符,表示內核是否被其區域歸一化(normalized)了。
- 第七個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
boxFilter()函數方框濾波所用的核為:
其中:
當normalize=true的時候,方框濾波就變成了下面要說的的均值濾波。
三、均值濾波
均值濾波的原理非常簡單,就是輸出圖像的每一個像素是核窗口內輸入圖像對應像素的像素的平均值( 所有像素加權系數相等),其實說白了它就是歸一化后的方框濾波。
但是均值濾波本身存在著固有的缺陷,即它不能很好地保護圖像細節,在圖像去噪的同時也破壞了圖像的細節部分,從而使圖像變得模糊,不能很好地去除噪聲點。
OpenCV中實現均值濾波的是blur函數
函數原型:
void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
參數:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該為CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。比如可以用Mat::Clone,以源圖片為模板,來初始化得到如假包換的目標圖。
- 第三個參數,Size類型(對Size類型稍后有講解)的ksize,內核的大小。一般這樣寫Size( w,h )來表示內核的大小( 其中,w 為像素寬度, h為像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- 第四個參數,Point類型的anchor,表示錨點(即被平滑的那個點),注意他有默認值Point(-1,-1)。如果這個點坐標是負值的話,就表示取核的中心為錨點,所以默認值Point(-1,-1)表示這個錨點在核的中心。
- 第五個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
當然要實現均勻濾波也可以使用上面提到的boxFilter函數,與方框濾波不同的是第六個參數normalize設為true。從blur函數的定義中可以看到blur函數只是調用了boxFilter而已:
void cv::blur( InputArray src, OutputArray dst,
Size ksize, Point anchor, int borderType )
{
boxFilter( src, dst, -1, ksize, anchor, true, borderType );
}
四、高斯濾波
圖像的高斯模糊過程就是圖像與服從二維正態分布的卷積核做卷積。由于正態分布又叫作高斯分布,所以這項技術就叫作高斯模糊。
圖像與圓形卷積核做卷積將會生成更加精確的焦外成像效果。由于高斯函數的傅立葉變換是另外一個高斯函數,所以高斯模糊對于圖像來說就是一個低通濾波操作。
高斯濾波器是一類根據高斯函數的形狀來選擇權值的線性平滑濾波器。高斯平滑濾波器對于抑制服從正態分布的噪聲非常有效。
一維零均值高斯函數為:
其中,高斯分布參數σ決定了高斯函數的寬度。
對于二維圖像來說,常用二維零均值離散高斯函數作平滑濾波器。
二維高斯函數為:
OpenCV中函數GaussianBlur實現了高斯濾波。
函數原型:
void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
參數:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。它可以是單獨的任意通道數的圖片,但需要注意,圖片深度應該為CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。比如可以用Mat::Clone,以源圖片為模板,來初始化得到如假包換的目標圖。
- 第三個參數,Size類型的ksize高斯內核的大小。其中ksize.width和ksize.height可以不同,但他們都必須為正數和奇數。或者,它們可以是零的,它們都是由sigma計算而來。
- 第四個參數,double類型的sigmaX,表示高斯核函數在X方向的的標準偏差。
- 第五個參數,double類型的sigmaY,表示高斯核函數在Y方向的的標準偏差。若sigmaY為零,就將它設為sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height計算出來。
為了結果的正確性著想,最好是把第三個參數Size,第四個參數sigmaX和第五個參數sigmaY全部指定到。- 第六個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
五、綜合示例代碼
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include<opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
int main(){
Mat img = imread("dog.jpg");
Mat out1,out2,out3;
boxFilter(img,out1,-1,Size(30,30));
blur(img, out2, Size(30, 30));
namedWindow("方框濾波", 2);
imshow("方框濾波", out1);
GaussianBlur(img, out3, Size(29, 29), 0, 0);
namedWindow("均值濾波", 2);
imshow("均值濾波", out2);
namedWindow("高斯濾波", 2);
imshow("高斯濾波", out3);
waitKey(0);
return 0;
}
參考:
http://blog.csdn.net/keith_bb/article/details/53869626
http://blog.csdn.net/poem_qianmo/article/details/22745559