ijg库解码超大型jpeg图片

摘要:
采用边解码边压缩的策略,每次解码一行或者若干行图片数据,然后对于这些解码的数据,进行DQT,这样解码完,也压缩完。

1. ijg库解码超大型jpeg图片(>100M)的时候,如何避免内存溢出。

采用边解码边压缩的策略,每次解码一行或者若干行图片数据,然后对于这些解码的数据,进行DQT(量化处理,过滤掉高频的数据,保持低频的数据),

这样解码完,也压缩完。

2. ijg库提供给我们的压缩接口都非常单一,仅有文件流操作,也就是仅仅只有从文件(图片)中读取,然后保存到文件中,而我们在解码大图片的时候,

一般是希望它能够留在缓存中,所以我们需要对源文件进行数据导向内存中

3. 一般而言,我们在进行图片压缩的时候,往往都希望能够随意调整图片的大小(w*h )比如原始图片时800*600,我们希望能够调整到300*300,而且

保证尽可能保持原有图片清晰度的情况

好现在对于每一个问题,我们来进行逐一的解决:

第一个问题:

1 #include<Windows.h>
2 #include <stdio.h>
3 #include "jpeglib.h"
4 #include <setjmp.h>
5 
6 
7 typedef structpicture{
8 
9  int image_height;     /**/
10  int image_width;   /**/
11  int quality;    /*质量亏损*/
12 } Picture;
13 
14 typedef structpicture Picture;
15 
16 Picture  m_pict;  //设定一个设置参数的变量
17 
18 typedef unsigned char *wu_char;
19 
20 wu_char outdata;  //开辟一个较大的一维数组
21 
22 
23 structmy_error_mgr {
24     struct jpeg_error_mgr pub;    /*"public" fields */
25 
26     jmp_buf setjmp_buffer;    /*for return to caller */
27 };
28 
29 typedef struct my_error_mgr *my_error_ptr;
30 
31 METHODDEF(void)
32 my_error_exit (j_common_ptr cinfo)
33 {
34   my_error_ptr myerr = (my_error_ptr) cinfo->err;
35   (*cinfo->err->output_message) (cinfo);
36   longjmp(myerr->setjmp_buffer, 1);
37 }
38 
39 
40 GLOBAL(int)
41 read_JPEG_file (char * filename,int*imagesize)
42 {
43   struct jpeg_decompress_struct cinfo;  //解压图片信息
44   struct my_error_mgr jerr;                //解压过程中错误信息
45   FILE * infile;                        /*资源文件 */
46   JSAMPARRAY buffer;                    /*每次读取[1~N]行缓冲数据 暂定为一行数据 */
47   int row_stride;        /*实际宽度大小*/
48   if ((infile = fopen(filename, "rb")) ==NULL) {
49     fprintf(stderr, "文件不存在 %s
", filename);
50     return 0;
51 }
52   cinfo.err = jpeg_std_error(&jerr.pub);    //解压过程中数据出错地址给予图片信息
53   jerr.pub.error_exit =my_error_exit;   
54   if (setjmp(jerr.setjmp_buffer)) {            //出错时,跳到这儿
55     jpeg_destroy_decompress(&cinfo);
56 fclose(infile);
57     return 0;
58 }
59 
60   jpeg_create_decompress(&cinfo);            //创建解压信息
61   jpeg_stdio_src(&cinfo, infile);           //获得资源信息
62   (void) jpeg_read_header(&cinfo, TRUE);             //获取图片信息
63   (void) jpeg_start_decompress(&cinfo);                //开始解压
64   row_stride = cinfo.output_width * cinfo.output_components;  //依据通道格式来进行每行宽度调整RGB格式3的倍数,灰度格式1的倍数
65   buffer = (*cinfo.mem->alloc_sarray)            
66         ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);            //分配每行数组的大小
67  
68   //write
69   structjpeg_compress_struct wcinfo;
70   structjpeg_error_mgr wjerr;
71  //FILE * outfile;        /* target file */
72   JSAMPROW row_pointer[1];    /*pointer to JSAMPLE row[s] */
73   int wrow_stride;        /*physical row width in image buffer */
74   wcinfo.err = jpeg_std_error(&wjerr);
75   jpeg_create_compress(&wcinfo);
76   /*需要在内存中完成解压和压缩,而且必须保证时间比较快,
77 *所以使用外部内存不够理想,需要对源码进行改动,实现
78 *将目的地接口改为我们申请的一个较小的内存块中,这里讲
79 *所有指向File文件的数据流修改为指向char/unsigned char
80 *型数组中,这里比较
81    */
82 
83   jpeg_stdio_dest(&wcinfo, outdata,imagesize);
84   /*保持原始的图片大小,保持质量亏损*/
85   wcinfo.image_width =cinfo.image_width ;  
86   wcinfo.image_height =cinfo.image_height;
87   wcinfo.input_components = 3;          /*# of color components per pixel */
88   wcinfo.in_color_space = JCS_RGB;       /*colorspace of input image */
89   jpeg_set_defaults(&wcinfo);
90   wcinfo.scale_num=3;     /*放大多少倍*/
91   wcinfo.scale_denom=14;
92   jpeg_set_quality(&wcinfo, m_pict.quality, TRUE );
93   jpeg_start_compress(&wcinfo, TRUE);
94   wrow_stride = m_pict.image_width * 3;    /*JSAMPLEs per row in image_buffer */
95  //int cmod=cinfo.image_height/wcinfo.image_height;
96   while (cinfo.output_scanline <cinfo.output_height) {
97     (void) jpeg_read_scanlines(&cinfo, buffer, 1);    //解压出数据
98     //if(cinfo.output_scanline%cmod==0)        
99     (void) jpeg_write_scanlines(&wcinfo,buffer,1);   //压缩数据
100 }
101 
102   (void) jpeg_finish_decompress(&cinfo);
103   jpeg_destroy_decompress(&cinfo);
104 fclose(infile);
105   jpeg_finish_compress(&wcinfo);
106   jpeg_destroy_compress(&wcinfo);
107   return 1;
108 }

