C++:实现BMP图像的读入与复制操作

BMP是英文Bitmap位图)的简写,也被称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。

通过本文,我们将学习BMP的存储结构以及如何通过编程语言C++对BMP格式存储的图像进行基本的读写操作(本文描述复制copy操作,针对24位图和32位图)。

一.BMP文件结构

为了顺利实现BMP图像copy,我们必须先了解BMP文件是如何进行存储的,其内部的编码结构是怎样的。

BMP文件图像由若干大小固定(文件头)和大小可变的结构体按一定的顺序够成,由于该文件格式几经演进,这些结构体的版本也很多。

位图文件由以下结构体依次构成:

(来自WIKI百科)

(翻译版)

其中可选表示有的BMP文件可能不存在对应的结构体,我们本篇文章所讨论的BMP图像只考虑必选的结构体。接下来我们将按顺序介绍Bitmap file header(位图文件头),DIB header(DIB头),Color table(调色板),Pixel Array(像素数组)这个重要的结构体。

Ⅰ.位图文件头(Bitmap file header):

这部分数据块位于文件开头,大小为14个字节,用于进行文件的识别与部分基本信息的存储。典型的应用程序会首先读取这部分数据以确保其为BMP文件且没有损坏。这部分所有的整数值都以小端序存放(即最低有效位前置)。

在C++的windows.h库中,有相应的BITMAPFILEHEADER结构体。(当然,你也可以选择自己编写一个位图文件头的结构体,但要注意成员变量的大小和顺序)

①bfType:2字节

用于标识BMP和DIB文件的魔数,一般为0x42 0x4D即ASCⅡ码对应的B M,BMP文件对应的bfType为0x4D42(B为low字节,M为high字节所以bfType=0x4D42,而不是0x424D)

由此我们可以编写程序来判断读入的是否为BMP文件

②bfSize:4字节

用于表示BMP文件的大小(单位为字节)

顺带地,我们介绍一下BMP文件的存储算法:

BMP文件通常是不进行压缩操作的,因此它们通常比一幅图像的压缩图像文件格式大很多。

根据颜色深度的不同,BMP图像上的一个像素可以用一个或多个字节(一个字节包含了8个数据位)来进行表示,一个像素对应的字节数为 位深度/8 。图片浏览器等基于字节的ASCⅡ码值来计算像素的颜色,然后从调色板中读出相应的值。

位深度为n说明可以有2^{n}种颜色,则n位2^{n}种颜色的包含调色板的位图近似字节数可以用下面的公式计算:

BMP的文件大小≈

width,height单位都是像素。

其中14字节对应的是位图文件头的大小,40字节对应的是DIB文件头的大小,4\cdot 2^{n}对应的是彩色调色板的大小(只在位深度小于8时存在)。

