JPEG图像压缩和解压缩操作

附上压缩和解压缩函数的封装:封装好的压缩和解压缩函数

PEG图像的解压缩操作

 

解压缩操作过程

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

2.        指定解压缩数据源

3.        获取文件信息

4.        为解压缩设定参数,包括图像大小,颜色空间

5.        开始解压缩

6.        取出数据

7.        解压缩完毕

8.        释放资源

 

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);

 

 

 

指定解压缩数据源

 

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

     FILE * infile;

     if ((infile = fopen("sample.jpg", "rb")) == NULL)

     {

         return 0;

     }

     jpeg_stdio_src(&cinfo, infile);

 

获取文件信息

 

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

     (void) jpeg_read_header(&cinfo, TRUE);

 

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

 

为解压缩设定参数

 

在完成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;

 

开始解压缩

 

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

     (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()的方法来实现。

 

 

 

取出数据

 

解开的数据是按照行取出的,数据像素按照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;

 

然后是计算每行需要的空间大小,比如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

     }

 

解压缩完毕

 

     (void) jpeg_finish_decompress(&cinfo);

 

释放资源

 

     jpeg_destroy_decompress(&cinfo);

     fclose(infile);

 

退出程序

 

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

     jpeg_destroy_decompress(&cinfo);

     jpeg_destroy(&cinfo);

 

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

     jpeg_abort_decompress(&cinfo);

     jpeg_abort(&cinfo);

 

 

完整例程

       //变量定义

     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开始,所以需要减1

         int 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);

 

 

JPEG图像的压缩操作

 

 

压缩操作过程

1.         JPEG对象分配空间并初始化

2.         指定图像输出目标

3.         为压缩设定参数,包括图像大小,颜色空间

4.        开始压缩

5.        写入数据

6.        压缩完毕

7.        释放资源

 

 

 

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);

 

指定图像输出目标

 

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

     FILE * outfile;   

     if ((outfile = fopen(filename, "wb")) == NULL)

     {

         return 0;

     }

     jpeg_stdio_dest(&cinfo, outfile);

 

 

为压缩设定参数

 

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

设定缺省参数之前需要指定的几个参数是:图像宽度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之间的整数,表示压缩比率。

 

开始压缩

 

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

     jpeg_start_compress(&cinfo, TRUE);

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

 

 

写入数据

 

     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);

     }

 

压缩完毕

 

     jpeg_finish_compress(&cinfo);

 

释放资源

 

     fclose(outfile);

     jpeg_destroy_compress(&cinfo);

 

退出程序

 

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

     jpeg_destroy_compress(&cinfo);

     jpeg_destroy(&cinfo);

 

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

     jpeg_abort_compress(&cinfo);

     jpeg_abort(&cinfo);

 

 

完整例程

       //变量定义

     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;

     }

     //以下的代码与上文解压缩操作章节中相同,不再赘述

 

THE END.

///

///

前一段时间做了一个项目,项目中有一部分是关于图像采集的,相关硬件有:高速数字相机(我们采用Basler A312f),工控机,服务器。其中相机通过1394视频线连接到工控机的1394视频卡上,工控机与服务器通过百兆网连接。具体工作流程为:工控机控制相机采集图像,帧率一般为40HZ左右,采集的图像为位图数据,需要自己在程序中封装为bmp格式,然后压缩成jpg格式,压缩后的图像一方面保存到本地硬盘,同时要传输到服务器上。

本文只介绍jpeg压缩的相关内容,刚开始我贪图省事,采用微软 的GDI Plus提供的功能,很是方便,先获取jpg图像的编码,然后根据图像的位图数据创建一个Bitmap类的对象,再保存图像为jpg格式就行了。但是我对GDI+还是很信任,总感觉不踏实(没有理由的),我们的系统一旦安装就要长期运行,我就是担心长期运行一段时间后GDI+会出问题,结果也证实了我的担心,我们实际使用的10套系统中有一套,连续运行3-5天后,GDI+就会崩溃,于是我决心采用IJG JPEG Library。您可以到www.ijg.org网站下载libjpeg的源码, IJG JPEG Library就是jpeg压缩库,是以源码的形式提供给软件开发人员的,当然在软件包里也有编译好的库文件,我们这里就只用到其中的libjpeg.lib,jconfig.h,jmorecfg.h,jpeglib.h这几个文件,下面我就介绍一下怎样在自己的程序里嵌入图像压缩功能。

一、建立编译环境

所谓建立编译环境,其实非常简单,就是把上面提到的4个文件拷贝到你的项目文件夹下,把libjpeg.lib添加到你的项目中,然后在你完成压缩功能的那个文件里加入#include "jpeglib.h",需要注意的是,libjpeg.lib是用c语言开发的,如果要用在你的C++程序里,需要用到extern "C",如下:

1. // TestLibjpeg.cpp : Defines the entry point for the console application.
2. //
3.  
4. #include "stdafx.h"
5. #include "memory.h"
6. extern "C" {
7. #include "jpeglib.h"
8. }

