From ce4d7d997f9eb2d4b472f73f9ac638ee53af9693 Mon Sep 17 00:00:00 2001 From: Taoran Lu <tlu@dolby.com> Date: Fri, 26 Jul 2019 01:21:56 +0200 Subject: [PATCH] JVET-O0432: LMCS encoder improvement --- cfg/encoder_intra_vtm.cfg | 4 +- cfg/encoder_lowdelay_P_vtm.cfg | 4 +- cfg/encoder_lowdelay_vtm.cfg | 4 +- cfg/encoder_randomaccess_vtm.cfg | 4 +- cfg/per-class/classH1.cfg | 3 +- cfg/per-class/classH2.cfg | 4 +- doc/software-manual.tex | 44 ++ source/App/EncoderApp/EncAppCfg.cpp | 39 ++ source/App/EncoderApp/EncAppCfg.h | 5 + source/Lib/CommonLib/RdCost.cpp | 8 + source/Lib/CommonLib/Slice.h | 7 + source/Lib/CommonLib/TypeDef.h | 2 + source/Lib/EncoderLib/EncGOP.cpp | 31 + source/Lib/EncoderLib/EncReshape.cpp | 819 +++++++++++++++++++++++++++ source/Lib/EncoderLib/EncReshape.h | 37 ++ 15 files changed, 1008 insertions(+), 7 deletions(-) diff --git a/cfg/encoder_intra_vtm.cfg b/cfg/encoder_intra_vtm.cfg index 4fa541779..ddc4dedad 100644 --- a/cfg/encoder_intra_vtm.cfg +++ b/cfg/encoder_intra_vtm.cfg @@ -110,7 +110,9 @@ ALF : 1 IBC : 0 # turned off in CTC AllowDisFracMMVD : 1 AffineAmvr : 0 -LumaReshapeEnable : 1 # luma reshaping. 0: disable 1:enable +LMCSEnable : 1 # LMCS: 0: disable, 1:enable +LMCSSignalType : 0 # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG +LMCSUpdateCtrl : 1 # LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP MIP : 1 # Fast tools diff --git a/cfg/encoder_lowdelay_P_vtm.cfg b/cfg/encoder_lowdelay_P_vtm.cfg index b2447be26..2d24145ce 100644 --- a/cfg/encoder_lowdelay_P_vtm.cfg +++ b/cfg/encoder_lowdelay_P_vtm.cfg @@ -126,7 +126,9 @@ MHIntra : 1 IBC : 0 # turned off in CTC AllowDisFracMMVD : 1 AffineAmvr : 0 -LumaReshapeEnable : 1 # luma reshaping. 0: disable 1:enable +LMCSEnable : 1 # LMCS: 0: disable, 1:enable +LMCSSignalType : 0 # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG +LMCSUpdateCtrl : 2 # LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP MIP : 1 # Fast tools diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg index 2a681a36a..1c7925030 100644 --- a/cfg/encoder_lowdelay_vtm.cfg +++ b/cfg/encoder_lowdelay_vtm.cfg @@ -130,7 +130,9 @@ Triangle : 1 IBC : 0 # turned off in CTC AllowDisFracMMVD : 1 AffineAmvr : 0 -LumaReshapeEnable : 1 # luma reshaping. 0: disable 1:enable +LMCSEnable : 1 # LMCS: 0: disable, 1:enable +LMCSSignalType : 0 # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG +LMCSUpdateCtrl : 2 # LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP MIP : 1 # Fast tools diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg index d3e23e2b4..7938ee72a 100644 --- a/cfg/encoder_randomaccess_vtm.cfg +++ b/cfg/encoder_randomaccess_vtm.cfg @@ -146,7 +146,9 @@ Triangle : 1 IBC : 0 # turned off in CTC AllowDisFracMMVD : 1 AffineAmvr : 1 -LumaReshapeEnable : 1 # luma reshaping. 0: disable 1:enable +LMCSEnable : 1 # LMCS: 0: disable, 1:enable +LMCSSignalType : 0 # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG +LMCSUpdateCtrl : 0 # LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP MIP : 1 DMVR : 1 SMVD : 1 diff --git a/cfg/per-class/classH1.cfg b/cfg/per-class/classH1.cfg index 8db1a0003..4505d2c22 100644 --- a/cfg/per-class/classH1.cfg +++ b/cfg/per-class/classH1.cfg @@ -3,7 +3,8 @@ LumaLevelToDeltaQPMode : 1 # Change luma delta QP based on aver isSDR : 0 # 1: SDR in PQ container, 0: HDR # ======= LMCS ======================= -LumaReshapeEnable : 0 # turned off in HDR CTC +LMCSEnable : 0 # turned off in HDR CTC +LMCSSignalType : 1 # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG #======== Chroma QP scale ============ WCGPPSEnable : 1 # enable WCG Chroma scale diff --git a/cfg/per-class/classH2.cfg b/cfg/per-class/classH2.cfg index babeabd73..1b26c7f09 100644 --- a/cfg/per-class/classH2.cfg +++ b/cfg/per-class/classH2.cfg @@ -2,8 +2,8 @@ LumaLevelToDeltaQPMode : 0 # Change luma delta QP based on average luma # ======= LMCS ======================= -LumaReshapeEnable : 0 # turned off in HDR CTC -ReshapeSignalType : 2 # set signal type to HLG if enabled +LMCSEnable : 1 # turned on in HLG CTC +LMCSSignalType : 2 # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG #======== Chroma QP scale ============ WCGPPSEnable : 0 # enable WCG Chroma scale diff --git a/doc/software-manual.tex b/doc/software-manual.tex index 32224ab11..2603c64e4 100644 --- a/doc/software-manual.tex +++ b/doc/software-manual.tex @@ -2071,6 +2071,50 @@ Enables or disables the use of low frequency non-separable transform (LFNST). Enables or disables the fast encoding of low frequency non-separable transform (LFNST). \\ +\Option{LMCSEnable} & +%\ShortOption{\None} & +\Default{true} & +Enables or disables the use of LMCS (luma mapping with chroma scaling). +\\ + +\Option{LMCSSignalType} & +%\ShortOption{\None} & +\Default{0} & +LMCS signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG. +\\ + +\Option{LMCSUpdateCtrl} & +%\ShortOption{\None} & +\Default{0} & +LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP. +\par +\begin{tabular}{cp{0.45\textwidth}} + 0 & Random access: derive a new LMCS model at each IRAP.\\ + 1 & All intra: derive a new LMCS model at each intra slice.\\ + 2 & Low delay: derive a new LMCS model every second. \\ +\end{tabular} +\\ + +\Option{LMCSAdpOption} & +%\ShortOption{\None} & +\Default{0} & +Adaptive LMCS mapping derivation options: Options 1 to 4 are for experimental testing purposes and need to set parameter LMCSInitialCW. +\par +\begin{tabular}{cp{0.45\textwidth}} + 0 & Automatic adaptive algorithm (default).\\ + 1 & Derives LMCS mapping with input LMCSInitialCW and enables LMCS for all slices. Uses a static LMCS mapping for low QP ($QP<=22$). \\ + 2 & Derives LMCS mapping with input LMCSInitialCW and enables LMCS only for slices in lowest temporal layer. \\ + 3 & In addition to 1, disables LMCS for intra slices. \\ + 4 & Derives LMCS mapping with input LMCSInitialCW and enables LMCS only for inter slices. \\ +\end{tabular} +\\ + +\Option{LMCSInitialCW} & +%\ShortOption{\None} & +\Default{0} & +LMCS initial total codeword (valid values [$0 - 1023$]) to be used in LMCS mapping derivation when LMCSAdpOption is not equal to 0. +\\ + \end{OptionTableNoShorthand} %% diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 780ca157c..ef2155302 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -919,8 +919,18 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("VirtualBoundariesPosX", cfg_virtualBoundariesPosX, cfg_virtualBoundariesPosX, "Locations of the vertical virtual boundaries in units of luma samples") ("VirtualBoundariesPosY", cfg_virtualBoundariesPosY, cfg_virtualBoundariesPosY, "Locations of the horizontal virtual boundaries in units of luma samples") ("EncDbOpt", m_encDbOpt, false, "Encoder optimization with deblocking filter") +#if JVET_O0432_LMCS_ENCODER + ("LMCSEnable", m_lumaReshapeEnable, false, "Enable LMCS (luma mapping with chroma scaling") + ("LMCSSignalType", m_reshapeSignalType, 0u, "Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG") + ("LMCSUpdateCtrl", m_updateCtrl, 0, "LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP") + ("LMCSAdpOption", m_adpOption, 0, "LMCS adaptation options: 0:automatic(default)," + "1: rsp both (CW66 for QP<=22), 2: rsp TID0 (for all QP)," + "3: rsp inter(CW66 for QP<=22), 4: rsp inter(for all QP).") + ("LMCSInitialCW", m_initialCW, 0u, "LMCS initial total codeword (0~1023) when LMCSAdpOption > 0") +#else ("LumaReshapeEnable", m_lumaReshapeEnable, false, "Enable Reshaping for Luma Channel") ("ReshapeSignalType", m_reshapeSignalType, 0u, "Input signal type: 0: SDR, 1:PQ, 2:HLG") +#endif ("IntraCMD", m_intraCMD, 0u, "IntraChroma MD: 0: none, 1:fixed to default wPSNR weight") ("LCTUFast", m_useFastLCTU, false, "Fast methods for large CTU") ("FastMrg", m_useFastMrg, false, "Fast methods for inter merge") @@ -2045,10 +2055,17 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) m_reshapeCW.binCW.resize(3); m_reshapeCW.rspFps = m_iFrameRate; +#if !JVET_O0432_LMCS_ENCODER m_reshapeCW.rspIntraPeriod = m_iIntraPeriod; +#endif m_reshapeCW.rspPicSize = m_iSourceWidth*m_iSourceHeight; m_reshapeCW.rspFpsToIp = std::max(16, 16 * (int)(round((double)m_iFrameRate /16.0))); m_reshapeCW.rspBaseQP = m_iQP; +#if JVET_O0432_LMCS_ENCODER + m_reshapeCW.updateCtrl = m_updateCtrl; + m_reshapeCW.adpOption = m_adpOption; + m_reshapeCW.initialCW = m_initialCW; +#endif #if ENABLE_TRACING g_trace_ctx = tracing_init(sTracingFile, sTracingRule); if( bTracingChannelsList && g_trace_ctx ) @@ -2434,7 +2451,11 @@ bool EncAppCfg::xCheckParameter() { m_intraCMD = 1; } +#if JVET_O0432_LMCS_ENCODER + else if (m_lumaReshapeEnable && (m_reshapeSignalType == RESHAPE_SIGNAL_SDR || m_reshapeSignalType == RESHAPE_SIGNAL_HLG)) +#else else if (m_lumaReshapeEnable && m_reshapeSignalType == RESHAPE_SIGNAL_SDR) +#endif { m_intraCMD = 0; } @@ -2442,6 +2463,18 @@ bool EncAppCfg::xCheckParameter() { m_lumaReshapeEnable = false; } +#if JVET_O0432_LMCS_ENCODER + if (m_lumaReshapeEnable) + { + xConfirmPara(m_updateCtrl < 0, "Min. LMCS Update Control is 0"); + xConfirmPara(m_updateCtrl > 2, "Max. LMCS Update Control is 2"); + xConfirmPara(m_adpOption < 0, "Min. LMCS Adaptation Option is 0"); + xConfirmPara(m_adpOption > 4, "Max. LMCS Adaptation Option is 4"); + xConfirmPara(m_initialCW < 0, "Min. Initial Total Codeword is 0"); + xConfirmPara(m_initialCW > 1023, "Max. Initial Total Codeword is 1023"); + if (m_updateCtrl > 0 && m_adpOption > 2) { m_adpOption -= 2; } + } +#endif xConfirmPara( m_cbQpOffset < -12, "Min. Chroma Cb QP Offset is -12" ); xConfirmPara( m_cbQpOffset > 12, "Max. Chroma Cb QP Offset is 12" ); @@ -3360,7 +3393,13 @@ void EncAppCfg::xPrintParameter() msg(VERBOSE, "Reshape:%d ", m_lumaReshapeEnable); if (m_lumaReshapeEnable) { +#if JVET_O0432_LMCS_ENCODER + msg(VERBOSE, "(Signal:%s ", m_reshapeSignalType == 0 ? "SDR" : (m_reshapeSignalType == 2 ? "HDR-HLG" : "HDR-PQ")); + msg(VERBOSE, "Opt:%d", m_adpOption); + if (m_adpOption > 0) { msg(VERBOSE, " CW:%d", m_initialCW); } +#else msg(VERBOSE, "(Sigal:%s ", m_reshapeSignalType==0? "SDR" : "HDR-PQ"); +#endif msg(VERBOSE, ") "); } msg(VERBOSE, "MIP:%d ", m_MIP); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 9ba6b9afb..d8bf7be18 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -304,6 +304,11 @@ protected: uint32_t m_reshapeSignalType; uint32_t m_intraCMD; ReshapeCW m_reshapeCW; +#if JVET_O0432_LMCS_ENCODER + int m_updateCtrl; + int m_adpOption; + uint32_t m_initialCW; +#endif bool m_encDbOpt; unsigned m_uiMaxCUWidth; ///< max. CU width in pixel unsigned m_uiMaxCUHeight; ///< max. CU height in pixel diff --git a/source/Lib/CommonLib/RdCost.cpp b/source/Lib/CommonLib/RdCost.cpp index 5118b933f..5f0dcb193 100644 --- a/source/Lib/CommonLib/RdCost.cpp +++ b/source/Lib/CommonLib/RdCost.cpp @@ -2938,7 +2938,11 @@ void RdCost::restoreReshapeLumaLevelToWeightTable() void RdCost::updateReshapeLumaLevelToWeightTable(SliceReshapeInfo &sliceReshape, Pel *wtTable, double cwt) { +#if JVET_O0432_LMCS_ENCODER + if (m_signalType == RESHAPE_SIGNAL_SDR || m_signalType == RESHAPE_SIGNAL_HLG) +#else if (m_signalType == RESHAPE_SIGNAL_SDR) +#endif { if (sliceReshape.getSliceReshapeModelPresentFlag()) { @@ -2991,7 +2995,11 @@ Distortion RdCost::getWeightedMSE(int compIdx, const Pel org, const Pel cur, con } // use luma to get weight double weight = 1.0; +#if JVET_O0432_LMCS_ENCODER + if (m_signalType == RESHAPE_SIGNAL_SDR || m_signalType == RESHAPE_SIGNAL_HLG) +#else if (m_signalType == RESHAPE_SIGNAL_SDR) +#endif { if (compIdx == COMPONENT_Y) { diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index 68cd5bb50..19d77128a 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -393,8 +393,15 @@ public: struct ReshapeCW { std::vector<uint32_t> binCW; +#if JVET_O0432_LMCS_ENCODER + int updateCtrl; + int adpOption; + uint32_t initialCW; +#endif int rspPicSize; +#if !JVET_O0432_LMCS_ENCODER int rspIntraPeriod; +#endif int rspFps; int rspBaseQP; int rspTid; diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index da35fe636..3d59785aa 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -116,6 +116,8 @@ #define JVET_O0590_REDUCE_DMVR_ORIG_MV_COST 1 // Reduce the DMVR cost of the original MV +#define JVET_O0432_LMCS_ENCODER 1 // JVET-O0432: LMCS encoder improvement + #define JVET_O0429_CRS_LAMBDA_FIX 1 // JVET-O0429: fix encoder lambda rounding used in CRS #define JVET_O0428_LMCS_CLEANUP 1 // JVET-O0428: LMCS cleanups diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index a24ce82a2..220161396 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -2104,10 +2104,17 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, { m_pcReshaper->preAnalyzerHDR(pcPic, pcSlice->getSliceType(), m_pcCfg->getReshapeCW(), m_pcCfg->getDualITree()); } +#if JVET_O0432_LMCS_ENCODER + else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG) + { + m_pcReshaper->preAnalyzerLMCS(pcPic, m_pcCfg->getReshapeSignalType(), pcSlice->getSliceType(), m_pcCfg->getReshapeCW()); + } +#else else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR) { m_pcReshaper->preAnalyzerSDR(pcPic, pcSlice->getSliceType(), m_pcCfg->getReshapeCW(), m_pcCfg->getDualITree()); } +#endif else { THROW("Reshaper for other signal currently not defined!"); @@ -2120,6 +2127,16 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, m_pcReshaper->initLUTfromdQPModel(); m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTableChromaMD(m_pcReshaper->getInvLUT()); } +#if JVET_O0432_LMCS_ENCODER + else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG) + { + if (m_pcReshaper->getReshapeFlag()) + { + m_pcReshaper->constructReshaperLMCS(); + m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight()); + } + } +#else else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR) { if (m_pcReshaper->getReshapeFlag()) @@ -2128,6 +2145,7 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight()); } } +#endif else { THROW("Reshaper for other signal currently not defined!"); @@ -2158,6 +2176,18 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, { m_pcEncLib->getRdCost()->restoreReshapeLumaLevelToWeightTable(); } +#if JVET_O0432_LMCS_ENCODER + else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG) + { + int modIP = pcPic->getPOC() - pcPic->getPOC() / m_pcCfg->getReshapeCW().rspFpsToIp * m_pcCfg->getReshapeCW().rspFpsToIp; + if (m_pcReshaper->getReshapeFlag() && m_pcCfg->getReshapeCW().updateCtrl == 2 && modIP == 0) + { + m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(true); + m_pcReshaper->constructReshaperLMCS(); + m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight()); + } + } +#else else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR) { int modIP = pcPic->getPOC() - pcPic->getPOC() / m_pcCfg->getReshapeCW().rspFpsToIp * m_pcCfg->getReshapeCW().rspFpsToIp; @@ -2168,6 +2198,7 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight()); } } +#endif else { THROW("Reshaper for other signal currently not defined!"); diff --git a/source/Lib/EncoderLib/EncReshape.cpp b/source/Lib/EncoderLib/EncReshape.cpp index 1217aea13..0c95c850f 100644 --- a/source/Lib/EncoderLib/EncReshape.cpp +++ b/source/Lib/EncoderLib/EncReshape.cpp @@ -103,6 +103,11 @@ void EncReshape::createEnc(int picWidth, int picHeight, uint32_t maxCUWidth, ui m_widthInCtus = (m_picWidth + m_maxCUWidth - 1) / m_maxCUWidth; m_heightInCtus = (m_picHeight + m_maxCUHeight - 1) / m_maxCUHeight; m_numCtuInFrame = m_widthInCtus * m_heightInCtus; +#if JVET_O0432_LMCS_ENCODER + m_binNum = PIC_CODE_CW_BINS; + initSeqStats(m_srcSeqStats); + initSeqStats(m_rspSeqStats); +#endif } void EncReshape::destroy() @@ -140,6 +145,417 @@ void EncReshape::preAnalyzerHDR(Picture *pcPic, const SliceType sliceType, const \param sliceType describe the slice type \param reshapeCW describe some input info */ +#if JVET_O0432_LMCS_ENCODER +void EncReshape::initSeqStats(SeqInfo &stats) +{ + for (int i = 0; i < m_binNum; i++) + { + stats.binVar[i] = 0.0; + stats.binHist[i] = 0.0; + stats.normVar[i] = 0.0; + } + stats.nonZeroCnt = 0; + stats.weightVar = 0.0; + stats.weightNorm = 0.0; + stats.minBinVar = 0.0; + stats.maxBinVar = 0.0; + stats.meanBinVar = 0.0; + stats.ratioStdU = 0.0; + stats.ratioStdV = 0.0; +} +void EncReshape::calcSeqStats(Picture *pcPic, SeqInfo &stats) +{ + PelBuf picY = pcPic->getOrigBuf(COMPONENT_Y); + const int width = picY.width; + const int height = picY.height; + const int stride = picY.stride; + uint32_t winLens = (m_binNum == PIC_CODE_CW_BINS) ? (std::min(height, width) / 240) : 2; + winLens = winLens > 0 ? winLens : 1; + + int64_t tempSq = 0; + int64_t topSum = 0, topSumSq = 0; + int64_t leftSum = 0, leftSumSq = 0; + int64_t *leftColSum = new int64_t[width]; + int64_t *leftColSumSq = new int64_t[width]; + int64_t *topRowSum = new int64_t[height]; + int64_t *topRowSumSq = new int64_t[height]; + int64_t *topColSum = new int64_t[width]; + int64_t *topColSumSq = new int64_t[width]; + uint32_t *binCnt = new uint32_t[m_binNum]; + memset(leftColSum, 0, width * sizeof(int64_t)); + memset(leftColSumSq, 0, width * sizeof(int64_t)); + memset(topRowSum, 0, height * sizeof(int64_t)); + memset(topRowSumSq, 0, height * sizeof(int64_t)); + memset(topColSum, 0, width * sizeof(int64_t)); + memset(topColSumSq, 0, width * sizeof(int64_t)); + memset(binCnt, 0, m_binNum * sizeof(uint32_t)); + + initSeqStats(stats); + for (uint32_t y = 0; y < height; y++) + { + for (uint32_t x = 0; x < width; x++) + { + const Pel pxlY = picY.buf[x]; + int64_t sum = 0, sumSq = 0; + uint32_t numPixInPart = 0; + uint32_t y1 = std::max((int)(y - winLens), 0); + uint32_t y2 = std::min((int)(y + winLens), (height - 1)); + uint32_t x1 = std::max((int)(x - winLens), 0); + uint32_t x2 = std::min((int)(x + winLens), (width - 1)); + uint32_t bx = 0, by = 0; + const Pel *pWinY = &picY.buf[0]; + numPixInPart = (x2 - x1 + 1) * (y2 - y1 + 1); + + if (x == 0 && y == 0) + { + for (by = y1; by <= y2; by++) + { + for (bx = x1; bx <= x2; bx++) + { + tempSq = pWinY[bx] * pWinY[bx]; + leftSum += pWinY[bx]; + leftSumSq += tempSq; + leftColSum[bx] += pWinY[bx]; + leftColSumSq[bx] += tempSq; + topColSum[bx] += pWinY[bx]; + topColSumSq[bx] += tempSq; + topRowSum[by] += pWinY[bx]; + topRowSumSq[by] += tempSq; + } + pWinY += stride; + } + topSum = leftSum; + topSumSq = leftSumSq; + sum = leftSum; + sumSq = leftSumSq; + } + else if (x == 0 && y > 0) + { + if (y < height - winLens) + { + pWinY += winLens*stride; + topRowSum[y + winLens] = 0; + topRowSumSq[y + winLens] = 0; + for (bx = x1; bx <= x2; bx++) + { + topRowSum[y + winLens] += pWinY[bx]; + topRowSumSq[y + winLens] += pWinY[bx] * pWinY[bx]; + } + topSum += topRowSum[y + winLens]; + topSumSq += topRowSumSq[y + winLens]; + } + if (y > winLens) + { + topSum -= topRowSum[y - 1 - winLens]; + topSumSq -= topRowSumSq[y - 1 - winLens]; + } + memset(leftColSum, 0, width * sizeof(int64_t)); + memset(leftColSumSq, 0, width * sizeof(int64_t)); + pWinY = &picY.buf[0]; + pWinY -= (y <= winLens ? y : winLens)*stride; + for (by = y1; by <= y2; by++) + { + for (bx = x1; bx <= x2; bx++) + { + leftColSum[bx] += pWinY[bx]; + leftColSumSq[bx] += pWinY[bx] * pWinY[bx]; + } + pWinY += stride; + } + leftSum = topSum; + leftSumSq = topSumSq; + sum = topSum; + sumSq = topSumSq; + } + else if (x > 0) + { + if (x < width - winLens) + { + pWinY -= (y <= winLens ? y : winLens)*stride; + if (y == 0) + { + leftColSum[x + winLens] = 0; + leftColSumSq[x + winLens] = 0; + for (by = y1; by <= y2; by++) + { + leftColSum[x + winLens] += pWinY[x + winLens]; + leftColSumSq[x + winLens] += pWinY[x + winLens] * pWinY[x + winLens]; + pWinY += stride; + } + } + else + { + leftColSum[x + winLens] = topColSum[x + winLens]; + leftColSumSq[x + winLens] = topColSumSq[x + winLens]; + if (y < height - winLens) + { + pWinY = &picY.buf[0]; + pWinY += winLens * stride; + leftColSum[x + winLens] += pWinY[x + winLens]; + leftColSumSq[x + winLens] += pWinY[x + winLens] * pWinY[x + winLens]; + } + if (y > winLens) + { + pWinY = &picY.buf[0]; + pWinY -= (winLens + 1) * stride; + leftColSum[x + winLens] -= pWinY[x + winLens]; + leftColSumSq[x + winLens] -= pWinY[x + winLens] * pWinY[x + winLens]; + } + } + topColSum[x + winLens] = leftColSum[x + winLens]; + topColSumSq[x + winLens] = leftColSumSq[x + winLens]; + leftSum += leftColSum[x + winLens]; + leftSumSq += leftColSumSq[x + winLens]; + } + if (x > winLens) + { + leftSum -= leftColSum[x - 1 - winLens]; + leftSumSq -= leftColSumSq[x - 1 - winLens]; + } + sum = leftSum; + sumSq = leftSumSq; + } + + double average = double(sum) / numPixInPart; + double variance = double(sumSq) / numPixInPart - average * average; + int binLen = m_reshapeLUTSize / m_binNum; + uint32_t binIdx = (uint32_t)(pxlY / binLen); + average = average / (double)(1 << (m_lumaBD - 10)); + variance = variance / (double)(1 << (2 * (m_lumaBD - 10))); + binIdx = (uint32_t)((pxlY >> (m_lumaBD - 10)) / binLen); + double varLog10 = log10(variance + 1.0); + stats.binVar[binIdx] += varLog10; + binCnt[binIdx]++; + } + picY.buf += stride; + } + + for (int b = 0; b < m_binNum; b++) + { + stats.binHist[b] = (double)binCnt[b] / (double)(m_reshapeCW.rspPicSize); + stats.binVar[b] = (binCnt[b] > 0) ? (stats.binVar[b] / binCnt[b]) : 0.0; + } + delete[] binCnt; + delete[] topColSum; + delete[] topColSumSq; + delete[] topRowSum; + delete[] topRowSumSq; + delete[] leftColSum; + delete[] leftColSumSq; + + stats.minBinVar = 5.0; + stats.maxBinVar = 0.0; + stats.meanBinVar = 0.0; + stats.nonZeroCnt = 0; + for (int b = 0; b < m_binNum; b++) + { + if (stats.binHist[b] > 0.001) + { + stats.nonZeroCnt++; + stats.meanBinVar += stats.binVar[b]; + if (stats.binVar[b] > stats.maxBinVar) { stats.maxBinVar = stats.binVar[b]; } + if (stats.binVar[b] < stats.minBinVar) { stats.minBinVar = stats.binVar[b]; } + } + } + stats.meanBinVar /= (double)stats.nonZeroCnt; + for (int b = 0; b < m_binNum; b++) + { + if (stats.meanBinVar > 0.0) + stats.normVar[b] = stats.binVar[b] / stats.meanBinVar; + stats.weightVar += stats.binHist[b] * stats.binVar[b]; + stats.weightNorm += stats.binHist[b] * stats.normVar[b]; + } + + picY = pcPic->getOrigBuf(COMPONENT_Y); + PelBuf picU = pcPic->getOrigBuf(COMPONENT_Cb); + PelBuf picV = pcPic->getOrigBuf(COMPONENT_Cr); + const int widthC = picU.width; + const int heightC = picU.height; + const int strideC = picU.stride; + double avgY = 0.0, avgU = 0.0, avgV = 0.0; + double varY = 0.0, varU = 0.0, varV = 0.0; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + avgY += picY.buf[x]; + varY += picY.buf[x] * picY.buf[x]; + } + picY.buf += stride; + } + for (int y = 0; y < heightC; y++) + { + for (int x = 0; x < widthC; x++) + { + avgU += picU.buf[x]; + avgV += picV.buf[x]; + varU += picU.buf[x] * picU.buf[x]; + varV += picV.buf[x] * picV.buf[x]; + } + picU.buf += strideC; + picV.buf += strideC; + } + avgY = avgY / (width * height); + avgU = avgU / (widthC * heightC); + avgV = avgV / (widthC * heightC); + varY = varY / (width * height) - avgY * avgY; + varU = varU / (widthC * heightC) - avgU * avgU; + varV = varV / (widthC * heightC) - avgV * avgV; + if (varY > 0) + { + stats.ratioStdU = sqrt(varU) / sqrt(varY); + stats.ratioStdV = sqrt(varV) / sqrt(varY); + } +} +void EncReshape::preAnalyzerLMCS(Picture *pcPic, const uint32_t signalType, const SliceType sliceType, const ReshapeCW& reshapeCW) +{ + m_sliceReshapeInfo.sliceReshaperModelPresentFlag = true; + m_sliceReshapeInfo.sliceReshaperEnableFlag = true; + int modIP = pcPic->getPOC() - pcPic->getPOC() / reshapeCW.rspFpsToIp * reshapeCW.rspFpsToIp; + if (sliceType == I_SLICE || (reshapeCW.updateCtrl == 2 && modIP == 0)) + { + if (m_sliceReshapeInfo.sliceReshaperModelPresentFlag == true) + { + m_reshapeCW = reshapeCW; + m_binNum = PIC_CODE_CW_BINS; + int stdMin = 16 << (m_lumaBD - 8); + int stdMax = 235 << (m_lumaBD - 8); + int binLen = m_reshapeLUTSize / m_binNum; + int startBinIdx = stdMin / binLen; + int endBinIdx = stdMax / binLen; + m_sliceReshapeInfo.reshaperModelMinBinIdx = startBinIdx; + m_sliceReshapeInfo.reshaperModelMaxBinIdx = endBinIdx; + m_initCWAnalyze = m_lumaBD > 10 ? (binLen >> (m_lumaBD - 10)) : m_lumaBD < 10 ? (binLen << (10 - m_lumaBD)) : binLen; + for (int b = 0; b < m_binNum; b++) { m_binCW[b] = m_initCWAnalyze; } + + m_reshape = true; + m_useAdpCW = false; + m_exceedSTD = false; + m_chromaWeight = 1.0; + m_sliceReshapeInfo.enableChromaAdj = 1; + m_rateAdpMode = 0; m_tcase = 0; + bool intraAdp = true, interAdp = true; + + calcSeqStats(pcPic, m_srcSeqStats); + if (m_binNum == PIC_CODE_CW_BINS) + { + if ((m_srcSeqStats.binHist[0] + m_srcSeqStats.binHist[m_binNum - 1]) > 0.005) { m_exceedSTD = true; } + if (m_srcSeqStats.binHist[m_binNum - 1] > 0.0003) { intraAdp = false; interAdp = false; } + if (m_srcSeqStats.binHist[0] > 0.03) { intraAdp = false; interAdp = false; } + } + else if (m_binNum == PIC_ANALYZE_CW_BINS) + { + if ((m_srcSeqStats.binHist[0] + m_srcSeqStats.binHist[1] + m_srcSeqStats.binHist[m_binNum - 2] + m_srcSeqStats.binHist[m_binNum - 1]) > 0.01) { m_exceedSTD = true; } + if ((m_srcSeqStats.binHist[m_binNum - 2] + m_srcSeqStats.binHist[m_binNum - 1]) > 0.0003) { intraAdp = false; interAdp = false; } + if ((m_srcSeqStats.binHist[0] + m_srcSeqStats.binHist[1]) > 0.03) { intraAdp = false; interAdp = false; } + } + if (m_exceedSTD) + { + for (int i = 0; i < m_binNum; i++) + { + if (m_srcSeqStats.binHist[i] > 0 && i < startBinIdx) { startBinIdx = i; } + if (m_srcSeqStats.binHist[i] > 0 && i > endBinIdx) { endBinIdx = i; } + } + m_sliceReshapeInfo.reshaperModelMinBinIdx = startBinIdx; + m_sliceReshapeInfo.reshaperModelMaxBinIdx = endBinIdx; + } + + if ((m_srcSeqStats.ratioStdU + m_srcSeqStats.ratioStdV) > 1.5 && m_srcSeqStats.binHist[1] > 0.5) { intraAdp = false; interAdp = false; } + if (m_srcSeqStats.ratioStdU > 0.36 && m_srcSeqStats.ratioStdV > 0.2 && m_reshapeCW.rspPicSize > 5184000) + { + m_sliceReshapeInfo.enableChromaAdj = 0; m_chromaWeight = 1.05; + if ((m_srcSeqStats.ratioStdU + m_srcSeqStats.ratioStdV) < 0.69) { m_chromaWeight = 0.95; } + } + + if (interAdp) + { + if (m_reshapeCW.adpOption) + { + m_reshapeCW.binCW[0] = 0; m_reshapeCW.binCW[1] = m_reshapeCW.initialCW; + m_rateAdpMode = m_reshapeCW.adpOption - 2 * (m_reshapeCW.adpOption / 2); + if (m_reshapeCW.adpOption == 2) { m_tcase = 9; } + else if (m_reshapeCW.adpOption > 2) { intraAdp = false; } + } + else if (signalType == RESHAPE_SIGNAL_SDR) + { + m_reshapeCW.binCW[0] = 0; m_reshapeCW.binCW[1] = 1022; + deriveReshapeParametersSDR(&intraAdp, &interAdp); + } + else if (signalType == RESHAPE_SIGNAL_HLG) + { + if (m_reshapeCW.updateCtrl == 0) + { + m_rateAdpMode = 0; m_tcase = 9; + m_reshapeCW.binCW[1] = 952; + if (m_srcSeqStats.meanBinVar < 2.5) { m_reshapeCW.binCW[1] = 840; } + } + else + { + m_useAdpCW = true; + m_rateAdpMode = 2; + if (m_binNum == PIC_CODE_CW_BINS) { m_reshapeCW.binCW[0] = 72; m_reshapeCW.binCW[1] = 58; } + else if (m_binNum == PIC_ANALYZE_CW_BINS) { m_reshapeCW.binCW[0] = 36; m_reshapeCW.binCW[1] = 30; } + if (m_srcSeqStats.meanBinVar < 2.5) { intraAdp = false; interAdp = false; } + } + } + } + + if (m_rateAdpMode == 2 && reshapeCW.rspBaseQP <= 22) { intraAdp = false; interAdp = false; } + m_sliceReshapeInfo.sliceReshaperEnableFlag = intraAdp; + if (!intraAdp && !interAdp) + { + m_sliceReshapeInfo.sliceReshaperModelPresentFlag = false; + m_reshape = false; + return; + } + + if (m_rateAdpMode == 1 && reshapeCW.rspBaseQP <= 22) + { + for (int i = 0; i < m_binNum; i++) + { + if (i >= startBinIdx && i <= endBinIdx) { m_binCW[i] = m_initCWAnalyze + 2; } + else { m_binCW[i] = 0; } + } + } + else if (m_useAdpCW) + { + if (signalType == RESHAPE_SIGNAL_SDR && m_reshapeCW.updateCtrl == 2) + { + m_binNum = PIC_ANALYZE_CW_BINS; + startBinIdx = startBinIdx * 2; + endBinIdx = endBinIdx * 2 + 1; + calcSeqStats(pcPic, m_srcSeqStats); + } + double alpha = 1.0, beta = 0.0; + deriveReshapeParameters(m_srcSeqStats.binVar, startBinIdx, endBinIdx, m_reshapeCW, alpha, beta); + for (int i = 0; i < m_binNum; i++) + { + if (i >= startBinIdx && i <= endBinIdx) { m_binCW[i] = (uint32_t)round(alpha*m_srcSeqStats.binVar[i] + beta); } + else { m_binCW[i] = 0; } + } + } + else + { + cwPerturbation(startBinIdx, endBinIdx, (uint16_t)m_reshapeCW.binCW[1]); + } + cwReduction(startBinIdx, endBinIdx); + } + m_chromaAdj = m_sliceReshapeInfo.enableChromaAdj; + } + else // Inter slices + { + m_sliceReshapeInfo.sliceReshaperModelPresentFlag = false; + m_sliceReshapeInfo.enableChromaAdj = m_chromaAdj; + if (!m_reshape) { m_sliceReshapeInfo.sliceReshaperEnableFlag = false; } + else + { + const int cTid = m_reshapeCW.rspTid; + bool enableRsp = m_tcase == 5 ? false : (m_tcase < 5 ? (cTid < m_tcase + 1 ? false : true) : (cTid <= 10 - m_tcase ? true : false)); + m_sliceReshapeInfo.sliceReshaperEnableFlag = enableRsp; + } + } +} +#else void EncReshape::preAnalyzerSDR(Picture *pcPic, const SliceType sliceType, const ReshapeCW& reshapeCW, bool isDualT) { m_sliceReshapeInfo.sliceReshaperModelPresentFlag = true; @@ -487,6 +903,7 @@ void EncReshape::preAnalyzerSDR(Picture *pcPic, const SliceType sliceType, const } } } +#endif // Bubble Sort to descending order with index void EncReshape::bubbleSortDsd(double* array, int * idx, int n) @@ -510,6 +927,307 @@ void EncReshape::bubbleSortDsd(double* array, int * idx, int n) } } +#if JVET_O0432_LMCS_ENCODER +void EncReshape::cwPerturbation(int startBinIdx, int endBinIdx, uint16_t maxCW) +{ + for (int i = 0; i < m_binNum; i++) + { + if (i >= startBinIdx && i <= endBinIdx) { m_binCW[i] = (uint32_t)round((double)maxCW / (endBinIdx - startBinIdx + 1)); } + else { m_binCW[i] = 0; } + } + + double hist = 0.0; + uint16_t delta1 = 0, delta2 = 0; + for (int i = 0; i < m_binNum; i++) + { + if (m_srcSeqStats.binHist[i] > 0.001) + { + hist = m_srcSeqStats.binHist[i] > 0.4 ? 0.4 : m_srcSeqStats.binHist[i]; + delta1 = (uint16_t)(10.0 * hist + 0.5); + delta2 = (uint16_t)(20.0 * hist + 0.5); + if (m_srcSeqStats.normVar[i] < 0.8) { m_binCW[i] = m_binCW[i] + delta2; } + else if (m_srcSeqStats.normVar[i] < 0.9) { m_binCW[i] = m_binCW[i] + delta1; } + if (m_srcSeqStats.normVar[i] > 1.2) { m_binCW[i] = m_binCW[i] - delta2; } + else if (m_srcSeqStats.normVar[i] > 1.1) { m_binCW[i] = m_binCW[i] - delta1; } + } + } +} +void EncReshape::cwReduction(int startBinIdx, int endBinIdx) +{ + int bdShift = m_lumaBD - 10; + int totCW = bdShift != 0 ? (bdShift > 0 ? m_reshapeLUTSize / (1 << bdShift) : m_reshapeLUTSize * (1 << (-bdShift))) : m_reshapeLUTSize; + int maxAllowedCW = totCW - 1, usedCW = 0; + for (int i = 0; i < m_binNum; i++) { usedCW += m_binCW[i]; } + if (usedCW > maxAllowedCW) + { + int deltaCW = usedCW - maxAllowedCW; + int divCW = deltaCW / (endBinIdx - startBinIdx + 1); + int modCW = deltaCW - divCW * (endBinIdx - startBinIdx + 1); + if (divCW > 0) + { + for (int i = startBinIdx; i <= endBinIdx; i++) { m_binCW[i] -= divCW; } + } + for (int i = startBinIdx; i <= endBinIdx; i++) + { + if (modCW == 0) break; + if (m_binCW[i] > 0) { m_binCW[i]--; modCW--; } + } + } +} +void EncReshape::deriveReshapeParametersSDR(bool *intraAdp, bool *interAdp) +{ + bool isSkipCase = false; + bool isLowCase = false; + int firstBinVarLessThanVal1 = 0; + int firstBinVarLessThanVal2 = 0; + int firstBinVarLessThanVal3 = 0; + double percBinVarLessThenVal1 = 0.0; + double percBinVarLessThenVal2 = 0.0; + double percBinVarLessThenVal3 = 0.0; + int *binIdxSortDsd = new int[m_binNum]; + double *binVarSortDsd = new double[m_binNum]; + double *binVarSortDsdCDF = new double[m_binNum]; + double ratioWeiVar = 0.0, ratioWeiVarNorm = 0.0; + int startBinIdx = m_sliceReshapeInfo.reshaperModelMinBinIdx; + int endBinIdx = m_sliceReshapeInfo.reshaperModelMaxBinIdx; + + for (int b = 0; b < m_binNum; b++) + { + binVarSortDsd[b] = m_srcSeqStats.binVar[b]; + binIdxSortDsd[b] = b; + } + bubbleSortDsd(binVarSortDsd, binIdxSortDsd, m_binNum); + binVarSortDsdCDF[0] = m_srcSeqStats.binHist[binIdxSortDsd[0]]; + for (int b = 1; b < m_binNum; b++) { binVarSortDsdCDF[b] = binVarSortDsdCDF[b - 1] + m_srcSeqStats.binHist[binIdxSortDsd[b]]; } + for (int b = 0; b < m_binNum - 1; b++) + { + if (binVarSortDsd[b] > 3.4) { firstBinVarLessThanVal1 = b + 1; } + if (binVarSortDsd[b] > 2.8) { firstBinVarLessThanVal2 = b + 1; } + if (binVarSortDsd[b] > 2.5) { firstBinVarLessThanVal3 = b + 1; } + } + percBinVarLessThenVal1 = binVarSortDsdCDF[firstBinVarLessThanVal1]; + percBinVarLessThenVal2 = binVarSortDsdCDF[firstBinVarLessThanVal2]; + percBinVarLessThenVal3 = binVarSortDsdCDF[firstBinVarLessThanVal3]; + delete[] binIdxSortDsd; + delete[] binVarSortDsd; + delete[] binVarSortDsdCDF; + + cwPerturbation(startBinIdx, endBinIdx, (uint16_t)m_reshapeCW.binCW[1]); + cwReduction(startBinIdx, endBinIdx); + initSeqStats(m_rspSeqStats); + for (int b = 0; b < m_binNum; b++) + { + double scale = (m_binCW[b] > 0) ? ((double)m_binCW[b] / (double)m_initCWAnalyze) : 1.0; + m_rspSeqStats.binHist[b] = m_srcSeqStats.binHist[b]; + m_rspSeqStats.binVar[b] = m_srcSeqStats.binVar[b] + 2.0 * log10(scale); + } + m_rspSeqStats.minBinVar = 5.0; + m_rspSeqStats.maxBinVar = 0.0; + m_rspSeqStats.meanBinVar = 0.0; + m_rspSeqStats.nonZeroCnt = 0; + for (int b = 0; b < m_binNum; b++) + { + if (m_rspSeqStats.binHist[b] > 0.001) + { + m_rspSeqStats.nonZeroCnt++; + m_rspSeqStats.meanBinVar += m_rspSeqStats.binVar[b]; + if (m_rspSeqStats.binVar[b] > m_rspSeqStats.maxBinVar) { m_rspSeqStats.maxBinVar = m_rspSeqStats.binVar[b]; } + if (m_rspSeqStats.binVar[b] < m_rspSeqStats.minBinVar) { m_rspSeqStats.minBinVar = m_rspSeqStats.binVar[b]; } + } + } + m_rspSeqStats.meanBinVar /= (double)m_rspSeqStats.nonZeroCnt; + for (int b = 0; b < m_binNum; b++) + { + if (m_rspSeqStats.meanBinVar > 0.0) + m_rspSeqStats.normVar[b] = m_rspSeqStats.binVar[b] / m_rspSeqStats.meanBinVar; + m_rspSeqStats.weightVar += m_rspSeqStats.binHist[b] * m_rspSeqStats.binVar[b]; + m_rspSeqStats.weightNorm += m_rspSeqStats.binHist[b] * m_rspSeqStats.normVar[b]; + } + ratioWeiVar = m_rspSeqStats.weightVar / m_srcSeqStats.weightVar; + ratioWeiVarNorm = m_rspSeqStats.weightNorm / m_srcSeqStats.weightNorm; + + if ((m_srcSeqStats.binHist[0] + m_srcSeqStats.binHist[m_binNum - 1]) > 0.0001 && m_srcSeqStats.binHist[m_binNum - 2] < 0.001) + { + if (percBinVarLessThenVal3 > 0.8 && percBinVarLessThenVal2 > 0.4 && m_srcSeqStats.binVar[m_binNum - 2] > 4.8) { isSkipCase = true; } + else if (percBinVarLessThenVal3 < 0.1 && percBinVarLessThenVal1 < 0.05 && m_srcSeqStats.binVar[m_binNum - 2] < 4.0) { isSkipCase = true; } + } + if (isSkipCase) { *intraAdp = false; *interAdp = false; return; } + + if (m_reshapeCW.rspPicSize > 5184000) { isLowCase = true; } + else if (m_srcSeqStats.binVar[1] > 4.0) { isLowCase = true; } + else if (m_rspSeqStats.meanBinVar > 3.4 && ratioWeiVarNorm > 1.005 && ratioWeiVar > 1.02) { isLowCase = true; } + else if (m_rspSeqStats.meanBinVar > 3.1 && ratioWeiVarNorm > 1.005 && ratioWeiVar > 1.04) { isLowCase = true; } + else if (m_rspSeqStats.meanBinVar > 2.8 && ratioWeiVarNorm > 1.01 && ratioWeiVar > 1.04) { isLowCase = true; } + + if (m_reshapeCW.updateCtrl == 0) + { + m_reshapeCW.binCW[1] = 1022; + if (isLowCase) + { + *intraAdp = false; + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 980; + if (m_srcSeqStats.binHist[m_binNum - 2] > 0.05) + { + m_reshapeCW.binCW[1] = 896; + if (m_srcSeqStats.binVar[m_binNum - 2] < 1.2) { m_reshapeCW.binCW[1] = 938; } + } + else if (percBinVarLessThenVal2 < 0.8 && percBinVarLessThenVal3 == 1.0) + { + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 938; + } + } + if (m_srcSeqStats.binHist[m_binNum - 2] < 0.001) + { + if (m_srcSeqStats.binHist[1] > 0.05 && m_srcSeqStats.binVar[1] > 3.0) + { + *intraAdp = true; + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 784; + } + else if (m_srcSeqStats.binHist[1] < 0.006) + { + *intraAdp = false; + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 1008; + } + else if (percBinVarLessThenVal3 < 0.5) + { + *intraAdp = true; + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 1022; + } + } + else if ((m_srcSeqStats.maxBinVar > 4.0 && m_rspSeqStats.meanBinVar > 3.2 && percBinVarLessThenVal2 < 0.25) || ratioWeiVar < 1.03) + { + *intraAdp = true; + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 1022; + } + if (*intraAdp == true && m_rateAdpMode == 0) { m_tcase = 9; } + } + else if (m_reshapeCW.updateCtrl == 1) + { + m_reshapeCW.binCW[1] = 952; + if (isLowCase) + { + if (m_reshapeCW.rspPicSize > 5184000) + { + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 812; + } + if (m_srcSeqStats.binHist[m_binNum - 2] > 0.05) + { + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 812; + if (m_srcSeqStats.binHist[m_binNum - 2] > 0.1 || m_srcSeqStats.binHist[1] > 0.1) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 924; + } + } + else if (percBinVarLessThenVal2 < 0.8 && percBinVarLessThenVal3 == 1.0) + { + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 896; + } + else if (percBinVarLessThenVal2 > 0.98 && m_srcSeqStats.binHist[1] > 0.05) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 784; + } + else if (percBinVarLessThenVal2 < 0.1) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 1022; + } + } + if (m_srcSeqStats.binHist[1] > 0.1 && (m_srcSeqStats.binVar[1] > 1.8 && m_srcSeqStats.binVar[1] < 3.0)) + { + m_rateAdpMode = 1; + if (m_srcSeqStats.binVar[m_binNum - 2] > 1.2 && m_srcSeqStats.binVar[m_binNum - 2] < 4.0) { m_reshapeCW.binCW[1] = 784; } + } + else if (m_srcSeqStats.binHist[m_binNum - 2] < 0.001) + { + if (m_srcSeqStats.binHist[1] > 0.05 && m_srcSeqStats.binVar[1] > 3.0) + { + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 784; + } + else if (m_srcSeqStats.binHist[1] < 0.006) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 980; + } + else if (percBinVarLessThenVal3 < 0.5) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 924; + } + } + else if ((m_srcSeqStats.maxBinVar > 4.0 && m_rspSeqStats.meanBinVar > 3.2 && percBinVarLessThenVal2 < 0.25) || ratioWeiVar < 1.03) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[1] = 980; + } + } + else + { + m_useAdpCW = true; + m_reshapeCW.binCW[0] = 36; m_reshapeCW.binCW[1] = 30; + if (isLowCase) + { + if (m_srcSeqStats.binHist[m_binNum - 2] > 0.05) + { + m_useAdpCW = false; + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 896; + if (m_srcSeqStats.binHist[1] > 0.005) { m_rateAdpMode = 0; } + } + else if (percBinVarLessThenVal2 < 0.8 && percBinVarLessThenVal3 == 1.0) { m_reshapeCW.binCW[1] = 28; } + } + if (m_srcSeqStats.binHist[1] > 0.1 && m_srcSeqStats.binVar[1] > 1.8 && m_srcSeqStats.binVar[1] < 3.0) + { + m_useAdpCW = false; + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 952; + } + else if (m_srcSeqStats.binHist[1] > 0.05 && m_srcSeqStats.binHist[m_binNum - 2] < 0.001 && m_srcSeqStats.binVar[1] > 3.0) + { + m_useAdpCW = false; + m_rateAdpMode = 1; + m_reshapeCW.binCW[1] = 784; + } + else if (m_srcSeqStats.binHist[1] > 0.05 && m_srcSeqStats.binHist[m_binNum - 2] < 0.005 && m_srcSeqStats.binVar[1] > 1.0 && m_srcSeqStats.binVar[1] < 1.5) + { + m_rateAdpMode = 2; + m_reshapeCW.binCW[0] = 38; + } + else if (m_srcSeqStats.binHist[1] < 0.005 && m_srcSeqStats.binHist[m_binNum - 2] > 0.05 && m_srcSeqStats.binVar[m_binNum - 2] > 1.0 && m_srcSeqStats.binVar[m_binNum - 2] < 1.5) + { + m_rateAdpMode = 2; + m_reshapeCW.binCW[0] = 36; + } + else if (m_srcSeqStats.binHist[1] > 0.02 && m_srcSeqStats.binHist[m_binNum - 2] > 0.04 && m_srcSeqStats.binVar[1] < 2.0 && m_srcSeqStats.binVar[m_binNum - 2] < 1.5) + { + m_rateAdpMode = 2; + m_reshapeCW.binCW[0] = 34; + } + else if ((m_srcSeqStats.binHist[1] > 0.05 && m_srcSeqStats.binHist[m_binNum - 2] > 0.2 && m_srcSeqStats.binVar[1] > 3.0 && m_srcSeqStats.binVar[1] < 4.0) || ratioWeiVar < 1.03) + { + m_rateAdpMode = 1; + m_reshapeCW.binCW[0] = 34; + } + else if (m_srcSeqStats.binVar[1] < 4.0 && percBinVarLessThenVal2 == 1.0 && percBinVarLessThenVal3 == 1.0) + { + m_rateAdpMode = 0; + m_reshapeCW.binCW[0] = 34; + } + if (m_useAdpCW && !isLowCase) { m_reshapeCW.binCW[1] = 66 - m_reshapeCW.binCW[0]; } + } +} +#else void EncReshape::deriveReshapeParametersSDRfromStats(uint32_t * blockBinCnt, double *blockBinVarSum, double* reshapeTH1, double* reshapeTH2, bool *intraAdp, bool *interAdp) { int binIdxSortDsd[PIC_ANALYZE_CW_BINS] = { 0 }; @@ -988,6 +1706,7 @@ void EncReshape::deriveReshapeParametersSDRfromStats(uint32_t * blockBinCnt, dou } } } +#endif void EncReshape::deriveReshapeParameters(double *array, int start, int end, ReshapeCW respCW, double &alpha, double &beta) { @@ -1112,6 +1831,105 @@ void EncReshape::initLUTfromdQPModel() #endif } +#if JVET_O0432_LMCS_ENCODER +void EncReshape::constructReshaperLMCS() +{ + int bdShift = m_lumaBD - 10; + int totCW = bdShift != 0 ? (bdShift > 0 ? m_reshapeLUTSize / (1 << bdShift) : m_reshapeLUTSize * (1 << (-bdShift))) : m_reshapeLUTSize; + int histLenth = totCW / m_binNum; + int log2HistLenth = floorLog2(histLenth); + int i; + + if (bdShift != 0) + { + for (int i = 0; i < PIC_ANALYZE_CW_BINS; i++) + { + m_binCW[i] = bdShift > 0 ? m_binCW[i] * (1 << bdShift) : m_binCW[i] / (1 << (-bdShift)); + } + } + if (m_binNum == PIC_ANALYZE_CW_BINS) + { + for (int i = 0; i < PIC_CODE_CW_BINS; i++) + { + m_binCW[i] = m_binCW[2 * i] + m_binCW[2 * i + 1]; + } + } + for (int i = 0; i <= PIC_CODE_CW_BINS; i++) + { + m_inputPivot[i] = m_initCW * i; + } +#if JVET_O0272_LMCS_SIMP_INVERSE_MAPPING + adjustLmcsPivot(); +#endif + + m_sliceReshapeInfo.reshaperModelMinBinIdx = 0; + m_sliceReshapeInfo.reshaperModelMaxBinIdx = PIC_CODE_CW_BINS - 1; + for (int i = 0; i < PIC_CODE_CW_BINS; i++) + { + if (m_binCW[i] > 0) + { + m_sliceReshapeInfo.reshaperModelMinBinIdx = i; + break; + } + } + for (int i = PIC_CODE_CW_BINS - 1; i >= 0; i--) + { + if (m_binCW[i] > 0) + { + m_sliceReshapeInfo.reshaperModelMaxBinIdx = i; + break; + } + } + + int maxAbsDeltaCW = 0, absDeltaCW = 0, deltaCW = 0; + for (int i = m_sliceReshapeInfo.reshaperModelMinBinIdx; i <= m_sliceReshapeInfo.reshaperModelMaxBinIdx; i++) + { + deltaCW = (int)m_binCW[i] - (int)m_initCW; + m_sliceReshapeInfo.reshaperModelBinCWDelta[i] = deltaCW; + absDeltaCW = (deltaCW < 0) ? (-deltaCW) : deltaCW; + if (absDeltaCW > maxAbsDeltaCW) { maxAbsDeltaCW = absDeltaCW; } + } + m_sliceReshapeInfo.maxNbitsNeededDeltaCW = std::max(1, 1 + floorLog2(maxAbsDeltaCW)); + + histLenth = m_initCW; + log2HistLenth = floorLog2(histLenth); + + int sumBins = 0; + for (i = 0; i < PIC_CODE_CW_BINS; i++) { sumBins += m_binCW[i]; } + CHECK(sumBins >= m_reshapeLUTSize, "SDR CW assignment is wrong!!"); + for (int i = 0; i < PIC_CODE_CW_BINS; i++) + { + m_reshapePivot[i + 1] = m_reshapePivot[i] + m_binCW[i]; + m_fwdScaleCoef[i] = ((int32_t)m_binCW[i] * (1 << FP_PREC) + (1 << (log2HistLenth - 1))) >> log2HistLenth; + if (m_binCW[i] == 0) + { + m_invScaleCoef[i] = 0; + m_chromaAdjHelpLUT[i] = 1 << CSCALE_FP_PREC; + } + else + { + m_invScaleCoef[i] = (int32_t)(m_initCW * (1 << FP_PREC) / m_binCW[i]); + m_chromaAdjHelpLUT[i] = m_invScaleCoef[i]; + } + } + for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++) + { + int idxY = lumaSample / m_initCW; + int tempVal = m_reshapePivot[idxY] + ((m_fwdScaleCoef[idxY] * (lumaSample - m_inputPivot[idxY]) + (1 << (FP_PREC - 1))) >> FP_PREC); + m_fwdLUT[lumaSample] = Clip3((Pel)0, (Pel)((1 << m_lumaBD) - 1), (Pel)(tempVal)); + + int idxYInv = getPWLIdxInv(lumaSample); + int invSample = m_inputPivot[idxYInv] + ((m_invScaleCoef[idxYInv] * (lumaSample - m_reshapePivot[idxYInv]) + (1 << (FP_PREC - 1))) >> FP_PREC); + m_invLUT[lumaSample] = Clip3((Pel)0, (Pel)((1 << m_lumaBD) - 1), (Pel)(invSample)); + } + for (i = 0; i < PIC_CODE_CW_BINS; i++) + { + int start = i*histLenth; + int end = (i + 1)*histLenth - 1; + m_cwLumaWeight[i] = m_fwdLUT[end] - m_fwdLUT[start]; + } +} +#else void EncReshape::constructReshaperSDR() { int bdShift = m_lumaBD - 10; @@ -1369,6 +2187,7 @@ void EncReshape::constructReshaperSDR() updateChromaScaleLUT(); #endif } +#endif #if JVET_O0272_LMCS_SIMP_INVERSE_MAPPING void EncReshape::adjustLmcsPivot() diff --git a/source/Lib/EncoderLib/EncReshape.h b/source/Lib/EncoderLib/EncReshape.h index 53bb44d42..df1df223e 100644 --- a/source/Lib/EncoderLib/EncReshape.h +++ b/source/Lib/EncoderLib/EncReshape.h @@ -49,6 +49,22 @@ // ==================================================================================================================== // Class definition // ==================================================================================================================== +#if JVET_O0432_LMCS_ENCODER +struct SeqInfo +{ + double binVar[PIC_ANALYZE_CW_BINS]; + double binHist[PIC_ANALYZE_CW_BINS]; + double normVar[PIC_ANALYZE_CW_BINS]; + int nonZeroCnt; + double weightVar; + double weightNorm; + double minBinVar; + double maxBinVar; + double meanBinVar; + double ratioStdU; + double ratioStdV; +}; +#endif class EncReshape : public Reshape { @@ -71,6 +87,11 @@ private: Pel m_cwLumaWeight[PIC_CODE_CW_BINS]; double m_chromaWeight; int m_chromaAdj; +#if JVET_O0432_LMCS_ENCODER + int m_binNum; + SeqInfo m_srcSeqStats; + SeqInfo m_rspSeqStats; +#endif public: EncReshape(); @@ -81,15 +102,31 @@ public: bool getSrcReshaped() { return m_srcReshaped; } void setSrcReshaped(bool b) { m_srcReshaped = b; } +#if JVET_O0432_LMCS_ENCODER + void initSeqStats(SeqInfo &stats); + void calcSeqStats(Picture *pcPic, SeqInfo &stats); + void preAnalyzerLMCS(Picture *pcPic, const uint32_t signalType, const SliceType sliceType, const ReshapeCW& reshapeCW); +#else void preAnalyzerSDR(Picture *pcPic, const SliceType sliceType, const ReshapeCW& reshapeCW, bool isDualT); +#endif void preAnalyzerHDR(Picture *pcPic, const SliceType sliceType, const ReshapeCW& reshapeCW, bool isDualT); void bubbleSortDsd(double *array, int * idx, int n); void swap(int *xp, int *yp) { int temp = *xp; *xp = *yp; *yp = temp; } void swap(double *xp, double *yp) { double temp = *xp; *xp = *yp; *yp = temp; } +#if JVET_O0432_LMCS_ENCODER + void cwPerturbation(int startBinIdx, int endBinIdx, uint16_t maxCW); + void cwReduction(int startBinIdx, int endBinIdx); + void deriveReshapeParametersSDR(bool *intraAdp, bool *interAdp); +#else void deriveReshapeParametersSDRfromStats(uint32_t *, double*, double* reshapeTH1, double* reshapeTH2, bool *intraAdp, bool *interAdp); +#endif void deriveReshapeParameters(double *array, int start, int end, ReshapeCW respCW, double &alpha, double &beta); void initLUTfromdQPModel(); +#if JVET_O0432_LMCS_ENCODER + void constructReshaperLMCS(); +#else void constructReshaperSDR(); +#endif ReshapeCW * getReshapeCW() { return &m_reshapeCW; } Pel * getWeightTable() { return m_cwLumaWeight; } double getCWeight() { return m_chromaWeight; } -- GitLab