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 64128です。
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<unsigned>(JVET_C0024_ZERO_OUT_TH, blockWidth) >> log2CGWidth;
const uint32_t heightInGroups = std::min<unsigned>(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
}