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_预测当前块的预测模式的主要内容,如果未能解决你的问题,请参考以下文章

文心一言 VS chatgpt (21)-- h264编码一帧数据的流程是怎样的?

H264帧内预测模式编号的编码过程

H264编码原理之帧间预测与帧内预测

H264编码原理之帧间预测与帧内预测

十五帧内编码:2预测当前块的预测模式

音视频 - H264结构