libjpeg用法

  • 博客分类:
  • Linux C编程
  • C语言编程
  • Linux
转自: 点击打开链接

libjpeg库简介

libjpeg是一个完全用C语言编写的库,包含了被广泛使用的JPEG解码、JPEG编码和其他的JPEG功能的实现。这个库由独立JPEG工作组维护。最新版本号是6b,于1998年发布。可以参考维基百科关于libjpeg的介绍http://zh.wikipedia.org/wiki/Libjpeg。

libjpeg库的数据结构
    用libjpeg库解码jpeg数据的时候,最重要的数据类型为struct jpeg_decompress_struct,一般变量定义成cinfo变量,该变量保存着jpeg数据的详细信息,也保存着解码之后输出数据的详细信息。

一般情况下,每次调用libjpeg库API(Application Programming Interface,应用程序编程接口)的时候都需要把这个变量作为第一个参数传入。另外用户也可以通过修改该变量来修改libjpeg行为,比如输出数据格式,libjpeg库可用的最大内存等等。

libjpeg库的使用

1、设置出错处理函数
    “天有不测风云”,我们使用libjpeg库的时候难免会产生错误,所以我们在使用libjpeg解码之前,首先要做好错误处理。在libjpeg库中,实现了默认错误处理函数,当错误发生时,比如如果内存不足(非常可能发生,后面会介绍)等,则默认错误处理函数将会调用exit函数结束整个进程,详细内容可以参考jerror.c文件。这个对于很多用户来说这样的特性是不合适的,不过libjpeg提供了接口让我们注册自定义错误处理函数。

在C语言中没有C++的异常处理机制,但是提供了setjmp和longjmp机制来实现类似的功能,如果你对这个机制不熟悉的话,请查阅C语言手册。

C代码  
  1. <span style="font-size: medium;">struct my_error_mgr {
  2. struct jpeg_error_mgr pub;    /* "public" fields */
  3. jmp_buf setjmp_buffer;    /* for return to caller */
  4. };
  5. typedef struct my_error_mgr * my_error_ptr;
  6. cinfo.err = jpeg_std_error(&jerr.pub);
  7. jerr.pub.error_exit = my_error_exit;
  8. if (setjmp(jerr.setjmp_buffer)) {
  9. jpeg_destroy_decompress(&cinfo);
  10. fclose(infile);
  11. return -1;
  12. }
  13. </span>

上述代码中的重点在于我们向libjpeg注册了一个my_error_exit回调函数,当发生错误的时候,该回调函数将会被调用。然后我们调用setjmp函数,设置一个返回点。这样我们在my_error_exit回调函数处理完错误信息之后,就可以调用longjmp函数返回到这里,在这个返回点进行资源的释放(非常重要,否则将会内存泄漏)。我们再看看my_error_exit回调函数的实现:

C代码  
  1. <span style="font-size: medium;">METHODDEF(void) my_error_exit (j_common_ptr cinfo)
  2. {
  3. my_error_ptr myerr = (my_error_ptr) cinfo->err;
  4. (*cinfo->err->output_message) (cinfo);
  5. longjmp(myerr->setjmp_buffer, 1);
  6. }
  7. </span>

可以通过检查cinfo->err->msg_code的值来判断错误类型,进行相应的处理。本例中只是简单的打印一个错误信息。最后调用longjmp跳转到setjmp调用的地方。

2、初始化解码对象
    要使用libjpeg解码jpeg数据,这步是必须要做的。

C代码  
  1. <span style="font-size: medium;">FILE * infile;
  2. if ((infile = fopen(filename, "rb")) == NULL) {
  3. fprintf(stderr, "can't open %s\n", filename);
  4. return -1;
  5. }
  6. jpeg_create_decompress(&cinfo);
  7. </span>

这步之后,如果结束解码或者出错之后,需要调用jpeg_destroy_decompress销毁解码对象,否则将会内存泄漏。
3、初始化源数据
    在libjpeg库中仅仅提供了文件作为输入数据的接口,在example.c中代码如下:
jpeg_stdio_src(&cinfo, infile);
    这个设计我个人觉得非常不合理,我觉得一个友好的库,需要能够接受各式各样的输入(内存数据,网络数据等等)。比较友好的做法是提供几种常用的输入数据支持(在libjpeg中如:文件输入等)。然后还要提供一个用户注册自定义输入函数(回调函数)的接口,这样库就可以适配现实生活中各式各样的输入数据类型了。Simon也在以前的博文中写过怎样修改libjpeg库,使之能够解码内存buffer中的jpeg数据,请参考《LibJpeg解码内存中的Jpeg数据》http://my.unix-center.net/~Simon_fu/?p=565。当然Simon没有扩展libjpeg库让其支持用户注册自定义输入函数(回调函数),有兴趣的朋友可以自行实现。
