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

原创粉丝点击