From f4f8dc1db1ac51e6c07a2723cb5fe5a012be56c1 Mon Sep 17 00:00:00 2001
From: "SARWER, Mohammed Golam" <m.sarwer@alibaba-inc.com>
Date: Mon, 11 May 2020 11:28:35 -0700
Subject: [PATCH]  JVET-R0110: Fix interface of mixed lossy/lossless coding

---
 doc/software-manual.tex             | 10 ++++-
 source/App/EncoderApp/EncApp.cpp    |  1 +
 source/App/EncoderApp/EncAppCfg.cpp | 67 +++++++++++++++++++----------
 source/App/EncoderApp/EncAppCfg.h   |  3 +-
 source/Lib/CommonLib/Picture.cpp    | 22 ++++++++++
 source/Lib/CommonLib/Picture.h      |  3 ++
 source/Lib/EncoderLib/EncCfg.h      |  9 ++--
 source/Lib/EncoderLib/EncGOP.cpp    |  9 +++-
 8 files changed, 93 insertions(+), 31 deletions(-)

diff --git a/doc/software-manual.tex b/doc/software-manual.tex
index 400706a62..6b97d0106 100644
--- a/doc/software-manual.tex
+++ b/doc/software-manual.tex
@@ -2055,10 +2055,16 @@ Allow signalling of entry points for WPP in slice header.
 Note that when a slice contains more than one tile, entry point offsets for tile are always present in the slice header.
 \\
 
-\Option{SliceLosslessArray} &
+\Option{MixedLossyLossless} &
 %\ShortOption{\None} &
 \Default{0} &
-Define pattern of lossless coding in unit of slices, 0 means lossy slice; 1 means lossless slice. Example:  1 1 0 0 means first 2 slices are lossless coded and rest of the slices are lossy coded. Default is all slices are lossy coded.
+Enable or disable mixed lossy/lossless coding. 0 means disable; 1 means enable. Mixed lossy/lossless can only be enable if CostMode is set to lossless.
+\\
+
+\Option{SliceLosslessArray} &
+%\ShortOption{\None} &
+\Default{\None} &
+Slice index array of lossless slices. Example:  1 5 6 means slices with index of 1, 5, and 6 are lossless coded. The rest of the slices are lossy coded. If MixedLossyLossless is disbaled, the values are ignored.
 \\
 
 \end{OptionTableNoShorthand}
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index ee51cd13e..a4e0fbec4 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -577,6 +577,7 @@ void EncApp::xInitLibCfg()
   //====== Parallel Merge Estimation ========
   m_cEncLib.setLog2ParallelMergeLevelMinus2(m_log2ParallelMergeLevel - 2);
 #if JVET_R0110_MIXED_LOSSLESS
+  m_cEncLib.setMixedLossyLossless(m_mixedLossyLossless);
   m_cEncLib.setSliceLosslessArray(m_sliceLosslessArray);
 #endif
 
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 3b5e2bac7..4ca154a01 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -621,7 +621,7 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 
   SMultiValueInput<double> cfg_adIntraLambdaModifier         (0, std::numeric_limits<double>::max(), 0, MAX_TLAYER); ///< Lambda modifier for Intra pictures, one for each temporal layer. If size>temporalLayer, then use [temporalLayer], else if size>0, use [size()-1], else use m_adLambdaModifier.
 #if JVET_R0110_MIXED_LOSSLESS
-  SMultiValueInput<uint32_t>  cfgSliceLosslessArray(0, std::numeric_limits<uint32_t>::max(), 0, std::numeric_limits<uint32_t>::max());
+  SMultiValueInput<uint16_t>  cfgSliceLosslessArray          (0, std::numeric_limits<uint16_t>::max(), 0, MAX_SLICES);
 #endif
 #if SHARP_LUMA_DELTA_QP
   const int defaultLumaLevelTodQp_QpChangePoints[]   =  {-3,  -2,  -1,   0,   1,   2,   3,   4,   5,   6};
