音視頻入門-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 爲負,位圖自頂向底掃描。
如果這個值的設置和原始位圖文件掃描方式不符,則圖像顯示可能會顛倒。

<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

內容有誤?聯繫作者:

聯繫作者


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章