首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CImg:插件(plugin)使用说明塈实现JPEG图像内存编码/解码

CImg:插件(plugin)使用说明塈实现JPEG图像内存编码/解码

作者头像
10km
发布2019-05-25 20:53:32
1.2K0
发布2019-05-25 20:53:32
举报
文章被收录于专栏:10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433470

杀鸡用牛刀?

如果你想对图像进行简单处理,你一般会想到用什么?可能多数人想到的是OpenCV。

对,OpenCV是个非常强大的图像视觉工具库,用途非常广泛。简单的图像处理用它肯定是可以的。

但OpenCV实在太庞大了,用起来有时反而不方便,就好比你现在肚子饿了只想简单吃个午饭,你是选择街边的饭馆买一份快餐15分钟解决问题,还是打电话给高级西餐厅订个位子要排队等到下周一才能吃上?

用OpenCV完成一些简单的图像处理就好比用一把牛刀杀鸡,能用但不好用,比如要写一个简单的测试程序,需要加载显示图像文件,并对图像做一个简单的处理(缩放,旋转,绘图),用OpenCV,就要多一个依赖库,可能还要为此编译OpenCV.而库中90%的功能都用不到,想想就好麻烦。

CImg是一个小型的C++语言跨平台的图像处理开源库,有多小型?核心代码只有一个CImg.h文件。但是这个工具就像一把瑞士军刀,拥有的功能却非常多,常用的图像显示,图像格式解码,矩阵运算,色彩空间转换,简单绘图。。。等等该有的功能都有了。

所以我在写一些没有性能有要求的测试程序的时候,会选择用CImg来完成,没有依赖库,编译出的代码到哪里都能运行,方便啊,不然呢,你写个测试程序给客户,客户的电脑上不了,为啥没装OpenCV,客户问啥是OpenCV?..你再解释一遍,再教客户安装OpenCV,想想都头大。

CImg插件

几年前就用过CImg,当时是用它在测试程序中做简单的图像显示,非常方便。在libjpeg的支持下也用它加载JPEG图像文件,当时还在困扰CImg没有提供对JPEG格式图像内存编码/解码的功能。所以为此花了挺大精力自己实现了jpeg图像的内存编码/解码功能,参见我之前的博文:

《libjpeg:实现jpeg内存解压缩塈转换色彩空间/压缩分辨率》 《libjpeg:实现jpeg内存压缩暨error_exit错误异常处理和个性化参数设置》

最近的工作中又要对JPEG图像进行内存解码了,原打算用之前写的代码,但我重新看了CImg的代码。才发现CImg在核心代码CImg.h之外还提供了很多插件(plugins),如下:

    │  CImg.h
	│  README.md
	│
	├─examples
	│  └─img
	│
	├─plugins
	│      add_fileformat.h
	│      bayer.h
	│      chlpca.h
	│      cvMat.h
	│      draw_gradient.h
	│      inpaint.h
	│      ipl.h
	│      ipl_alt.h
	│      jpeg_buffer.h
	│      loop_macros.h
	│      matlab.h
	│      nlmeans.h
	│      patchmatch.h
	│      skeleton.h
	│      tiff_stream.h
	│      tinymatwriter.h
	│      vrml.h
	│      vtk.h
	│
	└─resources
	        compile_win_icl.bat
	        compile_win_visualcpp.bat

可以看到有个plugins\jpeg_buffer.h,就是实现jpeg内存压缩和解压缩的。有了这个插件的支持,CImg类就多了load_jpeg_buffersave_jpeg_buffer两个成员函数,分别用于jpeg文件的压缩和解压缩。具体怎么用呢?examples文件夹下use_jpeg_buffer.cpp就是示例代码。以下代码来自use_jpeg_buffer.cpp,本文作者只是添加了中文注释

#include <cstdio>
// JPEG文件的读写需要libjpeg的支持,所以这里必须要include jpeglib.h jerror.h
#include <jpeglib.h>
#include <jerror.h>

