C语言 BMP图片的中值滤波

工作用到了这方面的知识,记录一下,用大白话说明一下。

2023年3月30日新增:

BMP图片格式解析快速求数组的中值,可以了解一下BMP图片的格式,快速求数组中值  在滤波中可以减少很多时间,也推荐看下!

一、BMP图片

1、一个BMP格式的图片可以分为三块,文件头信息,位图信息,位图数据(有的带调色板,可选)。

BMP文件头:    存储了该文件的类型,大小和存放信息等。

位图信息:        放BMP图像的高,宽,bpp等信息。

位图数据:        就是每个像素的值。

BMP图片的其余详细信息在代码的/* 头文件定义 */

 下图是BMP格式图片图像的存储方式:从左到右,从下到上,此处存放的应该是位图数据

2、具体操作

其他知识:什么是中值滤波?

由上图可知,BMP图片的像素存放方式很像一个二维平面坐标系,把下图P7想象成坐标原点(0,0),在这个3*3的区域内,将这九个数排序,假设从小到大排序是:P2,P4,P8,P3,P9,P1,P5,P6,P7。中值就是P9,把图中的P5换成P9。一个像素的“滤波”就完成了,其余像素的操作跟这个一致。

有的小伙伴会发现,这样搞,那边界的,如P7,P8,P9怎么替换?这就用到边界处理的,边界拓展——复制图像边界来处理?啥意思,即用次外圈的像素替换最外圈的像素,看不懂没关系,举个例子。下图,P1,P4分别用P2,P5替换,同理P8,P9分别用P5,P6替换,这里P1,P4,P7,P8,P9都是最外圈像素,P2,P5,P6都是次内圈像素。那四个角呢,跟它直接挨着的都可以替换它,如P7,可以用P4,P5,P8替换,因为刚处理过一次后P4,P5,P8都一样了嘿。

思路:文件嘛,在计算机看来都是二进制的0,1。如何读取呢?用fread读取BMP图片的数据,读取到的数据存放到数组data中,从data中取出9个数据,排序后取中值替换即可。

代码大部分都是直接参考别人的,只有边界处理是自己加的。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
 
/* 颜色表定义,注释掉7-13行代码亦可,因为代码未用到*/
typedef unsigned char BYTE; 
typedef struct tagRGBQUAD{
	BYTE rgbBlue;
	BYTE rgbGreen;
	BYTE rgbRed;
	BYTE rgbReserved;
} RGBQUAD;
 
/* 定义头文件型 */ 
#pragma pack(1)//声明1字节对齐,因为BMP文件是1字节保存的文件,而系统默认4字节对齐。
 
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("start1.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("文件的头长度为%ld\n",sizeof(BMPheader));
    data=(unsigned char*)malloc(length*sizeof(char));//分配空间
     
    /* 读文件,从fpr指向的文件中读出1个length长度(即图片的大小)的数据到data所指的内存空间去 */
    if(0==fread(data,1,length,fpr))
    {
    	printf("read failed\n");
    	exit(0);
    }
    fclose(fpr);//释放指针
    output->bmpheader=(BMPheader*)malloc(sizeof(BMPheader));
     
    /* 从data中拷贝sizeof(BMPheader)大小到output->bmpheader */
    memcpy(output->bmpheader,data,sizeof(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;//中间变量
    unsigned char border;
    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;
    	}
    }
    /* 边界问题:拓展边界——复制图像边界 */
    /* 下边界 */
    for(int q = 2; q < width; q++)
    {
    	output->bitmapdata[q]=output->bitmapdata[width+q];	
    }
    /* 上边界 */
    for(int w = width*(width-2)+2; w < width*(width-1)-1; w++)
    {
    	output->bitmapdata[w+width]=output->bitmapdata[w];	
    }
    /* 左边界 */
    for(int e = 0; e < height; e++)
    {
    	output->bitmapdata[e*height]=output->bitmapdata[e*height+1];	
    }
    /* 右边界 */
    for(int r = 0; r < height; r++)
    {
    	output->bitmapdata[r*height-1]=output->bitmapdata[r*height-2];	
    }
 
    /*
    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;
}

原图:无边界处理:

 有边界处理:

 参考内容:数字图像处理(一)——BMP图像的介绍和读取_小贾的博客-CSDN博客_bmp图像

                         中值滤波算法_jsjliuyun的博客-CSDN博客_中值滤波算法

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值