专栏首页10km的专栏CImg:插件(plugin)使用说明塈实现JPEG图像内存编码/解码

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

版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/82925459

杀鸡用牛刀?

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • maven:调用ant执行build.xml的两种方式

    sql2java是通过ant脚本组织项目来实现java代码生成的,所以要使用sql2java就必须使用ant来执行build.xml脚本。如果自己项目是用mav...

    用户1148648
  • C++11:如何判断一个类是另一个模板类的子类?

    版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net...

    用户1148648
  • c++:改造cmdline用于MSVC下的命令行参数解析

    版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net...

    用户1148648
  • MBean与JMX源码分析

    JMX(java Management Exetensions)在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。...

    歪歪梯
  • Verilog 99题之001-009

    002. 反相器的速度与哪些因素有关?什么是转换时间(transition time)和传播延迟(propagation delay)?

    NingHeChuan
  • PyTorch专栏(二十一):使用Sequence2Sequence网络和注意力进行翻译

    这可以通过序列到序列网络来实现,其中两个递归神经网络一起工作以将一个序列转换成另一个序列。编码器网络将输入序列压缩成向量,并且解码器网络将该向量展开成新的序列。

    磐创AI
  • Python3 io文本及原始流I/O工具用法详解

    io模块在解释器的内置open()之上实现了一些类来完成基于文件的输入和输出操作。这些类得到了适当的分解,从而可以针对不同的用途重新组合——例如,支持向一个网络...

    砸漏
  • 数据科学系列:数据处理(6)--字符串函数基于R(二)

    承接R&Python Data Science系列:数据处理(5)--字符串函数基于R(一),继续介绍R语言中的字符串函数。

    1480
  • linux或windows环境下pytorch的安装与检查验证(解决runtimeerror问题)

    但是其实这样比较容易断或者出现runtimeout的error。解决办法:使用pip先进行源的查找,在开始下载后停掉,然后找到屏幕上打印出来的链接,手动下载wh...

    砸漏
  • 算法提高 扶老奶奶过街

    AI那点小事

扫码关注云+社区

领取腾讯云代金券