影象基本變換---影象快速高斯模糊演算法
本文將詳細介紹經典高斯濾波的相關內容。
高斯濾波器實質上是一種訊號的濾波器,其用途是訊號的平滑處理。它是一類根據高斯函式的形狀來選擇權重的線性平滑濾波器,該濾波器對於抑制服從正態分佈的噪聲非常有效。高斯函式的公式如下所示:
一維高斯函式:
二維高斯函式:
對於二維高斯函式,它的分佈如下圖所示:
Fig.1二維Gauss分佈
對於二維高斯函式,我們設定兩個引數:高斯半徑r和方差sigma,由半徑r我們可以得到一個(2r+1)*(2r+1)大小的高斯核模板,計算函式程式碼如下(其中k是高斯模板的權係數,即歸一化係數):
private static double[,] GaussFuc(int r, double sigma)
{
int size = 2 * r + 1;
double[,] gaussResult = new double[size, size];
double k = 0.0;
for (int y = -r, h = 0; y <= r; y++, h++)
{
for (int x = -r, w =
0; x <= r
{
gaussResult[w, h] = (1.0 / (2.0 * Math.PI * sigma * sigma)) * (Math.Exp(-((double)x * (double)x + (double)y * (double)y) / (2.0 * sigma * sigma)));
k += gaussResult[w, h];
}
}
return gaussResult;
}
我們設定引數r=1,sigma=1.0,則得到一個3*3的高斯模板如下:
Fig.2 3*3高斯模板
通常,我們在影象處理中使用的高斯函式定義如下:
使用該公式得到3*3高斯模板如下:
Fig.3 3*3高斯模板
對模板修正,即可得到我們常用的3*3經典模板:
Fig.4經典3*3高斯模板
這就是經典高斯模板的計算過程,半徑不同,我們可以得到不同的模板。通常使用了3*3和5*5的經典模板:
Fig.5 Gauss 模板
得到高斯模板後,我們用它對影象進行卷積,就可以得到高斯濾波的結果影象了。
由於直接使用公式2-(97),計算量巨大,影響影象處理的,因此,我們採用快速演算法,即將公式2-(97)分解為如下公式:
這個公式可以理解為先對影象按行進行一次一維高斯濾波,在對結果影象按列進行一次一維高斯濾波,這樣速度將大大提高。
一維高斯濾波程式碼如下(包含歸一化):
- private static double[] GaussKernel1D(int r, double sigma)
- {
- double[] filter = new double[2 * r + 1];
- double sum = 0.0;
- for (int i = 0; i < filter.Length; i++)
- {
- filter[i] = Math.Exp((double)(-(i - r) * (i - r)) / (2.0 * sigma * sigma));
- sum += filter[i];
- }
- for (int i = 0; i < filter.Length; i++)
- {
- filter[i] = filter[i] / sum;
- }
- return filter;
- }
- private static double[] GaussKernel(int radius, double sigma)
- {
- int length=2*radius+1;
- double[] kernel = new double[length];
- double sum = 0.0;
- for (int i = 0; i < length; i++)
- {
- kernel[i] = Math.Exp((double)(-(i - radius) * (i - radius)) / (2.0 * sigma * sigma));
- sum += kernel[i];
- }
- for (int i = 0; i < length; i++)
- {
- kernel[i] = kernel[i] / sum;
- }
- return kernel;
- }
- ///
- /// Gauss filter process
- ///
- /// The source image.
- /// The radius of gauss kernel,from 0 to 100.
- /// The convince of gauss kernel, from 0 to 30.
- ///
- public static WriteableBitmap GaussFilter(WriteableBitmap src,int radius,double sigma) ////高斯濾波
- {
- if (src != null)
- {
- int w = src.PixelWidth;
- int h = src.PixelHeight;
- WriteableBitmap srcImage = new WriteableBitmap(w, h);
- byte[] srcValue = src.PixelBuffer.ToArray();
- byte[] tempValue=(byte[])srcValue.Clone();
- double[] kernel = GaussKernel(radius, sigma);
- double tempB = 0.0, tempG = 0.0, tempR = 0.0;
- int rem = 0;
- int t = 0;
- int v = 0;
- double K = 0.0;
- for (int y = 0; y < h; y++)
- {
- for (int x = 0; x < w; x++)
- {
- tempB = tempG = tempR = 0.0;
- for (int k = -radius; k <= radius; k++)
- {
- rem = (Math.Abs(x + k) % w);
- t = rem * 4 + y * w * 4;
- K=kernel[k+radius];
- tempB += srcValue[t] * K;
- tempG += srcValue[t + 1] * K;
- tempR += srcValue[t + 2] * K;
- }
- v = x * 4 + y * w * 4;
- tempValue[v] = (byte)tempB;
- tempValue[v + 1] = (byte)tempG;
- tempValue[v + 2] = (byte)tempR;
- }
- }
- for (int x = 0; x < w; x++)
- {
- for (int y = 0; y < h; y++)
- {
- tempB = tempG = tempR = 0.0;
- for (int k = -radius; k <= radius; k++)
- {
- rem = (Math.Abs(y + k) % h);
- t = rem * w * 4 + x * 4;
- K = kernel[k + radius];
- tempB += tempValue[t] * K;
- tempG += tempValue[t + 1] * K;
- tempR += tempValue[t + 2] * K;
- }
- v = x * 4 + y * w * 4;
- srcValue[v] = (byte)tempB;
- srcValue[v + 1] = (byte)tempG;
- srcValue[v + 2] = (byte)tempR;
- }
- }
- Stream sTemp = srcImage.PixelBuffer.AsStream();
- sTemp.Seek(0, SeekOrigin.Begin);
- sTemp.Write(srcValue, 0, w * 4 * h);
- return srcImage;
- }
- else
- {
- return null;
- }
- }