@@ -1102,7 +1102,8 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("SAOGreedyEnc",                                    m_saoGreedyMergeEnc,                              false, "SAO greedy merge encoding algorithm")
   ("EnablePicPartitioning",                           m_picPartitionFlag,                               false, "Enable picture partitioning (0: single tile, single slice, 1: multiple tiles/slices can be used)")
 #if JVET_R0110_MIXED_LOSSLESS
-  ("SliceLosslessArray", cfgSliceLosslessArray, cfgSliceLosslessArray, " Lossless slice array Last lossless flag in the  list will be repeated uniformly to cover any remaining slice")
+  ("MixedLossyLossless",                              m_mixedLossyLossless,                                  false, "Enable encoder to encode mixed lossy/lossless coding ")
+  ("SliceLosslessArray",                              cfgSliceLosslessArray, cfgSliceLosslessArray, " Lossless slice array Last lossless flag in the  list will be repeated uniformly to cover any remaining slice")
 #endif 
   ("TileColumnWidthArray",                            cfgTileColumnWidth,                  cfgTileColumnWidth, "Tile column widths in units of CTUs. Last column width in list will be repeated uniformly to cover any remaining picture width")
   ("TileRowHeightArray",                              cfgTileRowHeight,                      cfgTileRowHeight, "Tile row heights in units of CTUs. Last row height in list will be repeated uniformly to cover any remaining picture height")
@@ -1641,30 +1642,29 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   }
 
 #if JVET_R0110_MIXED_LOSSLESS
-  // store slice lossless array 
-  const int maxSlicesPerPicture = 600; // the maximum number of slices per picture is 600
-  m_sliceLosslessArray.resize(maxSlicesPerPicture);
-  if (cfgSliceLosslessArray.values.size() == 0)
+  if (m_costMode != COST_LOSSLESS_CODING && m_mixedLossyLossless)
   {
-    // Default all slices are lossy
-    for (uint32_t i = 0; i < maxSlicesPerPicture; i++)
-    {
-      m_sliceLosslessArray[i] = 0;
-    }
+    m_mixedLossyLossless = 0;
+    msg(WARNING, "*************************************************************************\n");
+    msg(WARNING, "* Mixed lossy lossles coding cannot enable in lossy costMode *\n");
+    msg(WARNING, "* Forcely disabled  m_mixedLossyLossless *\n");
+    msg(WARNING, "*************************************************************************\n");
   }
-  else
+  if (!m_mixedLossyLossless && cfgSliceLosslessArray.values.size() > 0)
+  {
+    msg(WARNING, "*************************************************************************\n");
+    msg(WARNING, "* Mixed lossy lossles coding is not enabled *\n");
+    msg(WARNING, "* ignoring the value of SliceLosslessArray *\n");
+    msg(WARNING, "*************************************************************************\n");
+  }
+
+  if (m_costMode == COST_LOSSLESS_CODING && m_mixedLossyLossless)
   {
+    m_sliceLosslessArray.resize(cfgSliceLosslessArray.values.size());
     for (uint32_t i = 0; i < cfgSliceLosslessArray.values.size(); i++)
     {
       m_sliceLosslessArray[i] = cfgSliceLosslessArray.values[i];
     }
-    for (uint32_t i = (uint32_t)cfgSliceLosslessArray.values.size(); i < maxSlicesPerPicture; i++)
-    {
-      if (cfgSliceLosslessArray.values.size() > 0)
-        m_sliceLosslessArray[i] = cfgSliceLosslessArray.values[cfgSliceLosslessArray.values.size() - 1];
-      else
-        m_sliceLosslessArray[i] = 0;
-    }
   }
 #endif
 
@@ -2290,12 +2290,33 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #endif
 #endif // ENABLE_QPA
 
-#if JVET_R0110_MIXED_LOSSLESS
-  if (m_costMode == COST_LOSSLESS_CODING && m_sliceLosslessArray[0]) // if first slice is lossless 
-#else
+
+
+
   if( m_costMode == COST_LOSSLESS_CODING )
