音视频入门文章目录

BMP 文件格式解析

BMP 文件由文件头、位图信息头、颜色信息和图形数据四部分组成。

位图文件头(14个字节)位图信息头(40个字节)颜色信息图形数据
  • 文件头与信息头一共是 54 字节
  • RGB 数据部分:

RGB24 文件存储的顺序是 RGB, RGB, RGB ...... RGB
BMP 文件 RGB 数据存储的顺序是 BGR, BGR, BGR ... BGR

位图文件头

位图文件头分 4 部分,共 14 字节
名称占用空间内容示例数据
bfType2字节标识,就是“BM”BM
bfSize4字节整个 BMP 文件的大小0x000C0036(786486)
bfReserved12字节保留字0
bfReserved22字节保留字0
bfOffBits4字节偏移数,即位图文件头+位图信息头+调色板的大小0x36(54)

位图信息头

位图信息头共 40 字节
名称占用空间内容示例数据
biSize4字节位图信息头的大小,为400x28(40)
biWidth4字节位图的宽度,单位是像素0x200(512)
biHeight4字节位图的高度,单位是像素0x200(512)
biPlanes2字节固定值11
biBitCount2字节每个像素的位数 1-黑白图,4-16色,8-256色,24-真彩色,32-带 alpha 通道0x18(24)
biCompression4字节压缩方式,BI_RGB(0)为不压缩0
biSizeImage4字节位图全部像素占用的字节数,BI_RGB时可设为00x0C
biXPelsPerMeter4字节水平分辨率(像素/米)0
biYPelsPerMeter4字节垂直分辨率(像素/米)0
biClrUsed4字节位图使用的颜色数 如果为0,则颜色数为2的biBitCount次方0
biClrImportant4字节重要的颜色数,0代表所有颜色都重要0

将 RGB24 像素点数据转成 BMP 格式图片

转换代码:

#include <stdio.h>
#include <stdlib.h>

// 彩虹的七种颜色
u_int32_t rainbowColors[] = {
        0XFF0000, // 红
        0XFFA500, // 橙
        0XFFFF00, // 黄
        0X00FF00, // 绿
        0X007FFF, // 青
        0X0000FF, // 蓝
        0X8B00FF  // 紫
};

/*bmp file header*/
typedef struct {
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
} BitmapFileHeader;

/*bmp info header*/
typedef struct {
    unsigned int   biSize; /* Size of info header */
    int            biWidth; /* Width of image */
    int            biHeight; /* Height of image */
    unsigned short biPlanes; /* Number of color planes */
    unsigned short biBitCount; /* Number of bits per pixel */
    unsigned int   biCompression; /* Type of compression to use */
    unsigned int   biSizeImage; /* Size of image data */
    int            biXPelsPerMeter; /* X pixels per meter */
    int            biYPelsPerMeter; /* Y pixels per meter */
    unsigned int   biClrUsed; /* Number of colors used */
    unsigned int   biClrImportant; /* Number of important colors */
} BitmapInfoHeader;

void writeRGBToBmp(char *filename, int width, int height) {
    FILE *bitmapFile = fopen(filename, "wb");
    if(!bitmapFile) {
        printf("Could not write file \n");
        return;
    }

    uint16_t bfType = 0x4d42;

    BitmapFileHeader fileHeader;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + width*height*3;
    fileHeader.bfOffBits = 0x36;

    BitmapInfoHeader infoHeader;
    infoHeader.biSize = sizeof(BitmapInfoHeader);
    infoHeader.biWidth = width;
    infoHeader.biHeight = height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biSizeImage = 0;
    infoHeader.biCompression = 0;
    infoHeader.biXPelsPerMeter = 5000;
    infoHeader.biYPelsPerMeter = 5000;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&bfType, sizeof(bfType), 1, bitmapFile);
    fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);
    fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);

    // 写入图像数据
    for (int i = 0; i < width; ++i) {

        // 当前颜色
        u_int32_t currentColor = rainbowColors[0];
        if(i < 100) {
            currentColor = rainbowColors[0];
        } else if(i < 200) {
            currentColor = rainbowColors[1];
        } else if(i < 300) {
            currentColor = rainbowColors[2];
        } else if(i < 400) {
            currentColor = rainbowColors[3];
        } else if(i < 500) {
            currentColor = rainbowColors[4];
        } else if(i < 600) {
            currentColor = rainbowColors[5];
        } else if(i < 700) {
            currentColor = rainbowColors[6];
        }
        // 当前颜色 R 分量
        u_int8_t R = (currentColor & 0xFF0000) >> 16;
        // 当前颜色 G 分量
        u_int8_t G = (currentColor & 0x00FF00) >> 8;
        // 当前颜色 B 分量
        u_int8_t B = currentColor & 0x0000FF;

        for (int j = 0; j < height; ++j) {
            // 按 BGR 顺序写入一个像素 RGB24 到文件中
            fwrite(&B, 1, 1, bitmapFile);
            fwrite(&G, 1, 1, bitmapFile);
            fwrite(&R, 1, 1, bitmapFile);
        }
    }

    // 关闭文件
    fclose(bitmapFile);
}

int main() {
    writeRGBToBmp("/Users/staff/Desktop/rainbow-700x700.bmp", 700, 700);
    return 0;
}

检查生成的 BMP 图片

Congratulations! 图片查看软件识别出我们的 BMP 图片了,预览正常!
BUT!好像哪里不对劲?!我们的彩虹倒过来了!
彩虹的颜色,从上到下应该是:
红 -> 橙 -> 黄 -> 绿 -> 青 -> 蓝 -> 紫
这张图是:
紫 -> 蓝 -> 青 -> 绿 -> 黄 -> 橙 -> 红

image-demo-rainbow-bmp-reverse

处理图片倒立问题

BitmapInfoHeader 中的 biHeight 字段,
biHeight 为正,位图自底向顶扫描,
biHeight 为负,位图自顶向底扫描。
如果这个值的设置和原始位图文件扫描方式不符,则图像显示可能会颠倒。

<br/>

将上面的转换代码中,BitmapInfoHeader 部分:

// infoHeader.biHeight = height;
infoHeader.biHeight = -height;

image-demo-rainbow-bmp

Congratulations!

成功用像素点拼出了一张 “真正” 的图片!


代码:

rgb-to-bmp

参考资料:

维基百科-BMP

位图(bmp)文件格式分析

RGB24转换成BMP图像

关于RGB32和RGB24的区别

non-dword-aligned-pixel-to-dword-aligned-bitmap

generate-bmp-file-from-array-of-rgb-values



binglingziyu
3 声望4 粉丝