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

杀鸡用牛刀?

如果你想对图像进行简单处理,你一般会想到用什么?可能多数人想到的是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>,否则会让整项目的代码编译耗时大大增加。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值