中值滤波实现

转自http://blog.csdn.net/foreverhuylee/article/details/22732003

1、什么是中值滤波?

中值滤波是对一个滑动窗口内的诸像素灰度值排序,用其中值代替窗口中心象素的原来灰度值,它是一种非线性的图像平滑法,它对脉冲干扰级椒盐噪声的抑制效果好,在抑制随机噪声的同时能有效保护边缘少受模糊。

中值滤波可以过滤尖峰脉冲。目的在于我们对于滤波后的数据更感兴趣。滤波后的数据保留的原图像的变化趋势,同时去除了尖峰脉冲对分析造成的影响。

    以一维信号的中值滤波举例。对灰度序列80、120、90、200、100、110、70,如果按大小顺序排列,其结果为70、80、90、10O、110、120、200,其中间位置上的灰度值为10O,则该灰度序列的中值即为100。一维信号中值滤波实际上就是用中值代替规定位置(一般指原始信号序列中心位置)的信号值。对前面所举的序列而言,中值滤波的结果是用中值100替代序列80、120、90、200、100、110、70中的信号序列中心位置值200,得到的滤波序列就是80、120、90、100、100、110、70。如果在此序列中200是一个噪声信号,则用此方法即可去除这个噪声点。

    二维中值滤波算法是:对于一幅图像的象素矩阵,取以目标象素为中心的一个子矩阵窗口,这个窗口可以是3*3 ,5*5 等根据需要选取,对窗口内的象素灰度排序,取中间一个值作为目标象素的新灰度值。窗口示例如ooooxoooo上面x为目标象素,和周围o组成3*3矩阵Array,然后对这9个元素的灰度进行排序,以排序后的中间元素Array[4]为x的新灰度值,如此就完成对象素x的中值滤波,再迭代对其他需要的象素进行滤波即可。

图像处理中,中值滤波的实现方法

1:通过从 图像中的某个 采样窗口取出奇数个数据进行排序
2: 用排序后的中值取代要处理的数据即可
中值滤波的算法实现过程,重点是排序,最常用的冒泡排序~~
把滤波区间的数据从小到大进行排序,然后取中值,(如果是奇数个数据,那么中值就只有一个了,如果偶数个数据,中值有两个,可以对两个数据再求平均)

下面是一个C语言实现中值滤波的函数:

  1. unsigned char GetMedianNum(int * bArray, int iFilterLen) 
  2.     int i,j;// 循环变量 
  3.     unsigned char bTemp; 
  4.      
  5.     // 用冒泡法对数组进行排序 
  6.     for (j = 0; j < iFilterLen - 1; j ++) 
  7.     { 
  8.         for (i = 0; i < iFilterLen - j - 1; i ++) 
  9.         { 
  10.             if (bArray[i] > bArray[i + 1]) 
  11.             { 
  12.                 // 互换 
  13.                 bTemp = bArray[i]; 
  14.                 bArray[i] = bArray[i + 1]; 
  15.                 bArray[i + 1] = bTemp; 
  16.             } 
  17.         } 
  18.     } 
  19.      
  20.     // 计算中值 
  21.     if ((iFilterLen & 1) > 0) 
  22.     { 
  23.         // 数组有奇数个元素,返回中间一个元素 
  24.         bTemp = bArray[(iFilterLen + 1) / 2]; 
  25.     } 
  26.     else 
  27.     { 
  28.         // 数组有偶数个元素,返回中间两个元素平均值 
  29.         bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2; 
  30.     } 
  31.  
  32.     return bTemp; 
