BMP文件的读取和保存

BMP文件介绍

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

BMP文件结构

数据类型定义

using BYTE = unsigned char;        //1Byte
using WORD = unsigned short int;   //2Byte
using DWORD = unsigned long int;   //4Byte
using LONG = long;                 //4Byte
using 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();
}

你可能感兴趣的:(数字图象处理)