第二个问题,如何将文件从文件区导向缓冲区

我们在压缩的时候,需要声明这个接口,来实现指针的传值,

 structjpeg_compress_struct wcinfo;
 struct jpeg_error_mgr wjerr;

同时需要用这个函数,将开辟的地址绑定,ijg源码提供的只有File* 接口,所以我们需要模仿这个函数,另外在写一个这个函数(最好方法就是用模板类来实现),这里我们只是简单的说下思路,

重写一个这个函数

jpeg_stdio_dest (j_compress_ptr cinfo, File* outfile,int *imagesize)

另写一个这样的函数,将参数修改为unsigned char *型

jpeg_stdio_dest (j_compress_ptr cinfo, unsigned char * outdata,int *imagesize)

然后在jpeglib.h中找到

EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile));

将其修改为

EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, unsigned char * outdata, int *imagesize/*返回压缩后图片大小*/));

所以和这个File * outfile的数据类型,修改完这些之后,还需要修改的几个地方

文件 jdatadst.c (jpeg数据目的地文件)中

找到这个结构体,修改或者增加几个自定义变量,中文解释部分为自己加的

typedef struct{
  struct jpeg_destination_mgr pub; /*public fields */
  unsigned char * outdata; /*自定义数据缓冲地*/
  FILE * outfile;          /*target stream */
  JOCTET * buffer;          /*start of buffer */
  int *imageSize;          /*表示图片大小*/
  int moveSize ;        /*偏移量大小*/
} my_destination_mgr;

找到这个函数

METHODDEF(boolean)
empty_output_buffer (j_compress_ptr cinfo)

将里面的文件操作,修改为内存复制便可,下面是将文件操作和缓存流结合起来放在一个文件中(加了一个标志位m_flag,ox011 表示文件操作,ox010便是缓存)

METHODDEF(boolean)
empty_output_buffer (j_compress_ptr cinfo)
{
  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
  if( dest->pub.m_flag==0x011){
  if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) !=
     (size_t) OUTPUT_BUF_SIZE)
     ERREXIT(cinfo, JERR_FILE_WRITE);
  }
  else{
     //使用的是内存缓冲数据管理
     //所以需要开辟一个新的数组
     /*这里存在一个疑问,数组的大小如何控制,偏移量如何管控? 
       需要去思考
       解答:  empty_mem_output_buffer 在这个函数中,因为使用了buffer不断的扩充内存,所以不需要控制
     */
      /*实现内存位置偏移,这里貌似存在一个问题,就是dest是一个临时的指针*/
     MEMCOPY( dest->outdata+ dest->moveSize,dest->buffer,OUTPUT_BUF_SIZE ); 
     dest->moveSize+=OUTPUT_BUF_SIZE;    //偏移增量
     *(dest->imageSize)=dest->moveSize; 
  }
  dest->pub.next_output_byte = dest->buffer;
  dest->pub.free_in_buffer =OUTPUT_BUF_SIZE;
  returnTRUE;
}

