图像增强-中值滤波

数字图像在其形成、传输记录的过程中往往会受到很多噪声的的污染,比如:椒盐噪声、高斯噪声等,为了抑制和消除这些随即产生的噪声而改善图像的质量,就需要去、对图像进行去噪处理,去噪也就是滤波处理。
中值滤波和同态滤波都是图像增强的方法,但是这两种方法是从不同的方式进行改善一副图片的质量。
中值滤波是图像平滑的一种方法 它是一种非线性平滑滤波技术,在一定条件下可以克服线性滤波带来的图像细节的模糊问题,特别是针对被椒盐噪声污染的图像。
所谓中值滤波,其中滤波就是前面讲的去噪,关键在于中值两字,中值从字面意思上讲就是中间的那个值也就是中心值。

举一个简单的例子:一维序列{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图像的存储、格式以及读取

现在讲解BMP4个组成部分:

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:不压缩;18位压缩;24位压缩)。
0022-0025:图像区数据的大小。
0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。
002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。
002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。

3.颜色表

  颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha32位位图的透明度值,一般不需要)。即首先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图像的读取

文件格式

典型的BMP图像文件由四部分组成。

1>位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息;

2>位图信息数据结构,它包含有BMP图像的宽、高、压缩方法以及定义颜色等信息;

3>调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;

4>位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其它小于24位的使用调色板中颜色所引值。也就是说 1,4,8位图(2色图像为8字节;16色图像位64字节;256色图像为1024字节。)

位图的读取

//读取文件头   

fread(&bf,sizeof(bf),1,fi);  

//读取文件信息头   

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进行降序排列
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);
 //冒泡排序
/*
<span style="white-space:pre">			</span> for(int k=0;k<9;k++)
<span style="white-space:pre">			</span> {
<span style="white-space:pre">					</span>for(int t=k+1;t<9;t++)<span style="white-space:pre">	</span>
<span style="white-space:pre">					</span>   if(pixel[k]>pixel[t])
<span style="white-space:pre">					</span>  {
<span style="white-space:pre">						</span>    temp=pixel[k];
<span style="white-space:pre">							</span>pixel[k]=pixel[t];
<span style="white-space:pre">							</span>pixel[t]=temp;
<span style="white-space:pre">					</span>  }
<span style="white-space:pre">			</span> }
*/
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;
}


代码地址 <href http://download.csdn.net/detail/cry_smile_hate/9492478>

猜你喜欢

转载自blog.csdn.net/cry_smile_hate/article/details/51149214