【HEVC学习与研究】29、解码第一个Coding Quadtree结构(1)

简介: 在开始研究这部分代码之前,重新回顾一下理论部分。 众所周知,H.264及其之前的编码标准中,VCL层的核心结构称作“宏块”(MacroBlock, MB),大小为16×16像素。

在开始研究这部分代码之前,重新回顾一下理论部分。

众所周知,H.264及其之前的编码标准中,VCL层的核心结构称作“宏块”(MacroBlock, MB),大小为16×16像素。一个宏块里面包含着一个16×16分辨率的亮度采样矩阵和两个8×8分辨率的色度采样矩阵。相应的,在HEVC中,编码所采用的块结构为树形编码单元(Coding Tree Unit, CTU),每个CTU的大小由编码器决定,并且通常要大于宏块的16×16大小。比CTU更小的单位称为树形编码块(Coding Tree Block, CTB),一个CTU由一个亮度信号CTB及其相应的色度CTB,以及其他一些语法元素构成。一个正方形的亮度CTB的边长(以像素为单位)可能取16、32或者64个像素,CTB越大则压缩效率越高。HEVC标准支持将一个CTB按照四叉树的方法分割为多个更小的块结构。


采用了四叉树分割的方法后,这个树结构保存了亮度和色度编码单元(Coding Unit, CU)的位置和大小。这个四叉树结构的根与当前的这个CTU对应,也就是说,一个亮度CU最大可能的大小就是亮度CTB的大小。通常,一个CU的组成包括一个亮度编码块(Coding Block, CB),一个色度CB以及其他相应的语法元素。一个CTB可能包含一个CU,也可能被分割成多个CU,每一个CU进一步分割成预测单元(Prediction Unit, PU)和树结构的变换单元(Transform Unit, TU)。


判断采用帧内预测还是帧间预测在CU层完成。PU的分割结构就以CU为根,根据基本的预测模式判定,亮度和色度CB被进一步分割,由亮度和色度预测块(Prediction Block, PB)中获取预测值。每一个PB的大小可能由64×64到4×4不等。预测的残差信号由TU进行编码。同PU类似,TU树形结构的根节点也是CU,一个亮度CB的残差信号可能由一个完整的变换块(Transform Block, TB)表示,也可能树形分割成多个子TB。对4×4、8×8、16×16和32×32的正方形TB块,HEVC依然采用整数变换;当TB大小为4×4,且采用帧内编码的残差信号则采用了一种类离散正弦变换的方法。


标准文档的7.3.8.4节给出了Coding quadtree的语法结构:


该结构中的第一个语法元素split_cu_flag[x0][y0]表明这个Coding quadtree是否继续四等分,下标x0和y0表示当前拟作为cu的像素块左上角像素相对于该帧左上角的坐标。从该结构的定义来看,Coding quadtree采用了递归的结构,只要split_cu_flag[x0][y0]为1,则这个Coding quadtree就会继续分割下去,直到split_cu_flag[x0][y0]为0时,这个Coding quadtree进一步处理为一个coding unit。

回到代码中。实现解析Coding quadtree的函数由Void TDecCu::xDecodeCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth, UInt& ruiIsLast)实现:

