BMP格式

BMP是英文Bitmap(位图)的简写,这种格式没有对图像数据进行压缩,文件里保存了各个像素实际的RGB值,理解起来比较直观,对学习图像文件格式的入门很有帮助。

下面列出文件的结构

bmp_3.png
bmp_6.png
bmp_7.png

示例代码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;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。