HEVC:三种残差扫描方式之g_sigLastScan & g_scanOrder

参考链接:

initSigLastScan函数详解:https://blog.csdn.net/HEVC_CJL/article/details/8496174

变换系数熵编码:https://blog.csdn.net/Dillon2015/article/details/104254432

扫描表说明:https://blog.csdn.net/chfe007/article/details/42213615

TU逆扫描过程:https://blog.csdn.net/chfe007/article/details/42213753

CG内部逆扫描过程:https://blog.csdn.net/chfe007/article/details/42213817

扫描表示例:https://blog.csdn.net/chfe007/article/details/42267431

g_sigLastScan replaced with g_scanOrder:https://mailman.videolan.org/pipermail/x265-devel/2014-February/003585.html

大约在2014年左右,HM代码中的g_sigLastScan被g_scanOrder取代了。相应地,函数initSigLastScan()class ScanGenerator 中的函数 GetNextIndex() 取代了。调用接口仍然在函数initROM() 里。

g_scanOrder相比g_sigLastScan的改进

  1. g_scanOrder支持宽高不相等时的三种扫描;
  2. 代码结构更清晰。

g_sigLastScan

initSigLastScan()代码分析

函数 initROM() 中调用了 initSigLastScan()

#define   MAX_CU_DEPTH   6
UInt* g_auiSigLastScan[3][MAX_CU_DEPTH]; // 用于存放光栅扫描与对角扫描、水平扫描、垂直扫描的对应关系。

Void initROM()
{
	int i, c;
	...
	initSigLastScan( g_auiSigLastScan[0][i], g_auiSigLastScan[1][i], g_auiSigLastScan[2][i], c, c ); 
	...
}
Void initSigLastScan(Uint* pBuffD, UInt* pBuffH, UInt* pBuffV, Int iWidth, Int iHeight)
// pBuffD存放对角扫描位置信息,pBuffH存放水平扫描位置信息,pBuffV存放垂直扫描位置信息。
{	
	contst UInt uiNumScanPos = UInt( iWidth * iWidth );	// 扫描位置的总个数
	UInt uiNextScanPos = 0;  // 当前扫描位置的序号
	if ( iWidth < 16 ) // 适用于TU2x2, TU4x4, 8x8(得到TU32x32中CG的对角扫描顺序)
	{
		UInt* pBuffTemp = pBuffD;
		if (iWidth == 8 )
		{
			pBuffTemp = g_sigLastScanCG32x32;	// 用于存放TU32x32块中CG(4x4)的扫描顺序。
		}
		for (UInt uiScanLine = 0; uiNextScanPos < uiNumScanPos; uiScanLine++ )
		{
			Int iPrimDim = Int( uiScanLine ); // uiScanLine利用了对角扫描的对称性,辅助获取扫描位置的行坐标iPrimDim。
			Int iScndDim = 0;  // 扫描位置的列坐标。对于从左下到右上的对角扫描,,左边上三角部分的列起始坐标为0.
			while( iPrimDim >= iWidth )
			{
				iScndDim++; // 根据对称关系获得右边下三角部分的列起始坐标
				iPrimDim--;	// 递减到(iWidth - 1)后,跳出while循环
			}
			while( iPrimDim >= 0 && iScndDim < iWidth )
			{
				pBuffTemp[ uiNextScanPos ] = iPrimDim * iWidth + iScndDim; // 存放对角扫描的光栅坐标,即转换扫描方式
				uiNextScanPos++;	// 对角扫描顺序递增
				iScndDim++;	// 从左下到右上,扫描过程中的列坐标递增
				iPrimDim--; // 从左下到右上,扫描过程中的行坐标递减
			}
		}
	}
	if( iWidth > 4 )
	{	// 对于TU8x8,TU16x16和TU32x32,以CG(4x4)为单位进行对角扫描,CG内采用TU4x4的对角扫描
		UInt uiNumBlkSide = iWidth >> 2; // 坐标转换为以CG(4x4)为单位
		UInt uiNumBlks = uiNumBlkSide * uiNumBlkSide; // CG的个数
		UInt log2Blk = g_aucConvertToBit[ uiNumBlkSide ] + 1;	// 以CG为单位,索引对应尺寸的对角扫描坐标。

		for( UInt uiBlk = 0; uiBlk < uiNumBlks; uiBlk++ )
		{
			uiNextScanPos = 0;	// CG(4x4)内扫描位置的序号
			UInt initBlkPos = g_auiSigLastScan[ SCAN_DIAG ] [ log2Blk ][ uiBlk ]; // 以CG为单位,索引对应尺寸的对角扫描坐标。
			if( iWidth == 32 )
			{
				initBlkPos = g_sigLastScanCG32x32[ uiBlk ];  // TU32x32中,以CG为单位的对角扫描信息。
			}
			UInt offsetY = initBlkPos / uiNumBlkSide;	// 当前CG在TU块中的行坐标
			UInt offsetX = initBlkPos - offsetY * uiNumBlkSide;	// 当前CG在TU块中的列坐标
			UInt offsetD = 4 * ( offsetX + offsetY * iWidth );	// 当前CG第一个像素在TU块中的光栅扫描位置。
			UInt offsetScan = 16 * uiBlk; // 已经扫描过的像素个数,注意是以CG为单位进行扫描。
			for( UInt uiScanLine = 0; uiNextScanPos < 16; uiScanLine++ )
			{ // CG(4x4)内的对角扫描,与上文原理同
				Int iPrimDim = Int( uiScanLine );
				Int iScndDim = 0;
				while( iPrimDim >= 4 )
				{
					iScndDim++;
					iPrimDim--;
				}
				while( iPrimDim > = 0 && iScndDim < 4)
				{
					pBuffD[ uiNextScanPos + offsetScan ] = iPrimDim * iWidth + iScndDim + offsetD;
					uiNextScanPos++;
					iScndDim++;
					iPrimDim--;
				}
			}
		}
	}			
	UInt uiCnt = 0;	// 当前扫描位置的序号
	if( iWidth > 2 )
	{	// 对于大于等于4x4的块,均以CG为单位进行扫描
		UInt numBlkSide = iWidth >> 2;	// 坐标转换为以CG(4x4)为单位 

		// 注意下面四层for循环中blkY & blkX 以及 y & x的位置,分别决定了CG和像素的扫描方式是垂直扫描还是水平扫描。
		for(Int blkY = 0; blkY < numBlkSide; blkY++)
		{
			for(Int blkX = 0; blkX < numBlkSide; blkX++)	// CG采用水平扫描
			{
				UInt offset = blkY * 4 * iWidth + blkX * 4;	// 当前CG第一个像素在TU块中的光栅扫描位置。
				for(Int y = 0; y < 4; y++)
				{
					for(Int x = 0; x < 4; x++)				// 像素采用水平扫描
					{
						pBuffH[uiCnt] = y * iWidth + x + offset;
						uiCnt++;
					}
				}
			}
		}
		
		uiCnt = 0;	// 当前扫描位置的序号,回到起始位置
		for(Int blkX = 0; blkX < numBlkSide; blkX++)
		{
			for(Int blkY = 0; blkY < numBlkSide; blkY++)	// CG采用垂直扫描
			{
				UInt offset = blkY * 4 * iWidth + blkX * 4;
				for(Int x = 0; x < 4; x++)
				{
					for(Int y = 0; y < 4; y++)				// 像素采用垂直扫描
					{
						pBuffV[uiCnt] = y * iWidth + x + offset;
						uiCnt++;
					}
				}
			}
		}
	}
	else
	{	// TU2x2比CG(4x4)的尺寸小,单独处理
		for(Int iY = 0; iY < iHeight ; iY++)
		{
			for(Int iX = 0; iX < iWidth; iX++)		// 像素采用水平扫描
			{
				pBuffH[uiCnt] = iY * iWidth + iX;
				uiCnt++;
			}
		}
		
		for(Int iX = 0; iX < iWidth ; iX++)
		{
			for(Int iY = 0; iY < iHeight; iY++)		// 像素采用垂直扫描
			{
				pBuffV[uiCnt] = iY * iWidth + iX;
				uiCnt++;
			}
		}
	}
}


