jpeg 解码分析
Jpeg 的解码分析
(赵海涛 2009-08-11)
一、主要函数 ........................................................................................................................................................... 2
1.1分配和初始化JPEG的解码对象。 .......................................................................................................... 2 1.2指定一个被解压的源文件。 ..................................................................................................................... 2 1.3调用jpeg_read_header()
来得到 image 的信息 ................................................................................ 2 1.4设置解码的参数 ......................................................................................................................................... 3
1.5 while (scan lines remain to be read) ............................................................................................................ 3
1.6 jpeg_finish_decompress(...); ........................................................................................................................ 3
1.7 释放Jpeg 的解码对象 .............................................................................................................................. 3
二、主要过程分析 ................................................................................................................................................... 4
2.1 Jpeg_read_scanlines (jdapistd.c) .............................................................................................................. 4 2.2 process_data_context_main (jdmainct.c) ..................................................................................................... 4
2.3 decompress_onepass (jdcoefct.c)................................................................................................................. 6
2.4 decode_mcu ................................................................................................................................................. 8
2.5 jpeg_idct_islow .......................................................................................................................................... 10
2.6 sep_upsample (jdsample.c) ........................................................................................................................ 15
2.7ycc_rgb_convert (jdcolor.c) ........................................................................................................................ 16
三、 profile............................................................................................................................................................. 18
四、数据流图 ......................................................................................................................................................... 19
五、调用关系 ......................................................................................................................................................... 20
5.1 Read_scanline: ........................................................................................................................................... 20
5.2 Process_data_contextmain: ........................................................................................................................ 21
5.3 Decompress_onepass: ................................................................................................................................ 21
5.4Upsample: ................................................................................................................................................... 22
六、性能提高的方法 ............................................................................................................................................. 22
七、解压缩图像的控制
................................................................................................................................. 23
7.1构造符 ....................................................................................................................................................... 23
7.2帧的解码控制程序 ................................................................................................................................... 24
7.3扫描的解码控制程序 ............................................................................................................................... 25
7.4重置间隔的解码控制程序 ....................................................................................................................... 26 7.5最小编码单元(MCU)的解码控制程序 .............................................................................................. 27 7.6压缩数据格式 ........................................................................................................................................... 28
八、Decode_MCU过程分析 ................................................................................................................................. 31
8.1 熵编码 ...................................................................................................................................................... 31
8.2采样精度 ................................................................................................................................................... 31
8.3多成分控制 ............................................................................................................................................... 32
8.3.1多成分交织 .................................................................................................................................... 32
8.3.2 最小编码单元 ............................................................................................................................... 34
8.4压缩数据的结构 ....................................................................................................................................... 34
8.5 交换格式 .................................................................................................................................................. 34
8.6 压缩图像数据的缩略格式 ...................................................................................................................... 34
8.6.1表说明数据的缩略格式 ................................................................................................................ 34 8.7 图像、帧和扫描 ...................................................................................................................................... 35
8.8 基本过程总结 .......................................................................................................................................... 35
8.9 基于DCT变换的编解码 ........................................................................................................................ 36 九、Idct和反量化过程分析 .................................................................................................................................. 38
9.1DCT压缩 ................................................................................................................................................... 38
9.1.1 平移 ....................................................................................................................................................... 38
9.1.2 DCT变换中采样点的方向 ........................................................................................................... 38 9.1.3 DCT变换和 DCT反变换 .................................................................................................................... 39
9.1.4 DCT系数的量化和反量化 ........................................................................................................... 39 9.1.5差分DC系数编码 ................................................................................................................................. 39
9.1.6 Z形顺序 ................................................................................................................................................. 40
9.2 点变换 ...................................................................................................................................................... 40
十、CP 的优化可能性........................................................................................................................................... 41
10.1 Jpeg 的热点 ............................................................................................................................................ 41
一、主要函数
1.1分配和初始化JPEG的解码对象。
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jerr 仅是为了提供一个错误的处理通用方法。
1.2指定一个被解压的源文件。
FILE * infile;
...
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_src(&cinfo, infile);
这个是为了提供一个文件的访问和打开的方法。提供了一个数据流
1.3调用jpeg_read_header()方法来得到 image 的信息
jpeg_read_header(&cinfo, TRUE);
这个方法会读取数据流的头信息~得到的信息包括了:图片的尺寸大小~和其他的被包含在JPEG 对象中的信息。应用程序使用这些信息来选择解压的参数。
如果你只是需要得到头信息而已~那么这个时候你可以调用jpeg_destroy()来释放对象。
2
1.4设置解码的参数
设置好解码参数之后~调用jpeg_start_decompress()来开始解码。这将初始化好内部的状态~并且分配好工作所需要的内存~并且准备好返回数据。
jpeg_start_decompress(&cinfo);
如果你使用的是多路操作的模式~比如2-pass 色彩量化~Jpeg_start_decompress() 将在数据输出之前做完所有的事情。在这种情况下~jpeg_start_decompress() 将消耗一段时间来完成该过程。如果使用的是 single-scan 的这种方法,JPEG 的默认方法,~jpeg_start_decompress()将会立即返回。
调用完这个之后~最终的输出的image 的尺寸~包括所需要的缩小比例~都将在JPEG的对象种了。可用的信息包括:
output_width缩小之后的图像宽度
output_height缩小之后的图像高度
out_color_components# 我们图像空间中的色彩成分
output_components# 每个像素种的的色彩成分
colormap如果存在的话~则为颜色表
actual_number_of_colors每个颜色表中的项数
在量化色彩时~output_components 为1~否则和out_color_components 相同。它是JSAMPE 的数值~并且在输出数组种的每个像素中被发出。
通常~你需要分配一个用来接收数据的缓冲。你每次扫描一行的时候都需要一个output_width * output_components 大小的缓冲~总共需要output_height 的行数会被扫描。
1.5 while (scan lines remain to be read)
jpeg_read_scanlines(...);
这个时候你就可以通过调用calling jpeg_read_scanlines()来读取解压的image 的数据
了。每次调用你将会读到一个或者多个行的数据。图像的返回顺序是从上而下的。如果
你必须使用从下而上的话~你就需要使用JPEG 的虚拟数组的机制来使这种你转变的更
有效率。这个例子可以在例子程序种看到。
1.6 jpeg_finish_decompress(...);
档所有的image 的数据都被读取之后~调用jpeg_finish_decompress() 来完成这个解
码的周期。这将导致工作的内存被释放。
1.7 释放Jpeg 的解码对象
jpeg_destroy_decompress(&cinfo);
当你完成了解码过程~用这个方法来释放这个结构。
3
二、主要过程分析
2.1 Jpeg_read_scanlines (jdapistd.c) GLOBAL(JDIMENSION)
该函数从缓冲区中去取得解压的图像数据。可以一次输出多行。
jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines,
JDIMENSION max_lines)
{
JDIMENSION row_ctr;
if (cinfo->global_state != DSTATE_SCANNING)
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
if (cinfo->output_scanline >= cinfo->output_height) {
WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
return 0;
}
/* 以上2行只是保证没有错误的输入条件和没有异常的状态发生*/
/* Call progress monitor hook if present */
if (cinfo->progress != NULL) {
cinfo->progress->pass_counter = (long) cinfo->output_scanline;
cinfo->progress->pass_limit = (long) cinfo->output_height;
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
}
/* 我们没有使用progress 这样的代码~当然如果使用了这样的方法也可以*/
/* Process some data */
row_ctr = 0;
(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); //这个才是主处理
cinfo->output_scanline += row_ctr; //标示我们现在处理到了哪个行了。
return row_ctr;
}
2.2 process_data_context_main (jdmainct.c)
首先看是否有可以取出的缓冲区~如果没有那么尝试去解压数据~并且添入到这个主缓冲区去。
4
/*
* Process some data.
* This handles the case where context rows must be provided.
*/
METHODDEF(void)
process_data_context_main (j_decompress_ptr cinfo,
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
JDIMENSION out_rows_avail)
{
my_main_ptr main = (my_main_ptr) cinfo->main;
/* Read input data if we haven't filled the main buffer yet */
if (! main->buffer_full) {
if (! (*cinfo->coef->decompress_data) (cinfo,
main->xbuffer[main->whichptr]))
return; /* suspension forced, can do nothing more */
main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */
main->iMCU_row_ctr++; /* count rows received */
}
/* Postprocessor typically will not swallow all the input data it is handed
* in one call (due to filling the output buffer first). Must be prepared
* to exit and restart. This switch lets us keep track of how far we got.
* Note that each case falls through to the next on successful completion.
*/
switch (main->context_state) {
case CTX_POSTPONED_ROW:
/* Call postprocessor using previously set pointers for postponed row */
(*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr],
&main->rowgroup_ctr, main->rowgroups_avail,
output_buf, out_row_ctr, out_rows_avail);
if (main->rowgroup_ctr < main->rowgroups_avail)
return; /* Need to suspend */
main->context_state = CTX_PREPARE_FOR_IMCU;
if (*out_row_ctr >= out_rows_avail)
return; /* Postprocessor exactly filled output buf */
/*FALLTHROUGH*/
case CTX_PREPARE_FOR_IMCU:
/* Prepare to process first M-1 row groups of this iMCU row */
main->rowgroup_ctr = 0;
main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1);
/* Check for bottom of image: if so, tweak pointers to "duplicate"
* the last sample row, and adjust rowgroups_avail to ignore padding rows.
*/
if (main->iMCU_row_ctr == cinfo->total_iMCU_rows)
set_bottom_pointers(cinfo);
main->context_state = CTX_PROCESS_IMCU;
/*FALLTHROUGH*/
case CTX_PROCESS_IMCU:
/* Call postprocessor using previously set pointers */
(*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr],
5
&main->rowgroup_ctr, main->rowgroups_avail,
output_buf, out_row_ctr, out_rows_avail);
if (main->rowgroup_ctr < main->rowgroups_avail)
return; /* Need to suspend */
/* After the first iMCU, change wraparound pointers to normal state */
if (main->iMCU_row_ctr == 1)
set_wraparound_pointers(cinfo);
/* Prepare to load new iMCU row using other xbuffer list */
main->whichptr ^= 1; /* 0=>1 or 1=>0 */
main->buffer_full = FALSE;
/* Still need to process last row group of this iMCU row, */
/* which is saved at index M+1 of the other xbuffer */
main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1);
main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2);
main->context_state = CTX_POSTPONED_ROW;
}
}
2.3 decompress_onepass (jdcoefct.c)
/*
* Decompress and return some data in the single-pass case.
* Always attempts to emit one fully interleaved MCU row ("iMCU" row).
* Input and output must run in lockstep since we have only a one-MCU buffer.
* Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
*
* NB: output_buf contains a plane for each component in image,
* which we index according to the component's SOF position.
*/
METHODDEF(int)
decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
{
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
JDIMENSION MCU_col_num; /* index of current MCU within row */
JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
int blkn, ci, xindex, yindex, yoffset, useful_width;
JSAMPARRAY output_ptr;
JDIMENSION start_col, output_col;
jpeg_component_info *com
r;
inverse_DCT_method_ptr inverse_DCT;
/* Loop to process as much as one whole iMCU row */
for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
yoffset++) {
for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col;
MCU_col_num++) {
/* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */
6
jzero_far((void FAR *) coef->MCU_buffer[0],
(size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK)));
if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) {
/* Suspension forced; update state counters and exit */
coef->MCU_vert_offset = yoffset;
coef->MCU_ctr = MCU_col_num;
return JPEG_SUSPENDED;
}
/* Determine where data should go in output_buf and do the IDCT thing.
* We skip dummy blocks at the right and bottom edges (but blkn gets
* incremented past them!). Note the inner loop relies on having
* allocated the MCU_buffer[] blocks sequentially.
*/
blkn = 0; /* index of current DCT block within MCU */
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
compptr = cinfo->cur_comp_info[ci];
/* Don't bother to IDCT an uninteresting component. */
if (! compptr->component_needed) {
blkn += compptr->MCU_blocks;
continue;
}
inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index];
useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
: compptr->last_col_width;
output_ptr = output_buf[compptr->component_index] +
yoffset * compptr->DCT_scaled_size;
start_col = MCU_col_num * compptr->MCU_sample_width;
for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
if (cinfo->input_iMCU_row < last_iMCU_row ||
yoffset+yindex < compptr->last_row_height) {
output_col = start_col;
for (xindex = 0; xindex < useful_width; xindex++) {
(*inverse_DCT) (cinfo, compptr,
(JCOEFPTR) coef->MCU_buffer[blkn+xindex],
output_ptr, output_col);
output_col += compptr->DCT_scaled_size;
}
}
blkn += compptr->MCU_width;
output_ptr += compptr->DCT_scaled_size;
}
}
}
/* Completed an MCU row, but perhaps not an iMCU row */
coef->MCU_ctr = 0;
}
/* Completed the iMCU row, advance counters for next one */
cinfo->output_iMCU_row++;
if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) {
start_iMCU_row(cinfo);
return JPEG_ROW_COMPLETED;
}
7
/* Completed the scan */
(*cinfo->inputctl->finish_input_pass) (cinfo);
return JPEG_SCAN_COMPLETED;
}
2.4 decode_mcu
/*
* Decode and return one MCU's worth of Huffman-compressed coefficients.
* The coefficients are reordered from zigzag order into natural array order,
* but are not dequantized.
*
* The i'th block of the MCU is stored into the block pointed to by
* MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER.
* (Wholesale zeroing is usually a little faster than retail...)
*
* Returns FALSE if data source requested suspension. In that case no
* changes have been made to permanent state. (Exception: some output
* coefficients may already have been assigned. This is harmless for
* this module, since we'll just re-assign them on the next call.)
*/
METHODDEF(boolean)
decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
{
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
int blkn;
BITREAD_STATE_VARS;
savable_state state;
/* Process restart marker if needed; may have to suspend */
if (cinfo->restart_interval) {
if (entropy->restarts_to_go == 0)
if (! process_restart(cinfo))
return FALSE;
}
/* If we've run out of data, just leave the MCU set to zeroes.
* This way, we return uniform gray for the remainder of the segment.
*/
if (! entropy->pub.insufficient_data) {
/* Load up working state */
BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
ASSIGN_STATE(state, entropy->saved);
/* Outer loop handles each block in the MCU */
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
8
JBLOCKROW block = MCU_data[blkn];
d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn];
d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn];
register int s, k, r;
/* Decode a single block's worth of coefficients */
/* Section F.2.2.1: decode the DC coefficient difference */
HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); //哈夫曼编码
if (s) {
CHECK_BIT_BUFFER(br_state, s, return FALSE);
r = GET_BITS(s);
s = HUFF_EXTEND(r, s);
}
if (entropy->dc_needed[blkn]) {
/* Convert DC difference to actual value, update last_dc_val */
int ci = cinfo->MCU_membership[blkn];
s += state.last_dc_val[ci];
state.last_dc_val[ci] = s;
/* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */
(*block)[0] = (JCOEF) s;
}
if (entropy->ac_needed[blkn]) {
/* Section F.2.2.2: decode the AC coefficients */
/* Since zeroes are skipped, output area must be cleared beforehand */
for (k = 1; k < DCTSIZE2; k++) {
HUFF_DECODE(s, br_state, actbl, return FALSE, label2);
r = s >> 4;
s &= 15;
if (s) {
k += r;
CHECK_BIT_BUFFER(br_state, s, return FALSE);
r = GET_BITS(s);
s = HUFF_EXTEND(r, s);
/* Output coefficient in natural (dezigzagged) order.
* Note: the extra entries in jpeg_natural_order[] will save us
* if k >= DCTSIZE2, which could happen if the data is corrupted.
*/
(*block)[jpeg_natural_order[k]] = (JCOEF) s;
} else {
if (r != 15)
break;
k += 15;
}
}
} else {
9
/* Section F.2.2.2: decode the AC coefficients */
/* In this path we just discard the values */
for (k = 1; k < DCTSIZE2; k++) {
HUFF_DECODE(s, br_state, actbl, return FALSE, label3);
r = s >> 4;
s &= 15;
if (s) {
k += r;
CHECK_BIT_BUFFER(br_state, s, return FALSE);
DROP_BITS(s);
} else {
if (r != 15)
break;
k += 15;
}
}
}
}
/* Completed MCU, so update state */
BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
ASSIGN_STATE(entropy->saved, state);
}
/* Account for restart interval (no-op if not using restarts) */
entropy->restarts_to_go--;
return TRUE;
}
2.5 jpeg_idct_islow
进行idct 和反量化运算。
/*
* Perform dequantization and inverse DCT on one block of coefficients.
*/
GLOBAL(void)
jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr,
JCOEFPTR coef_block,
JSAMPARRAY output_buf, JDIMENSION output_col) {
INT32 tmp0, tmp1, tmp2, tmp3;
INT32 tmp10, tmp11, tmp12, tmp13;
INT32 z1, z2, z3, z4, z5;
JCOEFPTR inptr;
10
ISLOW_MULT_TYPE * quantptr;
int * wsptr;
JSAMPROW outptr;
JSAMPLE *range_limit = IDCT_range_limit(cinfo);
int ctr;
int workspace[DCTSIZE2]; /* buffers data between passes */
SHIFT_TEMPS
/* Pass 1: process columns from input, store into work array. */
/* Note results are scaled up by sqrt(8) compared to a true IDCT; */
/* furthermore, we scale the results by 2**PASS1_BITS. */
inptr = coef_block;
quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table;
wsptr = workspace;
for (ctr = DCTSIZE; ctr > 0; ctr--) {
/* Due to quantization, we will usually find that many of the input
* coefficients are zero, especially the AC terms. We can exploit this
* by short-circuiting the IDCT calculation for any column in which all
* the AC terms are zero. In that case each output is equal to the
* DC coefficient (with scale factor as needed).
* With typical images and quantization tables, half or more of the
* column DCT calculations can be simplified this way.
*/
if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 &&
inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 &&
inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 &&
inptr[DCTSIZE*7] == 0) {
/* AC terms all zero */
int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS;
wsptr[DCTSIZE*0] = dcval;
wsptr[DCTSIZE*1] = dcval;
wsptr[DCTSIZE*2] = dcval;
wsptr[DCTSIZE*3] = dcval;
wsptr[DCTSIZE*4] = dcval;
wsptr[DCTSIZE*5] = dcval;
wsptr[DCTSIZE*6] = dcval;
wsptr[DCTSIZE*7] = dcval;
inptr++; /* advance pointers to next column */
quantptr++;
wsptr++;
continue;
}
/* Even part: reverse the even part of the forward DCT. */
/* The rotator is sqrt(2)*c(-6). */
z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]);
11
z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]);
z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]);
tmp0 = (z2 + z3) << CONST_BITS;
tmp1 = (z2 - z3) << CONST_BITS;
tmp10 = tmp0 + tmp3;
tmp13 = tmp0 - tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp1 - tmp2;
/* Odd part per figure 8; the matrix is unitary and hence its
* transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
*/
tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]);
tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]);
tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]);
tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]);
z1 = tmp0 + tmp3;
z2 = tmp1 + tmp2;
z3 = tmp0 + tmp2;
z4 = tmp1 + tmp3;
z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */
tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */
tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */
tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */
tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */
z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
z3 += z5;
z4 += z5;
tmp0 += z1 + z3;
tmp1 += z2 + z4;
tmp2 += z2 + z3;
tmp3 += z1 + z4;
/* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS);
12
wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS);
wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS);
wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS);
wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS);
wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS);
wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS);
wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS);
inptr++; /* advance pointers to next column */
quantptr++;
wsptr++;
}
/* Pass 2: process rows from work array, store into output array. */
/* Note that we must descale the results by a factor of 8 == 2**3, */
/* and also undo the PASS1_BITS scaling. */
wsptr = workspace;
for (ctr = 0; ctr < DCTSIZE; ctr++) {
outptr = output_buf[ctr] + output_col;
/* Rows of zeroes can be exploited in the same way as we did with columns.
* However, the column calculation has created many nonzero AC terms, so
* the simplification applies less often (typically 5% to 10% of the time).
* On machines with very fast multiplication, it's possible that the
* test takes more time than it's worth. In that case this section
* may be commented out.
*/
#ifndef NO_ZERO_ROW_TEST
if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 &&
wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) {
/* AC terms all zero */
JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3)
& RANGE_MASK];
outptr[0] = dcval;
outptr[1] = dcval;
outptr[2] = dcval;
outptr[3] = dcval;
outptr[4] = dcval;
outptr[5] = dcval;
outptr[6] = dcval;
outptr[7] = dcval;
wsptr += DCTSIZE; /* advance pointer to next row */
continue;
}
#endif
/* Even part: reverse the even part of the forward DCT. */
/* The rotator is sqrt(2)*c(-6). */
13
z2 = (INT32) wsptr[2];
z3 = (INT32) wsptr[6];
z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS;
tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS;
tmp10 = tmp0 + tmp3;
tmp13 = tmp0 - tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp1 - tmp2;
/* Odd part per figure 8; the matrix is unitary and hence its
* transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
*/
tmp0 = (INT32) wsptr[7];
tmp1 = (INT32) wsptr[5];
tmp2 = (INT32) wsptr[3];
tmp3 = (INT32) wsptr[1];
z1 = tmp0 + tmp3;
z2 = tmp1 + tmp2;
z3 = tmp0 + tmp2;
z4 = tmp1 + tmp3;
z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */
tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */
tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */
tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */
tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */
z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
z3 += z5;
z4 += z5;
tmp0 += z1 + z3;
tmp1 += z2 + z4;
tmp2 += z2 + z3;
tmp3 += z1 + z4;
/* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
14
outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0,
CONST_BITS+PASS1_BITS+3)
& RANGE_MASK];
wsptr += DCTSIZE; /* advance pointer to next row */
}
}
2.6 sep_upsample (jdsample.c) /*
* Control routine to do upsampling (and color conversion).
*
* In this version we upsample each component independently.
* We upsample one row group into the conversion buffer, then apply
* color conversion a row at a time.
*/
METHODDEF(void)
sep_upsample (j_decompress_ptr cinfo,
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
JDIMENSION in_row_groups_avail,
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
JDIMENSION out_rows_avail)
{
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
int ci;
jpeg_component_info * compptr;
JDIMENSION num_rows;
/* Fill the conversion buffer, if it's empty */
if (upsample->next_row_out >= cinfo->max_v_samp_factor) {
15
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
ci++, compptr++) {
/* Invoke per-component upsample method. Notice we pass a POINTER
* to color_buf[ci], so that fullsize_upsample can change it.
*/
(*upsample->methods[ci]) (cinfo, compptr,
input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]),
upsample->color_buf + ci);
}
upsample->next_row_out = 0;
}
/* Color-convert and emit rows */
/* How many we have in the buffer: */
num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out);
/* Not more than the distance to the end of the image. Need this test
* in case the image height is not a multiple of max_v_samp_factor:
*/
if (num_rows > upsample->rows_to_go)
num_rows = upsample->rows_to_go;
/* And not more than what the client can accept: */
out_rows_avail -= *out_row_ctr;
if (num_rows > out_rows_avail)
num_rows = out_rows_avail;
(*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf,
(JDIMENSION) upsample->next_row_out,
output_buf + *out_row_ctr,
(int) num_rows);
/* Adjust counts */
*out_row_ctr += num_rows;
upsample->rows_to_go -= num_rows;
upsample->next_row_out += num_rows;
/* When the buffer is emptied, declare this input row group consumed */
if (upsample->next_row_out >= cinfo->max_v_samp_factor)
(*in_row_group_ctr)++;
}
2.7ycc_rgb_convert (jdcolor.c)
这个函数是作为色彩空间转换用的。
接出来的图片是YUV 4:4:4 使用该方法来转换为rgb 888
/*
* Convert some rows of samples to the output colorspace.
*
* Note that we change from noninterleaved, one-plane-per-component format
* to interleaved-pixel format. The output buffer is therefore three times
* as wide as the input buffer.
16
* A starting row offset is provided only for the input buffer. The caller
* can easily adjust the passed output_buf value to accommodate any row
* offset required on that side.
*/
METHODDEF(void)
ycc_rgb_convert (j_decompress_ptr cinfo,
JSAMPIMAGE input_buf, JDIMENSION input_row,
JSAMPARRAY output_buf, int num_rows)
{
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
register int y, cb, cr;
register JSAMPROW outptr;
register JSAMPROW inptr0, inptr1, inptr2;
register JDIMENSION col;
JDIMENSION num_cols = cinfo->output_width;
/* copy these pointers into registers if possible */
register JSAMPLE * range_limit = cinfo->sample_range_limit;
register int * Crrtab = cconvert->Cr_r_tab;
register int * Cbbtab = cconvert->Cb_b_tab;
register INT32 * Crgtab = cconvert->Cr_g_tab;
register INT32 * Cbgtab = cconvert->Cb_g_tab;
SHIFT_TEMPS
while (--num_rows >= 0) {
inptr0 = input_buf[0][input_row];
inptr1 = input_buf[1][input_row];
inptr2 = input_buf[2][input_row];
input_row++;
outptr = *output_buf++;
for (col = 0; col < num_cols; col++) {
y = GETJSAMPLE(inptr0[col]);
cb = GETJSAMPLE(inptr1[col]);
cr = GETJSAMPLE(inptr2[col]);
/* Range-limiting is essential due to noise introduced by DCT losses. */
outptr[RGB_RED] = range_limit[y + Crrtab[cr]];
outptr[RGB_GREEN] = range_limit[y +
((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr],
SCALEBITS))];
outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]];
outptr += RGB_PIXELSIZE;
}
}
}
17
三、 profile
对解码一个1200万像素的图片进行20次解码并profile ~得出的结果显示以下几个函数为热点函数:
1. Jpeg_idct_isLow 41%
2. Ycc_rgb_convert 33%
3. decode_mcu 20%
pc 上大约为这样的比率~但是在ASIC 上由于指令和内存访问上的差别~有可能有
一些不同。但大致的热点应该是这三个。
还有一些条件会影响解码的速度~比如scale 的值~这个值是实际要显示的图片尺寸
和实际图片尺寸之间的比率。例如:800*600的屏幕上要显示一个4272*2848的图
片。那么就可以取1/4作为scale 的系数。这样可以跳过大多数idct 的过程~同时因
为产生的数据少~也减少了color_convert 的处理。
不使用任何的scale 的情况下~在pc 上的结果:
使用1/2的scale 情况下进行profile 的结果:
18
使用1/4的scale情况下进行profile 的结果:
四、数据流图
19
五、调用关系
5.1 Read_scanline:
20
5.2 Process_data_contextmain:
5.3 Decompress_onepass:
21
5.4Upsample:
六、性能提高的方法
1因为我们使用的屏幕是最大800*480的所以在进行idct 和反量化变换的时候可以只解一部分需要的数据~这个在Jpeg_6bSrc 中做了支持。可以做1/1 、1/2、 1/4、 1/8这几种的缩放,具体参看libjpeg.doc line 19 page25,。 如果做的是1/8 的缩放~可以节省大约3/4的idct解码时间。
当然我们可能需要选择性的在上述的几种优化的idct 算法中挑几个来做 cp。当然也可以不缩放直接进行全幅面的idct 但是这个时候消耗的内存较多~而且后期的de_sample 和color_convert 的压力也比较大。
2. color_convert 中不使用rgb 色彩空间~而使用yuv420 的色彩空间~这样就可以避免掉一次的yuv2rgb 的转换过程。但实际上如果使用1/8 或者1/4 的sacle 选项这个color_convert 的耗时也将大大减少。
3.可以将 Huffman 的编码做成cp。这个比较复杂~因为在里面的编码表涉及到多个huffman 表的运算。如果能做成cp 最好,如果不行那么也要想办法来减少这个时间(arm 指令的优化)。
22
七、解压缩图像的控制流程
7.1构造符
解码控制程序的关键是定位和解析各种构造符。第一个构造符必须是SOI(图像开始)构造 符。“Decoder_setup”程序会重设重置间隔(即使得Ri = 0),如果解码器中具备算术解码能力,需要将算术编码使用的条件表恢复到默认值。第二个构造符通常是 SOFn(帧的开始)构造符,如果不是这种构造符,那么第二个构造符肯定是下表中的一个。
注意:在识别任何构造符之前,解码器会自动丢弃X’FF’字节。 解码流程如下图所示:
23
各种构造符的更多的解释如下:
使用霍夫曼编码的解码过程可能出现DHT构造符。使用算术编码的解码过程可能出现DAC构造符。基于DCT变换的解码器会解释DQT构造符,所有的解码器都可能出现DRI构造符。APPn和COM构造符不会影响到解码过程。
根据定义,“构造符解释”程序紧跟在 SOI构造符之后。如果在压缩图像数据的开始没有找到SOI构造符,那么会报错。根据具体的应用,检测和处理错误的复杂性会有一些差别。
7.2帧的解码控制程序
下图介绍了帧的解码控制程序。
24
7.3扫描的解码控制程序
下图介绍了扫描的解码过程。
当所有的扫描间隔都解码完毕后,解码器会自动跳出该循环。
25
7.4重置间隔的解码控制程序
下图介绍了重置间隔的解码控制程序。“Reset_decoder”至少包括以下一些操作: a) 如果使用了算术编码,将利用D.2.7中的“Initdec”程序初始化算术解码器; b) 基于DCT变换的解码过程,将扫描内的各个图像成分的DC预测值(PRED)设为0 (参见F.2.1.3.1);
c) 无损编码过程,将扫描内的各个图像成分的预测值重设为默认值(参见 H.2.1); d) 依赖于具体的实现的初始化设置都是必需的。
26
在重置间隔的末尾,会继续寻找其他的构造符(如果在寻找过程中出现问题,会自动调用错 误处理程序(
7.5最小编码单元(MCU)的解码控制程序
最小编码单元的解码程序参见图E.10。
在图E.10中,Nb是最小编码单元中数据单元的个数。
数据单元的解码程序可以参见附录F, G和H。
27
7.6压缩数据格式
该附录定义了三种压缩数据格式:
a) 交换格式,详见B.2和B.3;
b) 压缩图像数据的缩略格式,详见 B.4;
c) 表说明数据的缩略格式,详见B.5。
B.1介绍了这些格式的必要组成部分。B.1.3和B.1.4中包含格式定义中用到的符号和图表说明表达
的基本规则。
压缩数据格式概述
压缩数据格式结构上包含一系列的参数、构造符和熵编码数据段。参数和构造符常常合在一起
作为构造符字段。压缩数据格式由字节序列构成,字节的各位都有确定的含义。
28
压缩数据格式的必要组成部分:
参数是关于编码过程、源图像特征和应用中一些可选特征的整数。这些参数可能是4比特、,
字节或者,字节的码字。除了一些可选的参数以外,包含关键信息的参数丢失,会直接导致解码过
程无法继续。
参数赋值就是给参数指定特定位长的无符号整数值。
,字节(16比特)长度的参数,在压缩数据比特流中,其最高位最先出现。,位长度的参数都
是成对出现,而且这一对是合并到一个字节中。参数对中的前面一个占用字节的高,位。无论是那
种长度的参数,都是高位在前,低位在后。
B.1.1.2 构造符
构造符是用于区分压缩数据格式的不同子块。大多数构造符是用来作为一些参数字段的开始,
也有一些构造符本身就作为独立部分。所有构造符都是两个字节的码字:X’FF’字节和一个大于,
小于X’FF’的字节构成(表B.1)。任何构造符之前都可能有若干个码字为X’FF’的填充字节。
注意 --- 这种特殊赋值方式中的构造符就保证了在解码过程中,解码器可以相对独立地解析压缩数据的各个子块,而不
依赖于压缩图像的其他部分。
所有构造符都是两个字节的码字:X’FF’字节和一个大于,小于X’FF’的字节构成。所有定义
构造符的第,个字节的说明参见表B.1。星号(*)表示相应的构造符的结束,这样的构造符不是用
来作为构造符字段的开始。
29
30
八、Decode_MCU过程分析
8.1 熵编码
有两种熵编码方法可供选择:霍夫曼编码和算术编码。霍夫曼编码程序使用图,和图,中的表 说明—霍夫曼表。算术编码程序使用算术编码条件表,这也是一种表说明。本标准没有指定默认的 霍夫曼表,实际应用中可以结合具体的条件来设置霍夫曼表。算术编码是有默认的条件表的。 基础顺序式编码过程使用霍夫曼编码,扩展 DCT变换编码和无损模式可以自由选择霍夫曼编码和算术编码。
注:实际上绝大多数的图片都是用了霍夫曼编码的方式,以至在jpeg_v6里都没有实现这样的解码算法。而在jpeg_v7中做了支持,我们可以考虑不对该算法做优化,个人认为不做支持也没有太大的问题。
8.2采样精度
基于DCT变换的编码过程,有两种采样精度可供选择:每个样本点 8位或者12位。当实际应用中需要其他精度时,可以根据需要在源图像样本点,位或者12位精度的基础上作适当
31
的平移即可。基础顺序式仅能使用8位精度。基于 DCT变换12位精度实现比8位要消耗更多的计算资源。
在下文中,8位和12位的 DCT编码过程有完全独立的标准要求。
无损编码过程的采样精度在2位到16位之间任意选择。
8.3多成分控制
本节将介绍如何控制不同成分的编码顺序,以及图像的各个数据单元应该如何选用表说明数据(数据单元是无损模式中的一个采样点,或者是基于DCT变换过程中的一个8×8采样块)
8.3.1多成分交织
图11介绍了编码过程中如何选择源图像数据中的成分以及表说明。图中有,个成分 A, B和 C,以及两个表说明(这里没有区分量化表和熵编码表)。
在顺序编码模式中,非交织 编码是指先编成分 A,等成分A全部结束后编成分B,最后是成分C。交织编码是指依次从成分A, B, C中取出一个数据单元,等,个数据单元编完后再依次从,个成分中各取出,个单元,依此往复。图12中给出了更形象的解释,图中的,个成分都是X行,Y列,每个成分有n个数据单元。
32
顺序控制中也有方法处理各个成分的维度不一致的情况,图13中给出了的例子中,成分B和成分C的横纵维度是成分A的一半。此时,每次从成分 A中取出,个数据单元,然后再依次从成分 B和C中取出,个数据单元,依此往复。当然各个成分之间还有更复杂的维度关系,更详细地解决方法可以参考附录A。
33
8.3.2 最小编码单元
多成分编码的一次交织数据就是最小编码单元(MCU)。如果压缩图像数据是非交织的,MCU就是一个数据单元。以图12为例,MCU就是一个数据单元。如果压缩数据是交织的,MCU包含来自各成分的一个或者多个数据单元。同样以图12 为例,第,个MCU是由,个交织数据单元
A1, B1, C1组成。图 13的第,个MCU由数据单元A1, A2, B1, C1组成。 8.4压缩数据的结构
图,中是作为压缩过程的输出,图,作为解压过程的输入,不同编码过程(有损和无损),不同的操作模式(顺序式,渐进式,无失真和分层模式),压缩图像数据都有统一的结构和参数。压缩图像数据的不同子段都是以两个字节的构造符开始,有些构造符后面紧跟着表说明、帧头或者扫描头等参数,有些是没有具体参数的图像开始和图像结束构造符。构造符和参数构成的整体就是构造符子段。
熵编码器输出的数据同样被划分为若干个子段,重置构造符就是用来划分熵编码数据段的。编码器交替输出重置构造符和熵编码数据,重置构造符和紧随的一段熵编码数据构成源图像数据的一个重置间隔。并不是每一个重置间隔都要解码,因为各个重置间隔都是独立编解码,尤其在一些特殊的应用场合,比如:并行编解码、易损坏的数据段和熵编码段中重置间隔的随机访问。
压缩数据有,种常见格式:
a) 交换格式
b) 压缩图像数据的缩略格式
c) 表说明数据的缩略格式
8.5 交换格式
除了部分必需的构造符字段和熵编码数据段以外,交换格式还必须包含解码过程中会用到的与量化和熵编码表说明相关的所有构造符字段。这样就能够保证图像可以在不同的应用环境中使用,不管在各个应用环境中表说明和压缩图像数据的关系。 8.6 压缩图像数据的缩略格式
压缩图像数据的缩略格式没有包含解压过程中用到的所有表说明,其他的和交换格式是一致的。这种格式适合于同样的应用环境内部图像数据的交换,这些没有包含的表说明可以在应用环境的缺省设置中定义。
8.6.1表说明数据的缩略格式
该格式中只包含表说明数据,主要用来告知解码器所在的应用环境用怎样的表说明来解压将要接收到的压缩图像数据。
34
8.7 图像、帧和扫描
压缩图像数据中只包含一幅图像,在顺序式和渐进式编码过程中,一幅图像只包含一帧数据,但在分层式编码过程中一幅图像会包含多帧数据。一帧数据包含一趟或者多趟扫描。顺序式编码过程中,一趟扫描会编完所有数据,不管是单成分还是多成分。 8.8 基本过程总结
其中每一个霍夫曼码表包含一个17字节的Bits 和一个256字节的霍夫曼表。解码的时候每一个成分使用一种AC码表和一种DC码表。这些码表在符号表解析时被载入。
图B.7中的构造符和参数的定义如下,各个参数的取值范围参见表B.5。
DHT:定义霍夫曼表构造符 , 标识霍夫曼表中参数的开始。
Lh:霍夫曼表长度 , 定义图B.7中所有霍夫曼表参数的长度。
Tc:霍夫曼表类型 , 0表示用于DC系数或者无失真模式,1表示用于AC系数。
Th:霍夫曼表位置标识符 , 从4个可能的存放位置中选择1个,嵌入到解码器中。
Li:长度为i的霍夫曼码字的个数 , 指定16个长度的码字数目, Li 也是BITS的一部分。
Vi,j:量化表元素 , 指定 64个元素中的第k个,其中k是Z形 DCT系数的下标。量化表 元素按照 Z形顺序排列。
35
表B.5中的 n定义的是当前 DHT构造符字段中的霍夫曼表格的数目。m定义了一个霍夫曼表t
中码字的个数,也就是各个不同长度码字数目的和:
不同的霍夫曼表格的m一般是不同的。 t
只要压缩数据中为特定位置定义了霍夫曼表,则该表需要取代该位置原来的霍夫曼表,并且在后面的扫描以及压缩图像中,都会使用该量化表,直到其再一次被覆盖。如果在扫描头中指定了霍夫曼表的位置,但是在表说明中从来没有定义过霍夫曼表,那么解压的结果就无法预知了。
8.9 基于DCT变换的编解码
图,介绍了所有基于DCT变换编码过程的基本过程。这里只是以单一成分为例,因为所有编 码过程针对图像的各个不同成分的操作是相互独立的,故而此处的基本过程具有普遍意义。
36
图6介绍了基于DCT变换解码过程的基本过程。其中的每一步都是编码过程中特定步骤的相反的操作。熵解码Z形序列,反量化,经过DCT逆变换,得到8×8采样块。
在编码过程中,成分的所有采样点被划分为若干个8×8数据块,每个数据块经过DCT正变换 (FDCT) ,转化为64个DCT系数 。其中的,个系数是DC系数,其他的63个系数是AC系数。
根据量化表(图,中的某一个表说明)的64个值,利用表中的每个值对相同位置的DCT系数 量化。该标准中没有定义缺省的量化表,在具体的应用中,用户会结合图像质量、显示设备设置和视觉条件来定制量化表。
量化之后,DC系数和63个AC系数将进行熵编码,如图,所示。前一个数据块中的量化DC系 数作为当前数据块量化DC系数的预测值,并对当前数据块量化DC系数和前一个数据块的量化DC系数的差值编码。63个AC系数没有经过这样的差值编码,而是将二维矩阵分布的系数重排成一维Z形序列,如图,所示。
经过量化重排的AC系数再利用熵编码程序进一步压缩。4.6中介绍了两种熵编码方法可供选 择,其一为霍夫曼编码,压缩图像必须包含霍夫曼表。另一种是算术编码,压缩图像中可以给出条件码表,若不提供,解码过程中将使用默认的条件表说明。
37
九、Idct和反量化过程分析
9.1DCT压缩
9.1.1 平移
在非差分帧计算FDCT之前,所有样本需要平移一位以变为一个带符号的数值,具体实现是减
p
去2-1,其中P是精度参数(参见B.2.2)。当P = 8时,减去128; 当 P = 12时,减去2048。非差分帧解码过程中,经过DCT逆变换生成重构图像样本之后,相反的平移操作可以
p
将采样点的值映射到原来的范围,也就是将DCT逆变换得到的值加上2-1,平移之后的采样
p
点值域范围是,到2-1。
9.1.2 DCT变换中采样点的方向
图A.4中的图像成分被划分为若干个8×8采样块,以便于计算DCT变换。该图中同时介绍了 采样块内采样点的方向和式A.3.3中DCT变换下标之间的对应关系。 采样块的划分和采样点的方向也同样适用于解码过程,以及输出的重构图像。编码过程中因为不完全MCU附加的采样点,在解码过程中都会被丢弃。
38
9.1.3 DCT变换和 DCT反变换
下式定义了DCT变换和DCT反变换的典型表达式:
注意 --- 在实际使用中,不能表达出下式中变量的完全精度。FDCT和量化程序的精度需
求,以及 IDCT和反量化程序
的精度需求在本标准的第,部分中有解释。
其中:
9.1.4 DCT系数的量化和反量化
DCT变换得到的64个DCT系数,利用统一的量化器进行量化。量化系数S的步长就是量化表vu中相同位置的元素Q的值,量化表参见B.2.2中帧参数T。量化器通过如下的圆整函数实现vuqi
的,也就是选取距离商值最近的一个整数:
Sq是量化DCT系数,利用量化步长规范化的结果。 vu
注意 --- 在实际使用中,不能表达出下式中变量的完全精度。FDCT和量化程序的精度需求在本标准的第,部分中有解
释。
在解码器中,上面的规范化可以利用下式进行反量化:
注意 --- 根据量化中圆整的特性,解码器中反量化得到的系数可能会超出理想的取值范围。 图A.5介绍了采样点,DCT系数和量化三者之间的关系。
9.1.5差分DC系数编码
量化结束后,接下来是熵编码,量化DC系数Sq的编码方式有别于其他63个量化AC系数。其00
实际的编码值是同一个成分内当前数据块的量化DC系数(DC也就是Sq)和前一个数据块量i00化DC系数(PRED)的差值(DIFF):
39
9.1.6 Z形顺序
量化后的熵编码中,量化AC系数被重新排列为一个Z形序列。量化 DC系数是单独编码的,参见 9.1.5;量化AC系数的Z形序列的排列顺序参见图A.6。
9.2 点变换
编码之前,可以选择性地进行点变换,即除以,的阶乘。有三种编码需要进行点变换:无失真编码、分层模式中的无失真差分帧编码和基于DCT变换模式中的逐位逼近编码。在无失真模式中,点变换是针对输入采样点。在分层模式差分编码中,点变换是针对输入采Pt样点和参考采样点的差值。这里的点变换就是将采样点或差值除以2作为变换结果,其中A1Pt是点变换参数。 在逐位逼近编码中,AC系数的点变换是系数被2商除,其中A1是逐位逼Al近的比特位置,最低位A1=0。DC系数的点变换是算术右移A1位,也就等同于系数被2商Pt除。 编码器输出时,所的结果需要乘上2。
注 具体的参数请参考itu-81中的b2.3章,还可以看 K.10中的例子
40
十、CP 的优化可能性
10.1 Jpeg 的热点
在JPEG 的解码过程中有2个部分比较耗费资源:
1) 位流的操作
位流的操作耗费资源的原因是操作频度很高,并且在这种操作中需要复杂的左移和右移的操作。
这个过程比较适合用cp来实现。这种操作对SRAM的需求很小,但是操作和结构比较复杂,需
要仔细分析来构造这个cp 。
2) 解码的霍夫曼的表查找
41
霍夫曼长度小于8的查找,使用2个256字节的表。但是其中look_nbits 表可以简化为 16字节
的表。因为这个表只是针对每个长度,做了一个快速索引。可以使用2个字节来表达一个长度,
第一个字节为开始的位置,第二个字节为该块的长度。
42