libjpeg 详解
来源:互联网 发布:萝莉酱llj222备用域名 编辑:程序博客网 时间:2024/05/17 23:36
最近工作中涉及到了图片的编解码,在网上确实找到了不少范例,但没发现
详尽介绍这个库的文章,对直接解码输出YUV进行介绍的文章页没找到。
都是讲输出RGB,然后进行RGB->YUV转换的。而我个人觉得,
既然输入图像就是YCbCr,就完全没必要进行RGB-> YUV的转换,
但这样做,图像是否有问题,那就不得而知了(有待确认)。
要深入理解这个开源库,除了readme所列的文档,参考代码(如example.c)
及网上的范例外,最直接的帮助就是源代码本身了。
本文主要从源代码分析,追根溯源,你将会对诸如为什么out_color_space设置
为YCbCr时,得到的图像是YUV444 packed 有个很清晰的理解。
版权归本人所有,如需转载,请注明出处:http://blog.csdn.net/happy08god/article/details/10198011
libjpeg (解码篇)
1. 我们的参数设定必须在jpeg_start_decompress之前,如果我们不设置参数,就会按照默认的来,
请问: 默认参数的设定是在哪个函数中进行的?
通过调用函数jpeg_read_header来设定,具体是:
jpeg_read_header -> jpeg_consume_input -> default_decompress_parms
在函数jpeg_consume_input 中,会将状态改为DSTATE_INHEADER,由于switch语句中此处无break,所以会接着执行
case DSTATE_INHEADER,里面的default_decompress_parms 就是做default设定的。
jpeg_consume_input 部分代码:
switch (cinfo->_state) { case DSTATE_START: /* Start-of-datastream actions: reset appropriate modules */ (*cinfo->inputctl->reset_input_controller) (cinfo); /* Initialize application's data source module */ (*cinfo->src->init_source) (cinfo); cinfo->_state = DSTATE_INHEADER; /*FALLTHROUGH*/ case DSTATE_INHEADER: retcode = (*cinfo->inputctl->consume_input) (cinfo); if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ /* Set up default parameters based on header data */ default_decompress_parms(cinfo); /* Set state: ready for start_decompress */ cinfo->_state = DSTATE_READY; } break;
转载请注明: http://blog.csdn.net/happy08god/article/details/10198011
default_decompress_parms里的部分代码:
if (cinfo->saw_JFIF_marker) { cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ } else if (cinfo->saw_Adobe_marker) { switch (cinfo->Adobe_transform) { case 0:cinfo->jpeg_color_space = JCS_RGB;break; case 1:cinfo->jpeg_color_space = JCS_YCbCr;break; default:WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform);cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */break; } } else { /* Saw no special markers, try to guess from the component IDs */ int cid0 = cinfo->comp_info[0].component_id; int cid1 = cinfo->comp_info[1].component_id; int cid2 = cinfo->comp_info[2].component_id; if (cid0 == 1 && cid1 == 2 && cid2 == 3)cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ else if (cid0 == 82 && cid1 == 71 && cid2 == 66)cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ else {TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2);cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ } } /* Always guess RGB is proper output colorspace. */ cinfo->out_color_space = JCS_RGB; break;
以及:
/* Set defaults for other decompression parameters. */ cinfo->scale_num = cinfo->block_size;/* 1:1 scaling */ cinfo->scale_denom = cinfo->block_size; cinfo->output_gamma = 1.0; cinfo->buffered_image = FALSE; cinfo->raw_data_out = FALSE; cinfo->dct_method = JDCT_DEFAULT; cinfo->do_fancy_upsampling = TRUE; cinfo->do_block_smoothing = TRUE; cinfo->quantize_colors = FALSE; /* We set these in case application only sets quantize_colors. */ cinfo->dither_mode = JDITHER_FS;#ifdef QUANT_2PASS_SUPPORTED cinfo->two_pass_quantize = TRUE;#else cinfo->two_pass_quantize = FALSE;#endif cinfo->desired_number_of_colors = 256; cinfo->colormap = NULL; /* Initialize for no mode change in buffered-image mode. */ cinfo->enable_1pass_quant = FALSE; cinfo->enable_al_quant = FALSE; cinfo->enable_2pass_quant = FALSE;
2. 做decompress的时候,最终处理前(输入)的数据是什么样的格式? 输出的又是怎样的?
在指定输入数据的时候,会调用jdatasrc.c里面的jpeg_stdio_src(输入数据是文件)或者
jpeg_mem_src (输入数据是内存数据),会设置cinfo里面的source manager指针成员,那么
之后就可以通过decompressor结构体指针cinfo“获取数据”了。
以下流程针对raw_data_out = FALSE(默认)的情况,如果raw_data_out为TRUE,会稍有不同。
分为两个部分,一个是针对jpeg_start_decompress,另一个针对jpeg_read_scanlines 。
(1) jpeg_read_scanlines
jpeg_start_decompress ( jdapistd.c )
-> jinit_master_decompress
output_pass_setup
jinit_master_decompress
-> master_selection
master_selection
-> jpeg_calc_output_dimensions
prepare_range_limit_table
jinit_color_deconverter
jinit_upsampler
jinit_d_post_controller
jinit_inverse_dct
jinit_huff_decoder
jinit_d_coef_controller
jinit_d_main_controller
output_pass_setup
-> (*cinfo->master->prepare_for_output_pass)
(此处调用的其实是在jinit_master_decompress函数中赋值的:
master->pub.prepare_for_output_pass = prepare_for_output_pass;
所以,此处调用的其实就是 prepare_for_output_pass )
--> 于是乎:
if (! cinfo->raw_data_out) {
if (! master->using_merged_upsample)
(*cinfo->cconvert->start_pass) (cinfo);
(*cinfo->upsample->start_pass) (cinfo);
if (cinfo->quantize_colors)
(*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass);
(*cinfo->post->start_pass) (cinfo,
(master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
(*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
}
上面的几个start_pass函数(quantize_colors为FALSE,所以不调Quantize start pass函数),请分别参考:
cconvert->pub.start_pass = start_pass_dcolor; ( jdcolor.c中的jinit_color_deconverter)
upsample->pub.start_pass = start_pass_upsample; ( jdsample.c中的jinit_upsampler)
post->pub.start_pass = start_pass_dpost; ( jdpostct.c中的 jinit_d_post_controller )
mainp->pub.start_pass = start_pass_main; ( jdmainct.c中的 jinit_d_main_controller )
那么依次看下来:
start_pass_dcolor ( 空函数)
start_pass_upsample : 也只是设置了next_row_out 和 rows_to_go 。
start_pass_dpost (传的是JBUF_PASS_THRU):
只是做了 post->pub.post_process_data = cinfo->upsample->upsample;
start_pass_main :
( cinfo->upsample->need_context_rows为FALSE,所以执行的是:
mainp->pub.process_data = process_data_simple_main;
mainp->buffer_full = FALSE;/* Mark buffer empty */
mainp->rowgroup_ctr = 0;
jpeg_start_decompress ( jdapistd.c ) -> jinit_master_decompress output_pass_setup jinit_master_decompress -> master_selection master_selection -> jpeg_calc_output_dimensions prepare_range_limit_table jinit_color_deconverter jinit_upsampler jinit_d_post_controller jinit_inverse_dct jinit_huff_decoder jinit_d_coef_controller jinit_d_main_controller output_pass_setup -> (*cinfo->master->prepare_for_output_pass) (此处调用的其实是在jinit_master_decompress函数中赋值的: master->pub.prepare_for_output_pass = prepare_for_output_pass; 所以,此处调用的其实就是 prepare_for_output_pass ) --> 于是乎: if (! cinfo->raw_data_out) { if (! master->using_merged_upsample)(*cinfo->cconvert->start_pass) (cinfo); (*cinfo->upsample->start_pass) (cinfo); if (cinfo->quantize_colors)(*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); (*cinfo->post->start_pass) (cinfo,(master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);} 上面的几个start_pass函数(quantize_colors为FALSE,所以不调Quantize start pass函数),请分别参考: cconvert->pub.start_pass = start_pass_dcolor; ( jdcolor.c中的jinit_color_deconverter) upsample->pub.start_pass = start_pass_upsample; ( jdsample.c中的jinit_upsampler) post->pub.start_pass = start_pass_dpost; ( jdpostct.c中的 jinit_d_post_controller ) mainp->pub.start_pass = start_pass_main; ( jdmainct.c中的 jinit_d_main_controller ) 那么依次看下来: start_pass_dcolor ( 空函数) start_pass_upsample : 也只是设置了next_row_out 和 rows_to_go 。 start_pass_dpost (传的是JBUF_PASS_THRU): 只是做了 post->pub.post_process_data = cinfo->upsample->upsample; start_pass_main : ( cinfo->upsample->need_context_rows为FALSE,所以执行的是: mainp->pub.process_data = process_data_simple_main;mainp->buffer_full = FALSE;/* Mark buffer empty */mainp->rowgroup_ctr = 0;
转载请注明来源: http://blog.csdn.net/happy08god/article/details/10198011
至此,jpeg_start_decompress分析结束。大家可能觉得很奇怪,怎么还没开始解压的具体操作啊?
对,这个函数主要是设置处理函数和对一些变量的初始化,真正解压出数据,还是在获取数据的
地方(如jpeg_read_scanlines 或 jpeg_read_raw_data)。所以,如果你只是想获取解压图像
的长宽或者一些其他参数,通过调用jpeg_start_decompress 或者 jpeg_calc_output_dimensions)
所花费的时间是比较少的。
(2)jpeg_read_scanlines
从传入的数据可以看到,输出的数据(一行)要放到JSAMPARRAY类型。
progress_monitor 用来监控解压情况(可以获取当前解压的百分比,如同winrar等解压软件一样)
之后会调用(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines);来进行解压,
并将解压后的数据存放到scanlines中。
如上面的分析,此处实际调用的是: mainp->pub.process_data = process_data_simple_main;
即 process_data_simple_main 函数 (jdmainct.c)。
(*cinfo->coef->decompress_data) (cinfo, mainp->buffer) 这句就是做的具体解压的动作。
对应的是jdcoefct.c里面的decompress_data (后面的我就不继续说了),得到的输出数据是
JSAMPIMAGE类型的,此类型实际上是将Y,U,V分开了 (所以我个人觉得YUV420P3的输入数据,处理
起来会稍微快些)。
后面进行的是后级处理 ( post-process ), 是通过(*cinfo->post->post_process_data) 调用的。
通过上面的post->pub.post_process_data = cinfo->upsample->upsample; 其实就是jdsample.c
中的sep_upsample
此处会分别调用各个component的upsample函数,如果要做upsample,那么upsample的输出buffer是在
jinit_upsampler中分配的,如下:
if (need_buffer) {
upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE,
(JDIMENSION) jround_up((long) cinfo->output_width,
(long) cinfo->max_h_samp_factor),
(JDIMENSION) cinfo->max_v_samp_factor);
}
之后会调用color_convert进行颜色空间的转换,如果你是YUV -> YUV,此时就不会做转换,
调用对应的null_convert函数;如果你使用默认的out_color_space,即RGB,而你的输入
为YUV,则调用ycc_rgb_convert进行YUV->RGB转换。
在color_convert处理之前,数据是以类似YUV420P3那样的三平面的方式存储的,之后
在调用null_convert的时候,会将数据依次收集起来,进行pack,得到YUV444 Packed
这样格式的数据。
(void)null_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows){ int ci; register int nc = cinfo->num_components; register JSAMPROW outptr; register JSAMPROW inptr; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; while (--num_rows >= 0) { for (ci = 0; ci < nc; ci++) { inptr = input_buf[ci][input_row]; outptr = output_buf[0] + ci; for (col = 0; col < num_cols; col++) {*outptr = *inptr++;/* needn't bother with GETJSAMPLE() here */outptr += nc; } } input_row++; output_buf++; }}
版权归本人所有,如需转载,请注明出处:http://blog.csdn.net/happy08god/article/details/10198011
- libjpeg 详解
- libjpeg编译使用详解
- LIBJPEG
- libjpeg
- libjpeg用法
- libjpeg样例
- libjpeg用法
- LibJpeg编译
- 使用libjpeg
- 移植libjpeg
- libjpeg用法
- libjpeg移植
- libjpeg移植
- libjpeg 使用
- libjpeg移植
- libjpeg用法
- libjpeg使用
- libjpeg使用
- 2013年8月九度Online Judge程序猿求职及面试月赛
- 从GitHub创建库
- hdu 4696 Answers 多校第十场
- IT 认证考试--软考--(中级项目)嵌入式系统设计师1:嵌入式系统设计师介绍
- VS2010 修改模板文件,增加默认注释
- libjpeg 详解
- 浅谈COOKIE和SESSION关系和区别等
- stack/queue的使用方法
- onSaveInstanceState和onRestoreInstanceState触发的时机
- 白书上的Treap模板
- hdu 4004 The Frog's Games
- 用interrupt()中断Java线程
- Qt输入法事件 QInputMethodEvent
- php底部运行机制