H264解析Demo11帧内预测编码_1_预测当前块的预测模式
Posted 叮咚咕噜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了H264解析Demo11帧内预测编码_1_预测当前块的预测模式相关的知识,希望对你有一定的参考价值。
1、调用
遍历所有的宏块,先将宏块通过cavlc、反变换、反量化解析出残差数据,然后再解码宏块
for (int idx = 0; idx < m_max_mb_number; idx++) //m_max_mb_number一帧数据中的所有16*16的宏块
{
m_macroblocks[idx] = new CMacroblock(m_pSODB, macroblockOffset, idx);
m_macroblocks[idx]->Set_paramaters(m_pps_active); //因为解析宏块需要用到pps,所以保存图像参数集
m_macroblocks[idx]->Set_slice_struct(this); //保存slice信息到宏块中
macroblockOffset += m_macroblocks[idx]->Parse_macroblock(); //解析宏块,返回当前解析了的宏块大小
m_macroblocks[idx]->Decode_macroblock();
}
2、解码宏块
//<预测编码><1>解码宏块
int CMacroblock::Decode_macroblock()
{
int err = 0;
if (m_mb_type == I4MB)
{
//<预测编码><1>获取预测块->与残差数据一起重建解码像素
err = get_intra_blocks_4x4();
if (err < 0)
{
return err;
}
}
return kPARSING_ERROR_NO_ERROR;
}
3、4*4子宏块解析
分别遍历获取16个字宏块的值
//<预测编码><1>函数内容:获取预测块->与残差数据一起重建解码像素
int CMacroblock::get_intra_blocks_4x4()
{
int err = 0;
for (UINT8 block_idx = 0; block_idx < 16; block_idx++)
{
err = get_pred_block_of_idx(block_idx); //<预测编码><1>分别调用获取16个子宏块的预测块
if (err < 0)
{
return err;
}
err = reconstruct_block_of_idx(block_idx);
if (err < 0)
{
return err;
}
dump_block_info(block_idx);
}
return kPARSING_ERROR_NO_ERROR;
}
4、获取子宏块预测值
1、获取预测模式
2、通过预测模式获取预测块
4.1、相邻块位置信息的表示
//<预测编码><2>相邻块位置结构
typedef struct NeighborBlockPos
{
UINT32 target_mb_idx; //<预测编码><2>某一个相邻子块在第几个宏块的内部
UINT8 block_row; //<预测编码><2>子块在目标宏块中的行
UINT8 block_column; //<预测编码><2>子块在目标宏块中的列
} NeighborBlockPos;
//<预测编码><2>相邻块信息
typedef struct NeighborBlocks
{
UINT8 flags; //Availability of neighbor blocks: 1 - left; 2 - top; 4 - top_right; 8 - top_left;下面值是否有效的判断
NeighborBlockPos left;
NeighborBlockPos top;
NeighborBlockPos top_right;
NeighborBlockPos top_left;
} NeighborBlocks;
4.2、根据4*4宏块的index映射出行列坐标
/* //<预测编码><2>根据4*4宏块的index映射出行列坐标
block position of each index:
row: 0 1 2 3
_______________
col:0 | 0 | 1 | 4 | 5 |
1 | 2 | 3 | 6 | 7 |
2 | 8 | 9 | 12| 13|
3 | 10| 11| 14| 15|
*/ //<预测编码><2>先排列左上角,再右上角
int block_index_to_position(UINT8 blkIdx, UINT8 &block_pos_row, UINT8 &block_pos_column)
{
/*
block8_index of each index: block4_index of each index:
row: 0 1 2 3 row: 0 1 2 3
_______________ _______________
col:0 | 0 | 0 | 1 | 1 | col:0 | 0 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 1 | 1 | 2 | 3 | 2 | 3 |
2 | 2 | 2 | 3 | 3 | 2 | 0 | 1 | 0 | 1 |
3 | 2 | 2 | 3 | 3 | 3 | 2 | 3 | 2 | 3 |
*/
UINT8 block8_idx = blkIdx / 4, block4_index = blkIdx % 4; /* 0 1 2 3 */
//<预测编码><2>block8_idx表示是哪个8*8宏块,block4_index表示8*8块中的第几个子块
/*
(block_row, block_column) of each index:
row: 0 1 2 3
_______________ _______________
col:0 | (0,0) | (1,0) | (0,0) | (1,0) |
1 | (0,1) | (1,1) | (0,1) | (1,1) |
2 | (0,0) | (1,0) | (0,0) | (1,0) |
3 | (0,1) | (1,1) | (0,1) | (1,1) |
*/
UINT8 block4_row = block4_index % 2, block4_column = block4_index / 2; /* 0 1 */
/*
(block_row, block_column) of each index:
row: 0 1 2 3
_______________ _______________
col:0 | (0,0) | (1,0) | (2,0) | (3,0) |
1 | (0,1) | (1,1) | (2,1) | (3,1) |
2 | (0,2) | (1,2) | (2,2) | (3,2) |
3 | (0,3) | (1,3) | (2,3) | (3,3) |
*/
UINT8 block_row = block4_row + 2 * (block8_idx % 2), block_column = block4_column + 2 * (block8_idx / 2);
block_pos_row = block_row;
block_pos_column = block_column;
return kPARSING_ERROR_NO_ERROR;
}
4.3获取相邻块位置和可用性
1、获取左邻块
2、获取上邻块
3、获取左上
4、获取右上
//<预测编码><3>获取相邻块有效性
int CMacroblock::get_neighbor_blocks_availablility(NeighborBlocks &neighbors, int block_idc_row, int block_idc_column)
{
int mb_idx = m_mb_idx;
int width_in_mb = m_slice->m_sps_active->Get_pic_width_in_mbs();
int height_in_mb = m_slice->m_sps_active->Get_pic_height_in_mbs();
bool left_edge_mb = (mb_idx % width_in_mb == 0); //<预测编码><3>是否在左边沿
bool top_edge_mb = (mb_idx < width_in_mb); //<预测编码><3>是否在最顶端
bool right_edge_mb = ((mb_idx + 1) % width_in_mb == 0); //<预测编码><3>是否在最右边
//<预测编码><3>计算左边沿块
if (!left_edge_mb)
{
neighbors.flags |= 1;
neighbors.left.target_mb_idx = (block_idc_row == 0 ? (mb_idx - 1) : mb_idx); //<预测编码><3>判断是否在同一宏块,还是需要减1
neighbors.left.block_row = (block_idc_row == 0 ? 3 : (block_idc_row - 1));
neighbors.left.block_column = block_idc_column;
}
else //左边沿宏块
{
if (block_idc_row == 0)
{
neighbors.flags &= 14;
}
else //内部块
{
neighbors.flags |= 1;
neighbors.left.target_mb_idx = mb_idx;
neighbors.left.block_row = block_idc_row - 1;
neighbors.left.block_column = block_idc_column;
}
}
if (!top_edge_mb)
{
neighbors.flags |= 2;
neighbors.top.target_mb_idx = (block_idc_column == 0 ? mb_idx - width_in_mb : mb_idx);
neighbors.top.block_row = block_idc_row;
neighbors.top.block_column = (block_idc_column == 0) ? 3 : block_idc_column - 1;
}
else //上边沿宏块
{
if (block_idc_column == 0)
{
neighbors.flags &= 13;
}
else //内部块
{
neighbors.flags |= 2;
neighbors.top.target_mb_idx = mb_idx;
neighbors.top.block_row = block_idc_row;
neighbors.top.block_column = block_idc_column - 1;
}
}
//<预测编码><3>获得左上角宏块
if ((left_edge_mb && block_idc_row == 0) || (top_edge_mb && block_idc_column == 0))
{
neighbors.flags &= 7;
}
else
{
neighbors.flags |= 8;
if (block_idc_row != 0 && block_idc_column != 0)
{
neighbors.top_left.target_mb_idx = mb_idx;
neighbors.top_left.block_row = block_idc_row - 1;
neighbors.top_left.block_column = block_idc_column - 1;
}
else if (block_idc_row == 0 && block_idc_column == 0)
{
neighbors.top_left.target_mb_idx = mb_idx - width_in_mb - 1;
neighbors.top_left.block_row = 3;
neighbors.top_left.block_column = 3;
}
else if (block_idc_row != 0 && block_idc_column == 0)
{
neighbors.top_left.target_mb_idx = mb_idx - width_in_mb;
neighbors.top_left.block_row = block_idc_row - 1;
neighbors.top_left.block_column = 3;
}
else if (block_idc_row == 0 && block_idc_column != 0)
{
neighbors.top_left.target_mb_idx = mb_idx - 1;
neighbors.top_left.block_row = 3;
neighbors.top_left.block_column = block_idc_column - 1;
}
}
//<预测编码><3>获取右上角
if ((right_edge_mb && block_idc_row == 3) || (top_edge_mb && block_idc_column == 0) ||
(block_idc_row == 3 && block_idc_column != 0) || (block_idc_row == 1 && block_idc_column == 1) || (block_idc_row == 1 && block_idc_column == 3))
{
neighbors.flags &= 11;
}
else
{
neighbors.flags |= 4;
if (block_idc_row == 3 && block_idc_column == 0)
{
neighbors.top_right.target_mb_idx = mb_idx - width_in_mb + 1;
neighbors.top_right.block_row = 0;
neighbors.top_right.block_column = 3;
}
else if (block_idc_column == 0 && block_idc_row != 3)
{
neighbors.top_right.target_mb_idx = mb_idx - width_in_mb;
neighbors.top_right.block_row = block_idc_row + 1;
neighbors.top_right.block_column = 3;
}
else if (block_idc_row != 3 && block_idc_column != 0)
{
neighbors.top_right.target_mb_idx = mb_idx;
neighbors.top_right.block_row = block_idc_row + 1;
neighbors.top_right.block_column = block_idc_column - 1;
}
}
return kPARSING_ERROR_NO_ERROR;
}
4.4预测当前块的预测模式
//<预测编码><4>推导帧内预测模式
bool dcPredModePredictionFlag = false; //<预测编码><4>DC模式的标志位
//<预测编码><4>分别表示4个预测方向是否是有效的
bool available_left = neighbors.flags & 1, available_top = neighbors.flags & 2, available_top_right = neighbors.flags & 4, available_top_left = neighbors.flags & 8;
int leftMode = -1, topMode = -1, this_intra_mode = -1;
if (!available_left || !available_top) //<预测编码><4>协议文档或十五、帧内编码:2、预测当前块的预测模式
{
dcPredModePredictionFlag = true;
}
else
{
//<预测编码><4>获取宏块类型、预测模式
CMacroblock *leftMB = m_slice->Get_macroblock_at_index(neighbors.left.target_mb_idx);
CMacroblock *topMB = m_slice->Get_macroblock_at_index(neighbors.top.target_mb_idx);
leftMBType = leftMB->m_mb_type;
topMBType = topMB->m_mb_type;
leftMode = leftMB->get_neighbor_block_intra_mode(neighbors.left);
topMode = topMB->get_neighbor_block_intra_mode(neighbors.top);
}
//<预测编码><4>根据标准文档:如果dcPredModePredictedFlag被设为1,或者相邻块A或B所在的宏块不是Intra 4×4或8×8模式,那么A或者B的预测模式intra4x4(8x8)PredModeA / B的值设为2;
if (dcPredModePredictionFlag || leftMBType != I4MB)
{
leftMode = 2;
}
if (dcPredModePredictionFlag || topMBType != I4MB)
{
topMode = 2;
}
//<预测编码><4>根据标准文档:从相邻块的帧内预测模式得出当前块的预测模式
int predIntra4x4PredMode = min(leftMode, topMode);
if (m_pred_struct[blkIdx].prev_intra_pred_mode_flag)
{
this_intra_mode = predIntra4x4PredMode;
}
else if (m_pred_struct[blkIdx].rem_intra_pred_mode < predIntra4x4PredMode)
{
this_intra_mode = m_pred_struct[blkIdx].rem_intra_pred_mode;
}
else
{
this_intra_mode = m_pred_struct[blkIdx].rem_intra_pred_mode + 1;
}
m_intra_pred_mode[blkIdx] = this_intra_mode; //<预测编码><4>保存当前宏块的预测模式,供之后使用
m_pred_struct[blkIdx].block_mode = this_intra_mode;
以上是关于H264解析Demo11帧内预测编码_1_预测当前块的预测模式的主要内容,如果未能解决你的问题,请参考以下文章