From b0dd97ce114476e69bb1c19032bf4a91cc35987d Mon Sep 17 00:00:00 2001
From: francoise <edouard.francois@technicolor.com>
Date: Wed, 30 Oct 2019 18:50:17 +0100
Subject: [PATCH] implementation of JVET-P0371: chroma residual scaling with
 offset

---
 cfg/encoder_randomaccess_vtm.cfg     |  1 +
 cfg/per-class/classH1.cfg            |  1 +
 source/App/EncoderApp/EncApp.cpp     |  3 +++
 source/App/EncoderApp/EncAppCfg.cpp  | 10 ++++++++++
 source/App/EncoderApp/EncAppCfg.h    |  3 +++
 source/Lib/CommonLib/Reshape.cpp     |  7 +++++++
 source/Lib/CommonLib/Slice.h         |  9 +++++++++
 source/Lib/CommonLib/TypeDef.h       |  1 +
 source/Lib/DecoderLib/DecLib.cpp     |  3 +++
 source/Lib/DecoderLib/VLCReader.cpp  | 11 +++++++++++
 source/Lib/EncoderLib/EncCfg.h       |  7 +++++++
 source/Lib/EncoderLib/EncGOP.cpp     |  7 +++++++
 source/Lib/EncoderLib/EncReshape.cpp | 11 +++++++++++
 source/Lib/EncoderLib/VLCWriter.cpp  | 10 ++++++++++
 14 files changed, 84 insertions(+)

diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg
index c3b70fe2b..673977990 100644
--- a/cfg/encoder_randomaccess_vtm.cfg
+++ b/cfg/encoder_randomaccess_vtm.cfg
@@ -150,6 +150,7 @@ AffineAmvr                   : 1
 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
+LMCSoffset                   : 6      # chroma residual scaling offset
 MIP                          : 1
 DMVR                         : 1
 SMVD                         : 1
diff --git a/cfg/per-class/classH1.cfg b/cfg/per-class/classH1.cfg
index e7cefecf6..afde72f0b 100644
--- a/cfg/per-class/classH1.cfg
+++ b/cfg/per-class/classH1.cfg
@@ -5,6 +5,7 @@ isSDR                         : 0           # 1: SDR in PQ container,   0: HDR
 # ======= LMCS =======================
 LMCSEnable                    : 1           # turned on in HDR CTC 
 LMCSSignalType                : 1           # Input signal type: 0:SDR, 1:HDR-PQ, 2:HDR-HLG
+LMCSoffset                    : 1           # chroma residual scaling offset
 
 #======== Chroma QP scale ============
 WCGPPSEnable                  : 0           # enable WCG Chroma scale
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index a774e69e1..19148d5bb 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -637,6 +637,9 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setReshapeSignalType                                 ( m_reshapeSignalType );
   m_cEncLib.setReshapeIntraCMD                                   ( m_intraCMD );
   m_cEncLib.setReshapeCW                                         ( m_reshapeCW );
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  m_cEncLib.setReshapeCSoffset                                   ( m_CSoffset );
+#endif
 
 #if JVET_O0756_CALCULATE_HDRMETRICS
   for (int i=0; i<hdrtoolslib::NB_REF_WHITE; i++)
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 550b9c605..5b5784bac 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -993,6 +993,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
                                                                                                                "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")
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  ("LMCSOffset",                                      m_CSoffset,                                           0, "LMCS chroma residual scaling offset")
+#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")
@@ -2709,6 +2712,10 @@ bool EncAppCfg::xCheckParameter()
     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 JVET_P0371_CHROMA_SCALING_OFFSET
+    xConfirmPara(m_CSoffset < -7, "Min. LMCS Offset value is -7");
+    xConfirmPara(m_CSoffset > 7, "Max. LMCS Offset value is 7");
+#endif
     if (m_updateCtrl > 0 && m_adpOption > 2) { m_adpOption -= 2; }
   }
 
@@ -3718,6 +3725,9 @@ void EncAppCfg::xPrintParameter()
       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); }
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+      msg(VERBOSE, " CSoffset:%d", m_CSoffset);
+#endif
       msg(VERBOSE, ") ");
     }
     msg(VERBOSE, "MIP:%d ", m_MIP);
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index c6493979f..76ae65dd9 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -332,6 +332,9 @@ protected:
   int       m_updateCtrl;
   int       m_adpOption;
   uint32_t  m_initialCW;
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  int       m_CSoffset;
+#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/Reshape.cpp b/source/Lib/CommonLib/Reshape.cpp
index 40a5ed723..c029fec70 100644
--- a/source/Lib/CommonLib/Reshape.cpp
+++ b/source/Lib/CommonLib/Reshape.cpp
@@ -225,6 +225,9 @@ void Reshape::copySliceReshaperInfo(SliceReshapeInfo& tInfo, SliceReshapeInfo& s
     tInfo.reshaperModelMinBinIdx = sInfo.reshaperModelMinBinIdx;
     memcpy(tInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof(int)*(PIC_CODE_CW_BINS));
     tInfo.maxNbitsNeededDeltaCW = sInfo.maxNbitsNeededDeltaCW;
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+    tInfo.chrResScalingOffset = sInfo.chrResScalingOffset;
+#endif
   }
   tInfo.sliceReshaperEnableFlag = sInfo.sliceReshaperEnableFlag;
   if (sInfo.sliceReshaperEnableFlag)
