BMP文件的读取和保存

来源:互联网 发布:万科金域名城业主论坛 编辑:程序博客网 时间:2024/04/30 07:26

BMP文件介绍

BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,采用位映射存储格式,但是不压缩的特性也带来了读取和保存上的方便。BMP文件的图像深度可选lbit、4bit、8bit、24bit以及32bit。BMP文件存储数据时,图像是以扫描行为单位进行存储。行内像素从左至右扫描,windows平台行扫描从下至上。Windows平台上的图形图像软件都支持BMP文件的读取和保存。

BMP文件结构

数据类型定义

using BYTE = unsigned char;        //1Byteusing WORD = unsigned short int;   //2Byteusing DWORD = unsigned long int;   //4Byteusing LONG = long;                 //4Byteusing LLONG = long long;           //8Byte

BMP文件头结构体

typedef struct tagBITMAPFILEHEADER{    WORD bfType;    DWORD bfSize;       //位图文件总大小包含填充的空字节    WORD bfReserved1;    WORD bfReserved2;    DWORD bfOffBits;}BITMAPFILEHEADER;

作者所用是MSVC14.0编译器,内存对齐方式是4字节对齐,所以文件头结构体的实际大小是4+4+2+2+4=16字节而不是2+4+2+2+4=14字节。如果直接向这个结构体填充数据,那么会实际读取16字节的数据,第3第4个字节被读取到内存空间但是却无法访问,实际应用过程把bfType成员从结构体中剔除,单独读取,结构体作为整体进行读取。

BMP文件信息头结构体

typedef struct tagBITMAPINFOHEADER {    DWORD biSize;    LONG biWidth;          //位图宽度,以像素为单位    LONG biHeight;         //位图高度,以像素为单位    WORD biPlanes;    WORD biBitCount;    DWORD biCompression;    DWORD biSizeImage;     //位图图像数据的实际大小    LONG biXPelsPerMeter;    LONG biYPelsPerMeter;    DWORD biClrUsed;    DWORD biClrImportant;}BITMAPINFOHEADER;
需要注意,图像的扫描行从下至上存储则biHeight成员为正数反之则为负数windows、Linux平台是正数,OS/2平台为负数。ps:博主没见过OS/2机器,查资料得知。

BMP文件调色板结构体

typedef struct tagRGBQUAD {    BYTE rgbBlue;    BYTE rgbGreen;    BYTE rgbRed;    BYTE rgbReserved;}RGBQUAD;

当BMP图像颜色深度是1bit、4bit和8bit时,调色板有2^1、2^4和2^8个。1bit像素深度的BMP位图称作二值图像。8bit像素深度的BMP图像通常用作灰度图,256个调色板每个调色板中的Blue,Green,Red颜色分量都相等,依次从0变化到255。当然也可以自定义调色板中颜色分量的数值。

BMP图像数据

BYTE* ImageData = new BYTE[biSizeImage]{0}

图像数据保存到字节数组,数组的大小为biSizeImage。biHeight*biWidth并不一定等于biSizeimage,因为扫描行存储是以4字节整数倍保存,填充的空行用0补齐。

示例函数(c++11)

WORD bfType;BITMAPFILEHEADER bitMapFileHeader;BITMAPINFOHEADER bitMapInfoHeader;RGBQUAD *rgbQuad;BYTE *imageData;//载入一个BMP文件,支持1bit、4bit、8bit、24bit、32bit图像深度void Load(const char* filepath){    std::ifstream imageFile(filepath, std::ios::in | std::ios::binary);    if (!imageFile)        return ;    imageFile.read((char*)&bfType, sizeof(WORD));    if (bfType != 0x4D42)        return ;    imageFile.read((char*)&bitMapFileHeader, sizeof(BITMAPFILEHEADER));    imageFile.read((char*)&bitMapInfoHeader, sizeof(BITMAPINFOHEADER));    imageData = new BYTE[bitMapInfoHeader.biSizeImage]{ 0 };    if (bitMapInfoHeader.biBitCount == 24 || bitMapInfoHeader.biBitCount == 32)    {        imageFile.read((char*)imageData, bitMapInfoHeader.biSizeImage);    }    else if (bitMapInfoHeader.biBitCount == 1 || bitMapInfoHeader.biBitCount == 4 || bitMapInfoHeader.biBitCount == 8)    {        int rgbQuadNum = (int)std::pow(2,bitMapInfoHeader.biBitCount);        rgbQuad = new RGBQUAD[rgbQuadNum];        imageFile.read((char*)this.rgbQuad, sizeof(RGBQUAD)*rgbQuadNum);        imageFile.read((char*)imageData, bitMapInfoHeader.biSizeImage);    }    imageFile.clear();    imageFile.close();}void Save(const char* filepath){    std::ofstream imageSaveFile(filepath, std::ios::out | std::ios::binary);    if (!imageSaveFile)    {        return ;    }    imageSaveFile.write((char*)&bfType, sizeof(WORD));    imageSaveFile.write((char*)&bitMapFileHeader, sizeof(BITMAPFILEHEADER));    imageSaveFile.write((char*)&bitMapInfoHeader, sizeof(BITMAPINFOHEADER));    if (bitMapInfoHeader.biBitCount == 1 || bitMapInfoHeader.biBitCount == 4 || bitMapInfoHeader.biBitCount == 8)    {        int rgbQuadNum = (int)std::pow(2,bitMapInfoHeader->biBitCount);        imageSaveFile.write((char*)this->rgbQuad, sizeof(RGBQUAD)*rgbQuadNum);    }    imageSaveFile.write((char*)imageData, bitMapInfoHeader->biSizeImage);    imageSaveFile.clear();    imageSaveFile.close();}
0 0