libjpeg库其实已经可以满足我们日常处理日常简单图片之间的转换了。下面就来介绍一下jpeg库的解压和压缩过程。
首先来讲解压操作过程:
1、分配jpeg对象结构体空间,并初始化。
2、指定解压数据源。
3、获取解压文件信息。
4、为解压设定参数,包括图像大小和颜色空间。
5、开始解压缩。
6、取数据。
7、解压完毕。
8、释放资源和退出程序。
1、分配jpeg对象结构体空间、并初始化。
解压缩过程中使用的JPEG对象是一个jpeg_decompress_struct的结构体。同时还需要定义一个用于错误处理的结构体对象,IJG中标准的错误结构体是jpeg_error_mgr。
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr jerr;
绑定jerr错误结构体至jpeg对象结构体。
- cinfo.err = jpeg_std_error(&jerr);
这个标准的错误处理结构将使程序在出现错误时调用exit()退出程序,如果不希望使用标准的错误处理方式,则可以通过自定义退出函数的方法自定义错误处理结构。
初始化cinfo结构体。
- jpeg_create_decompress(&cinfo);
2、指定解压数据源。
利用标准C库的文件指针打开相关jpg文件。
- FILE * infile;
- if ((infile = fopen("test.jpg", "rb")) == NULL)
- {
- perror("fopen fail");
- return 0;
- }
- jpeg_stdio_src(&cinfo, infile);
3、获取解压文件信息。
将图像的缺省信息填充到cinfo结构中以便程序使用。
- (void) jpeg_read_header(&cinfo, TRUE);
此时,常见的可用信息包括图像的宽cinfo.image_width,高cinfo.image_height,色彩空间cinfo.jpeg_color_space,颜色通道数cinfo.num_components等。
4、为解压设定参数,包括图像大小和颜色空间。
比如可以设定解出来的图像的大小,也就是与原图的比例。使用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;
5、开始解压缩。
根据设定的解压缩参数进行图像解压缩操作。
- (void) jpeg_start_decompress(&cinfo);
在完成解压缩操作后,会将解压后的图像信息填充至cinfo结构中。比如,输出图像宽度cinfo.output_width,输出图像高度cinfo.output_height,每个像素中的颜色通道数cinfo.output_components(比如灰度为1,全彩色为3)等。
一般情况下,这些参数是在jpeg_start_decompress后才被填充到cinfo中的,如果希望在调用jpeg_start_decompress之前就获得这些参数,可以通过调用jpeg_calc_output_dimensions()的方法来实现。
6、取数据。
解开的数据是按照行取出的,数据像素按照scanline来存储,scanline是从左到右,从上到下的顺序,每个像素对应的各颜色或灰度通道数据是依次存储,比如一个24-bit RGB真彩色的图像中,一个scanline中的数据存储模式是R,G,B,R,G,B,R,G,B,...,每条scanline是一个JSAMPLE类型的数组,一般来说就是unsigned char,定义于jmorecfg.h中。除了JSAMPLE,图像还定义了JSAMPROW和JSAMPARRAY,分别表示一行JSAMPLE和一个2D的JSAMPLE数组。
在此,我定义一个JSAMPARRAY类型的缓冲区变量来存放图像数据。
然后是计算每行需要的空间大小,比如RGB图像就是宽度×3,灰度图就是宽度×1
- row_stride = cinfo.output_width * cinfo.output_components;
为缓冲区分配空间,这里使用了libjpeg的内存管理器来完成分配。
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
- }
7、解压完毕。
- (void) jpeg_finish_decompress(&cinfo);
8、释放资源和退出程序。
- 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)
- {
- perror("fopen fail");
- 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);