天天看點

C語言 BMP圖檔處理

BMP是bitmap的縮寫形式,bitmap顧名思義,就是位圖也即Windows位圖。它一般由4部分組成:檔案頭資訊塊、圖像描述資訊塊、顔色表(在真彩色模式無顔色表)和圖像資料區組成。在系統中以BMP為擴充名儲存。   

    打開Windows的畫圖程式,在儲存圖像時,可以看到三個選項:2色位圖(黑白)、16色位圖、256色位圖和24位位圖。這是最普通的生成位圖的工具,在這裡講解的BMP位圖形式,主要就是指用畫圖生成的位圖(當然,也可以用其它工具軟體生成)。   

    現在講解BMP的4個組成部分:   

  1.檔案頭資訊塊   

  0000-0001:檔案辨別,為字母ASCII碼“BM”。   

  0002-0005:檔案大小。   

  0006-0009:保留,每位元組以“00”填寫。   

  000A-000D:記錄圖像資料區的起始位置。各位元組的資訊依次含義為:檔案頭資訊塊大小,圖像描述資訊塊的大小,圖像顔色表的大小,保留(為01)。   

  2.圖像描述資訊塊   

  000E-0011:圖像描述資訊塊的大小,常為28H。   

  0012-0015:圖像寬度。   

  0016-0019:圖像高度。   

  001A-001B:圖像的plane(平面?)總數(恒為1)。   

  001C-001D:記錄像素的位數,很重要的數值,圖像的顔色數由該值決定。   

  001E-0021:資料壓縮方式(數值位0:不壓縮;1:8位壓縮;2:4位壓縮)。   

  0022-0025:圖像區資料的大小。   

  0026-0029:水準每米有多少像素,在裝置無關位圖(.DIB)中,每位元組以00H填寫。   

  002A-002D:垂直每米有多少像素,在裝置無關位圖(.DIB)中,每位元組以00H填寫。   

  002E-0031:此圖像所用的顔色數,如值為0,表示所有顔色一樣重要。   

  3.顔色表   

    顔色表的大小根據所使用的顔色模式而定:2色圖像為8位元組;16色圖像位64位元組;256色圖像為1024位元組。其中,每4位元組表示一種顔色,并以B(藍色)、G(綠色)、R(紅色)、alpha(像素的透明度值,一般不需要)。即首先4位元組表示顔色号0的顔色,接下來表示顔色号1的顔色,依此類推。   

  4.圖像資料區   

    顔色表接下來位為位圖檔案的圖像資料區,在此部分記錄着每點像素對應的顔色号,其記錄方式也随顔色模式而定,既2色圖像每點占1位(8位為1位元組);16色圖像每點占4位(半位元組);256色圖像每點占8位(1位元組);真彩色圖像每點占24位(3位元組)。是以,整個資料區的大小也會随之變化。究其規律而言,可的出如下計算公式:圖像資料資訊大小=(圖像寬度*圖像高度*記錄像素的位數)/8。   

  然而,未壓縮的圖像資訊區的大小。除了真彩色模式外,其餘的均大于或等于資料資訊的大小。這是為什麼呢?原因有兩個:   

    1.BMP檔案記錄一行圖像是以位元組為機關的。是以,就不存在一個位元組中的資料位資訊表示的點在不同的兩行中。也就是說,設顯示模式位16色,在每個位元組配置設定兩個點資訊時,如果圖像的寬度位奇數,那麼最後一個像素點的資訊将獨占一個位元組,這個位元組的後4位将沒有意義。接下來的一個位元組将開始記錄下一行的資訊。   

    2.為了顯示的友善,除了真彩色外,其他的每中顔色模式的行位元組數要用資料“00”補齊為4的整數倍。如果顯示模式為16色,當圖像寬為19時,存儲時每行則要補充4-(19/2+1)%4=2個位元組(加1是因為裡面有一個像素點要獨占了一位元組)。如果顯示模式為256色,當圖像寬為19時,每行也要補充4-19%4=1個位元組。   

    還有一點我要申明,當螢幕初始化為16或256色模式時,一定要設定調色闆或修正顔色值,否則無法得到正确的圖像顔色。