unsigned char GetMedianNum(int * bArray, int iFilterLen)
{
	int i,j;// 循环变量
	unsigned char bTemp;
	
	// 用冒泡法对数组进行排序
	for (j = 0; j < iFilterLen - 1; j ++)
	{
		for (i = 0; i < iFilterLen - j - 1; i ++)
		{
			if (bArray[i] > bArray[i + 1])
			{
				// 互换
				bTemp = bArray[i];
				bArray[i] = bArray[i + 1];
				bArray[i + 1] = bTemp;
			}
		}
	}
	
	// 计算中值
	if ((iFilterLen & 1) > 0)
	{
		// 数组有奇数个元素,返回中间一个元素
		bTemp = bArray[(iFilterLen + 1) / 2];
	}
	else
	{
		// 数组有偶数个元素,返回中间两个元素平均值
		bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2;
	}

	return bTemp;
}
  1. 注:bArray 是一个整形指针,我们传入的一般是一个数组,用来存储待排序的数据 
  2.     iFilterLen 是滤波器的长度 
  3.    用在图像处理中时,由于像素的取值范围是0~255,刚好是unsigned char 的范围,所以函数的返回值是unsigned char,如果我们要处理的数是float型,或其他类型,返回值也可以更改~~返回值是bTemp,也即是我们想得到的中值 
注:bArray 是一个整形指针,我们传入的一般是一个数组,用来存储待排序的数据
    iFilterLen 是滤波器的长度
   用在图像处理中时,由于像素的取值范围是0~255,刚好是unsigned char 的范围,所以函数的返回值是unsigned char,如果我们要处理的数是float型,或其他类型,返回值也可以更改~~返回值是bTemp,也即是我们想得到的中值
  1. <span style="color: rgb(51, 51, 51);">下面是一个完整的C语言程序,用在图像处理中</span> 
