ijg库解码超大型jpeg图片
1. ijg库解码超大型jpeg图片(>100M)的时候,如何避免内存溢出。
采用边解码边压缩的策略,每次解码一行或者若干行图片数据,然后对于这些解码的数据,进行DQT(量化处理,过滤掉高频的数据,保持低频的数据),
这样解码完,也压缩完。
2. ijg库提供给我们的压缩接口都非常单一,仅有文件流操作,也就是仅仅只有从文件(图片)中读取,然后保存到文件中,而我们在解码大图片的时候,
一般是希望它能够留在缓存中,所以我们需要对源文件进行数据导向内存中
3. 一般而言,我们在进行图片压缩的时候,往往都希望能够随意调整图片的大小(w*h )比如原始图片时800*600,我们希望能够调整到300*300,而且
保证尽可能保持原有图片清晰度的情况
好现在对于每一个问题,我们来进行逐一的解决:
第一个问题:
#include<Windows.h>
#include <stdio.h>
#include "jpeglib.h"
#include <setjmp.h> typedef struct picture{ int image_height; /* 高 */
int image_width; /* 宽 */
int quality; /*质量亏损*/
} Picture; typedef struct picture Picture; Picture m_pict; //设定一个设置参数的变量 typedef unsigned char * wu_char; wu_char outdata; //开辟一个较大的一维数组 struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */
}; typedef struct my_error_mgr * my_error_ptr; 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, );
} GLOBAL(int)
read_JPEG_file (char * filename,int* imagesize)
{
struct jpeg_decompress_struct cinfo; //解压图片信息
struct my_error_mgr jerr; //解压过程中错误信息
FILE * infile; /* 资源文件 */
JSAMPARRAY buffer; /* 每次读取[1~N]行缓冲数据 暂定为一行数据 */
int row_stride; /*实际宽度大小*/
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "文件不存在 %s\n", filename);
return ;
}
cinfo.err = jpeg_std_error(&jerr.pub); //解压过程中数据出错地址给予图片信息
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer)) { //出错时,跳到这儿
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return ;
} jpeg_create_decompress(&cinfo); //创建解压信息
jpeg_stdio_src(&cinfo, infile); //获得资源信息
(void) jpeg_read_header(&cinfo, TRUE); //获取图片信息
(void) jpeg_start_decompress(&cinfo); //开始解压
row_stride = cinfo.output_width * cinfo.output_components; //依据通道格式来进行每行宽度调整RGB格式3的倍数,灰度格式1的倍数
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, ); //分配每行数组的大小 //write
struct jpeg_compress_struct wcinfo;
struct jpeg_error_mgr wjerr;
// FILE * outfile; /* target file */
JSAMPROW row_pointer[]; /* pointer to JSAMPLE row[s] */
int wrow_stride; /* physical row width in image buffer */
wcinfo.err = jpeg_std_error(&wjerr);
jpeg_create_compress(&wcinfo);
/*需要在内存中完成解压和压缩,而且必须保证时间比较快,
*所以使用外部内存不够理想,需要对源码进行改动,实现
*将目的地接口改为我们申请的一个较小的内存块中,这里讲
*所有指向File文件的数据流修改为指向char/unsigned char
*型数组中,这里比较
*/ jpeg_stdio_dest(&wcinfo, outdata,imagesize);
/*保持原始的图片大小,保持质量亏损*/
wcinfo.image_width =cinfo.image_width ;
wcinfo.image_height =cinfo.image_height;
wcinfo.input_components = ; /* # of color components per pixel */
wcinfo.in_color_space = JCS_RGB; /* colorspace of input image */
jpeg_set_defaults(&wcinfo);
wcinfo.scale_num=; /*放大多少倍*/
wcinfo.scale_denom=;
jpeg_set_quality(&wcinfo, m_pict.quality, TRUE );
jpeg_start_compress(&wcinfo, TRUE);
wrow_stride = m_pict.image_width * ; /* JSAMPLEs per row in image_buffer */
// int cmod=cinfo.image_height/wcinfo.image_height;
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, buffer, ); //解压出数据
//if(cinfo.output_scanline%cmod==0)
(void) jpeg_write_scanlines(&wcinfo,buffer,); //压缩数据
} (void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);
jpeg_finish_compress(&wcinfo);
jpeg_destroy_compress(&wcinfo);
return ;
}
第二个问题,如何将文件从文件区导向缓冲区
我们在压缩的时候,需要声明这个接口,来实现指针的传值,
struct jpeg_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; return TRUE;
}
同时在这个文件下面找到
METHODDEF(void)
term_destination (j_compress_ptr cinfo)
将这个文件的所有文件操作修改为内存复制
METHODDEF(void)
term_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; if(dest->pub.m_flag==0x011){
//表示使用文件为目的地
/* Write any data remaining in the buffer */
if (datacount > ) {
if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount)
ERREXIT(cinfo, JERR_FILE_WRITE);
}
fflush(dest->outfile);
/* Make sure we wrote the output file OK */
if (ferror(dest->outfile))
ERREXIT(cinfo, JERR_FILE_WRITE);
}else{
//否则为内存数据流
if (datacount > ){
assert(dest->outdata==NULL); //判断数组是否为空
MEMCOPY(dest->outdata+dest->moveSize,dest->buffer,datacount);
dest->moveSize+=datacount;
*(dest->imageSize)=dest->moveSize;
}
}
}
第三个问题,关于图片缩放问题
j_compress_ptr 这个结构体有这个两个变量来设置,但是只找到了等比例缩放
unsigned int scale_num, scale_denom; /* fraction by which to scale image */
ijg库解码超大型jpeg图片的更多相关文章
- ijg库的使用的几点注意
ijg库(http://www.ijg.org/)是用于处理jpeg解码和压缩的库,最新版本为2014发布的版本,可以在官网中下载jpegsr9a.zip 使用vs中个nmake 进行编译,对于这个版 ...
- linux gd库不支持jpeg解决办法
1. 查看gd库是否支持jpeg gd_info(); 2. 如果JPEG Support 不为1则不支持. 3.首先下载 libjpeg http://www.ijg.org/ ,进行安装 安装目录 ...
- JPEG解码--(1)JPEG文件格式概览
由于懒和人的忘性,以前做的一些笔记再回过头看时又有些生疏了,我决定把一些内容整理出来,以供有需要的来参考. 了解的人知道其价值所在,不知道的人就弃之如废物吧. 本篇是JPEG解码系列的第一篇--JPE ...
- Xcode6.1标准Framework静态库制作方法。工程转Framework,静态库加xib和图片。完美解决方案。
http://www.cocoachina.com/bbs/read.php?tid-282490.html Xcode6.1标准Framework静态库制作方法.工程转Framework,静态库加x ...
- iOS开发之静态库(五)—— 图片、界面xib等资源文件封装到静态框架framework
编译环境:Macbook Air + OS X 10.9.2 + XCode5.1 + iPhone5s(iOS7.0.3) 一.首先将资源文件打包成bundle 由于bundle是静态的,所以可以将 ...
- 渐进式jpeg(progressive jpeg)图片及其相关 --图片的两种加载方式
渐进式jpeg(progressive jpeg)图片及其相关 一.基本JPEG(baseline jpeg)和渐进JPEG 网络上那些色色的照片都是.jpg格式的("色色"指 ...
- BMP图片转换为JPEG图片
原文:BMP图片转换为JPEG图片 昨天在家学习,发现很多人把BMP图片转换为其它图片格式,有些人写得简单,有些人写得复杂. Insus.NET在想,一直在做文件上传,下载,或是图片剪切,都有进行过文 ...
- Java处理JPEG图片时,需要导入com.sun.image.codec.jpeg.JPEGImageEn,报错处理
Java处理JPEG图片时,需要导入com.sun.image.codec.jpeg.JPEGImageEn,会报错,不能使用相应的方法. 原因:java访问限制级api的时候,默认的eclipse设 ...
- 【emWin】例程十八:jpeg图片显示
说明:1.将文件拷入SD卡内即可在指定位置绘制jpeg图片文件,不必加载到储存器. 由于jpeg格式文件显示时需要进行解压缩,耗用动态内存,iCore3所有模块受emwin缓存的限制,jpeg ...
随机推荐
- 大商创 sql追踪 卖家入驻
' ' ' ', '', '', '') ' Query ' Query ' Query ' Query ' Query ' Query ' Query ' Query ' Query ' Query ...
- mysql 查表失败
我们数据库迁移,我进数据库的目录都需要拷贝什么到新的数据库才可以用,我直接拷贝的库报错了[]北京- 2016/1/26 16:07:33 mysql> use payment;Database ...
- JAVA经典算法40题(1-20)
[程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1.程序分析: 兔子的规律 ...
- Zabbix通过percona监控MySQL
因为Zabbix自带的MySQL监控没有提供可以直接使用的Key,所以一般不采用,业界的同学们都使用Percona Monitoring Plugins 监控 MySQL的方式 Percona介绍 P ...
- python RabbitMQ队列/redis
RabbitMQ队列 rabbitMQ是消息队列:想想之前的我们学过队列queue:threading queue(线程queue,多个线程之间进行数据交互).进程queue(父进程与子进程进行交互或 ...
- zjuoj 3607 Lazier Salesgirl
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3607 Lazier Salesgirl Time Limit: 2 Sec ...
- 匈牙利命名法、骆驼命名法、帕斯卡(pascal)命名法
(2008-05-24 13:37:55) 转载▼ 标签: 杂谈 分类: 编程杂文 一.匈牙利命名法: 广泛应用于象Microsoft Windows这样的环境中. Windows 编 ...
- webservice调用服务端数据时给soapenv:Envelope 添加自定义的命名空间
最近做第三方接口,服务端需要 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/&qu ...
- cxf(3.1.1) 客户端异常 请使用 @XmlType.name 和 @XmlType.namespace 为类分配不同的名称。
最近项目使用webService 于是就使用了最新版本 3.1.1 . cxf 客户端调用时老是出现这个错误,综合网上各种资料修改如下,问题解决 "@XmlType.name 和 @XmlT ...
- IIS7的集成模式下如何让自定义的HttpModule不处理静态文件(.html .css .js .jpeg等)请求
今天将开发好的ASP.NET站点部署到客户的服务器上后,发现了一个非常头疼的问题,那么就是IIS7的应用程序池是集成模式的话,ASP.NET项目中自定义的HttpModule会处理静态文件(.html ...