BMP是英文Bitmap(位图)的简写,这种格式没有对图像数据进行压缩,文件里保存了各个像素实际的RGB值,理解起来比较直观,对学习图像文件格式的入门很有帮助。
下面列出文件的结构
示例代码bmp.h
#ifndef __BMP_H__
#define __BMP_H__
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;
// bmp文件头(bmp file header)
// 提供文件的格式、大小等信息
typedef struct
{
WORD bfType; // 文件类型,BMP文件用0x4d42表示,即字符串"BM"。
DWORD bfSize; // 文件大小,单位为字节
WORD bfReserved1; // 保留,目前必须设置为0
WORD bfReserved2; // 保留,目前必须设置为0
DWORD bfOffBits; // 从文件开头到图像数据的偏移量
}BITMAPFILEHEADER;
// 位图信息头(bitmap information)
// 提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
typedef struct
{
DWORD biSize; // 位图信息头的长度,40字节
LONG biWidth; // 位图的宽度
LONG biHeight; // 位图的高度
WORD biPlanes; // 目标设备级别,必须为1
WORD biBitCount; // 每个像素所占位数(bit),二值图像为1,灰度图像为8,真彩色图像为24
DWORD biCompression; // 位图压缩类型
DWORD biSizeImage; // 实际的位图数据占用的字节数
LONG biXPelsPerMeter; // 指定目标设备的水平分辨率
LONG biYPelsPerMeter; // 指定目标设备的垂直分辨率
DWORD biClrUsed; // 位图实际用到的颜色数
DWORD biClrImportant; // 位图显示过程中重要的颜色数
}BITMAPINFOHEADER;
// 调色板(color palette)
// 可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
// 24位的真彩色不需要这部分
typedef struct
{
BYTE rgbBlue; // 蓝色分量
BYTE rgbGreen; // 绿色分量
BYTE rgbRed; // 红色分量
BYTE rgbReserved; // 保留字节,暂时不用
}RGBQUAD;
bool SaveRgb888ToBmpSaveRgb888ToBmp(char * fileName,unsigned char *imgBuffer, int imWidth, int imHeight);
#endif
示例代码bmp.cpp
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include"bmp.h"
int SaveRgb888ToBmp(char * fileName, unsigned char *imgBuffer, int imgWidth, int imgHeight)
{
if (!imgBuffer)
{
return -1;
}
int biBitCount = 8;// RGB888格式,每个颜色通道8bit
int colorTableSize = 0;// RGB888 不需要灰度图像颜色表
// windows规定一个扫描行所占的字节数必须是4的倍数,不足4的倍数则要对其进行扩充。
int lineByte = (imgWidth * biBitCount / 8 + 3) / 4 * 4;
FILE *fp = fopen(fileName, "wb");
if (!fp)
{
return -1;
}
// 填充文件头
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
colorTableSize + (lineByte * imHeight);
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize;
// 写文件头
fwrite(&fileHead , sizeof(LBITMAPfileHeadER), 1, fp);
// 填充文件信息头
BITMAPINFOHEADER infoHead;
infoHead.biSize = sizeof(BITMAPINFOHEADER); //40;
infoHead.biWidth = imgWidth;
infoHead.biHeight = 0 - imgHeight; // 正数表示RGB图像数据行的顺序倒置,负数表示RGB图像数据行的顺序正常
infoHead.biBitCount = biBitCount;
infoHead.biPlanes = 1;
infoHead.biCompression = 0;
infoHead.biSizeImage = lineByte * imgHeight;
infoHead.biXPelsPerMeter = 0;
infoHead.biYPelsPerMeter = 0;
infoHead.biClrUsed = 0;
infoHead.biClrImportant = 0;
//写文件信息头
fwrite(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);
// RGB888不需要颜色表
/*
RGBQUAD *pColorTable = new RGBQUAD[256];
for (int i = 0 ; i < 256 ; i++)
{
pColorTable[i].rgbBlue = i;
pColorTable[i].rgbGreen = i;
pColorTable[i].rgbRed = i;
//pColorTable[i].rgbReserved = 0;
}
fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
*/
// RGB to BGR, 这里需要把R和B的顺序兑换一下,具体原因还不知道
unsigned char tmp;
long imgSize = imgWidth * imgHeight * 3;
for(unsigned long i=0; i<=imgSize-3; i+=3)
{
tmp = imgBuffer[i];
imgBuffer[i] = imgBuffer[i+2];
imgBuffer[i+2] = tmp;
}
// 写真正的图像数据
fwrite(imgBuffer, imgHeight*lineByte, 1, fp);
fclose(fp);
return 0;
}