多媒体应用在现在电子产品中的地位越来越重要,尤其是在嵌入式设备中。本系列文章将会介绍利用libjpeg解码jpeg文件,libpng解码png文件,libgif解码gif文件。本文为第一篇,介绍使用libjpeg解码jpeg文件。

libjpeg简介

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

libjpeg库的数据结构

用libjpeg库解码jpeg数据的时候,最重要的数据类型为struct

jpeg_decompress_struct,一般变量定义成cinfo变量,该变量保存着jpeg数据的详细信息,也保存着解码之后输出数据的详细信

息。一般情况下,每次调用libjpeg库API的时候都需要把这个变量作为第一个参数传入。另外用户也可以通过修改该变量来修改libjpeg行为,比

如输出数据格式,libjpeg库可用的最大内存等等。

libjpeg库的使用

1、设置出错处理函数

“天有不测风云”,我们使用libjpeg库的时候难免会产生错误,所以我们在使用libjpeg解码之前,首先要做好错误处理。在libjpeg库中,

实现了默认错误处理函数,当错误发生时,比如如果内存不足(非常可能发生,后面会介绍)等,则默认错误处理函数将会调用exit函数结束整个进程,详细内

容可以参考jerror.c文件。这个对于很多用户来说这样的特性是不合适的,不过libjpeg提供了接口让我们注册自定义错误处理函数。

在C语言中没有C++的异常处理机制,但是提供了setjmp和longjmp机制来实现类似的功能,如果你对这个机制不熟悉的话,请查阅C语言手册。本文下面的代码片段都是出自libjpeg的example.c文件,可以查阅之。

1: /* We set up the normal JPEG error routines, then override error_exit. */

2: cinfo.err = jpeg_std_error(&jerr.pub);

3: jerr.pub.error_exit = my_error_exit;

4: /* Establish the setjmp return context for my_error_exit to use. */

5: if (setjmp(jerr.setjmp_buffer)) {

6: /* If we get here, the JPEG code has signaled an error.

7: * We need to clean up the JPEG object, close the input file, and return.

8: */

9: jpeg_destroy_decompress(&cinfo);

10: fclose(infile);

11: return 0;

12: }

上述代码中的重点在于我们向libjpeg注册了一个my_error_exit回调函数,当发生错误的时候,该回调函数将会被调用。然后我们调用

setjmp函数,设置一个返回点。这样我们在my_error_exit回调函数处理完错误信息之后,就可以调用longjmp函数返回到这里,在这个

返回点进行资源的释放(非常重要,否则将会内存泄漏)。我们再看看my_error_exit回调函数的实现:

1: /*

2: * Here's the routine that will replace the standard error_exit method:

3: */

4: METHODDEF(void)

5: my_error_exit (j_common_ptr cinfo)

6: {

7: /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */

8: my_error_ptr myerr = (my_error_ptr) cinfo->err;

9:

10: /* Always display the message. */

11: /* We could postpone this until after returning, if we chose. */

12: (*cinfo->err->output_message) (cinfo);

13:

14: /* Return control to the setjmp point */

15: longjmp(myerr->setjmp_buffer, 1);

16: }

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

2、初始化解码对象

要使用libjpeg解码jpeg数据,这步是必须要做的。

1: /* Now we can initialize the JPEG decompression object. */

2: jpeg_create_decompress(&cinfo);

这步之后,如果结束解码或者出错之后,需要调用jpeg_destroy_decompress销毁解码对象,否则将会内存泄漏。

3、初始化源数据

在libjpeg库中仅仅提供了文件作为输入数据的接口,在example.c中代码如下:

1: /* Step 2: specify data source (eg, a file) */

2: jpeg_stdio_src(&cinfo, infile);

这个设计我个人觉得非常不合理,我觉得一个友好的库,需要能够接受各式各样的输入(内存数据,网络数据等等)。比较友好的做法是提供几种常用的输入数据支

持(在libjpeg中如:文件输入等)。然后还要提供一个用户注册自定义输入函数(回调函数)的接口,这样库就可以适配现实生活中各式各样的输入数据类

