libjpeg 详解

最近工作中涉及到了图片的编解码,在网上确实找到了不少范例,但没发现

详尽介绍这个库的文章,对直接解码输出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

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值