心血來潮想瞭解下常用圖片的格式解析,翻看了一些資料後,發現最簡單的是bmp格式,所以先拿它開刀。 BMP格式 這種格式內的數據分為 三到四個 部分,依次是: 1. 文件信息頭 (14位元組)存儲著文件類型,文件大小等信息 2. 圖片信息頭 (40位元組)存儲著圖像的尺寸,顏色索引,位平面數等信息 3. ...
心血來潮想瞭解下常用圖片的格式解析,翻看了一些資料後,發現最簡單的是bmp格式,所以先拿它開刀。
BMP格式
這種格式內的數據分為三到四個部分,依次是:
- 文件信息頭 (14位元組)存儲著文件類型,文件大小等信息
- 圖片信息頭 (40位元組)存儲著圖像的尺寸,顏色索引,位平面數等信息
- 調色板 (由顏色索引數決定)【可以沒有此信息】
- 點陣圖數據 (由圖像尺寸決定)每一個像素的信息在這裡存儲
一般的bmp圖像都是24位,也就是真彩。每8位為一位元組,24位也就是使用三位元組來存儲每一個像素的信息,三個位元組對應存放r,g,b三原色的數據,每個位元組的存貯範圍都是0-255。
那麼以此類推,32點陣圖即每像素存儲r,g,b,a(Alpha通道,存儲透明度)四種數據。8點陣圖就是只有灰度這一種信息,還有二值圖,它只有兩種顏色,黑或者白。
文件信息頭格式
typedef struct tagBITMAPFILEHEADER {
unsigned short bfType; // 19778,必須是BM字元串,對應的十六進位為0x4d42,十進位為19778,否則不是bmp格式文件
unsigned int bfSize; // 文件大小
unsigned short bfReserved1; // 保留,必須設置為0
unsigned short bfReserved2; // 保留,必須設置為0
unsigned int bfOffBits; // 從文件頭到像素數據的偏移
} BITMAPFILEHEADER;
圖片信息頭格式
typedef struct tagBITMAPINFOHEADER {
unsigned int biSize; // 此結構體的大小
int biWidth; // 圖像的寬
int biHeight; // 圖像的高
unsigned short biPlanes; // 表示bmp圖片的平面屬,顯然顯示器只有一個平面,所以恆等於1
unsigned short biBitCount; // 一像素所占的位數,一般為24
unsigned int biCompression; // 說明圖象數據壓縮的類型,0為不壓縮。
unsigned int biSizeImage; // 像素數據所占大小, 這個值應該等於上面文件頭結構中bfSize-bfOffBits
int biXPelsPerMeter; // 說明水平解析度,用象素/米表示。一般為0
int biYPelsPerMeter; // 說明垂直解析度,用象素/米表示。一般為0
unsigned int biClrUsed; // 說明點陣圖實際使用的彩色表中的顏色索引數(設為0的話,則說明使用所有調色板項)。
unsigned int biClrImportant;// 說明對圖象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。
} BITMAPINFOHEADER;
調色板信息
這裡需要根據文件信息頭的bfOffBits是否等於54(由前面的固定14+40位元組得出)來判斷是否存在此調色板信息,如果是,則不存在;大於的話即存在。
可以根據需求提取其中的信息,或者直接移動到點陣圖數據區讀取像素信息。
這個地方可以表示為一個二維數組unsigned char palette[N][M], 其中N表示總的顏色索引數,M表示每像素占的位元組數。例如一個24點陣圖,每像素由3個位元組構成,M即為3,每個位元組可表示0-255共256種顏色,所以N為256 。
數組中存放的是索引信息,也就是一張映射表,標識顏色索引號與其代表的顏色的對應關係
點陣圖數據
這裡就存放著所有的像素信息了,每像素為一位元組,讀取出來後通過查詢調色板獲得顏色信息。
如果圖像是24位或是32位數據的點陣圖的話,點陣圖數據區就不是索引而是實際的像素值了。下麵說明一下,此時點陣圖數據區的每個像素的RGB顏色陣列排布:
24位RGB按照BGR的順序來存儲每個像素的各顏色通道的值,一個像素的所有顏色分量值都存完後才存下一個下一個像素,不進行交織存儲。
32位數據按照BGRA的順序存儲,其餘與24位點陣圖的方式一樣。
註意:由於點陣圖信息頭中的圖像高度是正數,所以點陣圖數據在文件中的排列順序是從左下角到右上角,以行為主序排列的。
也就是說,最先讀取到的是位於從上往下數最後一行最左端的像素,然後是同行向右一列的像素,讀取完一整行後,繼續讀取倒數第二行,然後繼續向上直到讀完所有數據。
對齊規則
我們知道Windows預設的掃描的最小單位是4位元組,如果數據對齊滿足這個值的話對於數據的獲取速度等都是有很大的增益的。因此,BMP圖像順應了這個要求,要求每行的數據的長度必須是4的倍數,如果不夠需要進行比特填充(以0填充),這樣可以達到按行的快速存取。這時,點陣圖數據區的大小就未必是 圖片寬×每像素位元組數×圖片高 能表示的了,因為每行可能還需要進行比特填充。
填充後的每行的位元組數為:
int iLineByteCnt = (((m_iImageWidth * m_iBitsPerPixel) + 31) >> 5) << 2;
其中m_iBitsPerPixel為每像素的比特數。
這樣,點陣圖數據區的大小為:
m_iImageDataSize = iLineByteCnt * m_iImageHeight;
我們在掃描完一行數據後,也可能接下來的數據並不是下一行的數據,可能需要跳過一段填充數據:
skip = 4 - ((m_iImageWidth * m_iBitsPerPixel)>>3) & 3;
實現代碼
以24點陣圖為例
/* BmpFormat.h
* 存放上述幾個結構體
*/
// 文件頭結構體
typedef struct tagBITMAPFILEHEADER {
unsigned short bfType; // 19778,必須是BM字元串,對應的十六進位為0x4d42,十進位為19778,否則不是bmp格式文件
unsigned int bfSize; // 文件大小
unsigned short bfReserved1; // 保留,必須設置為0
unsigned short bfReserved2; // 保留,必須設置為0
unsigned int bfOffBits; // 從文件頭到像素數據的偏移
} BITMAPFILEHEADER;
//圖像信息頭結構體
typedef struct tagBITMAPINFOHEADER {
unsigned int biSize; // 此結構體的大小
int biWidth; // 圖像的寬
int biHeight; // 圖像的高
unsigned short biPlanes; // 表示bmp圖片的平面屬,顯然顯示器只有一個平面,所以恆等於1
unsigned short biBitCount; // 一像素所占的位數,一般為24
unsigned int biCompression; // 說明圖象數據壓縮的類型,0為不壓縮。
unsigned int biSizeImage; // 像素數據所占大小, 這個值應該等於上面文件頭結構中bfSize-bfOffBits
int biXPelsPerMeter; // 說明水平解析度,用象素/米表示。一般為0
int biYPelsPerMeter; // 說明垂直解析度,用象素/米表示。一般為0
unsigned int biClrUsed; // 說明點陣圖實際使用的彩色表中的顏色索引數(設為0的話,則說明使用所有調色板項)。
unsigned int biClrImportant;// 說明對圖象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。
} BITMAPINFOHEADER;
//24點陣圖像素信息結構體
typedef struct _PixelInfo {
unsigned char b;
unsigned char g;
unsigned char r;
} PixelInfo;
/* TestBmp.c
*
*/
#include <stdio.h>
#include <malloc.h>
#include "BmpFormat.h"
int main(){
FILE* fp;
fp = fopen("image.bmp", "rb");//讀取同目錄下的image.bmp文件。
if(fp == NULL){
printf("打開'image.bmp'失敗!\n");
return -1;
}
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
//從文件里讀取信息
fread(&fileHeader, sizeof(fileHeader), 1, fp);
fread(&infoHeader, sizeof(infoHeader), 1, fp);
//解析頭信息
if(fileHeader.bfType != 19778){
printf("'image.bmp'不是一個bmp格式的文件。\n");
return -1;
}
//輸出文件信息
printf("大小:%dkb\n", fileHeader.bfSize);
printf("尺寸:寬%d 高%d\n", infoHeader.biWidth, infoHeader.biHeight);
printf("位數:%d\n", infoHeader.biBitCount);
printf("偏移量:%d\n", fileHeader.bfOffBits);
if(infoHeader.biBitCount != 24 || fileHeader.bfOffBits != 54){
printf("\n暫時只支持24點陣圖片。\n");
return -1;
}
//計算補齊量(這裡用更容易理解的除法和求餘,效果和位運算相同)
int offset = (infoHeader.biBitCount*infoHeader.biWidth/8)%4;
if (offset != 0){
offset = 4 - offset;
}
//動態創建一塊記憶體以存儲像素信息
int size = infoHeader.biHeight * (infoHeader.biWidth + offset);
PixelInfo* pixel = (PixelInfo*)malloc(size);
//迴圈讀取
for (i=0; i<infoHeader.biHeight; i++){
for (j=0; j<infoHeader.biWidth; j++){
fread(pixel, sizeof(PixelInfo), 1, fp);
}
if (offset != 0){
for (j=0; j<offset; j++){
fread(pixel, sizeof(unsigned char), 1, fp);
}
}
}
//可以在這裡對像素進行處理
//比如輸出左下角的像素信息
printf("\n左下角的像素的RGB為:%d\t%d\t%d\n", pixel[0].r, pixel[0].g, pixel[0].b);
//釋放記憶體
free(pixel);
return 0;
}
參考文章
常見圖片格式詳解 - Xiangism
BMP文件格式詳解(BMP file format)