// 这一行放在#include "CImg.h"前面,用于将jpeg_buffer.h插件加入CImg类的定义
// CImg.h中有多个下面这样的代码,将你定义的插件头文件 include到CImg类定义中
//             #ifdef cimg_plugin
//             #include cimg_plugin
//             #endif
//             #ifdef cimg_plugin1
//             #include cimg_plugin1
//             #endif
// 以此类推,如果你同时也想让CImg对象能转换成OpenCV的矩阵对象cv::Mat
// 就可以定义 cimg_plugin1 为  "plugins/cvMat.h"
//            #define cimg_plugin1 "plugins/cvMat.h"
#define cimg_plugin "plugins/jpeg_buffer.h"
#include "CImg.h"
using namespace cimg_library;
int main() {

  // 将一个JPEG文件的数据读取到内存缓冲区 'buffer_input'中
  const char *filename_input = "foo.jpg";
  std::fprintf(stderr," - Reading file '%s'\n",filename_input);
  std::FILE *file_input = std::fopen(filename_input,"rb");
  if (!file_input) { std::fprintf(stderr,"Input JPEG file not found !"); std::exit(0); }

  std::fprintf(stderr," - Construct input JPEG-coded buffer\n");
  unsigned buf_size = 500000; // 这里定义文件长度
  JOCTET *buffer_input = new JOCTET[buf_size];
  if (std::fread(buffer_input,sizeof(JOCTET),buf_size,file_input)) std::fclose(file_input);
  // -> 'buffer_input' is now a valid jpeg-coded memory buffer.

  std::fprintf(stderr," - Create CImg instance from JPEG-coded buffer\n");
  CImg<unsigned char> img;
  // 将内存缓冲区 'buffer_input'中的图像数据调用load_jpeg_buffer函数实现内存解压缩,buffer_input用完就可以删除了。
  img.load_jpeg_buffer(buffer_input, buf_size);
  delete[] buffer_input;

  // 然后你可以在CImg对象上做你想要的图像处理,比如下面的代码在图像上写文字 ‘ Hello!’,并显示出来
  std::fprintf(stderr," - Do simple processing\n");
  const unsigned char purple[] = { 255, 0, 0 };
  const unsigned char black[] = { 0, 0, 0 };
  img.mirror('y').draw_text(0,0,"   Hello!   ",purple,black,1,57);

  // Display image to see if everything's fine.
  img.display("Using 'jpeg_buffer.h' plugin");

  // 定义一个JPEG压缩输出缓冲区,因为无法预测JPEG压缩输出的数据尺寸,所以这里定义了原文件尺寸2倍。
  // 实际应用中为保险起见,应该以图像分辨率来决定缓冲区的大小, 
  std::fprintf(stderr," - Construct output JPEG-coded buffer\n");
  JOCTET *buffer_output = new JOCTET[2*buf_size];

  // 调用save_jpeg_buffer函数将处理过的CImg对象的图像数据压缩成JPEG格式写入输出缓冲区‘buffer_output ’
  // 调用结束时'buf_size'中会输出实际输出的数据长度
  img.save_jpeg_buffer(buffer_output,buf_size,60);


  // 将输出缓冲区‘buffer_output ’中的JPEG图像数据写入一个新文件
  const char *filename_output = "foo_output.jpg";
  std::fprintf(stderr," - Save output file '%s'\n",filename_output);
  std::FILE* file_output = std::fopen(filename_output,"wb");
  std::fwrite(buffer_output, sizeof(JOCTET), buf_size, file_output);
  std::fclose(file_output);
  delete[] buffer_output;

  std::fprintf(stderr," - All done !\n");
  return 0;
}

使用很简单吧?

示例代码虽然啰里啰嗦一大堆,关键代码其实就只有两行。唉,几年我要是多仔细看CImg一眼,知道plugins下还有宝可挖,我又何必费力自己实现JPEG内存解码呢,重复发明轮子,真的好无奈。

运行DEMO

如果你想知道CImg可以都能干哪些工作,运行一下它的demo就知道了。

windows下编译DEMO很简单,在执行resources文件夹下的批处理程序\compile_win_visualcpp.bat就会自动编译所有的DEMO,因为CImg.h文件很大,所以编译的时间有点久。

编译完成之后,运行CImg_demo.exe就会出现下面的界面,你可以选择你要执行的DEMO程序

NOTE

本文作者写这篇文章时用的CImg版本还是几年前下载的1.6.8,现在CImg的版本已经升级到2.3.6了,参见CImg官网 http://cimg.eu/ 或 github 上的官方仓库: https://github.com/dtschump/CImg/releases

另外作为一个简单小型的图像处理工具库,它有啥缺点呢?我觉得就最大的缺点就是编译时间偏长,CImg.h一个头文件就有2.8MB,编译这么大的源文件,编译器的负载很重,所以编译时间比较长,建议在尽量集中在一个cpp源码中使用CImg.h时不要到处随意#include <CImg.h>,否则会让整项目的代码编译耗时大大增加。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年10月02日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 杀鸡用牛刀?
  • CImg插件
  • 运行DEMO
  • NOTE
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档