@@ -262,7 +265,11 @@ void Reshape::constructReshaper()
     else
     {
       m_invScaleCoef[i] = (int32_t)(m_initCW * (1 << FP_PREC) / m_binCW[i]);
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+      m_chromaAdjHelpLUT[i] = (int32_t)(m_initCW * (1 << FP_PREC) / ( m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset ) );
+#else
       m_chromaAdjHelpLUT[i] = m_invScaleCoef[i];
+#endif
     }
   }
   for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++)
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index c0d36fcd8..ec635575a 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -451,6 +451,9 @@ public:
   uint32_t  reshaperModelMaxBinIdx;
   int       reshaperModelBinCWDelta[PIC_CODE_CW_BINS];
   int       maxNbitsNeededDeltaCW;
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  int       chrResScalingOffset;
+#endif
   void      setUseSliceReshaper(bool b)                                { sliceReshaperEnableFlag = b;            }
   bool      getUseSliceReshaper() const                                { return sliceReshaperEnableFlag;         }
   void      setSliceReshapeModelPresentFlag(bool b)                    { sliceReshaperModelPresentFlag = b;      }
@@ -484,6 +487,12 @@ public:
     {
       return false;
     }
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+    if (chrResScalingOffset != other.chrResScalingOffset)
+    {
+      return false;
+    }
+#endif
     if( memcmp( reshaperModelBinCWDelta, other.reshaperModelBinCWDelta, sizeof( reshaperModelBinCWDelta ) ) )
     {
       return false;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index e4cfb8d14..d7b01ffb5 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -53,6 +53,7 @@
 
 #define JVET_P0345_LD_GOP_8                               1 // JVET-P0345: low-delay gop size 8
 
+#define JVET_P0371_CHROMA_SCALING_OFFSET                  1 // JVET-P0371: Signalling offset for chroma residual scaling
 
 #define JVET_P0803_COMBINED_MIP_CLEANUP                   1 // JVET-P0803: Several MIP cleanups
 #define JVET_P0199_P0289_P0303_MIP_FULLMATRIX             1 // JVET-P0199/P0289/P0303: Full matrix multiplication for all MIP block shapes
diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp
index 58b680f47..051d1d161 100644
--- a/source/Lib/DecoderLib/DecLib.cpp
+++ b/source/Lib/DecoderLib/DecLib.cpp
@@ -1534,6 +1534,9 @@ bool DecLib::xDecodeSlice(InputNALUnit &nalu, int &iSkipFrame, int iPOCLastDispl
       tInfo.reshaperModelMinBinIdx = sInfo.reshaperModelMinBinIdx;
       memcpy(tInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof(int)*(PIC_CODE_CW_BINS));
       tInfo.maxNbitsNeededDeltaCW = sInfo.maxNbitsNeededDeltaCW;
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+      tInfo.chrResScalingOffset = sInfo.chrResScalingOffset;
+#endif
       tInfo.setUseSliceReshaper(pcSlice->getLmcsEnabledFlag());
       tInfo.setSliceReshapeChromaAdj(pcSlice->getLmcsChromaResidualScaleFlag());
       tInfo.setSliceReshapeModelPresentFlag(true);
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 0e695a0f8..3687b17bf 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -1004,6 +1004,17 @@ void HLSyntaxReader::parseLmcsAps( APS* aps )
     int signCW = code;
     info.reshaperModelBinCWDelta[i] = (1 - 2 * signCW) * absCW;
   }
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  READ_CODE(3, code, "lmcs_delta_abs_crs");
+  int absCW = code;
+  if (absCW > 0)
+  {
+    READ_CODE(1, code, "lmcs_delta_sign_crs_flag");
+  }
+  int signCW = code;
+  info.chrResScalingOffset = (1 - 2 * signCW) * absCW;
+#endif
+
   aps->setReshaperAPSInfo(info);
 }
 
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index feb45aadf..16eaf368d 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -340,6 +340,9 @@ protected:
   unsigned  m_reshapeSignalType;
   unsigned  m_intraCMD;
   ReshapeCW m_reshapeCW;
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  int       m_CSoffset;
+#endif
   bool      m_encDbOpt;
   bool      m_useFastLCTU;
   bool      m_useFastMrg;