-#endif
   {
+#if JVET_R0110_MIXED_LOSSLESS
+    bool firstSliceLossless = false;
+    if (m_mixedLossyLossless)
+    {
+      if (m_sliceLosslessArray.size() > 0)
+      {
+        for (uint32_t i = 0; i < m_sliceLosslessArray.size(); i++)
+        {
+          if (m_sliceLosslessArray[i] == 0)
+          {
+            firstSliceLossless = true;
+            break;
+          }
+        }
+      }
+    }
+    else
+    {
+      firstSliceLossless = true;
+    }
+    if (firstSliceLossless) // if first slice is lossless 
+#endif
     m_iQP = LOSSLESS_AND_MIXED_LOSSLESS_RD_COST_TEST_QP - ( ( m_internalBitDepth[CHANNEL_TYPE_LUMA] - 8 ) * 6 );
   }
 
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 0762b6d85..f97c7e33a 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -455,7 +455,8 @@ protected:
   bool      m_useEarlySkipDetection;                          ///< flag for using Early SKIP Detection
   bool      m_picPartitionFlag;                               ///< enable picture partitioning (0: single tile, single slice, 1: multiple tiles/slices can be used)
 #if JVET_R0110_MIXED_LOSSLESS
-  std::vector<uint32_t> m_sliceLosslessArray;                    ///< Slice lossless array
+  bool      m_mixedLossyLossless;                             ///< enable mixed lossy/lossless coding
+  std::vector<uint16_t> m_sliceLosslessArray;                 ///< Slice lossless array
 #endif
   std::vector<uint32_t> m_tileColumnWidth;                    ///< tile column widths in units of CTUs (last column width will be repeated uniformly to cover any remaining picture width)
   std::vector<uint32_t> m_tileRowHeight;                      ///< tile row heights in units of CTUs (last row height will be repeated uniformly to cover any remaining picture height)
diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp
index 5a73072f8..882378686 100644
--- a/source/Lib/CommonLib/Picture.cpp
+++ b/source/Lib/CommonLib/Picture.cpp
@@ -387,6 +387,28 @@ void Picture::allocateNewSlice()
     slice.initSlice();
   }
 }
