中值滤波算法
數字圖像在其形成、傳輸記錄的過程中往往會受到很多噪聲的的污染,比如:椒鹽噪聲、高斯噪聲等,為了抑制和消除這些隨即產生的噪聲而改善圖像的質量,就需要去、對圖像進行去噪處理,去噪也就是濾波處理。
中值濾波和同態濾波都是圖像增強的方法,但是這兩種方法是從不同的方式進行改善一副圖片的質量。
中值濾波是圖像平滑的一種方法?它是一種非線性平滑濾波技術,在一定條件下可以克服線性濾波帶來的圖像細節的模糊問題,特別是針對被椒鹽噪聲污染的圖像。
所謂中值濾波,其中濾波就是前面講的去噪,關鍵在于中值兩字,中值從字面意思上講就是中間的那個值也就是中心值。
舉一個簡單的例子:一維序列{0,3,4,0,7},進行中值濾波排序后為{0,0,3,4,7},則其中值為3。
數字圖像是以二維圖像來描述的,故對圖像的濾波也就是對二維數據序列的濾波,這個二維序列相當于一個二維矩陣,里面元素的值就是每個像素點的像素。
中值濾波通常采用一個含奇數個點的滑動窗口,用窗口的中的灰度值的中值來代替中心點的灰度值,其實就是對這個窗口中的灰度值進行排序,然后將其中值賦值給中心點即可。常用的中值濾波窗口形狀有線狀、方形、圓形以及十字形等。
注:對每一個像素的m*n鄰域進行計算,中值濾波對圖像的邊界用0做擴張,所以對邊界可能會出現扭曲。?
算法描述:
[1]?獲得源圖像的首地址及圖像的寬和高
[2]?開辟一塊內存緩沖區,用以暫存結果圖像,并初始化為0
[3]?逐個掃描圖像中的像素點,將其鄰域各元素的像素值從小到大進行排序,將求得到的中間值賦值給目標圖像中與當前點對應的像素點
[4]?循環步驟[3],直到處理完源圖像的全部像素點
[5]?將結果從內存緩沖區復制到源圖像的數據區
PS:?個人覺得中值濾波原理非常簡單,在做的過程中原理沒什么難度,一直讓我糾結的是怎么去讀取一副圖片的數據區(也就是這副圖片的像素值),對于我這個沒接觸過圖像的水貨來說困擾了我很久,當然利用matlab軟件讀取是很容易的事情,中值濾波也就其內嵌寫好的函數調用就好了,但是為了鍛煉一下自己,嘗試用c寫。
Bmp圖像的存儲、格式以及讀取
現在講解BMP的4個組成部分:
1.文件頭信息塊
0000-0001:文件標識,為字母ASCII碼“BM”。
0002-0005:文件大小。
0006-0009:保留,每字節以“00”填寫。
000A-000D:記錄圖像數據區的起始位置。各字節的信息依次含義為:文件頭信息塊大小,圖像描述信息塊的大小,圖像顏色表的大小,保留(為01)。
2.圖像描述信息塊
000E-0011:圖像描述信息塊的大小,常為28H。
0012-0015:圖像寬度。
0016-0019:圖像高度。
001A-001B:圖像的plane總數(恒為1)。
001C-001D:記錄像素的位數,很重要的數值,圖像的顏色數由該值決定。
001E-0021:數據壓縮方式(數值位0:不壓縮;1:8位壓縮;2:4位壓縮)。
0022-0025:圖像區數據的大小。
0026-0029:水平每米有多少像素,在設備無關位圖(.DIB)中,每字節以00H填寫。
002A-002D:垂直每米有多少像素,在設備無關位圖(.DIB)中,每字節以00H填寫。
002E-0031:此圖像所用的顏色數,如值為0,表示所有顏色一樣重要。
3.顏色表
顏色表的大小根據所使用的顏色模式而定:2色圖像為8字節;16色圖像位64字節;256色圖像為1024字節。其中,每4字節表示一種顏色,并以B(藍色)、G(綠色)、R(紅色)、alpha(32位位圖的透明度值,一般不需要)。即首先4字節表示顏色號1的顏色,接下來表示顏色號2的顏色,依此類推。
4.圖像數據區
顏色表接下來位為位圖文件的圖像數據區,在此部分記錄著每點像素對應的顏色號,其記錄方式也隨顏色模式而定,既2色圖像每點占1位(8位為1字節);16色圖像每點占4位(半字節);256色圖像每點占8位(1字節);真彩色圖像每點占24位(3字節)。所以,整個數據區的大小也會隨之變化。究其規律而言,可的出如下計算公式:圖像數據信息大小=(圖像寬度*圖像高度*記錄像素的位數)/8。
然而,未壓縮的圖像信息區的大小。除了真彩色模式外,其余的均大于或等于數據信息的大小。這是為什么呢?原因有兩個:
1.BMP文件記錄一行圖像是以字節為單位的。因此,就不存在一個字節中的數據位信息表示的點在不同的兩行中。也就是說,設顯示模式位16色,在每個字節分配兩個點信息時,如果圖像的寬度位奇數,那么最后一個像素點的信息將獨占一個字節,這個字節的后4位將沒有意義。接下來的一個字節將開始記錄下一行的信息。
2.為了顯示的方便,除了真彩色外,其他的每中顏色模式的行字節數要用數據“00”補齊為4的整數倍。如果顯示模式為16色,當圖像寬為19時,存儲時每行則要補充4-(19/2+1)%4=2個字節(加1是因為里面有一個像素點要獨占了一字節)。如果顯示模式為256色,當圖像寬為19時,每行也要補充4-19%4=1個字節。
由于bmp圖象是從下至上存儲的,所以我們不能進行直接順序讀取。詳細的說,bmp圖象存儲區數據是從1078偏移字節開始。文件內第一個圖象點實際上是對應圖象(320*200)第200行的最左邊的第一個點,而從1078開始的320個點則是圖象最下面一行對應的點,之后的321個點是圖象倒數第二行最左邊的第一個點。這樣,bmp文件最后一個字節對應的點是第一行最后邊的點了。
BMP圖像的讀取
1?文件格式
典型的BMP圖像文件由四部分組成。
1>位圖文件頭數據結構,它包含BMP圖像文件的類型、顯示內容等信息;
2>位圖信息數據結構,它包含有BMP圖像的寬、高、壓縮方法以及定義顏色等信息;
3>調色板,這個部分是可選的,有些位圖需要調色板,有些位圖,比如真彩色圖(24位的BMP)就不需要調色板;
4>位圖數據,這部分的內容根據BMP位圖使用的位數不同而不同,在24位圖中直接使用RGB,而其它小于24位的使用調色板中顏色所引值。也就是說?1,4,8位圖(2色圖像為8字節;16色圖像位64字節;256色圖像為1024字節。)。
2?位圖的讀取
1?//讀取文件頭???
2?fread(&bf,sizeof(bf),1,fi);??
3?//讀取文件信息頭???
4?fread(&bi,sizeof(bi),1,fi);??
??????在默認字節對齊方式下,這樣讀取BMP圖像會出現錯誤。由于系統默認情況下,結構體以4字節對齊,這樣,對于結構體中不夠4個字節的字段,會自動填充,使其也占據4個字節。對于像BMP圖像這樣采用1字節保存的文件,讀取就會出錯。
??????對于這個問題,解決辦法:
?I>?在讀取之前使用
?????#pragma?pack(1)
?????這條語句,將字節對齊方式設置為1字節對齊即可;
這樣即可
如果以上的關于BMP的知識理解了,那么就不難實現中值濾波了。
代碼如下:
/*filter.c */ #include<stdio.h> #include<stdlib.h> #include <string.h> typedef unsigned char BYTE; //顏色表定義 typedef struct tagRGBQUAD { BYTE rgbBlue;// 藍色的亮度(值范圍為0-255) BYTE rgbGreen; // 綠色的亮度(值范圍為0-255) BYTE rgbRed; // 紅色的亮度(值范圍為0-255) BYTE rgbReserved;// 保留,必須為0 } RGBQUAD; /*定義頭文件型 */ #pragma pack(1)//將字節對齊方式設置為1字節對齊 typedef struct { unsigned char id1;//位圖文件的類型,必須為BM(占用0-1字節) unsigned char id2; unsigned int filesize;//位圖文件的大小,以字節為單位(2-5字節) unsigned int reserved;// 位圖文件保留字,必須為0(6-9字節) unsigned int bitmapdataoffset;//位圖數據的起始位置,以相對于位圖(10-13字節) unsigned int bitmapheadersize;//BMP頭的大小,固定為40(14-17字節) unsigned int width;//圖片寬度;以像素為單位(18-21字節) unsigned int height;//圖片高度;以像素為單位(22-25字節) unsigned short planes;//圖片位面數,必須為1(26-27字節) unsigned short bitperpixel;//每個像素所需的位數,每個像素所需的位數(28-29字節)//只能是以下幾個數:1(雙色),4(16色),8(256色)或24(真彩色) 灰度級 unsigned int compression;//是否壓縮(30-33字節)//只能是以下幾個數:0(不壓縮),1(BI_RLE8壓縮類型),2(BI_RLE4壓縮類型) unsigned int bitmapdatasize;//位圖的大小,以字節為單位(34-37字節) unsigned int hresolution;//位圖水平分辨率,每米像素數(38-41字節) unsigned int vresolution;//位圖垂直分辨率,每米像素數(42-45字節) unsigned int colors;//位圖實際使用的顏色表中的顏色數(46-49字節) unsigned int importantcolors;//位圖顯示過程中重要的顏色數(50-53字節) //unsigned int bmiColors[1];//調色板;(54 - 57字節) unsigned char palette[256][4];//調色板 占256*4=1024字節 }BMPheader;//總大小40+14+1024=1078字節 typedef struct { BMPheader* bmpheader ; unsigned char* bitmapdata;//圖片數據; }BMPheaderfile; /*求文件長度的函數 */ long getfilesize(FILE *f) { long pos,len; pos=ftell(f);//ftell函數用于得到文件指針當前位置相對于文件首的偏移字節數 fseek(f,0,SEEK_END);//fseek函數用于移動文件指針相對于SEEK_END的偏移量為0 len=ftell(f);//len就是文件的長度 fseek(f,pos,SEEK_SET);//將文件指針移動到原來的地方 return len; } /*主函數 */ int main() { BMPheaderfile *output=(BMPheaderfile*)malloc(sizeof(BMPheaderfile));//定義一個輸出指針 unsigned char *data=NULL;FILE *fpr,*fpw; /*打開文件 */ if((fpr=fopen("start.bmp","rb"))==NULL) { printf("cannot open this file"); exit(0); } if((fpw=fopen("end1.bmp","wb"))==NULL) { printf("cannot wirte this file"); exit(0); } long length=getfilesize(fpr); printf("文件的長度為%ld\n",length); printf("文件的頭長度為%d\n",sizeof(BMPheader)); data=(unsigned char*)malloc(length*sizeof(char));//分配空間 if(0==fread(data,1,length,fpr))//讀文件,從fpr指向的文件中讀出length到data所指的內存空間去 { printf("read failed\n"); exit(0); } fclose(fpr);//釋放指針 output->bmpheader=(BMPheader*)malloc(sizeof(BMPheader)); memcpy(output->bmpheader,data,sizeof(BMPheader));//從data中拷貝sizeof(BMPheader)大小到output->bmpheader/*打印出圖像中頭文件的信息*/int height=output->bmpheader->height; int width=output->bmpheader->width; printf("filesize is %d\n",output->bmpheader->filesize); printf("該圖像每個像素所需要的位數:%d\n",output->bmpheader->bitperpixel); printf("height is %d\n",output->bmpheader->height); printf("width is %d\n",output->bmpheader->width); data=data+sizeof(BMPheader); output->bitmapdata=data;/*中值濾波算法(選擇3×3的滑動窗口)*/ unsigned char pixel[9]={0};//滑動窗口的像素值,初始為0 unsigned char mid;//中值 unsigned char temp;//中間變量 int flag; int m,i,j,x,h,w,y; for(j=1;j<height-1;j++) { for(i=1;i<width-1;i++) { //將3×3滑動窗口中的所有像素值放入pixel[m] m=0; for(y=j-1;y<=j+1;y++) for(x=i-1;x<=i+1;x++) { pixel[m]=data[y*width+x]; m=m+1; } //讓一位數組pixel[9]進行降序排列 do { flag=0;//循環結束的標志 for(m=0;m<9;m++) { if(pixel[m]<pixel[m+1]) { temp=pixel[m]; pixel[m]=pixel[m+1]; pixel[m+1]=temp; flag=1; }//if }//for } while (flag==1); mid=pixel[4]; output->bitmapdata[width*j+i]=mid; } } /* N=5的中值濾波線段狀 */ /*for(i=0;i<height;i++) { for(j=2;j<width-1;j++) { m=0; for(x=j-2;x<=j+2;x++) pixel[m++]=data[i*width+x]; for(h=0;h<5;h++) for(w=h+1;w<5;w++) { if(pixel[h]>pixel[w]) { temp=pixel[w]; pixel[w]=pixel[h]; pixel[h]=temp; } } output->bitmapdata[i*width+j]=pixel[2]; } } */ /* 保存圖像文件 */ fseek(fpw,0,0); //fseek(fpw,0,SEEK_SET) fwrite(output->bmpheader,1,sizeof(BMPheader),fpw);//寫入圖像的頭文件 fwrite(output->bitmapdata,1,length-sizeof(BMPheader),fpw);//寫入圖像的數據信息 fclose(fpw);//釋放指針 return 0; } 運行結果(左邊為源圖像,右邊為處理后的圖像)
最后,在和師兄交談的時候,我又產生了疑問,為什么中值濾波可以起到去噪的效果,這還是要回到開始中值濾波的基本原理來說,當選定一個窗口,然后對其窗口中的像素值進行排序時,用其中值去代替中心值,這樣就使每個像素值與周圍(鄰域)的像素值非常接近,這樣就使去除那些孤立的噪點,達到了很好的去噪效果。
總結
- 上一篇: MFC SetTimer函数的用法
- 下一篇: yyuc视图未更新,控制器修改可以看到,