当前位置: 首页 > news >正文

(原創) 如何使用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       0x42 0x4d 0 0 0 0 0 0 0 0 ,
33       54 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 1 0 24 0
34       0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
35       0 0 0 0
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 ExpandedBlockStart.gif ContractedBlock.gif /**/ /* 
  2InBlock.gif(C) OOMusou 2007 http://oomusou.cnblogs.com
  3InBlock.gif
  4InBlock.gifFilename    : BmpReadWriteCPP.cpp
  5InBlock.gifCompiler    : Visual C++ 8.0 / gcc 3.4.2 / BCB 6.0 / ISO C++
  6InBlock.gifDescription : Demo the how to read and write bmp by C++
  7InBlock.gifRelease     : 02/28/2007 1.0
  8ExpandedBlockEnd.gif*/

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

 39ExpandedSubBlockEnd.gif  }

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

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

 92ExpandedSubBlockEnd.gif  }

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

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

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

113ExpandedBlockEnd.gif}


87行

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


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

22行

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


也只要傳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語言數位影像處理, 全華

相关文章:

  • (原創) 物件導向與老子思想 (OO)
  • 到底什么是RBD?
  • 前几张一些总结
  • Windows Azure Web Site (16) Azure Web Site HTTPS
  • [程序员学英语]英语国际音标
  • C# vs C++之三:静态构造函数
  • Salesforce中所有常用类型字段的取值与赋值
  • 利用arcgis发布综合又详细的地理定位服务
  • SQL Server 6.5 如何升级到SQL Server 2000—以前原创(二)
  • 驱动注册的probe函数
  • Entity Framework中IQueryable, IEnumerable, IList的差别
  • oracle序列详解
  • Win C盘扩容
  • 下载网站-资源共享
  • 汇编逻辑运算指令
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • docker-consul
  • Java反射-动态类加载和重新加载
  • Linux后台研发超实用命令总结
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • Solarized Scheme
  • spring security oauth2 password授权模式
  • Vue 重置组件到初始状态
  • 阿里云应用高可用服务公测发布
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 深入 Nginx 之配置篇
  • 译米田引理
  • 带你开发类似Pokemon Go的AR游戏
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • (八)Flask之app.route装饰器函数的参数
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (转)iOS字体
  • (转)Sublime Text3配置Lua运行环境
  • .net Signalr 使用笔记
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .net操作Excel出错解决
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • /bin/bash^M: bad interpreter: No such file or directory
  • @column注解_MyBatis注解开发 -MyBatis(15)
  • @javax.ws.rs Webservice注解
  • @JsonSerialize注解的使用
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)
  • [ IOS ] iOS-控制器View的创建和生命周期
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • [<事务专题>]
  • [2008][note]腔内级联拉曼发射的,二极管泵浦多频调Q laser——
  • [android]-如何在向服务器发送request时附加已保存的cookie数据
  • [BT]BUUCTF刷题第4天(3.22)