+#if JVET_R0110_MIXED_LOSSLESS
+void Picture::fillSliceLossyLosslessArray(std::vector<uint16_t> sliceLosslessIndexArray, bool mixedLossyLossless)
+{
+  uint16_t numElementsinsliceLosslessIndexArray = (uint16_t)sliceLosslessIndexArray.size();
+  uint32_t numSlices = this->cs->pps->getNumSlicesInPic();
+  m_lossylosslessSliceArray.assign(numSlices, true); // initialize to all slices are lossless 
+  if (mixedLossyLossless)
+  {
+    m_lossylosslessSliceArray.assign(numSlices, false); // initialize to all slices are lossless 
+    CHECK(numElementsinsliceLosslessIndexArray == 0 , "sliceLosslessArray is empty, must need to configure for mixed lossy/lossless");
+
+    // mixed lossy/lossless slices, set only lossless slices;
+    for (uint16_t i = 0; i < numElementsinsliceLosslessIndexArray; i++)
+    {
+        CHECK(sliceLosslessIndexArray[i] >= numSlices || sliceLosslessIndexArray[i] < 0, "index of lossless slice is out of slice index bound");
+        m_lossylosslessSliceArray[sliceLosslessIndexArray[i]] = true;
+    }
+  } 
+  CHECK(m_lossylosslessSliceArray.size() < numSlices, "sliceLosslessArray size is less than number of slices");
+
+}
+#endif
 
 Slice *Picture::swapSliceObject(Slice * p, uint32_t i)
 {
diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h
index b705491e9..1d9879d36 100644
--- a/source/Lib/CommonLib/Picture.h
+++ b/source/Lib/CommonLib/Picture.h
@@ -158,6 +158,8 @@ struct Picture : public UnitArea
 #if JVET_R0110_MIXED_LOSSLESS
   void setLossyQPValue(int i)                 { m_lossyQP = i; }
   int getLossyQPValue()                       const { return m_lossyQP; }
+  void      fillSliceLossyLosslessArray(std::vector<uint16_t> sliceLosslessArray, bool mixedLossyLossless);
+  bool      losslessSlice(uint32_t sliceIdx)  const { return m_lossylosslessSliceArray[sliceIdx]; }
 #endif
 
   int           getSpliceIdx(uint32_t idx) const { return m_spliceIdx[idx]; }
@@ -218,6 +220,7 @@ public:
   int  m_ctuNums;
 #if JVET_R0110_MIXED_LOSSLESS
   int m_lossyQP;
+  std::vector<bool> m_lossylosslessSliceArray;
 #endif
   bool interLayerRefPicFlag;
 
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 0ec6d88d8..3d2de023a 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -489,7 +489,8 @@ protected:
   bool      m_gopBasedTemporalFilterEnabled;
   bool      m_noPicPartitionFlag;                             ///< no picture partitioning flag (single tile, single slice)
 #if JVET_R0110_MIXED_LOSSLESS
-  std::vector<uint32_t> m_sliceLosslessArray;                    ///< Slice lossless array
+  bool      m_mixedLossyLossless;                             ///< enable mixed lossy/lossless coding
+  std::vector<uint16_t> m_sliceLosslessArray;                      ///< Slice lossless array
 #endif
   std::vector<uint32_t> m_tileColumnWidth;                    ///< tile column widths in units of CTUs (last column width will be repeated uniformly to cover any remaining picture width)
   std::vector<uint32_t> m_tileRowHeight;                      ///< tile row heights in units of CTUs (last row height will be repeated uniformly to cover any remaining picture height)
@@ -1410,8 +1411,10 @@ public:
   uint32_t      getDeltaQpRD                    () const { return m_uiDeltaQpRD; }
   bool      getFastDeltaQp                  () const { return m_bFastDeltaQP; }
 #if JVET_R0110_MIXED_LOSSLESS
-  void      setSliceLosslessArray(std::vector<uint32_t> sliceLosslessArray) { m_sliceLosslessArray = sliceLosslessArray; }
-  const     std::vector<uint32_t>*   getSliceLosslessArray() const { return &m_sliceLosslessArray; }
+  void      setMixedLossyLossless(bool b) { m_mixedLossyLossless = b; }
+  bool      getMixedLossyLossless()       { return m_mixedLossyLossless; }
+  void      setSliceLosslessArray(std::vector<uint16_t> sliceLosslessArray) { m_sliceLosslessArray = sliceLosslessArray; }
+  const     std::vector<uint16_t>*   getSliceLosslessArray() const { return &m_sliceLosslessArray; }
 #endif
   //====== Tiles and Slices ========
   void      setNoPicPartitionFlag( bool b )                                { m_noPicPartitionFlag = b;              }
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index 813605db2..b0c049ca6 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -2667,7 +2667,12 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
     {
       DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "poc", pocCurr ) ) );
 #if JVET_R0110_MIXED_LOSSLESS
-      const std::vector<uint32_t> sliceLosslessArray = *(m_pcCfg->getSliceLosslessArray());
+      const std::vector<uint16_t> sliceLosslessArray = *(m_pcCfg->getSliceLosslessArray());
+      bool mixedLossyLossless = m_pcCfg->getMixedLossyLossless();
+      if (m_pcCfg->getCostMode() == COST_LOSSLESS_CODING)
+      {
+        pcPic->fillSliceLossyLosslessArray(sliceLosslessArray, mixedLossyLossless);
+      }
 #endif
 
       for(uint32_t sliceIdx = 0; sliceIdx < pcPic->cs->pps->getNumSlicesInPic(); sliceIdx++ )
@@ -2716,7 +2721,7 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
         bool isLossless = false;
         if (m_pcCfg->getCostMode() == COST_LOSSLESS_CODING)
         {
-          isLossless = (sliceLosslessArray[sliceIdx] != 0);
+          isLossless = pcPic->losslessSlice(sliceIdx);
         }
         m_pcSliceEncoder->setLosslessSlice(pcPic, isLossless);
 #endif
-- 
GitLab