<span style="color:#333333;">下面是一个完整的C语言程序,用在图像处理中</span>
  1. /*************************************************************************
  2. * 函数名称:
  3. *   MedianFilter()
  4. * 参数:
  5. *   int   iFilterH         - 滤波器的高度
  6. *   int   iFilterW         - 滤波器的宽度
  7. *   int   iFilterMX        - 滤波器的中心元素X坐标
  8. *   int   iFilterMY        - 滤波器的中心元素Y坐标
  9. * 说明:
  10. *   该函数对DIB图像进行中值滤波。
  11. ************************************************************************/ 
  12. #define iFilterW 1 
  13. #define iFilterH 1 
  14. #define iFilterMX 1 
  15. #define iFilterMY 1 
  16. #define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4) 
  17.  
  18.  
  19. unsigned char GetMedianNum(int * bArray, int iFilterLen); 
  20. void MedianFilter(unsigned char *pImg1,unsigned char *pImg,int nWidth,int nHeight) 
  21. {        
  22.     unsigned char   *lpSrc;                         // 指向源图像的指针  
  23.     unsigned char   *lpDst;                         // 指向要复制区域的指针 
  24.     int         aValue[iFilterH*iFilterW];          // 指向滤波器数组的指针 
  25.     int         i,j,k,l;                            // 循环变量  
  26.     int         lLineBytes;                         // 图像每行的字节数  
  27.     lLineBytes = WIDTHBYTES(nWidth * 8); 
  28.     for ( i=0;i<nWidth;i++,pImg++ ) 
  29.         (*pImg)=0; 
  30.     // 开始中值滤波 
  31.     // 行(除去边缘几行) 
  32.     for(i = iFilterMY; i < nHeight - iFilterH + iFilterMY + 1; i++) 
  33.     { 
  34.         // 列(除去边缘几列) 
  35.         for(j = iFilterMX; j < nWidth - iFilterW + iFilterMX + 1; j++) 
  36.         { 
  37.             // 指向新DIB第i行,第j个象素的指针 
  38.             lpDst = pImg + lLineBytes * (nHeight - 1 - i) + j; 
  39.              
  40.             // 读取滤波器数组 
  41.             for (k = 0; k < iFilterH; k++) 
  42.             { 
  43.                 for (l = 0; l < iFilterW; l++) 
  44.                 { 
  45.                     // 指向DIB第i - iFilterMY + k行,第j - iFilterMX + l个象素的指针 
  46.                     lpSrc = pImg1 + lLineBytes * (nHeight - 1 - i + iFilterMY - k) + j - iFilterMX + l; 
  47.                  
  48.                     // 保存象素值 
  49.                     aValue[k * iFilterW + l] = *lpSrc; 
  50.                 } 
  51.             } 
  52.              
  53.             // 获取中值 
  54.             * lpDst = GetMedianNum(aValue, iFilterH * iFilterW); 
  55.         } 
  56.     } 
  57.  
  58.  
  59. unsigned char GetMedianNum(int * bArray, int iFilterLen) 
  60.     int     i,j;            // 循环变量 
  61.     unsigned char bTemp; 
  62.      
  63.     // 用冒泡法对数组进行排序 
  64.     for (j = 0; j < iFilterLen - 1; j ++) 
  65.     { 
  66.         for (i = 0; i < iFilterLen - j - 1; i ++) 
  67.         { 
  68.             if (bArray[i] > bArray[i + 1]) 
  69.             { 
  70.                 // 互换 
  71.                 bTemp = bArray[i]; 
  72.                 bArray[i] = bArray[i + 1]; 
  73.                 bArray[i + 1] = bTemp; 
  74.             } 
  75.         } 
  76.     } 
  77.      
  78.     // 计算中值 
  79.     if ((iFilterLen & 1) > 0) 
  80.     { 
  81.         // 数组有奇数个元素,返回中间一个元素 
  82.         bTemp = bArray[(iFilterLen + 1) / 2]; 
  83.     } 
  84.     else 
  85.     { 
  86.         // 数组有偶数个元素,返回中间两个元素平均值 
  87.         bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2; 
  88.     } 
  89.      
  90.     return bTemp; 
/*************************************************************************
 * 函数名称:
 *   MedianFilter()
 * 参数:
 *   int   iFilterH			- 滤波器的高度
 *   int   iFilterW			- 滤波器的宽度
 *   int   iFilterMX		- 滤波器的中心元素X坐标
 *   int   iFilterMY		- 滤波器的中心元素Y坐标
 * 说明:
 *   该函数对DIB图像进行中值滤波。
 ************************************************************************/
#define iFilterW 1
#define iFilterH 1
#define iFilterMX 1
#define iFilterMY 1
#define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4)


unsigned char GetMedianNum(int * bArray, int iFilterLen);
void MedianFilter(unsigned char *pImg1,unsigned char *pImg,int nWidth,int nHeight)
{		
    unsigned char   *lpSrc;			                // 指向源图像的指针	
	unsigned char   *lpDst;		                 	// 指向要复制区域的指针
	int         aValue[iFilterH*iFilterW];		    // 指向滤波器数组的指针
	int			i,j,k,l;		                    // 循环变量	
	int			lLineBytes;		                    // 图像每行的字节数	
	lLineBytes = WIDTHBYTES(nWidth * 8);
	for ( i=0;i<nWidth;i++,pImg++ )
		(*pImg)=0;
	// 开始中值滤波
	// 行(除去边缘几行)
	for(i = iFilterMY; i < nHeight - iFilterH + iFilterMY + 1; i++)
	{
		// 列(除去边缘几列)
		for(j = iFilterMX; j < nWidth - iFilterW + iFilterMX + 1; j++)
		{
			// 指向新DIB第i行,第j个象素的指针
			lpDst = pImg + lLineBytes * (nHeight - 1 - i) + j;
			
			// 读取滤波器数组
			for (k = 0; k < iFilterH; k++)
			{
				for (l = 0; l < iFilterW; l++)
				{
					// 指向DIB第i - iFilterMY + k行,第j - iFilterMX + l个象素的指针
					lpSrc = pImg1 + lLineBytes * (nHeight - 1 - i + iFilterMY - k) + j - iFilterMX + l;
				
					// 保存象素值
					aValue[k * iFilterW + l] = *lpSrc;
				}
			}
			
			// 获取中值
			* lpDst = GetMedianNum(aValue, iFilterH * iFilterW);
		}
	}

}

unsigned char GetMedianNum(int * bArray, int iFilterLen)
{
	int		i,j;			// 循环变量
	unsigned char bTemp;
	
	// 用冒泡法对数组进行排序
	for (j = 0; j < iFilterLen - 1; j ++)
	{
		for (i = 0; i < iFilterLen - j - 1; i ++)
		{
			if (bArray[i] > bArray[i + 1])
			{
				// 互换
				bTemp = bArray[i];
				bArray[i] = bArray[i + 1];
				bArray[i + 1] = bTemp;
			}
		}
	}
	
	// 计算中值
	if ((iFilterLen & 1) > 0)
	{
		// 数组有奇数个元素,返回中间一个元素
		bTemp = bArray[(iFilterLen + 1) / 2];
	}
	else
	{
		// 数组有偶数个元素,返回中间两个元素平均值
		bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2;
	}
	
	return bTemp;
}


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

举一个简单的例子:一维序列{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图像的读取

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的知识理解了,那么就不难实现中值滤波了。

代码如下:

  1. /*
  2.     filter.c
  3. */ 
  4. #include<stdio.h> 
  5. #include<stdlib.h> 
  6. #include <string.h> 
  7. typedef unsigned char BYTE;  
  8. //颜色表定义 
  9. typedef struct tagRGBQUAD { 
  10. BYTE rgbBlue;// 蓝色的亮度(值范围为0-255) 
  11. BYTE rgbGreen; // 绿色的亮度(值范围为0-255) 
  12. BYTE rgbRed; // 红色的亮度(值范围为0-255) 
  13. BYTE rgbReserved;// 保留,必须为0 
  14. } RGBQUAD; 
  15. /*
  16.     定义头文件型
  17. */ 
  18. #pragma pack(1)//将字节对齐方式设置为1字节对齐 
  19. typedef struct 
  20. unsigned char id1;//位图文件的类型,必须为BM(占用0-1字节)  
  21. unsigned char id2; 
  22. unsigned int filesize;//位图文件的大小,以字节为单位(2-5字节) 
  23. unsigned int reserved;// 位图文件保留字,必须为0(6-9字节) 
  24. unsigned int bitmapdataoffset;//位图数据的起始位置,以相对于位图(10-13字节) 
  25. unsigned int bitmapheadersize;//BMP头的大小,固定为40(14-17字节) 
  26. unsigned int width;//图片宽度;以像素为单位(18-21字节) 
  27. unsigned int height;//图片高度;以像素为单位(22-25字节) 
  28. unsigned short planes;//图片位面数,必须为1(26-27字节) 
  29. unsigned short bitperpixel;//每个像素所需的位数,每个像素所需的位数(28-29字节) 
  30.    //只能是以下几个数:1(双色),4(16色),8(256色)或24(真彩色)  灰度级 
  31. unsigned int compression;//是否压缩(30-33字节) 
  32. //只能是以下几个数:0(不压缩),1(BI_RLE8压缩类型),2(BI_RLE4压缩类型) 
  33. unsigned int bitmapdatasize;//位图的大小,以字节为单位(34-37字节) 
  34. unsigned int hresolution;//位图水平分辨率,每米像素数(38-41字节) 
  35. unsigned int vresolution;//位图垂直分辨率,每米像素数(42-45字节) 
  36. unsigned int colors;//位图实际使用的颜色表中的颜色数(46-49字节) 
  37. unsigned int importantcolors;//位图显示过程中重要的颜色数(50-53字节) 
  38. //unsigned int  bmiColors[1];//调色板;(54 - 57字节) 
  39. unsigned char palette[256][4];//调色板 占256*4=1024字节 
  40. }BMPheader;//总大小40+14+1024=1078字节 
  41. typedef struct 
  42. BMPheader* bmpheader ; 
  43. unsigned char* bitmapdata;//图片数据; 
  44. }BMPheaderfile; 
  45. /*
  46.     求文件长度的函数
  47. */ 
  48. long getfilesize(FILE *f) 
  49. long pos,len; 
  50. pos=ftell(f);//ftell函数用于得到文件指针当前位置相对于文件首的偏移字节数 
  51. fseek(f,0,SEEK_END);//fseek函数用于移动文件指针相对于SEEK_END的偏移量为0 
  52. len=ftell(f);//len就是文件的长度 
  53. fseek(f,pos,SEEK_SET);//将文件指针移动到原来的地方 
  54. return len; 
  55. /*
  56.     主函数
  57. */ 
  58. int main() 
  59. BMPheaderfile *output=(BMPheaderfile*)malloc(sizeof(BMPheaderfile));//定义一个输出指针 
  60. unsigned char *data=NULL; 
  61.     FILE *fpr,*fpw; 
  62. /*
  63.   打开文件
  64. */ 
  65. if((fpr=fopen("start.bmp","rb"))==NULL) 
  66. printf("cannot open this file"); 
  67. exit(0); 
  68. if((fpw=fopen("end1.bmp","wb"))==NULL) 
  69. printf("cannot wirte this file"); 
  70. exit(0); 
  71. long length=getfilesize(fpr); 
  72. printf("文件的长度为%ld\n",length); 
  73. printf("文件的头长度为%d\n",sizeof(BMPheader)); 
  74. data=(unsigned char*)malloc(length*sizeof(char));//分配空间 
  75. if(0==fread(data,1,length,fpr))//读文件,从fpr指向的文件中读出length到data所指的内存空间去 
  76. printf("read failed\n"); 
  77. exit(0); 
  78. fclose(fpr);//释放指针 
  79. output->bmpheader=(BMPheader*)malloc(sizeof(BMPheader)); 
  80. memcpy(output->bmpheader,data,sizeof(BMPheader));//从data中拷贝sizeof(BMPheader)大小到output->bmpheader 
  81. /*
  82. 打印出图像中头文件的信息
  83. */ 
  84.     int height=output->bmpheader->height; 
  85. int width=output->bmpheader->width; 
  86. printf("filesize is %d\n",output->bmpheader->filesize); 
  87. printf("该图像每个像素所需要的位数:%d\n",output->bmpheader->bitperpixel); 
  88. printf("height is %d\n",output->bmpheader->height); 
  89. printf("width is %d\n",output->bmpheader->width); 
  90. data=data+sizeof(BMPheader); 
  91. output->bitmapdata=data; 
  92.   /*
  93.    中值滤波算法(选择3×3的滑动窗口)
  94.   */ 
  95. unsigned char pixel[9]={0};//滑动窗口的像素值,初始为0 
  96. unsigned char mid;//中值 
  97. unsigned char temp;//中间变量 
  98. int flag; 
  99. int m,i,j,x,h,w,y; 
  100. for(j=1;j<height-1;j++) 
  101. for(i=1;i<width-1;i++) 
  102. //将3×3滑动窗口中的所有像素值放入pixel[m] 
  103. m=0; 
  104. for(y=j-1;y<=j+1;y++) 
  105. for(x=i-1;x<=i+1;x++) 
  106. pixel[m]=data[y*width+x]; 
  107. m=m+1; 
  108. //让一位数组pixel[9]进行降序排列 
  109. do  
  110. flag=0;//循环结束的标志 
  111. for(m=0;m<9;m++) 
  112. if(pixel[m]<pixel[m+1]) 
  113. temp=pixel[m]; 
  114. pixel[m]=pixel[m+1]; 
  115. pixel[m+1]=temp; 
  116. flag=1; 
  117. }//if 
  118. }//for 
  119. } while (flag==1); 
  120. mid=pixel[4]; 
  121. output->bitmapdata[width*j+i]=mid; 
  122. /*
  123. N=5的中值滤波线段状
  124. */ 
  125. /*
  126.     for(i=0;i<height;i++)
  127. {
  128. for(j=2;j<width-1;j++)
  129. {
  130. m=0;
  131. for(x=j-2;x<=j+2;x++)
  132. pixel[m++]=data[i*width+x];
  133. for(h=0;h<5;h++)
  134. for(w=h+1;w<5;w++)
  135. {
  136. if(pixel[h]>pixel[w])
  137. {
  138. temp=pixel[w];
  139. pixel[w]=pixel[h];
  140. pixel[h]=temp;
  141. }
  142. }
  143. output->bitmapdata[i*width+j]=pixel[2];
  144. }
  145. }
  146. */ 
  147. /*
  148. 保存图像文件
  149. */ 
  150. fseek(fpw,0,0);  //fseek(fpw,0,SEEK_SET) 
  151. fwrite(output->bmpheader,1,sizeof(BMPheader),fpw);//写入图像的头文件 
  152. fwrite(output->bitmapdata,1,length-sizeof(BMPheader),fpw);//写入图像的数据信息 
  153. fclose(fpw);//释放指针 
  154. return 0; 

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

