一、解压缩操作过程

  1. 为JPEG对象分配空间并初始化
  2. 指定解压缩数据源
  3. 获取文件信息
  4. 为解压缩设定参数,包括图像大小,颜色空间
  5. 开始解压缩
  6. 取出数据
  7. 解压缩完毕
  8. 释放资源

1.1 为JPEG对象分配空间并初始化

解压缩过程中使用的JPEG对象是一个jpeg_decompress_struct的结构体。同时还需要定义一个用于错误处理的结构体对象,IJG中标准的错误结构体是jpeg_error_mgr。

struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;

然后是将错误处理结构对象绑定在JPEG对象上。

cinfo.err = jpeg_std_error(&jerr);

这个标准的错误处理结构将使程序在出现错误时调用exit()退出程序,如果不希望使用标准的错误处理方式,则可以通过自定义退出函数的方法自定义错误处理结构,详情见文章后面的专门章节。

初始化cinfo结构。

jpeg_create_decompress(&cinfo);

1.2 指定解压缩数据源

利用标准C中的文件指针传递要打开的jpg文件。

FILE * infile;
if ((infile = fopen("sample.jpg", "rb")) == NULL)
{return 0;
}jpeg_stdio_src(&cinfo, infile);

1.3 获取文件信息

IJG将图像的缺省信息填充到cinfo结构中以便程序使用。

(void) jpeg_read_header(&cinfo, TRUE);

此时,常见的可用信息包括图像的宽cinfo.image_width,高cinfo.image_height,色彩空间cinfo.jpeg_color_space,颜色通道数cinfo.num_components等。

1.4 为解压缩设定参数

在完成jpeg_read_header调用后,开始解压缩之前就可以进行解压缩参数的设定,也就是为cinfo结构的成员赋值。

比如可以设定解出来的图像的大小,也就是与原图的比例。使用scale_num和scale_denom两个参数,解出来的图像大小就是scale_num/scale_denom,但是IJG当前仅支持1/1, 1/2, 1/4,和1/8这几种缩小比例。

比如要取得1/2原图的图像,需要如下设定:

cinfo.scale_num=1;
cinfo.scale_denom=2;

也可以设定输出图像的色彩空间,即cinfo.out_color_space,可以把一个原本彩色的图像由真彩色JCS_RGB变为灰度JCS_GRAYSCALE。如:

cinfo.out_color_space=JCS_GRAYSCALE;

1.5 开始解压缩

根据设定的解压缩参数进行图像解压缩操作。

(void) jpeg_start_decompress(&cinfo);

在完成解压缩操作后,IJG就会将解压后的图像信息填充至cinfo结构中。比如,输出图像宽度cinfo.output_width,输出图像高度cinfo.output_height,每个像素中的颜色通道数cinfo.output_components(比如灰度为1,全彩色为3)等。

一般情况下,这些参数是在jpeg_start_decompress后才被填充到cinfo中的,如果希望在调用jpeg_start_decompress之前就获得这些参数,可以通过调用jpeg_calc_output_dimensions()的方法来实现。

1.6 取出数据

解开的数据是按照行取出的,数据像素按照scanline来存储,scanline是从左到右,从上到下的顺序,每个像素对应的各颜色或灰度通道数据是依次存储,比如一个24-bitRGB真彩色的图像中,一个scanline中的数据存储模式是R,G,B,R,G,B,R,G,B,…,每条scanline是一个JSAMPLE类型的数组,一般来说就是unsigned char,定义于jmorecfg.h中。

除了JSAMPLE,IJG还定义了JSAMPROW和JSAMPARRAY,分别表示一行JSAMPLE和一个2D的JSAMPLE数组。

在此,我们定义一个JSAMPARRAY类型的缓冲区变量来存放图像数据。
JSAMPARRAY buffer;// JSAMPARRAY 等价于 unsigned char * * 类型, 在jpeglib.h中定义

然后是计算每行需要的空间大小,比如RGB图像就是宽度×3,灰度图就是宽度×1

row_stride = cinfo.output_width * cinfo.output_components;

为缓冲区分配空间,这里使用了IJG的内存管理器来完成分配。

JPOOL_IMAGE表示分配的内存空间将在调用jpeg_finish_compress,jpeg_finish_decompress,jpeg_abort后被释放,而如果此参数改为JPOOL_PERMANENT则表示内存将一直到JPEG对象被销毁时才被释放。

row_stride如上所说,是每行数据的实际大小。

最后一个参数是要分配多少行数据。此处只分配了一行。

buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

output_scanline表示当前已经读取的行数,如此即可依次读出图像的所有数据,并填充到缓冲区中,参数1表示的是每次读取的行数。

