C++实现BMP图像读写功能详解

Chloe
Chloe
发布时间:2026-10-16| 6人浏览过
原创

BMP格式图像由文件头、信息头、颜色表及像素数据构成。通过C++进行二进制读写,需严格遵循结构体定义与内存对齐规则。本文提供完整的读写代码,涵盖1至32位深度的处理,帮助开发者掌握底层图像加载方法。

了解BMP文件格式结构

BMP(Bitmap)是一种标准的位图图像文件格式,其内部数据结构固定,主要分为四个部分:

  • 位图文件头(BITMAPFILEHEADER):大小为14字节,包含文件类型(魔数0x4D42)、文件大小以及从文件开头到位图数据的偏移量。
  • 位图信息头(BITMAPINFOHEADER):大小为40字节,记录图像的宽、高、位数(Color Depth)、压缩方式及分辨率等关键参数。
  • 颜色表(Color Map):对于索引色彩模式(如1位、4位、8位),需要颜色表来映射像素值到具体RGB颜色。256色调色板占用 $256 \times 4$ 字节;真彩色图像通常无此部分或长度为0。
  • 位图数据(Data Body):存储实际的像素值。BMP格式规定每一行的字节数必须是4的倍数(DWORD Alignment),不足部分以0填充。

准备条件

编写BMP读写程序前,需确保编译环境支持标准C++库,并引入必要的系统头文件。

  • 头文件:#include <stdio.h>用于文件操作,#include <stdlib.h>用于内存分配。
  • 结构体定义:必须准确定义 BITMAPFILEHEADER 和 BITMAPINFOHEADER 结构体,大小分别为14字节和40字节。注意编译器的字节对齐设置,确保结构体布局与Windows规范一致。
  • 数据类型使用 unsigned char 存储原始像素数据,避免符号扩展问题。

第一步:读取BMP文件

目的:解析BMP头部信息并加载像素数据到内存缓冲区。

1.1 打开文件与跳过文件头

BMP是二进制文件,必须以只读二进制模式打开。由于 BITMAPFILEHEADER 固定为14字节,直接跳过即可到达信息头。

FILE *fp = fopen(bmpName, "rb");
if (!fp) return false;

// 跳过14字节的文件头
fseek(fp, sizeof(BITMAPFILEHEADER), SEEK_SET);

1.2 解析信息头

读取 BITMAPINFOHEADER,从中提取图像宽度、高度、每像素位数(biBitCount)。这些信息决定了后续如何解析数据。

BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);

int bmpWidth = head.biWidth;
int bmpHeight = head.biHeight;
int biBitCount = head.biBitCount;

1.3 计算每行字节数与处理颜色表

这是最关键的一步。由于BMP要求每行字节数为4的整数倍,计算公式为:(width * bitsPerPixel / 8 + 3) & ~3 或等价整数运算。

如果是8位灰度或彩色图,还需要读取256项颜色表。每项占4字节(B, G, R, Reserved)。

// 计算每行实际占用的字节数(含填充)
int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;

RGBQUAD *pColorTable = nullptr;
if (biBitCount == 8) {
    pColorTable = new RGBQUAD[256];
    fread(pColorTable, sizeof(RGBQUAD), 256, fp);
}

1.4 读取像素数据

根据计算出的总字节数分配内存并一次性读取。注意BMP数据存储顺序通常是从最后一行开始向上(倒序),即左下角为原点。

unsigned char *pBmpBuf = new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf, 1, lineByte * bmpHeight, fp);
fclose(fp);
return true;

第二步:写入BMP文件

目的:将内存中的像素数据按照BMP规范重新组装并保存。

2.1 构建文件头

手动填写 BITMAPFILEHEADER。其中 bfOffBits 表示文件头+信息头+颜色表的总长度,通常为54或54+1024。

BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42; // 'BM'

int colorTableSize = (biBitCount == 8) ? 1024 : 0;
fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize;
fileHead.bfSize = fileHead.bfOffBits + lineByte * height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;

fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);

2.2 构建信息头

填写宽高、位数、压缩方式(0为非压缩)、扫描线大小等信息。

BITMAPINFOHEADER head;
head.biSize = 40;
head.biWidth = width;
head.biHeight = height;
head.biPlanes = 1;
head.biBitCount = biBitCount;
head.biCompression = BI_RGB; // 非压缩
head.biSizeImage = lineByte * height;
head.biXPelsPerMeter = 0;
head.biYPelsPerMeter = 0;
head.biClrUsed = 0;
head.biClrImportant = 0;

fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);

2.3 写入颜色表与数据

若为8位图像,先写入颜色表,再写入像素数据。最后关闭文件。

if (biBitCount == 8 && pColorTable) {
    fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
}

fwrite(imgBuf, 1, lineByte * height, fp);
fclose(fp);
return true;

注意事项与排错

  • 内存泄漏:上述代码使用了 new 分配内存,调用者必须在适当时机使用 delete[] 释放 pColorTable 和 pBmpBuf,否则会导致内存泄漏。
  • 行列反转:BMP格式原生存储顺序是从下到上、从左到右。如果需要从上到下显示,需要在渲染或进一步处理时进行行交换操作。
  • 对齐检查:如果保存的文件无法打开,请检查 lineByte 的计算是否正确,以及写入数据的大小是否与 biSizeImage 一致。

总结

通过以上步骤,你可以实现基础的C++ BMP图像读写功能。掌握 BITMAPFILEHEADER 和 BITMAPINFOHEADER 的结构是核心。对于更复杂的BMP变种(如RLE压缩或多层图像),需要在此基础上增加对应的解码逻辑。以上就是C++实现BMP图像读写的详细内容,更多关于AI编程与本地部署的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。