Void TDecCu::xDecodeCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth, UInt& ruiIsLast)
{
	TComPic* pcPic = pcCU->getPic();
	UInt uiCurNumParts    = pcPic->getNumPartInCU() >> (uiDepth<<1);
	UInt uiQNumParts      = uiCurNumParts>>2;

	Bool bBoundary = false;
	UInt uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
	UInt uiRPelX   = uiLPelX + (g_uiMaxCUWidth>>uiDepth)  - 1;
	UInt uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ];
	UInt uiBPelY   = uiTPelY + (g_uiMaxCUHeight>>uiDepth) - 1;

	TComSlice * pcSlice = pcCU->getPic()->getSlice(pcCU->getPic()->getCurrSliceIdx());
	Bool bStartInCU = pcCU->getSCUAddr()+uiAbsPartIdx+uiCurNumParts>pcSlice->getSliceSegmentCurStartCUAddr()&&pcCU->getSCUAddr()+uiAbsPartIdx<pcSlice->getSliceSegmentCurStartCUAddr();
	if((!bStartInCU) && ( uiRPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiBPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
	{
		m_pcEntropyDecoder->decodeSplitFlag( pcCU, uiAbsPartIdx, uiDepth );
	}
	else
	{
		bBoundary = true;
	}

	if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth ) ) || bBoundary )
	{
		UInt uiIdx = uiAbsPartIdx;
		if( (g_uiMaxCUWidth>>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
		{
			setdQPFlag(true);
			pcCU->setQPSubParts( pcCU->getRefQP(uiAbsPartIdx), uiAbsPartIdx, uiDepth ); // set QP to default QP
		}

		for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )
		{
			uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiIdx] ];
			uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiIdx] ];

			Bool bSubInSlice = pcCU->getSCUAddr()+uiIdx+uiQNumParts>pcSlice->getSliceSegmentCurStartCUAddr();
			if ( bSubInSlice )
			{
				if ( !ruiIsLast && ( uiLPelX < pcCU->getSlice()->getSPS()->getPicWidthInLumaSamples() ) && ( uiTPelY < pcCU->getSlice()->getSPS()->getPicHeightInLumaSamples() ) )
				{
					xDecodeCU( pcCU, uiIdx, uiDepth+1, ruiIsLast );
				}
				else
				{
					pcCU->setOutsideCUPart( uiIdx, uiDepth+1 );
				}
			}

			uiIdx += uiQNumParts;
		}
		if( (g_uiMaxCUWidth>>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
		{
			if ( getdQPFlag() )
			{
				UInt uiQPSrcPartIdx;
				if ( pcPic->getCU( pcCU->getAddr() )->getSliceSegmentStartCU(uiAbsPartIdx) != pcSlice->getSliceSegmentCurStartCUAddr() )
				{
					uiQPSrcPartIdx = pcSlice->getSliceSegmentCurStartCUAddr() % pcPic->getNumPartInCU();
				}
				else
				{
					uiQPSrcPartIdx = uiAbsPartIdx;
				}
				pcCU->setQPSubParts( pcCU->getRefQP( uiQPSrcPartIdx ), uiAbsPartIdx, uiDepth ); // set QP to default QP
			}
		}
		return;
	}

	if( (g_uiMaxCUWidth>>uiDepth) >= pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
	{
		setdQPFlag(true);
		pcCU->setQPSubParts( pcCU->getRefQP(uiAbsPartIdx), uiAbsPartIdx, uiDepth ); // set QP to default QP
	}

	if (pcCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
	{
		m_pcEntropyDecoder->decodeCUTransquantBypassFlag( pcCU, uiAbsPartIdx, uiDepth );
	}

	// decode CU mode and the partition size
	if( !pcCU->getSlice()->isIntra())
	{
		m_pcEntropyDecoder->decodeSkipFlag( pcCU, uiAbsPartIdx, uiDepth );
	}

	if( pcCU->isSkipped(uiAbsPartIdx) )
	{
		m_ppcCU[uiDepth]->copyInterPredInfoFrom( pcCU, uiAbsPartIdx, REF_PIC_LIST_0 );
		m_ppcCU[uiDepth]->copyInterPredInfoFrom( pcCU, uiAbsPartIdx, REF_PIC_LIST_1 );
		TComMvField cMvFieldNeighbours[MRG_MAX_NUM_CANDS << 1]; // double length for mv of both lists
		UChar uhInterDirNeighbours[MRG_MAX_NUM_CANDS];
		Int numValidMergeCand = 0;
		for( UInt ui = 0; ui < m_ppcCU[uiDepth]->getSlice()->getMaxNumMergeCand(); ++ui )
		{
			uhInterDirNeighbours[ui] = 0;
		}
		m_pcEntropyDecoder->decodeMergeIndex( pcCU, 0, uiAbsPartIdx, uiDepth );
		UInt uiMergeIndex = pcCU->getMergeIndex(uiAbsPartIdx);
		m_ppcCU[uiDepth]->getInterMergeCandidates( 0, 0, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand, uiMergeIndex );
		pcCU->setInterDirSubParts( uhInterDirNeighbours[uiMergeIndex], uiAbsPartIdx, 0, uiDepth );

		TComMv cTmpMv( 0, 0 );
		for ( UInt uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
		{        
			if ( pcCU->getSlice()->getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
			{
				pcCU->setMVPIdxSubParts( 0, RefPicList( uiRefListIdx ), uiAbsPartIdx, 0, uiDepth);
				pcCU->setMVPNumSubParts( 0, RefPicList( uiRefListIdx ), uiAbsPartIdx, 0, uiDepth);
				pcCU->getCUMvField( RefPicList( uiRefListIdx ) )->setAllMvd( cTmpMv, SIZE_2Nx2N, uiAbsPartIdx, uiDepth );
				pcCU->getCUMvField( RefPicList( uiRefListIdx ) )->setAllMvField( cMvFieldNeighbours[ 2*uiMergeIndex + uiRefListIdx ], SIZE_2Nx2N, uiAbsPartIdx, uiDepth );
			}
		}
		xFinishDecodeCU( pcCU, uiAbsPartIdx, uiDepth, ruiIsLast );
		return;
	}

	m_pcEntropyDecoder->decodePredMode( pcCU, uiAbsPartIdx, uiDepth );
	m_pcEntropyDecoder->decodePartSize( pcCU, uiAbsPartIdx, uiDepth );

	if (pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N )
	{
		m_pcEntropyDecoder->decodeIPCMInfo( pcCU, uiAbsPartIdx, uiDepth );

		if(pcCU->getIPCMFlag(uiAbsPartIdx))
		{
			xFinishDecodeCU( pcCU, uiAbsPartIdx, uiDepth, ruiIsLast );
			return;
		}
	}

	UInt uiCurrWidth      = pcCU->getWidth ( uiAbsPartIdx );
	UInt uiCurrHeight     = pcCU->getHeight( uiAbsPartIdx );

	// prediction mode ( Intra : direction mode, Inter : Mv, reference idx )
	m_pcEntropyDecoder->decodePredInfo( pcCU, uiAbsPartIdx, uiDepth, m_ppcCU[uiDepth]);

	// Coefficient decoding
	Bool bCodeDQP = getdQPFlag();
	m_pcEntropyDecoder->decodeCoeff( pcCU, uiAbsPartIdx, uiDepth, uiCurrWidth, uiCurrHeight, bCodeDQP );
	setdQPFlag( bCodeDQP );
	xFinishDecodeCU( pcCU, uiAbsPartIdx, uiDepth, ruiIsLast );
}

TDecEntropy::decodeSplitFlag   ( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth )函数调用m_pcEntropyDecoderIf->parseSplitFlag( pcCU, uiAbsPartIdx, uiDepth )来解析 split_cu_flag[x0][y0]的值。在这个过程中,由于规定了最大的CUDepth为4,所以只进行了4次分割便返回。其后,将处理coding block数据。






目录
相关文章
|
缓存 监控 前端开发
前端开发中的性能瓶颈分析与优化
【7月更文挑战第27天】前端开发中的性能优化是一个系统工程,需要从多个角度入手,综合运用多种策略。通过减少网络延迟、优化资源加载、优化DOM操作、优化JavaScript执行以及第三方服务优化等措施,可以显著提升前端应用的性能。同时,通过性能监控和调优工具的使用,可以持续监控和优化应用性能,确保用户获得流畅、高效的体验。
|
Kubernetes 网络协议 调度
在K8S中,如何具体实现Pod的IP地址发生变化时,不影响正常服务使用?
在K8S中,如何具体实现Pod的IP地址发生变化时,不影响正常服务使用?
|
存储 SQL 数据可视化
三维引擎系列(三):BIM数据管理与可视化功能
Ganos三维引擎的BIM数据管理分析解决方案,旨在充分发挥BIM模型价值,满足数字孪生技术发展的高精度需求。该方案通过结构化拆解BIM数据,实现统一管理和联合查询;支持精细化计算BIM模型指标,并与规划红线对比;同时提供高效渲染能力。Ganos内置多种功能,如ST_ImportIFC导入IFC格式数据,ST_As3DTiles生成3D Tiles瓦片数据结构,无需依赖第三方软件即可完成BIM数据的存储、计算与可视化展示。此外,通过简单的后端服务即可实现与渲染引擎的无缝对接,显著提升三维空间计算效率。
253 0
|
存储 编解码
FFmpeg开发笔记(三十)解析H.264码流中的SPS帧和PPS帧
《FFmpeg开发实战》书中介绍了音视频编码历史,重点讲述H.264的成功在于其分为视频编码层和网络抽象层。H.264帧类型包括SPS(序列参数集,含视频规格参数),PPS(图像参数集,含编码参数)和IDR帧(立即解码刷新,关键帧)。SPS用于计算视频宽高和帧率,PPS存储编码设置,IDR帧则标志新的解码序列。书中还配以图片展示各帧结构详情,完整内容可参考相关书籍。
654 7
FFmpeg开发笔记(三十)解析H.264码流中的SPS帧和PPS帧
|
存储 Java API
Mac安装jadx并配置环境
Mac安装jadx并配置环境
1298 0
|
XML SQL Java
EA详细使用教程
EA详细使用教程
EA详细使用教程
|
Python C# C++
工业基础类IFC—开源库汇总
工业基础类IFC—开源库汇总
工业基础类IFC—开源库汇总

热门文章

最新文章