diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index 186dc7925e7fe2b371e75e3a654dfadf08966154..7fb8e912451565566a46ba98e4ebc1a00826bef4 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -804,6 +804,18 @@ static const int ADAPTIVE_SUB_GROUP_SIZE_MMVD = MMVD_MAX_REFINE_NUM; static const int ADAPTIVE_SUB_GROUP_SIZE_MMVD_AFF = AF_MMVD_MAX_REFINE_NUM; #endif +#if JVET_AA0057_CCCM +static const int CCCM_WINDOW_SIZE = 6; +static const int CCCM_NUM_PARAMS = 7; +static const int CCCM_MIN_PU_SIZE = 0; // Set to 0 for no size restriction +static const int CCCM_REF_LINES_ABOVE_CTU = 0; // Number of chroma lines allowed to be included in the reference area above the CTU (0: no restrictions) +static const int CCCM_FILTER_PADDING = 1; // E.g. 3x3 filter needs one padded sample +static const int CCCM_MAX_REF_SAMPLES = ( 2 * CCCM_WINDOW_SIZE * ( 2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE ) ); +static const int CCCM_MATRIX_BITS = 28; +static const int CCCM_DECIM_BITS = 22; +static const int CCCM_DECIM_ROUND = ( 1 << (CCCM_DECIM_BITS - 1 ) ); +#endif + #if JVET_Y0152_TT_ENC_SPEEDUP static constexpr int FAST_METHOD_TT_ENC_SPEEDUP = 0x0001; ///< Embedding flag, which, if false, de-activates all the following ABT_ENC_SPEEDUP_* modes static constexpr int FAST_METHOD_HOR_XOR_VER = 0x0002; diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp index c89bd1d3398889ff2c0399aee1d15c94b3e7ca6c..705198c0beea465dadf9cad4e114382a6930fa63 100644 --- a/source/Lib/CommonLib/Contexts.cpp +++ b/source/Lib/CommonLib/Contexts.cpp @@ -2832,6 +2832,23 @@ const CtxSet ContextSetCfg::CclmDeltaFlags = ContextSetCfg::addCtxSet }); #endif +#if JVET_AA0057_CCCM +const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet +({ + { CNU, }, + { CNU, }, + { CNU, }, + { DWS, }, + { DWS, }, + { DWS, }, + { DWE, }, + { DWE, }, + { DWE, }, + { DWO, }, + { DWO, }, +}); +#endif + #elif SLICE_TYPE_WIN_SIZE const CtxSet ContextSetCfg::SplitFlag = ContextSetCfg::addCtxSet ({ @@ -4335,6 +4352,18 @@ const CtxSet ContextSetCfg::CclmDeltaFlags = ContextSetCfg::addCtxSet }); #endif +#if JVET_AA0057_CCCM +const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet +({ + { CNU, }, + { CNU, }, + { CNU, }, + { DWS, }, + { DWS, }, + { DWS, }, +}); +#endif + #else const CtxSet ContextSetCfg::SplitFlag = ContextSetCfg::addCtxSet ({ @@ -5406,6 +5435,16 @@ const CtxSet ContextSetCfg::CclmDeltaFlags = ContextSetCfg::addCtxSet }); #endif +#if JVET_AA0057_CCCM +const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet +({ + { CNU, }, + { CNU, }, + { CNU, }, + { DWS, }, +}); +#endif + #endif // clang-format on diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h index 7e0fa7fad7f1898a77e6c24bacbbebe6407421d1..fe9c0360a79efb5a162089bb4dd11bdb1a6af5d3 100644 --- a/source/Lib/CommonLib/Contexts.h +++ b/source/Lib/CommonLib/Contexts.h @@ -493,6 +493,9 @@ public: #endif #if JVET_Z0050_CCLM_SLOPE static const CtxSet CclmDeltaFlags; +#endif +#if JVET_AA0057_CCCM + static const CtxSet CccmFlag; #endif static const unsigned NumberOfContexts; diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp index 30e5663a59a5da08a1f42dca11793ed0b07d0178..f8e0a663193035b457196dffb6d478e3cdfc1432 100644 --- a/source/Lib/CommonLib/IntraPrediction.cpp +++ b/source/Lib/CommonLib/IntraPrediction.cpp @@ -112,6 +112,9 @@ IntraPrediction::IntraPrediction() #if JVET_V0130_INTRA_TMP m_pppTarPatch = NULL; #endif +#if JVET_AA0057_CCCM + m_cccmLumaBuf = nullptr; +#endif } IntraPrediction::~IntraPrediction() @@ -170,6 +173,11 @@ void IntraPrediction::destroy() m_pppTarPatch = NULL; } #endif + +#if JVET_AA0057_CCCM + delete[] m_cccmLumaBuf; + m_cccmLumaBuf = nullptr; +#endif } void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepthY) @@ -268,6 +276,13 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth m_calcTemplateDiff = calcTemplateDiff; #endif +#if JVET_AA0057_CCCM + if (m_cccmLumaBuf == nullptr) + { + m_cccmLumaBuf = new Pel[(2*MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2*CCCM_FILTER_PADDING) * (2*MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2*CCCM_FILTER_PADDING)]; + } +#endif + #if ENABLE_SIMD_TMP #ifdef TARGET_SIMD_X86 initIntraX86(); @@ -4731,6 +4746,14 @@ int isBelowLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const // LumaRecPixels void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea) { +#if JVET_AA0057_CCCM + if ( pu.cccmFlag ) + { + xCccmCreateLumaRef(pu); + return; + } +#endif + int iDstStride = 0; Pel* pDst0 = 0; int curChromaMode = pu.intraDir[1]; @@ -6769,4 +6792,685 @@ int IntraPrediction::calcTemplateDiff( Pel* ref, unsigned int uiStride, Pel** ta } #endif +#if JVET_AA0057_CCCM +void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, PelBuf &predCr, int intraDir ) +{ + if ( pu.cccmFlag ) + { + CccmModel cccmModelCb( pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) ); + CccmModel cccmModelCr( pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) ); + + if ( PU::cccmSingleModeAvail(pu, intraDir) ) + { + xCccmCalcModels(pu, cccmModelCb, cccmModelCr, 0, 0); + xCccmApplyModel(pu, COMPONENT_Cb, cccmModelCb, 0, 0, predCb); + xCccmApplyModel(pu, COMPONENT_Cr, cccmModelCr, 0, 0, predCr); + } + else + { + // Multimode case + int modelThr = xCccmCalcRefAver(pu); + + xCccmCalcModels(pu, cccmModelCb, cccmModelCr, 1, modelThr); + xCccmApplyModel(pu, COMPONENT_Cb, cccmModelCb, 1, modelThr, predCb); + xCccmApplyModel(pu, COMPONENT_Cr, cccmModelCr, 1, modelThr, predCr); + + xCccmCalcModels(pu, cccmModelCb, cccmModelCr, 2, modelThr); + xCccmApplyModel(pu, COMPONENT_Cb, cccmModelCb, 2, modelThr, predCb); + xCccmApplyModel(pu, COMPONENT_Cr, cccmModelCr, 2, modelThr, predCr); + } + } +} + +void IntraPrediction::xCccmApplyModel(const PredictionUnit& pu, const ComponentID compId, CccmModel &cccmModel, int modelId, int modelThr, PelBuf &piPred) const +{ + const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId)); + static Pel samples[CCCM_NUM_PARAMS]; + + CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu); + + for (int y = 0; y < refLumaBlk.height; y++) + { + for (int x = 0; x < refLumaBlk.width; x++) + { + if ( modelId == 1 && refLumaBlk.at( x, y ) > modelThr ) // Model 1: Include only samples below or equal to the threshold + { + continue; + } + if ( modelId == 2 && refLumaBlk.at( x, y ) <= modelThr) // Model 2: Include only samples above the threshold + { + continue; + } + + // 7-tap cross + samples[0] = refLumaBlk.at( x , y ); // C + samples[1] = refLumaBlk.at( x , y-1 ); // N + samples[2] = refLumaBlk.at( x , y+1 ); // S + samples[3] = refLumaBlk.at( x-1, y ); // W + samples[4] = refLumaBlk.at( x+1, y ); // E + samples[5] = cccmModel.nonlinear( refLumaBlk.at( x, y) ); + samples[6] = cccmModel.bias(); + + piPred.at(x, y) = ClipPel<Pel>( cccmModel.convolve(samples, CCCM_NUM_PARAMS), clpRng ); + } + } +} + +void IntraPrediction::xCccmCalcModels(const PredictionUnit& pu, CccmModel &cccmModelCb, CccmModel &cccmModelCr, int modelId, int modelThr) const +{ + int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY; + + const CPelBuf recoCb = pu.cs->picture->getRecoBuf(COMPONENT_Cb); + const CPelBuf recoCr = pu.cs->picture->getRecoBuf(COMPONENT_Cr); + PelBuf refLuma = xCccmGetLumaRefBuf(pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY); + + int M = CCCM_NUM_PARAMS; + + int sampleNum = areaWidth * areaHeight - pu.blocks[COMPONENT_Cb].width * pu.blocks[COMPONENT_Cb].height; + int sampleInd = 0; + + // Collect reference data to input matrix A and target vector Y + static Pel A[CCCM_NUM_PARAMS][CCCM_MAX_REF_SAMPLES]; + static Pel YCb[CCCM_MAX_REF_SAMPLES]; + static Pel YCr[CCCM_MAX_REF_SAMPLES]; + + for (int y = 0; y < areaHeight; y++) + { + for (int x = 0; x < areaWidth; x++) + { + if ( x >= refSizeX && y >= refSizeY ) + { + continue; + } + + if ( modelId == 1 && refLuma.at( x, y ) > modelThr ) // Model 1: Include only samples below or equal to the threshold + { + continue; + } + if ( modelId == 2 && refLuma.at( x, y ) <= modelThr) // Model 2: Include only samples above the threshold + { + continue; + } + + // 7-tap cross + A[0][sampleInd] = refLuma.at( x , y ); // C + A[1][sampleInd] = refLuma.at( x , y-1 ); // N + A[2][sampleInd] = refLuma.at( x , y+1 ); // S + A[3][sampleInd] = refLuma.at( x-1, y ); // W + A[4][sampleInd] = refLuma.at( x+1, y ); // E + A[5][sampleInd] = cccmModelCb.nonlinear( refLuma.at( x, y) ); + A[6][sampleInd] = cccmModelCb.bias(); + + YCb[sampleInd] = recoCb.at(refPosPicX + x, refPosPicY + y); + YCr[sampleInd++] = recoCr.at(refPosPicX + x, refPosPicY + y); + } + } + + if ( sampleInd == 0 ) // Number of samples can go to zero in the multimode case + { + cccmModelCb.clearModel(M); + cccmModelCr.clearModel(M); + return; + } + else + { + sampleNum = sampleInd; + } + + // Calculate autocorrelation matrix and cross-correlation vector + static CccmCovarianceInt::TE ATA; + static CccmCovarianceInt::Ty ATYCb; + static CccmCovarianceInt::Ty ATYCr; + + memset(ATA , 0x00, sizeof(TCccmCoeff) * CCCM_NUM_PARAMS * CCCM_NUM_PARAMS); + memset(ATYCb, 0x00, sizeof(TCccmCoeff) * CCCM_NUM_PARAMS); + memset(ATYCr, 0x00, sizeof(TCccmCoeff) * CCCM_NUM_PARAMS); + + for (int coli0 = 0; coli0 < M; coli0++) + { + for (int coli1 = coli0; coli1 < M; coli1++) + { + Pel *col0 = A[coli0]; + Pel *col1 = A[coli1]; + + for (int rowi = 0; rowi < sampleNum; rowi++) + { + ATA[coli0][coli1] += col0[rowi] * col1[rowi]; + } + } + } + + for (int coli = 0; coli < M; coli++) + { + Pel *col = A[coli]; + + for (int rowi = 0; rowi < sampleNum; rowi++) + { + ATYCb[coli] += col[rowi] * YCb[rowi]; + ATYCr[coli] += col[rowi] * YCr[rowi]; + } + } + + // Scale the matrix and vector to selected dynamic range + int matrixShift = CCCM_MATRIX_BITS - 2 * pu.cu->cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA) - ceilLog2(sampleNum); + + if ( matrixShift > 0 ) + { + for (int coli0 = 0; coli0 < M; coli0++) + { + for (int coli1 = coli0; coli1 < M; coli1++) + { + ATA[coli0][coli1] <<= matrixShift; + } + } + + for (int coli = 0; coli < M; coli++) + { + ATYCb[coli] <<= matrixShift; + } + + for (int coli = 0; coli < M; coli++) + { + ATYCr[coli] <<= matrixShift; + } + } + else if ( matrixShift < 0 ) + { + matrixShift = -matrixShift; + + for (int coli0 = 0; coli0 < M; coli0++) + { + for (int coli1 = coli0; coli1 < M; coli1++) + { + ATA[coli0][coli1] >>= matrixShift; + } + } + + for (int coli = 0; coli < M; coli++) + { + ATYCb[coli] >>= matrixShift; + } + + for (int coli = 0; coli < M; coli++) + { + ATYCr[coli] >>= matrixShift; + } + } + + // Solve the filter coefficients using LDL decomposition + CccmCovarianceInt cccmSolver; + CccmCovarianceInt::TE U; // Upper triangular L' of ATA's LDL decomposition + CccmCovarianceInt::Ty diag; // Diagonal of D + + bool decompOk = cccmSolver.ldlDecompose(ATA, U, diag, M); + + cccmSolver.ldlSolve(U, diag, ATYCb, cccmModelCb.params, M, decompOk); + cccmSolver.ldlSolve(U, diag, ATYCr, cccmModelCr.params, M, decompOk); +} + +// Calculate a single downsampled luma reference value (copied from IntraPrediction::xGetLumaRecPixels) +Pel IntraPrediction::xCccmGetLumaVal(const PredictionUnit& pu, const CPelBuf pi, const int x, const int y) const +{ + const Pel* piSrc = pi.buf; + const int iRecStride = pi.stride; + Pel ypval = 0; + if (pu.chromaFormat == CHROMA_444) + { + ypval = piSrc[x + iRecStride * y]; + } + else if (pu.chromaFormat == CHROMA_422) + { + int s = 2; + int offLeft = x > 0 ? -1 : 0; + s += piSrc[2 * x + iRecStride * y] * 2; + s += piSrc[2 * x + offLeft + iRecStride * y]; + s += piSrc[2 * x + 1 + iRecStride * y]; + ypval = s >> 2; + } + else if (pu.cs->sps->getCclmCollocatedChromaFlag()) + { + int s = 4; + int offLeft = x > 0 ? -1 : 0; + int offAbove = y > 0 ? -1 : 0; + s += piSrc[2 * x + iRecStride * 2 * y ] * 4; + s += piSrc[2 * x + offLeft + iRecStride * 2 * y ]; + s += piSrc[2 * x + 1 + iRecStride * 2 * y ]; + s += piSrc[2 * x + iRecStride * (2 * y + 1) ]; + s += piSrc[2 * x + iRecStride * (2 * y + offAbove)]; + ypval = s >> 3; + } + else + { + int s = 4; + int offLeft = x > 0 ? -1 : 0; + s += piSrc[2 * x + iRecStride * y * 2 ] * 2; + s += piSrc[2 * x + offLeft + iRecStride * y * 2 ]; + s += piSrc[2 * x + 1 + iRecStride * y * 2 ]; + s += piSrc[2 * x + iRecStride * (y * 2 + 1) ] * 2; + s += piSrc[2 * x + offLeft + iRecStride * (y * 2 + 1) ]; + s += piSrc[2 * x + 1 + iRecStride * (y * 2 + 1) ]; + ypval = s >> 3; + } + return ypval; +} + +// Using the same availability checking as in IntraPrediction::xGetLumaRecPixels +void IntraPrediction::xCccmCalcRefArea(const PredictionUnit& pu) +{ + CompArea chromaArea = pu.Cb(); + CompArea lumaArea = CompArea( COMPONENT_Y, pu.chromaFormat, chromaArea.lumaPos(), recalcSize( pu.chromaFormat, CHANNEL_TYPE_CHROMA, CHANNEL_TYPE_LUMA, chromaArea.size() ) );//needed for correct pos/size (4x4 Tus) + + const SizeType uiCWidth = chromaArea.width; + const SizeType uiCHeight = chromaArea.height; + + const CodingUnit& lumaCU = isChroma( pu.chType ) ? *pu.cs->picture->cs->getCU( lumaArea.pos(), CH_L ) : *pu.cu; + const CodingUnit& cu = *pu.cu; + const CompArea& area = isChroma( pu.chType ) ? chromaArea : lumaArea; + + const uint32_t uiTuWidth = area.width; + const uint32_t uiTuHeight = area.height; + + int iBaseUnitSize = ( 1 << MIN_CU_LOG2 ); + + const int iUnitWidth = iBaseUnitSize >> getComponentScaleX(area.compID, area.chromaFormat ); + const int iUnitHeight = iBaseUnitSize >> getComponentScaleY(area.compID, area.chromaFormat); + + const int iTUWidthInUnits = uiTuWidth / iUnitWidth; + const int iTUHeightInUnits = uiTuHeight / iUnitHeight; + const int iAboveUnits = iTUWidthInUnits; + const int iLeftUnits = iTUHeightInUnits; + const int chromaUnitWidth = iBaseUnitSize >> getComponentScaleX(COMPONENT_Cb, area.chromaFormat); + const int chromaUnitHeight = iBaseUnitSize >> getComponentScaleY(COMPONENT_Cb, area.chromaFormat); + const int topTemplateSampNum = 2 * uiCWidth; + const int leftTemplateSampNum = 2 * uiCHeight; + const int totalAboveUnits = (topTemplateSampNum + (chromaUnitWidth - 1)) / chromaUnitWidth; + const int totalLeftUnits = (leftTemplateSampNum + (chromaUnitHeight - 1)) / chromaUnitHeight; + const int totalUnits = totalLeftUnits + totalAboveUnits + 1; + const int aboveRightUnits = totalAboveUnits - iAboveUnits; + const int leftBelowUnits = totalLeftUnits - iLeftUnits; + + int avaiAboveRightUnits = 0; + int avaiLeftBelowUnits = 0; + bool bNeighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1]; + memset(bNeighborFlags, 0, totalUnits); + bool aboveIsAvailable, leftIsAvailable; + + int availlableUnit = isLeftAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.pos(), + iLeftUnits, iUnitHeight, (bNeighborFlags + iLeftUnits + leftBelowUnits - 1)); + + leftIsAvailable = availlableUnit == iTUHeightInUnits; + + availlableUnit = isAboveAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.pos(), + iAboveUnits, iUnitWidth, (bNeighborFlags + iLeftUnits + leftBelowUnits + 1)); + + aboveIsAvailable = availlableUnit == iTUWidthInUnits; + + if (leftIsAvailable) // if left is not available, then the below left is not available + { + avaiLeftBelowUnits = isBelowLeftAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.bottomLeftComp(area.compID), leftBelowUnits, iUnitHeight, (bNeighborFlags + leftBelowUnits - 1)); + } + + if (aboveIsAvailable) // if above is not available, then the above right is not available. + { + avaiAboveRightUnits = isAboveRightAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.topRightComp(area.compID), aboveRightUnits, iUnitWidth, (bNeighborFlags + iLeftUnits + leftBelowUnits + iAboveUnits + 1)); + } + + int columnsLeft, rowsAbove; + + PU::getCccmRefLineNum(pu, columnsLeft, rowsAbove); // Reference lines available left and above + + int refWidth = pu.blocks[COMPONENT_Cb].width + columnsLeft; // Reference buffer size excluding paddings + int refHeight = pu.blocks[COMPONENT_Cb].height + rowsAbove; + + int extWidth = avaiAboveRightUnits * chromaUnitWidth; + int extHeight = avaiLeftBelowUnits * chromaUnitHeight; + + refWidth += rowsAbove ? extWidth : 0; // Add above right if above is available + refHeight += columnsLeft ? extHeight : 0; // Add below left if left is available + + m_cccmRefArea = Area( columnsLeft, rowsAbove, refWidth, refHeight); // Position with respect to the PU +} + +// Return downsampled luma buffer that contains PU and the reference areas above and left of the PU +PelBuf IntraPrediction::xCccmGetLumaRefBuf(const PredictionUnit& pu, int &areaWidth, int &areaHeight, int &refSizeX, int &refSizeY, int &refPosPicX, int &refPosPicY ) const +{ + refSizeX = m_cccmRefArea.x; // Reference lines available left and above + refSizeY = m_cccmRefArea.y; + areaWidth = m_cccmRefArea.width; // Reference buffer size excluding paddings + areaHeight = m_cccmRefArea.height; + refPosPicX = pu.blocks[COMPONENT_Cb].x - refSizeX; // Position of the reference area in picture coordinates + refPosPicY = pu.blocks[COMPONENT_Cb].y - refSizeY; + + int refStride = areaWidth + 2 * CCCM_FILTER_PADDING; // Including paddings required for the 2D filter + int refOrigin = refStride * CCCM_FILTER_PADDING + CCCM_FILTER_PADDING; + + return PelBuf(m_cccmLumaBuf + refOrigin, refStride, areaWidth, areaHeight); // Points to the top-left corner of the reference area +} + +// Return downsampled luma buffer for a PU +PelBuf IntraPrediction::xCccmGetLumaPuBuf(const PredictionUnit& pu) const +{ + int puWidth = pu.blocks[COMPONENT_Cb].width; + int puHeight = pu.blocks[COMPONENT_Cb].height; + + int refStride = m_cccmRefArea.width + 2 * CCCM_FILTER_PADDING; // Including paddings required for the 2D filter + int refOrigin = refStride * (m_cccmRefArea.y + CCCM_FILTER_PADDING) + m_cccmRefArea.x + CCCM_FILTER_PADDING; + + return PelBuf(m_cccmLumaBuf + refOrigin, refStride, puWidth, puHeight); // Points to the top-left corner of the block +} + +int IntraPrediction::xCccmCalcRefAver(const PredictionUnit& pu) const +{ + int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY; + + PelBuf refLuma = xCccmGetLumaRefBuf(pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY); + + int numSamples = 0; + int sumSamples = 0; + + // Top samples + for (int y = 0; y < refSizeY; y++) + { + for (int x = 0; x < areaWidth; x++) + { + sumSamples += refLuma.at(x, y); + numSamples++; + } + } + + // Left samples + for (int y = refSizeY; y < areaHeight; y++) + { + for (int x = 0; x < refSizeX; x++) + { + sumSamples += refLuma.at(x, y); + numSamples++; + } + } + + return numSamples == 0 ? 512 : ( sumSamples + numSamples/2) / numSamples; +} + +void IntraPrediction::xCccmCreateLumaRef(const PredictionUnit& pu) +{ + const CPelBuf recoLuma = pu.cs->picture->getRecoBuf(COMPONENT_Y); + const int maxPosPicX = pu.cs->picture->chromaSize().width - 1; + const int maxPosPicY = pu.cs->picture->chromaSize().height - 1; + + xCccmCalcRefArea(pu); // Find the reference area + + int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY; + + PelBuf refLuma = xCccmGetLumaRefBuf(pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY); + + int puBorderX = refSizeX + pu.blocks[COMPONENT_Cb].width; + int puBorderY = refSizeY + pu.blocks[COMPONENT_Cb].height; + + // Generate down-sampled luma for the area covering both the PU and the top/left reference areas (+ top and left paddings) + for (int y = -CCCM_FILTER_PADDING; y < areaHeight; y++) + { + for (int x = -CCCM_FILTER_PADDING; x < areaWidth; x++) + { + if (( x >= puBorderX && y >= refSizeY ) || + ( y >= puBorderY && x >= refSizeX )) + { + continue; + } + + int chromaPosPicX = refPosPicX + x; + int chromaPosPicY = refPosPicY + y; + + chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX; + chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY; + + refLuma.at( x, y ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, chromaPosPicY); + } + } + + CHECK( CCCM_FILTER_PADDING != 1, "Only padding with one sample implemented" ); + + // Pad right of top reference area + for (int y = -1; y < refSizeY; y++) + { + refLuma.at( areaWidth, y ) = refLuma.at( areaWidth - 1, y ); + } + + // Pad right of PU + for (int y = refSizeY; y < puBorderY; y++) + { + refLuma.at( puBorderX, y ) = refLuma.at( puBorderX - 1, y ); + } + + // Pad right of left reference area + for (int y = puBorderY; y < areaHeight; y++) + { + refLuma.at( refSizeX, y ) = refLuma.at( refSizeX - 1, y ); + } + + // Pad below left reference area + for (int x = -1; x < refSizeX + 1; x++) + { + refLuma.at( x, areaHeight ) = refLuma.at( x, areaHeight - 1 ); + } + + // Pad below PU + for (int x = refSizeX; x < puBorderX + 1; x++) + { + refLuma.at( x, puBorderY ) = refLuma.at( x, puBorderY - 1 ); + } + + // Pad below right reference area + for (int x = puBorderX + 1; x < areaWidth + 1; x++) + { + refLuma.at( x, refSizeY ) = refLuma.at( x, refSizeY - 1 ); + } + + // In dualtree we can also use luma from the right and below (if not on CTU/picture boundary) + if ( CS::isDualITree( *pu.cs ) ) + { + int ctuWidth = pu.cs->sps->getMaxCUWidth() >> getComponentScaleX(COMPONENT_Cb, pu.chromaFormat); + int ctuHeight = pu.cs->sps->getMaxCUHeight() >> getComponentScaleY(COMPONENT_Cb, pu.chromaFormat); + + // Samples right of top reference area + int padPosPicX = refPosPicX + areaWidth; + + if ( padPosPicX <= maxPosPicX && (padPosPicX % ctuWidth) ) + { + for (int y = -1; y < refSizeY; y++) + { + int chromaPosPicY = refPosPicY + y; + chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY; + + refLuma.at( areaWidth, y ) = xCccmGetLumaVal(pu, recoLuma, padPosPicX, chromaPosPicY); + } + } + + // Samples right of PU + padPosPicX = refPosPicX + puBorderX; + + if ( padPosPicX <= maxPosPicX && (padPosPicX % ctuWidth) ) + { + for (int y = refSizeY; y < puBorderY; y++) + { + int chromaPosPicY = refPosPicY + y; + chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY; + + refLuma.at( puBorderX, y ) = xCccmGetLumaVal(pu, recoLuma, padPosPicX, chromaPosPicY); + } + } + + // Samples right of left reference area + padPosPicX = refPosPicX + refSizeX; + + if ( padPosPicX <= maxPosPicX ) + { + for (int y = puBorderY; y < areaHeight; y++) + { + int chromaPosPicY = refPosPicY + y; + chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY; + + refLuma.at( refSizeX, y ) = xCccmGetLumaVal(pu, recoLuma, padPosPicX, chromaPosPicY); + } + } + + // Samples below left reference area + int padPosPicY = refPosPicY + areaHeight; + + if ( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) ) + { + for (int x = -1; x < refSizeX + 1; x++) + { + int chromaPosPicX = refPosPicX + x; + chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX; + + refLuma.at( x, areaHeight ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, padPosPicY); + } + } + + // Samples below PU + padPosPicY = refPosPicY + puBorderY; + + if ( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) ) + { + for (int x = refSizeX; x < puBorderX; x++) // Just go to PU border as the next sample may be out of CTU (and not needed anyways) + { + int chromaPosPicX = refPosPicX + x; + chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX; + + refLuma.at( x, puBorderY ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, padPosPicY); + } + } + + // Samples below right reference area + padPosPicY = refPosPicY + refSizeY; + + if ( padPosPicY <= maxPosPicY ) + { + // Avoid going outside of right CTU border where these samples are not yet available + int puPosPicX = pu.blocks[COMPONENT_Cb].x; + int ctuRightEdgeDist = ctuWidth - (puPosPicX % ctuWidth) + refSizeX; + int lastPosX = ctuRightEdgeDist < areaWidth ? ctuRightEdgeDist : areaWidth; + + for (int x = puBorderX + 1; x < lastPosX; x++) // Just go to ref area border as the next sample may be out of CTU (and not needed anyways) + { + int chromaPosPicX = refPosPicX + x; + chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX; + + refLuma.at( x, refSizeY ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, padPosPicY); + } + } + } +} + +// LDL decomposing A to U'*diag*U +bool CccmCovarianceInt::ldlDecomp(TE A, TE U, Ty diag, int numEq) const +{ + for (int i = 0; i < numEq; i++) + { + diag[i] = A[i][i]; + + for (int k = i - 1; k >= 0; k--) + { + TCccmCoeff tmp = FIXED_MULT(U[k][i], U[k][i]); + diag[i] -= FIXED_MULT(tmp, diag[k]); + } + + if ( diag[i] <= 0) // A is singular + { + return false; + } + + for (int j = i + 1; j < numEq; j++) + { + TCccmCoeff scale = A[i][j]; + + for (int k = i - 1; k >= 0; k--) + { + TCccmCoeff tmp = FIXED_MULT(U[k][j], U[k][i]); + scale -= FIXED_MULT(tmp, diag[k]); + } + + U[i][j] = FIXED_DIV(scale, diag[i]); + } + } + + return true; +} + +// Solve U'z = y for z +void CccmCovarianceInt::ldlTransposeBacksubstitution(TE U, TCccmCoeff* y, TCccmCoeff* z, int numEq) const +{ + z[0] = y[0]; + + for (int i = 1; i < numEq; i++) + { + TCccmCoeff sum = 0; + + for (int j = 0; j < i; j++) + { + sum += FIXED_MULT(z[j], U[j][i]); + } + + z[i] = y[i] - sum; + } +} + +// Solve Ux = z for x +void CccmCovarianceInt::ldlBacksubstitution(TE U, TCccmCoeff* z, TCccmCoeff* x, int numEq) const +{ + x[numEq - 1] = z[numEq - 1]; + + for (int i = numEq - 2; i >= 0; i--) + { + TCccmCoeff sum = 0; + + for (int j = i + 1; j < numEq; j++) + { + sum += FIXED_MULT(U[i][j], x[j]); + } + + x[i] = z[i] - sum; + } +} + +bool CccmCovarianceInt::ldlDecompose(TE A, TE U, Ty diag, int numEq) const +{ + // Compute upper triangular U and diagonal D such that U'*D*U = A + // (U being the tranpose of L in LDL decomposition: L*D*L' = A) + + // Regularize A to reduce singularities + for (int i = 0; i < numEq; i++) + { + A[i][i] += 1; + } + + return ldlDecomp(A, U, diag, numEq); +} + +void CccmCovarianceInt::ldlSolve(TE U, Ty diag, TCccmCoeff* y, TCccmCoeff* x, int numEq, bool decompOk) const +{ + if ( decompOk ) + { + // Now, the equation is U'*D*U*x = y, where U is upper triangular + // Solve U'*aux = y for aux + Ty aux; + + ldlTransposeBacksubstitution(U, y, aux, numEq); + + // The equation is now D*U*x = aux, remove diagonal by scaling + for (int i = 0; i < numEq; i++) + { + aux[i] = FIXED_DIV(aux[i], diag[i]); + } + + // The equation is now U*x = aux, solve it for x (filter coefficients) + ldlBacksubstitution(U, aux, x, numEq); + } + else // A was singular + { + std::memset(x, 0, sizeof(TCccmCoeff) * numEq); + } +} +#endif + //! \} diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h index 6be53e1da73bc1ed74e805282dfe19989b52d371..1dbf3b02ec2889e231e059907532328bb1c18a85 100644 --- a/source/Lib/CommonLib/IntraPrediction.h +++ b/source/Lib/CommonLib/IntraPrediction.h @@ -91,6 +91,68 @@ public: typedef short TrainDataType; #endif +#if JVET_AA0057_CCCM +typedef int64_t TCccmCoeff; + +#define FIXED_MULT(x, y) TCccmCoeff((int64_t(x)*(y) + CCCM_DECIM_ROUND) >> CCCM_DECIM_BITS ) +#define FIXED_DIV(x, y) TCccmCoeff((int64_t(x) << CCCM_DECIM_BITS ) / (y) ) + +struct CccmModel +{ + TCccmCoeff params[CCCM_NUM_PARAMS]; + int bd; + int midVal; + + CccmModel(int bitdepth) + { + bd = bitdepth; + midVal = (1 << ( bitdepth - 1)); + } + + void clearModel(int numParams) + { + for( int i = 0; i < numParams - 1; i++) + { + params[i] = 0; + } + + params[numParams - 1] = 1 << CCCM_DECIM_BITS; // Default bias to 1 + } + + Pel convolve(Pel* vector, int numParams) + { + TCccmCoeff sum = 0; + + for( int i = 0; i < numParams; i++) + { + sum += params[i] * vector[i]; + } + + return Pel( (sum + CCCM_DECIM_ROUND ) >> CCCM_DECIM_BITS ); + } + + Pel nonlinear(const Pel val) { return (val * val + midVal) >> bd; } + Pel bias () { return midVal; } +}; + +struct CccmCovarianceInt +{ + using TE = TCccmCoeff[CCCM_NUM_PARAMS][CCCM_NUM_PARAMS]; + using Ty = TCccmCoeff[CCCM_NUM_PARAMS]; + + CccmCovarianceInt() {} + ~CccmCovarianceInt() {} + + bool ldlDecompose (TE A, TE U, Ty diag, int numEq) const; + void ldlSolve (TE U, Ty diag, TCccmCoeff* y, TCccmCoeff* x, int numEq, bool decompOk) const; + +private: + void ldlBacksubstitution (TE U, TCccmCoeff* z, TCccmCoeff* x, int numEq) const; + void ldlTransposeBacksubstitution(TE U, TCccmCoeff* y, TCccmCoeff* z, int numEq) const; + bool ldlDecomp (TE A, TE U, Ty outDiag, int numEq) const; +}; +#endif + class IntraPrediction { #if MMLM @@ -108,6 +170,11 @@ private: int m_yuvExtSize2; #endif +#if JVET_AA0057_CCCM + Area m_cccmRefArea; + Pel* m_cccmLumaBuf; +#endif + static const uint8_t m_aucIntraFilter[MAX_INTRA_FILTER_DEPTHS]; #if JVET_W0123_TIMD_FUSION static const uint8_t m_aucIntraFilterExt[MAX_INTRA_FILTER_DEPTHS]; @@ -234,6 +301,18 @@ public: virtual ~IntraPrediction(); void init (ChromaFormat chromaFormatIDC, const unsigned bitDepthY); + +#if JVET_AA0057_CCCM + void predIntraCCCM (const PredictionUnit& pu, PelBuf &predCb, PelBuf &predCr, int intraDir); + void xCccmCalcModels (const PredictionUnit& pu, CccmModel &cccmModelCb, CccmModel &cccmModelCr, int modelId, int modelThr) const; + void xCccmApplyModel (const PredictionUnit& pu, const ComponentID compId, CccmModel &cccmModel, int modelId, int modelThr, PelBuf &piPred) const; + void xCccmCreateLumaRef (const PredictionUnit& pu); + PelBuf xCccmGetLumaRefBuf (const PredictionUnit& pu, int &areaWidth, int &areaHeight, int &refSizeX, int &refSizeY, int &refPosPicX, int &refPosPicY) const; + PelBuf xCccmGetLumaPuBuf (const PredictionUnit& pu) const; + Pel xCccmGetLumaVal (const PredictionUnit& pu, const CPelBuf pi, const int x, const int y) const; + int xCccmCalcRefAver (const PredictionUnit& pu) const; + void xCccmCalcRefArea (const PredictionUnit& pu); +#endif #if ENABLE_DIMD static void deriveDimdMode (const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu); #if JVET_Z0050_DIMD_CHROMA_FUSION && ENABLE_DIMD diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 35de675264b2ff49e8e5f2c9cfddbda38e0c21a1..67201d86365cf5d7001b502058153ac20ee4303e 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -131,6 +131,7 @@ #define JVET_Y0116_EXTENDED_MRL_LIST 1 // JVET-Y0116: Extended MRL Candidate List #define JVET_Z0050_DIMD_CHROMA_FUSION 1 // JVET-Z0050: DIMD chroma mode and fusion of chroma intra prediction modes #define JVET_Z0050_CCLM_SLOPE 1 // JVET-Z0050: CCLM with slope adjustments +#define JVET_AA0057_CCCM 1 // JVET-AA0057: Convolutional cross-component model (CCCM) //IBC diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp index 58f16b619ea36964dde84615022fb11a30b47171..4b17a450b2f8462a2e1d0f4fc699455670f71cfa 100644 --- a/source/Lib/CommonLib/Unit.cpp +++ b/source/Lib/CommonLib/Unit.cpp @@ -647,6 +647,9 @@ void PredictionUnit::initData() #endif #if JVET_Z0050_CCLM_SLOPE cclmOffsets = {}; +#endif +#if JVET_AA0057_CCCM + cccmFlag = 0; #endif // inter data mergeFlag = false; @@ -765,6 +768,9 @@ PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData) #endif #if JVET_Z0050_CCLM_SLOPE cclmOffsets = predData.cclmOffsets; +#endif +#if JVET_AA0057_CCCM + cccmFlag = predData.cccmFlag; #endif return *this; } @@ -881,6 +887,9 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other ) #if JVET_Z0050_CCLM_SLOPE cclmOffsets = other.cclmOffsets; #endif +#if JVET_AA0057_CCCM + cccmFlag = other.cccmFlag; +#endif mergeFlag = other.mergeFlag; regularMergeFlag = other.regularMergeFlag; diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h index bf5c717a20063cb3ce43cbc2b664c12165a4f5b0..72b66531d4fa420513d258aa052281d180bea337 100644 --- a/source/Lib/CommonLib/Unit.h +++ b/source/Lib/CommonLib/Unit.h @@ -430,6 +430,9 @@ struct IntraPredictionData #if JVET_Z0050_CCLM_SLOPE CclmOffsets cclmOffsets; #endif +#if JVET_AA0057_CCCM + int cccmFlag; +#endif }; struct InterPredictionData diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp index 99e3374c8835ee88aad5e8dff65fad2c44edb6b8..96efd102c1e39b3696602fe30d87b61f0597feb7 100644 --- a/source/Lib/CommonLib/UnitTools.cpp +++ b/source/Lib/CommonLib/UnitTools.cpp @@ -1538,6 +1538,49 @@ bool PU::isDMChromaMIP(const PredictionUnit &pu) #endif } +#if JVET_AA0057_CCCM +void PU::getCccmRefLineNum(const PredictionUnit& pu, int& th, int& tv) +{ + const Area area = pu.blocks[COMPONENT_Cb]; + + th = area.x < CCCM_WINDOW_SIZE ? area.x : CCCM_WINDOW_SIZE; + tv = area.y < CCCM_WINDOW_SIZE ? area.y : CCCM_WINDOW_SIZE; + +#if CCCM_REF_LINES_ABOVE_CTU + int ctuHeight = pu.cs->sps->getMaxCUHeight() >> getComponentScaleY(COMPONENT_Cb, pu.chromaFormat); + int borderDist = area.y % ctuHeight; + int tvMax = borderDist + CCCM_REF_LINES_ABOVE_CTU; + + tv = tv > tvMax ? tvMax : tv; +#endif +} + +bool PU::cccmSingleModeAvail(const PredictionUnit& pu, int intraMode) +{ + const Area area = pu.blocks[COMPONENT_Cb]; + bool modeIsOk = intraMode == LM_CHROMA_IDX; + modeIsOk = modeIsOk && ( area.width * area.height >= CCCM_MIN_PU_SIZE ); + + return modeIsOk && (area.x > 0 || area.y > 0); +} + +bool PU::cccmMultiModeAvail(const PredictionUnit& pu, int intraMode) +{ +#if MMLM + const Area area = pu.blocks[COMPONENT_Cb]; + bool modeIsOk = intraMode == MMLM_CHROMA_IDX; + modeIsOk = modeIsOk && ( area.width * area.height >= CCCM_MIN_PU_SIZE ); + + int th, tv; + PU::getCccmRefLineNum(pu, th, tv); + const int nsamples = ((area.width + th) * (area.height + tv) - (area.area())); + return modeIsOk && nsamples >= 128; +#else + return false; +#endif +} +#endif + uint32_t PU::getIntraDirLuma( const PredictionUnit &pu ) { #if JVET_V0130_INTRA_TMP diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h index 047b568789e73419693c06441c7516413f8b468b..12f2a13350218a528d9433756bcfec9f07025cf9 100644 --- a/source/Lib/CommonLib/UnitTools.h +++ b/source/Lib/CommonLib/UnitTools.h @@ -459,6 +459,11 @@ namespace PU #if JVET_Z0050_CCLM_SLOPE bool hasCclmDeltaFlag(const PredictionUnit &pu, const int mode = -1); #endif +#if JVET_AA0057_CCCM + void getCccmRefLineNum (const PredictionUnit& pu, int& th, int& tv); + bool cccmSingleModeAvail(const PredictionUnit& pu, int intraMode); + bool cccmMultiModeAvail (const PredictionUnit& pu, int intraMode); +#endif } // TU tools diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index 5df22c2119f856115a58e8f446e78d9d40293db3..7341fc244d9f1dc9ad49031d5a4e509f1c4cdf07 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -2166,6 +2166,13 @@ void CABACReader::cclmDelta(PredictionUnit& pu, int8_t &delta) void CABACReader::cclmDeltaSlope(PredictionUnit& pu) { +#if JVET_AA0057_CCCM + if ( pu.cccmFlag ) + { + return; + } +#endif + if ( PU::hasCclmDeltaFlag( pu ) ) { bool deltaActive = m_BinDecoder.decodeBin(Ctx::CclmDeltaFlags(0)); @@ -2275,6 +2282,10 @@ bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu) #endif } +#if JVET_AA0057_CCCM + cccmFlag( pu ); +#endif + #if JVET_Z0050_CCLM_SLOPE cclmDeltaSlope( pu ); #endif @@ -2282,6 +2293,18 @@ bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu) return true; //it will only enter this function for LMC modes, so always return true ; } +#if JVET_AA0057_CCCM +void CABACReader::cccmFlag(PredictionUnit& pu) +{ + const unsigned intraDir = pu.intraDir[1]; + + if ( PU::cccmSingleModeAvail(pu, intraDir) || PU::cccmMultiModeAvail(pu, intraDir) ) + { + pu.cccmFlag = m_BinDecoder.decodeBin( Ctx::CccmFlag( 0 ) ); + } +} +#endif + void CABACReader::intra_chroma_pred_mode(PredictionUnit& pu) { RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2(STATS__CABAC_BITS__INTRA_DIR_ANG, pu.cu->blocks[pu.chType].lumaSize(), CHANNEL_TYPE_CHROMA); diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h index ac06665b361aa84b4e5336957b951a4653e8421b..3e04bfaa9d565f66a70da822b123b5bb6ad22315 100644 --- a/source/Lib/DecoderLib/CABACReader.h +++ b/source/Lib/DecoderLib/CABACReader.h @@ -119,6 +119,9 @@ public: void intra_chroma_pred_modes ( CodingUnit& cu ); bool intra_chroma_lmc_mode ( PredictionUnit& pu ); void intra_chroma_pred_mode ( PredictionUnit& pu ); +#if JVET_AA0057_CCCM + void cccmFlag ( PredictionUnit& pu ); +#endif #if ENABLE_DIMD void cu_dimd_flag (CodingUnit& cu); #endif diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp index 8df3da3ae03d8e87d33ef4e0b0c84b7e511773ad..462995d2e5835a54c32f52c56fb97cd9a3031403 100644 --- a/source/Lib/DecoderLib/DecCu.cpp +++ b/source/Lib/DecoderLib/DecCu.cpp @@ -535,12 +535,31 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID ) m_pcIntraPred->initIntraPatternChTypeISP(*tu.cu, area, pReco); } } +#if JVET_AA0057_CCCM + else if ( isLuma(compID) || !pu.cccmFlag ) +#else else +#endif { m_pcIntraPred->initIntraPatternChType(*tu.cu, area); } //===== get prediction signal ===== +#if JVET_AA0057_CCCM + if( compID != COMPONENT_Y && pu.cccmFlag ) + { + // Create both Cb and Cr predictions when here for Cb + if( compID == COMPONENT_Cb ) + { + const PredictionUnit& pu = *tu.cu->firstPU; + PelBuf predCr = cs.getPredBuf( tu.blocks[COMPONENT_Cr] ); + + m_pcIntraPred->xGetLumaRecPixels( pu, area ); + m_pcIntraPred->predIntraCCCM( pu, piPred, predCr, uiChFinalMode ); + } + } + else +#endif if( compID != COMPONENT_Y && PU::isLMCMode( uiChFinalMode ) ) { const PredictionUnit& pu = *tu.cu->firstPU; @@ -603,7 +622,11 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID ) } } #if SIGN_PREDICTION +#if JVET_AA0057_CCCM + if(isJCCR && compID == COMPONENT_Cb && !pu.cccmFlag) // Cr prediction was done already for CCCM +#else if(isJCCR && compID == COMPONENT_Cb) +#endif { m_pcIntraPred->initIntraPatternChType(*tu.cu, areaCr); if( PU::isLMCMode( uiChFinalMode ) ) diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp index 3633510798f7f4c83eac79a832f6c17cb1db8878..431aeb02ec198406c54976929fec121fc055fa60 100644 --- a/source/Lib/EncoderLib/CABACWriter.cpp +++ b/source/Lib/EncoderLib/CABACWriter.cpp @@ -1903,6 +1903,13 @@ void CABACWriter::cclmDelta(const PredictionUnit& pu, int8_t delta) void CABACWriter::cclmDeltaSlope(const PredictionUnit& pu) { +#if JVET_AA0057_CCCM + if ( pu.cccmFlag ) + { + return; + } +#endif + if ( PU::hasCclmDeltaFlag( pu ) ) { bool deltaActive = pu.cclmOffsets.isActive(); @@ -2009,11 +2016,26 @@ void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu) #endif } +#if JVET_AA0057_CCCM + cccmFlag( pu ); +#endif + #if JVET_Z0050_CCLM_SLOPE cclmDeltaSlope( pu ); #endif } +#if JVET_AA0057_CCCM +void CABACWriter::cccmFlag(const PredictionUnit& pu) +{ + const unsigned intraDir = pu.intraDir[1]; + + if ( PU::cccmSingleModeAvail(pu, intraDir) || PU::cccmMultiModeAvail(pu, intraDir) ) + { + m_BinEncoder.encodeBin( pu.cccmFlag ? 1 : 0, Ctx::CccmFlag( 0 ) ); + } +} +#endif void CABACWriter::intra_chroma_pred_mode(const PredictionUnit& pu) { diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h index 6025f69ed71d9e84bf371e183e9fb535a55ddf98..706ab6ffc6b27408e7ad102900f941f30c2b19dc 100644 --- a/source/Lib/EncoderLib/CABACWriter.h +++ b/source/Lib/EncoderLib/CABACWriter.h @@ -132,6 +132,9 @@ public: void intra_chroma_pred_modes ( const CodingUnit& cu ); void intra_chroma_lmc_mode ( const PredictionUnit& pu ); void intra_chroma_pred_mode ( const PredictionUnit& pu ); +#if JVET_AA0057_CCCM + void cccmFlag ( const PredictionUnit& pu ); +#endif void cu_residual ( const CodingUnit& cu, Partitioner& pm, CUCtx& cuCtx ); void rqt_root_cbf ( const CodingUnit& cu ); void adaptive_color_transform(const CodingUnit& cu); diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp index 3386bc85d0d7576e8d6821679f88bec3a1b4683c..5fd007e74fccf2d1c8a421ba5f2839fc13d5408b 100644 --- a/source/Lib/EncoderLib/IntraSearch.cpp +++ b/source/Lib/EncoderLib/IntraSearch.cpp @@ -1879,6 +1879,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner Distortion uiBestDist = 0; double dBestCost = MAX_DOUBLE; int32_t bestBDPCMMode = 0; +#if JVET_AA0057_CCCM + int cccmModeBest = 0; +#endif #if JVET_Z0050_CCLM_SLOPE CclmOffsets bestCclmOffsets = {}; CclmOffsets satdCclmOffsetsBest[NUM_CHROMA_MODE]; @@ -2407,6 +2410,88 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner pu.cclmOffsets.setAllZero(); #endif +#if JVET_AA0057_CCCM +#if MMLM + for (int32_t uiMode = 0; uiMode < 2; uiMode++) + { + int chromaIntraMode = uiMode ? MMLM_CHROMA_IDX : LM_CHROMA_IDX; +#else + for (int32_t uiMode = 0; uiMode < 1; uiMode++) + { + int chromaIntraMode = LM_CHROMA_IDX; +#endif + + if ( PU::cccmSingleModeAvail(pu, chromaIntraMode) || PU::cccmMultiModeAvail(pu, chromaIntraMode) ) + { + pu.cccmFlag = 1; + + // Original RD check code replicated from above + cs.setDecomp( pu.Cb(), false ); + cs.dist = baseDist; + //----- restore context models ----- + m_CABACEstimator->getCtx() = ctxStart; + + //----- chroma coding ----- + pu.intraDir[1] = chromaIntraMode; + + xRecurIntraChromaCodingQT( cs, partitioner, bestCostSoFar, ispType ); + if( lumaUsesISP && cs.dist == MAX_UINT ) + { + continue; + } + + if (cs.sps->getTransformSkipEnabledFlag()) + { + m_CABACEstimator->getCtx() = ctxStart; + } + + uint64_t fracBits = xGetIntraFracBitsQT( cs, partitioner, false, true, -1, ispType ); + Distortion uiDist = cs.dist; + double dCost = m_pcRdCost->calcRdCost( fracBits, uiDist - baseDist ); + + //----- compare ----- + if( dCost < dBestCost ) + { + if( lumaUsesISP && dCost < bestCostSoFar ) + { + bestCostSoFar = dCost; + } + for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ ) + { + const CompArea &area = pu.blocks[i]; + + saveCS.getRecoBuf ( area ).copyFrom( cs.getRecoBuf ( area ) ); +#if KEEP_PRED_AND_RESI_SIGNALS + saveCS.getPredBuf ( area ).copyFrom( cs.getPredBuf ( area ) ); + saveCS.getResiBuf ( area ).copyFrom( cs.getResiBuf ( area ) ); +#endif + saveCS.getPredBuf ( area ).copyFrom( cs.getPredBuf (area ) ); + cs.picture->getPredBuf( area ).copyFrom( cs.getPredBuf (area ) ); + cs.picture->getRecoBuf( area ).copyFrom( cs.getRecoBuf( area ) ); + + for( uint32_t j = 0; j < saveCS.tus.size(); j++ ) + { + saveCS.tus[j]->copyComponentFrom( *orgTUs[j], area.compID ); + } + } + + dBestCost = dCost; + uiBestDist = uiDist; + uiBestMode = chromaIntraMode; + bestBDPCMMode = cu.bdpcmModeChroma; +#if JVET_Z0050_DIMD_CHROMA_FUSION + isChromaFusion = pu.isChromaFusion; +#endif +#if JVET_Z0050_CCLM_SLOPE + bestCclmOffsets = pu.cclmOffsets; +#endif + cccmModeBest = pu.cccmFlag; + } + } + } + + pu.cccmFlag = 0; +#endif for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ ) { const CompArea &area = pu.blocks[i]; @@ -2438,6 +2523,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner #if JVET_Z0050_CCLM_SLOPE pu.cclmOffsets = bestCclmOffsets; #endif +#if JVET_AA0057_CCCM + pu.cccmFlag = cccmModeBest; +#endif #if JVET_Z0050_DIMD_CHROMA_FUSION pu.isChromaFusion = isChromaFusion; #endif @@ -6670,6 +6758,14 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio initIntraPatternChType( *currTU.cu, cbArea); initIntraPatternChType( *currTU.cu, crArea); +#if JVET_AA0057_CCCM + if( pu.cccmFlag ) + { + xGetLumaRecPixels( pu, cbArea ); + predIntraCCCM( pu, piPredCb, piPredCr, predMode ); + } + else +#endif if( PU::isLMCMode( predMode ) ) { xGetLumaRecPixels( pu, cbArea );