在之前的博客详细讲了一下目前VVC中的TPM技术的详细原理,其中涉及到了三角预测模式中对角线周围加权区域的自适应权重加权过程,链接为: H.266/VVC相关技术学习笔记:帧间预测中的TPM技术(Triangle partition Mode) 本篇博客将加权融合过程的函数xWeightedTriangleBlk()的代码附上,本人加了基本的注释,如果有错误或者有疑问的地方可以私信我。
//三角预测模式真正的加权过程在该函数中
void InterpolationFilter::xWeightedTriangleBlk( const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1 )
{
Pel* dst = predDst .get(compIdx).buf;//加权区的加权预测值
Pel* src0 = predSrc0.get(compIdx).buf;//分区一对应颜色分量的单向预测值
Pel* src1 = predSrc1.get(compIdx).buf;//分区二对应颜色分量的单向预测值
int32_t strideDst = predDst .get(compIdx).stride - width;//加权预测区的跨度
int32_t strideSrc0 = predSrc0.get(compIdx).stride - width;//分区一的跨度
int32_t strideSrc1 = predSrc1.get(compIdx).stride - width;//分区二的跨度
const char log2WeightBase = 3;//权重基的log2设置为3,权重基为8,就是除以8,这和三角权重表中一致,权重表中的权重基本都为8分之几
const ClpRng clipRng = pu.cu->slice->clpRngs().comp[compIdx];
const int32_t clipbd = clipRng.bd;
const int32_t shiftDefault = std::max<int>(2, (IF_INTERNAL_PREC - clipbd));//默认移位值
const int32_t offsetDefault = (1<<(shiftDefault-1)) + IF_INTERNAL_OFFS;//默认偏移值
const int32_t shiftWeighted = std::max<int>(2, (IF_INTERNAL_PREC - clipbd)) + log2WeightBase;//加权移位值
const int32_t offsetWeighted = (1 << (shiftWeighted - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);//加权偏移值
const int32_t ratioWH = (width > height) ? (width / height) : 1;//PU宽高比
const int32_t ratioHW = (width > height) ? 1 : (height / width);//PU高宽比
const bool longWeight = (compIdx == COMPONENT_Y);//是否是长权重,即亮度分量的预测权重会设置大一些,色度的话会小一些
const int32_t weightedLength = longWeight ? 7 : 3;//如果是亮度分量,设置为7,即7/8
int32_t weightedStartPos = ( splitDir == 0 ) ? ( 0 - (weightedLength >> 1) * ratioWH ) : ( width - ((weightedLength + 1) >> 1) * ratioWH );//加权的开始位置
int32_t weightedEndPos = weightedStartPos + weightedLength * ratioWH - 1;//加权的结束位置。
int32_t weightedPosoffset = ( splitDir == 0 ) ? ratioWH : -ratioWH;//加权位置的偏置。
Pel tmpPelWeighted;//临时的像素加权值
int32_t weightIdx;//权重索引
int32_t x, y, tmpX, tmpY, tmpWeightedStart, tmpWeightedEnd;
//X ,Y分量,以及X、Y分量的缓存,加权开始的缓存以及加权结束的缓存
for( y = 0; y < height; y+= ratioHW )//逐行遍历,然后对逐行上逐个像素点进行遍历
{
for( tmpY = ratioHW; tmpY > 0; tmpY-- )
{
for( x = 0; x < weightedStartPos; x++ )//从行上的0位置到加权开始的位置,逐像素遍历,将非加权区的预测值直接嵌位后保存到最终预测值的缓存predDst中
{
*dst++ = ClipPel( rightShift( (splitDir == 0 ? *src1 : *src0) + offsetDefault, shiftDefault), clipRng );//得到各自分区的预测值(未加权)
src0++;
src1++;
}
tmpWeightedStart = std::max((int32_t)0, weightedStartPos);
tmpWeightedEnd = std::min(weightedEndPos, (int32_t)(width - 1));
weightIdx = 1;
if( weightedStartPos < 0 )
{
weightIdx += abs(weightedStartPos) / ratioWH;
}
for( x = tmpWeightedStart; x <= tmpWeightedEnd; x+= ratioWH )//从行上的加权开始位置到该行加权结束位置,逐像素遍历,利用两个分区的预测值加权计算加权区的预测值,嵌位后保存到predDst中
{
for( tmpX = ratioWH; tmpX > 0; tmpX-- )
{
tmpPelWeighted = Clip3( 1, 7, longWeight ? weightIdx : (weightIdx * 2));//根据离分割线的距离得到不同的加权权重
tmpPelWeighted = splitDir ? ( 8 - tmpPelWeighted ) : tmpPelWeighted;
*dst++ = ClipPel( rightShift( (tmpPelWeighted*(*src0++) + ((8 - tmpPelWeighted) * (*src1++)) + offsetWeighted), shiftWeighted ), clipRng );
}
weightIdx ++;
}
for( x = weightedEndPos + 1; x < width; x++ )//从行上的加权结束位置到该行末尾的位置,逐像素遍历,将非加权区的预测值直接嵌位后保存到最终预测值的缓存predDst中
{
*dst++ = ClipPel( rightShift( (splitDir == 0 ? *src0 : *src1) + offsetDefault, shiftDefault ), clipRng );
src0++;
src1++;
}
dst += strideDst;
src0 += strideSrc0;
src1 += strideSrc1;
}
weightedStartPos += weightedPosoffset;
weightedEndPos += weightedPosoffset;
}
}