while (cinfo.output_scanline < cinfo.output_height)
{(void) jpeg_read_scanlines(&cinfo, buffer, 1);//do something
}

1.7 解压缩完毕

(void) jpeg_finish_decompress(&cinfo);

1.8 释放资源

jpeg_destroy_decompress(&cinfo);
fclose(infile);

1.9 退出程序

如果不再需要JPEG对象,则使用

jpeg_destroy_decompress(&cinfo);
//或
jpeg_destroy(&cinfo);

而如果还希望继续使用JPEG对象,则可使用

jpeg_abort_decompress(&cinfo);
//或
jpeg_abort(&cinfo);

1.10 完整例程

//变量定义
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile;
JSAMPARRAY buffer;
int row_stride;
//绑定标准错误处理结构
cinfo.err = jpeg_std_error(&jerr);
//初始化JPEG对象
jpeg_create_decompress(&cinfo);
//指定图像文件
if ((infile = fopen("sample.jpg", "rb")) == NULL)
{return;
}
jpeg_stdio_src(&cinfo, infile);
//读取图像信息
(void) jpeg_read_header(&cinfo, TRUE);
//设定解压缩参数,此处我们将图像长宽缩小为原图的1/2
cinfo.scale_num=1;
cinfo.scale_denom=2;
//开始解压缩图像
(void) jpeg_start_decompress(&cinfo);
//本程序功能是应用GDI+在客户区绘制图像
CClientDC dc(this);
Bitmap bm( cinfo.output_width , cinfo.output_height);
Graphics graphics(dc.GetSafeHdc());
Graphics gdc(&bm);
//分配缓冲区空间
row_stride = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
//读取数据
while (cinfo.output_scanline < cinfo.output_height)
{(void) jpeg_read_scanlines(&cinfo, buffer, 1);//output_scanline是从1开始,所以需要减1int line=cinfo.output_scanline-1;for(int i=0;i<cinfo.output_width;i++){//绘制位图,本例中假设读取的sample.jpg图像为RGB真彩色图像
//因此,实际上cinfo.output_components就等于3,灰度图则需另作处理
bm.SetPixel(i,line,Color(255,(BYTE)buffer[0][i*3],(BYTE)buffer[0][i*3+1],(BYTE)buffer[0][i*3+2]));}
}
//结束解压缩操作
(void) jpeg_finish_decompress(&cinfo);
//释放资源
jpeg_destroy_decompress(&cinfo);
fclose(infile);
//在客户区绘制位图
graphics.DrawImage(&bm,0,0);

二、压缩操作过程

  1. 为JPEG对象分配空间并初始化
  2. 指定图像输出目标
  3. 为压缩设定参数,包括图像大小,颜色空间
  4. 开始压缩
  5. 写入数据
  6. 压缩完毕
  7. 释放资源

2.1 为JPEG对象分配空间并初始化

压缩过程中使用的JPEG对象是一个jpeg_compress_struct的结构体。同时还需要定义一个用于错误处理的结构体对象,IJG中标准的错误结构体是jpeg_error_mgr。

struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;

然后是将错误处理结构对象绑定在JPEG对象上。

cinfo.err = jpeg_std_error(&jerr);

这个标准的错误处理结构将使程序在出现错误时调用exit()退出程序,如果不希望使用标准的错误处理方式,则可以通过自定义退出函数的方法自定义错误处理结构,详情见文章后面的专门章节。

初始化cinfo结构。

jpeg_create_compress(&cinfo);

2.2 指定图像输出目标

利用标准C中的文件指针传递要输出的jpg文件。

FILE * outfile;
if ((outfile = fopen(filename, "wb")) == NULL)
{return 0;
}
jpeg_stdio_dest(&cinfo, outfile);

2.3 为压缩设定参数

在开始压缩数据之前需要为压缩指定几个参数和缺省参数。

设定缺省参数之前需要指定的几个参数是:图像宽度cinfo.image_width,图像高度cinfo.image_height,图像的颜色通道数cinfo.input_components(比如RGB图像为3,灰度图为1),图像颜色空间cinfo.in_color_space(比如真彩色JCS_RGB,灰度图JCS_GRAYSCALE)。

如:

cinfo.image_width = 800;
cinfo.image_height = 600;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;

然后是设定缺省设置

jpeg_set_defaults(&cinfo);

注意此处,在set default之前,必须设定in_color_space,因为某些缺省参数的设定需要正确的color space值。

在此之后还可以对其他的一些参数进行设定。具体有哪些参数可以查询libjpeg.doc文档。

