(原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)

Abstract
若要做影像處理,第一件事情就是要能將圖片讀進來變成array,才能套用各種演算法,之前我的作法是用.NET的GDI+,方便雖方便,但缺點就是被綁死在.NET平台,如作SW/HW CoDesign的SystemC,不能使用.NET,又如嵌入式系統,只能在Linux上使用gcc,有沒有僅使用C/C++ standard library,就能夠讀入圖形檔的方式呢?

Introduction
以下這個範例,是個純C的程式,在C++也沒有問題,只需最基本的stdio.h和stdlib.h,唯一的缺憾是只能讀取bmp格式,但若要作影像處理或電腦視覺則已經足夠,也可在SystemC和gcc下編譯。

C語言 / BmpReadWriteC.c

 1 /* 
 2 (C) OOMusou 2007 http://oomusou.cnblogs.com
 3 
 4 Filename    : BmpReadWriteC.c
 5 Compiler    : Visual C++ 8.0 / ANSI C
 6 Description : Demo the how to read and write bmp by standard library
 7 Release     : 02/03/2007 1.0
 8 */
 9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 int bmp_read(unsigned char *image, int xsize, int ysize, const char *filename) {
14   char fname_bmp[128];
15   FILE *fp;
16   unsigned char header[54];
17     
18   sprintf(fname_bmp, "%s.bmp", filename);
19     
20   if (!(fp = fopen(fname_bmp, "rb"))) 
21     return -1;
22       
23   fread(header, sizeof(unsigned char), 54, fp);
24   fread(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 3, fp);
25     
26   fclose(fp);
27   return 0;
28 }
29 
30 int bmp_write(unsigned char *image, int xsize, int ysize, char *filename) {
31   unsigned char header[54= {
32     0x420x4d00000000,
33     54000400000000000010240
34     00000000000000000000
35     0000
36   };
37   long file_size = (long)xsize * (long)ysize * 3 + 54;
38   long width, height;
39   char fname_bmp[128];
40   FILE *fp;
41     
42   header[2= (unsigned char)(file_size &0x000000ff);
43   header[3= (file_size >> 8& 0x000000ff;
44   header[4= (file_size >> 16& 0x000000ff;
45   header[5= (file_size >> 24& 0x000000ff;
46     
47   width = xsize;
48   header[18= width & 0x000000ff;
49   header[19= (width >> 8&0x000000ff;
50   header[20= (width >> 16&0x000000ff;
51   header[21= (width >> 24&0x000000ff;
52     
53   height = ysize;
54   header[22= height &0x000000ff;
55   header[23= (height >> 8&0x000000ff;
56   header[24= (height >> 16&0x000000ff;
57   header[25= (height >> 24&0x000000ff;
58 
59   sprintf(fname_bmp, "%s.bmp", filename);
60     
61   if (!(fp = fopen(fname_bmp, "wb"))) 
62     return -1;
63       
64   fwrite(header, sizeof(unsigned char), 54, fp);
65   fwrite(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 3, fp);
66     
67   fclose(fp);
68   return 0;
69 }
70 
71 int main() {
72   unsigned char *image;
73   int xsize = 512;
74   int ysize = 512;
75 
76   image = (unsigned char *)malloc((size_t)xsize * ysize * 3);
77   if (image == NULL) 
78     return -1;
79       
80   bmp_read(image, xsize, ysize, "clena");
81   bmp_write(image, xsize, ysize, "clena_clone_C");
82     
83   free(image);
84 }


純C的程式好處是compiler門檻低,但現在C++的compiler已經很普遍,而且以上的寫法,缺點就是不能用image[y][x].R這種subscripting的寫法,所以我試著用vector以及C++新的fstream讀取bmp檔。
C++ / BmpReadWriteCPP.cpp

  1/* 
  2(C) OOMusou 2007 http://oomusou.cnblogs.com
  3
  4Filename    : BmpReadWriteCPP.cpp
  5Compiler    : Visual C++ 8.0 / gcc 3.4.2 / BCB 6.0 / ISO C++
  6Description : Demo the how to read and write bmp by C++
  7Release     : 02/28/2007 1.0
  8*/

  9#include <iostream>
 10#include <fstream>
 11#include <vector>
 12
 13using namespace std;
 14
 15struct Color {
 16  int R;
 17  int G;
 18  int B;
 19}
;
 20
 21bool bmpRead(vector<vector<Color> > &imageVec, const char* fileName) {
 22  ifstream file(fileName,ios::in | ios::binary);
 23  if (!file)
 24    return false;
 25    
 26  // skip header
 27  const ifstream::off_type headerSize = 54;
 28  file.seekg(headerSize, ios::beg);
 29  // read body
 30  for(size_t y = 0; y != imageVec.size(); ++y) {
 31    for(size_t x = 0; x != imageVec[0].size(); ++x) {
 32      char chR,chG,chB;
 33      file.get(chB).get(chG).get(chR);
 34      
 35      imageVec[y][x].B = chB;
 36      imageVec[y][x].G = chG;
 37      imageVec[y][x].R = chR;
 38    }

 39  }

 40  
 41  file.close();
 42  
 43  return true;
 44}

 45
 46bool bmpWrite(vector<vector<Color> > &imageVec, const char* fileName) {
 47  const int headerSize = 54;
 48  
 49  char header[headerSize] = {
 50      0x420x4d00000000,
 51        54000400000000000010240
 52        00000000000000000000
 53        0000
 54    }
;
 55    
 56  int ysize = imageVec.size();
 57  int xsize = imageVec[0].size();
 58    
 59  long file_size = (long)ysize * xsize * 3 + 54;
 60  header[2= (unsigned char)(file_size &0x000000ff);
 61  header[3= (file_size >> 8& 0x000000ff;
 62  header[4= (file_size >> 16& 0x000000ff;
 63  header[5= (file_size >> 24& 0x000000ff;
 64    
 65  long width = xsize;
 66  header[18= width & 0x000000ff;
 67  header[19= (width >> 8&0x000000ff;
 68  header[20= (width >> 16&0x000000ff;
 69  header[21= (width >> 24&0x000000ff;
 70    
 71  long height = ysize;
 72  header[22= height &0x000000ff;
 73  header[23= (height >> 8&0x000000ff;
 74  header[24= (height >> 16&0x000000ff;
 75  header[25= (height >> 24&0x000000ff;
 76    
 77  ofstream file(fileName,ios::out | ios::binary);
 78  if (!file)
 79    return false;
 80  
 81  // write header  
 82  file.write(header, headerSize);
 83  // write body
 84  for(size_t y = 0; y != imageVec.size(); ++y) {
 85    for(size_t x = 0; x != imageVec[0].size(); ++x) {
 86      char chB = imageVec[y][x].B;
 87      char chG = imageVec[y][x].G;
 88      char chR = imageVec[y][x].R;
 89      
 90      file.put(chB).put(chG).put(chR);
 91    }

 92  }

 93  
 94  file.close();
 95  
 96  return true;
 97}

 98
 99int main() {
100  const size_t sizey = 512;
101  const size_t sizex = 512;
102  
103  vector<vector<Color> > imageVec(sizey, vector<Color>(sizex));
104  if (!bmpRead(imageVec, "clena.bmp")) {
105    cout << "Read image error!!" << endl;
106    return -1;
107  }

108    
109  if (!bmpWrite(imageVec, "clena_clone_cpp.bmp")) {
110    cout << "Write image error!!" << endl;
111    return -1;
112  }

113}


87行

char chB = imageVec[y][x].B;


使用了subscripting的寫法,將來做影像處理是不是更好寫呢?

22行

bool bmpRead(vector<vector<Color> > &imageVec, const char* fileName)


也只要傳vector reference就好了,不用再傳sizey,sizex。

原圖


Remark
若要詳細研究BMP格式,在Charles Petzold的Programming Windows[2] Ch.15有詳細完整的介紹。

Conclusion
C++的寫法還是比C人性化很多,而且可以使用subscripting方式做影像處理,若您的compiler許可,建議用C++的寫法。

See Also
(原創) 如何使用ANSI C讀寫24位元的BMP圖檔? (C/C++) (C) (Image Processing)
(原創) 如何使用ANSI C讀寫32位元的BMP圖檔? (C/C++) (C) (Image Processing)
(原創) 如何使用ANSI C讀寫24/32位元的BMP圖檔? (C/C++) (C) (Image Processing)
(原創) 如何使用C++/CLI读/写jpg檔? (C++/CLI)
(原創) 如何用程序的方式载入jpg图形文件? (C#/ASP.NET)
(原創) 由一維陣列模擬二維陣列(多維陣列) (C/C++) (C)
(原創) 如何動態建立二維陣列(多維陣列)? (C/C++) (C)

Reference
Charles Petzold 1998, Programming Windows, Microsoft Press
吳上立 / 林宏墩 編著,C語言數位影像處理, 全華

posted on 2007-02-03 18:03  真 OO无双  阅读(29372)  评论(5编辑  收藏  举报

导航