From 1532d56b07d63e9c0f8b34995866b0effe845182 Mon Sep 17 00:00:00 2001 From: Ted <jhuhong-jheng@kwai.com> Date: Fri, 20 Jan 2023 10:03:07 -0800 Subject: [PATCH] JVET-AC0147: Subsampling is not applied to CCCM --- source/App/EncoderApp/EncApp.cpp | 3 + source/App/EncoderApp/EncAppCfg.cpp | 6 + source/App/EncoderApp/EncAppCfg.h | 3 + source/Lib/CommonLib/CommonDef.h | 4 + source/Lib/CommonLib/Contexts.cpp | 30 ++ source/Lib/CommonLib/IntraPrediction.cpp | 482 +++++++++++++++++++++++ source/Lib/CommonLib/IntraPrediction.h | 12 + source/Lib/CommonLib/Slice.h | 7 + source/Lib/CommonLib/TypeDef.h | 1 + source/Lib/CommonLib/Unit.cpp | 9 + source/Lib/CommonLib/Unit.h | 3 + source/Lib/CommonLib/UnitTools.cpp | 40 ++ source/Lib/DecoderLib/CABACReader.cpp | 13 + source/Lib/DecoderLib/VLCReader.cpp | 3 + source/Lib/EncoderLib/CABACWriter.cpp | 12 + source/Lib/EncoderLib/EncCfg.h | 7 + source/Lib/EncoderLib/EncCu.cpp | 3 + source/Lib/EncoderLib/EncLib.cpp | 3 + source/Lib/EncoderLib/IntraSearch.cpp | 313 ++++++++++++++- source/Lib/EncoderLib/IntraSearch.h | 8 + source/Lib/EncoderLib/VLCWriter.cpp | 3 + 21 files changed, 963 insertions(+), 2 deletions(-) diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 96c08dd6a..3406715f5 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -863,6 +863,9 @@ void EncApp::xInitLibCfg() #if JVET_AB0155_SGPM m_cEncLib.setUseSgpm ( m_sgpm ); #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + m_cEncLib.setUseCccm ( m_cccm ); +#endif #if ENABLE_OBMC m_cEncLib.setUseObmc ( m_OBMC ); #endif diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 331e19ee1..81d29ecbe 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -1102,6 +1102,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) #if JVET_AB0155_SGPM ( "SGPM", m_sgpm, true, "Enable spatial geometric partitioning mode\n" ) #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + ( "CCCM", m_cccm, 2, "CCCM mode (0:off, 1:on, 2:on subsampling and no subsampling) [default: 2]") +#endif #if ENABLE_OBMC ("OBMC", m_OBMC, true, "Overlapping Block Motion Compensation") #endif @@ -5235,6 +5238,9 @@ void EncAppCfg::xPrintParameter() #if JVET_AB0155_SGPM msg(VERBOSE, "SGPM:%d ", m_sgpm); #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + msg(VERBOSE, "CCCM:%d ", m_cccm); +#endif #if JVET_V0130_INTRA_TMP msg( VERBOSE, "IntraTMP:%d ", m_intraTMP ); msg( VERBOSE, "IntraTmpMaxSize:%d ", m_intraTmpMaxSize ); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 6ef67b368..3763e9fbb 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -463,6 +463,9 @@ protected: #if JVET_AB0155_SGPM bool m_sgpm; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + int m_cccm; +#endif #if ENABLE_OBMC bool m_OBMC; #endif diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index 474172b0d..20c247d8b 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -965,6 +965,10 @@ static const int CCCM_NUM_MODES = 6; static const int CCCM_NUM_MODES = 3; #endif #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING +static const int CCCM_NO_SUB_NUM_PARAMS = 11; +static const double CCCM_NO_SUB_WEIGHT = 1.1; +#endif #endif #if JVET_AA0126_GLM diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp index 4323ce25f..945e98cb6 100644 --- a/source/Lib/CommonLib/Contexts.cpp +++ b/source/Lib/CommonLib/Contexts.cpp @@ -3053,6 +3053,19 @@ const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet #if JVET_AA0057_CCCM const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet ({ +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + { CNU, CNU, }, + { CNU, CNU, }, + { CNU, CNU, }, + { DWS, DWS, }, + { DWS, DWS, }, + { DWS, DWS, }, + { DWE, DWE, }, + { DWE, DWE, }, + { DWE, DWE, }, + { DWO, DWO, }, + { DWO, DWO, }, +#else { CNU, }, { CNU, }, { CNU, }, @@ -3064,6 +3077,7 @@ const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet { DWE, }, { DWO, }, { DWO, }, +#endif }); #endif @@ -4682,12 +4696,21 @@ const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet #if JVET_AA0057_CCCM const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet ({ +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + { CNU, CNU, }, + { CNU, CNU, }, + { CNU, CNU, }, + { DWS, DWS, }, + { DWS, DWS, }, + { DWS, DWS, }, +#else { CNU, }, { CNU, }, { CNU, }, { DWS, }, { DWS, }, { DWS, }, +#endif }); #endif @@ -5841,10 +5864,17 @@ const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet #if JVET_AA0057_CCCM const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet ({ +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + { CNU, CNU, }, + { CNU, CNU, }, + { CNU, CNU, }, + { DWS, DWS, }, +#else { CNU, }, { CNU, }, { CNU, }, { DWS, }, +#endif }); #endif diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp index 73492398d..7f74d2408 100644 --- a/source/Lib/CommonLib/IntraPrediction.cpp +++ b/source/Lib/CommonLib/IntraPrediction.cpp @@ -126,8 +126,13 @@ IntraPrediction::IntraPrediction() m_pppTarPatch = NULL; #endif #if JVET_AA0057_CCCM +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + m_cccmLumaBuf[0] = nullptr; + m_cccmLumaBuf[1] = nullptr; +#else m_cccmLumaBuf = nullptr; #endif +#endif } IntraPrediction::~IntraPrediction() @@ -210,9 +215,17 @@ void IntraPrediction::destroy() #endif #if JVET_AA0057_CCCM +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + delete[] m_cccmLumaBuf[0]; + m_cccmLumaBuf[0] = nullptr; + + delete[] m_cccmLumaBuf[1]; + m_cccmLumaBuf[1] = nullptr; +#else delete[] m_cccmLumaBuf; m_cccmLumaBuf = nullptr; #endif +#endif } void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepthY) @@ -358,11 +371,26 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth #endif #if JVET_AA0057_CCCM +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if( m_cccmLumaBuf[0] == nullptr ) + { + m_cccmLumaBuf[0] = new Pel[(2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2 * CCCM_FILTER_PADDING) * (2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2 * CCCM_FILTER_PADDING)]; + } + + if( m_cccmLumaBuf[1] == nullptr ) + { + const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, chromaFormatIDC ); + const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, chromaFormatIDC ); + + m_cccmLumaBuf[1] = new Pel[(2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + (CCCM_FILTER_PADDING << chromaScaleX) + (CCCM_FILTER_PADDING << chromaScaleY)) * (2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + (CCCM_FILTER_PADDING << chromaScaleX) + (CCCM_FILTER_PADDING << chromaScaleY))]; + } +#else 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 +#endif #if ENABLE_SIMD_TMP #ifdef TARGET_SIMD_X86 @@ -6454,6 +6482,13 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom #if JVET_AA0057_CCCM if ( pu.cccmFlag ) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if( pu.cccmNoSubFlag ) + { + xCccmCreateLumaNoSubRef( pu, chromaArea ); + return; + } +#endif xCccmCreateLumaRef(pu, chromaArea); return; } @@ -8923,6 +8958,38 @@ void IntraPrediction::xCccmSetLumaRefValue( const PredictionUnit& pu ) void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, PelBuf &predCr, int intraDir ) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if( pu.cccmNoSubFlag ) + { + CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_LUMA ) ); + CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_LUMA ) ); + +#if JVET_AB0143_CCCM_TS + if( intraDir == LM_CHROMA_IDX || intraDir == MDLM_L_IDX || intraDir == MDLM_T_IDX ) +#else + if( PU::cccmSingleModeAvail( pu, intraDir ) ) +#endif + { + 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 ); + } + } + else +#endif if ( pu.cccmFlag ) { CccmModel<CCCM_NUM_PARAMS> cccmModelCb( pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) ); @@ -8959,6 +9026,10 @@ void IntraPrediction::xCccmApplyModel(const PredictionUnit& pu, const ComponentI const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId)); static Pel samples[CCCM_NUM_PARAMS]; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + CHECK( pu.cccmNoSubFlag, "cccmNoSubFlag shall be disabled" ); +#endif + CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu); for (int y = 0; y < refLumaBlk.height; y++) @@ -8992,6 +9063,10 @@ void IntraPrediction::xCccmCalcModels(const PredictionUnit& pu, CccmModel<CCCM_N { int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + CHECK( pu.cccmNoSubFlag, "cccmNoSubFlag shall be disabled" ); +#endif + 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); @@ -9076,13 +9151,376 @@ void IntraPrediction::xCccmCalcModels(const PredictionUnit& pu, CccmModel<CCCM_N } } +#if JVET_AC0147_CCCM_NO_SUBSAMPLING +void IntraPrediction::xCccmCalcModels( const PredictionUnit& pu, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCb, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCr, int modelId, int modelThr ) +{ + 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 ); + + CHECK( !pu.cccmNoSubFlag, "cccmNoSubFlag shall be enabled" ); + + PelBuf refLuma = xCccmGetLumaRefBuf( pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY ); + + int sampleNum = 0; + const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int stepX = 1 << chromaScaleX; + const int stepY = 1 << chromaScaleY; + +#if JVET_AB0174_CCCM_DIV_FREE + int chromaOffsetCb = 1 << ( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_CHROMA ) - 1 ); + int chromaOffsetCr = 1 << ( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_CHROMA ) - 1 ); + + if( refSizeX || refSizeY ) + { + int refPosX = refSizeX > 0 ? refSizeX - 1 : 0; + int refPosY = refSizeY > 0 ? refSizeY - 1 : 0; + + refPosX = (refPosX + refPosPicX) >> chromaScaleX; + refPosY = (refPosY + refPosPicY) >> chromaScaleY; + + chromaOffsetCb = recoCb.at( refPosX, refPosY ); + chromaOffsetCr = recoCr.at( refPosX, refPosY ); + } +#endif + + // Collect reference data to input matrix A and target vector Y + static Pel A[CCCM_NO_SUB_NUM_PARAMS][CCCM_MAX_REF_SAMPLES]; + static Pel YCb[CCCM_MAX_REF_SAMPLES]; + static Pel YCr[CCCM_MAX_REF_SAMPLES]; + +#if JVET_AB0143_CCCM_TS + int yStart = pu.cccmFlag == 2 ? refSizeY : 0; + int yEnd = pu.cccmFlag == 3 ? refSizeY : areaHeight; + int xStart = pu.cccmFlag == 3 ? refSizeX : 0; + int xEnd = pu.cccmFlag == 2 ? refSizeX : areaWidth; +#else + int yStart = 0; + int yEnd = areaHeight; + int xStart = 0; + int xEnd = areaWidth; +#endif + + for( int y = yStart; y < yEnd; y += stepY ) + { + for( int x = xStart; x < xEnd; x += stepX ) + { + if( x >= refSizeX && y >= refSizeY ) + { + continue; + } + + const Pel* src0 = refLuma.bufAt( x, y ); + const Pel* src1 = refLuma.bufAt( x, y + 1 ); + + if( modelId == 1 && src0[0] > modelThr ) // Model 1: Include only samples below or equal to the threshold + { + continue; + } + if( modelId == 2 && src0[0] <= modelThr ) // Model 2: Include only samples above the threshold + { + continue; + } + + A[0][sampleNum] = src0[0]; + A[1][sampleNum] = src0[-1]; + A[2][sampleNum] = src0[1]; + A[3][sampleNum] = src1[0]; + A[4][sampleNum] = src1[-1]; + A[5][sampleNum] = src1[1]; + A[6][sampleNum] = cccmModelCb.nonlinear( src0[0] ); + A[7][sampleNum] = cccmModelCb.nonlinear( src1[0] ); + A[8][sampleNum] = cccmModelCb.nonlinear( src0[1] ); + A[9][sampleNum] = cccmModelCb.nonlinear( src0[-1] ); + A[10][sampleNum] = cccmModelCb.bias(); + + const int refPosX = (refPosPicX + x) >> chromaScaleX; + const int refPosY = (refPosPicY + y) >> chromaScaleY; + + YCb[sampleNum] = recoCb.at( refPosX, refPosY ); + YCr[sampleNum++] = recoCr.at( refPosX, refPosY ); + } + } + + if( !sampleNum ) // Number of samples can go to zero in the multimode case + { + cccmModelCb.clearModel(); + cccmModelCr.clearModel(); + } + else + { +#if JVET_AB0174_CCCM_DIV_FREE + m_cccmNoSubSolver.solve2( A, YCb, YCr, sampleNum, chromaOffsetCb, chromaOffsetCr, cccmModelCb, cccmModelCr ); +#else + m_cccmNoSubSolver.solve2( A, YCb, YCr, sampleNum, cccmModelCb, cccmModelCr ); +#endif + } +} + +void IntraPrediction::xCccmApplyModel( const PredictionUnit& pu, const ComponentID compId, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModel, int modelId, int modelThr, PelBuf &piPred ) const +{ + const ClpRng& clpRng( pu.cu->cs->slice->clpRng( compId ) ); + static Pel samples[CCCM_NO_SUB_NUM_PARAMS]; + + CHECK( !pu.cccmNoSubFlag, "cccmNoSubFlag shall be enabled" ); + + CPelBuf refLumaBlk = xCccmGetLumaPuBuf( pu ); + const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int stepX = 1 << chromaScaleX; + const int stepY = 1 << chromaScaleY; + + for( int y = 0; y < refLumaBlk.height; y += stepY ) + { + for( int x = 0; x < refLumaBlk.width; x += stepX ) + { + const Pel* src0 = refLumaBlk.bufAt( x, y ); + const Pel* src1 = refLumaBlk.bufAt( x, y + 1 ); + + if( modelId == 1 && src0[0] > modelThr ) // Model 1: Include only samples below or equal to the threshold + { + continue; + } + if( modelId == 2 && src0[0] <= modelThr ) // Model 2: Include only samples above the threshold + { + continue; + } + + samples[0] = src0[0]; + samples[1] = src0[-1]; + samples[2] = src0[1]; + samples[3] = src1[0]; + samples[4] = src1[-1]; + samples[5] = src1[1]; + samples[6] = cccmModel.nonlinear( src0[0] ); + samples[7] = cccmModel.nonlinear( src1[0] ); + samples[8] = cccmModel.nonlinear( src0[1] ); + samples[9] = cccmModel.nonlinear( src0[-1] ); + samples[10] = cccmModel.bias(); + + piPred.at( x >> chromaScaleX, y >> chromaScaleY ) = ClipPel<Pel>( cccmModel.convolve( samples ), clpRng ); + } + } +} + +void IntraPrediction::xCccmCreateLumaNoSubRef( const PredictionUnit& pu, CompArea chromaArea ) +{ + const CPelBuf recoLuma = pu.cs->picture->getRecoBuf( COMPONENT_Y ); + const int maxPosPicX = pu.cs->picture->lumaSize().width - 1; + const int maxPosPicY = pu.cs->picture->lumaSize().height - 1; + + xCccmCalcRefArea( pu, chromaArea ); // Find the reference area + + int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY; + + CHECK( !pu.cccmNoSubFlag, "cccmNoSubFlag shall be enabled" ); + + PelBuf refLuma = xCccmGetLumaRefBuf( pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY ); + + const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + + int puBorderX = refSizeX + (m_cccmBlkArea.width << chromaScaleX); + int puBorderY = refSizeY + (m_cccmBlkArea.height << chromaScaleY); + +#if JVET_AB0174_CCCM_DIV_FREE + xCccmSetLumaRefValue( pu ); +#endif + + const int filterPaddingX = CCCM_FILTER_PADDING << chromaScaleX; + const int filterPaddingY = CCCM_FILTER_PADDING << chromaScaleY; + + // luma for the area covering both the PU and the top/left reference areas (+ top and left paddings) + for( int y = -filterPaddingY; y < areaHeight; y++ ) + { + for( int x = -filterPaddingX; 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 ); + } + } + + // Pad right of top reference area + for( int x = 0; x < filterPaddingX; x++ ) + { + for( int y = -filterPaddingY; y < refSizeY; y++ ) + { + refLuma.at( areaWidth + x, y ) = refLuma.at( areaWidth - 1, y ); + } + + // Pad right of PU + for( int y = refSizeY; y < puBorderY; y++ ) + { + refLuma.at( puBorderX + x, y ) = refLuma.at( puBorderX - 1, y ); + } + + // Pad right of left reference area + for( int y = puBorderY; y < areaHeight; y++ ) + { + refLuma.at( refSizeX + x, y ) = refLuma.at( refSizeX - 1, y ); + } + } + + for( int y = 0; y < filterPaddingY; y++ ) + { + // Pad below left reference area + for( int x = -filterPaddingX; x < refSizeX + filterPaddingX; x++ ) + { + refLuma.at( x, areaHeight + y ) = refLuma.at( x, areaHeight - 1 ); + } + + // Pad below PU + for( int x = refSizeX; x < puBorderX + filterPaddingX; x++ ) + { + refLuma.at( x, puBorderY + y ) = refLuma.at( x, puBorderY - 1 ); + } + + // Pad below right reference area + for( int x = puBorderX + filterPaddingX; x < areaWidth + filterPaddingX; x++ ) + { + refLuma.at( x, refSizeY + y ) = 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 = -filterPaddingY; y < refSizeY; y++ ) + { + int chromaPosPicY = refPosPicY + y; + chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY; + + for( int x = 0; x < filterPaddingX; x++ ) + { + refLuma.at( areaWidth + x, 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; + + for( int x = 0; x < filterPaddingX; x++ ) + { + refLuma.at( puBorderX + x, 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; + + for( int x = 0; x < filterPaddingX; x++ ) + { + refLuma.at( refSizeX + x, y ) = xCccmGetLumaVal( pu, recoLuma, padPosPicX, chromaPosPicY ); + } + } + } + + // Samples below left reference area + int padPosPicY = refPosPicY + areaHeight; + + if( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) ) + { + for( int x = -filterPaddingX; x < refSizeX + filterPaddingX; x++ ) + { + int chromaPosPicX = refPosPicX + x; + chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX; + + for( int y = 0; y < filterPaddingY; y++ ) + { + refLuma.at( x, areaHeight + y ) = 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; + + for( int y = 0; y < filterPaddingY; y++ ) + { + refLuma.at( x, puBorderY + y ) = 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; + + for( int y = 0; y < filterPaddingY; y++ ) + { + refLuma.at( x, refSizeY + y ) = xCccmGetLumaVal( pu, recoLuma, chromaPosPicX, padPosPicY ); + } + } + } + } +} +#endif + // 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 JVET_AC0147_CCCM_NO_SUBSAMPLING + if( pu.cccmNoSubFlag || pu.chromaFormat == CHROMA_444 ) +#else if (pu.chromaFormat == CHROMA_444) +#endif { ypval = piSrc[x + iRecStride * y]; } @@ -9177,6 +9615,25 @@ void IntraPrediction::xCccmCalcRefArea(const PredictionUnit& pu, CompArea chroma // 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 { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if( pu.cccmNoSubFlag ) + { + const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + + refPosPicX = m_cccmRefArea.x << chromaScaleX; // Position of the reference area in picture coordinates + refPosPicY = m_cccmRefArea.y << chromaScaleY; + refSizeX = ((m_cccmBlkArea.x - m_cccmRefArea.x) << chromaScaleX); // Reference lines available left and above + refSizeY = ((m_cccmBlkArea.y - m_cccmRefArea.y) << chromaScaleY); + areaWidth = m_cccmRefArea.width << chromaScaleX; // Reference buffer size excluding paddings + areaHeight = m_cccmRefArea.height << chromaScaleY; + + int refStride = areaWidth + 2 * (CCCM_FILTER_PADDING << chromaScaleX); // Including paddings required for the 2D filter + int refOrigin = refStride * (CCCM_FILTER_PADDING << chromaScaleY) + (CCCM_FILTER_PADDING << chromaScaleX); + return PelBuf( m_cccmLumaBuf[1] + refOrigin, refStride, areaWidth, areaHeight ); // Points to the top-left corner of the reference area + } +#endif + refSizeX = m_cccmBlkArea.x - m_cccmRefArea.x; // Reference lines available left and above refSizeY = m_cccmBlkArea.y - m_cccmRefArea.y; areaWidth = m_cccmRefArea.width; // Reference buffer size excluding paddings @@ -9187,12 +9644,33 @@ PelBuf IntraPrediction::xCccmGetLumaRefBuf(const PredictionUnit& pu, int &areaWi int refStride = areaWidth + 2 * CCCM_FILTER_PADDING; // Including paddings required for the 2D filter int refOrigin = refStride * CCCM_FILTER_PADDING + CCCM_FILTER_PADDING; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + return PelBuf( m_cccmLumaBuf[0] + refOrigin, refStride, areaWidth, areaHeight ); // Points to the top-left corner of the reference area +#else return PelBuf(m_cccmLumaBuf + refOrigin, refStride, areaWidth, areaHeight); // Points to the top-left corner of the reference area +#endif } // Return downsampled luma buffer for a PU PelBuf IntraPrediction::xCccmGetLumaPuBuf(const PredictionUnit& pu) const { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if( pu.cccmNoSubFlag ) + { + const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() ); + + int refSizeX = ((m_cccmBlkArea.x - m_cccmRefArea.x) << chromaScaleX); // Reference lines available left and above + int refSizeY = ((m_cccmBlkArea.y - m_cccmRefArea.y) << chromaScaleY); + int tuWidth = m_cccmBlkArea.width << chromaScaleX; + int tuHeight = m_cccmBlkArea.height << chromaScaleY; + + int refStride = (m_cccmRefArea.width + 2 * CCCM_FILTER_PADDING) << chromaScaleX; // Including paddings required for the 2D filter + int refOrigin = refStride * (refSizeY + (CCCM_FILTER_PADDING << chromaScaleY)) + refSizeX + (CCCM_FILTER_PADDING << chromaScaleX); + return PelBuf( m_cccmLumaBuf[1] + refOrigin, refStride, tuWidth, tuHeight ); // Points to the top-left corner of the block + } +#endif + int refSizeX = m_cccmBlkArea.x - m_cccmRefArea.x; // Reference lines available left and above int refSizeY = m_cccmBlkArea.y - m_cccmRefArea.y; int tuWidth = m_cccmBlkArea.width; @@ -9200,7 +9678,11 @@ PelBuf IntraPrediction::xCccmGetLumaPuBuf(const PredictionUnit& pu) const int refStride = m_cccmRefArea.width + 2 * CCCM_FILTER_PADDING; // Including paddings required for the 2D filter int refOrigin = refStride * (refSizeY + CCCM_FILTER_PADDING) + refSizeX + CCCM_FILTER_PADDING; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + return PelBuf( m_cccmLumaBuf[0] + refOrigin, refStride, tuWidth, tuHeight ); // Points to the top-left corner of the block +#else return PelBuf(m_cccmLumaBuf + refOrigin, refStride, tuWidth, tuHeight); // Points to the top-left corner of the block +#endif } int IntraPrediction::xCccmCalcRefAver(const PredictionUnit& pu) const diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h index 96c332be4..ef56518da 100644 --- a/source/Lib/CommonLib/IntraPrediction.h +++ b/source/Lib/CommonLib/IntraPrediction.h @@ -197,7 +197,11 @@ private: #if JVET_AA0057_CCCM Area m_cccmBlkArea; Area m_cccmRefArea; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + Pel* m_cccmLumaBuf[2]; +#else Pel* m_cccmLumaBuf; +#endif #if JVET_AB0174_CCCM_DIV_FREE int m_cccmLumaOffset; #endif @@ -300,6 +304,9 @@ protected: #if JVET_AB0092_GLM_WITH_LUMA CccmCovariance<GLM_NUM_PARAMS, GLM_MAX_REF_SAMPLES> m_glmSolver; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + CccmCovariance<CCCM_NO_SUB_NUM_PARAMS, GLM_MAX_REF_SAMPLES> m_cccmNoSubSolver; +#endif // prediction void xPredIntraPlanar ( const CPelBuf &pSrc, PelBuf &pDst @@ -390,6 +397,11 @@ public: #if JVET_AB0174_CCCM_DIV_FREE void xCccmSetLumaRefValue (const PredictionUnit& pu); #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + void xCccmCreateLumaNoSubRef ( const PredictionUnit& pu, CompArea chromaArea ); + void xCccmApplyModel ( const PredictionUnit& pu, const ComponentID compId, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModel, int modelId, int modelThr, PelBuf &piPred ) const; + void xCccmCalcModels ( const PredictionUnit& pu, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCb, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCr, int modelId, int modelThr ); +#endif #endif #if JVET_AB0092_GLM_WITH_LUMA void xGlmCalcModel (const PredictionUnit& pu, const ComponentID compId, const CompArea& chromaArea, CccmModel<GLM_NUM_PARAMS> &glmModel); diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index 0d24575d7..2555c41fb 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -1727,6 +1727,9 @@ private: #if JVET_AB0155_SGPM bool m_sgpm; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + int m_cccm; +#endif #if JVET_V0130_INTRA_TMP bool m_intraTMP; ///< intra Template Matching unsigned m_intraTmpMaxSize; ///< max CU size for which intra TMP is allowed @@ -2272,6 +2275,10 @@ void setCCALFEnabledFlag( bool b ) void setUseSgpm (bool b) { m_sgpm = b; } bool getUseSgpm () const { return m_sgpm; } #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + void setUseCccm( int i ) { m_cccm = i; } + int getUseCccm() const { return m_cccm; } +#endif #if ENABLE_OBMC void setUseOBMC ( bool b ) { m_OBMC = b; } diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index c6960dd4a..9cdc466cd 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -147,6 +147,7 @@ #define JVET_AA0057_CCCM 1 // JVET-AA0057: Convolutional cross-component model (CCCM) #if JVET_AA0057_CCCM #define JVET_AB0143_CCCM_TS 1 // JVET-AB0143: CCCM template selection +#define JVET_AC0147_CCCM_NO_SUBSAMPLING 1 // JVET-AC0147: Subsampling is not applied to CCCM #endif #define JVET_AA0126_GLM 1 // JVET-AA0126: Gradient linear model #if JVET_AA0126_GLM diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp index 3f2d00b06..2781e3a5c 100644 --- a/source/Lib/CommonLib/Unit.cpp +++ b/source/Lib/CommonLib/Unit.cpp @@ -739,6 +739,9 @@ void PredictionUnit::initData() #endif #if JVET_AA0057_CCCM cccmFlag = 0; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + cccmNoSubFlag = 0; +#endif #endif // inter data mergeFlag = false; @@ -873,6 +876,9 @@ PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData) #endif #if JVET_AA0057_CCCM cccmFlag = predData.cccmFlag; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + cccmNoSubFlag = predData.cccmNoSubFlag; +#endif #endif return *this; } @@ -1004,6 +1010,9 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other ) #endif #if JVET_AA0057_CCCM cccmFlag = other.cccmFlag; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + cccmNoSubFlag = other.cccmNoSubFlag; +#endif #endif mergeFlag = other.mergeFlag; diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h index f953b7329..e0240e9a6 100644 --- a/source/Lib/CommonLib/Unit.h +++ b/source/Lib/CommonLib/Unit.h @@ -481,6 +481,9 @@ struct IntraPredictionData #endif #if JVET_AA0057_CCCM int cccmFlag; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + int cccmNoSubFlag; +#endif #endif }; diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp index c2863ca4b..2e8b98aa3 100644 --- a/source/Lib/CommonLib/UnitTools.cpp +++ b/source/Lib/CommonLib/UnitTools.cpp @@ -1845,6 +1845,16 @@ void PU::getCccmRefLineNum(const PredictionUnit& pu, const Area area, int& th, i #if JVET_AA0057_CCCM bool PU::cccmSingleModeAvail(const PredictionUnit& pu, int intraMode) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cs->sps->getUseCccm() == 0 ) + { + return false; + } + else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 ) ) + { + return false; + } +#endif const Area area = pu.blocks[COMPONENT_Cb]; bool modeIsOk = intraMode == LM_CHROMA_IDX; modeIsOk = modeIsOk && ( area.width * area.height >= CCCM_MIN_PU_SIZE ); @@ -1859,6 +1869,16 @@ bool PU::cccmSingleModeAvail(const PredictionUnit& pu, int intraMode) bool PU::cccmMultiModeAvail(const PredictionUnit& pu, int intraMode) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cs->sps->getUseCccm() == 0 ) + { + return false; + } + else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 ) ) + { + return false; + } +#endif #if MMLM const Area area = pu.blocks[COMPONENT_Cb]; #if JVET_AB0143_CCCM_TS @@ -1906,6 +1926,16 @@ bool PU::cccmMultiModeAvail(const PredictionUnit& pu, int intraMode) #if JVET_AB0143_CCCM_TS bool PU::isLeftCccmMode(const PredictionUnit& pu, int intraMode) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cs->sps->getUseCccm() == 0 ) + { + return false; + } + else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 ) ) + { + return false; + } +#endif const Area area = pu.blocks[COMPONENT_Cb]; bool modeIsOk = (intraMode == MDLM_L_IDX); modeIsOk = modeIsOk && (area.width * area.height >= CCCM_MIN_PU_SIZE); @@ -1925,6 +1955,16 @@ bool PU::isLeftCccmMode(const PredictionUnit& pu, int intraMode) bool PU::isTopCccmMode(const PredictionUnit& pu, int intraMode) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cs->sps->getUseCccm() == 0 ) + { + return false; + } + else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 ) ) + { + return false; + } +#endif const Area area = pu.blocks[COMPONENT_Cb]; bool modeIsOk = (intraMode == MDLM_T_IDX); modeIsOk = modeIsOk && (area.width * area.height >= CCCM_MIN_PU_SIZE); diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index 097e40526..e7fbf8aa3 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -2469,6 +2469,12 @@ bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu) #if JVET_AA0057_CCCM void CABACReader::cccmFlag(PredictionUnit& pu) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cs->sps->getUseCccm() == 0 ) + { + return; + } +#endif const unsigned intraDir = pu.intraDir[1]; #if JVET_AB0143_CCCM_TS @@ -2513,6 +2519,13 @@ void CABACReader::cccmFlag(PredictionUnit& pu) pu.cccmFlag = (intraDir == MDLM_T_IDX || intraDir == MMLM_T_IDX) ? 3 : (intraDir == MDLM_L_IDX || intraDir == MMLM_L_IDX) ? 2 : 1; #else pu.cccmFlag = (intraDir == MDLM_T_IDX) ? 3 : (intraDir == MDLM_L_IDX) ? 2 : 1; +#endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + pu.cccmNoSubFlag = 0; + if ( pu.cs->sps->getUseCccm() == 2 ) + { + pu.cccmNoSubFlag += m_BinDecoder.decodeBin( Ctx::CccmFlag( 1 ) ); + } #endif } #endif diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp index c6811834f..bbc6dee5e 100644 --- a/source/Lib/DecoderLib/VLCReader.cpp +++ b/source/Lib/DecoderLib/VLCReader.cpp @@ -2502,6 +2502,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS) #endif #if JVET_AB0155_SGPM READ_FLAG(uiCode, "sps_sgpm_enabled_flag"); pcSPS->setUseSgpm(uiCode != 0); +#endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + READ_UVLC(uiCode, "sps_cccm_cand"); pcSPS->setUseCccm(uiCode); #endif if( pcSPS->getChromaFormatIdc() != CHROMA_400) { diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp index bf3e32091..2fdb6f726 100644 --- a/source/Lib/EncoderLib/CABACWriter.cpp +++ b/source/Lib/EncoderLib/CABACWriter.cpp @@ -2214,6 +2214,12 @@ void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu) #if JVET_AA0057_CCCM void CABACWriter::cccmFlag(const PredictionUnit& pu) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cs->sps->getUseCccm() == 0 ) + { + return; + } +#endif const unsigned intraDir = pu.intraDir[1]; #if JVET_AB0143_CCCM_TS @@ -2251,6 +2257,12 @@ void CABACWriter::cccmFlag(const PredictionUnit& pu) #endif { m_BinEncoder.encodeBin( pu.cccmFlag ? 1 : 0, Ctx::CccmFlag( 0 ) ); +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if ( pu.cccmFlag && ( pu.cs->sps->getUseCccm() == 2 ) ) + { + m_BinEncoder.encodeBin( pu.cccmNoSubFlag ? 1 : 0, Ctx::CccmFlag( 1 ) ); + } +#endif } } #endif diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index 0e8270d5b..9380a9a7d 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -470,6 +470,9 @@ protected: #if JVET_AB0155_SGPM bool m_sgpm; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + int m_cccm; +#endif #if ENABLE_OBMC bool m_OBMC; #endif @@ -1488,6 +1491,10 @@ public: void setUseSgpm (bool b) { m_sgpm = b; } bool getUseSgpm () const { return m_sgpm; } #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + void setUseCccm (int i) { m_cccm = i; } + int getUseCccm () const { return m_cccm; } +#endif #if ENABLE_OBMC void setUseObmc ( bool b ) { m_OBMC = b; } bool getUseObmc () const { return m_OBMC; } diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp index b7296561c..a52e35046 100644 --- a/source/Lib/EncoderLib/EncCu.cpp +++ b/source/Lib/EncoderLib/EncCu.cpp @@ -2403,6 +2403,9 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS #if INTRA_TRANS_ENC_OPT m_pcIntraSearch->m_skipTimdLfnstMtsPass = false; m_modeCtrl->resetLfnstCost(); +#endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + m_pcIntraSearch->m_skipCCCMSATD = false; #endif for( int trGrpIdx = 0; trGrpIdx < grpNumMax; trGrpIdx++ ) { diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 57f16c82a..769ad8c4a 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -1617,6 +1617,9 @@ void EncLib::xInitSPS( SPS& sps ) #if JVET_AB0155_SGPM sps.setUseSgpm ( m_sgpm ); #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + sps.setUseCccm ( m_cccm ); +#endif #if ENABLE_OBMC sps.setUseOBMC ( m_OBMC ); #endif diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp index 7160deb4e..5200d238a 100644 --- a/source/Lib/EncoderLib/IntraSearch.cpp +++ b/source/Lib/EncoderLib/IntraSearch.cpp @@ -180,7 +180,12 @@ void IntraSearch::destroy() #if JVET_AB0143_CCCM_TS for (uint32_t cccmIdx = 0; cccmIdx < CCCM_NUM_MODES; cccmIdx++) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + m_cccmStorage[0][cccmIdx].destroy(); + m_cccmStorage[1][cccmIdx].destroy(); +#else m_cccmStorage[cccmIdx].destroy(); +#endif } #endif @@ -283,7 +288,12 @@ void IntraSearch::init( EncCfg* pcEncCfg, #if JVET_AB0143_CCCM_TS for (uint32_t cccmIdx = 0; cccmIdx < CCCM_NUM_MODES; cccmIdx++) { +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + m_cccmStorage[0][cccmIdx].create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); + m_cccmStorage[1][cccmIdx].create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); +#else m_cccmStorage[cccmIdx].create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); +#endif } #endif #if JVET_AB0155_SGPM @@ -2540,6 +2550,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner int32_t bestBDPCMMode = 0; #if JVET_AA0057_CCCM int cccmModeBest = 0; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + int cccmNoSubBest = 0; +#endif #endif #if JVET_Z0050_CCLM_SLOPE CclmOffsets bestCclmOffsets = {}; @@ -2848,6 +2861,247 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner #else uint32_t chromaCandCccmModes[CCCM_NUM_MODES] = { LM_CHROMA_IDX, MDLM_L_IDX, MDLM_T_IDX }; #endif + +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + int64_t satdCccmSortedCost[2][CCCM_NUM_MODES]; + int satdCccmModeList[2][CCCM_NUM_MODES]; + + for (int i = 0; i < CCCM_NUM_MODES; i++) + { + satdCccmSortedCost[0][i] = LLONG_MAX; // for the mode not pre-select by SATD, do RDO by default, so set the initial value 0. + satdCccmSortedCost[1][i] = LLONG_MAX; // for the mode not pre-select by SATD, do RDO by default, so set the initial value 0. + satdCccmModeList[0][i] = chromaCandCccmModes[i]; + satdCccmModeList[1][i] = chromaCandCccmModes[i]; + } + int64_t bestCccmCost[2] = { LLONG_MAX, LLONG_MAX}; + + bool isCccmFullEnabled = PU::cccmSingleModeAvail(pu, LM_CHROMA_IDX); + bool isCccmLeftEnabled = PU::isLeftCccmMode(pu, MDLM_L_IDX); + bool isCccmTopEnabled = PU::isTopCccmMode(pu, MDLM_T_IDX); +#if MMLM + bool isMultiCccmFullEnabled = PU::cccmMultiModeAvail(pu, MMLM_CHROMA_IDX); + bool isMultiCccmLeftEnabled = PU::cccmMultiModeAvail(pu, MMLM_L_IDX); + bool isMultiCccmTopEnabled = PU::cccmMultiModeAvail(pu, MMLM_T_IDX); +#endif + + const UnitArea localUnitArea(cs.area.chromaFormat, Area(0, 0, (pu.Cb().width) << 1, (pu.Cb().height) << 1)); + PelUnitBuf cccmStorage[2][CCCM_NUM_MODES]; + + pu.cccmFlag = 1; + + for (int sub = 0; sub < pu.cu->slice->getSPS()->getUseCccm(); sub++) + { + pu.cccmNoSubFlag = sub; + + xGetLumaRecPixels(pu, pu.Cb()); + + bool isCCCMEnabled = false; + + for (int idx = 0; idx < CCCM_NUM_MODES; idx++) + { + int mode = chromaCandCccmModes[idx]; + if (idx == 0) + { + isCCCMEnabled = isCccmFullEnabled; + pu.cccmFlag = 1; + } + else if (idx == 1) + { + isCCCMEnabled = isCccmLeftEnabled; + pu.cccmFlag = 2; + } + else if (idx == 2) + { + isCCCMEnabled = isCccmTopEnabled; + pu.cccmFlag = 3; + } +#if MMLM + else if (idx == 3) + { + isCCCMEnabled = isMultiCccmFullEnabled; + pu.cccmFlag = 1; + } + else if (idx == 4) + { + isCCCMEnabled = isMultiCccmLeftEnabled; + pu.cccmFlag = 2; + } + else if (idx == 5) + { + isCCCMEnabled = isMultiCccmTopEnabled; + pu.cccmFlag = 3; + } +#endif + + if (isCCCMEnabled) + { + pu.intraDir[1] = mode; // temporary assigned, for SATD checking. + + if ( ( sub == 1 ) && m_skipCCCMSATD ) + { + if (m_isCccmNoSubModeEnabledInRdo[mode] == 0) + { + continue; + } + } + + int64_t sad = 0; + int64_t sadCb = 0; + int64_t satdCb = 0; + int64_t sadCr = 0; + int64_t satdCr = 0; + CodingStructure& cs = *(pu.cs); + + DistParam distParamSadCb; + DistParam distParamSatdCb; + DistParam distParamSadCr; + DistParam distParamSatdCr; + + cccmStorage[sub][idx] = m_cccmStorage[sub][idx].getBuf(localUnitArea); + + CompArea areaCb = pu.Cb(); + PelBuf orgCb = cs.getOrgBuf(areaCb); + CompArea areaCr = pu.Cr(); + PelBuf orgCr = cs.getOrgBuf(areaCr); + + m_pcRdCost->setDistParam(distParamSadCb, orgCb, cccmStorage[sub][idx].Cb(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, false); + m_pcRdCost->setDistParam(distParamSatdCb, orgCb, cccmStorage[sub][idx].Cb(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, true); + distParamSadCb.applyWeight = false; + distParamSatdCb.applyWeight = false; + m_pcRdCost->setDistParam(distParamSadCr, orgCr, cccmStorage[sub][idx].Cr(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, false); + m_pcRdCost->setDistParam(distParamSatdCr, orgCr, cccmStorage[sub][idx].Cr(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, true); + + distParamSadCr.applyWeight = false; + distParamSatdCr.applyWeight = false; + + predIntraCCCM(pu, cccmStorage[sub][idx].Cb(), cccmStorage[sub][idx].Cr(), mode); + + sadCb = distParamSadCb.distFunc(distParamSadCb) * 2; + satdCb = distParamSatdCb.distFunc(distParamSatdCb); + sad += std::min(sadCb, satdCb); + sadCr = distParamSadCr.distFunc(distParamSadCr) * 2; + satdCr = distParamSatdCr.distFunc(distParamSatdCr); + sad += std::min(sadCr, satdCr); + + satdCccmSortedCost[sub][idx] = sad; + + if (sad < bestCccmCost[sub]) + { + bestCccmCost[sub] = sad; + } + } + } + } + int tempCccmIdx = 0; + int64_t tempCccmCost = 0; +#if MMLM + for (int i = 1; i < 4; i++) +#else + for (int i = 1; i < 3; i++) +#endif + { + for (int j = i + 1; j < CCCM_NUM_MODES; j++) + { + if (satdCccmSortedCost[0][j] < satdCccmSortedCost[0][i]) + { + tempCccmIdx = satdCccmModeList[0][i]; + satdCccmModeList[0][i] = satdCccmModeList[0][j]; + satdCccmModeList[0][j] = tempCccmIdx; + + tempCccmCost = satdCccmSortedCost[0][i]; + satdCccmSortedCost[0][i] = satdCccmSortedCost[0][j]; + satdCccmSortedCost[0][j] = tempCccmCost; + } + } + } + +#if MMLM + bool isCccmModeEnabledInRdo[2][MMLM_T_IDX + 1] = { false }; + isCccmModeEnabledInRdo[0][satdCccmModeList[0][0]] = true; + for (int i = 1; i < 4; i++) +#else + bool isCccmModeEnabledInRdo[MDLM_T_IDX + 1] = { false }; + isCccmModeEnabledInRdo[satdCccmModeList[0]] = true; + for (int i = 1; i < 3; i++) +#endif + { + if (satdCccmSortedCost[0][i] >= 1.15 * bestCccmCost[0]) + { + break; + } + isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] = true; + } + + if (pu.cu->slice->getSPS()->getUseCccm() == 2) + { + if (bestCccmCost[1] < bestCccmCost[0]) + { +#if MMLM + for (int i = 0; i < 4; i++) +#else + for (int i = 0; i < 3; i++) +#endif + { + if (isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] && (satdCccmSortedCost[0][i] >= 1.2 * bestCccmCost[1])) + { + isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] = false; + } + else + { + bestCccmCost[0] = satdCccmSortedCost[0][i]; + } + } + } + else + { + bestCccmCost[1] = bestCccmCost[0]; + } + + tempCccmIdx = 0; + tempCccmCost = 0; + for (int i = 1; i < 2; i++) + { + for (int j = i + 1; j < 6; j++) + { + if (satdCccmSortedCost[1][j] < satdCccmSortedCost[1][i]) + { + tempCccmIdx = satdCccmModeList[1][i]; + satdCccmModeList[1][i] = satdCccmModeList[1][j]; + satdCccmModeList[1][j] = tempCccmIdx; + + tempCccmCost = satdCccmSortedCost[1][i]; + satdCccmSortedCost[1][i] = satdCccmSortedCost[1][j]; + satdCccmSortedCost[1][j] = tempCccmCost; + } + } + } + + isCccmModeEnabledInRdo[1][satdCccmModeList[1][0]] = true; + for (int i = 1; i < 2; i++) + { + if (satdCccmSortedCost[1][i] >= CCCM_NO_SUB_WEIGHT * bestCccmCost[1]) + { + if (satdCccmSortedCost[1][i - 1] > bestCccmCost[0]) + { + bestCccmCost[0] = satdCccmSortedCost[1][i - 1]; + } + bestCccmCost[1] = satdCccmSortedCost[1][i - 1]; + break; + } + isCccmModeEnabledInRdo[1][satdCccmModeList[1][i]] = true; + } + if (m_skipCCCMSATD == false) + { + m_skipCCCMSATD = true; + for (int i = 0; i < (MMLM_T_IDX + 1); i++) + { + m_isCccmNoSubModeEnabledInRdo[i] = isCccmModeEnabledInRdo[1][i]; + } + } + } + pu.cccmFlag = 0; + pu.cccmNoSubFlag = 0; +#else int64_t satdCccmSortedCost[CCCM_NUM_MODES]; int satdCccmModeList[CCCM_NUM_MODES]; @@ -3004,6 +3258,7 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner pu.cccmFlag = 0; #endif +#endif #if MMLM m_encPreRDRun = false; @@ -3036,6 +3291,33 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner { modeIsEnable[satdModeList[uiMaxMode - 1 - i]] = 0; // disable the last reducedModeNumber modes } +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if (pu.cu->slice->getSPS()->getUseCccm() == 2) + { + if (satdSortedCost[uiMaxMode - 1 - reducedModeNumber] > bestCccmCost[0]) + { + modeIsEnable[satdModeList[uiMaxMode - 1 - reducedModeNumber]] = 0; // disable the last reducedModeNumber modes + } + else if (satdSortedCost[uiMaxMode - 1 - reducedModeNumber] < bestCccmCost[0]) + { + for (int i = 3; i > 0; i--) + { + if ((satdCccmSortedCost[0][i] > satdSortedCost[uiMaxMode - 1 - reducedModeNumber]) && (isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]])) + { + isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] = false; + break; + } + } + } + if (satdSortedCost[uiMaxMode - 1 - reducedModeNumber] < bestCccmCost[1]) + { + if ((satdCccmSortedCost[1][1] > satdSortedCost[uiMaxMode - 1 - reducedModeNumber]) && (isCccmModeEnabledInRdo[1][satdCccmModeList[1][1]])) + { + isCccmModeEnabledInRdo[1][satdCccmModeList[1][1]] = false; + } + } + } +#endif // save the dist Distortion baseDist = cs.dist; @@ -3380,8 +3662,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner #if JVET_AA0057_CCCM #if JVET_AB0143_CCCM_TS int chromaIntraModeInCCCM = LM_CHROMA_IDX; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + bool isCCCMEnabled = isCccmFullEnabled; +#else isCCCMEnabled = isCccmFullEnabled; - +#endif pu.cccmFlag = 1; for (int32_t uiMode = 0; uiMode < CCCM_NUM_MODES; uiMode++) @@ -3418,8 +3703,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner pu.cccmFlag = 3; } #endif - +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + if (!isCccmModeEnabledInRdo[0][chromaIntraModeInCCCM] && !isCccmModeEnabledInRdo[1][chromaIntraModeInCCCM]) +#else if (!isCccmModeEnabledInRdo[chromaIntraModeInCCCM]) +#endif { continue; } @@ -3442,6 +3730,16 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner pu.cccmFlag = 1; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + for (int sub = 0; sub < pu.cu->slice->getSPS()->getUseCccm(); sub++) + { + pu.cccmNoSubFlag = sub; + if (!isCccmModeEnabledInRdo[sub][chromaIntraModeInCCCM]) + { + continue; + } +#endif + // Original RD check code replicated from above cs.setDecomp( pu.Cb(), false ); cs.dist = baseDist; @@ -3452,7 +3750,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner #if JVET_AB0143_CCCM_TS pu.intraDir[1] = chromaIntraModeInCCCM; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType, cccmStorage[sub][uiMode]); +#else xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType, cccmStorage[uiMode]); +#endif #else pu.intraDir[1] = chromaIntraMode; @@ -3518,6 +3820,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner #if JVET_AA0126_GLM bestGlmIdc = pu.glmIdc; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + cccmNoSubBest = pu.cccmNoSubFlag; + } +#endif } } } @@ -3557,6 +3863,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner #endif #if JVET_AA0057_CCCM pu.cccmFlag = cccmModeBest; +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + pu.cccmNoSubFlag = cccmNoSubBest; +#endif #endif #if JVET_Z0050_DIMD_CHROMA_FUSION pu.isChromaFusion = isChromaFusion; diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h index eb6607246..ec04a6de3 100644 --- a/source/Lib/EncoderLib/IntraSearch.h +++ b/source/Lib/EncoderLib/IntraSearch.h @@ -489,8 +489,12 @@ private: PelStorage m_tmpStorageLCU; PelStorage m_colorTransResiBuf; #if JVET_AB0143_CCCM_TS +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + PelStorage m_cccmStorage[2][CCCM_NUM_MODES]; +#else PelStorage m_cccmStorage[CCCM_NUM_MODES]; #endif +#endif protected: // interface to option EncCfg* m_pcEncCfg; @@ -524,6 +528,10 @@ public: #if INTRA_TRANS_ENC_OPT bool m_skipTimdLfnstMtsPass; #endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + bool m_skipCCCMSATD; + int m_isCccmNoSubModeEnabledInRdo[MMLM_T_IDX + 1]; +#endif IntraSearch(); ~IntraSearch(); diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp index f33f57216..336324a9d 100644 --- a/source/Lib/EncoderLib/VLCWriter.cpp +++ b/source/Lib/EncoderLib/VLCWriter.cpp @@ -1573,6 +1573,9 @@ void HLSWriter::codeSPS( const SPS* pcSPS ) #endif #if JVET_AB0155_SGPM WRITE_FLAG(pcSPS->getUseSgpm() ? 1 : 0, "sps_sgpm_enabled_flag"); +#endif +#if JVET_AC0147_CCCM_NO_SUBSAMPLING + WRITE_UVLC(pcSPS->getUseCccm() , "sps_cccm_cand"); #endif if( pcSPS->getChromaFormatIdc() != CHROMA_400) { -- GitLab