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