举一个简单的例子:一维序列{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图像的读取

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的知识理解了,那么就不难实现中值滤波了。

代码如下:

  1. /*
  2.     filter.c
  3. */ 
  4. #include<stdio.h> 
  5. #include<stdlib.h> 
  6. #include <string.h> 
  7. typedef unsigned char BYTE;  
  8. //颜色表定义 
  9. typedef struct tagRGBQUAD { 
  10. BYTE rgbBlue;// 蓝色的亮度(值范围为0-255) 
  11. BYTE rgbGreen; // 绿色的亮度(值范围为0-255) 
  12. BYTE rgbRed; // 红色的亮度(值范围为0-255) 
  13. BYTE rgbReserved;// 保留,必须为0 
  14. } RGBQUAD; 
  15. /*
  16.     定义头文件型
  17. */ 
  18. #pragma pack(1)//将字节对齐方式设置为1字节对齐 
  19. typedef struct 
  20. unsigned char id1;//位图文件的类型,必须为BM(占用0-1字节)  
  21. unsigned char id2; 
  22. unsigned int filesize;//位图文件的大小,以字节为单位(2-5字节) 
  23. unsigned int reserved;// 位图文件保留字,必须为0(6-9字节) 
  24. unsigned int bitmapdataoffset;//位图数据的起始位置,以相对于位图(10-13字节) 
  25. unsigned int bitmapheadersize;//BMP头的大小,固定为40(14-17字节) 
  26. unsigned int width;//图片宽度;以像素为单位(18-21字节) 
  27. unsigned int height;//图片高度;以像素为单位(22-25字节) 
  28. unsigned short planes;//图片位面数,必须为1(26-27字节) 
  29. unsigned short bitperpixel;//每个像素所需的位数,每个像素所需的位数(28-29字节) 
  30.    //只能是以下几个数:1(双色),4(16色),8(256色)或24(真彩色)  灰度级 
  31. unsigned int compression;//是否压缩(30-33字节) 
  32. //只能是以下几个数:0(不压缩),1(BI_RLE8压缩类型),2(BI_RLE4压缩类型) 
  33. unsigned int bitmapdatasize;//位图的大小,以字节为单位(34-37字节) 
  34. unsigned int hresolution;//位图水平分辨率,每米像素数(38-41字节) 
  35. unsigned int vresolution;//位图垂直分辨率,每米像素数(42-45字节) 
  36. unsigned int colors;//位图实际使用的颜色表中的颜色数(46-49字节) 
  37. unsigned int importantcolors;//位图显示过程中重要的颜色数(50-53字节) 
  38. //unsigned int  bmiColors[1];//调色板;(54 - 57字节) 
  39. unsigned char palette[256][4];//调色板 占256*4=1024字节 
  40. }BMPheader;//总大小40+14+1024=1078字节 
  41. typedef struct 
  42. BMPheader* bmpheader ; 
  43. unsigned char* bitmapdata;//图片数据; 
  44. }BMPheaderfile; 
  45. /*
  46.     求文件长度的函数
  47. */ 
  48. long getfilesize(FILE *f) 
  49. long pos,len; 
  50. pos=ftell(f);//ftell函数用于得到文件指针当前位置相对于文件首的偏移字节数 
  51. fseek(f,0,SEEK_END);//fseek函数用于移动文件指针相对于SEEK_END的偏移量为0 
  52. len=ftell(f);//len就是文件的长度 
  53. fseek(f,pos,SEEK_SET);//将文件指针移动到原来的地方 
  54. return len; 
  55. /*
  56.     主函数
  57. */ 
  58. int main() 
  59. BMPheaderfile *output=(BMPheaderfile*)malloc(sizeof(BMPheaderfile));//定义一个输出指针 
  60. unsigned char *data=NULL; 
  61.     FILE *fpr,*fpw; 
  62. /*
  63.   打开文件
  64. */ 
  65. if((fpr=fopen("start.bmp","rb"))==NULL) 
  66. printf("cannot open this file"); 
  67. exit(0); 
  68. if((fpw=fopen("end1.bmp","wb"))==NULL) 
  69. printf("cannot wirte this file"); 
  70. exit(0); 
  71. long length=getfilesize(fpr); 
  72. printf("文件的长度为%ld\n",length); 
  73. printf("文件的头长度为%d\n",sizeof(BMPheader)); 
  74. data=(unsigned char*)malloc(length*sizeof(char));//分配空间 
  75. if(0==fread(data,1,length,fpr))//读文件,从fpr指向的文件中读出length到data所指的内存空间去 
  76. printf("read failed\n"); 
  77. exit(0); 
  78. fclose(fpr);//释放指针 
  79. output->bmpheader=(BMPheader*)malloc(sizeof(BMPheader)); 
  80. memcpy(output->bmpheader,data,sizeof(BMPheader));//从data中拷贝sizeof(BMPheader)大小到output->bmpheader 
  81. /*
  82. 打印出图像中头文件的信息
  83. */ 
  84.     int height=output->bmpheader->height; 
  85. int width=output->bmpheader->width; 
  86. printf("filesize is %d\n",output->bmpheader->filesize); 
  87. printf("该图像每个像素所需要的位数:%d\n",output->bmpheader->bitperpixel); 
  88. printf("height is %d\n",output->bmpheader->height); 
  89. printf("width is %d\n",output->bmpheader->width); 
  90. data=data+sizeof(BMPheader); 
  91. output->bitmapdata=data; 
  92.   /*
  93.    中值滤波算法(选择3×3的滑动窗口)
  94.   */ 
  95. unsigned char pixel[9]={0};//滑动窗口的像素值,初始为0 
  96. unsigned char mid;//中值 
  97. unsigned char temp;//中间变量 
  98. int flag; 
  99. int m,i,j,x,h,w,y; 
  100. for(j=1;j<height-1;j++) 
  101. for(i=1;i<width-1;i++) 
  102. //将3×3滑动窗口中的所有像素值放入pixel[m] 
  103. m=0; 
  104. for(y=j-1;y<=j+1;y++) 
  105. for(x=i-1;x<=i+1;x++) 
  106. pixel[m]=data[y*width+x]; 
  107. m=m+1; 
  108. //让一位数组pixel[9]进行降序排列 
  109. do  
  110. flag=0;//循环结束的标志 
  111. for(m=0;m<9;m++) 
  112. if(pixel[m]<pixel[m+1]) 
  113. temp=pixel[m]; 
  114. pixel[m]=pixel[m+1]; 
  115. pixel[m+1]=temp; 
  116. flag=1; 
  117. }//if 
  118. }//for 
  119. } while (flag==1); 
  120. mid=pixel[4]; 
  121. output->bitmapdata[width*j+i]=mid; 
  122. /*
  123. N=5的中值滤波线段状
  124. */ 
  125. /*
  126.     for(i=0;i<height;i++)
  127. {
  128. for(j=2;j<width-1;j++)
  129. {
  130. m=0;
  131. for(x=j-2;x<=j+2;x++)
  132. pixel[m++]=data[i*width+x];
  133. for(h=0;h<5;h++)
  134. for(w=h+1;w<5;w++)
  135. {
  136. if(pixel[h]>pixel[w])
  137. {
  138. temp=pixel[w];
  139. pixel[w]=pixel[h];
  140. pixel[h]=temp;
  141. }
  142. }
  143. output->bitmapdata[i*width+j]=pixel[2];
  144. }
  145. }
  146. */ 
  147. /*
  148. 保存图像文件
  149. */ 
  150. fseek(fpw,0,0);  //fseek(fpw,0,SEEK_SET) 
  151. fwrite(output->bmpheader,1,sizeof(BMPheader),fpw);//写入图像的头文件 
  152. fwrite(output->bitmapdata,1,length-sizeof(BMPheader),fpw);//写入图像的数据信息 
  153. fclose(fpw);//释放指针 
  154. return 0; 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值