C代碼

1. //ReadBitMap
2. //
3. #include <string.h> 
4. #include <math.h>   
5. #include <stdio.h>   
6. #include <stdlib.h>   
7. #include <malloc.h>
8.   
9.   
10. #define   WIDTHBYTES(bits) (((bits)+31)/32*4)
11.   
12. typedef unsigned char BYTE;  
13. typedef unsigned short WORD;  
14. typedef unsigned long DWORD;  
15. typedef long LONG;  
16.   
17.   
18. //位圖檔案頭資訊結構定義
19. //其中不包含檔案類型資訊(由于結構體的記憶體結構決定,要是加了的話将不能正确讀取檔案資訊)
20.   
21. typedef struct
22.   
23. DWORD bfSize;           //檔案大小
24. WORD   bfReserved1;     //保留字,不考慮
25. WORD   bfReserved2;     //保留字,同上
26. DWORD bfOffBits;        //實際位圖資料的偏移位元組數,即前三個部分長度之和
27. } BITMAPFILEHEADER;   
28.   
29.   
30. //資訊頭BITMAPINFOHEADER,也是一個結構,其定義如下:
31.   
32. typedef struct
33. //public:
34. DWORD   biSize;             //指定此結構體的長度,為40
35. LONG    biWidth;            //位圖寬
36. LONG    biHeight;           //位圖高
37. WORD    biPlanes;           //平面數,為1
38. WORD    biBitCount;         //采用顔色位數,可以是1,2,4,8,16,24,新的可以是32
39. DWORD   biCompression;      //壓縮方式,可以是0,1,2,其中0表示不壓縮
40. DWORD   biSizeImage;        //實際位圖資料占用的位元組數
41. LONG    biXPelsPerMeter;    //X方向分辨率
42. LONG    biYPelsPerMeter;    //Y方向分辨率
43. DWORD   biClrUsed;          //使用的顔色數,如果為0,則表示預設值(2^顔色位數)
44. DWORD   biClrImportant;     //重要顔色數,如果為0,則表示所有顔色都是重要的
45. } BITMAPINFOHEADER;   
46.   
47.   
48. //調色闆Palette,當然,這裡是對那些需要調色闆的位圖檔案而言的。24位和32位是不需要調色闆的。
49. //(似乎是調色闆結構體個數等于使用的顔色數。)
50.   
51. typedef struct
52. //public:
53. BYTE     rgbBlue; //該顔色的藍色分量
54. BYTE     rgbGreen; //該顔色的綠色分量
55. BYTE     rgbRed; //該顔色的紅色分量
56. BYTE     rgbReserved; //保留值
57. } RGBQUAD;  
58.   
59.   
60.   
61. void
62. {  
63. printf("位圖檔案頭:\n");  
64. printf("檔案大小:%d\n",pBmpHead->bfSize);  
65. printf("保留字:%d\n",pBmpHead->bfReserved1);  
66. printf("保留字:%d\n",pBmpHead->bfReserved2);  
67. printf("實際位圖資料的偏移位元組數:%d\n",pBmpHead->bfOffBits);  
68.   
69. }  
70.   
71.   
72. void
73. {  
74. printf("位圖資訊頭:\n");  
75. printf("結構體的長度:%d\n",pBmpInforHead->biSize);  
76. printf("位圖寬:%d\n",pBmpInforHead->biWidth);  
77. printf("位圖高:%d\n",pBmpInforHead->biHeight);  
78. printf("biPlanes平面數:%d\n",pBmpInforHead->biPlanes);  
79. printf("biBitCount采用顔色位數:%d\n",pBmpInforHead->biBitCount);  
80. printf("壓縮方式:%d\n",pBmpInforHead->biCompression);  
81. printf("biSizeImage實際位圖資料占用的位元組數:%d\n",pBmpInforHead->biSizeImage);  
82. printf("X方向分辨率:%d\n",pBmpInforHead->biXPelsPerMeter);  
83. printf("Y方向分辨率:%d\n",pBmpInforHead->biYPelsPerMeter);  
84. printf("使用的顔色數:%d\n",pBmpInforHead->biClrUsed);  
85. printf("重要顔色數:%d\n",pBmpInforHead->biClrImportant);  
86. }  
87.   
88. void
89. {   
90. printf("(%-3d,%-3d,%-3d)   ",pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);  
91.   
92. }  
93.   
94.   
95.   
96. void
97. {  
98.   
99. BITMAPFILEHEADER   bitHead;  
100. BITMAPINFOHEADER bitInfoHead;   
101. FILE* pfile;  
102.   
103. char
104. printf("please input the .bmp file name:\n");  
105. scanf("%s",strFile);  
106.   
107. pfile = fopen(strFile,"rb");//打開檔案
108.   
109. if(pfile!=NULL)  
110. {  
111. "file bkwood.bmp open success.\n");  
112. //讀取位圖檔案頭資訊
113. WORD
114. sizeof(WORD),pfile);  
115. if(fileType != 0x4d42)  
116.    {  
117. "file is not .bmp file!");  
118. return;  
119.    }  
120. //fseek(pfile,2,SEEK_CUR);   // "BM"
121. sizeof(tagBITMAPFILEHEADER),pfile);  
122.     
123.    showBmpHead(&bitHead);  
124. "\n\n");  
125.   
126. //讀取位圖資訊頭資訊
127. sizeof(BITMAPINFOHEADER),pfile);  
128.    showBmpInforHead(&bitInfoHead);  
129. "\n");  
130. }  
131. else
132. {  
133. "file open fail!\n");  
134. return;  
135. }  
136.   
137.   
138. tagRGBQUAD *pRgb ;  
139.   
140. if(bitInfoHead.biBitCount < 24)//有調色闆
141. {   
142. //讀取調色盤結資訊
143. long nPlantNum = long(pow(2,double(bitInfoHead.biBitCount)));    //   Mix color Plant Number;
144. sizeof(tagRGBQUAD));   
145. sizeof(tagRGBQUAD));  
146. int
147.     
148. "Color Plate Number: %d\n",nPlantNum);  
149.   
150. "顔色闆資訊:\n");  
151. for (int
152.    {  
153. if
154.     {  
155. "\n");  
156.     }  
157.     showRgbQuan(&pRgb[i]);  
158.      
159.    }  
160.   
161. "\n");  
162.     
163. }  
164.   
165.   
166. int
167. int
168. //配置設定記憶體空間把源圖存入記憶體   
169. int l_width   = WIDTHBYTES(width* bitInfoHead.biBitCount);//計算位圖的實際寬度并確定它為32的倍數
170. BYTE    *pColorData=(BYTE
171. memset(pColorData,0,height*l_width);     
172. long
173.   
174. //把位圖資料資訊讀到數組裡   
175. fread(pColorData,1,nData,pfile);     
176.       
177.   
178.   
179. //将位圖資料轉化為RGB資料
180. tagRGBQUAD* dataOfBmp;  
181. dataOfBmp = (tagRGBQUAD *)malloc(width*height*sizeof(tagRGBQUAD));//用于儲存各像素對應的RGB資料
182. memset(dataOfBmp,0,width*height*sizeof(tagRGBQUAD));  
183.   
184. if(bitInfoHead.biBitCount<24)//有調色闆,即位圖為非真彩色 
185. {  
186. int
187. int
188. if
189.    {  
190. for(int
191. for(int
192.      {  
193. BYTE
194. //k:取得該像素顔色資料在實際資料數組中的序号
195. //j:提取目前像素的顔色的具體值    
196.       mixIndex = pColorData[k];  
197. switch(j%8)  
198.       {  
199. case
200.        mixIndex = mixIndex<<7;  
201.        mixIndex = mixIndex>>7;  
202. break;  
203. case
204.        mixIndex = mixIndex<<6;  
205.        mixIndex = mixIndex>>7;  
206. break;  
207. case
208.        mixIndex = mixIndex<<5;  
209.        mixIndex = mixIndex>>7;  
210. break;  
211.   
212. case
213.        mixIndex = mixIndex<<4;  
214.        mixIndex = mixIndex>>7;  
215. break;  
216. case
217.        mixIndex = mixIndex<<3;  
218.        mixIndex = mixIndex>>7;  
219. break;  
220.   
221. case
222.        mixIndex = mixIndex<<2;  
223.        mixIndex = mixIndex>>7;  
224. break;  
225. case
226.        mixIndex = mixIndex<<1;  
227.        mixIndex = mixIndex>>7;  
228. break;  
229.   
230. case
231.        mixIndex = mixIndex>>7;  
232. break;  
233.       }  
234.   
235. //将像素資料儲存到數組中對應的位置
236.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;  
237.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;  
238.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;  
239.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;  
240.       index++;  
241.   
242.     }  
243.    }  
244.   
245. if(bitInfoHead.biBitCount==2)  
246.    {  
247. for(int
248. for(int
249.      {  
250. BYTE
251. //k:取得該像素顔色資料在實際資料數組中的序号
252. //j:提取目前像素的顔色的具體值    
253.       mixIndex = pColorData[k];  
254. switch(j%4)  
255.       {  
256. case
257.        mixIndex = mixIndex<<6;  
258.        mixIndex = mixIndex>>6;  
259. break;  
260. case
261.        mixIndex = mixIndex<<4;  
262.        mixIndex = mixIndex>>6;  
263. break;  
264. case
265.        mixIndex = mixIndex<<2;  
266.        mixIndex = mixIndex>>6;  
267. break;  
268. case
269.        mixIndex = mixIndex>>6;  
270. break;  
271.       }  
272.   
273. //将像素資料儲存到數組中對應的位置
274.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;  
275.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;  
276.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;  
277.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;  
278.       index++;  
279.   
280.   
281.      }  
282.    }  
283. if(bitInfoHead.biBitCount == 4)  
284.    {  
285. for(int
286. for(int
287.      {  
288. BYTE
289.       k = i*l_width + j/2;  
290.       mixIndex = pColorData[k];  
291. if(j%2==0)  
292. //低      
293.        mixIndex = mixIndex<<4;  
294.        mixIndex = mixIndex>>4;  
295.       }  
296. else
297. //高
298.        mixIndex = mixIndex>>4;  
299.       }  
300.   
301.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;  
302.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;  
303.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;  
304.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;  
305.       index++;  
306.   
307.      }  
308.   
309.    }  
310. if(bitInfoHead.biBitCount == 8)  
311.    {  
312. for(int
313. for(int
314.      {  
315. BYTE
316.   
317.       k = i*l_width + j;  
318.   
319.       mixIndex = pColorData[k];  
320.   
321.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;  
322.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;  
323.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;  
324.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;  
325.       index++;  
326.        
327.        
328.   
329.      }  
330.    }  
331. if(bitInfoHead.biBitCount == 16)  
332.    {  
333. for(int
334. for(int
335.      {  
336. WORD
337.   
338.       k = i*l_width + j*2;  
339. WORD
340.       shortTemp = pColorData[k+1];  
341.       shortTemp = shortTemp<<8;  
342.       
343.       mixIndex = pColorData[k] + shortTemp;  
344.   
345.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;  
346.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;  
347.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;  
348.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;  
349.       index++;  
350.      }  
351.    }  
352. }  
353. else//位圖為24位真彩色
354. {  
355. int
356. int
357. for(int
358. for(int
359.     {  
360.      k = i*l_width + j*3;  
361.      dataOfBmp[index].rgbRed = pColorData[k+2];     
362.      dataOfBmp[index].rgbGreen = pColorData[k+1];     
363.      dataOfBmp[index].rgbBlue = pColorData[k];      
364.      index++;  
365.     }              
366. }  
367.   
368.   
369. printf("像素資料資訊:\n");  
370. for (int
371. {  
372. if
373.    {  
374. "\n");  
375.    }  
376.    showRgbQuan(&dataOfBmp[i]);  
377. }  
378.   
379. fclose(pfile);   
380. if
381. {  
382.    free(pRgb);  
383. }  
384. free(dataOfBmp);  
385. free(pColorData);  
386. printf("\n");  
387.   
388. }