二、压缩步骤

1、申请并初始化jpeg压缩对象,同时要指定错误处理器

1. struct jpeg_compress_struct jcs;
2.  
3. // 声明错误处理器,并赋值给jcs.err域
4. struct jpeg_error_mgr jem;
5. jcs.err = jpeg_std_error(&jem);
6.  
7. jpeg_create_compress(&jcs);

2、指定压缩后的图像所存放的目标文件,注意,目标文件应以二进制模式打开

1. f=fopen("03.jpg","wb");
2. if (f==NULL)
3. {
4. delete [] data;
5. delete [] pDataConv;
6. return 0;
7. }
8. jpeg_stdio_dest(&jcs, f);

3、设置压缩参数,主要参数有图像宽、高、色彩通道数(1:索引图像,3:其他),色彩空间(JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像),压缩质量等,如下:

1. jcs.image_width = nWidth;    // 为图的宽和高,单位为像素
2. jcs.image_height = nHeight;
3. jcs.input_components = 1;   // 在此为1,表示灰度图, 如果是彩色位图,则为3
4. jcs.in_color_space = JCS_GRAYSCALE; //JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像
5.  
6. jpeg_set_defaults(&jcs);
7. jpeg_set_quality (&jcs, 80, true);

需要注意的是,jpeg_set_defaults函数一定要等设置好图像宽、高、色彩通道数计色彩空间四个参数后才能调用,因为这个函数要用到这四个值,调用jpeg_set_defaults函数后,jpeglib库采用默认的设置对图像进行压缩,如果需要改变设置,如压缩质量,调用这个函数后,可以调用其它设置函数,如jpeg_set_quality函数。其实图像压缩时有好多参数可以设置,但大部分我们都用不着设置,只需调用jpeg_set_defaults函数值为默认值即可。

4、上面的工作准备完成后,就可以压缩了,压缩过程非常简单,首先调用jpeg_start_compress,然后可以对每一行进行压缩,也可以对若干行进行压缩,甚至可以对整个的图像进行一次压缩,压缩完成后,记得要调用jpeg_finish_compress函数,如下:

01. jpeg_start_compress(&jcs, TRUE);
02.  
03. JSAMPROW row_pointer[1];   // 一行位图
04. int row_stride;      // 每一行的字节数
05.  
06. row_stride = jcs.image_width;  // 如果不是索引图,此处需要乘以3
07.  
08. // 对每一行进行压缩
09. while (jcs.next_scanline < jcs.image_height) {
10. row_pointer[0] = & pDataConv[jcs.next_scanline * row_stride];
11. jpeg_write_scanlines(&jcs, row_pointer, 1);
12. }
13.  
14. jpeg_finish_compress(&jcs);

5、最后就是释放压缩工作过程中所申请的资源了,主要就是jpeg压缩对象,由于在本例中我是直接用的局部变量,所以只需调用jpeg_destroy_compress这个函数即可,如下:

1. jpeg_destroy_compress(&jcs);

三、解压缩步骤

解压缩步骤与压缩步骤非常相似,只是解压缩对象为jpeg_decompress_struct类型,步骤如下:

1、声明并初始化解压缩对象,同时制定错误信息管理器

1. struct jpeg_decompress_struct cinfo;
2. struct jpeg_error_mgr jerr;
3.  
4. cinfo.err = jpeg_std_error(&jerr);
5. jpeg_create_decompress(&cinfo);

2、打开jpg图像文件,并指定为解压缩对象的源文件

1. FILE *f = fopen(strSourceFileName,"rb");
2. if (f==NULL)
3. {
4. printf("Open file error!\n");
5. return;
6. }
7. //
8. jpeg_stdio_src(&cinfo, f);

3、读取图像信息

1. jpeg_read_header(&cinfo, TRUE);

4、根据图像信息申请一个图像缓冲区

1. data = new BYTEcinfo.image_width*cinfo.image_height*cinfo.num_components];

5、开始解压缩

01. jpeg_start_decompress(&cinfo);
02.  
03. JSAMPROW row_pointer[1];
04. while (cinfo.output_scanline < cinfo.output_height)
05. {
06. row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.image_width*cinfo.num_components];
07. jpeg_read_scanlines(&cinfo,row_pointer ,
08. 1);
09. }
10. jpeg_finish_decompress(&cinfo);

6、释放资源

1. jpeg_destroy_decompress(&cinfo);
2.  
3. fclose(f);

好了,利用IJG JPEG Library进行图像压缩就介绍到这里,希望对大家有所帮助,实例代码已经实现了图像的压缩和解压缩的全部功能,命令格式为:“TestLibjpeg.exe j|j24|b 源文件名 目标文件名”,其中,j选项将源文件压缩为jpg格式,不改变色彩模式,j24选项将源文件压缩为24为jpg格式,b选项将源文件解压缩为bmp格式,该实例并没有提供文件有效性的验证,如果要引入自己的代码,请自己实现文件有效性的验证。


评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值