transformskip和intra block copy, palatte mode 一样,是针对screen content 的编码工具,该工具的提出是因为对于screen content,其经常包含尖锐边缘或者跳变,相关性极差,这种情况下进行正常的transform反而会带来负面影响,还不如跳过变换环节,直接对残差进行量化。下面来直接看代码:
if (pcCU->getTransformSkip(uiAbsPartIdx, compID) != 0)//开启transformskip
{
xTransformSkip(pcResidual, uiStride, m_plTempCoeff, rTu, compID);
}
else. //执行正常的transform
{
const Int channelBitDepth = pcCU->getSlice()->getSPS()->getBitDepth(toChannelType(compID));
xT(channelBitDepth, rTu.useDST(compID), pcResidual, uiStride, m_plTempCoeff, uiWidth, uiHeight, pcCU->getSlice()->getSPS()->getMaxLog2TrDynamicRange(toChannelType(compID)));
}
#if DEBUG_TRANSFORM_AND_QUANTISE
std::cout << g_debugCounter << ": " << uiWidth << "x" << uiHeight << " channel " << compID << " TU between transform and quantiser\n";
printBlock(m_plTempCoeff, uiWidth, uiHeight, uiWidth);
#endif // if DEBUG_TRANSFORM_AND_QUANTISE
xQuant(rTu, m_plTempCoeff, rpcCoeff,
#if ADAPTIVE_QP_SELECTION
pcArlCoeff,
#endif // if ADAPTIVE_QP_SELECTION
uiAbsSum, compID, cQP);//执行量化
Void TComTrQuant::xTransformSkip(Pel *piBlkResi, UInt uiStride, TCoeff *psCoeff, TComTU &rTu, const ComponentID component)
{
const TComRectangle &rect = rTu.getRect(component);
const Int width = rect.width;
const Int height = rect.height;
const Int maxLog2TrDynamicRange = rTu.getCU()->getSlice()->getSPS()->getMaxLog2TrDynamicRange(toChannelType(component));
const Int channelBitDepth = rTu.getCU()->getSlice()->getSPS()->getBitDepth(toChannelType(component));
Int iTransformShift = getTransformShift(channelBitDepth, rTu.GetEquivalentLog2TrSize(component), maxLog2TrDynamicRange);
//rTu.GetEquivalentLog2TrSize(component) always is 2.
if (rTu.getCU()->getSlice()->getSPS()->getSpsRangeExtension().getExtendedPrecisionProcessingFlag())
{
iTransformShift = std::max<Int>(0, iTransformShift);//这个iTransformShift是因为在编码器里变换和量化融合在一起,量化步骤包含了变换步骤中乘以1/128的部分,所以在这里要先乘上128
}
const Bool rotateResidual = rTu.isNonTransformedResidualRotated(component);//对残差进行旋转,因为对于intra prediction来说,越远预测的越差,残差越大
const UInt uiSizeMinus1 = (width * height) - 1;
if (iTransformShift >= 0)
{
for (UInt y = 0, coefficientIndex = 0; y < height; y++)
{
for (UInt x = 0; x < width; x++, coefficientIndex++)
{
psCoeff[rotateResidual ? (uiSizeMinus1 - coefficientIndex) : coefficientIndex] = TCoeff(piBlkResi[(y * uiStride) + x]) << iTransformShift;
}
}
}
else // for very high bit depths
{
iTransformShift = -iTransformShift;
const TCoeff offset = 1 << (iTransformShift - 1);
for (UInt y = 0, coefficientIndex = 0; y < height; y++)
{
for (UInt x = 0; x < width; x++, coefficientIndex++)
{
psCoeff[rotateResidual ? (uiSizeMinus1 - coefficientIndex) : coefficientIndex] = (TCoeff(piBlkResi[(y * uiStride) + x]) + offset) >> iTransformShift;
}
}
}
}
在h265中,对于每个CU来说是否开启transformskip完全有RDO来选择;
for (Int modeId = firstCheckId; modeId < 2; modeId++)//第一次TransformSkip为false,第二次TransformSkip为true,通过rdcost来选择是否用TransformSkip;
{
DEBUG_STRING_NEW(sModeString)
Int default0Save1Load2 = 0;
singleDistTmpLuma = 0;
if (modeId == firstCheckId)
{
default0Save1Load2 = 1;
}
else
{
default0Save1Load2 = 2;
}
pcCU->setTransformSkipSubParts(modeId, COMPONENT_Y, uiAbsPartIdx, totalAdjustedDepthChan);//把modeId赋值给m_puhTransformSkip,会导致在xIntraCodingTUBlock里使用xTransformskip 或者xT;
xIntraCodingTUBlock(pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaSingle, false, singleDistTmpLuma, COMPONENT_Y, rTu DEBUG_STRING_PASS_INTO(sModeString), default0Save1Load2);
......
}