今天讲一下目前VTM6.0版本中的CIIP技术,CIIP即为帧内帧间联合预测技术,这属于Merge系列的一个分支。
该技术需要先计算当前预测块的帧内预测值,即用Planar、DC、角度预测等传统的帧内预测模式去预测当前块的帧内预测值存储下来。然后再利用帧间的预测方式去预测当前块的帧间预测值,最后将帧内帧间的各自预测值通过某种加权方式加权得到最终当前块的预测值。
该技术对亮度块和色度块都要做,不过在目前的版本中,亮度帧内和色度帧内都要只用Planar模式。帧间模式就是用Merge列表中最优的那个MV去预测。下面是代码中帧内预测模式选择的地方,pu.intraDir[0] 里面存的是亮度分量的帧内预测模式为Planar,pu.intraDir[1] 里面存的是色度分量的帧内预测模式DM,这里的DM实际上会从色度块对应的亮度块去获取其亮度块的帧内模式,因此获取到的帧内模式也为Planar。
if (uiNoResidualPass == 0 && RdModeList[uiMrgHADIdx].isCIIP)
{
cu.mmvdSkip = false;
mergeCtx.setMergeInfo(pu, uiMergeCand);
pu.mhIntraFlag = true;
pu.regularMergeFlag = false;
pu.intraDir[0] = PLANAR_IDX;//帧内亮度模式采用Planar模式
CHECK(pu.intraDir[0]<0 || pu.intraDir[0]>(NUM_LUMA_MODE - 1), "out of intra mode");
pu.intraDir[1] = DM_CHROMA_IDX;//帧内色度模式采用DM模式
}
当获取到当前编码块的帧内预测值,以及对应的帧间预测值后,就需要进一步的加权,加权系数有两个,一个是帧内加权系数Wintra,另一个是帧间加权系数Winter,加权的方式如下所示:
如果上块和左边的块都是帧内模式:则(Wintra,Winter)=(3,1);
如果上块和左边的块都是帧间模式:则(Wintra,Winter)=(1,3);
如果一个是帧内模式一个是帧间模式:则(Wintra,Winter)=(2,2);
预测的具体公式为:
Pred= (wMerge * Pinter+ wIntra * Pintra + 2) >> 2;
具体在代码中的加权函数如下:
//CIIP模式的加权函数,对帧内预测值和帧间预测值进行三种权值的加权
void IntraPrediction::geneWeightedPred(const ComponentID compId, PelBuf &pred, const PredictionUnit &pu, Pel *srcBuf)
{
const int width = pred.width;
const int height = pred.height;
const int srcStride = width;
const int dstStride = pred.stride;
Pel* dstBuf = pred.buf;
int wIntra, wMerge;//帧内帧间的权重
const Position posBL = pu.Y().bottomLeft();//左下角的块
const Position posTR = pu.Y().topRight();//右上角的块
const PredictionUnit *neigh0 = pu.cs->getPURestricted(posBL.offset(-1, 0), pu, CHANNEL_TYPE_LUMA);//左下角的块设置为邻居0
const PredictionUnit *neigh1 = pu.cs->getPURestricted(posTR.offset(0, -1), pu, CHANNEL_TYPE_LUMA);//右下角的块设置为邻居1
bool isNeigh0Intra = neigh0 && (CU::isIntra(*neigh0->cu));//邻居0是否为帧内模式
bool isNeigh1Intra = neigh1 && (CU::isIntra(*neigh1->cu));//邻居1是否为帧内模式
if (isNeigh0Intra && isNeigh1Intra)
{
wIntra = 3; wMerge = 1;
}
else
{
if (!isNeigh0Intra && !isNeigh1Intra)
{
wIntra = 1; wMerge = 3;
}
else
{
wIntra = 2; wMerge = 2;
}
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
//邻居0是否为帧内模式这里计算最终的加权预测值
dstBuf[y*dstStride + x] = (wMerge * dstBuf[y*dstStride + x] + wIntra * srcBuf[y*srcStride + x] + 2) >> 2;
}
}
}