比如最常用的一个参数就是压缩比。

jpeg_set_quality(&cinfo, quality, TRUE);

quality是个0~100之间的整数,表示压缩比率。

2.4 开始压缩

根据设定的压缩参数进行图像压缩操作。

jpeg_start_compress(&cinfo, TRUE);

开始压缩过程后就不可以修改cinfo对象参数。

2.5 写入数据

row_stride = image_width * 3;    //假设用到的图示RGB真彩色三通道

同上文介绍的解压缩操作中介绍的,要写入的数据是按照行写入的,数据像素按照scanline来存储,与读取数据的不同是使用jpeg_write_scanlines。

类似于解压缩操作中的cinfo.output_scanline < cinfo.output_height机制,压缩过程使用的cinfo.next_scanline < cinfo.image_height来判断是否完成写入数据。

在此,假设image_buffer是个JSAMPARRAY类型变量,其中保存的是要输出的图像数据,比如可以是用上文中的解压缩操作从某JPEG文件中获得的数据。

JSAMPROW row_pointer;
while (cinfo.next_scanline < cinfo.image_height)
{//找到图像中的某一行,写入目标文件row_pointer = image_buffer[cinfo.next_scanline];(void) jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}

2.6 压缩完毕

jpeg_finish_compress(&cinfo);

2.7 释放资源

fclose(outfile);
jpeg_destroy_compress(&cinfo);

2.8 退出程序

如果不再需要JPEG对象,则使用

jpeg_destroy_compress(&cinfo);
//或
jpeg_destroy(&cinfo);

而如果还希望继续使用JPEG对象,则可使用

jpeg_abort_compress(&cinfo);
//或
jpeg_abort(&cinfo);

2.9 完整例程

//变量定义
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile;
JSAMPROW row_pointer;
int row_stride;
//绑定标准错误处理结构
cinfo.err = jpeg_std_error(&jerr);
//初始化JPEG对象
jpeg_create_compress(&cinfo);
//指定目标图像文件
if ((outfile = fopen("dest.jpg", "wb")) == NULL)
{return;
}
jpeg_stdio_dest(&cinfo, outfile);
//设定压缩参数
cinfo.image_width = image_width;
cinfo.image_height = image_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
//此处设压缩比为90%
jpeg_set_quality(&cinfo, 90, TRUE);
//开始压缩
jpeg_start_compress(&cinfo, TRUE);
//假设使用的是RGB图像
row_stride = image_width * 3;
//写入数据
while (cinfo.next_scanline < cinfo.image_height)
{row_pointer = image_buffer[cinfo.next_scanline];(void) jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}//压缩完毕
jpeg_finish_compress(&cinfo);
//释放资源
fclose(outfile);
jpeg_destroy_compress(&cinfo);

三、错误处理

在使用默认错误处理结构jpeg_error_mgr的情况下,程序在遇到错误后将调用exit直接退出程序,用户如果不希望使用这种直接退出的方式处理错误的话就需要自定义错误处理结构。

依照example.c中的例子,IJG推荐使用C语言的setjmp和longjmp机制来重写错误处理结构。

首先,需要定义一个包含标准错误处理结构类型变量的自定义结构。

同时,程序将需要引入头文件setjmp.h。

#include <setjmp.h>
struct my_error_mgr
{struct jpeg_error_mgr pub;jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;

以及一个错误处理函数。在出现错误时程序将跳转到本函数中,而本函数将跳转到setjmp设定的程序位置。

METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{my_error_ptr myerr = (my_error_ptr) cinfo->err;(*cinfo->err->output_message) (cinfo);longjmp(myerr->setjmp_buffer, 1);
}

以解压缩过程为例,原程序将被修改为如下形式。

struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
//此处做了修改
//struct jpeg_error_mgr jerr;
struct my_error_mgr jerr;     FILE * infile;
JSAMPARRAY buffer;
int row_stride;
//此处做了修改
//cinfo.err = jpeg_std_error(&jerr);
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer))
{//在正常情况下,setjmp将返回0,而如果程序出现错误,即调用my_error_exit
//然后程序将再次跳转于此,同时setjmp将返回在my_error_exit中由longjmp第二个参数设定的值1
//并执行以下代码jpeg_destroy_decompress(&cinfo);fclose(infile);return;
}     jpeg_create_decompress(&cinfo);
if ((infile = fopen("sample.jpg", "rb")) == NULL)
{return;
}//以下的代码与上文解压缩操作章节中相同,不再赘述

