diff --git a/cfg/per-sequence/Robot_444.cfg b/cfg/per-sequence/Robot_444.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e8495c94387ce92c117befb66b91275c3a60d939 --- /dev/null +++ b/cfg/per-sequence/Robot_444.cfg @@ -0,0 +1,11 @@ +#======== File I/O =============== +InputFile : sc_robot_1280x720_30_8bit_300_444.yuv +InputBitDepth : 8 # Input bitdepth +InputChromaFormat : 444 # Ratio of luminance to chrominance samples +FrameRate : 30 # Frame Rate per second +FrameSkip : 0 # Number of frames to be skipped in input +SourceWidth : 1280 # Input frame width +SourceHeight : 720 # Input frame height +FramesToBeEncoded : 300 # Number of frames to be coded + +Level : 6.2 diff --git a/cfg/per-sequence/Robot_RGB.cfg b/cfg/per-sequence/Robot_RGB.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6fc4be981268717be82746b1119b1431680b034a --- /dev/null +++ b/cfg/per-sequence/Robot_RGB.cfg @@ -0,0 +1,14 @@ +#======== File I/O =============== +InputFile : sc_robot_1280x720_30_8bit_300.rgb +InputBitDepth : 8 # Input bitdepth +InputChromaFormat : 444 # Ratio of luminance to chrominance samples +FrameRate : 30 # Frame Rate per second +FrameSkip : 0 # Number of frames to be skipped in input +SourceWidth : 1280 # Input frame width +SourceHeight : 720 # Input frame height +FramesToBeEncoded : 300 # Number of frames to be coded +InputColourSpaceConvert : RGBtoGBR # Non-normative colour space conversion to apply to input video +SNRInternalColourSpace : 1 # Evaluate SNRs in GBR order +OutputInternalColourSpace : 0 # Convert recon output back to RGB order. Use --OutputColourSpaceConvert GBRtoRGB on decoder to produce a matching output file. + +Level : 6.2 diff --git a/doc/software-manual.tex b/doc/software-manual.tex index 6f687cfb4eb51f699cc03bc7ff2dd63e8e5ab275..bc1a03fab5775f02cecae3307eb799648e6f0284 100755 --- a/doc/software-manual.tex +++ b/doc/software-manual.tex @@ -2365,6 +2365,12 @@ Adaptive LMCS mapping derivation options: Options 1 to 4 are for experimental te LMCS initial total codeword (valid values [$0 - 1023$]) to be used in LMCS mapping derivation when LMCSAdpOption is not equal to 0. \\ +\Option{ColorTransform} & +%\ShortOption{\None} & +\Default{false} & +Enables or disables the use of adaptive color transform (ACT). +\\ + \end{OptionTableNoShorthand} %% diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 6a645fe736d0f4b99d5ccf8bdb6fcc39bd900bd8..ca11a602377208fdc117b6b69190b344f82b4242 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -345,6 +345,10 @@ void EncApp::xInitLibCfg() m_cEncLib.setDMVR ( m_DMVR ); m_cEncLib.setMMVD ( m_MMVD ); m_cEncLib.setMmvdDisNum (m_MmvdDisNum); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_cEncLib.setRGBFormatFlag(m_rgbFormat); + m_cEncLib.setUseColorTrans(m_useColorTrans); +#endif m_cEncLib.setPLTMode ( m_PLTMode ); m_cEncLib.setJointCbCr ( m_JointCbCrMode ); m_cEncLib.setIBCMode ( m_IBCMode ); diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 929c39cda09471f81382ed53e0d1e60a07327ad8..8b4b0af309ad73488e632d43c4e96844c6c8be11 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -983,6 +983,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("AffineAmvrEncOpt", m_AffineAmvrEncOpt, false, "Enable encoder optimization of affine AMVR") ("DMVR", m_DMVR, false, "Decoder-side Motion Vector Refinement") ("MmvdDisNum", m_MmvdDisNum, 8, "Number of MMVD Distance Entries") +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + ("ColorTransform", m_useColorTrans, false, "Enable the color transform") +#endif ("PLT", m_PLTMode, 0u, "PLTMode (0x1:enabled, 0x0:disabled) [default: disabled]") ("JointCbCr", m_JointCbCrMode, false, "Enable joint coding of chroma residuals (JointCbCr, 0:off, 1:on)") ( "IBC", m_IBCMode, 0u, "IBCMode (0x1:enabled, 0x0:disabled) [default: disabled]") @@ -1808,6 +1811,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) m_inputColourSpaceConvert = stringToInputColourSpaceConvert(inputColourSpaceConvert, true); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_rgbFormat = (m_inputColourSpaceConvert == IPCOLOURSPACE_RGBtoGBR && m_chromaFormatIDC == CHROMA_444) ? true : false; +#endif switch (m_conformanceWindowMode) { @@ -2490,6 +2496,9 @@ bool EncAppCfg::xCheckParameter() #endif xConfirmPara( m_LMChroma, "LMChroma only allowed with NEXT profile" ); xConfirmPara( m_ImvMode, "IMV is only allowed with NEXT profile" ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + xConfirmPara(m_useColorTrans, "ACT Mode only allowed with NEXT profile"); +#endif xConfirmPara( m_PLTMode, "PLT Mode only allowed with NEXT profile"); xConfirmPara(m_IBCMode, "IBC Mode only allowed with NEXT profile"); xConfirmPara( m_HashME, "Hash motion estimation only allowed with NEXT profile" ); @@ -3786,6 +3795,10 @@ void EncAppCfg::xPrintParameter() msg(VERBOSE, "MmvdDisNum:%d ", m_MmvdDisNum); msg(VERBOSE, "JointCbCr:%d ", m_JointCbCrMode); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_useColorTrans = (m_chromaFormatIDC == CHROMA_444 && m_costMode != COST_LOSSLESS_CODING) ? m_useColorTrans : 0u; + msg(VERBOSE, "ACT:%d ", m_useColorTrans); +#endif m_PLTMode = ( m_chromaFormatIDC == CHROMA_444) ? m_PLTMode : 0u; msg(VERBOSE, "PLT:%d ", m_PLTMode); msg(VERBOSE, "IBC:%d ", m_IBCMode); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 6e3f778f431bd4f0776470bbd76b4eac146d94ed..db4244e4ce1a20a83f6a36834bb20109cef819eb 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -317,6 +317,10 @@ protected: bool m_DMVR; bool m_MMVD; int m_MmvdDisNum; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool m_rgbFormat; + bool m_useColorTrans; +#endif unsigned m_PLTMode; bool m_JointCbCrMode; #if JVET_P0058_CHROMA_TS diff --git a/source/Lib/CommonLib/Buffer.cpp b/source/Lib/CommonLib/Buffer.cpp index 586f8728f27d08de6ab6402c1bfc3ab34668b687..5bd275e4cf0aef33930cfe17a09489c367192a0b 100644 --- a/source/Lib/CommonLib/Buffer.cpp +++ b/source/Lib/CommonLib/Buffer.cpp @@ -920,3 +920,76 @@ const CPelUnitBuf PelStorage::getBuf( const UnitArea &unit ) const return ( chromaFormat == CHROMA_400 ) ? CPelUnitBuf( chromaFormat, getBuf( unit.Y() ) ) : CPelUnitBuf( chromaFormat, getBuf( unit.Y() ), getBuf( unit.Cb() ), getBuf( unit.Cr() ) ); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +template<> +void UnitBuf<Pel>::colorSpaceConvert(const UnitBuf<Pel> &other, const bool forward) +{ + const Pel* pOrg0 = bufs[COMPONENT_Y].buf; + const Pel* pOrg1 = bufs[COMPONENT_Cb].buf; + const Pel* pOrg2 = bufs[COMPONENT_Cr].buf; + const int strideOrg = bufs[COMPONENT_Y].stride; + + Pel* pDst0 = other.bufs[COMPONENT_Y].buf; + Pel* pDst1 = other.bufs[COMPONENT_Cb].buf; + Pel* pDst2 = other.bufs[COMPONENT_Cr].buf; + const int strideDst = other.bufs[COMPONENT_Y].stride; + + int width = bufs[COMPONENT_Y].width; + int height = bufs[COMPONENT_Y].height; + int r, g, b; + int y0, cg, co; + + CHECK(bufs[COMPONENT_Y].stride != bufs[COMPONENT_Cb].stride || bufs[COMPONENT_Y].stride != bufs[COMPONENT_Cr].stride, "unequal stride for 444 content"); + CHECK(other.bufs[COMPONENT_Y].stride != other.bufs[COMPONENT_Cb].stride || other.bufs[COMPONENT_Y].stride != other.bufs[COMPONENT_Cr].stride, "unequal stride for 444 content"); + CHECK(bufs[COMPONENT_Y].width != other.bufs[COMPONENT_Y].width || bufs[COMPONENT_Y].height != other.bufs[COMPONENT_Y].height, "unequal block size") + + if (forward) + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + r = pOrg2[x]; + g = pOrg0[x]; + b = pOrg1[x]; + + pDst0[x] = (g << 1) + r + b; + pDst1[x] = (g << 1) - r - b; + pDst2[x] = ((r - b) << 1); + pDst0[x] = (pDst0[x] + 2) >> 2; + pDst1[x] = (pDst1[x] + 2) >> 2; + pDst2[x] = (pDst2[x] + 2) >> 2; + } + pOrg0 += strideOrg; + pOrg1 += strideOrg; + pOrg2 += strideOrg; + pDst0 += strideDst; + pDst1 += strideDst; + pDst2 += strideDst; + } + } + else + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + y0 = pOrg0[x]; + cg = pOrg1[x]; + co = pOrg2[x]; + + pDst0[x] = (y0 + cg); + pDst1[x] = (y0 - cg - co); + pDst2[x] = (y0 - cg + co); + } + + pOrg0 += strideOrg; + pOrg1 += strideOrg; + pOrg2 += strideOrg; + pDst0 += strideDst; + pDst1 += strideDst; + pDst2 += strideDst; + } + } +} +#endif \ No newline at end of file diff --git a/source/Lib/CommonLib/Buffer.h b/source/Lib/CommonLib/Buffer.h index d71cd94f2b10f6db1a37854ae5cae7eb134514d5..46f26ace336ed8cb6143675596d682c6c7da2685 100644 --- a/source/Lib/CommonLib/Buffer.h +++ b/source/Lib/CommonLib/Buffer.h @@ -758,6 +758,9 @@ struct UnitBuf UnitBuf< T> subBuf (const UnitArea& subArea); const UnitBuf<const T> subBuf (const UnitArea& subArea) const; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void colorSpaceConvert(const UnitBuf<T> &other, const bool forward); +#endif }; typedef UnitBuf< Pel> PelUnitBuf; @@ -873,6 +876,17 @@ void UnitBuf<T>::addAvg(const UnitBuf<const T> &other1, const UnitBuf<const T> & } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +template<typename T> +void UnitBuf<T>::colorSpaceConvert(const UnitBuf<T> &other, const bool forward) +{ + THROW("Type not supported"); +} + +template<> +void UnitBuf<Pel>::colorSpaceConvert(const UnitBuf<Pel> &other, const bool forward); +#endif + template<typename T> void UnitBuf<T>::extendSingleBorderPel() { diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp index c942c5828c37c1afbb65584e3c2f836c694b5828..1928eb9377999d5bb134280f6a3a8c0972a9ef4b 100644 --- a/source/Lib/CommonLib/CodingStructure.cpp +++ b/source/Lib/CommonLib/CodingStructure.cpp @@ -68,6 +68,10 @@ CodingStructure::CodingStructure(CUCache& cuCache, PUCache& puCache, TUCache& tu , m_puCache ( puCache ) , m_tuCache ( tuCache ) , bestParent ( nullptr ) +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + , tmpColorSpaceCost(MAX_DOUBLE) + , firstColorSpaceSelected(true) +#endif , resetIBCBuffer (false) { for( uint32_t i = 0; i < MAX_NUM_COMPONENT; i++ ) @@ -93,6 +97,11 @@ CodingStructure::CodingStructure(CUCache& cuCache, PUCache& puCache, TUCache& tu features.resize( NUM_ENC_FEATURES ); treeType = TREE_D; modeType = MODE_TYPE_ALL; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + tmpColorSpaceIntraCost[0] = MAX_DOUBLE; + tmpColorSpaceIntraCost[1] = MAX_DOUBLE; + firstColorSpaceTestOnly = false; +#endif } void CodingStructure::destroy() diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h index d017a250beba35fbacb6fc4a2411bc1d0a7ea4ce..8bf19a426447ce4f680450a0f62852ac83cbf9af 100644 --- a/source/Lib/CommonLib/CodingStructure.h +++ b/source/Lib/CommonLib/CodingStructure.h @@ -243,6 +243,12 @@ private: public: CodingStructure *bestParent; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + double tmpColorSpaceCost; + bool firstColorSpaceSelected; + double tmpColorSpaceIntraCost[2]; + bool firstColorSpaceTestOnly; +#endif bool resetIBCBuffer; MotionBuf getMotionBuf( const Area& _area ); diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index 365e8f6d294df2923148d2dd275c81d688edc489..acea627bf414388b56b5dea4c68aa8600292bb92 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -496,6 +496,10 @@ static const int ENC_PPS_ID_RPR = 3; static const int SCALE_RATIO_BITS = 14; static const int MAX_SCALING_RATIO = 8; // max scaling ratio allowed in the software, it is used to allocated an internla buffer in the rescaling static const std::pair<int, int> SCALE_1X = std::pair<int, int>( 1 << SCALE_RATIO_BITS, 1 << SCALE_RATIO_BITS ); // scale ratio 1x +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +static const int DELTA_QP_FOR_Y_Cg = -5; +static const int DELTA_QP_FOR_Co = -3; +#endif // ==================================================================================================================== // Macro functions diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp index 3280c087b967de6ece82dac6bf0b424f7e688205..b04af79396d40b3c042ee61e2cefdcb257db4400 100644 --- a/source/Lib/CommonLib/Contexts.cpp +++ b/source/Lib/CommonLib/Contexts.cpp @@ -451,6 +451,16 @@ const CtxSet ContextSetCfg::QtRootCbf = ContextSetCfg::addCtxSet { 4, }, }); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +const CtxSet ContextSetCfg::ACTFlag = ContextSetCfg::addCtxSet +({ + { CNU, }, + { CNU, }, + { CNU, }, + { DWS, }, + }); +#endif + const CtxSet ContextSetCfg::QtCbf[] = { ContextSetCfg::addCtxSet @@ -1216,6 +1226,16 @@ const CtxSet ContextSetCfg::QtRootCbf = ContextSetCfg::addCtxSet { 4, }, }); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +const CtxSet ContextSetCfg::ACTFlag = ContextSetCfg::addCtxSet +({ + { CNU, }, + { CNU, }, + { CNU, }, + { DWS, }, + }); +#endif + const CtxSet ContextSetCfg::QtCbf[] = { ContextSetCfg::addCtxSet diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h index 519f6ac646ccd1f02df8fdb9aeae26c648f20a15..c793149f1fac7014d7a36c618c48d8712ab56782 100644 --- a/source/Lib/CommonLib/Contexts.h +++ b/source/Lib/CommonLib/Contexts.h @@ -226,6 +226,9 @@ public: static const CtxSet Mvd; static const CtxSet BDPCMMode; static const CtxSet QtRootCbf; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + static const CtxSet ACTFlag; +#endif static const CtxSet QtCbf [3]; // [ channel ] static const CtxSet SigCoeffGroup [2]; // [ ChannelType ] static const CtxSet LastX [2]; // [ ChannelType ] diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp index da35d1054a71f5d8edeb5a55a4c5ab15df5afdb6..ed4092ff381bb9a109d3e58bd8435c773bec9b71 100644 --- a/source/Lib/CommonLib/InterPrediction.cpp +++ b/source/Lib/CommonLib/InterPrediction.cpp @@ -127,6 +127,11 @@ void InterPrediction::destroy() } m_triangleBuf.destroy(); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_colorTransResiBuf[0].destroy(); + m_colorTransResiBuf[1].destroy(); + m_colorTransResiBuf[2].destroy(); +#endif if (m_storedMv != nullptr) { @@ -190,6 +195,11 @@ void InterPrediction::init( RdCost* pcRdCost, ChromaFormat chromaFormatIDC, cons } m_triangleBuf.create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_colorTransResiBuf[0].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); + m_colorTransResiBuf[1].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); + m_colorTransResiBuf[2].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); +#endif m_iRefListIdx = -1; diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h index 42a76a9115f1f36be3abd77296a429a9da9dcd92..924186262392a6f3c27b96ec388c6687febb457e 100644 --- a/source/Lib/CommonLib/InterPrediction.h +++ b/source/Lib/CommonLib/InterPrediction.h @@ -180,6 +180,10 @@ protected: #if JVET_J0090_MEMORY_BANDWITH_MEASURE CacheModel *m_cacheModel; #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + PelStorage m_colorTransResiBuf[3]; // 0-org; 1-act; 2-tmp +#endif + public: InterPrediction(); virtual ~InterPrediction(); diff --git a/source/Lib/CommonLib/Quant.cpp b/source/Lib/CommonLib/Quant.cpp index 27eb90af5b35abb685dc4b92a2ac3b8185ea954c..9886fc514edab8f20166624a246f95bc1babf33f 100644 --- a/source/Lib/CommonLib/Quant.cpp +++ b/source/Lib/CommonLib/Quant.cpp @@ -530,6 +530,9 @@ void Quant::init( uint32_t uiMaxTrSize, #if T0196_SELECTIVE_RDOQ m_useSelectiveRDOQ = useSelectiveRDOQ; #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_resetStore = true; +#endif } #if ENABLE_SPLIT_PARALLELISM @@ -1044,6 +1047,10 @@ void Quant::xInitScalingList( const Quant* other ) } } } + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_pairCheck = 0; +#endif } /** destroy quantization matrix array @@ -1436,5 +1443,38 @@ void Quant::invTrSkipDeQuantOneSample(TransformUnit &tu, const ComponentID &comp #endif } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void Quant::lambdaAdjustColorTrans(bool forward) +{ + if (m_resetStore) + { + for (uint8_t component = 0; component < MAX_NUM_COMPONENT; component++) + { + ComponentID compID = (ComponentID)component; + int delta_QP = (compID == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + double lamdbaAdjustRate = pow(2.0, delta_QP / 3.0); + m_lambdasStore[0][component] = m_lambdas[component]; + m_lambdasStore[1][component] = m_lambdas[component] * lamdbaAdjustRate; + } + m_resetStore = false; + } + + if (forward) + { + CHECK(m_pairCheck == 1, "lambda has been already adjusted"); + m_pairCheck = 1; + } + else + { + CHECK(m_pairCheck == 0, "lambda has not been adjusted"); + m_pairCheck = 0; + } + + for (uint8_t component = 0; component < MAX_NUM_COMPONENT; component++) + { + m_lambdas[component] = m_lambdasStore[m_pairCheck][component]; + } +} +#endif //! \} diff --git a/source/Lib/CommonLib/Quant.h b/source/Lib/CommonLib/Quant.h index 89c0d6596019e10d45a269161b75f7aa72e2930d..b4a6aa10948fce4d8488d251529db5f829e2deca 100644 --- a/source/Lib/CommonLib/Quant.h +++ b/source/Lib/CommonLib/Quant.h @@ -67,6 +67,9 @@ struct TrQuantParams /// QP struct class QpParam { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +public: +#endif int Qps[2]; int pers[2]; int rems[2]; @@ -121,6 +124,10 @@ public: #endif void setLambda ( const double dLambda ) { m_dLambda = dLambda; } double getLambda () const { return m_dLambda; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void lambdaAdjustColorTrans(bool forward); + void resetStore() { m_resetStore = true; } +#endif int* getQuantCoeff ( uint32_t list, int qp, uint32_t sizeX, uint32_t sizeY ) { return m_quantCoef [sizeX][sizeY][list][qp]; }; //!< get Quant Coefficent int* getDequantCoeff ( uint32_t list, int qp, uint32_t sizeX, uint32_t sizeY ) { return m_dequantCoef [sizeX][sizeY][list][qp]; }; //!< get DeQuant Coefficent @@ -184,12 +191,20 @@ private: private: #if RDOQ_CHROMA_LAMBDA double m_lambdas[MAX_NUM_COMPONENT]; +#endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + double m_lambdasStore[2][MAX_NUM_COMPONENT]; // 0-org; 1-act + bool m_resetStore; #endif bool m_scalingListEnabledFlag; bool m_isScalingListOwner; int *m_quantCoef [SCALING_LIST_SIZE_NUM][SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM][SCALING_LIST_REM_NUM]; ///< array of quantization matrix coefficient 4x4 int *m_dequantCoef [SCALING_LIST_SIZE_NUM][SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM][SCALING_LIST_REM_NUM]; ///< array of dequantization matrix coefficient 4x4 + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + int m_pairCheck; +#endif };// END CLASS DEFINITION Quant diff --git a/source/Lib/CommonLib/RdCost.cpp b/source/Lib/CommonLib/RdCost.cpp index 0b384017a8d679f1d80c6fe6fb123f8ba440a5a1..7a028186b4fdb2a504a626ec28fd3a418aa8f213 100644 --- a/source/Lib/CommonLib/RdCost.cpp +++ b/source/Lib/CommonLib/RdCost.cpp @@ -92,6 +92,45 @@ void RdCost::setLambda( double dLambda, const BitDepths &bitDepths ) m_dLambdaMotionSAD[1] = sqrt(dLambda); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void RdCost::lambdaAdjustColorTrans(bool forward, ComponentID componentID) +{ + if (m_resetStore) + { + for (uint8_t component = 0; component < MAX_NUM_COMPONENT; component++) + { + ComponentID compID = (ComponentID)component; + int delta_QP = (compID == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + double lamdbaAdjustRate = pow(2.0, delta_QP / 3.0); + + m_lambdaStore[0][component] = m_dLambda; + m_DistScaleStore[0][component] = m_DistScale; + + m_lambdaStore[1][component] = m_dLambda * lamdbaAdjustRate; + m_DistScaleStore[1][component] = double(1 << SCALE_BITS) / m_lambdaStore[1][component]; + } + m_resetStore = false; + } + + if (forward) + { + CHECK(m_pairCheck == 1, "lambda has been already adjusted"); + m_pairCheck = 1; + } + else + { + CHECK(m_pairCheck == 0, "lambda has not been adjusted"); + m_pairCheck = 0; + } + + m_dLambda = m_lambdaStore[m_pairCheck][componentID]; + m_DistScale = m_DistScaleStore[m_pairCheck][componentID]; + if (m_pairCheck == 0) + { + CHECK(m_DistScale != m_DistScaleUnadjusted, "lambda should be adjusted to the original value"); + } +} +#endif // Initialize Function Pointer by [eDFunc] void RdCost::init() @@ -181,6 +220,10 @@ void RdCost::init() m_motionLambda = 0; m_iCostScale = 0; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_resetStore = true; + m_pairCheck = 0; +#endif } diff --git a/source/Lib/CommonLib/RdCost.h b/source/Lib/CommonLib/RdCost.h index e6421d8b7c6f84ced1b89807b89db8923cdcfb5e..94fbaef5099a61c9b1f65930e1ca1eb920e25161 100644 --- a/source/Lib/CommonLib/RdCost.h +++ b/source/Lib/CommonLib/RdCost.h @@ -117,6 +117,12 @@ private: #endif double m_DistScale; double m_dLambdaMotionSAD[2 /* 0=standard, 1=for transquant bypass when mixed-lossless cost evaluation enabled*/]; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + double m_lambdaStore[2][3]; // 0-org; 1-act + double m_DistScaleStore[2][3]; // 0-org; 1-act + bool m_resetStore; + int m_pairCheck; +#endif // for motion cost Mv m_mvPredictor; @@ -306,6 +312,11 @@ public: inline std::vector<double>& getLumaLevelWeightTable () { return m_lumaLevelToWeightPLUT; } #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void lambdaAdjustColorTrans(bool forward, ComponentID compID); + void resetStore() { m_resetStore = true; } +#endif + private: static Distortion xGetSSE ( const DistParam& pcDtParam ); diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index 525470cfbd9fea89dfce6905bef1a28a48f48249..abc712d29bbddc445bb6ee526b3e4b4fc3e76cea 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -999,6 +999,9 @@ private: bool m_wrapAroundEnabledFlag; unsigned m_wrapAroundOffset; unsigned m_IBCFlag; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool m_useColorTrans; +#endif unsigned m_PLTMode; bool m_lumaReshapeEnable; @@ -1251,6 +1254,10 @@ public: bool getUseReshaper() const { return m_lumaReshapeEnable; } void setIBCFlag(unsigned IBCFlag) { m_IBCFlag = IBCFlag; } unsigned getIBCFlag() const { return m_IBCFlag; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void setUseColorTrans(bool value) { m_useColorTrans = value; } + bool getUseColorTrans() const { return m_useColorTrans; } +#endif void setPLTMode(unsigned PLTMode) { m_PLTMode = PLTMode; } unsigned getPLTMode() const { return m_PLTMode; } void setUseSBT( bool b ) { m_SBT = b; } diff --git a/source/Lib/CommonLib/TrQuant.h b/source/Lib/CommonLib/TrQuant.h index f2afdbb0862893d7a82685fc7d59fa49099e8445..369b4bb0dc1bfc8012d748571a1e962b7be43a3f 100644 --- a/source/Lib/CommonLib/TrQuant.h +++ b/source/Lib/CommonLib/TrQuant.h @@ -115,7 +115,10 @@ public: double getLambda () const { return m_quant->getLambda(); } DepQuant* getQuant() { return m_quant; } - +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void lambdaAdjustColorTrans(bool forward) { m_quant->lambdaAdjustColorTrans(forward); } + void resetStore() { m_quant->resetStore(); } +#endif #if ENABLE_SPLIT_PARALLELISM void copyState( const TrQuant& other ); diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 2ecb6695793dc05830e69573497a97cdcbe768a2..e730ceb6ed05f01dadb065b9191a7a70913f8dd5 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -112,6 +112,8 @@ #define JVET_P1000_REMOVE_TRANFORMSHIFT_IN_TS_MODE 1 // JVET-P1000: Remove Transformshift in TS mode +#define JVET_P0517_ADAPTIVE_COLOR_TRANSFORM 1 // JVET-P0517: adaptive color transform + #define JVET_P0090_32BIT_MVD 1 // JVET-P0090: Limitation of abs_mvd_min2 binarization within 32-bit #define JVET_P0298_DISABLE_LEVELMAPPING_IN_BYPASS 1 // JVET-P0298: Disable level mapping in bypass mode diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp index 81b6b703c1ec2d98de300e48d12d7c7065c351a6..6db4bbe4f96e8fb0a887695b3f0c32cb835a3cbe 100644 --- a/source/Lib/CommonLib/Unit.cpp +++ b/source/Lib/CommonLib/Unit.cpp @@ -265,6 +265,9 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other ) mmvdSkip = other.mmvdSkip; affine = other.affine; affineType = other.affineType; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + colorTransform = other.colorTransform; +#endif triangle = other.triangle; transQuantBypass = other.transQuantBypass; bdpcmMode = other.bdpcmMode; @@ -319,6 +322,9 @@ void CodingUnit::initData() mmvdSkip = false; affine = false; affineType = 0; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + colorTransform = false; +#endif triangle = false; transQuantBypass = false; bdpcmMode = 0; diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h index 01d498bb12e0f240d82a55dcd964fa8b33f5b078..872fa18a78e3dbdf1d9e2225ca3d4f6991d11b1c 100644 --- a/source/Lib/CommonLib/Unit.h +++ b/source/Lib/CommonLib/Unit.h @@ -308,6 +308,9 @@ struct CodingUnit : public UnitArea bool mmvdSkip; bool affine; int affineType; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool colorTransform; +#endif bool triangle; bool transQuantBypass; int bdpcmMode; diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index 3c22783a1c8ea9481387ba46cbde96693ced30f3..d3ebef1164d303b589ea5820ce412a1c3acbdf28 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -853,6 +853,9 @@ bool CABACReader::coding_unit( CodingUnit &cu, Partitioner &partitioner, CUCtx& // skip data if( cu.skip ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cu.colorTransform = false; +#endif cs.addTU ( cu, partitioner.chType ); #if !JVET_P0400_REMOVE_SHARED_MERGE_LIST pu.shareParentPos = cu.shareParentPos; @@ -865,8 +868,17 @@ bool CABACReader::coding_unit( CodingUnit &cu, Partitioner &partitioner, CUCtx& // prediction mode and partitioning data pred_mode ( cu ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (CU::isIntra(cu)) + { + adaptive_color_transform(cu); + } +#endif if (CU::isPLT(cu)) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cu.colorTransform = false; +#endif cs.addTU(cu, partitioner.chType); if (cu.isSepTree()) { @@ -1508,6 +1520,14 @@ bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu) 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); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (pu.cu->colorTransform) + { + pu.intraDir[CHANNEL_TYPE_CHROMA] = DM_CHROMA_IDX; + return; + } +#endif + // LM chroma mode #if JVET_P0059_CHROMA_BDPCM @@ -1566,6 +1586,9 @@ void CABACReader::cu_residual( CodingUnit& cu, Partitioner &partitioner, CUCtx& } if( !cu.rootCbf ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cu.colorTransform = false; +#endif TransformUnit& tu = cu.cs->addTU(cu, partitioner.chType); tu.depth = 0; for( unsigned c = 0; c < tu.blocks.size(); c++ ) @@ -1578,6 +1601,14 @@ void CABACReader::cu_residual( CodingUnit& cu, Partitioner &partitioner, CUCtx& return; } } + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (CU::isInter(cu) || CU::isIBC(cu)) + { + adaptive_color_transform(cu); + } +#endif + cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_LUMA] = false; cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_CHROMA] = false; cuCtx.lfnstLastScanPos = false; @@ -1611,6 +1642,26 @@ void CABACReader::rqt_root_cbf( CodingUnit& cu ) DTRACE( g_trace_ctx, D_SYNTAX, "rqt_root_cbf() ctx=0 root_cbf=%d pos=(%d,%d)\n", cu.rootCbf ? 1 : 0, cu.lumaPos().x, cu.lumaPos().y ); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void CABACReader::adaptive_color_transform(CodingUnit& cu) +{ + if (!cu.slice->getSPS()->getUseColorTrans()) + { + return; + } + + if (cu.isSepTree()) + { + return; + } + + if (CU::isInter(cu) || CU::isIBC(cu) || CU::isIntra(cu)) + { + cu.colorTransform = (m_BinDecoder.decodeBin(Ctx::ACTFlag())); + } +} +#endif + void CABACReader::sbt_mode( CodingUnit& cu ) { const uint8_t sbtAllowed = cu.checkAllowedSbt(); @@ -3076,9 +3127,16 @@ void CABACReader::transform_unit( TransformUnit& tu, CUCtx& cuCtx, Partitioner& } else { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool lumaCbfIsInferredACT = (cu.colorTransform && cu.predMode == MODE_INTRA && trDepth == 0 && !chromaCbfs.sigChroma(area.chromaFormat)); + bool lastCbfIsInferred = lumaCbfIsInferredACT; // ISP and ACT are mutually exclusive + bool previousCbf = false; + bool rootCbfSoFar = false; +#else bool previousCbf = false; bool rootCbfSoFar = false; - bool lastCbfIsInferred = false; + bool lastCbfIsInferred = false; +#endif if (cu.ispMode) { uint32_t nTus = cu.ispMode == HOR_INTRA_SUBPARTITIONS ? cu.lheight() >> floorLog2(tu.lheight()) : cu.lwidth() >> floorLog2(tu.lwidth()); @@ -3513,7 +3571,11 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID ) void CABACReader::isp_mode( CodingUnit& cu ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || !cu.cs->sps->getUseISP() || cu.bdpcmMode || !CU::canUseISP( cu, getFirstComponentOfChannel( cu.chType ) ) || cu.colorTransform ) +#else if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || !cu.cs->sps->getUseISP() || cu.bdpcmMode || !CU::canUseISP( cu, getFirstComponentOfChannel( cu.chType ) ) ) +#endif { cu.ispMode = NOT_INTRA_SUBPARTITIONS; return; @@ -4281,6 +4343,7 @@ void CABACReader::mip_flag( CodingUnit& cu ) cu.mipFlag = false; return; } + #if !JVET_P0803_COMBINED_MIP_CLEANUP if( cu.lwidth() > cu.cs->sps->getMaxTbSize() || cu.lheight() > cu.cs->sps->getMaxTbSize()) { diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h index 95bb0abcacef1614351f28441832d41c3b69cfdc..190794e65e32cdd7535db1153e4eeeea17b33604 100644 --- a/source/Lib/DecoderLib/CABACReader.h +++ b/source/Lib/DecoderLib/CABACReader.h @@ -94,6 +94,9 @@ public: void intra_chroma_pred_mode ( PredictionUnit& pu ); void cu_residual ( CodingUnit& cu, Partitioner& pm, CUCtx& cuCtx ); void rqt_root_cbf ( CodingUnit& cu ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void adaptive_color_transform(CodingUnit& cu); +#endif void sbt_mode ( CodingUnit& cu ); bool end_of_ctu ( CodingUnit& cu, CUCtx& cuCtx ); void mip_flag ( CodingUnit& cu ); diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp index 59e452134a3f5f9f2c8d0e10c58b1bb6f0f5e759..90c4ba07b5250fce678947031d002e355600eb3d 100644 --- a/source/Lib/DecoderLib/DecCu.cpp +++ b/source/Lib/DecoderLib/DecCu.cpp @@ -379,6 +379,157 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID ) #endif } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void DecCu::xIntraRecACTBlk(TransformUnit& tu) +{ + CodingStructure &cs = *tu.cs; + const PredictionUnit &pu = *tu.cs->getPU(tu.blocks[COMPONENT_Y], CHANNEL_TYPE_LUMA); + const Slice &slice = *cs.slice; + + CHECK(!tu.Y().valid() || !tu.Cb().valid() || !tu.Cr().valid(), "Invalid TU"); + CHECK(&pu != tu.cu->firstPU, "wrong PU fetch"); + CHECK(tu.cu->ispMode, "adaptive color transform cannot be applied to ISP"); + CHECK(pu.intraDir[CHANNEL_TYPE_CHROMA] != DM_CHROMA_IDX, "chroma should use DM mode for adaptive color transform"); + +#if JVET_P1006_PICTURE_HEADER + bool flag = slice.getPicHeader()->getLmcsEnabledFlag() && (slice.isIntra() || (!slice.isIntra() && m_pcReshape->getCTUFlag())); + if (flag && slice.getPicHeader()->getLmcsChromaResidualScaleFlag() && (tu.cbf[COMPONENT_Cb] || tu.cbf[COMPONENT_Cr])) +#else + bool flag = slice.getLmcsEnabledFlag() && (slice.isIntra() || (!slice.isIntra() && m_pcReshape->getCTUFlag())); + if (flag && slice.getLmcsChromaResidualScaleFlag() && (tu.cbf[COMPONENT_Cb] || tu.cbf[COMPONENT_Cr])) +#endif + { + const Area area = tu.Y().valid() ? tu.Y() : Area(recalcPosition(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].pos()), recalcSize(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].size())); + const CompArea &areaY = CompArea(COMPONENT_Y, tu.chromaFormat, area); + int adj = m_pcReshape->calculateChromaAdjVpduNei(tu, areaY); + tu.setChromaAdj(adj); + } + + for (int i = 0; i < getNumberValidComponents(tu.chromaFormat); i++) + { + ComponentID compID = (ComponentID)i; + const CompArea &area = tu.blocks[compID]; + const ChannelType chType = toChannelType(compID); + + PelBuf piPred = cs.getPredBuf(area); + m_pcIntraPred->initIntraPatternChType(*tu.cu, area); + if (PU::isMIP(pu, chType)) + { +#if JVET_P0803_COMBINED_MIP_CLEANUP + m_pcIntraPred->initIntraMip(pu, area); +#else + m_pcIntraPred->initIntraMip(pu); +#endif + m_pcIntraPred->predIntraMip(compID, piPred, pu); + } + else + { + m_pcIntraPred->predIntraAng(compID, piPred, pu); + } + + PelBuf piResi = cs.getResiBuf(area); + + QpParam cQP(tu, compID); + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + cQP.Qps[qpIdx] = cQP.Qps[qpIdx] + (compID == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + cQP.pers[qpIdx] = cQP.Qps[qpIdx] / 6; + cQP.rems[qpIdx] = cQP.Qps[qpIdx] % 6; + } + + if (tu.jointCbCr && isChroma(compID)) + { + if (compID == COMPONENT_Cb) + { + PelBuf resiCr = cs.getResiBuf(tu.blocks[COMPONENT_Cr]); + if (tu.jointCbCr >> 1) + { + m_pcTrQuant->invTransformNxN(tu, COMPONENT_Cb, piResi, cQP); + } + else + { + QpParam qpCr(tu, COMPONENT_Cr); + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + qpCr.Qps[qpIdx] = qpCr.Qps[qpIdx] + DELTA_QP_FOR_Co; + qpCr.pers[qpIdx] = qpCr.Qps[qpIdx] / 6; + qpCr.rems[qpIdx] = qpCr.Qps[qpIdx] % 6; + } + + m_pcTrQuant->invTransformNxN(tu, COMPONENT_Cr, resiCr, qpCr); + } + m_pcTrQuant->invTransformICT(tu, piResi, resiCr); + } + } + else + { + if (TU::getCbf(tu, compID)) + { + m_pcTrQuant->invTransformNxN(tu, compID, piResi, cQP); + } + else + { + piResi.fill(0); + } + } + + flag = flag && (tu.blocks[compID].width*tu.blocks[compID].height > 4); +#if JVET_P1006_PICTURE_HEADER + if (flag && (TU::getCbf(tu, compID) || tu.jointCbCr) && isChroma(compID) && slice.getPicHeader()->getLmcsChromaResidualScaleFlag()) +#else + if (flag && (TU::getCbf(tu, compID) || tu.jointCbCr) && isChroma(compID) && slice.getLmcsChromaResidualScaleFlag()) +#endif + { + piResi.scaleSignal(tu.getChromaAdj(), 0, tu.cu->cs->slice->clpRng(compID)); + } + + cs.setDecomp(area); + } + + cs.getResiBuf(tu).colorSpaceConvert(cs.getResiBuf(tu), false); + + for (int i = 0; i < getNumberValidComponents(tu.chromaFormat); i++) + { + ComponentID compID = (ComponentID)i; + const CompArea &area = tu.blocks[compID]; + + PelBuf piPred = cs.getPredBuf(area); + PelBuf piResi = cs.getResiBuf(area); + PelBuf piReco = cs.getRecoBuf(area); + + PelBuf tmpPred; +#if JVET_P1006_PICTURE_HEADER + if (slice.getPicHeader()->getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || slice.isIntra()) && compID == COMPONENT_Y) +#else + if (slice.getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || slice.isIntra()) && compID == COMPONENT_Y) +#endif + { + CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size()); + tmpPred = m_tmpStorageLCU->getBuf(tmpArea); + tmpPred.copyFrom(piPred); + } + + piPred.reconstruct(piPred, piResi, tu.cu->cs->slice->clpRng(compID)); + piReco.copyFrom(piPred); + +#if JVET_P1006_PICTURE_HEADER + if (slice.getPicHeader()->getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || slice.isIntra()) && compID == COMPONENT_Y) +#else + if (slice.getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || slice.isIntra()) && compID == COMPONENT_Y) +#endif + { + piPred.copyFrom(tmpPred); + } + + if (cs.pcv->isEncoder) + { + cs.picture->getRecoBuf(area).copyFrom(piReco); + cs.picture->getPredBuf(area).copyFrom(piPred); + } + } +} +#endif + void DecCu::xReconIntraQT( CodingUnit &cu ) { @@ -401,6 +552,15 @@ void DecCu::xReconIntraQT( CodingUnit &cu ) } return; } + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cu.colorTransform) + { + xIntraRecACTQT(cu); + } + else + { +#endif const uint32_t numChType = ::getNumberValidChannels( cu.chromaFormat ); for( uint32_t chType = CHANNEL_TYPE_LUMA; chType < numChType; chType++ ) @@ -410,6 +570,9 @@ void DecCu::xReconIntraQT( CodingUnit &cu ) xIntraRecQT( cu, ChannelType( chType ) ); } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + } +#endif } void DecCu::xReconPLT(CodingUnit &cu, ComponentID compBegin, uint32_t numComp) @@ -523,6 +686,16 @@ DecCu::xIntraRecQT(CodingUnit &cu, const ChannelType chType) } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void DecCu::xIntraRecACTQT(CodingUnit &cu) +{ + for (auto &currTU : CU::traverseTUs(cu)) + { + xIntraRecACTBlk(currTU); + } +} +#endif + /** Function for filling the PCM buffer of a CU using its reconstructed sample array * \param pCU pointer to current CU * \param depth CU Depth @@ -619,6 +792,12 @@ void DecCu::xReconInter(CodingUnit &cu) if (cu.rootCbf) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cu.colorTransform) + { + cs.getResiBuf(cu).colorSpaceConvert(cs.getResiBuf(cu), false); + } +#endif #if REUSE_CU_RESULTS const CompArea &area = cu.blocks[COMPONENT_Y]; CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size()); @@ -690,7 +869,20 @@ void DecCu::xDecodeInterTU( TransformUnit & currTU, const ComponentID compID ) //===== inverse transform ===== PelBuf resiBuf = cs.getResiBuf(area); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + QpParam cQP(currTU, compID); + if (currTU.cu->colorTransform) + { + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + cQP.Qps[qpIdx] = cQP.Qps[qpIdx] + (compID == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + cQP.pers[qpIdx] = cQP.Qps[qpIdx] / 6; + cQP.rems[qpIdx] = cQP.Qps[qpIdx] % 6; + } + } +#else const QpParam cQP(currTU, compID); +#endif if( currTU.jointCbCr && isChroma(compID) ) { @@ -703,7 +895,20 @@ void DecCu::xDecodeInterTU( TransformUnit & currTU, const ComponentID compID ) } else { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + QpParam qpCr(currTU, COMPONENT_Cr); + if (currTU.cu->colorTransform) + { + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + qpCr.Qps[qpIdx] = qpCr.Qps[qpIdx] + DELTA_QP_FOR_Co; + qpCr.pers[qpIdx] = qpCr.Qps[qpIdx] / 6; + qpCr.rems[qpIdx] = qpCr.Qps[qpIdx] % 6; + } + } +#else const QpParam qpCr( currTU, COMPONENT_Cr ); +#endif m_pcTrQuant->invTransformNxN( currTU, COMPONENT_Cr, resiCr, qpCr ); } m_pcTrQuant->invTransformICT( currTU, resiBuf, resiCr ); diff --git a/source/Lib/DecoderLib/DecCu.h b/source/Lib/DecoderLib/DecCu.h index 4ef4076c087fdbc9a351563c09a61ec0e525a8da..d13ffcd07d2c36da3bc4e4b8281e1b8edf1eb389 100644 --- a/source/Lib/DecoderLib/DecCu.h +++ b/source/Lib/DecoderLib/DecCu.h @@ -82,6 +82,9 @@ public: /// reconstruct Ctu information protected: void xIntraRecQT ( CodingUnit& cu, const ChannelType chType ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void xIntraRecACTQT(CodingUnit& cu); +#endif void xReconInter ( CodingUnit& cu ); void xDecodeInterTexture( CodingUnit& cu ); @@ -89,6 +92,9 @@ protected: void xFillPCMBuffer ( CodingUnit& cu ); void xIntraRecBlk ( TransformUnit& tu, const ComponentID compID ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void xIntraRecACTBlk(TransformUnit& tu); +#endif void xDecodeInterTU ( TransformUnit& tu, const ComponentID compID ); void xDeriveCUMV ( CodingUnit& cu ); diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp index a422fc3dc57bddf20d99feb58982443dece46c1b..209303c8b231acd3375777d95206ca8b70c26f8b 100644 --- a/source/Lib/DecoderLib/VLCReader.cpp +++ b/source/Lib/DecoderLib/VLCReader.cpp @@ -1579,6 +1579,16 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS) READ_FLAG( uiCode, "sps_affine_amvr_enabled_flag" ); pcSPS->setAffineAmvrEnabledFlag ( uiCode != 0 ); } READ_FLAG( uiCode, "gbi_flag" ); pcSPS->setUseGBi ( uiCode != 0 ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (pcSPS->getChromaFormatIdc() == CHROMA_444) + { + READ_FLAG(uiCode, "act_flag"); pcSPS->setUseColorTrans(uiCode != 0); + } + else + { + pcSPS->setUseColorTrans(false); + } +#endif if (pcSPS->getChromaFormatIdc() == CHROMA_444) { READ_FLAG( uiCode, "plt_flag"); pcSPS->setPLTMode ( uiCode != 0 ); diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp index 3e01f31666bbe1d7d355846d238fc4658ef44459..08656e59fca661834e37f255398683eee4af4eec 100644 --- a/source/Lib/EncoderLib/CABACWriter.cpp +++ b/source/Lib/EncoderLib/CABACWriter.cpp @@ -650,6 +650,9 @@ void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, C if( cu.skip ) { CHECK( !cu.firstPU->mergeFlag, "Merge flag has to be on!" ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + CHECK(cu.colorTransform, "ACT should not be enabled for skip mode"); +#endif PredictionUnit& pu = *cu.firstPU; prediction_unit ( pu ); end_of_ctu ( cu, cuCtx ); @@ -659,8 +662,17 @@ void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, C // prediction mode and partitioning data pred_mode ( cu ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (CU::isIntra(cu)) + { + adaptive_color_transform(cu); + } +#endif if (CU::isPLT(cu)) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + CHECK(cu.colorTransform, "ACT should not be enabled for PLT mode"); +#endif if (cu.isSepTree()) { if (isLuma(partitioner.chType)) @@ -1134,7 +1146,6 @@ void CABACWriter::intra_luma_pred_mode( const PredictionUnit& pu ) return; } extend_ref_line( pu ); - isp_mode( *pu.cu ); // prev_intra_luma_pred_flag @@ -1257,6 +1268,13 @@ void CABACWriter::intra_chroma_pred_mode(const PredictionUnit& pu) #endif const unsigned intraDir = pu.intraDir[1]; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (pu.cu->colorTransform) + { + CHECK(pu.intraDir[CHANNEL_TYPE_CHROMA] != DM_CHROMA_IDX, "chroma should use DM for adaptive color transform"); + return; + } +#endif if (pu.cs->sps->getUseLMChroma() && pu.cu->checkCCLMAllowed()) { m_BinEncoder.encodeBin(PU::isLMCMode(intraDir) ? 1 : 0, Ctx::CclmModeFlag(0)); @@ -1310,10 +1328,20 @@ void CABACWriter::cu_residual( const CodingUnit& cu, Partitioner& partitioner, C if( !cu.rootCbf ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + CHECK(cu.colorTransform, "ACT should not be enabled for root_cbf = 0"); +#endif return; } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (CU::isInter(cu) || CU::isIBC(cu)) + { + adaptive_color_transform(cu); + } +#endif + cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_LUMA] = false; cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_CHROMA] = false; cuCtx.lfnstLastScanPos = false; @@ -1348,6 +1376,27 @@ void CABACWriter::rqt_root_cbf( const CodingUnit& cu ) DTRACE( g_trace_ctx, D_SYNTAX, "rqt_root_cbf() ctx=0 root_cbf=%d pos=(%d,%d)\n", cu.rootCbf ? 1 : 0, cu.lumaPos().x, cu.lumaPos().y ); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void CABACWriter::adaptive_color_transform(const CodingUnit& cu) +{ + if (!cu.slice->getSPS()->getUseColorTrans()) + { + return; + } + + if (cu.isSepTree()) + { + CHECK(cu.colorTransform, "adaptive color transform should be disabled when dualtree and localtree are enabled"); + return; + } + + if (CU::isInter(cu) || CU::isIBC(cu) || CU::isIntra(cu)) + { + m_BinEncoder.encodeBin(cu.colorTransform, Ctx::ACTFlag()); + } +} +#endif + void CABACWriter::sbt_mode( const CodingUnit& cu ) { uint8_t sbtAllowed = cu.checkAllowedSbt(); @@ -2767,9 +2816,17 @@ void CABACWriter::transform_unit( const TransformUnit& tu, CUCtx& cuCtx, Partiti } else { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool lumaCbfIsInferredACT = (cu.colorTransform && cu.predMode == MODE_INTRA && trDepth == 0 && !chromaCbfs.sigChroma(area.chromaFormat)); + CHECK(lumaCbfIsInferredACT && !TU::getCbfAtDepth(tu, COMPONENT_Y, trDepth), "adaptive color transform cannot have all zero coefficients"); + bool lastCbfIsInferred = lumaCbfIsInferredACT; // ISP and ACT are mutually exclusive + bool previousCbf = false; + bool rootCbfSoFar = false; +#else bool previousCbf = false; bool rootCbfSoFar = false; - bool lastCbfIsInferred = false; + bool lastCbfIsInferred = false; +#endif if (cu.ispMode) { uint32_t nTus = cu.ispMode == HOR_INTRA_SUBPARTITIONS ? cu.lheight() >> floorLog2(tu.lheight()) : cu.lwidth() >> floorLog2(tu.lwidth()); @@ -3211,7 +3268,11 @@ void CABACWriter::mts_coding( const TransformUnit& tu, ComponentID compID ) void CABACWriter::isp_mode( const CodingUnit& cu ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || !cu.cs->sps->getUseISP() || cu.bdpcmMode || !CU::canUseISP( cu, getFirstComponentOfChannel( cu.chType ) ) || cu.colorTransform ) +#else if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || !cu.cs->sps->getUseISP() || cu.bdpcmMode || !CU::canUseISP( cu, getFirstComponentOfChannel( cu.chType ) ) ) +#endif { CHECK( cu.ispMode != NOT_INTRA_SUBPARTITIONS, "cu.ispMode != 0" ); return; diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h index 7e60fe96954b7a852138a428c790f87ad039b50e..ecb5f977002b52c708882b815a791baba4122acf 100644 --- a/source/Lib/EncoderLib/CABACWriter.h +++ b/source/Lib/EncoderLib/CABACWriter.h @@ -103,6 +103,9 @@ public: void intra_chroma_pred_mode ( const PredictionUnit& pu ); void cu_residual ( const CodingUnit& cu, Partitioner& pm, CUCtx& cuCtx ); void rqt_root_cbf ( const CodingUnit& cu ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void adaptive_color_transform(const CodingUnit& cu); +#endif void sbt_mode ( const CodingUnit& cu ); void end_of_ctu ( const CodingUnit& cu, CUCtx& cuCtx ); void mip_flag ( const CodingUnit& cu ); diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index fc4f120c5d7aa9d57ee3c850363e5a726c0d407f..33401ed400f83ff0cd89d706b8141de437640fe8 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -327,6 +327,10 @@ protected: bool m_DMVR; bool m_MMVD; int m_MmvdDisNum; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool m_rgbFormat; + bool m_useColorTrans; +#endif unsigned m_PLTMode; bool m_JointCbCrMode; unsigned m_IBCMode; @@ -991,6 +995,12 @@ public: bool getMMVD () const { return m_MMVD; } void setMmvdDisNum ( int b ) { m_MmvdDisNum = b; } int getMmvdDisNum () const { return m_MmvdDisNum; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void setRGBFormatFlag(bool value) { m_rgbFormat = value; } + bool getRGBFormatFlag() const { return m_rgbFormat; } + void setUseColorTrans(bool value) { m_useColorTrans = value; } + bool getUseColorTrans() const { return m_useColorTrans; } +#endif void setPLTMode ( unsigned n) { m_PLTMode = n; } unsigned getPLTMode () const { return m_PLTMode; } void setJointCbCr ( bool b ) { m_JointCbCrMode = b; } diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp index 09508c8c87c10f6af8338e58a880d083be96fbf9..b04bdc86eec9bfc91f30e7711c212249cc4fac53 100644 --- a/source/Lib/EncoderLib/EncCu.cpp +++ b/source/Lib/EncoderLib/EncCu.cpp @@ -724,6 +724,32 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par m_pcInterSearch->resetSavedAffineMotion(); double bestIntPelCost = MAX_DOUBLE; + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (tempCS->slice->getSPS()->getUseColorTrans()) + { + tempCS->tmpColorSpaceCost = MAX_DOUBLE; + bestCS->tmpColorSpaceCost = MAX_DOUBLE; + tempCS->firstColorSpaceSelected = true; + bestCS->firstColorSpaceSelected = true; + } + + if (tempCS->slice->getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS)) + { + tempCS->firstColorSpaceTestOnly = false; + bestCS->firstColorSpaceTestOnly = false; + tempCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE; + tempCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE; + bestCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE; + bestCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE; + + if (tempCS->bestParent && tempCS->bestParent->firstColorSpaceTestOnly) + { + tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true; + } + } +#endif + do { for (int i = compBegin; i < (compBegin + numComp); i++) @@ -821,7 +847,40 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par } else if( currTestMode.type == ETM_INTRA ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS)) + { + bool skipSecColorSpace = false; + skipSecColorSpace = xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? true : false)); + + if (!skipSecColorSpace && !tempCS->firstColorSpaceTestOnly) + { + xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? false : true)); + } + + if (!tempCS->firstColorSpaceTestOnly) + { + if (tempCS->tmpColorSpaceIntraCost[0] != MAX_DOUBLE && tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE) + { + double skipCostRatio = m_pcEncCfg->getRGBFormatFlag() ? 1.1 : 1.0; + if (tempCS->tmpColorSpaceIntraCost[1] > (skipCostRatio*tempCS->tmpColorSpaceIntraCost[0])) + { + tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true; + } + } + } + else + { + CHECK(tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE, "the RD test of the second color space should be skipped"); + } + } + else + { + xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, false); + } +#else xCheckRDCostIntra( tempCS, bestCS, partitioner, currTestMode ); +#endif } else if (currTestMode.type == ETM_PALETTE) { @@ -1704,8 +1763,11 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, tempCS->prevQP[partitioner.chType] = oldPrevQp; } - +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, bool adaptiveColorTrans) +#else void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode ) +#endif { double bestInterCost = m_modeCtrl->getBestInterCost(); double costSize2Nx2NmtsFirstPass = m_modeCtrl->getMtsSize2Nx2NFirstPassCost(); @@ -1750,6 +1812,24 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC m_modeCtrl->setISPWasTested(false); #endif m_pcIntraSearch->invalidateBestModeCost(); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (sps.getUseColorTrans() && !CS::isDualITree(*tempCS)) + { + if ((m_pcEncCfg->getRGBFormatFlag() && adaptiveColorTrans) || (!m_pcEncCfg->getRGBFormatFlag() && !adaptiveColorTrans)) + { + m_pcIntraSearch->invalidateBestRdModeFirstColorSpace(); + } + } + + bool foundZeroRootCbf = false; + if (sps.getUseColorTrans()) + { + CHECK(tempCS->treeType != TREE_D || partitioner.treeType != TREE_D, "localtree should not be applied when adaptive color transform is enabled"); + CHECK(tempCS->modeType != MODE_TYPE_ALL || partitioner.modeType != MODE_TYPE_ALL, "localtree should not be applied when adaptive color transform is enabled"); + CHECK(adaptiveColorTrans && (CS::isDualITree(*tempCS) || partitioner.chType != CHANNEL_TYPE_LUMA), "adaptive color transform cannot be applied to dual-tree"); + } +#endif + for( int trGrpIdx = 0; trGrpIdx < grpNumMax; trGrpIdx++ ) { const uint8_t startMtsFlag = trGrpIdx > 0; @@ -1761,6 +1841,12 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC { for( uint8_t mtsFlag = startMtsFlag; mtsFlag <= endMtsFlag; mtsFlag++ ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (sps.getUseColorTrans() && !CS::isDualITree(*tempCS)) + { + m_pcIntraSearch->setSavedRdModeIdx(trGrpIdx*(NUM_LFNST_NUM_PER_SET * 2) + lfnstIdx * 2 + mtsFlag); + } +#endif if (mtsFlag > 0 && lfnstIdx > 0) { continue; @@ -1787,6 +1873,9 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC cu.lfnstIdx = lfnstIdx; cu.mtsFlag = mtsFlag; cu.ispMode = NOT_INTRA_SUBPARTITIONS; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cu.colorTransform = adaptiveColorTrans; +#endif CU::addPUs( cu ); @@ -1803,13 +1892,22 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC { bestCostSoFar = encTestMode.maxCostAllowed; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + validCandRet = m_pcIntraSearch->estIntraPredLumaQT(cu, partitioner, bestCostSoFar, mtsFlag, startMTSIdx[trGrpIdx], endMTSIdx[trGrpIdx], (trGrpIdx > 0), !cu.colorTransform ? bestCS : nullptr); + if ((!validCandRet || (cu.ispMode && cu.firstTU->cbf[COMPONENT_Y] == 0))) +#else validCandRet = m_pcIntraSearch->estIntraPredLumaQT( cu, partitioner, bestCostSoFar, mtsFlag, startMTSIdx[ trGrpIdx ], endMTSIdx[ trGrpIdx ], ( trGrpIdx > 0 ) ); - if( sps.getUseLFNST() && ( !validCandRet || ( cu.ispMode && cu.firstTU->cbf[ COMPONENT_Y ] == 0 ) ) ) + if (sps.getUseLFNST() && (!validCandRet || (cu.ispMode && cu.firstTU->cbf[COMPONENT_Y] == 0))) +#endif { continue; } -#if JVET_P1026_ISP_LFNST_COMBINATION +#if JVET_P1026_ISP_LFNST_COMBINATION +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (m_pcEncCfg->getUseFastISP() && validCandRet && !mtsFlag && !lfnstIdx && !cu.colorTransform) +#else if (m_pcEncCfg->getUseFastISP() && validCandRet && !mtsFlag && !lfnstIdx) +#endif { m_modeCtrl->setISPMode(cu.ispMode); m_modeCtrl->setISPLfnstIdx(cu.lfnstIdx); @@ -1819,6 +1917,17 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC } #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (sps.getUseColorTrans() && m_pcEncCfg->getRGBFormatFlag() && !CS::isDualITree(*tempCS) && !cu.colorTransform) + { + double curLumaCost = m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist); + if (curLumaCost > bestCS->cost) + { + continue; + } + } +#endif + useIntraSubPartitions = cu.ispMode != NOT_INTRA_SUBPARTITIONS; if( !partitioner.isSepTree( *tempCS ) ) { @@ -1841,12 +1950,29 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC if( !partitioner.isSepTree( *tempCS ) ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (!cu.colorTransform) + { + cu.cs->picture->getRecoBuf(cu.Y()).copyFrom(cu.cs->getRecoBuf(COMPONENT_Y)); + cu.cs->picture->getPredBuf(cu.Y()).copyFrom(cu.cs->getPredBuf(COMPONENT_Y)); + } + else + { + cu.cs->picture->getRecoBuf(cu).copyFrom(cu.cs->getRecoBuf(cu)); + cu.cs->picture->getPredBuf(cu).copyFrom(cu.cs->getPredBuf(cu)); + } +#else cu.cs->picture->getRecoBuf( cu.Y() ).copyFrom( cu.cs->getRecoBuf( COMPONENT_Y ) ); cu.cs->picture->getPredBuf(cu.Y()).copyFrom(cu.cs->getPredBuf(COMPONENT_Y)); +#endif } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if( tempCS->area.chromaFormat != CHROMA_400 && ( partitioner.chType == CHANNEL_TYPE_CHROMA || !cu.isSepTree() ) && !cu.colorTransform ) +#else if( tempCS->area.chromaFormat != CHROMA_400 && ( partitioner.chType == CHANNEL_TYPE_CHROMA || !cu.isSepTree() ) ) +#endif { TUIntraSubPartitioner subTuPartitioner( partitioner ); m_pcIntraSearch->estIntraPredChromaQT( cu, ( !useIntraSubPartitions || ( cu.isSepTree() && !isLuma( CHANNEL_TYPE_CHROMA ) ) ) ? partitioner : subTuPartitioner, maxCostAllowedForChroma ); @@ -1864,6 +1990,14 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC cu.rootCbf |= cu.firstTU->cbf[t] != 0; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (!cu.rootCbf) + { + cu.colorTransform = false; + foundZeroRootCbf = true; + } +#endif + // Get total bits for current mode: encode CU m_CABACEstimator->resetBits(); @@ -1879,6 +2013,9 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC m_CABACEstimator->cu_skip_flag ( cu ); } m_CABACEstimator->pred_mode ( cu ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_CABACEstimator->adaptive_color_transform(cu); +#endif m_CABACEstimator->cu_pred_data ( cu ); m_CABACEstimator->bdpcm_mode ( cu, ComponentID(partitioner.chType) ); #if JVET_P0059_CHROMA_BDPCM @@ -1949,6 +2086,17 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) ); #else DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda() ); +#endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (sps.getUseColorTrans() && !CS::isDualITree(*tempCS)) + { + int colorSpaceIdx = ((m_pcEncCfg->getRGBFormatFlag() && adaptiveColorTrans) || (!m_pcEncCfg->getRGBFormatFlag() && !adaptiveColorTrans)) ? 0 : 1; + if (tempCS->cost < tempCS->tmpColorSpaceIntraCost[colorSpaceIdx]) + { + tempCS->tmpColorSpaceIntraCost[colorSpaceIdx] = tempCS->cost; + bestCS->tmpColorSpaceIntraCost[colorSpaceIdx] = tempCS->cost; + } + } #endif if( !sps.getUseLFNST() ) { @@ -2048,9 +2196,15 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC } } } //trGrpIdx -#if JVET_P1026_ISP_LFNST_COMBINATION +#if JVET_P1026_ISP_LFNST_COMBINATION +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if(!adaptiveColorTrans) +#endif m_modeCtrl->setBestNonDCT2Cost(bestNonDCT2Cost); #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + return foundZeroRootCbf; +#endif } @@ -3647,6 +3801,13 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct m_CABACEstimator->getCtx() = m_CurrCtx->start; m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, (numResidualPass != 0), true, chroma); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (tempCS->slice->getSPS()->getUseColorTrans()) + { + bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost; + bestCS->firstColorSpaceSelected = tempCS->firstColorSpaceSelected; + } +#endif xEncodeDontSplit(*tempCS, partitioner); #if ENABLE_QPA_SUB_CTU @@ -3737,6 +3898,13 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best { m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, false, true, chroma); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (tempCS->slice->getSPS()->getUseColorTrans()) + { + bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost; + bestCS->firstColorSpaceSelected = tempCS->firstColorSpaceSelected; + } +#endif xEncodeDontSplit(*tempCS, partitioner); @@ -4479,6 +4647,13 @@ void EncCu::xEncodeInterResidual( CodingStructure *&tempCS if( skipResidual || histBestSbt == MAX_UCHAR || !CU::isSbtMode( histBestSbt ) ) { m_pcInterSearch->encodeResAndCalcRdInterCU( *tempCS, partitioner, skipResidual ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (tempCS->slice->getSPS()->getUseColorTrans()) + { + bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost; + bestCS->firstColorSpaceSelected = tempCS->firstColorSpaceSelected; + } +#endif numRDOTried += mtsAllowed ? 2 : 1; xEncodeDontSplit( *tempCS, partitioner ); @@ -4626,6 +4801,13 @@ void EncCu::xEncodeInterResidual( CodingStructure *&tempCS //try residual coding m_pcInterSearch->encodeResAndCalcRdInterCU( *tempCS, partitioner, skipResidual ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (tempCS->slice->getSPS()->getUseColorTrans()) + { + bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost; + bestCS->firstColorSpaceSelected = tempCS->firstColorSpaceSelected; + } +#endif numRDOTried++; xEncodeDontSplit( *tempCS, partitioner ); diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h index 42adec40484de60fc601de09c8326fc4a1f27172..5a7efe54bf32496cfd7ecd96231f7f7d31833f96 100644 --- a/source/Lib/EncoderLib/EncCu.h +++ b/source/Lib/EncoderLib/EncCu.h @@ -192,7 +192,11 @@ protected: void xCheckModeSplit ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode, const ModeType modeTypeParent, bool &skipInterPass ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode, bool adaptiveColorTrans); +#else void xCheckRDCostIntra ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode ); +#endif void xCheckDQP ( CodingStructure& cs, Partitioner& partitioner, bool bKeepCtx = false); void xFillPCMBuffer ( CodingUnit &cu); diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 8ac19750753fe4681b37b517d4d40bdf9d5afa21..7734dd7e6953a8e793ca1b943d82abe3e9eb1f3f 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -1397,6 +1397,9 @@ void EncLib::xInitSPS(SPS &sps) sps.setBdofDmvrSlicePresentFlag(m_DMVR || m_BIO); sps.setAffineAmvrEnabledFlag ( m_AffineAmvr ); sps.setUseDMVR ( m_DMVR ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + sps.setUseColorTrans(m_useColorTrans); +#endif sps.setPLTMode ( m_PLTMode); sps.setIBCFlag ( m_IBCMode); sps.setWrapAroundEnabledFlag ( m_wrapAround ); diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp index cfc71f5440569c4d5b2645a86583aac3eedc3bd6..266cc3a998e9acea19ca46d8412dc326801e5c4d 100644 --- a/source/Lib/EncoderLib/EncModeCtrl.cpp +++ b/source/Lib/EncoderLib/EncModeCtrl.cpp @@ -440,6 +440,16 @@ bool CacheBlkInfoCtrl::isSkip( const UnitArea& area ) return m_codedCUInfo[idx1][idx2][idx3][idx4]->isSkip; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +char CacheBlkInfoCtrl::getSelectColorSpaceOption(const UnitArea& area) +{ + unsigned idx1, idx2, idx3, idx4; + getAreaIdx(area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4); + + return m_codedCUInfo[idx1][idx2][idx3][idx4]->selectColorSpaceOption; +} +#endif + bool CacheBlkInfoCtrl::isMMVDSkip(const UnitArea& area) { unsigned idx1, idx2, idx3, idx4; @@ -1901,11 +1911,65 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt relatedCU.isSkip |= bestCU->skip; relatedCU.isMMVDSkip |= bestCU->mmvdSkip; relatedCU.GBiIdx = bestCU->GBiIdx; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (bestCU->slice->getSPS()->getUseColorTrans()) + { + if (m_pcEncCfg->getRGBFormatFlag()) + { + if (bestCU->colorTransform && bestCU->rootCbf) + { + relatedCU.selectColorSpaceOption = 1; + } + else + { + relatedCU.selectColorSpaceOption = 2; + } + } + else + { + if (!bestCU->colorTransform || !bestCU->rootCbf) + { + relatedCU.selectColorSpaceOption = 1; + } + else + { + relatedCU.selectColorSpaceOption = 2; + } + } + } +#endif } else if (CU::isIBC(*bestCU)) { relatedCU.isIBC = true; relatedCU.isSkip |= bestCU->skip; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (bestCU->slice->getSPS()->getUseColorTrans()) + { + if (m_pcEncCfg->getRGBFormatFlag()) + { + if (bestCU->colorTransform && bestCU->rootCbf) + { + relatedCU.selectColorSpaceOption = 1; + } + else + { + relatedCU.selectColorSpaceOption = 2; + } + } + else + { + if (!bestCU->colorTransform || !bestCU->rootCbf) + { + relatedCU.selectColorSpaceOption = 1; + } + else + { + relatedCU.selectColorSpaceOption = 2; + } + } + } +#endif } else if( CU::isIntra( *bestCU ) ) { diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h index 38a3fc072271ed0f722bc5bc7481fe8af0bf0b31..55947d907ad30cb647d9b44ed66b681112abf56d 100644 --- a/source/Lib/EncoderLib/EncModeCtrl.h +++ b/source/Lib/EncoderLib/EncModeCtrl.h @@ -439,6 +439,9 @@ struct CodedCUInfo Mv saveMv [NUM_REF_PIC_LIST_01][MAX_STORED_CU_INFO_REFS]; uint8_t GBiIdx; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + char selectColorSpaceOption; // 0 - test both two color spaces; 1 - only test the first color spaces; 2 - only test the second color spaces +#endif #if JVET_P1026_ISP_LFNST_COMBINATION uint16_t ispPredModeVal; double bestDCT2NonISPCost; @@ -498,6 +501,10 @@ public: bool getInter( const UnitArea& area ); void setGbiIdx( const UnitArea& area, uint8_t gBiIdx ); uint8_t getGbiIdx( const UnitArea& area ); + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + char getSelectColorSpaceOption(const UnitArea& area); +#endif }; #if REUSE_CU_RESULTS diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp index a0a043c4180840b9643da97ff07abb35bd9352f8..f7a1c54876cda07e122b79bd4be36f10346aa661 100644 --- a/source/Lib/EncoderLib/EncSlice.cpp +++ b/source/Lib/EncoderLib/EncSlice.cpp @@ -107,6 +107,10 @@ void EncSlice::init( EncLib* pcEncLib, const SPS& sps ) void EncSlice::setUpLambda( Slice* slice, const double dLambda, int iQP) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_pcRdCost->resetStore(); + m_pcTrQuant->resetStore(); +#endif // store lambda m_pcRdCost ->setLambda( dLambda, slice->getSPS()->getBitDepths() ); diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp index d18b39381248a9bc458a357d713bed0b89558c1d..c667b281065a0e7ab697a244a3300fda2a2d05ff 100644 --- a/source/Lib/EncoderLib/InterSearch.cpp +++ b/source/Lib/EncoderLib/InterSearch.cpp @@ -6520,6 +6520,9 @@ uint8_t InterSearch::skipSbtByRDCost( int width, int height, int mtDepth, uint8_ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &partitioner, Distortion *puiZeroDist /*= NULL*/ , const bool luma, const bool chroma +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + , PelUnitBuf* orgResi +#endif ) { const UnitArea& currArea = partitioner.currArea(); @@ -6530,6 +6533,9 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par const uint32_t numTBlocks = getNumberValidTBlocks ( *cs.pcv ); const CodingUnit &cu = *cs.getCU(partitioner.chType); const unsigned currDepth = partitioner.currTrDepth; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + const bool colorTransFlag = cs.cus[0]->colorTransform; +#endif bool bCheckFull = !partitioner.canSplit( TU_MAX_TR_SPLIT, cs ); if( cu.sbtInfo && partitioner.canSplit( PartSplit( cu.getSbtTuSplit() ), cs ) ) @@ -6552,6 +6558,9 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par Distortion uiSingleDist = 0; Distortion uiSingleDistComp [3] = { 0, 0, 0 }; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + uint64_t uiSingleFracBits[3] = { 0, 0, 0 }; +#endif TCoeff uiAbsSum [3] = { 0, 0, 0 }; const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() ); @@ -6567,6 +6576,11 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par tu.mtsIdx = MTS_DCT2_DCT2; #endif tu.checkTuNoResidual( partitioner.currPartIdx() ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + Position tuPos = tu.Y(); + tuPos.relativeTo(cu.Y()); + const UnitArea relativeUnitArea(tu.chromaFormat, Area(tuPos, tu.Y().size())); +#endif const Slice &slice = *cs.slice; #if JVET_P1006_PICTURE_HEADER @@ -6668,6 +6682,15 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par #endif } } + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + m_pcTrQuant->lambdaAdjustColorTrans(true); + m_pcRdCost->lambdaAdjustColorTrans(true, compID); + } +#endif + const int crossCPredictionModesToTest = preCalcAlpha != 0 ? 2 : 1; const int numTransformCandidates = nNumTransformCands; const bool isOneMode = crossCPredictionModesToTest == 1 && numTransformCandidates == 1; @@ -6719,7 +6742,20 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par } tu.compAlpha[compID] = bUseCrossCPrediction ? preCalcAlpha : 0; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + QpParam cQP(tu, compID); // note: uses tu.transformSkip[compID] + if (colorTransFlag) + { + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + cQP.Qps[qpIdx] = cQP.Qps[qpIdx] + (compID == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + cQP.pers[qpIdx] = cQP.Qps[qpIdx] / 6; + cQP.rems[qpIdx] = cQP.Qps[qpIdx] % 6; + } + } +#else const QpParam cQP(tu, compID); // note: uses tu.transformSkip[compID] +#endif #if RDOQ_CHROMA_LAMBDA m_pcTrQuant->selectLambda(compID); @@ -6824,7 +6860,18 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par } else #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cs.slice->getSPS()->getUseColorTrans()) + { + nonCoeffCost = m_pcRdCost->calcRdCost(nonCoeffFracBits, nonCoeffDist, false); + } + else + { + nonCoeffCost = m_pcRdCost->calcRdCost(nonCoeffFracBits, nonCoeffDist); + } +#else nonCoeffCost = m_pcRdCost->calcRdCost(nonCoeffFracBits, nonCoeffDist); +#endif } if ((puiZeroDist != NULL) && isFirstMode) @@ -6925,6 +6972,9 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par uiAbsSum[compID] = currAbsSum; uiSingleDistComp[compID] = currCompDist; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + uiSingleFracBits[compID] = currCompFracBits; +#endif minCost[compID] = currCompCost; if (uiAbsSum[compID] == 0) @@ -6959,8 +7009,33 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par tu.copyComponentFrom( bestTU, compID ); csFull->getResiBuf( compArea ).copyFrom( saveCS.getResiBuf( compArea ) ); } + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + m_pcTrQuant->lambdaAdjustColorTrans(false); + m_pcRdCost->lambdaAdjustColorTrans(false, compID); + } +#endif + } // component loop +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + PelUnitBuf orgResidual = orgResi->subBuf(relativeUnitArea); + PelUnitBuf invColorTransResidual = m_colorTransResiBuf[2].getBuf(relativeUnitArea); + csFull->getResiBuf(currArea).colorSpaceConvert(invColorTransResidual, false); + + for (uint32_t c = 0; c < numTBlocks; c++) + { + const ComponentID compID = (ComponentID)c; + uiSingleDistComp[c] = m_pcRdCost->getDistPart(orgResidual.bufs[c], invColorTransResidual.bufs[c], sps.getBitDepth(toChannelType(compID)), compID, DF_SSE); + minCost[c] = m_pcRdCost->calcRdCost(uiSingleFracBits[c], uiSingleDistComp[c]); + } + } +#endif + if ( chroma && tu.blocks[COMPONENT_Cb].valid() ) { const CompArea& cbArea = tu.blocks[COMPONENT_Cb]; @@ -6974,6 +7049,12 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par #endif && tu.blocks[COMPONENT_Cb].width * tu.blocks[COMPONENT_Cb].height > 4; double minCostCbCr = minCost[COMPONENT_Cb] + minCost[COMPONENT_Cr]; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + minCostCbCr += minCost[COMPONENT_Y]; // ACT should consider three-component cost + } +#endif bool isLastBest = false; CompStorage orgResiCb[4], orgResiCr[4]; // 0:std, 1-3:jointCbCr @@ -7006,10 +7087,26 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par // encoder bugfix: initialize mtsIdx for chroma under JointCbCrMode. tu.mtsIdx[COMPONENT_Cb] = tu.mtsIdx[COMPONENT_Cr] = MTS_DCT2_DCT2; #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + int codedCbfMask = 0; + ComponentID codeCompId = (tu.jointCbCr >> 1 ? COMPONENT_Cb : COMPONENT_Cr); + ComponentID otherCompId = (codeCompId == COMPONENT_Cr ? COMPONENT_Cb : COMPONENT_Cr); + + if (colorTransFlag) + { + m_pcTrQuant->lambdaAdjustColorTrans(true); + m_pcTrQuant->selectLambda(codeCompId); + } + else + { + m_pcTrQuant->selectLambda(codeCompId); + } +#else const QpParam cQP(tu, COMPONENT_Cb); // note: uses tu.transformSkip[compID] #if RDOQ_CHROMA_LAMBDA m_pcTrQuant->selectLambda(COMPONENT_Cb); +#endif #endif // Lambda is loosened for the joint mode with respect to single modes as the same residual is used for both chroma blocks const int absIct = abs( TU::getICTMode(tu) ); @@ -7034,10 +7131,24 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par m_pcTrQuant->setLambda(m_pcTrQuant->getLambda() / (cRescale*cRescale)); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + Distortion currCompDistY = MAX_UINT64; + QpParam qpCbCr(tu, codeCompId); + if (colorTransFlag) + { + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + qpCbCr.Qps[qpIdx] = qpCbCr.Qps[qpIdx] + (codeCompId == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + qpCbCr.pers[qpIdx] = qpCbCr.Qps[qpIdx] / 6; + qpCbCr.rems[qpIdx] = qpCbCr.Qps[qpIdx] % 6; + } + } +#else int codedCbfMask = 0; ComponentID codeCompId = (tu.jointCbCr >> 1 ? COMPONENT_Cb : COMPONENT_Cr); ComponentID otherCompId = (codeCompId == COMPONENT_Cr ? COMPONENT_Cb : COMPONENT_Cr); const QpParam qpCbCr(tu, codeCompId); +#endif tu.getCoeffs(otherCompId).fill(0); // do we need that? TU::setCbfAtDepth(tu, otherCompId, tu.depth, false); @@ -7084,12 +7195,30 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par crResi.scaleSignal(tu.getChromaAdj(), 0, tu.cu->cs->slice->clpRng(COMPONENT_Cr)); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + PelUnitBuf orgResidual = orgResi->subBuf(relativeUnitArea); + PelUnitBuf invColorTransResidual = m_colorTransResiBuf[2].getBuf(relativeUnitArea); + csFull->getResiBuf(currArea).colorSpaceConvert(invColorTransResidual, false); + + currCompDistY = m_pcRdCost->getDistPart(orgResidual.bufs[COMPONENT_Y], invColorTransResidual.bufs[COMPONENT_Y], sps.getBitDepth(toChannelType(COMPONENT_Y)), COMPONENT_Y, DF_SSE); + currCompDistCb = m_pcRdCost->getDistPart(orgResidual.bufs[COMPONENT_Cb], invColorTransResidual.bufs[COMPONENT_Cb], sps.getBitDepth(toChannelType(COMPONENT_Cb)), COMPONENT_Cb, DF_SSE); + currCompDistCr = m_pcRdCost->getDistPart(orgResidual.bufs[COMPONENT_Cr], invColorTransResidual.bufs[COMPONENT_Cr], sps.getBitDepth(toChannelType(COMPONENT_Cr)), COMPONENT_Cr, DF_SSE); + currCompCost = m_pcRdCost->calcRdCost(uiSingleFracBits[COMPONENT_Y] + currCompFracBits, currCompDistY + currCompDistCr + currCompDistCb, false); + } + else + { +#endif currCompDistCb = m_pcRdCost->getDistPart(csFull->getOrgResiBuf(cbArea), cbResi, channelBitDepth, COMPONENT_Cb, DF_SSE); currCompDistCr = m_pcRdCost->getDistPart(csFull->getOrgResiBuf(crArea), crResi, channelBitDepth, COMPONENT_Cr, DF_SSE); #if WCG_EXT currCompCost = m_pcRdCost->calcRdCost(currCompFracBits, currCompDistCr + currCompDistCb, false); #else currCompCost = m_pcRdCost->calcRdCost(currCompFracBits, currCompDistCr + currCompDistCb); +#endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + } #endif } else @@ -7102,6 +7231,12 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par uiAbsSum[COMPONENT_Cr] = currAbsSum; uiSingleDistComp[COMPONENT_Cb] = currCompDistCb; uiSingleDistComp[COMPONENT_Cr] = currCompDistCr; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + uiSingleDistComp[COMPONENT_Y] = currCompDistY; + } +#endif minCostCbCr = currCompCost; isLastBest = (cbfMask == jointCbfMasksToTest.back()); if (!isLastBest) @@ -7121,6 +7256,12 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par csFull->getResiBuf( cbArea ).copyFrom( saveCS.getResiBuf( cbArea ) ); csFull->getResiBuf( crArea ).copyFrom( saveCS.getResiBuf( crArea ) ); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + m_pcTrQuant->lambdaAdjustColorTrans(false); + } +#endif } } @@ -7209,6 +7350,9 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par { xEstimateInterResidualQT(*csSplit, partitioner, bCheckFull ? nullptr : puiZeroDist , luma, chroma +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + , orgResi +#endif ); csSplit->cost = m_pcRdCost->calcRdCost( csSplit->fracBits, csSplit->dist ); @@ -7298,10 +7442,22 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa const SPS &sps = *cs.sps; const PPS &pps = *cs.pps; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool colorTransAllowed = cs.slice->getSPS()->getUseColorTrans() && luma && chroma; + if (cs.slice->getSPS()->getUseColorTrans()) + { + CHECK(cu.treeType != TREE_D || partitioner.treeType != TREE_D, "localtree should not be applied when adaptive color transform is enabled"); + CHECK(cu.modeType != MODE_TYPE_ALL || partitioner.modeType != MODE_TYPE_ALL, "localtree should not be applied when adaptive color transform is enabled"); + } +#endif + if( skipResidual ) // No residual coding : SKIP mode { cu.skip = true; cu.rootCbf = false; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cu.colorTransform = false; +#endif CHECK( cu.sbtInfo != 0, "sbtInfo shall be 0 if CU has no residual" ); cs.getResiBuf().fill(0); { @@ -7405,20 +7561,152 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa cs.getResiBuf().bufs[1].subtract(cs.getPredBuf().bufs[1]); cs.getResiBuf().bufs[2].subtract(cs.getPredBuf().bufs[2]); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + const UnitArea curUnitArea = partitioner.currArea(); + CodingStructure &saveCS = *m_pSaveCS[1]; + saveCS.pcv = cs.pcv; + saveCS.picture = cs.picture; + saveCS.area.repositionTo(curUnitArea); + saveCS.clearCUs(); + saveCS.clearPUs(); + saveCS.clearTUs(); + for (const auto &ppcu : cs.cus) + { + CodingUnit &pcu = saveCS.addCU(*ppcu, ppcu->chType); + pcu = *ppcu; + } + for (const auto &ppu : cs.pus) + { + PredictionUnit &pu = saveCS.addPU(*ppu, ppu->chType); + pu = *ppu; + } + + PelUnitBuf orgResidual, colorTransResidual; + const UnitArea localUnitArea(cs.area.chromaFormat, Area(0, 0, cu.Y().width, cu.Y().height)); + orgResidual = m_colorTransResiBuf[0].getBuf(localUnitArea); + colorTransResidual = m_colorTransResiBuf[1].getBuf(localUnitArea); + orgResidual.copyFrom(cs.getResiBuf()); + if (colorTransAllowed) + { + cs.getResiBuf().colorSpaceConvert(colorTransResidual, true); + } + + const TempCtx ctxStart(m_CtxCache, m_CABACEstimator->getCtx()); + int numAllowedColorSpace = (colorTransAllowed ? 2 : 1); + Distortion zeroDistortion = 0; + + double bestCost = MAX_DOUBLE; + bool bestColorTrans = false; + bool bestRootCbf = false; + uint8_t bestsbtInfo = 0; + uint8_t orgSbtInfo = cu.sbtInfo; + int bestIter = 0; + + auto blkCache = dynamic_cast<CacheBlkInfoCtrl*>(m_modeCtrl); + bool rootCbfFirstColorSpace = true; + + for (int iter = 0; iter < numAllowedColorSpace; iter++) + { + if (colorTransAllowed && !m_pcEncCfg->getRGBFormatFlag() && iter) + { + continue; + } + char colorSpaceOption = blkCache->getSelectColorSpaceOption(cu); + if (colorTransAllowed) + { + if (colorSpaceOption) + { + CHECK(colorSpaceOption > 2 || colorSpaceOption < 0, "invalid color space selection option"); + if (colorSpaceOption == 1 && iter) + { + continue; + } + if (colorSpaceOption == 2 && !iter) + { + continue; + } + } + } + if (!colorSpaceOption) + { + if (iter && !rootCbfFirstColorSpace) + { + continue; + } + if (colorTransAllowed && cs.bestParent && cs.bestParent->tmpColorSpaceCost != MAX_DOUBLE) + { + if (cs.bestParent->firstColorSpaceSelected && iter) + { + continue; + } + if (m_pcEncCfg->getRGBFormatFlag()) + { + if (!cs.bestParent->firstColorSpaceSelected && !iter) + { + continue; + } + } + } + } + bool colorTransFlag = (colorTransAllowed && m_pcEncCfg->getRGBFormatFlag()) ? (1 - iter) : iter; + cu.colorTransform = colorTransFlag; + cu.sbtInfo = orgSbtInfo; + + m_CABACEstimator->resetBits(); + m_CABACEstimator->getCtx() = ctxStart; + cs.clearTUs(); + cs.fracBits = 0; + cs.dist = 0; + cs.cost = 0; +#else Distortion zeroDistortion = 0; const TempCtx ctxStart( m_CtxCache, m_CABACEstimator->getCtx() ); +#endif + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (colorTransFlag) + { + cs.getOrgResiBuf().bufs[0].copyFrom(colorTransResidual.bufs[0]); + cs.getOrgResiBuf().bufs[1].copyFrom(colorTransResidual.bufs[1]); + cs.getOrgResiBuf().bufs[2].copyFrom(colorTransResidual.bufs[2]); + memset(m_pTempPel, 0, sizeof(Pel) * localUnitArea.blocks[0].area()); + zeroDistortion = 0; + for (int compIdx = 0; compIdx < 3; compIdx++) + { + ComponentID componentID = (ComponentID)compIdx; + const CPelBuf zeroBuf(m_pTempPel, localUnitArea.blocks[compIdx]); + zeroDistortion += m_pcRdCost->getDistPart(zeroBuf, orgResidual.bufs[compIdx], sps.getBitDepth(toChannelType(componentID)), componentID, DF_SSE); + } + xEstimateInterResidualQT(cs, partitioner, NULL, luma, chroma, &orgResidual); + } + else + { + zeroDistortion = 0; +#endif if (luma) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cs.getOrgResiBuf().bufs[0].copyFrom(orgResidual.bufs[0]); +#else cs.getOrgResiBuf().bufs[0].copyFrom(cs.getResiBuf().bufs[0]); +#endif } if (chroma) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cs.getOrgResiBuf().bufs[1].copyFrom(orgResidual.bufs[1]); + cs.getOrgResiBuf().bufs[2].copyFrom(orgResidual.bufs[2]); +#else cs.getOrgResiBuf().bufs[1].copyFrom(cs.getResiBuf().bufs[1]); cs.getOrgResiBuf().bufs[2].copyFrom(cs.getResiBuf().bufs[2]); +#endif } xEstimateInterResidualQT(cs, partitioner, &zeroDistortion, luma, chroma); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + } +#endif TransformUnit &firstTU = *cs.getTU( partitioner.chType ); cu.rootCbf = false; @@ -7449,6 +7737,10 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa if (zeroCost < cs.cost || !cu.rootCbf) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + cs.cost = zeroCost; + cu.colorTransform = false; +#endif cu.sbtInfo = 0; cu.rootCbf = false; @@ -7463,7 +7755,52 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa } cu.firstTU = cu.lastTU = &tu; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (!iter) + { + rootCbfFirstColorSpace = cu.rootCbf; + } + if (cs.cost < bestCost) + { + bestIter = iter; + if (cu.rootCbf && cu.colorTransform) + { + cs.getResiBuf(curUnitArea).colorSpaceConvert(cs.getResiBuf(curUnitArea), false); + } + + if (iter != (numAllowedColorSpace - 1)) + { + bestCost = cs.cost; + bestColorTrans = cu.colorTransform; + bestRootCbf = cu.rootCbf; + bestsbtInfo = cu.sbtInfo; + + saveCS.clearTUs(); + for (const auto &ptu : cs.tus) + { + TransformUnit &tu = saveCS.addTU(*ptu, ptu->chType); + tu = *ptu; + } + saveCS.getResiBuf(curUnitArea).copyFrom(cs.getResiBuf(curUnitArea)); + } + } + } + + if (bestIter != (numAllowedColorSpace - 1)) + { + cu.colorTransform = bestColorTrans; + cu.rootCbf = bestRootCbf; + cu.sbtInfo = bestsbtInfo; + cs.clearTUs(); + for (const auto &ptu : saveCS.tus) + { + TransformUnit &tu = cs.addTU(*ptu, ptu->chType); + tu = *ptu; + } + cs.getResiBuf(curUnitArea).copyFrom(saveCS.getResiBuf(curUnitArea)); + } +#endif // all decisions now made. Fully encode the CU, including the headers: m_CABACEstimator->getCtx() = ctxStart; @@ -7564,6 +7901,23 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa cs.dist = finalDistortion; cs.fracBits = finalFracBits; cs.cost = m_pcRdCost->calcRdCost(cs.fracBits, cs.dist); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cs.slice->getSPS()->getUseColorTrans()) + { + if (cs.cost < cs.tmpColorSpaceCost) + { + cs.tmpColorSpaceCost = cs.cost; + if (m_pcEncCfg->getRGBFormatFlag()) + { + cs.firstColorSpaceSelected = cu.colorTransform || !cu.rootCbf; + } + else + { + cs.firstColorSpaceSelected = !cu.colorTransform || !cu.rootCbf; + } + } + } +#endif CHECK(cs.tus.size() == 0, "No TUs present"); } @@ -7578,7 +7932,9 @@ uint64_t InterSearch::xGetSymbolFracBitsInter(CodingStructure &cs, Partitioner & if( cu.firstPU->mergeFlag && !cu.rootCbf ) { cu.skip = true; - +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + CHECK(cu.colorTransform, "ACT should not be enabled for skip mode"); +#endif if( cs.pps->getTransquantBypassEnabledFlag() ) { m_CABACEstimator->cu_transquant_bypass_flag( cu ); diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h index bd0982b2e12e509ea661bb0d92a0fc9967f662e1..de6ed7729f6470d85a1600c02ab0d97d9c66c8b1 100644 --- a/source/Lib/EncoderLib/InterSearch.h +++ b/source/Lib/EncoderLib/InterSearch.h @@ -600,6 +600,9 @@ public: void xEncodeInterResidualQT (CodingStructure &cs, Partitioner &partitioner, const ComponentID &compID); void xEstimateInterResidualQT (CodingStructure &cs, Partitioner &partitioner, Distortion *puiZeroDist = NULL , const bool luma = true, const bool chroma = true +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + , PelUnitBuf* orgResi = NULL +#endif ); uint64_t xGetSymbolFracBitsInter (CodingStructure &cs, Partitioner &partitioner); uint64_t xCalcPuMeBits (PredictionUnit& pu); diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp index edfc7e944595e0512797b06d5fb4d839658791c6..e99c01be6140dcebb85af90edc6f6404f6bf065f 100644 --- a/source/Lib/EncoderLib/IntraSearch.cpp +++ b/source/Lib/EncoderLib/IntraSearch.cpp @@ -163,6 +163,9 @@ void IntraSearch::destroy() } m_tmpStorageLCU.destroy(); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_colorTransResiBuf.destroy(); +#endif m_isInitialized = false; #if JVET_P0077_LINE_CG_PALETTE if (m_truncBinBits != nullptr) @@ -238,6 +241,9 @@ void IntraSearch::init( EncCfg* pcEncCfg, IntraPrediction::init( cform, pcEncCfg->getBitDepth( CHANNEL_TYPE_LUMA ) ); m_tmpStorageLCU.create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + m_colorTransResiBuf.create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE))); +#endif for( uint32_t ch = 0; ch < MAX_NUM_TBLOCKS; ch++ ) { @@ -370,7 +376,11 @@ double IntraSearch::findInterCUCost( CodingUnit &cu ) return COST_UNKNOWN; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst, CodingStructure* bestCS) +#else bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst ) +#endif { CodingStructure &cs = *cu.cs; const SPS &sps = *cs.sps; @@ -421,13 +431,36 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, mtsUsageFlag = 0; } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + const bool colorTransformIsEnabled = sps.getUseColorTrans() && !CS::isDualITree(cs); + const bool isFirstColorSpace = colorTransformIsEnabled && ((m_pcEncCfg->getRGBFormatFlag() && cu.colorTransform) || (!m_pcEncCfg->getRGBFormatFlag() && !cu.colorTransform)); + const bool isSecondColorSpace = colorTransformIsEnabled && ((m_pcEncCfg->getRGBFormatFlag() && !cu.colorTransform) || (!m_pcEncCfg->getRGBFormatFlag() && cu.colorTransform)); +#endif + double bestCurrentCost = bestCostSoFar; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool ispCanBeUsed = sps.getUseISP() && cu.mtsFlag == 0 && cu.lfnstIdx == 0 && CU::canUseISP(width, height, cu.cs->sps->getMaxTbSize()); + bool saveDataForISP = ispCanBeUsed && (!colorTransformIsEnabled || isFirstColorSpace); + bool testISP = ispCanBeUsed && (!colorTransformIsEnabled || !cu.colorTransform); +#else bool testISP = sps.getUseISP() && cu.mtsFlag == 0 && cu.lfnstIdx == 0 && CU::canUseISP( width, height, cu.cs->sps->getMaxTbSize() ); +#endif + +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if ( saveDataForISP ) + { + //reset the intra modes lists variables + m_ispCandListHor.clear(); + m_ispCandListVer.clear(); + } +#endif if( testISP ) { //reset the variables used for the tests +#if !JVET_P0517_ADAPTIVE_COLOR_TRANSFORM m_ispCandListHor.clear(); m_ispCandListVer.clear(); +#endif m_regIntraRDListWithCosts.clear(); int numTotalPartsHor = (int)width >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT)); int numTotalPartsVer = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT)); @@ -485,6 +518,25 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, numModesForFullRD = numModesAvailable; #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (isSecondColorSpace) + { + uiRdModeList.clear(); + if (m_numSavedRdModeFirstColorSpace[m_savedRdModeIdx] > 0) + { + for (int i = 0; i < m_numSavedRdModeFirstColorSpace[m_savedRdModeIdx]; i++) + { + uiRdModeList.push_back(m_savedRdModeFirstColorSpace[m_savedRdModeIdx][i]); + } + } + else + { + return false; + } + } + else + { +#endif if( mtsUsageFlag != 2 ) { // this should always be true @@ -671,7 +723,11 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, } } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if ( saveDataForISP ) +#else if ( testISP ) +#endif { // we save the regular intra modes list m_ispCandListHor = uiRdModeList; @@ -871,7 +927,11 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, CandCostList.push_back(0); } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if ( saveDataForISP ) +#else if ( testISP ) +#endif { // we add the MPMs to the list that contains only regular intra modes for (int j = 0; j < numCand; j++) @@ -934,8 +994,6 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, } } - - CHECK( numModesForFullRD != uiRdModeList.size(), "Inconsistent state!" ); // after this point, don't use numModesForFullRD @@ -971,7 +1029,11 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, uiRdModeList.push_back(bestMipMode); } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if ( saveDataForISP ) +#else if ( testISP ) +#endif { m_ispCandListHor.resize(std::min<size_t>(m_ispCandListHor.size(), maxSize)); } @@ -991,6 +1053,9 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, return false; } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + } +#endif int numNonISPModes = (int)uiRdModeList.size(); @@ -1088,19 +1153,34 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, } #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + for (int mode = isSecondColorSpace ? 0 : -2 * int(testBDPCM); mode < (int)uiRdModeList.size(); mode++) +#else for (int mode = -2 * int(testBDPCM); mode < (int)uiRdModeList.size(); mode++) +#endif { // set CU/PU to luma prediction mode ModeInfo uiOrgMode; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (sps.getUseColorTrans() && !m_pcEncCfg->getRGBFormatFlag() && isSecondColorSpace && mode) + { + continue; + } + + if (mode < 0 || (isSecondColorSpace && m_savedBDPCMModeFirstColorSpace[m_savedRdModeIdx][mode])) + { + cu.bdpcmMode = mode < 0 ? -mode : m_savedBDPCMModeFirstColorSpace[m_savedRdModeIdx][mode]; +#else if ( mode < 0 ) { cu.bdpcmMode = -mode; - +#endif #if JVET_P0803_COMBINED_MIP_CLEANUP uiOrgMode = ModeInfo( false, false, 0, NOT_INTRA_SUBPARTITIONS, cu.bdpcmMode == 2 ? VER_IDX : HOR_IDX ); #else uiOrgMode = ModeInfo(false, 0, NOT_INTRA_SUBPARTITIONS, cu.bdpcmMode == 2 ? VER_IDX : HOR_IDX); #endif +#if !JVET_P0517_ADAPTIVE_COLOR_TRANSFORM cu.mipFlag = uiOrgMode.mipFlg; #if JVET_P0803_COMBINED_MIP_CLEANUP pu.mipTransposedFlag = uiOrgMode.mipTrFlg; @@ -1108,18 +1188,25 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, cu.ispMode = uiOrgMode.ispMod; pu.multiRefIdx = uiOrgMode.mRefId; pu.intraDir[CHANNEL_TYPE_LUMA] = uiOrgMode.modeId; +#endif } else { cu.bdpcmMode = 0; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + uiOrgMode = uiRdModeList[mode]; + } + if (!cu.bdpcmMode && uiRdModeList[mode].ispMod == INTRA_SUBPARTITIONS_RESERVED) +#else if (uiRdModeList[mode].ispMod == INTRA_SUBPARTITIONS_RESERVED) +#endif { if (mode == numNonISPModes) // the list needs to be sorted only once { #if JVET_P1026_ISP_LFNST_COMBINATION if (m_pcEncCfg->getUseFastISP()) { - m_modeCtrl->setBestPredModeDCT2( uiBestPUMode.modeId ); + m_modeCtrl->setBestPredModeDCT2(uiBestPUMode.modeId); } if (!xSortISPCandList(bestCurrentCost, csBest->cost, uiBestPUMode)) break; @@ -1133,8 +1220,13 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, #if JVET_P1026_ISP_LFNST_COMBINATION cu.lfnstIdx = m_curIspLfnstIdx; #endif +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + uiOrgMode = uiRdModeList[mode]; + } +#else } uiOrgMode = uiRdModeList[mode]; +#endif cu.mipFlag = uiOrgMode.mipFlg; #if JVET_P0803_COMBINED_MIP_CLEANUP pu.mipTransposedFlag = uiOrgMode.mipTrFlg; @@ -1147,7 +1239,13 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, CHECK(pu.multiRefIdx && (pu.intraDir[0] == PLANAR_IDX), "Error: combination of MRL and Planar mode not supported"); CHECK(cu.ispMode && cu.mipFlag, "Error: combination of ISP and MIP not supported"); CHECK(cu.ispMode && pu.multiRefIdx, "Error: combination of ISP and MRL not supported"); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + CHECK(cu.ispMode&& cu.colorTransform, "Error: combination of ISP and ACT not supported"); + + pu.intraDir[CHANNEL_TYPE_CHROMA] = cu.colorTransform ? DM_CHROMA_IDX : pu.intraDir[CHANNEL_TYPE_CHROMA]; +#else } +#endif // set context models m_CABACEstimator->getCtx() = ctxStart; @@ -1185,6 +1283,13 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, } else { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cu.colorTransform) + { + tmpValidReturn = xRecurIntraCodingACTQT(*csTemp, partitioner, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId, moreProbMTSIdxFirst); + } + else +#endif tmpValidReturn = xRecurIntraCodingLumaQT( *csTemp, partitioner, uiBestPUMode.ispMod ? bestCurrentCost : MAX_DOUBLE, -1, TU_NO_ISP, uiBestPUMode.ispMod, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId, moreProbMTSIdxFirst ); } @@ -1222,6 +1327,15 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, if( tmpValidReturn ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (isFirstColorSpace) + { + if (m_pcEncCfg->getRGBFormatFlag() || !cu.ispMode) + { + sortRdModeListFirstColorSpace(uiOrgMode, csTemp->cost, cu.bdpcmMode, m_savedRdModeFirstColorSpace[m_savedRdModeIdx], m_savedRdCostFirstColorSpace[m_savedRdModeIdx], m_savedBDPCMModeFirstColorSpace[m_savedRdModeIdx], m_numSavedRdModeFirstColorSpace[m_savedRdModeIdx]); + } + } +#endif // check r-d cost if( csTemp->cost < csBest->cost ) { @@ -1280,6 +1394,18 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, } } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (sps.getUseColorTrans() && !CS::isDualITree(cs)) + { + if ((m_pcEncCfg->getRGBFormatFlag() && !cu.colorTransform) && csBest->cost != MAX_DOUBLE && bestCS->cost != MAX_DOUBLE && mode >= 0) + { + if (csBest->cost > bestCS->cost) + { + break; + } + } + } +#endif } // Mode loop cu.ispMode = uiBestPUMode.ispMod; #if JVET_P1026_ISP_LFNST_COMBINATION @@ -1288,6 +1414,13 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, if( validReturn ) { +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cu.colorTransform) + { + cs.useSubStructure(*csBest, partitioner.chType, pu, true, true, keepResi, keepResi); + } + else +#endif cs.useSubStructure( *csBest, partitioner.chType, pu.singleChan( CHANNEL_TYPE_LUMA ), true, true, keepResi, keepResi ); } csBest->releaseIntermediateData(); @@ -1301,6 +1434,12 @@ bool IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, pu.multiRefIdx = uiBestPUMode.mRefId; pu.intraDir[ CHANNEL_TYPE_LUMA ] = uiBestPUMode.modeId; cu.bdpcmMode = bestBDPCMMode; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (cu.colorTransform) + { + CHECK(pu.intraDir[CHANNEL_TYPE_CHROMA] != DM_CHROMA_IDX, "chroma should use DM mode for adaptive color transform"); + } +#endif } } @@ -3629,6 +3768,175 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp } } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void IntraSearch::xIntraCodingACTTUBlock(TransformUnit &tu, const ComponentID &compID, Distortion& ruiDist, std::vector<TrMode>* trModes, const bool loadTr) +{ + if (!tu.blocks[compID].valid()) + { + CHECK(1, "tu does not exist"); + } + + CodingStructure &cs = *tu.cs; + const SPS &sps = *cs.sps; + const Slice &slice = *cs.slice; + const CompArea &area = tu.blocks[compID]; + const CompArea &crArea = tu.blocks[COMPONENT_Cr]; + + PelBuf piOrgResi = cs.getOrgResiBuf(area); + PelBuf piResi = cs.getResiBuf(area); + PelBuf crOrgResi = cs.getOrgResiBuf(crArea); + PelBuf crResi = cs.getResiBuf(crArea); + TCoeff uiAbsSum = 0; + + CHECK(tu.jointCbCr && compID == COMPONENT_Cr, "wrong combination of compID and jointCbCr"); + bool jointCbCr = tu.jointCbCr && compID == COMPONENT_Cb; + + m_pcRdCost->setChromaFormat(cs.sps->getChromaFormatIdc()); + + m_pcTrQuant->lambdaAdjustColorTrans(true); + + if (jointCbCr) + { + ComponentID compIdCode = (tu.jointCbCr >> 1 ? COMPONENT_Cb : COMPONENT_Cr); + m_pcTrQuant->selectLambda(compIdCode); + } + else + { + m_pcTrQuant->selectLambda(compID); + } + +#if JVET_P1006_PICTURE_HEADER + bool flag = slice.getPicHeader()->getLmcsEnabledFlag() && (slice.isIntra() || (!slice.isIntra() && m_pcReshape->getCTUFlag())) && (tu.blocks[compID].width*tu.blocks[compID].height > 4); + if (flag && isChroma(compID) && slice.getPicHeader()->getLmcsChromaResidualScaleFlag()) +#else + bool flag = slice.getLmcsEnabledFlag() && (slice.isIntra() || (!slice.isIntra() && m_pcReshape->getCTUFlag())) && (tu.blocks[compID].width*tu.blocks[compID].height > 4); + if (flag && isChroma(compID) && slice.getLmcsChromaResidualScaleFlag()) +#endif + { + int cResScaleInv = tu.getChromaAdj(); + double cResScale = (double)(1 << CSCALE_FP_PREC) / (double)cResScaleInv; + m_pcTrQuant->setLambda(m_pcTrQuant->getLambda() / (cResScale*cResScale)); + } + + if (jointCbCr) + { + // Lambda is loosened for the joint mode with respect to single modes as the same residual is used for both chroma blocks + const int absIct = abs(TU::getICTMode(tu)); + const double lfact = (absIct == 1 || absIct == 3 ? 0.8 : 0.5); + m_pcTrQuant->setLambda(lfact * m_pcTrQuant->getLambda()); + } + if (sps.getJointCbCrEnabledFlag() && isChroma(compID) && (slice.getSliceQp() > 18)) + { + m_pcTrQuant->setLambda(1.3 * m_pcTrQuant->getLambda()); + } + + if (isLuma(compID)) + { + QpParam cQP(tu, compID); + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + cQP.Qps[qpIdx] = cQP.Qps[qpIdx] + (compID == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + cQP.pers[qpIdx] = cQP.Qps[qpIdx] / 6; + cQP.rems[qpIdx] = cQP.Qps[qpIdx] % 6; + } + + if (trModes) + { +#if JVET_P0273_MTSIntraMaxCand + m_pcTrQuant->transformNxN(tu, compID, cQP, trModes, m_pcEncCfg->getMTSIntraMaxCand()); +#else + m_pcTrQuant->transformNxN(tu, compID, cQP, trModes, CU::isIntra(*tu.cu) ? m_pcEncCfg->getIntraMTSMaxCand() : m_pcEncCfg->getInterMTSMaxCand()); +#endif +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[compID] = trModes->at(0).first; +#else + tu.mtsIdx = trModes->at(0).first; +#endif + } + m_pcTrQuant->transformNxN(tu, compID, cQP, uiAbsSum, m_CABACEstimator->getCtx(), loadTr); + + if (uiAbsSum > 0) + { + m_pcTrQuant->invTransformNxN(tu, compID, piResi, cQP); + } + else + { + piResi.fill(0); + } + } + else + { + int codedCbfMask = 0; + ComponentID codeCompId = (tu.jointCbCr ? (tu.jointCbCr >> 1 ? COMPONENT_Cb : COMPONENT_Cr) : compID); + QpParam qpCbCr(tu, codeCompId); + for (int qpIdx = 0; qpIdx < 2; qpIdx++) + { + qpCbCr.Qps[qpIdx] = qpCbCr.Qps[qpIdx] + (codeCompId == COMPONENT_Cr ? DELTA_QP_FOR_Co : DELTA_QP_FOR_Y_Cg); + qpCbCr.pers[qpIdx] = qpCbCr.Qps[qpIdx] / 6; + qpCbCr.rems[qpIdx] = qpCbCr.Qps[qpIdx] % 6; + } + + if (tu.jointCbCr) + { + ComponentID otherCompId = (codeCompId == COMPONENT_Cr ? COMPONENT_Cb : COMPONENT_Cr); + tu.getCoeffs(otherCompId).fill(0); + TU::setCbfAtDepth(tu, otherCompId, tu.depth, false); + } + + PelBuf& codeResi = (codeCompId == COMPONENT_Cr ? crResi : piResi); + uiAbsSum = 0; + m_pcTrQuant->transformNxN(tu, codeCompId, qpCbCr, uiAbsSum, m_CABACEstimator->getCtx()); + if (uiAbsSum > 0) + { + m_pcTrQuant->invTransformNxN(tu, codeCompId, codeResi, qpCbCr); + codedCbfMask += (codeCompId == COMPONENT_Cb ? 2 : 1); + } + else + { + codeResi.fill(0); + } + + if (tu.jointCbCr) + { + if (tu.jointCbCr == 3 && codedCbfMask == 2) + { + codedCbfMask = 3; + TU::setCbfAtDepth(tu, COMPONENT_Cr, tu.depth, true); + } + if (tu.jointCbCr != codedCbfMask) + { + ruiDist = std::numeric_limits<Distortion>::max(); + m_pcTrQuant->lambdaAdjustColorTrans(false); + return; + } + m_pcTrQuant->invTransformICT(tu, piResi, crResi); + uiAbsSum = codedCbfMask; + } + } + +#if JVET_P1006_PICTURE_HEADER + if (flag && uiAbsSum > 0 && isChroma(compID) && slice.getPicHeader()->getLmcsChromaResidualScaleFlag()) +#else + if (flag && uiAbsSum > 0 && isChroma(compID) && slice.getLmcsChromaResidualScaleFlag()) +#endif + { + piResi.scaleSignal(tu.getChromaAdj(), 0, slice.clpRng(compID)); + if (jointCbCr) + { + crResi.scaleSignal(tu.getChromaAdj(), 0, slice.clpRng(COMPONENT_Cr)); + } + } + + m_pcTrQuant->lambdaAdjustColorTrans(false); + + ruiDist += m_pcRdCost->getDistPart(piOrgResi, piResi, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE); + if (jointCbCr) + { + ruiDist += m_pcRdCost->getDistPart(crOrgResi, crResi, sps.getBitDepth(toChannelType(COMPONENT_Cr)), COMPONENT_Cr, DF_SSE); + } +} +#endif + bool IntraSearch::xIntraCodingLumaISP(CodingStructure& cs, Partitioner& partitioner, const double bestCostSoFar) { int subTuCounter = 0; @@ -4300,66 +4608,718 @@ bool IntraSearch::xRecurIntraCodingLumaQT( CodingStructure &cs, Partitioner &par return retVal; } -ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitioner& partitioner, const double bestCostSoFar, const PartSplit ispType ) +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +bool IntraSearch::xRecurIntraCodingACTQT(CodingStructure &cs, Partitioner &partitioner, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst) { - UnitArea currArea = partitioner.currArea(); - const bool keepResi = cs.sps->getUseLMChroma() || KEEP_PRED_AND_RESI_SIGNALS; - if( !currArea.Cb().valid() ) return ChromaCbfs( false ); + const UnitArea &currArea = partitioner.currArea(); + uint32_t currDepth = partitioner.currTrDepth; + const Slice &slice = *cs.slice; + const SPS &sps = *cs.sps; + bool bCheckFull = !partitioner.canSplit(TU_MAX_TR_SPLIT, cs); + bool bCheckSplit = !bCheckFull; - TransformUnit &currTU = *cs.getTU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA ); - const PredictionUnit &pu = *cs.getPU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA ); + TempCtx ctxStart(m_CtxCache, m_CABACEstimator->getCtx()); + TempCtx ctxBest(m_CtxCache); - bool lumaUsesISP = false; - uint32_t currDepth = partitioner.currTrDepth; - const PPS &pps = *cs.pps; - ChromaCbfs cbfs ( false ); + CodingStructure *csSplit = nullptr; + CodingStructure *csFull = nullptr; + if (bCheckSplit) + { + csSplit = &cs; + } + else if (bCheckFull) + { + csFull = &cs; + } - if (currDepth == currTU.depth) + bool validReturnFull = false; + + if (bCheckFull) { - if (!currArea.Cb().valid() || !currArea.Cr().valid()) - { - return cbfs; - } + TransformUnit &tu = csFull->addTU(CS::getArea(*csFull, currArea, partitioner.chType), partitioner.chType); + tu.depth = currDepth; + const CodingUnit &cu = *csFull->getCU(tu.Y().pos(), CHANNEL_TYPE_LUMA); + const PredictionUnit &pu = *csFull->getPU(tu.Y().pos(), CHANNEL_TYPE_LUMA); + CHECK(!tu.Y().valid() || !tu.Cb().valid() || !tu.Cr().valid(), "Invalid TU"); + CHECK(tu.cu != &cu, "wrong CU fetch"); + CHECK(cu.ispMode, "adaptive color transform cannot be applied to ISP"); + CHECK(pu.intraDir[CHANNEL_TYPE_CHROMA] != DM_CHROMA_IDX, "chroma should use DM mode for adaptive color transform"); + // 1. intra prediction and forward color transform - CodingStructure &saveCS = *m_pSaveCS[1]; - saveCS.pcv = cs.pcv; - saveCS.picture = cs.picture; - saveCS.area.repositionTo( cs.area ); - saveCS.initStructData( MAX_INT, false, true ); + PelUnitBuf orgBuf = csFull->getOrgBuf(tu); + PelUnitBuf predBuf = csFull->getPredBuf(tu); + PelUnitBuf resiBuf = csFull->getResiBuf(tu); + PelUnitBuf orgResiBuf = csFull->getOrgResiBuf(tu); - if( !currTU.cu->isSepTree() && currTU.cu->ispMode ) + for (int i = 0; i < getNumberValidComponents(tu.chromaFormat); i++) { - saveCS.clearCUs(); - CodingUnit& auxCU = saveCS.addCU( *currTU.cu, partitioner.chType ); - auxCU.ispMode = currTU.cu->ispMode; - saveCS.sps = currTU.cs->sps; - saveCS.clearPUs(); - saveCS.addPU( *currTU.cu->firstPU, partitioner.chType ); - } + ComponentID compID = (ComponentID)i; + const CompArea &area = tu.blocks[compID]; + const ChannelType chType = toChannelType(compID); - TransformUnit &tmpTU = saveCS.addTU(currArea, partitioner.chType); + PelBuf piOrg = orgBuf.bufs[compID]; + PelBuf piPred = predBuf.bufs[compID]; + PelBuf piResi = resiBuf.bufs[compID]; + initIntraPatternChType(*tu.cu, area); + if (PU::isMIP(pu, chType)) + { +#if JVET_P0803_COMBINED_MIP_CLEANUP + initIntraMip(pu, area); +#endif + predIntraMip(compID, piPred, pu); + } + else + { + predIntraAng(compID, piPred, pu); + } - cs.setDecomp(currArea.Cb(), true); // set in advance (required for Cb2/Cr2 in 4:2:2 video) + piResi.copyFrom(piOrg); +#if JVET_P1006_PICTURE_HEADER + if (slice.getPicHeader()->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag() && compID == COMPONENT_Y) +#else + if (slice.getLmcsEnabledFlag() && m_pcReshape->getCTUFlag() && compID == COMPONENT_Y) +#endif + { + CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size()); + PelBuf tmpPred = m_tmpStorageLCU.getBuf(tmpArea); + tmpPred.copyFrom(piPred); + piResi.rspSignal(m_pcReshape->getFwdLUT()); + piResi.subtract(tmpPred); + } + else + piResi.subtract(piPred); + } - const unsigned numTBlocks = ::getNumberValidTBlocks( *cs.pcv ); + resiBuf.colorSpaceConvert(orgResiBuf, true); - CompArea& cbArea = currTU.blocks[COMPONENT_Cb]; - CompArea& crArea = currTU.blocks[COMPONENT_Cr]; - double bestCostCb = MAX_DOUBLE; - double bestCostCr = MAX_DOUBLE; - Distortion bestDistCb = 0; - Distortion bestDistCr = 0; - int maxModesTested = 0; - bool earlyExitISP = false; + // 2. luma residual optimization + double dSingleCostLuma = MAX_DOUBLE; + bool checkTransformSkip = sps.getTransformSkipEnabledFlag(); + int bestLumaModeId = 0; + uint8_t nNumTransformCands = cu.mtsFlag ? 4 : 1; + uint8_t numTransformIndexCands = nNumTransformCands; - TempCtx ctxStartTU( m_CtxCache ); - TempCtx ctxStart ( m_CtxCache ); - TempCtx ctxBest ( m_CtxCache ); + const bool tsAllowed = TU::isTSAllowed(tu, COMPONENT_Y); +#if JVET_P1026_MTS_SIGNALLING + const bool mtsAllowed = CU::isMTSAllowed(cu, COMPONENT_Y); +#else + const bool mtsAllowed = TU::isMTSAllowed(tu, COMPONENT_Y); +#endif + std::vector<TrMode> trModes; - ctxStartTU = m_CABACEstimator->getCtx(); + if (sps.getUseLFNST()) + { + checkTransformSkip &= tsAllowed; + checkTransformSkip &= !cu.mtsFlag; + checkTransformSkip &= !cu.lfnstIdx; + + if (!cu.mtsFlag && checkTransformSkip) + { + trModes.push_back(TrMode(0, true)); //DCT2 + trModes.push_back(TrMode(1, true)); //TS + } + } + else + { + nNumTransformCands = 1 + (tsAllowed ? 1 : 0) + (mtsAllowed ? 4 : 0); // DCT + TS + 4 MTS = 6 tests + + trModes.push_back(TrMode(0, true)); //DCT2 + if (tsAllowed) + { + trModes.push_back(TrMode(1, true)); + } + if (mtsAllowed) + { + for (int i = 2; i < 6; i++) + { + trModes.push_back(TrMode(i, true)); + } + } + } + + CodingStructure &saveLumaCS = *m_pSaveCS[0]; + TransformUnit *tmpTU = nullptr; + Distortion singleDistTmpLuma = 0; + uint64_t singleTmpFracBits = 0; + double singleCostTmp = 0; + int firstCheckId = (sps.getUseLFNST() && mtsCheckRangeFlag && cu.mtsFlag) ? mtsFirstCheckId : 0; + int lastCheckId = sps.getUseLFNST() ? ((mtsCheckRangeFlag && cu.mtsFlag) ? (mtsLastCheckId + (int)checkTransformSkip) : (numTransformIndexCands - (firstCheckId + 1) + (int)checkTransformSkip)) : trModes[nNumTransformCands - 1].first; + bool isNotOnlyOneMode = sps.getUseLFNST() ? lastCheckId != firstCheckId : nNumTransformCands != 1; + + if (isNotOnlyOneMode) + { + saveLumaCS.pcv = csFull->pcv; + saveLumaCS.picture = csFull->picture; + saveLumaCS.area.repositionTo(csFull->area); + saveLumaCS.clearTUs(); + tmpTU = &saveLumaCS.addTU(currArea, partitioner.chType); + } + + bool cbfBestMode = false; + bool cbfBestModeValid = false; + bool cbfDCT2 = true; + + m_pcRdCost->lambdaAdjustColorTrans(true, COMPONENT_Y); + + for (int modeId = firstCheckId; modeId <= lastCheckId; modeId++) + { + uint8_t transformIndex = modeId; + csFull->getResiBuf(tu.Y()).copyFrom(csFull->getOrgResiBuf(tu.Y())); + + m_CABACEstimator->getCtx() = ctxStart; + m_CABACEstimator->resetBits(); + + if (sps.getUseLFNST()) + { + if ((transformIndex < lastCheckId) || ((transformIndex == lastCheckId) && !checkTransformSkip)) //we avoid this if the mode is transformSkip + { + // Skip checking other transform candidates if zero CBF is encountered and it is the best transform so far + if (m_pcEncCfg->getUseFastLFNST() && transformIndex && !cbfBestMode && cbfBestModeValid) + { + continue; + } + } + } + else + { +#if JVET_AHG14_LOSSLESS + if (!(m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING)) + { +#endif + if (!cbfDCT2 || (m_pcEncCfg->getUseTransformSkipFast() && bestLumaModeId == 1)) + { + break; + } + if (!trModes[modeId].second) + { + continue; + } +#if JVET_AHG14_LOSSLESS + } +#endif +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[COMPONENT_Y] = trModes[modeId].first; +#else + tu.mtsIdx = trModes[modeId].first; +#endif + } + + singleDistTmpLuma = 0; + if (sps.getUseLFNST()) + { + if (cu.mtsFlag) + { + if (moreProbMTSIdxFirst) + { + uint32_t uiIntraMode = pu.intraDir[CHANNEL_TYPE_LUMA]; + + if (transformIndex == 1) + { +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DST7_DCT8 : MTS_DCT8_DST7; +#else + tu.mtsIdx = (uiIntraMode < 34) ? MTS_DST7_DCT8 : MTS_DCT8_DST7; +#endif + } + else if (transformIndex == 2) + { +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DCT8_DST7 : MTS_DST7_DCT8; +#else + tu.mtsIdx = (uiIntraMode < 34) ? MTS_DCT8_DST7 : MTS_DST7_DCT8; +#endif + } + else + { +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex; +#else + tu.mtsIdx = MTS_DST7_DST7 + transformIndex; +#endif + } + } + else + { +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex; +#else + tu.mtsIdx = MTS_DST7_DST7 + transformIndex; +#endif + } + } + else + { +#if JVET_P0058_CHROMA_TS + tu.mtsIdx[COMPONENT_Y] = transformIndex; +#else + tu.mtsIdx = transformIndex; +#endif + } + + if (!cu.mtsFlag && checkTransformSkip) + { + xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma, modeId == 0 ? &trModes : nullptr, true); + if (modeId == 0) + { + for (int i = 0; i < 2; i++) + { + if (trModes[i].second) + { + lastCheckId = trModes[i].first; + } + } + } + } + else + { + xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma); + } + } + else + { + if (nNumTransformCands > 1) + { + xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma, modeId == 0 ? &trModes : nullptr, true); + if (modeId == 0) + { + for (int i = 0; i < nNumTransformCands; i++) + { + if (trModes[i].second) + { + lastCheckId = trModes[i].first; + } + } + } + } + else + { + xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma); + } + } + + //----- determine rate and r-d cost ----- + if ((sps.getUseLFNST() ? (modeId == lastCheckId && modeId != 0 && checkTransformSkip) : (trModes[modeId].first != 0)) && !TU::getCbfAtDepth(tu, COMPONENT_Y, currDepth)) + { + //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden. + singleCostTmp = MAX_DOUBLE; + } + else + { + singleTmpFracBits = xGetIntraFracBitsQT(*csFull, partitioner, true, false, -1, TU_NO_ISP); + singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma); + } + + if (singleCostTmp < dSingleCostLuma) + { + dSingleCostLuma = singleCostTmp; + validReturnFull = true; + + if (sps.getUseLFNST()) + { + bestLumaModeId = modeId; + cbfBestMode = TU::getCbfAtDepth(tu, COMPONENT_Y, currDepth); + cbfBestModeValid = true; + } + else + { + bestLumaModeId = trModes[modeId].first; + if (trModes[modeId].first == 0) + { + cbfDCT2 = TU::getCbfAtDepth(tu, COMPONENT_Y, currDepth); + } + } + + if (bestLumaModeId != lastCheckId) + { + saveLumaCS.getResiBuf(tu.Y()).copyFrom(csFull->getResiBuf(tu.Y())); + tmpTU->copyComponentFrom(tu, COMPONENT_Y); + ctxBest = m_CABACEstimator->getCtx(); + } + } + } + + m_pcRdCost->lambdaAdjustColorTrans(false, COMPONENT_Y); + + if (sps.getUseLFNST()) + { + if (!validReturnFull) + { + csFull->cost = MAX_DOUBLE; + return false; + } + } + else + { + CHECK(!validReturnFull, "no transform mode was tested for luma"); + } + + csFull->setDecomp(currArea.Y(), true); + csFull->setDecomp(currArea.Cb(), true); + + if (bestLumaModeId != lastCheckId) + { + csFull->getResiBuf(tu.Y()).copyFrom(saveLumaCS.getResiBuf(tu.Y())); + tu.copyComponentFrom(*tmpTU, COMPONENT_Y); + m_CABACEstimator->getCtx() = ctxBest; + } + + // 3 chroma residual optimization + CodingStructure &saveChromaCS = *m_pSaveCS[1]; + saveChromaCS.pcv = csFull->pcv; + saveChromaCS.picture = csFull->picture; + saveChromaCS.area.repositionTo(csFull->area); + saveChromaCS.initStructData(MAX_INT, false, true); + tmpTU = &saveChromaCS.addTU(currArea, partitioner.chType); + + CompArea& cbArea = tu.blocks[COMPONENT_Cb]; + CompArea& crArea = tu.blocks[COMPONENT_Cr]; + + ctxStart = m_CABACEstimator->getCtx(); + m_CABACEstimator->resetBits(); + tu.jointCbCr = 0; + +#if JVET_P1006_PICTURE_HEADER + bool doReshaping = (slice.getPicHeader()->getLmcsEnabledFlag() && slice.getPicHeader()->getLmcsChromaResidualScaleFlag() && (slice.isIntra() || m_pcReshape->getCTUFlag()) && (cbArea.width * cbArea.height > 4)); +#else + bool doReshaping = (slice.getLmcsEnabledFlag() && slice.getLmcsChromaResidualScaleFlag() && (slice.isIntra() || m_pcReshape->getCTUFlag()) && (cbArea.width * cbArea.height > 4)); +#endif + if (doReshaping) + { + const Area area = tu.Y().valid() ? tu.Y() : Area(recalcPosition(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].pos()), recalcSize(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].size())); + const CompArea &areaY = CompArea(COMPONENT_Y, tu.chromaFormat, area); + int adj = m_pcReshape->calculateChromaAdjVpduNei(tu, areaY); + tu.setChromaAdj(adj); + } + + CompStorage orgResiCb[5], orgResiCr[5]; // 0:std, 1-3:jointCbCr (placeholder at this stage), 4:crossComp + orgResiCb[0].create(cbArea); + orgResiCr[0].create(crArea); + orgResiCb[0].copyFrom(csFull->getOrgResiBuf(cbArea)); + orgResiCr[0].copyFrom(csFull->getOrgResiBuf(crArea)); + if (doReshaping) + { + int cResScaleInv = tu.getChromaAdj(); + orgResiCb[0].scaleSignal(cResScaleInv, 1, slice.clpRng(COMPONENT_Cb)); + orgResiCr[0].scaleSignal(cResScaleInv, 1, slice.clpRng(COMPONENT_Cr)); + } + + // 3.1 regular chroma residual coding + csFull->getResiBuf(cbArea).copyFrom(orgResiCb[0]); + csFull->getResiBuf(crArea).copyFrom(orgResiCr[0]); + + for (uint32_t c = COMPONENT_Cb; c < ::getNumberValidTBlocks(*csFull->pcv); c++) + { + const ComponentID compID = ComponentID(c); + Distortion singleDistChroma = 0; + xIntraCodingACTTUBlock(tu, compID, singleDistChroma); + xGetIntraFracBitsQTChroma(tu, compID); + } + + Position tuPos = tu.Y(); + tuPos.relativeTo(cu.Y()); + const UnitArea relativeUnitArea(tu.chromaFormat, Area(tuPos, tu.Y().size())); + PelUnitBuf invColorTransResidual = m_colorTransResiBuf.getBuf(relativeUnitArea); + csFull->getResiBuf(tu).colorSpaceConvert(invColorTransResidual, false); + + Distortion totalDist = 0; + for (uint32_t c = COMPONENT_Y; c < ::getNumberValidTBlocks(*csFull->pcv); c++) + { + const ComponentID compID = ComponentID(c); + const CompArea& area = tu.blocks[compID]; + PelBuf piOrg = csFull->getOrgBuf(area); + PelBuf piReco = csFull->getRecoBuf(area); + PelBuf piPred = csFull->getPredBuf(area); + PelBuf piResi = invColorTransResidual.bufs[compID]; + + piReco.reconstruct(piPred, piResi, cs.slice->clpRng(compID)); + + if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getReshaper() +#if JVET_P1006_PICTURE_HEADER + & slice.getPicHeader()->getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD())))) +#else + & slice.getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD())))) +#endif + { + const CPelBuf orgLuma = csFull->getOrgBuf(csFull->area.blocks[COMPONENT_Y]); + if (compID == COMPONENT_Y && !(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled())) + { + CompArea tmpArea1(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size()); + PelBuf tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1); + tmpRecLuma.copyFrom(piReco); + tmpRecLuma.rspSignal(m_pcReshape->getInvLUT()); + totalDist += m_pcRdCost->getDistPart(piOrg, tmpRecLuma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma); + } + else + { + totalDist += m_pcRdCost->getDistPart(piOrg, piReco, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma); + } + } + else + { + totalDist += m_pcRdCost->getDistPart(piOrg, piReco, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE); + } + } + + m_CABACEstimator->getCtx() = ctxStart; + uint64_t totalBits = xGetIntraFracBitsQT(*csFull, partitioner, true, true, -1, TU_NO_ISP); + double totalCost = m_pcRdCost->calcRdCost(totalBits, totalDist); + + saveChromaCS.getResiBuf(cbArea).copyFrom(csFull->getResiBuf(cbArea)); + saveChromaCS.getResiBuf(crArea).copyFrom(csFull->getResiBuf(crArea)); + saveChromaCS.getRecoBuf(tu).copyFrom(csFull->getRecoBuf(tu)); + tmpTU->copyComponentFrom(tu, COMPONENT_Cb); + tmpTU->copyComponentFrom(tu, COMPONENT_Cr); + ctxBest = m_CABACEstimator->getCtx(); + + // 3.2 jointCbCr + double bestCostJointCbCr = totalCost; + Distortion bestDistJointCbCr = totalDist; + uint64_t bestBitsJointCbCr = totalBits; + int bestJointCbCr = tu.jointCbCr; assert(!bestJointCbCr); + + bool lastIsBest = false; + std::vector<int> jointCbfMasksToTest; + if (sps.getJointCbCrEnabledFlag() && (TU::getCbf(tu, COMPONENT_Cb) || TU::getCbf(tu, COMPONENT_Cr))) + { + jointCbfMasksToTest = m_pcTrQuant->selectICTCandidates(tu, orgResiCb, orgResiCr); + } + + for (int cbfMask : jointCbfMasksToTest) + { + m_CABACEstimator->getCtx() = ctxStart; + m_CABACEstimator->resetBits(); + + Distortion distTmp = 0; + tu.jointCbCr = (uint8_t)cbfMask; + + csFull->getResiBuf(cbArea).copyFrom(orgResiCb[cbfMask]); + csFull->getResiBuf(crArea).copyFrom(orgResiCr[cbfMask]); + xIntraCodingACTTUBlock(tu, COMPONENT_Cb, distTmp); + + double costTmp = std::numeric_limits<double>::max(); + uint64_t bitsTmp = 0; + if (distTmp < std::numeric_limits<Distortion>::max()) + { + csFull->getResiBuf(tu).colorSpaceConvert(invColorTransResidual, false); + distTmp = 0; + for (uint32_t c = COMPONENT_Y; c < ::getNumberValidTBlocks(*csFull->pcv); c++) + { + const ComponentID compID = ComponentID(c); + const CompArea& area = tu.blocks[compID]; + PelBuf piOrg = csFull->getOrgBuf(area); + PelBuf piReco = csFull->getRecoBuf(area); + PelBuf piPred = csFull->getPredBuf(area); + PelBuf piResi = invColorTransResidual.bufs[compID]; + + piReco.reconstruct(piPred, piResi, cs.slice->clpRng(compID)); + if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getReshaper() +#if JVET_P1006_PICTURE_HEADER + & slice.getPicHeader()->getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD())))) +#else + & slice.getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD())))) +#endif + { + const CPelBuf orgLuma = csFull->getOrgBuf(csFull->area.blocks[COMPONENT_Y]); + if (compID == COMPONENT_Y && !(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled())) + { + CompArea tmpArea1(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size()); + PelBuf tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1); + tmpRecLuma.copyFrom(piReco); + tmpRecLuma.rspSignal(m_pcReshape->getInvLUT()); + distTmp += m_pcRdCost->getDistPart(piOrg, tmpRecLuma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma); + } + else + { + distTmp += m_pcRdCost->getDistPart(piOrg, piReco, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma); + } + } + else + { + distTmp += m_pcRdCost->getDistPart(piOrg, piReco, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE); + } + } + + bitsTmp = xGetIntraFracBitsQT(*csFull, partitioner, true, true, -1, TU_NO_ISP); + costTmp = m_pcRdCost->calcRdCost(bitsTmp, distTmp); + } + + if (costTmp < bestCostJointCbCr) + { + bestCostJointCbCr = costTmp; + bestDistJointCbCr = distTmp; + bestBitsJointCbCr = bitsTmp; + bestJointCbCr = tu.jointCbCr; + lastIsBest = (cbfMask == jointCbfMasksToTest.back()); + + // store data + if (!lastIsBest) + { + saveChromaCS.getResiBuf(cbArea).copyFrom(csFull->getResiBuf(cbArea)); + saveChromaCS.getResiBuf(crArea).copyFrom(csFull->getResiBuf(crArea)); + saveChromaCS.getRecoBuf(tu).copyFrom(csFull->getRecoBuf(tu)); + tmpTU->copyComponentFrom(tu, COMPONENT_Cb); + tmpTU->copyComponentFrom(tu, COMPONENT_Cr); + + ctxBest = m_CABACEstimator->getCtx(); + } + } + } + + if (!lastIsBest) + { + csFull->getResiBuf(cbArea).copyFrom(saveChromaCS.getResiBuf(cbArea)); + csFull->getResiBuf(crArea).copyFrom(saveChromaCS.getResiBuf(crArea)); + csFull->getRecoBuf(tu).copyFrom(saveChromaCS.getRecoBuf(tu)); + tu.copyComponentFrom(*tmpTU, COMPONENT_Cb); + tu.copyComponentFrom(*tmpTU, COMPONENT_Cr); + + m_CABACEstimator->getCtx() = ctxBest; + } + tu.jointCbCr = bestJointCbCr; + csFull->picture->getRecoBuf(tu).copyFrom(csFull->getRecoBuf(tu)); + + csFull->dist += bestDistJointCbCr; + csFull->fracBits += bestBitsJointCbCr; + csFull->cost = m_pcRdCost->calcRdCost(csFull->fracBits, csFull->dist); + } + + bool validReturnSplit = false; + if (bCheckSplit) + { + if (partitioner.canSplit(TU_MAX_TR_SPLIT, *csSplit)) + { + partitioner.splitCurrArea(TU_MAX_TR_SPLIT, *csSplit); + } + + bool splitIsSelected = true; + do + { + bool tmpValidReturnSplit = xRecurIntraCodingACTQT(*csSplit, partitioner, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId, moreProbMTSIdxFirst); + if (sps.getUseLFNST()) + { + if (!tmpValidReturnSplit) + { + splitIsSelected = false; + break; + } + } + else + { + CHECK(!tmpValidReturnSplit, "invalid RD of sub-TU partitions for ACT"); + } + } while (partitioner.nextPart(*csSplit)); + + partitioner.exitCurrSplit(); + + if (splitIsSelected) + { + unsigned compCbf[3] = { 0, 0, 0 }; + for (auto &currTU : csSplit->traverseTUs(currArea, partitioner.chType)) + { + for (unsigned ch = 0; ch < getNumberValidTBlocks(*csSplit->pcv); ch++) + { + compCbf[ch] |= (TU::getCbfAtDepth(currTU, ComponentID(ch), currDepth + 1) ? 1 : 0); + } + } + + for (auto &currTU : csSplit->traverseTUs(currArea, partitioner.chType)) + { + TU::setCbfAtDepth(currTU, COMPONENT_Y, currDepth, compCbf[COMPONENT_Y]); + TU::setCbfAtDepth(currTU, COMPONENT_Cb, currDepth, compCbf[COMPONENT_Cb]); + TU::setCbfAtDepth(currTU, COMPONENT_Cr, currDepth, compCbf[COMPONENT_Cr]); + } + + m_CABACEstimator->getCtx() = ctxStart; + csSplit->fracBits = xGetIntraFracBitsQT(*csSplit, partitioner, true, true, -1, TU_NO_ISP); + csSplit->cost = m_pcRdCost->calcRdCost(csSplit->fracBits, csSplit->dist); + + validReturnSplit = true; + } + } + + bool retVal = false; + if (csFull || csSplit) + { + if (sps.getUseLFNST()) + { + if (validReturnFull || validReturnSplit) + { + retVal = true; + } + } + else + { + CHECK(!validReturnFull && !validReturnSplit, "illegal TU optimization"); + retVal = true; + } + } + return retVal; +} +#endif + +ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitioner& partitioner, const double bestCostSoFar, const PartSplit ispType ) +{ + UnitArea currArea = partitioner.currArea(); + const bool keepResi = cs.sps->getUseLMChroma() || KEEP_PRED_AND_RESI_SIGNALS; + if( !currArea.Cb().valid() ) return ChromaCbfs( false ); + + + TransformUnit &currTU = *cs.getTU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA ); + const PredictionUnit &pu = *cs.getPU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA ); + + bool lumaUsesISP = false; + uint32_t currDepth = partitioner.currTrDepth; + const PPS &pps = *cs.pps; + ChromaCbfs cbfs ( false ); + + if (currDepth == currTU.depth) + { + if (!currArea.Cb().valid() || !currArea.Cr().valid()) + { + return cbfs; + } + + + CodingStructure &saveCS = *m_pSaveCS[1]; + saveCS.pcv = cs.pcv; + saveCS.picture = cs.picture; + saveCS.area.repositionTo( cs.area ); + saveCS.initStructData( MAX_INT, false, true ); + + if( !currTU.cu->isSepTree() && currTU.cu->ispMode ) + { + saveCS.clearCUs(); + CodingUnit& auxCU = saveCS.addCU( *currTU.cu, partitioner.chType ); + auxCU.ispMode = currTU.cu->ispMode; + saveCS.sps = currTU.cs->sps; + saveCS.clearPUs(); + saveCS.addPU( *currTU.cu->firstPU, partitioner.chType ); + } + + TransformUnit &tmpTU = saveCS.addTU(currArea, partitioner.chType); + + + cs.setDecomp(currArea.Cb(), true); // set in advance (required for Cb2/Cr2 in 4:2:2 video) + + const unsigned numTBlocks = ::getNumberValidTBlocks( *cs.pcv ); + + CompArea& cbArea = currTU.blocks[COMPONENT_Cb]; + CompArea& crArea = currTU.blocks[COMPONENT_Cr]; + double bestCostCb = MAX_DOUBLE; + double bestCostCr = MAX_DOUBLE; + Distortion bestDistCb = 0; + Distortion bestDistCr = 0; + int maxModesTested = 0; + bool earlyExitISP = false; + + TempCtx ctxStartTU( m_CtxCache ); + TempCtx ctxStart ( m_CtxCache ); + TempCtx ctxBest ( m_CtxCache ); + + ctxStartTU = m_CABACEstimator->getCtx(); currTU.jointCbCr = 0; // Do predictions here to avoid repeating the "default0Save1Load2" stuff @@ -4831,7 +5791,74 @@ uint64_t IntraSearch::xFracModeBitsIntra(PredictionUnit &pu, const uint32_t &uiM return m_CABACEstimator->getEstFracBits(); } +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM +void IntraSearch::sortRdModeListFirstColorSpace(ModeInfo mode, double cost, char bdpcmMode, ModeInfo* rdModeList, double* rdCostList, char* bdpcmModeList, int& candNum) +{ + if (candNum == 0) + { + rdModeList[0] = mode; + rdCostList[0] = cost; + bdpcmModeList[0] = bdpcmMode; + candNum++; + return; + } + int insertPos = -1; + for (int pos = candNum - 1; pos >= 0; pos--) + { + if (cost < rdCostList[pos]) + { + insertPos = pos; + } + } + + if (insertPos >= 0) + { + for (int i = candNum - 1; i >= insertPos; i--) + { + rdModeList[i + 1] = rdModeList[i]; + rdCostList[i + 1] = rdCostList[i]; + bdpcmModeList[i + 1] = bdpcmModeList[i]; + } + rdModeList[insertPos] = mode; + rdCostList[insertPos] = cost; + bdpcmModeList[insertPos] = bdpcmMode; + candNum++; + } + else + { + rdModeList[candNum] = mode; + rdCostList[candNum] = cost; + bdpcmModeList[candNum] = bdpcmMode; + candNum++; + } + + CHECK(candNum > FAST_UDI_MAX_RDMODE_NUM, "exceed intra mode candidate list capacity"); + + return; +} + +void IntraSearch::invalidateBestRdModeFirstColorSpace() +{ + int numSaveRdClass = 4 * NUM_LFNST_NUM_PER_SET * 2; + int savedRdModeListSize = FAST_UDI_MAX_RDMODE_NUM; + + for (int i = 0; i < numSaveRdClass; i++) + { + m_numSavedRdModeFirstColorSpace[i] = 0; + for (int j = 0; j < savedRdModeListSize; j++) + { +#if JVET_P0803_COMBINED_MIP_CLEANUP + m_savedRdModeFirstColorSpace[i][j] = ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, 0); +#else + m_savedRdModeFirstColorSpace[i][j] = ModeInfo(false, 0, NOT_INTRA_SUBPARTITIONS, 0); +#endif + m_savedBDPCMModeFirstColorSpace[i][j] = 0; + m_savedRdCostFirstColorSpace[i][j] = MAX_DOUBLE; + } + } +} +#endif void IntraSearch::encPredIntraDPCM( const ComponentID &compID, PelBuf &pOrg, PelBuf &pDst, const uint32_t &uiDirMode ) { @@ -5196,7 +6223,6 @@ void IntraSearch::xGetNextISPMode(ModeInfo& modeInfo, const ModeInfo* lastMode, CHECK(leftIntraMode != candidate.modeId || rightIntraMode != candidate.modeId, "wrong intra mode and lfnstIdx values!"); numSubPartsRefMode = m_ispTestedModes[refLfnstIdx].getNumCompletedSubParts((ISPType)candidate.ispMod, candidate.modeId); CHECK(numSubPartsRefMode <= 0, "Wrong value of the number of subpartitions completed!"); - } else { diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h index fa157f153fc1fb44d02a993f904d9c4efe1d4411..e21e6f1778e98f70a68a0dea19b92e1082ca83be 100644 --- a/source/Lib/EncoderLib/IntraSearch.h +++ b/source/Lib/EncoderLib/IntraSearch.h @@ -359,6 +359,13 @@ private: ModeInfo m_savedRdModeList[ NUM_LFNST_NUM_PER_SET ][ NUM_LUMA_MODE ]; int32_t m_savedNumRdModes[ NUM_LFNST_NUM_PER_SET ]; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + ModeInfo m_savedRdModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM]; + char m_savedBDPCMModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM]; + double m_savedRdCostFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM]; + int m_numSavedRdModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2]; + int m_savedRdModeIdx; +#endif static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> m_uiSavedRdModeListLFNST; static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> m_uiSavedHadModeListLFNST; @@ -367,6 +374,9 @@ private: static_vector<double, FAST_UDI_MAX_RDMODE_NUM> m_dSavedHadListLFNST; PelStorage m_tmpStorageLCU; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + PelStorage m_colorTransResiBuf; +#endif protected: // interface to option EncCfg* m_pcEncCfg; @@ -430,8 +440,11 @@ public: double findInterCUCost ( CodingUnit &cu ); public: - +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool estIntraPredLumaQT(CodingUnit &cu, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false, CodingStructure* bestCS = NULL); +#else bool estIntraPredLumaQT ( CodingUnit &cu, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false ); +#endif void estIntraPredChromaQT ( CodingUnit &cu, Partitioner& pm, const double maxCostAllowed = MAX_DOUBLE ); void PLTSearch ( CodingStructure &cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp); #if !JVET_P0077_LINE_CG_PALETTE @@ -440,6 +453,12 @@ public: uint64_t xFracModeBitsIntra (PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &compID); void invalidateBestModeCost () { for( int i = 0; i < NUM_LFNST_NUM_PER_SET; i++ ) m_bestModeCostValid[ i ] = false; }; +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void sortRdModeListFirstColorSpace(ModeInfo mode, double cost, char bdpcmMode, ModeInfo* rdModeList, double* rdCostList, char* bdpcmModeList, int& candNum); + void invalidateBestRdModeFirstColorSpace(); + void setSavedRdModeIdx(int idx) { m_savedRdModeIdx = idx; } +#endif + protected: // ------------------------------------------------------------------------------------------------------------------- @@ -468,9 +487,15 @@ protected: #endif void xIntraCodingTUBlock (TransformUnit &tu, const ComponentID &compID, const bool &checkCrossCPrediction, Distortion& ruiDist, const int &default0Save1Load2 = 0, uint32_t* numSig = nullptr, std::vector<TrMode>* trModes=nullptr, const bool loadTr=false ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + void xIntraCodingACTTUBlock(TransformUnit &tu, const ComponentID &compID, Distortion& ruiDist, std::vector<TrMode>* trModes = nullptr, const bool loadTr = false); +#endif ChromaCbfs xRecurIntraChromaCodingQT( CodingStructure &cs, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE, const PartSplit ispType = TU_NO_ISP ); bool xRecurIntraCodingLumaQT ( CodingStructure &cs, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE, const int subTuIdx = -1, const PartSplit ispType = TU_NO_ISP, const bool ispIsCurrentWinner = false, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + bool xRecurIntraCodingACTQT(CodingStructure &cs, Partitioner& pm, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false); +#endif bool xIntraCodingLumaISP ( CodingStructure& cs, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE ); void encPredIntraDPCM( const ComponentID &compID, PelBuf &pOrg, PelBuf &pDst, const uint32_t &uiDirMode ); diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp index 0bd805f5266b228ac130bdeec9a4c5dd824f5671..61e63d55ab360859e0c730ea6cdd9cddddc5ac06 100644 --- a/source/Lib/EncoderLib/VLCWriter.cpp +++ b/source/Lib/EncoderLib/VLCWriter.cpp @@ -989,6 +989,12 @@ void HLSWriter::codeSPS( const SPS* pcSPS ) WRITE_FLAG( pcSPS->getAffineAmvrEnabledFlag() ? 1 : 0, "sps_affine_amvr_enabled_flag" ); } WRITE_FLAG( pcSPS->getUseGBi() ? 1 : 0, "gbi_flag" ); +#if JVET_P0517_ADAPTIVE_COLOR_TRANSFORM + if (pcSPS->getChromaFormatIdc() == CHROMA_444) + { + WRITE_FLAG(pcSPS->getUseColorTrans() ? 1 : 0, "act_flag"); + } +#endif if (pcSPS->getChromaFormatIdc() == CHROMA_444) { WRITE_FLAG(pcSPS->getPLTMode() ? 1 : 0, "plt_flag" );