H264解析Demo10变换量化_1_CAVLC结果还原为系数矩阵

Posted 叮咚咕噜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了H264解析Demo10变换量化_1_CAVLC结果还原为系数矩阵相关的知识,希望对你有一定的参考价值。

前面已经通过cavlc解析出了numCoeff、trailingOnes、trailingSigns(拖尾系数符号)、levels、totalZeros、runBefore等值,在反变换量化之前,需要根据这些值还原系数矩阵

一、定义

定义数据保存矩阵:int m_residual_matrix_luma[16][4][4];
定义还原函数:Restore_coeff_matrix

二、知识储备

一个宏块在进行CAVLC编码时是如何划分的:4 * 4模式是如何划分的呢?首先将16 * 16的宏块划分成4个8 * 8的,每个8 * 8的都是由cbp的一个bit表示的

基本编程思路:cavlc结果->还原为8 * 8的矩阵->4 * 4的矩阵->再填充矩阵数组

三、代码实现

1、重建8 * 8系数矩阵框架:

//<变换量化><1>根据cavlc结果还原系数矩阵
void CResidual::Restore_coeff_matrix()
{
	UINT8 cbp_luma = m_macroblock_belongs->m_cbp_luma;
	UINT8 cbp_chroma = m_macroblock_belongs->m_cbp_chroma;

	//<变换量化><1>4*4宏块
	if (m_macroblock_belongs->m_mb_type == I4MB)
	{
		for (int blk8Idx = 0; blk8Idx < 4; blk8Idx++)
		{
			if (cbp_luma & (1 << blk8Idx))		//<变换量化><1>判断是否有系数矩阵
			{
				restore_8x8_coeff_block_luma(m_coeff_matrix_luma, blk8Idx, LUMA);		//<变换量化><1>处理8*8的块,将里面4个4*4的系数矩阵都解析出来
			}
		}
	}
}

2、解析8 * 8矩阵

//<变换量化><1>重建8 * 8系数矩阵接口
void CResidual::restore_8x8_coeff_block_luma(int (*matrix)[4][4], int idx, int blockType)
{
	int blkColumnIdxStart = 2 * (idx / 2),  blkRowIdxStart = 2 * (idx % 2);		//<变换量化><2>计算宏块索引序号,宏块的计数是针对于整体16 * 16的块而言的,第一行0-3,第二行4-7
	for (int columnIdx = blkColumnIdxStart; columnIdx < blkColumnIdxStart + 2; columnIdx ++)
	{
		for (int rowIdx = blkRowIdxStart; rowIdx < blkRowIdxStart + 2; rowIdx++)
		{
			restore_4x4_coeff_block_luma(columnIdx, rowIdx, blockType);
		}
	}
}

3、解析4 * 4矩阵

void CResidual::restore_4x4_coeff_block_luma(int column_idx, int row_idx, int blockType)
{
	int max_coeff_num = 0, start_idx = 0;
	Coeff4x4Block *targetBlock = NULL;
	switch (blockType)
	{
	case LUMA:
		max_coeff_num = 16;
		targetBlock = &luma_residual[column_idx][row_idx];		//<变换量化><2>前面cavlc解析出的结果
		break;
	case LUMA_INTRA16x16AC:
		start_idx = 1;		//<变换量化><2>设置为1是因为直流分量已经被单独解析了,所以这里需要从1开始
		max_coeff_num = 15;
		targetBlock = &luma_residual16x16_AC[column_idx][row_idx];
		break;
	default:
		break;
	}

	int coeffBuf[16] = { 0 };
	UINT8 numCoeff = targetBlock->numCoeff, coeffIdx = numCoeff;
	UINT8 trailingOnes = targetBlock->trailingOnes, trailingLeft = trailingOnes;
	UINT8 totalZeros = targetBlock->totalZeros;

	// write trailing ones
	//<变换量化><2>[10 0 2 3 0 0 5 3 0 1....]
	//<变换量化><2>[10 2 3 5 3 1]   先写为这种,后面再补0
	for (int i = numCoeff - trailingOnes, j = trailingOnes - 1; j >= 0; j--)
	{
		coeffBuf[i++] = targetBlock->trailingSign[j];
	}

	// write levels
	//<变换量化><2>写非零非拖尾系数的值
	for (int i = numCoeff - trailingOnes - 1; i >= 0; i--)
	{
		coeffBuf[i] = targetBlock->levels[numCoeff - trailingOnes - 1 - i];
	}

	// move levels with run_before
	//<变换量化><2>[10 0 2 3 0 0 5 3 0 1....]
	//<变换量化><2>[10 2 3 5 3 1 0 0 0 0]		totalZeros=4  - runBefore=1
	//<变换量化><2>[10 2 3 5 3 0 0 0 0 1]		totalZeros=3  - runBefore=0
	//<变换量化><2>[10 2 3 5 0 0 0 3 0 1]		totalZeros=3  - runBefore=2
	//<变换量化><2>[10 2 3 0 0 0 5 3 0 1]
	for (int i = numCoeff - 1; i > 0; i--)
	{
		swap(coeffBuf[i], coeffBuf[i + totalZeros]);
		totalZeros -= targetBlock->runBefore[i];
	}
	swap(coeffBuf[0], coeffBuf[totalZeros]);	//<变换量化><2>如果totalZeros还不为0,需要交换最低频前面的0

	insert_matrix(m_coeff_matrix_luma, coeffBuf, start_idx, max_coeff_num, column_idx, row_idx);		//<变换量化><2>插入系数矩阵数组

// 	int blkIdx = position_to_block_index(row_idx, column_idx);
// 	coeff_invers_transform(m_coeff_matrix_luma[blkIdx], m_residual_matrix_luma[blkIdx]);
}

4、填充系数矩阵数组:

注意因为是Z字型扫描,所以我们定义了一个坐标数组
在这里插入图片描述

以上是关于H264解析Demo10变换量化_1_CAVLC结果还原为系数矩阵的主要内容,如果未能解决你的问题,请参考以下文章

H264解析Demo10变换量化_2_反量化

H264解析Demo11帧内预测编码_1_预测当前块的预测模式

H264解析Demo11帧内预测编码_2_获得参考的像素值

十四变换编码:2H264的整数变换方法——整数变换与量化

视频编解码 — DCT变换和量化

H264编码器13(CAVLC和CABAC简介)