JPEG图像的解压缩操作相关推荐

  1. PDF可以通过OCR图文识别软件转换为JPEG图像吗

    2019独角兽企业重金招聘Python工程师标准>>> FineReader Mac版,全称ABBYY FineReader Pro for Mac,是一款流行的OCR图文识别软件, ...

  2. nsf5隐写算法 matlab,基于纹理复杂度的JPEG图像自适应隐写

    0 引言 隐写术是一种利用载体的掩蔽效应将秘密信息隐藏,实现信息秘密传递的技术.隐写载体可以是图像.音频.视频.文本等网络数字媒体.由于JPEG已成为目前互联网上进行图像传输时最常用的图像格式,因此以 ...

  3. 使用libjpeg进行JPEG图像解码

     如题:如何对test.jpg进行解码? 注:这里使用libjpeg库进行图像解码.也可以不使用libjpeg库,但是比较繁琐. 直接上代码: #include "jpeglib.h&q ...

  4. 2、OpenCV图像的读写操作

    OpenCV图像的读写操作 概要 图像由像素组成. 像素可以被认为是非常小的正方形结构,当连接在一起时会生成图像. 它们是任何图像的最小组成部分. 如果您仔细查看前面的图像,您将能够在图像中看到一些正 ...

  5. 在 LCD 上显示 jpeg 图像

    1.图片格式有很多,一般最常用的有三种: JPEG(或 JPG). PNG. BMP. 在 LCD 上显示 BMP 图片格式: BMP 图像虽然没有失真.并且解析简单,但是由于图像数据没有进行任何压缩 ...

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

    杀鸡用牛刀? 如果你想对图像进行简单处理,你一般会想到用什么?可能多数人想到的是OpenCV. 对,OpenCV是个非常强大的图像视觉工具库,用途非常广泛.简单的图像处理用它肯定是可以的. 但Open ...

  7. 【原创】JPEG图像密写研究(三) 数据流译码

    [原创]这次更新比较慢,译码过程比想象中复杂一些,更主要是译出来的DCT系数无法确定是否正确,要想验证就需要再进行正向压缩编码,再次形成jpeg图像验证正确,后续工作正在开展,这里就说一说译码的主要思 ...

  8. JPEG图像压缩算法的python实现

    摘要 文章在研究JPEG压缩编码对图像数据压缩的基本原理的基础上,设计了JPEG图像压缩算法程序实现流程,利用 Python语言对程序进行了编写,并实现了对压缩质量进行控制,验证了JPEG压缩编码对图 ...

  9. 理解图像中卷积操作的含义

    原文地址:https://blog.csdn.net/chaipp0607/article/details/72236892?locationNum=9&fps=1 上文用生动的例子来解释卷积 ...

最新文章

  1. 朝聚眼科完成4亿元B轮融资,兰馨亚洲和阳光融汇投资...
  2. ASP.NET文件上传
  3. 《STL源码剖析》学习--6章--power算法分析
  4. 多种脚本语言生成九九乘法口诀表
  5. Debian 忘记root密码的处理(passwd:command not found)
  6. 调用 php_最全的PHP反序列化漏洞的理解和应用
  7. ONVIF Device Manager修改设备密码
  8. 中的数组怎么转成结构体_PLC知识,什么是数组和结构体?
  9. 使用 npm script 的钩子
  10. SpringBoot系列: 单元测试2
  11. 3.Kong入门与实战 基于Nginx和OpenResty的云原生微服务网关 --- Kong 的管理运维
  12. MSP---企业上云需要考虑的问题
  13. android11代码关机
  14. 知网文献最新下载技巧
  15. html圆圈里面问号,UTF-8编码的html页面显示-(问号)而不是字符
  16. 闲时整理3--Android调用指纹验证
  17. 自助点餐php,餐饮类小程序:微信自助点餐小程序v2.0.12_开源完整版前后端源码_已测试...
  18. CrossWalk - Android 动态加载so库文件
  19. signature=5a522a8356f9906b0b775bdada02a4c6,阜阳境内车辆违章信息公示(4月3—4月23)
  20. python部署服务器

热门文章

  1. Python for循环遍历字典(dict)的方法
  2. k8spod生命周期
  3. LeetCode 605[Python]. 种花问题 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
  4. 图神经网络对抗攻击的研究学习(一)
  5. MATLAB simulink 模型验证学习笔记
  6. 1_一些文献中的英文解释和用法整理
  7. 解决桌面图标无法拖动的方法
  8. python通讯录管理系统姓名年龄号码_基于互联网的移动通讯录管理系统的制作方法...
  9. ERP现状及未来发展趋势分析?
  10. 【WCN685X】WCN685X hostapd 设置country码不生效问题分析及解决方案