H.266/VVC的块扫描顺序包括对角扫描、水平扫描和垂直扫描顺序(以4x4块为例)
/// coefficient scanning type used in ACS
// 系数扫描类型
enum CoeffScanType
{
SCAN_DIAG = 0, ///< up-right diagonal scan 对角扫描
SCAN_TRAV_HOR = 1, // 水平扫描
SCAN_TRAV_VER = 2, // 垂直扫描
SCAN_NUMBER_OF_TYPES
};
在扫描的时候,又分为整块扫描和按照子块扫描:
enum CoeffScanGroupType
{
SCAN_UNGROUPED = 0,//整块扫描
SCAN_GROUPED_4x4 = 1,//子块扫描
SCAN_NUMBER_OF_GROUP_TYPES = 2
};
对于按照子块扫描,每一种尺寸的变换块的子块扫描的宽度高度如下表所示,其中N的值为1 2 4 8 16 32 64 128.
uint32_t g_log2SbbSize[MAX_CU_DEPTH + 1][MAX_CU_DEPTH + 1][2] =
//===== luma/chroma =====
{
{ { 0,0 },{ 0,1 },{ 0,2 },{ 0,3 },{ 0,4 },{ 0,4 },{ 0,4 },{ 0,4 } },//1xN
{ { 1,0 },{ 1,1 },{ 1,1 },{ 1,3 },{ 1,3 },{ 1,3 },{ 1,3 },{ 1,3 } },//2xN
{ { 2,0 },{ 1,1 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 } },//4xN
{ { 3,0 },{ 3,1 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 } },//8xN
{ { 4,0 },{ 3,1 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 } },//16xN
{ { 4,0 },{ 3,1 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 } },//32xN
{ { 4,0 },{ 3,1 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 } },//64xN
{ { 4,0 },{ 3,1 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 },{ 2,2 } } //128xN
};
为了方便起见,VTM代码中将所有的扫描顺序预存在g_scanOrder变量中,如下所示。对于每一种扫描类型,都使用ScanElement变量表示。其中idx表示当前像素在块中的光栅扫描位置,(x,y)表示当前像素在块中对应的位置。
// flexible conversion from relative to absolute index
struct ScanElement
{
uint32_t idx;
uint16_t x;
uint16_t y;
};
ScanElement *g_scanOrder[SCAN_NUMBER_OF_GROUP_TYPES][SCAN_NUMBER_OF_TYPES][MAX_CU_SIZE / 2 + 1][MAX_CU_SIZE / 2 + 1];
VTM按照每一种扫描顺序将对应位置的idx、x和y存储在g_scanOrder中(扫描顺序就是存储顺序)。对于子块扫描,其子块之间的顺序也是按照相应的扫描顺序决定的,以8x8的对角扫描顺序为例,如下图所示(该扫描顺序也是8x8LFNST对应的对角扫描顺序)
void initROM()
{
gp_sizeIdxInfo = new SizeIndexInfoLog2();
gp_sizeIdxInfo->init(MAX_CU_SIZE);
//初始化m_sizeToIdxTab 1 2 4 8 16 32 64 128
//初始化m_idxToSizeTab 0 1 2 3 4 5 6 7
SizeIndexInfoLog2 sizeInfo;
sizeInfo.init(MAX_CU_SIZE);
// initialize scan orders
// 初始化扫描顺序
// blockHeightIdx和blockWidthIdx为m_idxToSizeTab中的值
for (uint32_t blockHeightIdx = 0; blockHeightIdx < sizeInfo.numAllHeights(); blockHeightIdx++)
{
for (uint32_t blockWidthIdx = 0; blockWidthIdx < sizeInfo.numAllWidths(); blockWidthIdx++)
{
const uint32_t blockWidth = sizeInfo.sizeFrom(blockWidthIdx);
const uint32_t blockHeight = sizeInfo.sizeFrom(blockHeightIdx);
const uint32_t totalValues = blockWidth * blockHeight;
//--------------------------------------------------------------------------------------------------
//non-grouped scan orders
//整块扫描顺序
//遍历扫描类型:对角扫描、水平扫描和垂直扫描顺序
for (uint32_t scanTypeIndex = 0; scanTypeIndex < SCAN_NUMBER_OF_TYPES; scanTypeIndex++)
{
const CoeffScanType scanType = CoeffScanType(scanTypeIndex);
ScanElement * scan = nullptr;
if (blockWidthIdx < sizeInfo.numWidths() && blockHeightIdx < sizeInfo.numHeights())
{
scan = new ScanElement[totalValues];
}
g_scanOrder[SCAN_UNGROUPED][scanType][blockWidthIdx][blockHeightIdx] = scan;
if (scan == nullptr)
{
continue;
}
ScanGenerator fullBlockScan(blockWidth, blockHeight, blockWidth, scanType);
//按照扫描顺序存储在scan数组中
//idx表示像素位置的光栅扫描位置
//x和y分别表示像素在块中的位置
for (uint32_t scanPosition = 0; scanPosition < totalValues; scanPosition++)
{
const int rasterPos = fullBlockScan.GetNextIndex( 0, 0 );
const int posY = rasterPos / blockWidth;
const int posX = rasterPos - ( posY * blockWidth );
scan[scanPosition].idx = rasterPos;
scan[scanPosition].x = posX;
scan[scanPosition].y = posY;
}
}
//--------------------------------------------------------------------------------------------------
//grouped scan orders
//子块扫描顺序
//由于存在高频调零技术,子块扫描的时候对于尺寸为64的块仅扫描前32行/列的数据
const uint32_t* log2Sbb = g_log2SbbSize[floorLog2(blockWidth)][floorLog2(blockHeight)];
const uint32_t log2CGWidth = log2Sbb[0];
const uint32_t log2CGHeight = log2Sbb[1];
const uint32_t groupWidth = 1 << log2CGWidth;//子块宽
const uint32_t groupHeight = 1 << log2CGHeight;//子块高
const uint32_t widthInGroups = std::min(JVET_C0024_ZERO_OUT_TH, blockWidth) >> log2CGWidth;
const uint32_t heightInGroups = std::min(JVET_C0024_ZERO_OUT_TH, blockHeight) >> log2CGHeight;
const uint32_t groupSize = groupWidth * groupHeight;//子块尺寸
const uint32_t totalGroups = widthInGroups * heightInGroups;//子块总数
//遍历扫描类型:对角、水平和垂直扫描
for (uint32_t scanTypeIndex = 0; scanTypeIndex < SCAN_NUMBER_OF_TYPES; scanTypeIndex++)
{
const CoeffScanType scanType = CoeffScanType(scanTypeIndex);
ScanElement *scan = new ScanElement[totalValues];
g_scanOrder[SCAN_GROUPED_4x4][scanType][blockWidthIdx][blockHeightIdx] = scan;
if ( blockWidth > JVET_C0024_ZERO_OUT_TH || blockHeight > JVET_C0024_ZERO_OUT_TH )
{
for (uint32_t i = 0; i < totalValues; i++)
{
scan[i].idx = totalValues - 1;
scan[i].x = blockWidth - 1;
scan[i].y = blockHeight - 1;
}
}
ScanGenerator fullBlockScan(widthInGroups, heightInGroups, groupWidth, scanType);
for (uint32_t groupIndex = 0; groupIndex < totalGroups; groupIndex++)
{
const uint32_t groupPositionY = fullBlockScan.GetCurrentY();
const uint32_t groupPositionX = fullBlockScan.GetCurrentX();
const uint32_t groupOffsetX = groupPositionX * groupWidth;
const uint32_t groupOffsetY = groupPositionY * groupHeight;
const uint32_t groupOffsetScan = groupIndex * groupSize;
ScanGenerator groupScan(groupWidth, groupHeight, blockWidth, scanType);
for (uint32_t scanPosition = 0; scanPosition < groupSize; scanPosition++)
{
const int rasterPos = groupScan.GetNextIndex( groupOffsetX, groupOffsetY );
const int posY = rasterPos / blockWidth;
const int posX = rasterPos - ( posY * blockWidth );
scan[groupOffsetScan + scanPosition].idx = rasterPos;
scan[groupOffsetScan + scanPosition].x = posX;
scan[groupOffsetScan + scanPosition].y = posY;
}
fullBlockScan.GetNextIndex(0, 0);
}
}
//--------------------------------------------------------------------------------------------------
}
}
// initialize CoefTopLeftDiagScan8x8 for LFNST
// 初始化LFNST的左上角8x8块的对角扫描顺序
for( uint32_t blockWidthIdx = 0; blockWidthIdx < sizeInfo.numAllWidths(); blockWidthIdx++ )
{
const uint32_t blockWidth = sizeInfo.sizeFrom( blockWidthIdx );
const static uint8_t g_auiXYDiagScan8x8[ 64 ][ 2 ] =
{
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, 2 }, { 1, 1 }, { 2, 0 }, { 0, 3 }, { 1, 2 },
{ 2, 1 }, { 3, 0 }, { 1, 3 }, { 2, 2 }, { 3, 1 }, { 2, 3 }, { 3, 2 }, { 3, 3 },
{ 0, 4 }, { 0, 5 }, { 1, 4 }, { 0, 6 }, { 1, 5 }, { 2, 4 }, { 0, 7 }, { 1, 6 },
{ 2, 5 }, { 3, 4 }, { 1, 7 }, { 2, 6 }, { 3, 5 }, { 2, 7 }, { 3, 6 }, { 3, 7 },
{ 4, 0 }, { 4, 1 }, { 5, 0 }, { 4, 2 }, { 5, 1 }, { 6, 0 }, { 4, 3 }, { 5, 2 },
{ 6, 1 }, { 7, 0 }, { 5, 3 }, { 6, 2 }, { 7, 1 }, { 6, 3 }, { 7, 2 }, { 7, 3 },
{ 4, 4 }, { 4, 5 }, { 5, 4 }, { 4, 6 }, { 5, 5 }, { 6, 4 }, { 4, 7 }, { 5, 6 },
{ 6, 5 }, { 7, 4 }, { 5, 7 }, { 6, 6 }, { 7, 5 }, { 6, 7 }, { 7, 6 }, { 7, 7 }
};
for( int i = 0; i < 64; i++ )
{
g_coefTopLeftDiagScan8x8[ blockWidthIdx ][ i ].idx = g_auiXYDiagScan8x8[ i ][ 0 ] + g_auiXYDiagScan8x8[ i ][ 1 ] * blockWidth;
g_coefTopLeftDiagScan8x8[ blockWidthIdx ][ i ].x = g_auiXYDiagScan8x8[ i ][ 0 ];
g_coefTopLeftDiagScan8x8[ blockWidthIdx ][ i ].y = g_auiXYDiagScan8x8[ i ][ 1 ];
}
}
#if !JVET_Q0806
for( int idxH = MAX_CU_DEPTH - MIN_CU_LOG2; idxH >= 0; --idxH )
{
for( int idxW = MAX_CU_DEPTH - MIN_CU_LOG2; idxW >= 0; --idxW )
{
int numW = 1 << idxW;
int numH = 1 << idxH;
int ratioW = std::max( 0, idxW - idxH );
int ratioH = std::max( 0, idxH - idxW );
int sum = std::max( (numW >> ratioW), (numH >> ratioH) ) - 1;
for( int y = 0; y < numH; y++ )
{
int idxY = y >> ratioH;
for( int x = 0; x < numW; x++ )
{
int idxX = x >> ratioW;
g_triangleMvStorage[TRIANGLE_DIR_135][idxH][idxW][y][x] = (idxX == idxY) ? 2 : (idxX > idxY ? 0 : 1);
g_triangleMvStorage[TRIANGLE_DIR_45][idxH][idxW][y][x] = (idxX + idxY == sum) ? 2 : (idxX + idxY > sum ? 1 : 0);
}
}
}
}
for (int idxH = 0; idxH < MAX_CU_DEPTH - MIN_CU_LOG2 + 2; ++idxH)
{
for (int idxW = 0; idxW < MAX_CU_DEPTH - MIN_CU_LOG2 + 2; ++idxW)
{
const int nCbH = 1 << (idxH + 1);
const int nCbW = 1 << (idxW + 1);
const int nCbR = (nCbW > nCbH) ? nCbW / nCbH : nCbH / nCbW;
// let SIMD can read at least 64-bit when at last row
g_triangleWeights[0][idxH][idxW] = new int16_t[nCbH * nCbW + 4];
g_triangleWeights[1][idxH][idxW] = new int16_t[nCbH * nCbW + 4];
for (int y = 0; y < nCbH; y++)
{
for (int x = 0; x < nCbW; x++)
{
g_triangleWeights[0][idxH][idxW][y*nCbW + x] = (nCbW > nCbH) ? Clip3(0, 8, x / nCbR - y + 4) : Clip3(0, 8, x - y / nCbR + 4);
g_triangleWeights[1][idxH][idxW][y*nCbW + x] = (nCbW > nCbH) ? Clip3(0, 8, nCbH - 1 - x / nCbR - y + 4) : Clip3(0, 8, nCbW - 1 - x - y / nCbR + 4);
}
}
}
}
#else
initGeoTemplate();
#endif
::memset(g_isReusedUniMVsFilled, 0, sizeof(g_isReusedUniMVsFilled));
#if JVET_Q0503_Q0712_PLT_ENCODER_IMPROV_BUGFIX
for (int qp = 0; qp < 57; qp++)
{
int qpRem = (qp + 12) % 6;
int qpPer = (qp + 12) / 6;
int quantiserScale = g_quantScales[0][qpRem];
int quantiserRightShift = QUANT_SHIFT + qpPer;
double threshQP = ((double)(1 << quantiserRightShift)) / quantiserScale;
g_paletteQuant[qp] = (int)(threshQP*0.16 + 0.5);
}
#endif
}