4、读取jpeg文件的头信息
    这个和初始化解码对象一样,是必须要调用的,是约定,没什么好说的。

Java代码  
  1. <span style="font-size: medium;">jpeg_read_header(&cinfo, TRUE);
  2. </span>

5、设置解码参数
    很多情况下,这步非常重要。比如设置输出格式,设置scale(缩放)等等功能都是在这一步设置。参数设置通过修改上步得到cinfo的值来实现。这里简单介绍一下一些常用的字段。out_color_space:输出的颜色格式,libjpeg定义如下:

C代码  
  1. <span style="font-size: medium;">typedef enum {
  2. JCS_UNKNOWN,        /* error/unspecified */
  3. JCS_GRAYSCALE,        /* monochrome */
  4. JCS_RGB,        /* red/green/blue */
  5. JCS_YCbCr,        /* Y/Cb/Cr (also known as YUV) */
  6. JCS_CMYK,        /* C/M/Y/K */
  7. JCS_YCCK,        /* Y/Cb/Cr/K */
  8. #ifdef ANDROID_RGB
  9. JCS_RGBA_8888,  /* red/green/blue/alpha */
  10. JCS_RGB_565     /* red/green/blue in 565 format */
  11. #endif
  12. } J_COLOR_SPACE;
  13. </span>

我们可以看出谷歌在Android扩展了几种输出格式,那么如果你需要的颜色格式输出格式libjpeg不支持(比如:YUYV等颜色格式),那么请参考Android对libjpeg的扩展自行修改,不用担心复杂性,实现起来比较easy。请重点研究jdcolor.c文件中的jinit_color_deconverter函数。

scale_num,scale_denom:因为实际的显示设备千变万化,我们可能需要根据实际情况对输出数据进行一些缩放才能够显示。libjpeg支持对输出数据进行缩放(scale),这个变量就是用来设置缩放的参数。目前libjpeg支持1/2,1/4,1/8三种缩放。

mem:可以指定内存管理相关的内容,比如分配和释放内存,指定libjpeg可以使用的最大内存。默认情况下不同的平台下面都有一个libjpeg默认最大可用内存值,比如Android平台上面该值为10000000L(10M),请参考jmemxxxx.c文件中的DEFAULT_MAX_MEM,了解不同平台的默认最大内存值。通过修改mem->pub.max_memory_to_use的值,库的使用者可以自定义libjpeg可以使用的最大内存值。

6、开始解码
    经过前面的参数设置,我们可以开始解码了,没有什么好说的。
jpeg_start_decompress(&cinfo);

7、读取解码数据(我们打印到终端看看)

C代码  
  1. <span style="font-size: medium;">    buffer = (unsigned char *) malloc(cinfo.output_width * cinfo.output_components);
  2. while (cinfo.output_scanline < cinfo.output_height) {
  3. jpeg_read_scanlines(&cinfo, &buffer, 1);
  4. for (i=0; i<cinfo.output_width; i++) {
  5. printf("%02X%02X%02X ", buffer[i*3], buffer[i*3+1], buffer[i*3+2]);
  6. if (0 == ((i+1) % 10) || i == cinfo.output_width-1) {
  7. printf("\n");
  8. }
  9. }
  10. printf("least:%d\n\n", cinfo.output_height-cinfo.output_scanline);
  11. }</span>

请注意虽然函数jpeg_read_scanlines可以指定一次读多少行,但是目前该函数还是只能支持一次只读1行。

8、结束解码

C代码  
  1. <span style="font-size: medium;">jpeg_finish_decompress(&cinfo);
  2. </span>

9、释放解码对象

C代码  
  1. <span style="font-size: medium;">jpeg_destroy_decompress(&cinfo);
  2. </span>

至此一个jpeg数据已经解析完成了。虽然步骤不少但是对于常规的使用还是比较简单的。

总结
    libjpeg对于baseline的jpeg数据解码比较好,但是解码progressive的jpeg数据的时候,对内存的需求比较大(我测试过的progressive的图片曾经发现过消耗70M内存)。如果你的硬件能够有硬件解码jpeg的能力,请尽可能使用硬件解码jpeg数据。
    简单的说baseline   jpeg要全部下载后才能观看;progressive   jpeg采用了类似fgs的技术,可以先传个质量粗糙的,然后逐渐精细,直至全部传完。举个例子,浏览有些网页时,看到图片一行行的出现,但都很清晰,一般都是baseline的,而如果开始就是一大块,但就像有好多马赛克似的,看不清楚,然后慢慢就好了,这种一般就是progressive的了

备注:附件里test.rar里面是test.c文件,包含了示例代码,我测试通过了。内存不用设置的时候,也能处理1.68MB的图片,更大的没试过。jpegsrc.v6b.tar.gz是从官网http://sourceforge.net/projects/libjpeg下载的linux上编译通过了的库源码,如果在linux上用,请不要下载官网的.zip文件,那个在linux下面编译会出错。官网下载地址:http://sourceforge.net/projects/libjpeg/files/libjpeg/6b/jpegsrc.v6b.tar.gz/download 打开是个倒计时下载的。

