音视频入门-03-RGB转成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!好像哪里不对劲?!我们的彩虹倒过来了!彩虹的颜色,从上到下应该是:红 -> 橙 -> 黄 -> 绿 -> 青 -> 蓝 -> 紫这张图是:紫 -> 蓝 -> 青 -> 绿 -> 黄 -> 橙 -> 红

image-demo-rainbow-bmp-reverse

处理图片倒立问题

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


将上面的转换代码中,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

内容有误?联系作者:

联系作者

发布了19 篇原创文章 · 获赞 3 · 访问量 3696

猜你喜欢

转载自blog.csdn.net/binglingziyu/article/details/100805178