同时在这个文件下面找到

METHODDEF(void)
term_destination (j_compress_ptr cinfo)

将这个文件的所有文件操作修改为内存复制

1 METHODDEF(void)
2 term_destination (j_compress_ptr cinfo)
3 {
4   my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
5   size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
6 
7   if(dest->pub.m_flag==0x011){
8     //表示使用文件为目的地
9   /*Write any data remaining in the buffer */
10   if (datacount > 0) {
11     if (JFWRITE(dest->outfile, dest->buffer, datacount) !=datacount)
12 ERREXIT(cinfo, JERR_FILE_WRITE);
13 }
14   fflush(dest->outfile);
15   /*Make sure we wrote the output file OK */
16   if (ferror(dest->outfile))
17 ERREXIT(cinfo, JERR_FILE_WRITE);
18   }else{
19   //否则为内存数据流
20 if (datacount > 0){
21     assert(dest->outdata==NULL);        //判断数组是否为空
22     MEMCOPY(dest->outdata+dest->moveSize,dest->buffer,datacount);    
23     dest->moveSize+=datacount;
24     *(dest->imageSize)=dest->moveSize;
25 }
26 }
27 }

第三个问题,关于图片缩放问题

j_compress_ptr 这个结构体有这个两个变量来设置,但是只找到了等比例缩放

  unsigned int scale_num, scale_denom; /*fraction by which to scale image */

免责声明:文章转载自《ijg库解码超大型jpeg图片》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇WP 状态栏处理[隐藏、控制显示]十九、多文件上传(ajaxFileupload实现多文件上传功能)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

工具软件 PYUV打开raw图片

引自:http://blog.csdn.net/lavenderss/article/details/51495648  [pYUV]如何打开YUV/RGB图片 pYUV工具本身使用起来比较简单,但如果选项设置错误,会导致图像显示失真或错误,让人误以为是图片本身的问题,这里介绍两个比较典型类型的图片打开方式,其他类型图片打开方式触类旁通即可。 1.  ...

(原)调用jpeglib对图像进行压缩

网址:http://www.cnblogs.com/darkknightzh/p/4973828.html。未经允许,严禁转载。 参考网站: http://dev.w3.org/Amaya/libjpeg/example.c http://www.360doc.com/content/13/0226/15/2036337_268016821.shtml 1...

Android 下的EXIF

一.什么是Exif Exif(Exchangeable Image File 可交换图像文件)是一种图象文件格式,它的数据存储与JPEG格式是完全相同的。实际上Exif格式就是在JPEG格式头部插入了数码照片的信息,包括拍 摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及全球定位系统(GPS...

(转)了解一下,各种图片格式的区别

  在开发过程中,经常涉及到要用到图片,但是图片有很多不同的格式,他们之间有什么区别呢,我们在使用的时候又该如何选择呢?本文介绍和比较几种常见图片文件格式的优缺点,并介绍不同的文件格式对应用程序性能的影响。 有损vs无损 图片文件格式有可能会对图片的文件大小进行不同程度的压缩,图片的压缩分为有损压缩和无损压缩两种。 有损压缩:指在压缩文件大小的过程中,损...

渐进式jpeg(progressive jpeg)图片及其相关

最近看有些网站上的jpg格式的图片在呈现的时候,有两种方式,一种是自上而下扫描式的,还有一种就是先是全部的模糊图片,然后逐渐清晰(就像GIF格式的交错显示)。 一、基本JPEG(baseline jpeg)和渐进JPEG 图片的尺寸大小: 张鑫旭个人博客看到: 同一张jpg图片,如果保存为基本式和渐进式那个尺寸更小呢? 根据我拿3终不同风格图片做测试,发现...

ie浏览器不支持cmyk模式下的JPEG图片

  今天分享一个小小的技巧,就是解决颜色模式为印刷格式CMYK四色模式的JPEG图片上传到服务器上不被支持的问题。  如果你用photo shop打开该图片,细心的人就会发现JPG图像标题栏的图片名称后面紧跟着该图像的颜色模式,如果是RGB的就显示RGB,如果是四色模式CMYK的就显示CMYK。而通常网络上使用的基本都是RGB格式。况且IE6.9-ie8...