Bmp图像原理与应用

BMP图像原理与应用

简介

BMP(Bitmap)是一种常见的位图图像文件格式,它使用像素点阵列来表示图像。BMP文件通常以.bmp为扩展名。

BMP格式最大的特征是没有任何的压缩,因此文件尺寸比较大,不适合网络传输,但是优点是这种图像格式中的数据读取出来不需要任何解码器解码就可以直接使用,所以为了便于大家对图像格式进行了解,就先以BMP格式进行讲解。

image-20240524124457607

文件结构

BMP文件由文件头、信息头和图像数据组成。

文件头(File Header)

BMP文件的文件头长度为14个字节,包含文件类型、大小和图像数据的偏移量等信息。

字段 长度 (字节) 描述
文件类型 2 文件类型标识,一般为"BM"
文件大小 4 整个BMP文件的大小
保留字段1 2 保留字段,一般为0
保留字段2 2 保留字段,一般为0
数据偏移量 4 图像数据相对于文件头的偏移量

信息头(Bitmap Information Header)

BMP文件的信息头长度为40个字节,包含图像的宽度、高度、颜色平面数、比特数等信息。

根据OS的不同,长度也会有所不同,Windows and OS/2 bitmap headers

具体长度可查询[https://en.wikipedia.org/wiki/BMP_file_format]

字段 长度 (字节) 描述
头大小 4 信息头的长度,一般为40
图像宽度 4 图像的宽度(以像素为单位)
图像高度 4 图像的高度(以像素为单位)
颜色平面数 2 颜色平面数,一般为1
比特数 2 每个像素的位数,常见的值为1、4、8、16、24、32
压缩方式 4 图像压缩方式,常见的值为0(不压缩)
图像大小 4 图像的大小(字节为单位),可以为0
水平分辨率 4 每米像素数,一般为0
垂直分辨率 4 每米像素数,一般为0
颜色索引数 4 使用的颜色索引数,如果为0则表示使用所有可能的颜色索引

图像数据

图像数据紧随信息头之后,按照从左到右、自下而上的顺序存储每个像素的颜色信息。根据比特数的不同,每个像素的颜色信息可能占用不同的字节数。 注意存储颜色分量的存储顺序,因为是小端存储,3 个字节的数据(蓝、绿、红)-->BGR。显示时要注意转换。

出于文件存储目的,只有每行的大小必须是 4 字节的倍数,而文件偏移量可以是任意的。

存储一行像素所需的总字节数可以按以下方式计算:

image-20240524125329726

int width_th=(width*24/8)+((4-(width*24/8)%4)%4);

示例

以下是一个简单的BMP文件的文件头示例:

文件类型: BM
文件大小: 300
保留字段1: 0
保留字段2: 0
数据偏移量: 54

以下是一个简单的BMP文件的信息头示例:

头大小: 40
图像宽度: 100
图像高度: 100
颜色平面数: 1
比特数: 24
压缩方式: 0
图像大小: 0
水平分辨率: 0
垂直分辨率: 0
颜色索引数: 0
#pragma pack(1) //字节对齐
typedef struct BITMAPFILEHEADER /* size: 14 */
{
   unsigned short bfType;	// 文件的类型,该值必需是0x4D42,也就是字符'BM'。
   unsigned int  bfSize;	// 位图文件的大小,用字节为单位
   unsigned short bfReserved1;// 保留,必须设置为0
   unsigned short bfReserved2;// 保留,必须设置为0
   unsigned int  bfOffBits;// 位图数据距离文件开头偏移量,用字节为单位
} BMPFILEHEADER;

  
typedef struct BITMAPINFOHEADER  /* size: 40 */
{
   unsigned int  biSize;			// BITMAPINFOHEADER结构所需要的字数
   unsigned int  Width;				// 图像宽度,单位为像素
   unsigned int  Height;			// 图像高度,单位为像素,负数,则说明图像是正向的
   unsigned short biPlanes;			// 为目标设备说明位面数,其值将总是被设为1
   unsigned short biBitCount;		// 一个像素占用的bit位,值位1、4、8、16、24、32
   unsigned int  biCompression;		// 压缩类型
   unsigned int  biSizeImage;		// 位图数据的大小,以字节为单位
   unsigned int  biXPelsPerMeter;	// 水平分辨率,单位 像素/米
   unsigned int  biYPelsPerMeter;	// 垂直分辨率,单位 像素/米
   unsigned int  biClrUsed;			// 当该字段设置为 0 时,颜色表中的颜色数量基于 biBitCount 字段(1 表示 2 种颜色,4 表示 16 种,8 表示 256 种,24 表示无颜色表)。
   unsigned int  biClrImportant;	// 指定颜色表的前 x 颜色对 DIB 很重要。如果其余颜色不可用,图像仍然以可接受的方式保留其含义。 
} BMPINFOHEADER;
#pragma pack() //取消字节对齐

BMP图片数据结构信息

使用HexWorkshop HexEditor(x64)打开bmp文件查看数据信息,方便运行调试

image-20240512224420993

代码练习

显示一张BMP格式的图片在LCD任意位置上

设计程序,实现在LCD上显示一张分辨率为800*480大小的24bit的bmp图片,要求图像不失真可以在开发板的LCD上显示。

int ShowBmp(int x,int y,int*lcd_mp)
{

	//1.打开待显示的BMP图像  fopen
	FILE * bmp_sss = fopen("10.bmp","rb");
	if (NULL == bmp_sss)
	{
		return -1;
	}
	//2.读取BMP文件的图像信息,获取BMP的宽和高
	BITMAPINFOHEADER headerinfosss;
	fseek(bmp_sss,14,SEEK_SET);
	fread(&headerinfosss,1,40,bmp_sss); //读取40字节
	printf("bmp width = %d,height = %d biSizeImage=%d\n",headerinfosss.biWidth,headerinfosss.biHeight);

	int width=headerinfosss.biWidth;
	int height=headerinfosss.biHeight;

	int width_th=(width*24/8)+((4-(width*24/8)%4)%4);
	int th=((4-(width*24/8)%4)%4);
	//3.读取BMP图*片的颜色分量  
	//char bmp_buf[(378*3+2)*306] = {0};
	char bmp_buf[width_th*height*3];
	fread(bmp_buf,1,width_th*height*3,bmp_sss);

	printf("读取图片颜色分量成功");
	//4.关闭BMP
	fclose(bmp_sss);


	//5.循环的把BMP图像的颜色分量依次写入到LCD的像素点中 
	int i = 0;
	int data = 0;

	for (int h = height; h > 0; h--)   //BMP文件的存储方式自下而上的
	{
		
		for (int hx =0; hx < width ; ++hx)   //从左到右
		{
			
			//把BMP图片的一个像素点的颜色分量转换为LCD屏幕的一个像素点的颜色分量格式  ARGB <--- BGR
			data |= bmp_buf[i];			//B
			data |= bmp_buf[i+1]<<8;	//G
			data |= bmp_buf[i+2]<<16;  	//R

			lcd_mp[(h+y-1)*800+hx+x] = data;  //RGB RGB RGB ....    //

			i+=3;  
			data = 0;
		}
		i+=th;    //字节补齐
		printf("i=%d\n",i);
	}
	return 0;

}

缩放BMP图片为原来的二分之一

设计算法,要求把一张任意尺寸的BMP图片等比例且不失真的缩小为原来的1/2,并生成一张新的BMP图片,要求BMP图片的路径都需要通过命令行进行传递

int ZoomBmp()
{
	//1.打开待显示的BMP图像  fopen
	FILE * bmp_fp = fopen("10.bmp","rb");
	if (NULL == bmp_fp)
	{
		return -1;
	}

	//2.读取BMP文件的图像信息,获取BMP的宽和高
	BITMAPFILEHEADER fileinfo;
	BITMAPINFOHEADER headerinfo;
	fread(&fileinfo,14,1,bmp_fp); //读取14字节
	fread(&headerinfo,40,1,bmp_fp); //读取40字节
	printf("bmp bfSize = %d\n",fileinfo.bfSize);
	printf("bmp width = %d,height = %d\n",headerinfo.biWidth,headerinfo.biHeight);

	int width=headerinfo.biWidth;
	int height=headerinfo.biHeight;
	int width_th=(width*24/8)+((4-(width*24/8)%4)%4);
    int th=((4-(width*24/8)%4)%4);
	printf("width_th=%d\n",width_th);

	//3.准备缩略图

	int width2=headerinfo.biWidth/2;   
	int height2=headerinfo.biHeight/2;
	int width_th2=(width2*24/8)+((4-(width2*24/8)%4)%4);

	fileinfo.bfSize= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +width_th2*height2;
	headerinfo.biWidth=width2;
	headerinfo.biHeight=height2;
	headerinfo.biSizeImage=width_th2*height2*3;

	printf("bmp bfSize = %d\n",fileinfo.bfSize);
	printf("bmp width = %d,height = %d\n",headerinfo.biWidth,headerinfo.biHeight);
	printf("width_th2=%d\n",width_th2);

	FILE * bmp_zoomp = fopen("z.bmp","wb");

    fwrite(&fileinfo,sizeof(fileinfo),1,bmp_zoomp); // 写入文件头
    fwrite(&headerinfo,sizeof(headerinfo),1,bmp_zoomp); // 写入信息头


	int a=ftell(bmp_zoomp);
	printf("%d\n",a);

	unsigned char buffer[3]; // 用于存储一个像素的颜色分量(RGB)

	for(int i=0;i<height;i+=2)
	{
		fseek(bmp_fp,54+width_th*i,SEEK_SET);
		//printf("%ld\n",ftell(bmp_fp));
		for(int j=0;j<width2;j++)
		{
			fread(buffer, 3, 1, bmp_fp); // 从源文件读取一个像素的颜色分量
			fwrite(buffer,3,1,bmp_zoomp);
			fseek(bmp_fp,3,SEEK_CUR);
			//printf("%d\n",j);
		}
		if(th)    //判断是否需要字节补齐
		{	
			printf("%d",th);
			unsigned char padding = 0;
			fwrite(&padding,th, 1, bmp_zoomp);	
		}
	}

	fclose(bmp_fp);
	fclose(bmp_zoomp);
	return 0;
}

posted @   sanjiudemiao  阅读(118)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示