型了。Simon也在以前的博文中写过怎样修改libjpeg库,使之能够解码内存buffer中的jpeg数据,请参考《LibJpeg解码内存中的Jpeg数据》。当然Simon没有扩展libjpeg库让其支持用户注册自定义输入函数(回调函数),有兴趣的朋友可以自行实现。

4、读取jpeg文件的头信息

这个和初始化解码对象一样,是必须要调用的,是约定,没什么好说的。

1: /* Step 3: read file parameters with jpeg_read_header() */

2: (void) jpeg_read_header(&cinfo, TRUE);

5、设置解码参数

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

out_color_space:输出的颜色格式,libjpeg定义如下:

1: /* Known color spaces. */

2: typedef enum {

3: JCS_UNKNOWN, /* error/unspecified */

4: JCS_GRAYSCALE, /* monochrome */

5: JCS_RGB, /* red/green/blue */

6: JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */

7: JCS_CMYK, /* C/M/Y/K */

8: JCS_YCCK, /* Y/Cb/Cr/K */

9: #ifdef ANDROID_RGB

10: JCS_RGBA_8888, /* red/green/blue/alpha */

11: JCS_RGB_565 /* red/green/blue in 565 format */

12: #endif

13: } J_COLOR_SPACE;

我们可以看出谷歌在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、开始解码

经过前面的参数设置,我们可以开始解码了,没有什么好说的。

1: /* Step 5: Start decompressor */

2: (void) jpeg_start_decompress(&cinfo);

7、读取解码数据

1: /* Here we use the library's state variable cinfo.output_scanline as the

2: * loop counter, so that we don't have to keep track ourselves.

3: */

4: while (cinfo.output_scanline < cinfo.output_height) {

5: /* jpeg_read_scanlines expects an array of pointers to scanlines.

6: * Here the array is only one element long, but you could ask for

7: * more than one scanline at a time if that's more convenient.

8: */

9: (void) jpeg_read_scanlines(&cinfo, buffer, 1);

10: /* Assume put_scanline_someplace wants a pointer and sample count. */

11: put_scanline_someplace(buffer[0], row_stride);

12: }

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

8、结束解码

1: /* Step 7: Finish decompression */

2: (void) jpeg_finish_decompress(&cinfo);

9、释放解码对象

1: /* Step 8: Release JPEG decompression object */

2:

3: /* This is an important step since it will release a good deal of memory. */

4: jpeg_destroy_decompress(&cinfo);

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

总结

libjpeg对于baseline的jpeg数据解码比较好,但是解码progressive的jpeg数据的时候,对内存的需求比较大(我测试过的

progressive的图片曾经发现过消耗70M内存)。如果你的硬件能够有硬件解码jpeg的能力,请尽可能使用硬件解码jpeg数据。

