2009-11-11 11:26
在上一周的x264关于MB模式选择学习后,就JM15.1中模式选择,作为一个比较。
在JM里面,编码的最小的单元还是MB,核心函数还是encode_one_macroblock,不过在JM里面,已经将encode_one_macroblock分成了4个等级,由configfile里面的RDOptimization的设置值来选择不同等级的encode_one_macroblock,函数表现形式如下: switch (params->rdopt) { case 0: encode_one_macroblock = encode_one_macroblock_low; break; case 1: default: encode_one_macroblock = encode_one_macroblock_high; break; case 2: encode_one_macroblock = encode_one_macroblock_highfast; break; case 3: encode_one_macroblock = encode_one_macroblock_highloss; break; } 现在就encode_one_macroblock_low (Slice *currSlice, Macroblock *currMB)进行讨论: 前面这个部分主要是进行参数的设置,ex, //这个Intra主要是对帧间帧内编码的初始的一个选择初始化: intra = (short) (islice || (pslice && img->mb_y==img->mb_y_upd && img->mb_y_upd!=img->mb_y_intra)); //获得SearchMode的flag. UMHEX_decide_intrabk_SAD(); smpUMHEX_decide_intrabk_SAD(); //===== MOTION ESTIMATION FOR 16x16, 16x8, 8x16 BLOCKS ===== //这在个for循环里面对以上3个模式进行比较MV的cost, for (min_cost=INT_MAX, mode=1; mode<4; mode++) { //针对上面的不同的MB的分块,进行块的循环 for (cost=0, block=0; block<(mode==1?1:2); block++) { //进行MB的划分:在下面有详解 PartitionMotionSearch (currMB, mode, block, lambda_mf); //其他函数省略 } } 在完成了16*16,8*16,16*8之后,进行判断是否进行再次的分割, //下面的和上面的类似,进行的是8x8, 8x4, 4x8 and 4x4的分割, if ((!inter_skip) && enc_mb.valid[P8x8]) { } //========= C H O O S E B E S T M A C R O B L O C K M O D E ========= if (enc_mb.valid[0] && bslice) // check DIRECT MODE { if(have_direct) { switch(params->Transform8x8Mode) { case 1: // Mixture of 8x8 & 4x4 transform cost = ((cost8x8_direct < cost_direct) || !(enc_mb.valid[5] && enc_mb.valid[6] && enc_mb.valid[7])) ? cost8x8_direct : cost_direct; break; case 2: // 8x8 Transform only cost = cost8x8_direct; break; default: // 4x4 Transform only cost = cost_direct; break; } } else { //!have_direct cost = GetDirectCostMB (currMB, bslice); } if (cost!=INT_MAX) { cost -= (int)floor(16*enc_mb.lambda_md+0.4999); } if (cost <= min_cost) { if(active_sps->direct_8x8_inference_flag && params->Transform8x8Mode) { if(params->Transform8x8Mode==2) currMB->luma_transform_size_8x8_flag=1; else { if(cost8x8_direct < cost_direct) currMB->luma_transform_size_8x8_flag=1; else currMB->luma_transform_size_8x8_flag=0; } } else currMB->luma_transform_size_8x8_flag=0; //进行PartitionMotionSearch解析,对里面的重要的参数说明 PartitionMotionSearch (Macroblock *currMB, int blocktype, int block8x8, int *lambda_factor) { // 这里的bx0, by0两个数组分别对应了SKIP模式,16×16,16×8,8×16,P8×8这四种模式的横坐标和纵坐标。 //如图所示的16×16宏块,首先划分为4个8×8子块(因为PartitionMotionSearch()函数处理的最小块的尺寸为//8×8),以 4×4block为单位设定坐标,图上已标出4个8×8子块左上角的块坐标。SKIP模式实际上并不牵涉到这个//函数,因此坐标全部置零;16×16模式只 有第一个坐标起作用,后三个置零;16×8只有前两个有意义,标出两个//partition的左上角坐标,如图标出了(0,0),(0,2),对照bx0, by0可以看到相应坐标值;最多子块情况为4个8×8,即最//后一组坐标。 ![]() static int bx0[5][4] = {{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,2,0,0}, {0,2,0,2}}; static int by0[5][4] = {{0,0,0,0}, {0,0,0,0}, {0,2,0,0}, {0,0,0,0}, {0,0,2,2}}; 。。。。 int step_h0 = (params->part_size[ parttype][0]);//这个是对分割的类型的水平的x大小 int step_v0 = (params->part_size[ parttype][1]);//y int step_h = (params->part_size[blocktype][0]);//块的水平x大小,如:16*16,8*16,16*8 int step_v = (params->part_size[blocktype][1]);//y 。。。。。 int by = by0[parttype][block8x8]; int bx = bx0[parttype][block8x8]; //进行帧间编码时,对前后向参考帧 for (list=0; list<numlists;list++) { //对前向或后向的参考帧进行比较 { //===== LOOP OVER SUB MACRO BLOCK partitions for (v=by; v<by + step_v0; v += step_v) { pic_block_y = img->block_y + v; for (h=bx; h<bx+step_h0; h+=step_h) { all_mv = img->all_mv[list][ref][blocktype][v][h]; pic_block_x = img->block_x + h; //--- motion search for block --- if (Motion_Selected != 1) { // BlockMotionSearch 主要是为当前MB找到在参考帧中的最小cost, mcost = BlockMotionSearch (currMB, ref, list, h<<2, v<<2, blocktype, search_range, lambda_factor); *m_cost += mcost; if ( (params->Transform8x8Mode == 1) && params->RDOQ_CP_MV && (blocktype == 4) && currMB->luma_transform_size_8x8_flag) { tmp_mv8[list][ref][v][h][0] = all_mv[0]; tmp_mv8[list][ref][v][h][1] = all_mv[1]; motion_cost8[list][ref][block8x8] = mcost; } } else updateMV_mp(m_cost, ref, list, h, v, blocktype, lambda_factor, block8x8); //--- set motion vectors and reference frame (for motion vector prediction) --- for (j=pic_block_y; j<pic_block_y + step_v; j++) { memset(&ref_array [j][pic_block_x], ref, step_h * sizeof(char)); for (i=pic_block_x; i<pic_block_x + step_h; i++) { memcpy(mv_array [j][i], all_mv, 2* sizeof(short)); } } } } } } } 以上是对JM里面的encode_one_macroblock_low;进行的分析,其他的encode_one_macroblock流程类似。 针对前面的X264的宏块模式的选择:JM在做这一部分的时候,过程比较详细,考虑的也很前面,针对不同的RDoption会有不同的宏块的编码方式,这样得到的信号在信噪比,图像质量方面会有提高,但是计算的复杂度,编码时间等方面就不如x264的直接。 |