@@ -991,6 +994,10 @@ public:
   uint32_t  getReshapeIntraCMD              ()                           { return m_intraCMD; }
   void      setReshapeCW                    (const ReshapeCW &reshapeCW) { m_reshapeCW = reshapeCW; }
   const ReshapeCW& getReshapeCW             ()                           { return m_reshapeCW; }
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  void      setReshapeCSoffset              (int CSoffset)          { m_CSoffset = CSoffset; }
+  int       getReshapeCSoffset              ()                      { return m_CSoffset; }
+#endif
   void      setMaxCUWidth                   ( uint32_t  u )      { m_maxCUWidth  = u; }
   uint32_t      getMaxCUWidth                   () const         { return m_maxCUWidth; }
   void      setMaxCUHeight                  ( uint32_t  u )      { m_maxCUHeight = u; }
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index d51c70c99..85f895a32 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -1753,6 +1753,10 @@ void EncGOP::xPicInitLMCS(Picture *pic, Slice *slice)
     m_pcReshaper->setSrcReshaped(false);
     m_pcReshaper->setRecReshaped(true);
 
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+    m_pcReshaper->getSliceReshaperInfo().chrResScalingOffset = m_pcCfg->getReshapeCSoffset();
+#endif
+
     if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
     {
       m_pcReshaper->preAnalyzerHDR(pic, sliceType, m_pcCfg->getReshapeCW(), m_pcCfg->getDualITree());
@@ -1854,6 +1858,9 @@ void EncGOP::xPicInitLMCS(Picture *pic, Slice *slice)
       tInfo.reshaperModelMinBinIdx = sInfo.reshaperModelMinBinIdx;
       memcpy(tInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof(int)*(PIC_CODE_CW_BINS));
       tInfo.maxNbitsNeededDeltaCW = sInfo.maxNbitsNeededDeltaCW;
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+      tInfo.chrResScalingOffset = sInfo.chrResScalingOffset;
+#endif
       m_pcEncLib->getApsMap()->setChangedFlag((lmcsAPS->getAPSId() << NUM_APS_TYPE_LEN) + LMCS_APS);
     }
 
diff --git a/source/Lib/EncoderLib/EncReshape.cpp b/source/Lib/EncoderLib/EncReshape.cpp
index b92b0cdb4..034d6b119 100644
--- a/source/Lib/EncoderLib/EncReshape.cpp
+++ b/source/Lib/EncoderLib/EncReshape.cpp
@@ -93,6 +93,9 @@ void  EncReshape::createEnc(int picWidth, int picHeight, uint32_t maxCUWidth, ui
   m_sliceReshapeInfo.reshaperModelMinBinIdx = 0;
   m_sliceReshapeInfo.reshaperModelMaxBinIdx = PIC_CODE_CW_BINS - 1;
   memset(m_sliceReshapeInfo.reshaperModelBinCWDelta, 0, (PIC_CODE_CW_BINS) * sizeof(int));
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  m_sliceReshapeInfo.chrResScalingOffset = 0;
+#endif
 
   m_picWidth = picWidth;
   m_picHeight = picHeight;
@@ -962,7 +965,11 @@ void EncReshape::initLUTfromdQPModel()
     else
     {
       m_invScaleCoef[i] = (int32_t)(m_initCW * (1 << FP_PREC) / m_binCW[i]);
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+      m_chromaAdjHelpLUT[i] = (int32_t)(m_initCW * (1 << FP_PREC) / (m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset));
+#else
       m_chromaAdjHelpLUT[i] = m_invScaleCoef[i];
+#endif
     }
   }
   for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++)
@@ -1054,7 +1061,11 @@ void EncReshape::constructReshaperLMCS()
     else
     {
       m_invScaleCoef[i] = (int32_t)(m_initCW * (1 << FP_PREC) / m_binCW[i]);
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+      m_chromaAdjHelpLUT[i] = (int32_t)(m_initCW * (1 << FP_PREC) / (m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset));
+#else
       m_chromaAdjHelpLUT[i] = m_invScaleCoef[i];
+#endif
     }
   }
   for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++)
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 023d0a90c..31005a474 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -579,6 +579,16 @@ void HLSWriter::codeLmcsAps( APS* pcAPS )
       WRITE_FLAG(signCW, "lmcs_delta_sign_cw_flag[ i ]");
     }
   }
+#if JVET_P0371_CHROMA_SCALING_OFFSET
+  int deltaCRS = param.chrResScalingOffset;
+  int signCRS = (deltaCRS < 0) ? 1 : 0;
+  int absCRS = (deltaCRS < 0) ? (-deltaCRS) : deltaCRS;
+  WRITE_CODE(absCRS, 3, "lmcs_delta_crs_val");
+  if (absCRS > 0)
+  {
+    WRITE_FLAG(signCRS, "lmcs_delta_crs_val_flag");
+  }
+#endif
 }
 
 void HLSWriter::codeScalingListAps( APS* pcAPS )
-- 
GitLab