g_scanOrder

initRom() 和 GetNextIndex()代码分析

Void initROM()
{
	...
	// initialise scan orders
	for(UInt log2BlockHeight = 0; log2BlockHeight < MAX_CU_DEPTH; log2BlockHeight++)
	{	// 与TU高度尺寸相关
		for(UInt log2BlockWidth = 0; log2BlockWidth < MAX_CU_DEPTH; log2BlockWidth++)
		{	// 与TU宽度尺寸相关
			const UInt blockWidth = 1 << log2BlockWidth; // TU块的像素宽度
			const UInt blockHeight = 1 << log2BlockHeight;	// TU块的像素高度
			const UInt totalValues = blockWidth * blockHeight; 	// TU块的像素个数
			
			// non-grouped scan orders 以像素为单位的扫描
			for(UInt scanTypeIndex = 0; scanTypeIndex < SCAN_NUMBER_OF_TYPES; scanTypeIndex++)
			{	// 三种扫描方式:0为对角扫描,1为水平扫描,2为垂直扫描
				const COEFF_SCAN_TYPE scanType = COEFF_SCAN_TYPE(scanTypeIndex);
				g_scanOrder[SCAN_UNGROUPED][scanTYpe][log2BlockWidth][log2BlockHeight] = new UInt[totalValues];	// 分配内存
				ScanGenerator fullBlockScan(blockWidth, blockHeight, blockWidth, scanType);	// 定义对象和初始化
				for(UInt scanPosition = 0; scanPosition < totalValues; scanPosition++)
				{
					g_scanOrder[SCAN_UNGROUPED][scanType][log2BlockWidth][log2BlockHeight][scanPosition] = fullBlockScan.GetNextIndex(0,0);	// 计算得到对角扫描、垂直扫描和水平扫描的位置对应关系
				}
			}
			// grouped scan orders  以CG(4x4)为单位的扫描
			const UInt groupWidth = 1 << MLS_CG_LOG2_WIDTH;	// CG宽度等于4
			const UInt groupHeight = 1 << MLS_CG_LOG2_HEIGHT;	// CG高度等于4
			const UInt widthInGroups = blockWidth >> MLS_CG_LOG2_WIDTH; // 转换为以CG为单位的宽度
			const UInt heightInGroups = blockHeight >> MLS_CG_LOG2_HEIGHT; // 转换为以CG为单位的高度
			
			const UInt groupSize = groupWidth * groupHeight; // CG内的像素个数
			const UInt totalGroups = widthInGroups * heightGroups;	// CG的个数
			
			for(UInt scanTypeIndex = 0; scanTypeIndex < SCAN_NUMBER_OF_TYPES; scanTypeIndex++)
			{	// 三种扫描方式:0为对角扫描,1为水平扫描,2为垂直扫描
				const COEFF_SCAN_TYPE scanType = COEFF_SCAN_TYPE(scanTypeIndex);
				g_scanOrder[SCAN_GROUPED_4x4][scanType][log2BlockWidth][log2BlockHeight] = new UInt[totalValues];
				ScanGenerator fullBlockScan(widthInGroups, heightInGroups, groupWidth, scanType); // 定义以像素为单位的对象和初始化
				for(UInt groupsIndex = 0; groupIndex < totalGroups; groupIndex++)
				{
					const UInt groupPositionY = fullBlockScan.GetCurrentY(); // 以CG为单位的Y坐标
					const UInt groupPositionX = fullBlockScan.GetCurrentX(); // 以CG为单位的X坐标
					const UInt groupOffsetX = groupPositionX * groupWidth;	// 以像素为单位的CG-X坐标
					const UInt groupOffsetY = groupPositionY * groupHeight; // 以像素为单位的CG-Y坐标
					const UInt groupOffsetScan = groupIndex * groupSize;	// 以像素为单位的CG偏移坐标
						
					ScanGenerator groupScan(groupWidth, groupHeight, blockWidth, scanType); // 定义以CG为单位的对象和初始化
						
					for(UInt scanPosition = 0; scanPosition < groupSize; scanPosition++)
					{
						g_scanOrder[SCAN_GROUPED_4x4][scanType][log2BlockWidth][log2BlockHeight][groupOffsetScan + scanPosition] = groupScan.GetNextIndex(groupOffsetX, groupOffsetY);	// 计算得到以CG为单位的对角扫描、垂直扫描和水平扫描的位置对应关系					
					}
					fullBlockScan.GetNextIndex(0, 0); // 计算得到CG内以像素为单位的对角扫描、垂直扫描和水平扫描的位置对应关系
				}
			}
		}
	}
}
class ScanGenerator
{
private:
	UInt m_line, m_column;
	const UInt m_blockWidth , m_blockHeight;
	const UInt m_stride;
	const COEFF_SCAN_TYPE m_scanType;
	
public:
	ScanGenerator(UInt blockWidth, UInt blockHeight, UInt stride, COEFF_SCAN_TYPE scanType)
		:m_line(0),m_colume(0),m_blockWidth(blockWidth),m_blockHeight(blockHeight), m_stride(stride),m_scanType(scanType)
		{}
		...
	UInt GetNextIndex(UInt blockOffsetX, UInt blockOffsetY)
	{
		Int rtn = ((m_line + blockOffsetY) * m_stride) + m_column + blockOffsetX;
		
		// advance line and column to the next position
		switch(m_scanType)
		{
			case SCAN_DIAG:	// 对角扫描
			{
				if((m_column == (m_blockWidth - 1)) || (m_line == 0) // if we reach the end of a rank, go diagonally down to the next one
				{
					m_line += m_column + 1;
					m_column = 0;
						
					if(m_line >= m_blockHeight) // if that takes us outside the block, adjust so that we are back on the bottom row
					{
						m_column += m_line - (m_blockHeight - 1);
						m_line = m_blockHeight - 1;
					}
				}
				else
				{
					m_column++;
					m_line--;
				}
			}
			break;
			case SCAN_HOR:	// 水平扫描
			{
				if (m_column == (m_blockWidth - 1))
				{
					m_line++;
					m_column = 0;
				}
				else
				{
					m_column++;
				}				
			}
			break;
			
			case SCAN_VER:	// 垂直扫描
			{
				if (m_line == (m_blockHeight - 1))
				{
					m_column++;
					m_line = 0;
				}
				else
				{
					m_line++;
				}
			}
			break;
				
			default:
			{
				std::cerr << "ERROR:Unknown scan type \"" << m_scanType << "\"in ScanGenerator::GetNextIndex" << std::endl;
				exit(1);
			}
			break;
		}
		return rtn;
	}
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值