C語言讀取BMP格式圖片
BMP
擴展名 | .bmp |
---|---|
開發者 | Microsoft |
格式 | 圖像文件格式 |
BMP取自位圖BitMaP的縮寫,也稱爲DIB(與設備無關的位圖)是微軟視窗圖形子系統(Graphics Device Interface)內部使用的一種位圖圖形格式,它是微軟視窗平臺上的一個簡單的圖形文件格式。
圖像通常保存的顏色深度有2(1位)、16(4位)、256(8位)、65536(16位)和1670萬(24位)種顏色(其中位是表示每點所用的數據位)。8位圖像可以是索引彩色圖像外,也可以是灰階圖像。表示透明的alpha通道也可以保存在一個類似於灰階圖像的獨立文件中。帶有集成的alpha通道的32位版本已經隨着Windows XP出現,它在視窗的登錄和主題系統中都有使用,但是還沒有多少圖像編輯軟件支持。
目錄[隱藏] |
[編輯]存儲算法
BMP文件通常是不壓縮的,所以它們通常比同一幅圖像的壓縮圖像文件格式要大很多。例如,一個800×600的24位幾乎佔據1.4MB空間。因此它們通常不適合在因特網或者其他低速或者有容量限制的媒介上進行傳輸。
根據顏色深度的不同,圖像上的一個像素可以用一個或者多個字節表示,它由n/8所確定(n是位深度,1字節包含8個數據位)。圖片瀏覽器等基於字節的ASCII值計算像素的顏色,然後從調色板中讀出相應的值。更爲詳細的信息請參閱下面關於位圖文件的部分。
n位2n種顏色的包含調色板的位圖近似字節數可以用下面的公式計算:
BMP文件大小 ,其中高度(height)和寬度(width)都以像素爲單位。
需要注意的是上面公式中的54是位圖文件的文件頭,是彩色調色板的大小。 如果位圖文件不包含調色板,如24位,32位位圖,則位圖的近似字節數可以用下面的公式計算:
BMP文件大小 ,其中高度(height)和寬度(width)都以像素爲單位。
另外需要注意的是這是一個近似值,對於n位的位圖圖像來說,儘管可能有最多種顏色,一個特定的圖像可能並不會使用這些所有的顏色。由於彩色調色板僅僅定義了圖像所用的顏色,所以實際的彩色調色板將小於。
如果想知道這些值是如何得到的,請參考下面文件格式的部分。
由於存儲算法本身決定的因素,根據幾個圖像參數的不同計算出的大小與實際的文件大小將會有一些細小的差別。
[編輯]典型的文件格式
典型的位圖文件格式通常包含下面幾個數據塊:
- 位圖頭:保存位圖文件的總體信息。
- 位圖信息:保存位圖圖像的詳細信息。
- 調色板:保存所用顏色的定義。
- 位圖數據:保存一個又一個像素的實際圖像。
下面的部分將會詳細地描述位圖文件中保存的數據。需要注意的是這是標準位圖的文件格式,其他一些位圖圖像可能根據生成文件的應用程序不同所使用格式可能會有細微的區別。
[編輯]位圖頭
這部分是識別信息,典型的應用程序會首先普通讀取這部分數據以確保的確是位圖文件並且沒有損壞。
- 字節 #0-1 保存位圖文件的標識符,這兩個字節的典型數據是BM。
- 字節 #2-5 使用一個dword保存位圖文件大小。
- 字節 #6-9 是保留部分,留做以後的擴展使用,對實際的解碼格式沒有影響。
- 字節 #10-13 保存位圖數據位置的地址偏移,也就是起始地址。
[編輯]位圖信息
這部分告訴應用程序圖像的詳細信息,在屏幕上顯示圖像將會使用這些信息,它從文件的第15個字節開始。
- 字節 #14-17 定義以下用來描述影像的區塊(BitmapInfoHeader)的大小。它的值是:40 - Windows 3.2、95、NT、12 - OS/2 1.x、240 - OS/2 2.x
- 字節 #18-21 保存位圖寬度(以像素個數表示)。
- 字節 #22-25 保存位圖高度(以像素個數表示)。
- 字節 #26-27 保存所用彩色位面的個數。不經常使用。
- 字節 #28-29 保存每個像素的位數,它是圖像的顏色深度。常用值是1、4、8(灰階)和24(彩色)。
- 字節 #30-33 定義所用的壓縮算法。允許的值是0、1、2、3、4、5。
0 - 沒有壓縮(也用BI_RGB表示)
1 - 行程長度編碼 8位/像素(也用BI_RLE8表示)
2 - 行程長度編碼4位/像素(也用BI_RLE4表示)
3 - Bit field(也用BI_BITFIELDS表示)
4 - JPEG圖像(也用BI_JPEG表示)
5 - PNG圖像(也用BI_PNG表示)
然而,由於大多數位圖文件都是不壓縮的,所以最常用的值是0。
- 字節 #34-37 保存圖像大小。這是原始(en:raw)位圖數據的大小,不要與文件大小混淆。
- 字節 #38-41 保存圖像水平方向分辨率。
- 字節 #42-45 保存圖像豎直方向分辨率。
- 字節 #46-49 保存所用顏色數目。
- 字節 #50-53 保存所用重要顏色數目。當每個顏色都重要時這個值與顏色數目相等。
[編輯]調色板
這部分定義了圖像中所用的顏色。如上所述,位圖圖像一個像素接着一個像素儲存,每個像素使用一個或者多個字節的值表示,所以調色板的目的就是要告訴應用程序這些值所對應的實際顏色。
典型的位圖文件使用RGB彩色模型。在這種模型中,每種顏色都是由不同強度(從0到最大強度)的紅色(R)、綠色(G)和藍色(B)組成的,也就是說,每種顏色都可以使用紅色、綠色和藍色的值所定義。
在位圖文件的實現中,調色板可以包含很多條目,條目個數就是圖像中所使用的顏色的個數。每個條目包含4個字節:其中三個表示紅色、綠色和藍色,第四個字節沒有使用(大多數應用程序將它設爲0)。對於每個字節,數值0表示相應的顏色在當前的圖像文件中沒有使用,而數值255表示那個顏色使用最大的強度。
[編輯]位圖數據
這部分逐個像素表示圖像。像素是從下到上、從左到右保存的。每個像素使用一個或者多個字節表示。如果一個圖像水平線的字節數不是4的倍數,這行就使用空字節補齊,通常是ASCII碼0。
範例: 有一張5*5的圖片,應該會有25個pixels,但是因爲5不是4的倍數所以會顯示成: xxxxx000 xxxxx000 xxxxx000 xxxxx000 xxxxx000
x代表調色盤的編號 0代表Null_character
有一張4*4的圖片,應該會有16個pixels,但是因爲是4的倍數所以會顯示成:
xxxx xxxx xxxx xxxx
[編輯]其他
儘管文件大小比較大,但是位圖文件的簡單性、在微軟視窗和其他地方的廣泛使用以及這種格式的優秀文檔標準以及沒有專利約束,使得它成爲其他操作系統圖像處理程序能夠讀寫的一種最爲常用的格式。
X Window System使用類似的.XBM格式表示一位黑白圖像以及.XPM(pixelmap)表示彩色圖像。另外還有一種.RAW格式,它除了保存原始數據之外沒有任何其他信息。其他還有Portable Pixmap file format(.PPM)和Truevision TGA(.TGA),但是它們用得很少或者只用於特殊目的。儘管其他格式也保存爲“位圖”(與矢量圖不同),但是它們使用數據壓縮或者顏色索引,所以它們不是嚴格意義上的位圖。
如果您已經詳細閱讀維基百科對bmp文件格式的描述,就不難發現,讀取bmp大致只需要用到fseek,fopen,fread等函數即可以完成,下面我給出相應的C代碼,讀取的圖像頭文件信息主要是offset,width,height,數據信息主要是r[][],g[][],b[][],同時把數據輸出到相應的txt文件中。
#include <stdio.h>
#include <stdlib.h>
#define BITMAPFILEHEADERLENGTH 14 // The bmp FileHeader length is 14
#define BM 19778 // The ASCII code for BM
/* Test the file is bmp file or not */
void bmpFileTest(FILE* fpbmp);
/* To get the OffSet of header to data part */
void bmpHeaderPartLength(FILE* fpbmp);
/* To get the width and height of the bmp file */
void BmpWidthHeight(FILE* fpbmp);
//get r,g,b data
void bmpDataPart(FILE* fpbmp);
// output data to corresponding txt file
void bmpoutput(FILE *fpout);
unsigned int OffSet = 0; // OffSet from Header part to Data Part
long width ; // The Width of the Data Part
long height ; // The Height of the Data Part
unsigned char r[2000][2000],output_r[2000][2000];
unsigned char g[2000][2000],output_g[2000][2000];
unsigned char b[2000][2000],output_b[2000][2000];
int main(int argc, char* argv[])
{
/* Open bmp file */
unsigned char *fp_temp;
FILE *fpbmp;
FILE *fpout;
fpbmp= fopen("1.bmp", "rb");
if (fpbmp == NULL)
{
printf("Open bmp failed!!!\n");
return 1;
}
fpout= fopen("out.bmp", "wb+");
if (fpout == NULL)
{
printf("Open out.bmp failed!!!\n");
return 1;
}
bmpFileTest(fpbmp); //Test the file is bmp file or not
bmpHeaderPartLength(fpbmp); //Get the length of Header Part
BmpWidthHeight(fpbmp); //Get the width and width of the Data Part
//
fseek(fpbmp, 0L, SEEK_SET);
fseek(fpout, 0L, SEEK_SET);
fp_temp=malloc(OffSet);
fread(fp_temp, 1, OffSet, fpbmp);
fwrite(fp_temp,1,OffSet,fpout);
bmpDataPart(fpbmp); //Reserve the data to file
/*
如果您想對圖片進行處理,請您再這裏插入處理函數!!!!!!!!!!!!!!!!!!
*/
bmpoutput(fpout);
fclose(fpbmp);
fclose(fpout);
return 0;
}
void bmpoutput(FILE* fpout)
{
int i, j=0;
int stride;
unsigned char* pixout=NULL;
stride=(24*width+31)/8;
stride=stride/4*4;
pixout=malloc(stride);
fseek(fpout, OffSet, SEEK_SET);
for(j=0;j<height;j++)
{
for(i=0;i<width;i++)
{
pixout[i*3+2]=output_r[height-1-j][i];
pixout[i*3+1]=output_g[height-1-j][i];
pixout[i*3] =output_b[height-1-j][i];
}
fwrite(pixout, 1, stride, fpout);
}
}
void bmpDataPart(FILE* fpbmp)
{
int i, j=0;
int stride;
unsigned char* pix=NULL;
FILE* fpr;
FILE* fpg;
FILE* fpb;
if((fpr=fopen("bmpr.txt","w+")) == NULL)
{
printf("Failed to construct file bmpr.txt.!!!");
exit(1);
}
if((fpg=fopen("bmpg.txt","w+")) == NULL)
{
printf("Failed to construct file bmpg.txt.!!!");
exit(1);
}
if((fpb=fopen("bmpb.txt","w+")) == NULL)
{
printf("Failed to construct file bmpb.txt.!!!");
exit(1);
}
fseek(fpbmp, OffSet, SEEK_SET);
stride=(24*width+31)/8;
stride=stride/4*4;
pix=malloc(stride);
for(j=0;j<height;j++)
{
fread(pix, 1, stride, fpbmp);
for(i=0;i<width;i++)
{
r[height-1-j][i] =pix[i*3+2];
g[height-1-j][i] =pix[i*3+1];
b[height-1-j][i] =pix[i*3];
output_r[height-1-j][i] =pix[i*3+2];
output_g[height-1-j][i] =pix[i*3+1];
output_b[height-1-j][i] =pix[i*3];
}
}
for(i =0; i < height; i++)
{
for(j = 0; j < width-1; j++)
{
fprintf(fpb,"%4d",b[i][j]);
fprintf(fpg,"%4d",g[i][j]);
fprintf(fpr,"%4d",r[i][j]);
}
fprintf(fpb,"%4d\n",b[i][j]);
fprintf(fpg,"%4d\n",g[i][j]);
fprintf(fpr,"%4d\n",r[i][j]);
}
fclose(fpr);
fclose(fpg);
fclose(fpb);
}
void bmpFileTest(FILE* fpbmp)
{
unsigned short bfType = 0;
fseek(fpbmp, 0L, SEEK_SET);//seek_set 起始位置
fread(&bfType, sizeof(char), 2, fpbmp);
if (BM != bfType)
{
printf("This file is not bmp file.!!!\n");
exit(1);
}
}
/* To get the OffSet of header to data part */
void bmpHeaderPartLength(FILE* fpbmp)
{
fseek(fpbmp, 10L, SEEK_SET);
fread(&OffSet, sizeof(char), 4, fpbmp);
printf("The Header Part is of length %d.\n", OffSet);
}
/* To get the width and height of the bmp file */
void BmpWidthHeight(FILE* fpbmp)
{
fseek(fpbmp, 18L, SEEK_SET);
fread(&width, sizeof(char), 4, fpbmp);
fseek(fpbmp, 22L, SEEK_SET);
fread(&height, sizeof(char), 4, fpbmp);
printf("The Width of the bmp file is %ld.\n", width);
printf("The Height of the bmp file is %ld.\n", height);
}