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