最近实验室要跟进AVS会议了,我先看了一下和VVC中相同的技术,帧内预测中的CCLM,在AVS中名字换成了TSCPM,也叫两步色度预测模式,可能是因为专利的冲突,因此才换名字的。实则换汤不换药~
AVS是国内的数字音视频编解码技术标准工作组
参照国际视频标准会议H.266/VVC
目前AVS的参考软件是HPM3.2版本,TSCPM是其中帧内色度预测模式的一种,和VVC的最新参考软件版本VTM5.0中的CCLM一模一样,区别就在于选点的方式不同,而且没有MDLM技术。
下面我来讲一下HPM3.2中的TSCPM技术的细节和具体的代码实现
一、HPM3.2中首先选取固定位置的四个点,然后分了以下三种情况:
①如果左一列和上一行同时存在,则左和上分别取首尾两点如上图所示的01点和23点;
②如果上边存在,但是左边不存在,则对上边等间隔取四个点,间隔为宽度的1/4
③如果左边存在,但是上边不存在,则对左边等间隔取四个点,间隔为高度的1/4
if (bAboveAvaillable)
{
pCur = pCurAbove;
//要获取重建的亮度需要去从xGetSrcPixel()函数中获取,即VTM中的pSrc
int idx = ((numSteps - 1) * uiCWidth) / minDim;
iRefPointLuma[0] = xGetSrcPixel(0, 1); // 上一行的第一个参考点的重建亮度;
iRefPointChroma[0] = pCur[0];//对应的重建色度
iRefPointLuma[1] = xGetSrcPixel(idx, 1); // 上一行的最后一个参考点的重建亮度;
iRefPointChroma[1] = pCur[idx];
// 如果左一列不存在,即仅有一条边,则一条边使用四个点
if (!bLeftAvaillable && uiCWidth >= 4)
{
int uiStep = uiCWidth >> 2;//跳点间隔是宽度的1/4
for (int i = 0; i < 4; i++)
{
iRefPointLuma[i] = xGetSrcPixel(i * uiStep, 1); // pSrc[i * uiStep];
iRefPointChroma[i] = pCur[i * uiStep];
}
}
}
//如果左有效,和上面是操作相同
if (bLeftAvaillable)
{
pCur = pCurLeft;
int idx = ((numSteps - 1) * uiCHeight) / minDim;
iRefPointLuma[2] = xGetSrcPixel(0, 0); // pSrc[0];
iRefPointChroma[2] = pCur[0];
iRefPointLuma[3] = xGetSrcPixel(idx, 0); // pSrc[idx * iSrcStride];
iRefPointChroma[3] = pCur[idx];
// using 4 points when only one border
if (!bAboveAvaillable && uiCHeight >= 4)
{
int uiStep = uiCHeight >> 2;
for (int i = 0; i < 4; i++)
{
iRefPointLuma[i] = xGetSrcPixel(i * uiStep, 0); // pSrc[i * uiStep * iSrcStride];
iRefPointChroma[i] = pCur[i * uiStep];
}
}
}
二、然后对四个点进行排序得到最大点和最小点,也分了三种情况:
①如果左一列和上一行同时存在,则对左和上分别取到的首尾两点进行四次比较,得到两个较小的点,然后求平均得到最小点。得到两个较大点,求平均得到最大点;
if ((bAboveAvaillable && bLeftAvaillable)
|| (bAboveAvaillable && !bLeftAvaillable && uiCWidth >= 4)
|| (bLeftAvaillable && !bAboveAvaillable && uiCHeight >= 4))
{
#if SIMP_TSCPM
//如果上和左都存在,则将取到的四个点进行排序,找出两个较大和两个较小的点
int minGrpIdx[2] = { 0, 2 };
int maxGrpIdx[2] = { 1, 3 };
int *tmpMinGrp = minGrpIdx;
int *tmpMaxGrp = maxGrpIdx;
if (iRefPointLuma[tmpMinGrp[0]] > iRefPointLuma[tmpMinGrp[1]]) xExchange(tmpMinGrp[0], tmpMinGrp[1], int);
if (iRefPointLuma[tmpMaxGrp[0]] > iRefPointLuma[tmpMaxGrp[1]]) xExchange(tmpMaxGrp[0], tmpMaxGrp[1], int);
if (iRefPointLuma[tmpMinGrp[0]] > iRefPointLuma[tmpMaxGrp[1]]) xExchange(tmpMinGrp, tmpMaxGrp, int *);
if (iRefPointLuma[tmpMinGrp[1]] > iRefPointLuma[tmpMaxGrp[0]]) xExchange(tmpMinGrp[1], tmpMaxGrp[0], int);
assert(iRefPointLuma[tmpMaxGrp[0]] >= iRefPointLuma[tmpMinGrp[0]]);
assert(iRefPointLuma[tmpMaxGrp[0]] >= iRefPointLuma[tmpMinGrp[1]]);
assert(iRefPointLuma[tmpMaxGrp[1]] >= iRefPointLuma[tmpMinGrp[0]]);
assert(iRefPointLuma[tmpMaxGrp[1]] >= iRefPointLuma[tmpMinGrp[1]]);
//两个较小的点求平均,得到最小点
xMin = (iRefPointLuma[tmpMinGrp[0]] + iRefPointLuma[tmpMinGrp[1]] + 1) >> 1;
yMin = (iRefPointChroma[tmpMinGrp[0]] + iRefPointChroma[tmpMinGrp[1]] + 1) >> 1;
//两个较大的点求平均,得到最大点
xMax = (iRefPointLuma[tmpMaxGrp[0]] + iRefPointLuma[tmpMaxGrp[1]] + 1) >> 1;
yMax = (iRefPointChroma[tmpMaxGrp[0]] + iRefPointChroma[tmpMaxGrp[1]] + 1) >> 1;
}
②如果上边存在,但是左边不存在,则只取上一行前两个采样点最为最大最小点
else if (bAboveAvaillable)
{
//如果只有上一行可用,则只从前两个点中找出最大和最小点
for (int k = 0; k < 2; k++)
{
if (iRefPointLuma[k] > xMax)
{
xMax = iRefPointLuma[k];
yMax = iRefPointChroma[k];
}
if (iRefPointLuma[k] < xMin)
{
xMin = iRefPointLuma[k];
yMin = iRefPointChroma[k];
}
}
}
③如果左边存在,但是上边不存在,则只取左一列前两个采样点最为最大最小点
else if (bLeftAvaillable)
{
//如果只有左一列可用,则只从后两个点中找出最大和最小点
for (int k = 2; k < 4; k++)
{
if (iRefPointLuma[k] > xMax)
{
xMax = iRefPointLuma[k];
yMax = iRefPointChroma[k];
}
if (iRefPointLuma[k] < xMin)
{
xMin = iRefPointLuma[k];
yMin = iRefPointChroma[k];
}
}
}
三、用最大最小点计算线性模型的参数a,b,shift:
这部分和VTM中的基本类似,基本是照搬过来的,我就直接放代码了
if (bLeftAvaillable || bAboveAvaillable)
{
*a = 0;
*iShift = 16;
int diff = xMax - xMin;
int add = 0;
int shift = 0;
if (diff > 64)
{
shift = (uiInternalBitDepth > 8) ? uiInternalBitDepth - 6 : 2;
add = shift ? 1 << (shift - 1) : 0;
diff = (diff + add) >> shift;
if (uiInternalBitDepth == 10)
{
assert(shift == 4 && add == 8); // for default 10bit
}
}
if (diff > 0)
{
*a = ((yMax - yMin) * g_aiTscpmDivTable64[diff - 1] + add) >> shift;
}
*b = yMin - (((*a) * xMin) >> (*iShift));
}
if (!bLeftAvaillable && !bAboveAvaillable)
{
*a = 0;
*b = 1 << (uiInternalBitDepth - 1);
*iShift = 0;
return;
}