(原創) 如何使用ANSI C讀寫24/32位元的BMP圖檔? (C/C++) (C) (Image Processing)
Abstract
本文介紹如何使用ANSI C同時讀寫24/32位元的BMP圖檔做簡單的影像處理,並解析BMP格式。
Introduction
在(原創) 如何使用ANSI C讀寫24位元的BMP圖檔? (C/C++) (C) (Image Processing)與(原創) 如何使用ANSI C讀寫32位元的BMP圖檔? (C/C++) (C) (Image Processing)中,我們發現24位元與32位元BMP格式大致上相同,僅有些許的差異,是否能打造一個能同時讀寫24/32位元BMP圖檔的ANSI C程式?
24位元BMP與32位元BMP的差異
在(原創) 如何使用ANSI C讀寫32位元的BMP圖檔? (C/C++) (C) (Image Processing)有詳述兩種BMP的差異,更簡單的說,若我們能從BMP header中得知目前是24位元還是32位元BMP,然後動態的處理1個pixel該用3 byte還是4 byte處理,就能同時讀寫24/32位元BMP。
C語言 / Bmp24_32ReadWrite.c
2 (C) OOMusou 2007 http://oomusou.cnblogs.com
3
4 Filename : Bmp24_32ReadWrite.c
5 Compiler : Visual C++ 8.0 / ANSI C
6 Description : Demo the how to read and write bmp by standard library
7 Release : 05/18/2008 1.0
8 */
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 int upside_down(const char *fname_s, const char *fname_t) {
13 FILE *fp_s = NULL; // source file handler
14 FILE *fp_t = NULL; // target file handler
15 unsigned int x,y; // for loop counter
16 unsigned int width, height; // image width, image height
17 unsigned char *image_s = NULL; // source image array
18 unsigned char *image_t = NULL; // target image array
19 unsigned char R, G, B; // color of R, G, B
20 unsigned int y_avg; // average of y axle
21 unsigned int y_t; // target of y axle
22
23 unsigned char header[54] = {
24 0x42, // identity : B
25 0x4d, // identity : M
26 0, 0, 0, 0, // file size
27 0, 0, // reserved1
28 0, 0, // reserved2
29 54, 0, 0, 0, // RGB data offset
30 40, 0, 0, 0, // struct BITMAPINFOHEADER size
31 0, 0, 0, 0, // bmp width
32 0, 0, 0, 0, // bmp height
33 1, 0, // planes
34 24, 0, // bit per pixel
35 0, 0, 0, 0, // compression
36 0, 0, 0, 0, // data size
37 0, 0, 0, 0, // h resolution
38 0, 0, 0, 0, // v resolution
39 0, 0, 0, 0, // used colors
40 0, 0, 0, 0 // important colors
41 };
42
43 unsigned int file_size; // file size
44 unsigned int rgb_raw_data_offset; // RGB raw data offset
45 unsigned short bit_per_pixel; // bit per pixel
46 unsigned short byte_per_pixel; // byte per pixel
47
48 fp_s = fopen(fname_s, "rb");
49 if (fp_s == NULL) {
50 printf("fopen fp_s error\n");
51 return -1;
52 }
53
54 // move offset to 10 to find rgb raw data offset
55 fseek(fp_s, 10, SEEK_SET);
56 fread(&rgb_raw_data_offset, sizeof(unsigned int), 1, fp_s);
57 // move offset to 18 to get width & height;
58 fseek(fp_s, 18, SEEK_SET);
59 fread(&width, sizeof(unsigned int), 1, fp_s);
60 fread(&height, sizeof(unsigned int), 1, fp_s);
61 // get bit per pixel
62 fseek(fp_s, 28, SEEK_SET);
63 fread(&bit_per_pixel, sizeof(unsigned short), 1, fp_s);
64 byte_per_pixel = bit_per_pixel / 8;
65 // move offset to rgb_raw_data_offset to get RGB raw data
66 fseek(fp_s, rgb_raw_data_offset, SEEK_SET);
67
68 image_s = (unsigned char *)malloc((size_t)width * height * byte_per_pixel);
69 if (image_s == NULL) {
70 printf("malloc images_s error\n");
71 return -1;
72 }
73
74 image_t = (unsigned char *)malloc((size_t)width * height * byte_per_pixel);
75 if (image_t == NULL) {
76 printf("malloc image_t error\n");
77 return -1;
78 }
79
80 fread(image_s, sizeof(unsigned char), (size_t)(long)width * height * byte_per_pixel, fp_s);
81
82 // vertical inverse algorithm
83 y_avg = 0 + (height-1);
84
85 for(y = 0; y != height; ++y) {
86 for(x = 0; x != width; ++x) {
87 R = *(image_s + byte_per_pixel * (width * y + x) + 2);
88 G = *(image_s + byte_per_pixel * (width * y + x) + 1);
89 B = *(image_s + byte_per_pixel * (width * y + x) + 0);
90
91 y_t = y_avg - y;
92
93 *(image_t + byte_per_pixel * (width * y_t + x) + 2) = R;
94 *(image_t + byte_per_pixel * (width * y_t + x) + 1) = G;
95 *(image_t + byte_per_pixel * (width * y_t + x) + 0) = B;
96 }
97 }
98
99 // write to new bmp
100 fp_t = fopen(fname_t, "wb");
101 if (fp_t == NULL) {
102 printf("fopen fname_t error\n");
103 return -1;
104 }
105
106 // file size
107 file_size = width * height * byte_per_pixel + rgb_raw_data_offset;
108 header[2] = (unsigned char)(file_size & 0x000000ff);
109 header[3] = (file_size >> 8) & 0x000000ff;
110 header[4] = (file_size >> 16) & 0x000000ff;
111 header[5] = (file_size >> 24) & 0x000000ff;
112
113 // width
114 header[18] = width & 0x000000ff;
115 header[19] = (width >> 8) & 0x000000ff;
116 header[20] = (width >> 16) & 0x000000ff;
117 header[21] = (width >> 24) & 0x000000ff;
118
119 // height
120 header[22] = height &0x000000ff;
121 header[23] = (height >> 8) & 0x000000ff;
122 header[24] = (height >> 16) & 0x000000ff;
123 header[25] = (height >> 24) & 0x000000ff;
124
125 // bit per pixel
126 header[28] = bit_per_pixel;
127
128 // write header
129 fwrite(header, sizeof(unsigned char), rgb_raw_data_offset, fp_t);
130 // write image
131 fwrite(image_t, sizeof(unsigned char), (size_t)(long)width * height * byte_per_pixel, fp_t);
132
133 fclose(fp_s);
134 fclose(fp_t);
135
136 return 0;
137 }
138
139 int main() {
140 upside_down("capture32.bmp", "capture32_v.bmp");
141 }
原圖(24位元)
執行結果(24位元)
原圖(32位元)
執行結果(32位元)
34行
暫時將header設定為24位元,後面會依照實際狀況去修改。
45、46行
unsigned short byte_per_pixel; // byte per pixel
新增兩個變數,判斷bit per pixel與byte per pixel。
61行
fseek(fp_s, 28, SEEK_SET);
fread(&bit_per_pixel, sizeof(unsigned short), 1, fp_s);
byte_per_pixel = bit_per_pixel / 8;
從BMP header讀出目前的圖片是24位元還是32位元,並計算出一個pixel是3 byte還是4 byte,之後就依照byte_per_pixel變數去做運算。
之後的程式,凡事遇到處理byte處,就不用寫死3 byte或4 byte了,完全依照byte_per_pixel決定,道理相同,我們就不在多做解釋。
125行
header[28] = bit_per_pixel;
從新依照實際的bit_per_pixel變數寫入文字檔,不再受限於24 bit。
Conclusion
之所以還分(原創) 如何使用ANSI C讀寫32位元的BMP圖檔? (C/C++) (C) (Image Processing)討論32位元BMP,實為了解說方便,在實務上,建議用此程式,可一次讀寫24/32位元BMP。
See Also
(原創) 如何使用ANSI C讀寫24位元的BMP圖檔? (C/C++) (C) (Image Processing)
(原創) 如何使用ANSI C讀寫32位元的BMP圖檔? (C/C++) (C) (Image Processing)
(原創) 如何將CMOS所擷取的影像傳到PC端? (IC Design) (DE2)
(原創) 如何使用ISO C++讀寫bmp圖檔? (C/C++) (Image Processing)
(原創) 如何使用C++/CLI读/写jpg檔? (C++/CLI)
(原創) 如何用程序的方式载入jpg图形文件? (C#/ASP.NET)
Reference
[1] swwuyam的BMP檔案格式
[2] Charles Petzold 1998, Programming Windows, Microsoft Press
瘋小貓的華麗冒險的點陣圖(Bitmap)檔案格式
BMP文件格式分析
賴岱佑、劉敏 2007,數位影像處理 技術手冊,文魁資訊
井上誠喜、八木申行、林 正樹、中須英輔、三古公二、奧井誠人 著 2006,吳上立,林宏燉 編譯,C語言數位影像處理,全華出版社