(注意:调色板仅仅定义了图像所用的颜色,所以实际彩色调色板将小于4\cdot 2^{n}

若位图文件不包含调色板,如我们接下来要讨论的24位和32位位图,那么位图的近似字节数公式可以修改为:

BMP的文件大小≈

,width,height单位都是像素。

③bfReserved1:2字节

会被保留,实际值因创建程序而异。

④bfReserved2:2字节

会被保留,实际值因创建程序而异。

⑤bfOffBits:4字节

位图数据(像素数组)的地址偏移,也就是像素数组起始地址。

Ⅱ.DIB头(DIB Header):

这部分内容告诉应用程序BMP图像的详细信息,在屏幕上显示图像会使用到这部分信息。由于微软对DIB格式进行了多次的修改,这部分内容对应的字节数有不同的版本,我们现在通常使用的系统对应了40字节。

下表为不同版本的DIB头:

在C++的windows.h库中,有相应的BITMAPINFOHEADER结构体。(当然,你也可以选择自己编写一个位图文件头的结构体,但要注意成员变量的大小和顺序)

①biSize:4字节

标明该DIB头文件的大小(通常固定为40字节)

②biWidth:4字节

位图宽度,单位为像素(有符号整数)

③biHeight:4字节

位图高度,单位为像素(有符号整数)

④biPlanes:2字节

色彩平面数;只有1为有效值

⑤biBitCount:2字节

每个像素所占用的位数,即图像的色深,典型值为1、4、8、16、24、32

⑥biCompression:4字节

所使用的压缩方式,详情见下表

⑦biSizeImage:4字节

图像大小,即指原始位图数据的大小。

⑧biXPlesPerMeter:4字节

图像的横向分辨率,单位为像素每米(有符号整数)

⑨biYpelsPerMeter:4字节

图像的纵向分辨率,单位为像素每米(有符号整数)

⑩biClrUsed:4字节

调色版的颜色数,为0时表示颜色数就是默认的2^{n}

⑪biClrImportant:4字节

重要颜色数,为0时表明所有颜色都是重要的,一般BMP图像不使用本项

Ⅲ.调色板(Color table):

这部分只在BMP图像的位深度小于8时存在,它定义了图像所使用的颜色。BMP图像像素区的像素是一个接一个存储的,每个像素使用一个或者多个字节的值表示,所以调色板的目的就是要告诉应用程序这些值所对应的实际颜色。、

典型的BMP图像文件使用RGB彩色模型。在这种模型中,每种颜色都是由强度不同(从0到最大强度)的红色(R),绿色(G),蓝色(B)组成,也就是说,每种颜色可以用红色、绿色、蓝色的值所定义。

在C++的windows.h库中,有相应的RGBQUAD结构体。(当然,你也可以选择自己编写一个位图文件头的结构体,但要注意成员变量的大小和顺序)

每个条目用来描述一种颜色,包含了四个字节,其中三个表示蓝色、绿色、红色,第四个字节没有使用(大多是应用程序将它设置为0);对于每个字节,数值0表示该颜色分量在当前的颜色中没有使用,而数值255表示这种颜色分量使用的最大强度。

(24位图像素对应3个字节,32位图像素对应4个字节)

Ⅳ.像素数组(Pixel Array):

这个部分逐个像素表示图像,每个像素使用一个或者多个字节表示。

通常,像素是从下到上,从左到右保存的。(注:如果使用的不是BITMAPCOREHEADER,那么未压缩的Windows位图还可以从上到下进行存储,此时图像高度为负值。)

每一行的末尾需要填充若干个字节的数据(不一定为0)使得该行的长度为4字节的倍数(扫描行)。像素数组读入内存后,每一行的起始地址必须为4的倍数,这个限制仅针对内存中的像素数组,针对存储时,仅要求每一行的大小为4字节的倍数,对文件的偏移没有限制。

例如:对于24位BMP图像,其一个像素对应3个字节,如果他的宽度为1个像素,一行就有3个字节,那么我们需要在该行末尾填充1字节;如果宽度为2个像素,一行就有6个字节,那么我们需要在该行末尾填充2个字节;宽度为4个像素时,一行就有12个字节,那么我们就不需要填充。

二.C++实现BMP格式图像读入

注:本文使用Visual Studio 2022进行代码编写,导入了<iostream>,<windows.h>,<fstream>库,设定对齐边界为2。

接下来我们先定义初始BMP文件的文件头变量,DIB头变量,像素数组。

BITMAPFILEHEADER结构体

BITMAPINFOHEADER结构体

两种RGB像素结构体(分别对应24位与32位)

定义全局变量is32_flag用于判断是否为32位BMP图像,如果是32位BMP图像,在DIB头之后还存在一段长度为84字节的信息,我们通过自定义Plus结构体来处理这段信息。

Plus结构体

再定义former_width用于表示原图宽度,former_height用于表示原图高度

定义bmp_in_address用于存储原图像地址(相对路径/绝对路径)

使用者需传入原图像地址,之后我们调用bmp_read函数来进行bmp文件的读入,bmp_read函数返回值为bool类型,如果返回值为true说明正确打开文件,否则说明打开文件遇到了问题,返回false,终止程序。

接下来我们就可以开始读入BMP图像了,首先打开对应地址的文件,判断对应文件是否存在:

然后我们读入头文件,并判断该文件类型是否为BMP格式:

接着我们读入DIB头,并输出头文件和DIB头里面的关键信息(用于debug):

接下来我们需要判断是否有读入Plus结构体的必要,即对BMP图像位深度进行判断:

然后我们文件操作的读指针就来到了像素区,先记录原图像的宽度和高度:

前面提到,BMP文件有补齐一行字节数为4的倍数的操作,所以我们需要计算扫描一行所需要的字节数,并计算出补齐所花的字节数,这是一个简单的数学处理:

然后我们就可以开始对像素数组内存储的信息进行读取了,分为24位和32位两种情况:

这样就完成了BMP文件的常规读取操作,最后不要忘记关闭文件,返回true表示成功读入文件。

运行一下程序,所用的图像为:

得到输出:

符合预期,说明读入成功

附上bmp_read函数的整体代码:

bool bmp_read(string s)
{
	//跟随地址以二进制读入bmp文件
	ifstream former_bmp_info(s, ios::in | ios::binary);
	if (!former_bmp_info)//若无法打开对应文件
	{
		cout << "Open File Failed!" << endl;
		former_bmp_info.close();
		return false;
	}
	else
	{
		cout << "Open File Successfully!" << endl << endl;
	}

	former_bmp_info.read((char*)&former_bmp_FILE_Head, sizeof(BITMAPFILEHEADER));//读文件头
	//判断是否正确读入bmp文件
	if (former_bmp_FILE_Head.bfType != 0x4D42)
	{
		cout << "该文件非BMP格式!" << endl;
		return false;
	}
	else
	{
		cout << "该文件为BMP格式!" << endl << endl;
	}

	former_bmp_info.read((char*)&former_bmp_DIB_Head, sizeof(BITMAPINFOHEADER));//读DIB头


	cout << "位图文件头:" << endl;
	cout << "DIB头占用字节数:" << "14 bytes" << endl;
	cout << "位图文件类型: 0x" << hex << former_bmp_FILE_Head.bfType << endl;//16进制
	cout << "位图文件大小: " << dec << former_bmp_FILE_Head.bfSize << " bytes" << endl;
	cout << "偏移字节数: " << former_bmp_FILE_Head.bfOffBits << endl;
	cout << endl;


	cout << "DIB头占用字节数:" << former_bmp_DIB_Head.biSize << " bytes" << endl;
	cout << "位图宽度: " << former_bmp_DIB_Head.biWidth << endl;
	cout << "位图高度: " << former_bmp_DIB_Head.biHeight << endl;
	cout << "位图压缩类型: " << former_bmp_DIB_Head.biCompression << endl;
	cout << "位图像素位数: " << former_bmp_DIB_Head.biBitCount << endl;
	cout << "位图数据总占用字节数: " << former_bmp_DIB_Head.biSizeImage << " bytes" << endl;
	cout << endl;

	if (former_bmp_DIB_Head.biBitCount == 32)
	{
		is32_flag = true;
		cout << "输入的图片为32位" << endl;
		former_bmp_info.read((char*)&former_bmp_Plus, sizeof(Plus));//32位图像会多一个Plus部分
	}

	former_width = former_bmp_DIB_Head.biWidth;
	former_height = former_bmp_DIB_Head.biHeight;

	//计算扫描一行所需要的字节数
	int former_line_byte = (former_bmp_DIB_Head.biBitCount * former_width / 8 + 3) / 4 * 4;
	int need_zeros = former_line_byte - former_bmp_DIB_Head.biBitCount * former_width / 8;

	if (is32_flag)
	{
		former_rgb_data2 = new  RGB_data2[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			former_bmp_info.read((char*)former_rgb_data2 + i * former_width * former_bmp_DIB_Head.biBitCount / 8, former_bmp_DIB_Head.biBitCount / 8 * former_width);
			former_bmp_info.seekg(need_zeros, ios::cur);//忽略掉补位
		}
	}
	else
	{
		former_rgb_data1 = new  RGB_data[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			former_bmp_info.read((char*)former_rgb_data1 + i * former_width * former_bmp_DIB_Head.biBitCount / 8, former_bmp_DIB_Head.biBitCount / 8 * former_width);
			former_bmp_info.seekg(need_zeros, ios::cur);//忽略掉补位
		}
	}
	former_bmp_info.close();
	return true;
}

三、C++实现BMP格式图像复制

接下来再讲如何在成功读入BMP格式图像的基础上对BMP格式图像进行复制。

与读入操作类似的,我们需要一个地址(相对路径/绝对路径)来输出我们复制后的文件,定义为bmp_out_address

使用者需传入输出的图像地址,之后我们调用bmp_copy函数来进行bmp文件的复制输出,bmp_copy函数返回值为bool类型,如果返回值为true说明正确输出文件,否则说明输出文件遇到了问题,返回false,终止程序。

接下来我们就可以开始复制BMP图像了:

首先直接进行赋值操作,由于是复制,可以直接把原先的文件头和DIB头传给后来的文件头和DIB头,然后创建输出文件流,判断能否正确打开文件:

接着我们读入文件头和DIB头:

然后我们判断是否需要读入Plus结构体:

然后我们考虑扫描行的字节数以及补齐操作:

然后我们就可以开始进行像素数组的复制了,分为24位和32位两种情况:

这样就完成了BMP文件的复制操作,最后记得关闭文件,返回true表示成功复制文件。

运行一下程序,所用的24位图像为:

得到如下输出:

检查文件目录,发现复制成功:

运行一下程序,所用的32位图像为:


得到如下输出:

检查文件目录,发现复制成功:

附上bmp_copy函数的整体代码:

bool bmp_copy(string s)
{
	BITMAPFILEHEADER copy_bmp_FILE_Head = former_bmp_FILE_Head;
	BITMAPINFOHEADER copy_bmp_DIB_Head = former_bmp_DIB_Head;
	ofstream bmp_out(s, ios::out | ios::binary);
	if (!bmp_out)
	{
		cout << "Creat File Failed!" << endl;
		bmp_out.close();
		return false;
	}
	else
	{
		cout << "Creat File Successfully!" << endl;
	}
	bmp_out.write((char*)&copy_bmp_FILE_Head, sizeof(BITMAPFILEHEADER));
	bmp_out.write((char*)&copy_bmp_DIB_Head, sizeof(BITMAPINFOHEADER));
	if(is32_flag)bmp_out.write((char*)&former_bmp_Plus, sizeof(Plus));

	int copy_line_byte = (copy_bmp_DIB_Head.biBitCount * copy_bmp_DIB_Head.biWidth / 8 + 3) / 4 * 4;
	int need_zeros = copy_line_byte - copy_bmp_DIB_Head.biBitCount * copy_bmp_DIB_Head.biWidth / 8;
	string temp_zero = "00000";//用0补齐,当然你也可以自定义补齐语句,这没有大碍

	if (is32_flag)
	{
		RGB_data2* copy_rgb_data = new RGB_data2[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			for (int j = 0; j < former_width; j++)
			{
				*(copy_rgb_data + i * former_width + j) = *(former_rgb_data2 + i * former_width + j);
			}
		}
		//写入bmp文件
		for (int i = 0; i < former_height; i++) {
			bmp_out.write((char*)copy_rgb_data + i * former_width * copy_bmp_DIB_Head.biBitCount / 8, copy_bmp_DIB_Head.biBitCount / 8 * former_width);
			bmp_out.write((char*)&temp_zero, need_zeros);
		}
		delete[] copy_rgb_data;
	}
	else
	{
		RGB_data* copy_rgb_data = new RGB_data[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			for (int j = 0; j < former_width; j++)
			{
				*(copy_rgb_data + i * former_width + j) = *(former_rgb_data1 + i * former_width + j);
			}
		}
		//写入bmp文件
		for (int i = 0; i < former_height; i++) {
			bmp_out.write((char*)copy_rgb_data + i * former_width * copy_bmp_DIB_Head.biBitCount / 8, copy_bmp_DIB_Head.biBitCount / 8 * former_width);
			bmp_out.write((char*)&temp_zero, need_zeros);
		}
		delete[] copy_rgb_data;
	}
	bmp_out.close();
	return true;
}

至此,BMP文件的读入和复制输出操作已经讲解完毕,关于更复杂的旋转操作会在后续文章中给出具体的代码实现,感谢浏览!

最后附上源代码:

#include <iostream>
#include<windows.h>
#include<fstream>
#pragma pack(2)//设置对齐边界为2

using namespace std;

struct RGB_data
{
	BYTE blue;
	BYTE red;
	BYTE green;
};
struct RGB_data2
{
	BYTE blue;
	BYTE red;
	BYTE green;
	BYTE rgbReserved;
};
struct Plus//32位图会额外有一段
{
	DWORD        bV5RedMask;
	DWORD        bV5GreenMask;
	DWORD        bV5BlueMask;
	DWORD        bV5AlphaMask;
	DWORD        bV5CSType;
	CIEXYZTRIPLE bV5Endpoints;
	DWORD        bV5GammaRed;
	DWORD        bV5GammaGreen;
	DWORD        bV5GammaBlue;
	DWORD        bV5Intent;
	DWORD        bV5ProfileData;
	DWORD        bV5ProfileSize;
	DWORD        bV5Reserved;
};


bool is32_flag = false;

string bmp_in_address;
string bmp_out_address;

BITMAPFILEHEADER former_bmp_FILE_Head;
BITMAPINFOHEADER former_bmp_DIB_Head;
RGB_data* former_rgb_data1;//用于24位BMP图像
RGB_data2* former_rgb_data2;//用于32位BMP图像

Plus former_bmp_Plus;
int former_width;//原图宽度
int former_height;//原图高度




bool bmp_read(string s)
{
	//跟随地址以二进制读入bmp文件
	ifstream former_bmp_info(s, ios::in | ios::binary);
	if (!former_bmp_info)//若无法打开对应文件
	{
		cout << "Open File Failed!" << endl;
		former_bmp_info.close();
		return false;
	}
	else
	{
		cout << "Open File Successfully!" << endl << endl;
	}

	former_bmp_info.read((char*)&former_bmp_FILE_Head, sizeof(BITMAPFILEHEADER));//读文件头
	//判断是否正确读入bmp文件
	if (former_bmp_FILE_Head.bfType != 0x4D42)
	{
		cout << "该文件非BMP格式!" << endl;
		return false;
	}
	else
	{
		cout << "该文件为BMP格式!" << endl << endl;
	}

	former_bmp_info.read((char*)&former_bmp_DIB_Head, sizeof(BITMAPINFOHEADER));//读DIB头


	cout << "位图文件头:" << endl;
	cout << "DIB头占用字节数:" << "14 bytes" << endl;
	cout << "位图文件类型: 0x" << hex << former_bmp_FILE_Head.bfType << endl;//16进制
	cout << "位图文件大小: " << dec << former_bmp_FILE_Head.bfSize << " bytes" << endl;
	cout << "偏移字节数: " << former_bmp_FILE_Head.bfOffBits << endl;
	cout << endl;


	cout << "DIB头占用字节数:" << former_bmp_DIB_Head.biSize << " bytes" << endl;
	cout << "位图宽度: " << former_bmp_DIB_Head.biWidth << endl;
	cout << "位图高度: " << former_bmp_DIB_Head.biHeight << endl;
	cout << "位图压缩类型: " << former_bmp_DIB_Head.biCompression << endl;
	cout << "位图像素位数: " << former_bmp_DIB_Head.biBitCount << endl;
	cout << "位图数据总占用字节数: " << former_bmp_DIB_Head.biSizeImage << " bytes" << endl;
	cout << endl;

	if (former_bmp_DIB_Head.biBitCount == 32)
	{
		is32_flag = true;
		cout << "输入的图片为32位" << endl;
		former_bmp_info.read((char*)&former_bmp_Plus, sizeof(Plus));//32位图像会多一个Plus部分
	}

	former_width = former_bmp_DIB_Head.biWidth;
	former_height = former_bmp_DIB_Head.biHeight;

	//计算扫描一行所需要的字节数
	int former_line_byte = (former_bmp_DIB_Head.biBitCount * former_width / 8 + 3) / 4 * 4;
	int need_zeros = former_line_byte - former_bmp_DIB_Head.biBitCount * former_width / 8;

	if (is32_flag)
	{
		former_rgb_data2 = new  RGB_data2[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			former_bmp_info.read((char*)former_rgb_data2 + i * former_width * former_bmp_DIB_Head.biBitCount / 8, former_bmp_DIB_Head.biBitCount / 8 * former_width);
			former_bmp_info.seekg(need_zeros, ios::cur);//忽略掉补位
		}
	}
	else
	{
		former_rgb_data1 = new  RGB_data[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			former_bmp_info.read((char*)former_rgb_data1 + i * former_width * former_bmp_DIB_Head.biBitCount / 8, former_bmp_DIB_Head.biBitCount / 8 * former_width);
			former_bmp_info.seekg(need_zeros, ios::cur);//忽略掉补位
		}
	}
	former_bmp_info.close();
	return true;
}

bool bmp_copy(string s)
{
	BITMAPFILEHEADER copy_bmp_FILE_Head = former_bmp_FILE_Head;
	BITMAPINFOHEADER copy_bmp_DIB_Head = former_bmp_DIB_Head;
	ofstream bmp_out(s, ios::out | ios::binary);
	if (!bmp_out)
	{
		cout << "Creat File Failed!" << endl;
		bmp_out.close();
		return false;
	}
	else
	{
		cout << "Creat File Successfully!" << endl;
	}
	bmp_out.write((char*)&copy_bmp_FILE_Head, sizeof(BITMAPFILEHEADER));
	bmp_out.write((char*)&copy_bmp_DIB_Head, sizeof(BITMAPINFOHEADER));
	if(is32_flag)bmp_out.write((char*)&former_bmp_Plus, sizeof(Plus));

	int copy_line_byte = (copy_bmp_DIB_Head.biBitCount * copy_bmp_DIB_Head.biWidth / 8 + 3) / 4 * 4;
	int need_zeros = copy_line_byte - copy_bmp_DIB_Head.biBitCount * copy_bmp_DIB_Head.biWidth / 8;
	string temp_zero = "00000";//用0补齐,当然你也可以自定义补齐语句,这没有大碍

	if (is32_flag)
	{
		RGB_data2* copy_rgb_data = new RGB_data2[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			for (int j = 0; j < former_width; j++)
			{
				*(copy_rgb_data + i * former_width + j) = *(former_rgb_data2 + i * former_width + j);
			}
		}
		//写入bmp文件
		for (int i = 0; i < former_height; i++) {
			bmp_out.write((char*)copy_rgb_data + i * former_width * copy_bmp_DIB_Head.biBitCount / 8, copy_bmp_DIB_Head.biBitCount / 8 * former_width);
			bmp_out.write((char*)&temp_zero, need_zeros);
		}
		delete[] copy_rgb_data;
	}
	else
	{
		RGB_data* copy_rgb_data = new RGB_data[former_width * former_height];
		for (int i = 0; i < former_height; i++)
		{
			for (int j = 0; j < former_width; j++)
			{
				*(copy_rgb_data + i * former_width + j) = *(former_rgb_data1 + i * former_width + j);
			}
		}
		//写入bmp文件
		for (int i = 0; i < former_height; i++) {
			bmp_out.write((char*)copy_rgb_data + i * former_width * copy_bmp_DIB_Head.biBitCount / 8, copy_bmp_DIB_Head.biBitCount / 8 * former_width);
			bmp_out.write((char*)&temp_zero, need_zeros);
		}
		delete[] copy_rgb_data;
	}
	bmp_out.close();
	return true;
}

int main()
{
	cout << "请输入你要查看的BMP图像的地址:" << endl;
	cin >> bmp_in_address;
	cout << endl;
	if (!bmp_read(bmp_in_address))return 0;

	cout << endl;

	cout << "请输入你要输出的BMP图像的地址:" << endl;
	cin >> bmp_out_address;
	if (!bmp_copy(bmp_out_address))cout << "复制文件失败" << endl;
	else cout << "复制文件成功" << endl;
	return 0;
}

  • 24
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是C++语言实现BMP文件的读写代码示例: ```c++ #include <iostream> #include <fstream> #include <vector> using namespace std; #pragma pack(push, 1) // BMP文件头 struct BMPHeader { char signature[2]; uint32_t fileSize; uint32_t reserved; uint32_t dataOffset; }; // BMP信息头 struct BMPInfoHeader { uint32_t size; uint32_t width; uint32_t height; uint16_t planes; uint16_t bitCount; uint32_t compression; uint32_t imageSize; uint32_t xPixelsPerMeter; uint32_t yPixelsPerMeter; uint32_t colorsUsed; uint32_t colorsImportant; }; #pragma pack(pop) // 读取BMP文件 vector<uint8_t> readBMP(const char *filename, BMPInfoHeader &infoHeader) { ifstream file(filename, ios::binary); BMPHeader header; file.read(reinterpret_cast<char *>(&header), sizeof(BMPHeader)); if (header.signature[0] != 'B' || header.signature[1] != 'M') { cerr << "Error: Invalid BMP file" << endl; return vector<uint8_t>(); } file.read(reinterpret_cast<char *>(&infoHeader), sizeof(BMPInfoHeader)); if (infoHeader.bitCount != 24) { cerr << "Error: Only support 24-bit BMP file" << endl; return vector<uint8_t>(); } file.seekg(header.dataOffset, ios::beg); vector<uint8_t> imageData(infoHeader.imageSize); file.read(reinterpret_cast<char *>(imageData.data()), infoHeader.imageSize); file.close(); return imageData; } // 写入BMP文件 bool writeBMP(const char *filename, const BMPInfoHeader &infoHeader, const vector<uint8_t> &imageData) { ofstream file(filename, ios::binary); BMPHeader header; header.signature[0] = 'B'; header.signature[1] = 'M'; header.fileSize = sizeof(BMPHeader) + sizeof(BMPInfoHeader) + imageData.size(); header.reserved = 0; header.dataOffset = sizeof(BMPHeader) + sizeof(BMPInfoHeader); file.write(reinterpret_cast<char *>(&header), sizeof(BMPHeader)); file.write(reinterpret_cast<const char *>(&infoHeader), sizeof(BMPInfoHeader)); file.write(reinterpret_cast<const char *>(imageData.data()), imageData.size()); file.close(); return true; } int main() { BMPInfoHeader infoHeader; vector<uint8_t> imageData = readBMP("input.bmp", infoHeader); // 处理BMP图像数据 writeBMP("output.bmp", infoHeader, imageData); return 0; } ``` 该代码实现BMP文件的读取和写入,使用了C++中的结构体和文件流操作。其中,`readBMP`函数可以读取BMP文件的文件头和图像数据,返回一个字节向量,`writeBMP`函数可以将图像数据写入BMP文件。 为了测试,可以使用24位BMP格式的图像文件作为输入文件,该程序将读入这个文件,然后将图像数据处理后写入一个新的文件。 需要注意的是,BMP文件是二进制文件,因此在读取和写入文件时需要使用二进制模式打开文件。 另外,由于BMP文件中的元素存储顺序可能与机器的存储顺序不同,因此在读取和写入文件时需要使用`pragma pack`指令来确保结构体的对齐方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值