解码jpg图片c语言,图像解码之一——使用libjpeg解码jpeg图片相关推荐

  1. c语言 vc++6.0 插入图片,C语言VC++6.0环境中如何插入图片

    getimage / putimage / loadimage / saveimage 这一组命令和 IMAGE 对象可以实现图像处理的相关功能,下面逐个介绍. (有点类似 tc 中的 imagesi ...

  2. JPEG系列一 JPEG图片的文件格式

    JPEG图片的文件格式 互联网上广泛使用的image/jpeg 图片,准确来说,全称应该叫做使用 JPEG标准压缩图像,使用JFIF标准封装图像数据的图形文件. JPEG 是一个压缩标准,JFIF 是 ...

  3. JPEG系列一 JPEG图片的文件格式

    https://blog.csdn.net/shelldon/article/details/54144406 JPEG图片的文件格式 互联网上广泛使用的image/jpeg 图片,准确来说,全称应该 ...

  4. 如何在电脑/手机上将JPEG图片保存为PDF?

    文章来源:https://www.reneelab.com.cn/convert-jpeg-to-pdf.html 目录 一.JPEG与PDF 二.如何在计算机上将JPEG图片转PDF 1.使用都叫兽 ...

  5. Python实现jpg/png/jpeg图片转base64编码文件

    python实现图片转base64编码文件 #Python实现jpg/png/jpeg图片转base64编码文件 # 打开图片文件(可以是jpg/png/JPEG格式)转为二进制文件 with ope ...

  6. 图像解码之一——使用libjpeg解码jpeg图片

    多媒体应用在现在电子产品中的地位越来越重要,尤其是在嵌入式设备中.本系列文章将会介绍利用libjpeg解码jpeg文件,libpng解码png文件,libgif解码gif文件.本文为第一篇,介绍使用l ...

  7. Python 对图像进行base64编码及解码读取为numpy、opencv、matplot需要的格式

    Python 对图像进行base64编码及解码读取为numpy.opencv.matplot需要的格式 1. 效果图 2. 源码 参考 这篇博客将介绍Python如何对图像进行base64编解码及读取 ...

  8. 【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )

    文章目录 一.获取图像真实宽高 二.计算解码区域 三.设置解码参数 内存复用 像素格式 四.图像绘制 五.执行效果 六.源码及资源下载 官方文档 API : BitmapRegionDecoder 在 ...

  9. 红外线遥控c语言程序,红外线遥控解码接收程序-c语言讲解学习.pdf

    红外线遥控解码接收程序 -C 语言 .txt 铁饭碗的真实含义不是在一个地方吃一辈子饭, 而是一 辈子到哪儿都有饭吃.就算是一坨屎,也有遇见屎壳郎的那天.所以你大可不必为今天的自 己有太多担忧.红外线 ...

  10. 红外接收器c语言软件,红外线遥控解码接收程序_C语言.doc

    红外线遥控解码接收程序_C语言.doc (9页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 红外线遥控解码接收程序-C语言.txt铁饭碗 ...

最新文章

  1. 一只蝙蝠的自述,在朋友圈火了
  2. c语言将ascii码存入eeprom,微机原理复习题答案+_Fixed
  3. C#的访问修饰符Protected
  4. 解决问题ImportError: HDFStore requires PyTables, quot;No module named 'tables'quot; problem importing
  5. 产品经理挑战赛,你敢来吗?
  6. IT公司笔试题总结(三)
  7. glassfish默认密码_在MySQL上使用含盐密码的GlassFish JDBC安全性
  8. matlab里wblrnd函数,matlab随机函数
  9. 斗鱼 虎牙24 小时直播电影教程
  10. 支付宝付款弹窗 被浏览器拦截
  11. R语言使用lm函数构建多元回归模型、并根据模型系数写出回归方程、使用summary函数计算出模型的汇总统计信息(R方、F统计量、残差分布、残差标准误差、系数等)
  12. python批量计算cosine distance
  13. 截止频率的估算-例题
  14. MIGO为玩家带来接近无限的可能
  15. 如何清理占用计算机内存,告诉你如何深度清理电脑内存
  16. MySQL之分库分表
  17. 超过4G如何制作NTFS格式WINPE?
  18. LeetCode695. 岛屿的最大面积(C++版)
  19. android七大主流Android音乐播放器横向评测
  20. 竞赛通知|首届工业数字孪生大赛

热门文章

  1. 古诗词学习-迢迢牵牛星+长歌行+小雅·采薇+敕勒歌+悯农(其一)+小儿垂钓+蝉+正月十五夜+望月怀远+十五夜望月寄杜郎中
  2. C语言 将一个字符串转换为字符,每两个字符间用空格隔开
  3. 个人企业征信体系介绍
  4. java连接打印机并进行打印
  5. date linux 计算日期,科技常识:linux命令详解date使用方法(计算母亲节和父亲节日期脚本示例)...
  6. 100天精通Andriod逆向——第4天:各种抓包工具学习
  7. linux命令里的xz是干嘛的,Ubuntu中的xz命令使用
  8. linux下xz格式,linux下 x.tar.xz格式文件的解压方法
  9. 电动汽车相关功率计算
  10. true launch bar 和 editplus