熱線電話:0755-23712116
郵(you)箱(xiang):contact@legoupos.cn
地址:深圳市寶(bao)安區沙井街道后亭(ting)茅洲(zhou)山工業(ye)園(yuan)工業(ye)大廈全至科技創新園(yuan)科創大廈2層2A
本(ben)文(wen)主要介紹(shao)了高斯濾波器的原理及其實(shi)現過(guo)程(cheng)
高斯(si)濾(lv)波(bo)(bo)器是(shi)(shi)一種線性濾(lv)波(bo)(bo)器,能(neng)夠(gou)有效的(de)(de)抑(yi)制噪(zao)聲,平滑(hua)圖像。其作用原理和(he)均(jun)(jun)(jun)值(zhi)(zhi)(zhi)濾(lv)波(bo)(bo)器類似(si),都是(shi)(shi)取濾(lv)波(bo)(bo)器窗口(kou)內的(de)(de)像素的(de)(de)均(jun)(jun)(jun)值(zhi)(zhi)(zhi)作為輸出。其窗口(kou)模(mo)板的(de)(de)系(xi)(xi)數(shu)和(he)均(jun)(jun)(jun)值(zhi)(zhi)(zhi)濾(lv)波(bo)(bo)器不(bu)同(tong),均(jun)(jun)(jun)值(zhi)(zhi)(zhi)濾(lv)波(bo)(bo)器的(de)(de)模(mo)板系(xi)(xi)數(shu)都是(shi)(shi)相同(tong)的(de)(de)為1;而(er)(er)高斯(si)濾(lv)波(bo)(bo)器的(de)(de)模(mo)板系(xi)(xi)數(shu),則隨著距離模(mo)板中心的(de)(de)增大而(er)(er)系(xi)(xi)數(shu)減小(xiao)。所以,高斯(si)濾(lv)波(bo)(bo)器相比于均(jun)(jun)(jun)值(zhi)(zhi)(zhi)濾(lv)波(bo)(bo)器對圖像個模(mo)糊程度較小(xiao)。
什么是高斯濾波器
既然(ran)名稱為高(gao)斯(si)濾波(bo)器,那么(me)其和高(gao)斯(si)分(fen)布(bu)(正態(tai)分(fen)布(bu))是有一(yi)定的關系(xi)的。一(yi)個二維的高(gao)斯(si)函數如下:
其中為點坐標,在圖像處理中可認為是整數;
是標準差。要想得到一個高斯濾波器的模板,可以對高斯函數進行離散化,得到的高斯函數值作為模板的系數。例如:要產生一個
的高斯濾波器模板,以模板的中心位置為坐標原點進行取樣。模板在各個位置的坐標,如下所示(x軸水平向右,y軸豎直向下)
這樣,將各個位置的坐標帶入到高斯函數中,得到的值就是模板的系數。
對于窗口模板的大小為 ,模板中各個元素值(zhi)的計算(suan)公(gong)式如下:
這樣計算出來的(de)模板有兩種形(xing)式:小數和整數。
· 小數(shu)形式的模板,就是直接計(ji)算得(de)到的值,沒有經過(guo)任何的處理;
· 整數形式的,則需要進行歸一化處理,將模板左上角的值歸一化為1,下面會具體介紹。使用整數的模板時,需要在模板的前面加一個系數,系數為,也就是模板(ban)系數和(he)的倒數。
高斯模板的生成
知道(dao)模板生成的(de)原(yuan)理,實現(xian)起來也就不困(kun)難了
void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{
static const double pi = 3.1415926;
int center = ksize / 2; // 模板的(de)中心位(wei)置,也就是(shi)坐標的(de)原(yuan)點
double x2, y2;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - center, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - center, 2);
double g = exp(-(x2 + y2) / (2 * sigma * sigma));
g /= 2 * pi * sigma;
window[i][j] = g;
}
}
double k = 1 / window[0][0]; // 將(jiang)左上(shang)角的系數歸(gui)一(yi)化為1
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
window[i][j] *= k;
}
}
}
需要一個二維數組,存放生成的系數(這里假設模板的最大尺寸不會超過11);第二個參數是模板的大小(不要超過11);第三個參數就比較重要了,是高斯分布的標準差。
生成的過程,首先根據模板的大小,找到模板的中心位置ksize/2。 然后就是遍歷,根據高斯分布的函數,計算模板中每個系數的值。
需要注意的是,最后歸一化的過程,使用模板左上角的系數的倒數作為歸一化的系數(左上角的系數值被歸一化為1),模板中的每個系數都乘以該值(左上角系數的倒數),然后將得到的值取整,就得到了整數型的高斯濾波器模板。
下面截圖生成的是,大小為的模板
對上(shang)述解(jie)結果取(qu)整后得到如下模板:
這個模板就比較熟悉了,其就是根據的高斯函數生成的模板(ban)。
至于(yu)小數形式(shi)的(de)(de)生成也比(bi)較簡單(dan),去掉歸(gui)一化的(de)(de)過(guo)程(cheng),并且(qie)在求(qiu)解過(guo)程(cheng)后,模(mo)板的(de)(de)每(mei)個(ge)系數要(yao)除以所(suo)有系數的(de)(de)和。具(ju)體(ti)代碼如下:
void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{
static const double pi = 3.1415926;
int center = ksize / 2; // 模板的中心(xin)位(wei)置,也就是坐標的原點
double x2, y2;
double sum = 0;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - center, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - center, 2);
double g = exp(-(x2 + y2) / (2 * sigma * sigma));
g /= 2 * pi * sigma;
sum += g;
window[i][j] = g;
}
}
//double k = 1 / window[0][0]; // 將左上角的(de)系(xi)數歸一化為1
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
window[i][j] /= sum;
}
}
}
的小數型模板。
值的意義及選取
通過上述的實現過程,不難發現,高斯濾波器模板的生成最重要的參數就是高斯分布的標準差。標準差代表著數據的離散程度,如果
較小,那么生成的模板的中心系數較大,而周圍的系數較小,這樣對圖像的平滑效果就不是很明顯;反之,
較(jiao)大,則生成的模(mo)板(ban)的各(ge)個系數(shu)相差就不(bu)是很(hen)大,比(bi)(bi)較(jiao)類似均值模(mo)板(ban),對圖像的平(ping)滑效果比(bi)(bi)較(jiao)明顯。
來看下一維高斯分布的概率分布密度圖:
橫軸表示可能得取值x,豎軸表示概率分布密度F(x),那么不難理解這樣一個曲線與x軸圍成的圖形面積為1。(標準差)決定了這個圖形的寬度,可以得出這樣的結論:
越大,則圖形越寬,尖峰越小,圖形較為平緩;
越小,則圖形越窄,越集中,中間部分也就越尖,圖形變化比較劇烈。這其實很好理解,如果sigma也就是標準差越大,則表示該密度分布一定比較分散,由于面積為1,于是尖峰部分減小,寬度越寬(分布越分散);同理,
當越小時,說明密度分布較為集中,于是尖峰越尖,寬度越窄!
于是可以得到如下結論:越大,分布越分散,各部分比重差別不大,于是生成的模板各元素值差別不大,類似于平均模板;
越(yue)小(xiao),分(fen)(fen)布越(yue)集中(zhong),中(zhong)間部分(fen)(fen)所占比重(zhong)遠(yuan)(yuan)(yuan)遠(yuan)(yuan)(yuan)高于(yu)其他部分(fen)(fen),反映到高斯模板上就(jiu)是中(zhong)心元素值(zhi)遠(yuan)(yuan)(yuan)遠(yuan)(yuan)(yuan)大于(yu)其他元素值(zhi),于(yu)是自(zi)然(ran)而然(ran)就(jiu)相當于(yu)中(zhong)間值(zhi)得點運算。
基于OpenCV的實現
在(zai)生成高(gao)斯模板好,其(qi)簡單的實現和其(qi)他的空間濾波器沒有區別,具體(ti)代碼(ma)如下(xia):
void GaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{
CV_Assert(src.channels() || src.channels() == 3); // 只處理(li)單通道或者三通道圖像(xiang)
const static double pi = 3.1415926;
// 根(gen)據窗口大小(xiao)和sigma生成高斯濾波器模板
// 申(shen)請一(yi)個(ge)二維數(shu)組,存放生成的高斯模(mo)板矩陣
double **templateMatrix = new double*[ksize];
for (int i = 0; i < ksize; i++)
templateMatrix[i] = new double[ksize];
int origin = ksize / 2; // 以模板的中心為原(yuan)點
double x2, y2;
double sum = 0;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - origin, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - origin, 2);
// 高斯函數前的常(chang)數可以不用計算,會在(zai)歸一化(hua)的過(guo)程中(zhong)給消去
double g = exp(-(x2 + y2) / (2 * sigma * sigma));
sum += g;
templateMatrix[i][j] = g;
}
}
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
templateMatrix[i][j] /= sum;
cout << templateMatrix[i][j] << " ";
}
cout << endl;
}
// 將模板應用到圖像(xiang)中(zhong)
int border = ksize / 2;
copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);
int channels = dst.channels();
int rows = dst.rows - border;
int cols = dst.cols - border;
for (int i = border; i < rows; i++)
{
for (int j = border; j < cols; j++)
{
double sum[3] = { 0 };
for (int a = -border; a <= border; a++)
{
for (int b = -border; b <= border; b++)
{
if (channels == 1)
{
sum[0] += templateMatrix[border + a][border + b] * dst.at(i + a, j + b);
}
else if (channels == 3)
{
Vec3b rgb = dst.at(i + a, j + b);
auto k = templateMatrix[border + a][border + b];
sum[0] += k * rgb[0];
sum[1] += k * rgb[1];
sum[2] += k * rgb[2];
}
}
}
for (int k = 0; k < channels; k++)
{
if (sum[k] < 0)
sum[k] = 0;
else if (sum[k] > 255)
sum[k] = 255;
}
if (channels == 1)
dst.at(i, j) = static_cast(sum[0]);
else if (channels == 3)
{
Vec3b rgb = { static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2]) };
dst.at(i, j) = rgb;
}
}
}
// 釋(shi)放模板數組(zu)
for (int i = 0; i < ksize; i++)
delete[] templateMatrix[i];
delete[] templateMatrix;
}
只處理單通道或者三通道圖像,模板生成后,其濾波(卷積過程)就比較簡單了。不過,這樣的高斯濾波過程,其循環運算次數為,其中m,n為圖像的尺寸;ksize為高斯濾波器的尺寸。這樣其時間復雜度為
,隨濾(lv)波(bo)器(qi)(qi)的(de)模板的(de)尺(chi)寸呈平方增(zeng)長,當高斯濾(lv)波(bo)器(qi)(qi)的(de)尺(chi)寸較大時,其運(yun)算(suan)效率(lv)是極低的(de)。為了(le),提高濾(lv)波(bo)的(de)運(yun)算(suan)速度,可以將二(er)維(wei)的(de)高斯濾(lv)波(bo)過程分(fen)解開來(lai)。
分離實現高斯濾波
由于高(gao)斯函數(shu)的(de)(de)可分(fen)離性,尺寸較大(da)的(de)(de)高(gao)斯濾(lv)波器可以分(fen)成(cheng)兩步進(jin)行:首(shou)先將圖像在水平(ping)(豎直(zhi))方向(xiang)與一(yi)維高(gao)斯函數(shu)進(jin)行卷積;然后(hou)將卷積后(hou)的(de)(de)結果在豎直(zhi)(水平(ping))方向(xiang)使(shi)用(yong)相同的(de)(de)一(yi)維高(gao)斯函數(shu)得到的(de)(de)模板(ban)進(jin)行卷積運算。具體實(shi)現代碼(ma)如下:
// 分(fen)離的計算
void separateGaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{
CV_Assert(src.channels()==1 || src.channels() == 3); // 只處理單(dan)通道或(huo)者(zhe)三通道圖像(xiang)
// 生(sheng)成一維的高斯濾(lv)波模板(ban)
double *matrix = new double[ksize];
double sum = 0;
int origin = ksize / 2;
for (int i = 0; i < ksize; i++)
{
// 高斯函數前(qian)的(de)常(chang)數可以不用計算,會在歸(gui)一化(hua)的(de)過程(cheng)中(zhong)給(gei)消(xiao)去
double g = exp(-(i - origin) * (i - origin) / (2 * sigma * sigma));
sum += g;
matrix[i] = g;
}
// 歸(gui)一(yi)化(hua)
for (int i = 0; i < ksize; i++)
matrix[i] /= sum;
// 將模板應用(yong)到(dao)圖像中
int border = ksize / 2;
copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);
int channels = dst.channels();
int rows = dst.rows - border;
int cols = dst.cols - border;
// 水平(ping)方(fang)向
for (int i = border; i < rows; i++)
{
for (int j = border; j < cols; j++)
{
double sum[3] = { 0 };
for (int k = -border; k <= border; k++)
{
if (channels == 1)
{
sum[0] += matrix[border + k] * dst.at(i, j + k); // 行(xing)不變(bian),列變(bian)化;先做水平(ping)方(fang)向的卷(juan)積
}
else if (channels == 3)
{
Vec3b rgb = dst.at(i, j + k);
sum[0] += matrix[border + k] * rgb[0];
sum[1] += matrix[border + k] * rgb[1];
sum[2] += matrix[border + k] * rgb[2];
}
}
for (int k = 0; k < channels; k++)
{
if (sum[k] < 0)
sum[k] = 0;
else if (sum[k] > 255)
sum[k] = 255;
}
if (channels == 1)
dst.at(i, j) = static_cast(sum[0]);
else if (channels == 3)
{
Vec3b rgb = { static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2]) };
dst.at(i, j) = rgb;
}
}
}
// 豎直(zhi)方向
for (int i = border; i < rows; i++)
{
for (int j = border; j < cols; j++)
{
double sum[3] = { 0 };
for (int k = -border; k <= border; k++)
{
if (channels == 1)
{
sum[0] += matrix[border + k] * dst.at(i + k, j); // 列不變,行變化;豎直(zhi)方向的卷積
}
else if (channels == 3)
{
Vec3b rgb = dst.at(i + k, j);
sum[0] += matrix[border + k] * rgb[0];
sum[1] += matrix[border + k] * rgb[1];
sum[2] += matrix[border + k] * rgb[2];
}
}
for (int k = 0; k < channels; k++)
{
if (sum[k] < 0)
sum[k] = 0;
else if (sum[k] > 255)
sum[k] = 255;
}
if (channels == 1)
dst.at(i, j) = static_cast(sum[0]);
else if (channels == 3)
{
Vec3b rgb = { static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2]) };
dst.at(i, j) = rgb;
}
}
}
delete[] matrix;
}
代碼沒有重構較長,不過其實現原理是比較簡單的。首先得到一維高斯函數的模板,在卷積(濾波)的過程中,保持行不變,列變化,在水平方向上做卷積運算;接著在上述得到的結果上,保持列不邊,行變化,在豎直方向上做卷積運算。這樣分解開來,算法的時間復雜度為,運算(suan)量和濾波器的模板尺(chi)寸呈線性增長。
在(zai)OpenCV也有對高斯濾(lv)波器(qi)的封裝GaussianBlur,其聲明如下:
CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
二維高斯函數的標準差在x和y方向上應該分別有一個標準差,在上面的代碼中一直設其在x和y方向的標準是相等的,在OpenCV中的高斯濾波器中,可以在x和y方向上設置不同的標準差。
下圖是自己實現的高斯濾波器和OpenCV中的GaussianBlur的結果對比
上圖是的高斯(si)濾波器,可(ke)以看出兩(liang)個(ge)實現得到的結果沒有很大的區別。
總結
高斯濾波器是一種線性平滑濾波器,其濾波器的模板是對二維高斯函數離散得到。由于高斯模板的中心值最大,四周逐漸減小,其濾波后的結果相對于均值濾波器來說更好。
高斯濾波器最重要的參數就是高斯分布的標準差,標準差和高斯濾波器的平滑能力有很大的能力,
越大,高斯濾波器的頻帶就較寬,對圖像的平滑程度就越好。通過調節
參數(shu),可以平衡對圖(tu)像(xiang)的噪聲的抑制和對圖(tu)像(xiang)的模糊。
文章轉自Brook_icv //www.cnblogs.com/wangguchangqing/p/6407717.html