参考链接:
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的改进:
- g_scanOrder支持宽高不相等时的三种扫描;
- 代码结构更清晰。
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;
}
}