乐趣区

音视频入门03RGB转成BMP图片

音视频入门文章目录

BMP 文件格式解析

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

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

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

位图文件头

位图文件头分 4 部分,共 14 字节

名称 占用空间 内容 示例数据
bfType 2 字节 标识,就是“BM” BM
bfSize 4 字节 整个 BMP 文件的大小 0x000C0036(786486)
bfReserved1 2 字节 保留字 0
bfReserved2 2 字节 保留字 0
bfOffBits 4 字节 偏移数,即位图文件头 + 位图信息头 + 调色板的大小 0x36(54)

位图信息头

位图信息头共 40 字节

名称 占用空间 内容 示例数据
biSize 4 字节 位图信息头的大小,为 40 0x28(40)
biWidth 4 字节 位图的宽度,单位是像素 0x200(512)
biHeight 4 字节 位图的高度,单位是像素 0x200(512)
biPlanes 2 字节 固定值 1 1
biBitCount 2 字节 每个像素的位数 1- 黑白图,4-16 色,8-256 色,24- 真彩色,32- 带 alpha 通道 0x18(24)
biCompression 4 字节 压缩方式,BI_RGB(0)为不压缩 0
biSizeImage 4 字节 位图全部像素占用的字节数,BI_RGB 时可设为 0 0x0C
biXPelsPerMeter 4 字节 水平分辨率(像素 / 米) 0
biYPelsPerMeter 4 字节 垂直分辨率(像素 / 米) 0
biClrUsed 4 字节 位图使用的颜色数 如果为 0,则颜色数为 2 的 biBitCount 次方 0
biClrImportant 4 字节 重要的颜色数,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!好像哪里不对劲?!我们的彩虹倒过来了!
彩虹的颜色,从上到下应该是:
红 -> 橙 -> 黄 -> 绿 -> 青 -> 蓝 -> 紫
这张图是:
紫 -> 蓝 -> 青 -> 绿 -> 黄 -> 橙 -> 红

处理图片倒立问题

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

<br/>

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

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

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

内容有误?联系作者:


退出移动版