libjpeg库的用法相关推荐

  1. python gui界面设置数据储存在哪里_我整理的一些常用Python库!让你快速记住这些库的用法!建议收藏...

    Python的一大特色是其丰富的模块,基本上只要你能想到的常见的开发需求,都能找到别人已经实现的库直接使用,或者相关工具或则框架来辅助实现.但这对于新人来说也是一个问题:这么多库,我要从哪里学起?怎样 ...

  2. python xpath语法-Python爬虫之XPath语法和lxml库的用法

    本来打算写的标题是XPath语法,但是想了一下Python中的解析库lxml,使用的是Xpath语法,同样也是效率比较高的解析方法,所以就写成了XPath语法和lxml库的用法 安装 为什么要用这个库 ...

  3. python xpath语法-Python爬虫基础之XPath语法与lxml库的用法详解

    前言 本来打算写的标题是XPath语法,但是想了一下Python中的解析库lxml,使用的是Xpath语法,同样也是效率比较高的解析方法,所以就写成了XPath语法和lxml库的用法 XPath 即为 ...

  4. Python 的 requests 库的用法

    Python爬虫利器一之Requests库的用法:http://cuiqingcai.com/2556.html Python利用Requests库写爬虫(一):http://www.jianshu. ...

  5. python中mako中loop_python中Mako库实例用法

    Mako是一个模板库.一种嵌入式的语言,能够实现简化组件布局以及继承,主要的用途也是和作用域有关,但是效果是最直接切灵活的,这些都是mako的基本功能,掌握了基础内容,接下来就是详细的了解讲述,从几个 ...

  6. Python Pillow(PIL)库的用法介绍(二)

    Python Pillow(PIL)库的用法介绍(二) 在上一篇文章中介绍了Pillow库的一些基本用法,参考:https://blog.csdn.net/weixin_43790276/articl ...

  7. Python binarytree库的用法介绍

    Python binarytree库的用法介绍 binarytree 库是一个 Python 的第三方库.这个库实现了一些二叉树相关的常用方法,使用二叉树时,可以直接调用,不需要再自己实现. 同时,b ...

  8. Python heapq库的用法介绍

    Python heapq库的用法介绍 一.heapq库简介 heapq 库是Python标准库之一,提供了构建小顶堆的方法和一些对小顶堆的基本操作方法(如入堆,出堆等),可以用于实现堆排序算法. 堆是 ...

  9. php tp框架选择题,thinkPHP框架单元测试库tpunit用法示例

    本文实例讲述了thinkPHP框架单元测试库tpunit用法.分享给大家供大家参考,具体如下: thinkphp本身并没有提供相应的单元测试支持,所以这里介绍一个可以对tp进行单元测试的库tpunit ...

最新文章

  1. 从人类交互通信发展简史看元宇宙发展趋势及商业价值
  2. 从零开始学_JavaScript_系列(24)——查看对象属性,合并数组
  3. python3 list 列表 倒序
  4. Zygote启动分析
  5. InstallShield2013 error 6109
  6. MySQL数据库性能优化--SQL优化
  7. ssm插入数据时候栈溢出_程序员算法与数据结构基础中的基础,栈与递归
  8. 独立成分分析ICA系列4:ICA的最优估计方法综述
  9. C/C++新建注册表项实例
  10. c#使用PdfiumViewer展示、打印pdf文档
  11. ACM基础——OJ上的Java代码提交规范
  12. 零基础学python还是c语言-零基础学Python之前需要学c语言吗
  13. 小度wifi驱动的交叉编译及安装
  14. uniapp使用iconfont图标
  15. word一打字就有下划线_word打字自带下划线 为什么WORD打字时总带有下划线,如何解决?...
  16. 【技巧记录】如何批量制作文件夹/文件夹名
  17. 网站被qq拦截应该怎么处理
  18. 启善企业微信自动加好友助手教程
  19. 【前端技术】一篇文章搞掂:CSS
  20. 两个学院少了计算机相关专业招生?985华中科技大学计算机考研

热门文章

  1. 关于前端 后端 数据库 时间的设置与传递
  2. 软件需求规格说明书--文档模板
  3. 转录组表达量计RPKM、FPKM、TPM说明
  4. 待业一年多,我终于找到工作啦,月薪1.5万,双休不加班
  5. Dev-C++常用快捷键
  6. 二次型化标准形的三种方法
  7. idea jdk版本设置
  8. vb程序设计教程第4版龚沛曾 实验答案解析
  9. 传统安防监控摄像头Onvif云台控制直播流如何转换成GB/T28181对接到国标视频平台公安内网
  10. -1岁的产品经理日记——part2(笔经,群面篇)