1 高斯濾波簡介
了解高斯濾波之前,我們首先熟悉一下高斯噪聲。高斯噪聲是指它的概率密度函數服從高斯分布(即正態分布)的一類噪聲。如果一個噪聲,它的幅度分布服從高斯分布,而它的功率譜密度又是均勻分布的,則稱它為高斯白噪聲。高斯白噪聲的二階矩不相關,一階矩為常數,是指先后信號在時間上的相關性,高斯白噪聲包括熱噪聲和散粒噪聲。
高斯濾波器是一類根據高斯函數的形狀來選擇權值的線性平滑濾波器。高斯平滑濾波器對於抑制服從正態分布的噪聲非常有效。一維零均值高斯函數為:
g(x)=exp( -x^2/(2 sigma^2)
其中,高斯分布參數Sigma決定了高斯函數的寬度。對於圖像處理來說,常用二維零均值離散高斯函數作平滑濾波器,高斯函數的圖形:
2 高斯濾波函數
對於圖像來說,高斯濾波器是利用高斯核的一個2維的卷積算子,用於圖像模糊化(去除細節和噪聲)。
1) 高斯分布
一維高斯分布:
二維高斯分布:
2) 高斯核
理論上,高斯分布在所有定義域上都有非負值,這就需要一個無限大的卷積核。實際上,僅需要取均值周圍3倍標准差內的值,以外部份直接去掉即可。 如下圖為一個標准差為1.0的整數值高斯核。
3 高斯濾波性質
高斯函數具有五個重要的性質,這些性質使得它在早期圖像處理中特別有用.這些性質表明,高斯平滑濾波器無論在空間域還是在頻率域都是十分有效的低通濾波器,且在實際圖像處理中得到了工程人員的有效使用.高斯函數具有五個十分重要的性質,它們是:
(1)二維高斯函數具有旋轉對稱性,即濾波器在各個方向上的平滑程度是相同的.一般來說,一幅圖像的邊緣方向是事先不知道的,因此,在濾波前是無法確定一個方向上比另一方向上需要更多的平滑.旋轉對稱性意味着高斯平滑濾波器在后續邊緣檢測中不會偏向任一方向.
(2)高斯函數是單值函數.這表明,高斯濾波器用像素鄰域的加權均值來代替該點的像素值,而每一鄰域像素點權值是隨該點與中心點的距離單調增減的.這一性質是很重要的,因為邊緣是一種圖像局部特征,如果平滑運算對離算子中心很遠的像素點仍然有很大作用,則平滑運算會使圖像失真.
(3)高斯函數的傅立葉變換頻譜是單瓣的.正如下面所示,這一性質是高斯函數付立葉變換等於高斯函數本身這一事實的直接推論.圖像常被不希望的高頻信號所污染(噪聲和細紋理).而所希望的圖像特征(如邊緣),既含有低頻分量,又含有高頻分量.高斯函數付立葉變換的單瓣意味着平滑圖像不會被不需要的高頻信號所污染,同時保留了大部分所需信號.
(4)高斯濾波器寬度(決定着平滑程度)是由參數σ表征的,而且σ和平滑程度的關系是非常簡單的.σ越大,高斯濾波器的頻帶就越寬,平滑程度就越好.通過調節平滑程度參數σ,可在圖像特征過分模糊(過平滑)與平滑圖像中由於噪聲和細紋理所引起的過多的不希望突變量(欠平滑)之間取得折衷.
(5)由於高斯函數的可分離性,較大尺寸的高斯濾波器可以得以有效地實現.二維高斯函數卷積可以分兩步來進行,首先將圖像與一維高斯函數進行卷積,然后將卷積結果與方向垂直的相同一維高斯函數卷積.因此,二維高斯濾波的計算量隨濾波模板寬度成線性增長而不是成平方增長.
4 高斯濾波應用
高斯濾波后圖像被平滑的程度取決於標准差。它的輸出是領域像素的加權平均,同時離中心越近的像素權重越高。因此,相對於均值濾波(mean filter)它的平滑效果更柔和,而且邊緣保留的也更好。
高斯濾波被用作為平滑濾波器的本質原因是因為它是一個低通濾波器,見下圖。而且,大部份基於卷積平滑濾波器都是低通濾波器。
圖.高斯濾波器(標准差=3像素)的頻率響應。The spatial frequency axis is marked
in cycles per pixel, and hence no value above 0.5 has a real meaning。
5 高斯濾波步驟
(1)移動相關核的中心元素,使它位於輸入圖像待處理像素的正上方
(2)將輸入圖像的像素值作為權重,乘以相關核
(3)將上面各步得到的結果相加做為輸出
6 高斯濾波源碼(C語言版)
1 // gaosilvbo.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <stdlib.h> 6 #include <math.h> 7 8 typedef unsigned long DWORD; 9 typedef long LONG; 10 typedef unsigned short WORD; 11 typedef unsigned char BYTE; 12 13 typedef struct tagRGBQUAD { 14 BYTE rgbBlue; 15 BYTE rgbGreen; 16 BYTE rgbRed; 17 BYTE rgbReserved; 18 } RGBQUAD; 19 20 #pragma pack (2) /*指定按字節對齊*/ 21 typedef struct tagBITMAPFILEHEADER { 22 WORD bfType; 23 DWORD bfSize; 24 WORD bfReserved1; 25 WORD bfReserved2; 26 DWORD bfOffBits; 27 } BITMAPFILEHEADER; 28 29 //恢復對齊狀態 30 typedef struct tagBITMAPINFOHEADER{ 31 DWORD biSize; 32 LONG biWidth; 33 LONG biHeight; 34 WORD biPlanes; 35 WORD biBitCount; 36 DWORD biCompression; 37 DWORD biSizeImage; 38 LONG biXPelsPerMeter; 39 LONG biYPelsPerMeter; 40 DWORD biClrUsed; 41 DWORD biClrImportant; 42 } BITMAPINFOHEADER; 43 44 unsigned char *pTempBmpBuf; //讀入圖像數據的指針 45 46 unsigned char *pBmpBuf; //讀入圖像數據的指針 47 48 49 int bmpWidth; //圖像的寬 50 int bmpHeight; //圖像的高 51 RGBQUAD *pColorTable; //顏色表指針 52 int biBitCount; //圖像類型,每像素位數 53 54 bool readBmp(char *bmpName) 55 { 56 //二進制讀方式打開指定的圖像文件 57 58 FILE *fp=fopen(bmpName,"rb"); 59 if(fp==0) return 0; 60 61 62 //跳過位圖文件頭結構BITMAPFILEHEADER 63 fseek(fp, sizeof(BITMAPFILEHEADER),0); 64 65 //定義位圖信息頭結構變量,讀取位圖信息頭進內存,存放在變量head中 66 BITMAPINFOHEADER head; 67 fread(&head, sizeof(BITMAPINFOHEADER), 1,fp); 68 69 //獲取圖像寬、高、每像素所占位數等信息 70 bmpWidth = head.biWidth; 71 bmpHeight = head.biHeight; 72 biBitCount = head.biBitCount; 73 74 75 //定義變量,計算圖像每行像素所占的字節數(必須是的倍數) 76 int lineByte=(bmpWidth * biBitCount/8+3)/4*4; 77 78 //灰度圖像有顏色表,且顏色表表項為 79 if(biBitCount==8){ 80 //申請顏色表所需要的空間,讀顏色表進內存 81 pColorTable=new RGBQUAD[256]; 82 fread(pColorTable,sizeof(RGBQUAD),256,fp); 83 } 84 85 86 //申請位圖數據所需要的空間,讀位圖數據進內存 87 pTempBmpBuf=new unsigned char[lineByte * bmpHeight]; 88 89 pBmpBuf=new unsigned char[lineByte * bmpHeight]; 90 fread(pTempBmpBuf,1,lineByte * bmpHeight,fp); 91 fseek(fp, 1078,0); 92 fread(pBmpBuf,1,lineByte * bmpHeight,fp); 93 94 //關閉文件 95 fclose(fp); 96 return 1; 97 } 98 99 bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height,int biBitCount, RGBQUAD *pColorTable) 100 { 101 102 if(!imgBuf) 103 return 0; 104 105 //顏色表大小,以字節為單位,灰度圖像顏色表為字節,彩色圖像顏色表大小為 106 int colorTablesize=0; 107 if(biBitCount==8) 108 colorTablesize=1024; 109 110 //待存儲圖像數據每行字節數為的倍數 111 int lineByte=(width * biBitCount/8+3)/4*4; 112 113 //以二進制寫的方式打開文件 114 FILE *fp=fopen(bmpName,"wb"); 115 if(fp==0) return 0; 116 117 //申請位圖文件頭結構變量,填寫文件頭信息 118 BITMAPFILEHEADER fileHead; 119 fileHead.bfType = 0x4D42;//bmp類型 120 121 //bfSize是圖像文件個組成部分之和 122 fileHead.bfSize= sizeof(BITMAPFILEHEADER) 123 + sizeof(BITMAPINFOHEADER) 124 + colorTablesize + lineByte*height; 125 126 fileHead.bfReserved1 = 0; 127 fileHead.bfReserved2 = 0; 128 129 //bfOffBits是圖像文件前個部分所需空間之和 130 fileHead.bfOffBits=54+colorTablesize; 131 132 //寫文件頭進文件 133 fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp); 134 135 //申請位圖信息頭結構變量,填寫信息頭信息 136 BITMAPINFOHEADER head; 137 head.biBitCount=biBitCount; 138 head.biClrImportant=0; 139 head.biClrUsed=0; 140 head.biCompression=0; 141 head.biHeight=height; 142 head.biPlanes=1; 143 head.biSize=40; 144 head.biSizeImage=lineByte*height; 145 head.biWidth=width; 146 head.biXPelsPerMeter=0; 147 head.biYPelsPerMeter=0; 148 149 //寫位圖信息頭進內存 150 fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp); 151 152 //如果灰度圖像,有顏色表,寫入文件 153 if(biBitCount==8) 154 fwrite(pColorTable, sizeof(RGBQUAD),256, fp); 155 156 //寫位圖數據進文件 157 fwrite(imgBuf, height*lineByte, 1, fp); 158 159 //關閉文件 160 fclose(fp); 161 return 1; 162 } 163 164 int _tmain(int argc, _TCHAR* argv[]) 165 { 166 //讀入指定BMP文件進內存 167 char readPath[]="guass_test.bmp"; 168 readBmp(readPath); 169 //輸出圖像的信息 170 printf("width=%d,height=%d,biBitCount=%d\n", 171 bmpWidth,bmpHeight,biBitCount); 172 173 //每行字節數 174 int lineByte=(bmpWidth*biBitCount/8+3)/4*4; 175 //定義最終寫入的數據體 176 //pBmpBuf=new unsigned char[lineByte * bmpHeight]; 177 //循環變量,圖像的坐標 178 int y,x; 179 180 //循環變量,針對彩色圖像,遍歷每像素的三個分量 181 int k; 182 //單精度變量暫存計算后的灰度值(針對灰度圖像) 183 float TempNum; 184 //指向TempBmpbuf的指針 185 unsigned char *TemPtr; 186 //指向BmpBuf的指針 187 unsigned char *Ptr; 188 //定義*3的模板(拉普拉斯) 189 float CoefArray[9]={1.0f,2.0f,1.0f,2.0f,4.0f,2.0f,1.0f,2.0f,1.0f}; 190 //定義模板前乘的系數(拉普拉斯) 191 float coef=(float)(1.0/16.0);; 192 193 //lapulas濾波 194 if(biBitCount==8){//對於灰度圖像 195 for(y=1;y<bmpHeight-1;y++){ 196 for(x=0;x<bmpWidth-1;x++){ 197 198 TemPtr=pTempBmpBuf+y*lineByte+x; 199 Ptr=pBmpBuf+y*lineByte+x; 200 201 TempNum=(float)((unsigned char)*(TemPtr+lineByte-1))*CoefArray[0]; 202 TempNum+=(float)((unsigned char)*(TemPtr+lineByte))*CoefArray[1]; 203 TempNum+=(float)((unsigned char)*(TemPtr+lineByte+1))*CoefArray[2]; 204 TempNum+=(float)((unsigned char)*(TemPtr-1))*CoefArray[3]; 205 TempNum+=(float)((unsigned char)*TemPtr)*CoefArray[4]; 206 TempNum+=(float)((unsigned char)*(TemPtr+1))*CoefArray[5]; 207 TempNum+=(float)((unsigned char)*(TemPtr-lineByte-1))*CoefArray[6]; 208 TempNum+=(float)((unsigned char)*(TemPtr-lineByte))*CoefArray[7]; 209 TempNum+=(float)((unsigned char)*(TemPtr-lineByte+1))*CoefArray[8]; 210 211 TempNum*=coef; 212 213 if(TempNum>255.0) *Ptr =(BYTE)255; 214 else if(TempNum<0.0) 215 *Ptr =(unsigned char)fabs(TempNum); 216 //用到了fabs函數,需要添加math.h頭文件 217 else *Ptr=(char)TempNum; 218 } 219 } 220 } 221 222 else if(biBitCount==24){//彩色圖像 223 for(y=0;y<bmpHeight/2;y++){ 224 for(x=0;x<bmpWidth/2;x++){ 225 for(k=0;k<3;k++)//每像素RGB三個分量分別置才變成黑色 226 *(pBmpBuf+y*lineByte+x*3+k)=0; 227 } 228 } 229 } 230 //將圖像數據存盤 231 char writePath[]="gauss_result.BMP"; 232 saveBmp(writePath, pBmpBuf, bmpWidth, 233 bmpHeight, biBitCount, pColorTable); 234 //清除緩沖區,pBmpBuf和pColorTable是全局變量,在文件讀入時申請的空間 235 delete []pBmpBuf; 236 delete []pTempBmpBuf; 237 if(biBitCount==8) 238 delete []pColorTable; 239 return 0; 240 }
高斯濾波處理之后: 高斯濾波處理之前: