From a29425ab4e7a75d584030abeab1254014c28efa7 Mon Sep 17 00:00:00 2001 From: Frank Bossen <frank@bossentech.com> Date: Mon, 27 Jan 2020 03:10:59 +0100 Subject: [PATCH] JVET-Q0795: Cross-component ALF --- doc/software-manual.tex | 12 + source/App/EncoderApp/EncApp.cpp | 7 + source/App/EncoderApp/EncAppCfg.cpp | 18 +- source/App/EncoderApp/EncAppCfg.h | 7 + source/Lib/CommonLib/AdaptiveLoopFilter.cpp | 269 +++- source/Lib/CommonLib/AdaptiveLoopFilter.h | 26 + source/Lib/CommonLib/AlfParameters.h | 55 + source/Lib/CommonLib/CodingStatistics.h | 6 + source/Lib/CommonLib/CommonDef.h | 11 + source/Lib/CommonLib/Contexts.cpp | 10 + source/Lib/CommonLib/Contexts.h | 3 + source/Lib/CommonLib/Slice.cpp | 32 +- source/Lib/CommonLib/Slice.h | 66 +- source/Lib/CommonLib/TypeDef.h | 1 + source/Lib/DecoderLib/CABACReader.cpp | 58 + source/Lib/DecoderLib/CABACReader.h | 5 + source/Lib/DecoderLib/DecLib.cpp | 79 +- source/Lib/DecoderLib/VLCReader.cpp | 143 +- source/Lib/DecoderLib/VLCReader.h | 3 + source/Lib/EncoderLib/CABACWriter.cpp | 63 + source/Lib/EncoderLib/CABACWriter.h | 4 + .../Lib/EncoderLib/EncAdaptiveLoopFilter.cpp | 1346 +++++++++++++++++ source/Lib/EncoderLib/EncAdaptiveLoopFilter.h | 68 + source/Lib/EncoderLib/EncCfg.h | 18 +- source/Lib/EncoderLib/EncGOP.cpp | 40 + source/Lib/EncoderLib/EncLib.cpp | 6 + source/Lib/EncoderLib/VLCWriter.cpp | 104 ++ 27 files changed, 2415 insertions(+), 45 deletions(-) diff --git a/doc/software-manual.tex b/doc/software-manual.tex index 582dcbe4f..4b25c8218 100644 --- a/doc/software-manual.tex +++ b/doc/software-manual.tex @@ -2233,6 +2233,18 @@ switched at CTB level. Set to 1 to disable alternative chroma filters. Value shall be in the range 1..8. \\ +\Option{CCALF} & +%\ShortOption{\None} & +\Default{true} & +Enables cross-component ALF. +\\ + +\Option{CCALFQpTh} & +%\ShortOption{\None} & +\Default{37} & +QP threshold above which the encoder reduces cross-component ALF usage. +\\ + \Option{SMVD} & %\ShortOption{\None} & \Default{false} & diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 77f765178..51e75c630 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -212,6 +212,9 @@ void EncApp::xInitLibCfg() m_cEncLib.setNoPartitionConstraintsOverrideConstraintFlag ( !m_SplitConsOverrideEnabledFlag ); m_cEncLib.setNoSaoConstraintFlag ( !m_bUseSAO ); m_cEncLib.setNoAlfConstraintFlag ( !m_alf ); +#if JVET_Q0795_CCALF + m_cEncLib.setNoAlfConstraintFlag ( !m_ccalf ); +#endif m_cEncLib.setNoRefWraparoundConstraintFlag ( m_bNoRefWraparoundConstraintFlag ); m_cEncLib.setNoTemporalMvpConstraintFlag ( m_TMVPModeId ? false : true ); m_cEncLib.setNoSbtmvpConstraintFlag ( m_SubPuMvpMode ? false : true ); @@ -755,6 +758,10 @@ void EncApp::xInitLibCfg() m_cEncLib.setForceSingleSplitThread ( m_forceSplitSequential ); #endif m_cEncLib.setUseALF ( m_alf ); +#if JVET_Q0795_CCALF + m_cEncLib.setUseCCALF ( m_ccalf ); + m_cEncLib.setCCALFQpThreshold ( m_ccalfQpThreshold ); +#endif m_cEncLib.setLmcs ( m_lmcsEnabled ); m_cEncLib.setReshapeSignalType ( m_reshapeSignalType ); m_cEncLib.setReshapeIntraCMD ( m_intraCMD ); diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index cdad7cff4..5feb6cdb8 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -87,6 +87,9 @@ EncAppCfg::EncAppCfg() , m_noPartitionConstraintsOverrideConstraintFlag(false) , m_bNoSaoConstraintFlag(false) , m_bNoAlfConstraintFlag(false) +#if JVET_Q0795_CCALF +, m_noCCAlfConstraintFlag(false) +#endif , m_bNoRefWraparoundConstraintFlag(false) , m_bNoTemporalMvpConstraintFlag(false) , m_bNoSbtmvpConstraintFlag(false) @@ -1357,7 +1360,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("NumWppExtraLines", m_numWppExtraLines, 0, "Number of additional wpp lines to switch when threads are blocked") ("DebugCTU", m_debugCTU, -1, "If DebugBitstream is present, load frames up to this POC from this bitstream. Starting with DebugPOC-frame at CTUline containin debug CTU.") ("EnsureWppBitEqual", m_ensureWppBitEqual, false, "Ensure the results are equal to results with WPP-style parallelism, even if WPP is off") - ( "ALF", m_alf, true, "Adpative Loop Filter\n" ) + ( "ALF", m_alf, true, "Adaptive Loop Filter\n" ) +#if JVET_Q0795_CCALF + ( "CCALF", m_ccalf, true, "Cross-component Adaptive Loop Filter" ) + ( "CCALFQpTh", m_ccalfQpThreshold, 37, "QP threshold above which encoder reduces CCALF usage") +#endif ( "ScalingRatioHor", m_scalingRatioHor, 1.0, "Scaling ratio in hor direction" ) ( "ScalingRatioVer", m_scalingRatioVer, 1.0, "Scaling ratio in ver direction" ) ( "FractionNumFrames", m_fractionOfFrames, 1.0, "Encode a fraction of the specified in FramesToBeEncoded frames" ) @@ -2533,6 +2540,12 @@ bool EncAppCfg::xCheckParameter() } +#if JVET_Q0795_CCALF + if (!m_alf) + { + xConfirmPara( m_ccalf, "CCALF cannot be enabled when ALF is disabled" ); + } +#endif xConfirmPara( m_iSourceWidth % SPS::getWinUnitX(m_chromaFormatIDC) != 0, "Picture width must be an integer multiple of the specified chroma subsampling"); @@ -3604,6 +3617,9 @@ void EncAppCfg::xPrintParameter() msg( VERBOSE, "MCTS:%d ", m_MCTSEncConstraint ); msg( VERBOSE, "SAO:%d ", (m_bUseSAO)?(1):(0)); msg( VERBOSE, "ALF:%d ", m_alf ? 1 : 0 ); +#if JVET_Q0795_CCALF + msg( VERBOSE, "CCALF:%d ", m_ccalf ? 1 : 0 ); +#endif msg( VERBOSE, "WPP:%d ", (int)m_useWeightedPred); msg( VERBOSE, "WPB:%d ", (int)m_useWeightedBiPred); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 4ce37e402..f71b66571 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -137,6 +137,9 @@ protected: bool m_noPartitionConstraintsOverrideConstraintFlag; bool m_bNoSaoConstraintFlag; bool m_bNoAlfConstraintFlag; +#if JVET_Q0795_CCALF + bool m_noCCAlfConstraintFlag; +#endif bool m_bNoRefWraparoundConstraintFlag; bool m_bNoTemporalMvpConstraintFlag; bool m_bNoSbtmvpConstraintFlag; @@ -664,6 +667,10 @@ protected: bool m_forceDecodeBitstream1; bool m_alf; ///< Adaptive Loop Filter +#if JVET_Q0795_CCALF + bool m_ccalf; + int m_ccalfQpThreshold; +#endif double m_scalingRatioHor; double m_scalingRatioVer; diff --git a/source/Lib/CommonLib/AdaptiveLoopFilter.cpp b/source/Lib/CommonLib/AdaptiveLoopFilter.cpp index 998041f9c..b1ef4b056 100644 --- a/source/Lib/CommonLib/AdaptiveLoopFilter.cpp +++ b/source/Lib/CommonLib/AdaptiveLoopFilter.cpp @@ -63,6 +63,9 @@ AdaptiveLoopFilter::AdaptiveLoopFilter() } m_deriveClassificationBlk = deriveClassificationBlk; +#if JVET_Q0795_CCALF + m_filterCcAlf = filterBlkCcAlf<CC_ALF>; +#endif m_filter5x5Blk = filterBlk<ALF_FILTER_5>; m_filter7x7Blk = filterBlk<ALF_FILTER_7>; @@ -285,6 +288,47 @@ const int AdaptiveLoopFilter::m_classToFilterMapping[NUM_FIXED_FILTER_SETS][MAX_ { 16, 31, 32, 15, 60, 30, 4, 17, 19, 25, 22, 20, 4, 53, 19, 21, 22, 46, 25, 55, 26, 48, 63, 58, 55 }, }; +#if JVET_Q0795_CCALF +void AdaptiveLoopFilter::applyCcAlfFilter(CodingStructure &cs, ComponentID compID, const PelBuf &dstBuf, + const PelUnitBuf &recYuvExt, uint8_t *filterControl, + const short filterSet[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF], + const int selectedFilterIdx) +{ + int ctuIdx = 0; + for( int yPos = 0; yPos < m_picHeight; yPos += m_maxCUHeight ) + { + for( int xPos = 0; xPos < m_picWidth; xPos += m_maxCUWidth ) + { + const int width = ( xPos + m_maxCUWidth > m_picWidth ) ? ( m_picWidth - xPos ) : m_maxCUWidth; + const int height = ( yPos + m_maxCUHeight > m_picHeight ) ? ( m_picHeight - yPos ) : m_maxCUHeight; + const UnitArea area( m_chromaFormat, Area( xPos, yPos, width, height ) ); + const int chromaScaleX = getComponentScaleX( compID, m_chromaFormat ); + const int chromaScaleY = getComponentScaleY( compID, m_chromaFormat ); + + Area blkDst(xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, height >> chromaScaleY); + Area blkSrc(xPos, yPos, width, height); + + int filterIdx = + (filterControl == nullptr) + ? selectedFilterIdx + : filterControl[(yPos >> cs.pcv->maxCUHeightLog2) * cs.pcv->widthInCtus + (xPos >> cs.pcv->maxCUWidthLog2)]; + bool skipFiltering = (filterControl != nullptr && filterIdx == 0) ? true : false; + if (!skipFiltering) + { + if (filterControl != nullptr) + filterIdx--; + + const int16_t *filterCoeff = filterSet[filterIdx]; + + m_filterCcAlf(dstBuf, recYuvExt, blkDst, blkSrc, compID, filterCoeff, m_clpRngs, cs, m_alfVBLumaCTUHeight, + m_alfVBLumaPos); + } + ctuIdx++; + } + } +} +#endif + void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs) { @@ -342,6 +386,12 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs) for( int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ ) { ctuEnableFlag |= m_ctuEnableFlag[compIdx][ctuIdx] > 0; +#if JVET_Q0795_CCALF + if (cu->slice->m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1]) + { + ctuEnableFlag |= m_ccAlfFilterControl[compIdx - 1][ctuIdx] > 0; + } +#endif } int rasterSliceAlfPad = 0; if( ctuEnableFlag && isCrossedByVirtualBoundaries( cs, xPos, yPos, width, height, clipTop, clipBottom, clipLeft, clipRight, numHorVirBndry, numVerVirBndry, horVirBndryPos, verVirBndryPos, rasterSliceAlfPad ) ) @@ -417,6 +467,24 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs) , m_alfVBChmaCTUHeight , m_alfVBChmaPos ); } +#if JVET_Q0795_CCALF + if (cu->slice->m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1]) + { + const int filterIdx = m_ccAlfFilterControl[compIdx - 1][ctuIdx]; + + if (filterIdx != 0) + { + const Area blkSrc(0, 0, w >> chromaScaleX, h >> chromaScaleY); + Area blkDst(xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, + height >> chromaScaleY); + + const int16_t *filterCoeff = m_ccAlfFilterParam.ccAlfCoeff[compIdx - 1][filterIdx - 1]; + + m_filterCcAlf(recYuv.get(compID), tmpYuv, blkDst, blkSrc, compID, filterCoeff, m_clpRngs, cs, + m_alfVBLumaCTUHeight, m_alfVBLumaPos); + } + } +#endif } xStart = xEnd; @@ -427,46 +495,63 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs) } else { - const UnitArea area( cs.area.chromaFormat, Area( xPos, yPos, width, height ) ); - if( m_ctuEnableFlag[COMPONENT_Y][ctuIdx] ) - { - Area blk( xPos, yPos, width, height ); - deriveClassification( m_classifier, tmpYuv.get( COMPONENT_Y ), blk, blk ); - short filterSetIndex = alfCtuFilterIndex[ctuIdx]; - short *coeff; - short *clip; - if (filterSetIndex >= NUM_FIXED_FILTER_SETS) + const UnitArea area( cs.area.chromaFormat, Area( xPos, yPos, width, height ) ); + if( m_ctuEnableFlag[COMPONENT_Y][ctuIdx] ) { - coeff = m_coeffApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS]; - clip = m_clippApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS]; + Area blk( xPos, yPos, width, height ); + deriveClassification( m_classifier, tmpYuv.get( COMPONENT_Y ), blk, blk ); + short filterSetIndex = alfCtuFilterIndex[ctuIdx]; + short *coeff; + short *clip; + if (filterSetIndex >= NUM_FIXED_FILTER_SETS) + { + coeff = m_coeffApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS]; + clip = m_clippApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS]; + } + else + { + coeff = m_fixedFilterSetCoeffDec[filterSetIndex]; + clip = m_clipDefault; + } + m_filter7x7Blk(m_classifier, recYuv, tmpYuv, blk, blk, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs + , m_alfVBLumaCTUHeight + , m_alfVBLumaPos + ); } - else + + for( int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ ) { - coeff = m_fixedFilterSetCoeffDec[filterSetIndex]; - clip = m_clipDefault; - } - m_filter7x7Blk(m_classifier, recYuv, tmpYuv, blk, blk, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs - , m_alfVBLumaCTUHeight - , m_alfVBLumaPos - ); - } + ComponentID compID = ComponentID( compIdx ); + const int chromaScaleX = getComponentScaleX( compID, tmpYuv.chromaFormat ); + const int chromaScaleY = getComponentScaleY( compID, tmpYuv.chromaFormat ); - for( int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ ) - { - ComponentID compID = ComponentID( compIdx ); - const int chromaScaleX = getComponentScaleX( compID, tmpYuv.chromaFormat ); - const int chromaScaleY = getComponentScaleY( compID, tmpYuv.chromaFormat ); + if (m_ctuEnableFlag[compIdx][ctuIdx]) + { + Area blk(xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, height >> chromaScaleY); + uint8_t alt_num = m_ctuAlternative[compIdx][ctuIdx]; + m_filter5x5Blk(m_classifier, recYuv, tmpYuv, blk, blk, compID, m_chromaCoeffFinal[alt_num], + m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_alfVBChmaCTUHeight, + m_alfVBChmaPos); + } +#if JVET_Q0795_CCALF + if (cu->slice->m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1]) + { + const int filterIdx = m_ccAlfFilterControl[compIdx - 1][ctuIdx]; - if( m_ctuEnableFlag[compIdx][ctuIdx] ) - { - Area blk( xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, height >> chromaScaleY ); - uint8_t alt_num = m_ctuAlternative[compIdx][ctuIdx]; - m_filter5x5Blk(m_classifier, recYuv, tmpYuv, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs - , m_alfVBChmaCTUHeight - , m_alfVBChmaPos); + if (filterIdx != 0) + { + Area blkDst(xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, height >> chromaScaleY); + Area blkSrc(xPos, yPos, width, height); + + const int16_t *filterCoeff = m_ccAlfFilterParam.ccAlfCoeff[compIdx - 1][filterIdx - 1]; + + m_filterCcAlf(recYuv.get(compID), tmpYuv, blkDst, blkSrc, compID, filterCoeff, m_clpRngs, cs, + m_alfVBLumaCTUHeight, m_alfVBLumaPos); + } + } +#endif } } - } ctuIdx++; } } @@ -581,6 +666,10 @@ void AdaptiveLoopFilter::create( const int picWidth, const int picHeight, const m_numCTUsInWidth = ( m_picWidth / m_maxCUWidth ) + ( ( m_picWidth % m_maxCUWidth ) ? 1 : 0 ); m_numCTUsInHeight = ( m_picHeight / m_maxCUHeight ) + ( ( m_picHeight % m_maxCUHeight ) ? 1 : 0 ); m_numCTUsInPic = m_numCTUsInHeight * m_numCTUsInWidth; +#if JVET_Q0795_CCALF + m_filterShapesCcAlf[0].push_back(AlfFilterShape(size_CC_ALF)); + m_filterShapesCcAlf[1].push_back(AlfFilterShape(size_CC_ALF)); +#endif m_filterShapes[CHANNEL_TYPE_LUMA].push_back( AlfFilterShape( 7 ) ); m_filterShapes[CHANNEL_TYPE_CHROMA].push_back( AlfFilterShape( 5 ) ); m_alfVBLumaPos = m_maxCUHeight - ALF_VB_POS_ABOVE_CTUROW_LUMA; @@ -657,6 +746,11 @@ void AdaptiveLoopFilter::create( const int picWidth, const int picHeight, const m_clipDefault[i] = m_alfClippingValues[CHANNEL_TYPE_LUMA][0]; } m_created = true; + +#if JVET_Q0795_CCALF + m_ccAlfFilterControl[0] = new uint8_t[m_numCTUsInPic]; + m_ccAlfFilterControl[1] = new uint8_t[m_numCTUsInPic]; +#endif } void AdaptiveLoopFilter::destroy() @@ -678,6 +772,22 @@ void AdaptiveLoopFilter::destroy() m_filterShapes[CHANNEL_TYPE_LUMA].clear(); m_filterShapes[CHANNEL_TYPE_CHROMA].clear(); m_created = false; + +#if JVET_Q0795_CCALF + m_filterShapesCcAlf[0].clear(); + m_filterShapesCcAlf[1].clear(); + if ( m_ccAlfFilterControl[0] ) + { + delete [] m_ccAlfFilterControl[0]; + m_ccAlfFilterControl[0] = nullptr; + } + + if ( m_ccAlfFilterControl[1] ) + { + delete [] m_ccAlfFilterControl[1]; + m_ccAlfFilterControl[1] = nullptr; + } +#endif } void AdaptiveLoopFilter::deriveClassification( AlfClassifier** classifier, const CPelBuf& srcLuma, const Area& blkDst, const Area& blk ) @@ -1152,3 +1262,96 @@ void AdaptiveLoopFilter::filterBlk(AlfClassifier **classifier, const PelUnitBuf pImgYPad6 += srcStride2; } } + +#if JVET_Q0795_CCALF +template<AlfFilterType filtTypeCcAlf> +void AdaptiveLoopFilter::filterBlkCcAlf(const PelBuf &dstBuf, const CPelUnitBuf &recSrc, const Area &blkDst, + const Area &blkSrc, const ComponentID compId, const int16_t *filterCoeff, + const ClpRngs &clpRngs, CodingStructure &cs, int vbCTUHeight, int vbPos) +{ + CHECK(1 << floorLog2(vbCTUHeight) != vbCTUHeight, "Not a power of 2"); + + CHECK(!isChroma(compId), "Must be chroma"); + + const SPS* sps = cs.slice->getSPS(); + ChromaFormat nChromaFormat = sps->getChromaFormatIdc(); + const int clsSizeY = 4; + const int clsSizeX = 4; + const int startHeight = blkDst.y; + const int endHeight = blkDst.y + blkDst.height; + const int startWidth = blkDst.x; + const int endWidth = blkDst.x + blkDst.width; + const int scaleX = getComponentScaleX(compId, nChromaFormat); + const int scaleY = getComponentScaleY(compId, nChromaFormat); + + CHECK( startHeight % clsSizeY, "Wrong startHeight in filtering" ); + CHECK( startWidth % clsSizeX, "Wrong startWidth in filtering" ); + CHECK( ( endHeight - startHeight ) % clsSizeY, "Wrong endHeight in filtering" ); + CHECK( ( endWidth - startWidth ) % clsSizeX, "Wrong endWidth in filtering" ); + + CPelBuf srcBuf = recSrc.get(COMPONENT_Y); + const int lumaStride = srcBuf.stride; + const Pel * lumaPtr = srcBuf.buf + blkSrc.y * lumaStride + blkSrc.x; + + const int chromaStride = dstBuf.stride; + Pel * chromaPtr = dstBuf.buf + blkDst.y * chromaStride + blkDst.x; + + for( int i = 0; i < endHeight - startHeight; i += clsSizeY ) + { + for( int j = 0; j < endWidth - startWidth; j += clsSizeX ) + { + for( int ii = 0; ii < clsSizeY; ii++ ) + { + int row = ii; + int col = j; + Pel *srcSelf = chromaPtr + col + row * chromaStride; + + int offset1 = lumaStride; + int offset2 = -lumaStride; + int offset3 = 2 * lumaStride; + row <<= scaleY; + col <<= scaleX; + const Pel *srcCross = lumaPtr + col + row * lumaStride; + + int pos = ((startHeight + i + ii) << scaleY) & (vbCTUHeight - 1); + if (pos == (vbPos - 2) || pos == (vbPos + 1)) + { + offset3 = offset1; + } + else if (pos == (vbPos - 1) || pos == vbPos) + { + offset1 = 0; + offset2 = 0; + offset3 = 0; + } + + for (int jj = 0; jj < clsSizeX; jj++) + { + const int jj2 = (jj << scaleX); + const int offset0 = 0; + + int sum = 0; + const Pel currSrcCross = srcCross[offset0 + jj2]; + sum += filterCoeff[0] * (srcCross[offset2 + jj2 ] - currSrcCross); + sum += filterCoeff[1] * (srcCross[offset0 + jj2 - 1] - currSrcCross); + sum += filterCoeff[2] * (srcCross[offset0 + jj2 + 1] - currSrcCross); + sum += filterCoeff[3] * (srcCross[offset1 + jj2 - 1] - currSrcCross); + sum += filterCoeff[4] * (srcCross[offset1 + jj2 ] - currSrcCross); + sum += filterCoeff[5] * (srcCross[offset1 + jj2 + 1] - currSrcCross); + sum += filterCoeff[6] * (srcCross[offset3 + jj2 ] - currSrcCross); + + sum = (sum + ((1 << m_scaleBits ) >> 1)) >> m_scaleBits; + const int offset = 1 << clpRngs.comp[compId].bd >> 1; + sum = ClipPel(sum + offset, clpRngs.comp[compId]) - offset; + sum += srcSelf[jj]; + srcSelf[jj] = ClipPel(sum, clpRngs.comp[compId]); + } + } + } + + chromaPtr += chromaStride * clsSizeY; + + lumaPtr += lumaStride * clsSizeY << getComponentScaleY(compId, nChromaFormat); + } +} +#endif diff --git a/source/Lib/CommonLib/AdaptiveLoopFilter.h b/source/Lib/CommonLib/AdaptiveLoopFilter.h index f93fd8e6c..39d173114 100644 --- a/source/Lib/CommonLib/AdaptiveLoopFilter.h +++ b/source/Lib/CommonLib/AdaptiveLoopFilter.h @@ -91,6 +91,13 @@ public: const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, const int shift, const int vbCTUHeight, int vbPos); void deriveClassification( AlfClassifier** classifier, const CPelBuf& srcLuma, const Area& blkDst, const Area& blk ); +#if JVET_Q0795_CCALF + template<AlfFilterType filtTypeCcAlf> + static void filterBlkCcAlf(const PelBuf &dstBuf, const CPelUnitBuf &recSrc, const Area &blkDst, const Area &blkSrc, + const ComponentID compId, const int16_t *filterCoeff, const ClpRngs &clpRngs, + CodingStructure &cs, int vbCTUHeight, int vbPos); +#endif + template<AlfFilterType filtType> static void filterBlk(AlfClassifier **classifier, const PelUnitBuf &recDst, const CPelUnitBuf &recSrc, const Area &blkDst, const Area &blk, const ComponentID compId, const short *filterSet, @@ -100,6 +107,17 @@ public: const Area &blkDst, const Area &blk, const int shift, const int vbCTUHeight, int vbPos); +#if JVET_Q0795_CCALF + void (*m_filterCcAlf)(const PelBuf &dstBuf, const CPelUnitBuf &recSrc, const Area &blkDst, const Area &blkSrc, + const ComponentID compId, const int16_t *filterCoeff, const ClpRngs &clpRngs, + CodingStructure &cs, int vbCTUHeight, int vbPos); + void applyCcAlfFilter(CodingStructure &cs, ComponentID compID, const PelBuf &dstBuf, const PelUnitBuf &recYuvExt, + uint8_t * filterControl, + const short filterSet[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF], + const int selectedFilterIdx); + CcAlfFilterParam &getCcAlfFilterParam() { return m_ccAlfFilterParam; } + uint8_t* getCcAlfControlIdc(const ComponentID compID) { return m_ccAlfFilterControl[compID-1]; } +#endif void (*m_filter5x5Blk)(AlfClassifier **classifier, const PelUnitBuf &recDst, const CPelUnitBuf &recSrc, const Area &blkDst, const Area &blk, const ComponentID compId, const short *filterSet, const short *fClipSet, const ClpRng &clpRng, CodingStructure &cs, const int vbCTUHeight, @@ -117,6 +135,11 @@ public: protected: bool isCrossedByVirtualBoundaries( const CodingStructure& cs, const int xPos, const int yPos, const int width, const int height, bool& clipTop, bool& clipBottom, bool& clipLeft, bool& clipRight, int& numHorVirBndry, int& numVerVirBndry, int horVirBndryPos[], int verVirBndryPos[], int& rasterSliceAlfPad ); +#if JVET_Q0795_CCALF + static constexpr int m_scaleBits = 7; // 8-bits + CcAlfFilterParam m_ccAlfFilterParam; + uint8_t* m_ccAlfFilterControl[2]; +#endif static const int m_classToFilterMapping[NUM_FIXED_FILTER_SETS][MAX_NUM_ALF_CLASSES]; static const int m_fixedFilterSetCoeff[ALF_FIXED_FILTER_NUM][MAX_NUM_ALF_LUMA_COEFF]; short m_fixedFilterSetCoeffDec[NUM_FIXED_FILTER_SETS][MAX_NUM_ALF_CLASSES * MAX_NUM_ALF_LUMA_COEFF]; @@ -127,6 +150,9 @@ protected: short m_chromaCoeffFinal[MAX_NUM_ALF_ALTERNATIVES_CHROMA][MAX_NUM_ALF_CHROMA_COEFF]; AlfParam* m_alfParamChroma; Pel m_alfClippingValues[MAX_NUM_CHANNEL_TYPE][MaxAlfNumClippingValues]; +#if JVET_Q0795_CCALF + std::vector<AlfFilterShape> m_filterShapesCcAlf[2]; +#endif std::vector<AlfFilterShape> m_filterShapes[MAX_NUM_CHANNEL_TYPE]; AlfClassifier** m_classifier; short m_coeffFinal[MAX_NUM_ALF_CLASSES * MAX_NUM_ALF_LUMA_COEFF]; diff --git a/source/Lib/CommonLib/AlfParameters.h b/source/Lib/CommonLib/AlfParameters.h index abaef3a49..45fa9b275 100644 --- a/source/Lib/CommonLib/AlfParameters.h +++ b/source/Lib/CommonLib/AlfParameters.h @@ -48,9 +48,17 @@ enum AlfFilterType { ALF_FILTER_5, ALF_FILTER_7, +#if JVET_Q0795_CCALF + CC_ALF, +#endif ALF_NUM_OF_FILTER_TYPES }; + +#if JVET_Q0795_CCALF +static const int size_CC_ALF = -1; +#endif + struct AlfFilterShape { AlfFilterShape( int size ) @@ -97,6 +105,16 @@ struct AlfFilterShape filterType = ALF_FILTER_7; } +#if JVET_Q0795_CCALF + else if (size == size_CC_ALF) + { + size = 4; + filterLength = 8; + numCoeff = 8; + filterSize = 8; + filterType = CC_ALF; + } +#endif else { filterType = ALF_NUM_OF_FILTER_TYPES; @@ -231,6 +249,43 @@ struct AlfParam } }; +#if JVET_Q0795_CCALF +struct CcAlfFilterParam +{ + bool ccAlfFilterEnabled[2]; + bool ccAlfFilterIdxEnabled[2][MAX_NUM_CC_ALF_FILTERS]; + uint8_t ccAlfFilterCount[2]; + short ccAlfCoeff[2][MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF]; + int newCcAlfFilter[2]; + int numberValidComponents; + CcAlfFilterParam() + { + reset(); + } + void reset() + { + std::memset( ccAlfFilterEnabled, false, sizeof( ccAlfFilterEnabled ) ); + std::memset( ccAlfFilterIdxEnabled, false, sizeof( ccAlfFilterIdxEnabled ) ); + std::memset( ccAlfCoeff, 0, sizeof( ccAlfCoeff ) ); + ccAlfFilterCount[0] = ccAlfFilterCount[1] = MAX_NUM_CC_ALF_FILTERS; + numberValidComponents = 3; + newCcAlfFilter[0] = newCcAlfFilter[1] = 0; + } + const CcAlfFilterParam& operator = ( const CcAlfFilterParam& src ) + { + std::memcpy( ccAlfFilterEnabled, src.ccAlfFilterEnabled, sizeof( ccAlfFilterEnabled ) ); + std::memcpy( ccAlfFilterIdxEnabled, src.ccAlfFilterIdxEnabled, sizeof( ccAlfFilterIdxEnabled ) ); + std::memcpy( ccAlfCoeff, src.ccAlfCoeff, sizeof( ccAlfCoeff ) ); + ccAlfFilterCount[0] = src.ccAlfFilterCount[0]; + ccAlfFilterCount[1] = src.ccAlfFilterCount[1]; + numberValidComponents = src.numberValidComponents; + newCcAlfFilter[0] = src.newCcAlfFilter[0]; + newCcAlfFilter[1] = src.newCcAlfFilter[1]; + + return *this; + } +}; +#endif //! \} #endif // end of #ifndef __ALFPARAMETERS__ diff --git a/source/Lib/CommonLib/CodingStatistics.h b/source/Lib/CommonLib/CodingStatistics.h index 375ed6ec2..59b610717 100644 --- a/source/Lib/CommonLib/CodingStatistics.h +++ b/source/Lib/CommonLib/CodingStatistics.h @@ -117,6 +117,9 @@ enum CodingStatisticsType STATS__CABAC_BITS__MULTI_REF_LINE, STATS__CABAC_BITS__SYMMVD_FLAG, STATS__CABAC_BITS__BDPCM_MODE, +#if JVET_Q0795_CCALF + STATS__CABAC_BITS__CROSS_COMPONENT_ALF_BLOCK_LEVEL_IDC, +#endif STATS__TOOL_TOTAL_FRAME,// This is a special case and is not included in the report. STATS__TOOL_AFF, STATS__TOOL_EMT, @@ -209,6 +212,9 @@ static inline const char* getName(CodingStatisticsType name) "CABAC_BITS__MULTI_REF_LINE", "CABAC_BITS__SYMMVD_FLAG", "CABAC_BITS__BDPCM_MODE", +#if JVET_Q0795_CCALF + "CABAC_BITS__CROSS_COMPONENT_ALF_BLOCK_LEVEL_IDC", +#endif "TOOL_FRAME", "TOOL_AFFINE", "TOOL_EMT", diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index f24085c10..03f20a9b0 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -187,6 +187,12 @@ static const int MAX_NUM_ALF_CHROMA_COEFF = 7; static const int MAX_ALF_FILTER_LENGTH = 7; static const int MAX_NUM_ALF_COEFF = MAX_ALF_FILTER_LENGTH * MAX_ALF_FILTER_LENGTH / 2 + 1; static const int MAX_ALF_PADDING_SIZE = 4; +#if JVET_Q0795_CCALF +#define MAX_NUM_CC_ALF_FILTERS 4 +static constexpr int MAX_NUM_CC_ALF_CHROMA_COEFF = 8; +static constexpr int CCALF_DYNAMIC_RANGE = 6; +static constexpr int CCALF_BITS_PER_COEFF_LEVEL = 3; +#endif static const int ALF_FIXED_FILTER_NUM = 64; static const int ALF_CTB_MAX_NUM_APS = 8; @@ -700,6 +706,11 @@ static inline int ceilLog2(uint32_t x) #define PARL_PARAM0(DEF) #endif +#if JVET_Q0795_CCALF +static const uint32_t CCALF_CANDS_COEFF_NR = 8; +static const int CCALF_SMALL_TAB[CCALF_CANDS_COEFF_NR] = { 0, 1, 2, 4, 8, 16, 32, 64 }; +#endif + //! \} #endif // end of #ifndef __COMMONDEF__ diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp index 749b86739..840e71802 100644 --- a/source/Lib/CommonLib/Contexts.cpp +++ b/source/Lib/CommonLib/Contexts.cpp @@ -825,6 +825,16 @@ const CtxSet ContextSetCfg::AlfUseTemporalFilt = ContextSetCfg::addCtxSet { 0, }, }); +#if JVET_Q0795_CCALF +const CtxSet ContextSetCfg::CcAlfFilterControlFlag = ContextSetCfg::addCtxSet +({ + { 35, 35, 35, 35, 35, 35 }, + { 35, 35, 35, 35, 35, 35 }, + { 35, 35, 35, 35, 35, 35 }, + { DWS, DWS, DWS, DWS, DWS, DWS }, +}); +#endif + const CtxSet ContextSetCfg::CiipFlag = ContextSetCfg::addCtxSet ({ { 50, }, diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h index e831206ce..d17ce051c 100644 --- a/source/Lib/CommonLib/Contexts.h +++ b/source/Lib/CommonLib/Contexts.h @@ -263,6 +263,9 @@ public: static const CtxSet ctbAlfFlag; static const CtxSet ctbAlfAlternative; static const CtxSet AlfUseTemporalFilt; +#if JVET_Q0795_CCALF + static const CtxSet CcAlfFilterControlFlag; +#endif static const CtxSet CiipFlag; static const CtxSet SmvdFlag; static const CtxSet IBCFlag; diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp index e923fdf52..a612ec221 100644 --- a/source/Lib/CommonLib/Slice.cpp +++ b/source/Lib/CommonLib/Slice.cpp @@ -128,6 +128,12 @@ Slice::Slice() } memset(m_alfApss, 0, sizeof(m_alfApss)); +#if JVET_Q0795_CCALF + m_ccAlfFilterParam.reset(); + resetTileGroupAlfEnabledFlag(); + resetTileGroupCcAlCbfEnabledFlag(); + resetTileGroupCcAlCrfEnabledFlag(); +#endif m_sliceMap.initSliceMap(); } @@ -171,6 +177,13 @@ void Slice::initSlice() m_isDRAP = false; m_latestDRAPPOC = MAX_INT; resetTileGroupAlfEnabledFlag(); +#if JVET_Q0795_CCALF + m_ccAlfFilterParam.reset(); + m_tileGroupCcAlfCbEnabledFlag = 0; + m_tileGroupCcAlfCrEnabledFlag = 0; + m_tileGroupCcAlfCbApsId = -1; + m_tileGroupCcAlfCrApsId = -1; +#endif } void Slice::inheritFromPicHeader( PicHeader *picHeader, const PPS *pps, const SPS *sps ) @@ -212,7 +225,15 @@ void Slice::inheritFromPicHeader( PicHeader *picHeader, const PPS *pps, const SP setTileGroupAlfEnabledFlag(COMPONENT_Cr, picHeader->getAlfEnabledFlag(COMPONENT_Cr)); setTileGroupNumAps(picHeader->getNumAlfAps()); setAlfAPSs(picHeader->getAlfAPSs()); - setTileGroupApsIdChroma(picHeader->getAlfApsIdChroma()); + setTileGroupApsIdChroma(picHeader->getAlfApsIdChroma()); +#if JVET_Q0795_CCALF + setTileGroupCcAlfCbEnabledFlag(picHeader->getCcAlfEnabledFlag(COMPONENT_Cb)); + setTileGroupCcAlfCrEnabledFlag(picHeader->getCcAlfEnabledFlag(COMPONENT_Cr)); + setTileGroupCcAlfCbApsId(picHeader->getCcAlfCbApsId()); + setTileGroupCcAlfCrApsId(picHeader->getCcAlfCrApsId()); + m_ccAlfFilterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1] = picHeader->getCcAlfEnabledFlag(COMPONENT_Cb); + m_ccAlfFilterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1] = picHeader->getCcAlfEnabledFlag(COMPONENT_Cr); +#endif } void Slice::setNumEntryPoints( const PPS *pps ) @@ -806,6 +827,15 @@ void Slice::copySliceInfo(Slice *pSrc, bool cpyAlmostAll) m_scalingRatio[i][j] = pSrc->m_scalingRatio[i][j]; } } +#if JVET_Q0795_CCALF + m_ccAlfFilterParam = pSrc->m_ccAlfFilterParam; + m_ccAlfFilterControl[0] = pSrc->m_ccAlfFilterControl[0]; + m_ccAlfFilterControl[1] = pSrc->m_ccAlfFilterControl[1]; + m_tileGroupCcAlfCbEnabledFlag = pSrc->m_tileGroupCcAlfCbEnabledFlag; + m_tileGroupCcAlfCrEnabledFlag = pSrc->m_tileGroupCcAlfCrEnabledFlag; + m_tileGroupCcAlfCbApsId = pSrc->m_tileGroupCcAlfCbApsId; + m_tileGroupCcAlfCrApsId = pSrc->m_tileGroupCcAlfCrApsId; +#endif } diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index b95485113..584b3f952 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -242,6 +242,9 @@ class ConstraintInfo bool m_noPartitionConstraintsOverrideConstraintFlag; bool m_noSaoConstraintFlag; bool m_noAlfConstraintFlag; +#if JVET_Q0795_CCALF + bool m_noCCAlfConstraintFlag; +#endif bool m_noRefWraparoundConstraintFlag; bool m_noTemporalMvpConstraintFlag; bool m_noSbtmvpConstraintFlag; @@ -286,6 +289,9 @@ public: , m_noPartitionConstraintsOverrideConstraintFlag(false) , m_noSaoConstraintFlag (false) , m_noAlfConstraintFlag (false) +#if JVET_Q0795_CCALF + , m_noCCAlfConstraintFlag (false) +#endif , m_noRefWraparoundConstraintFlag(false) , m_noTemporalMvpConstraintFlag(false) , m_noSbtmvpConstraintFlag (false) @@ -353,6 +359,10 @@ public: void setNoSaoConstraintFlag(bool bVal) { m_noSaoConstraintFlag = bVal; } bool getNoAlfConstraintFlag() const { return m_noAlfConstraintFlag; } void setNoAlfConstraintFlag(bool bVal) { m_noAlfConstraintFlag = bVal; } +#if JVET_Q0795_CCALF + bool getNoCCAlfConstraintFlag() const { return m_noCCAlfConstraintFlag; } + void setNoCCAlfConstraintFlag(bool val) { m_noCCAlfConstraintFlag = val; } +#endif bool getNoJointCbCrConstraintFlag() const { return m_noJointCbCrConstraintFlag; } void setNoJointCbCrConstraintFlag(bool bVal) { m_noJointCbCrConstraintFlag = bVal; } bool getNoRefWraparoundConstraintFlag() const { return m_noRefWraparoundConstraintFlag; } @@ -1128,7 +1138,9 @@ private: ProfileTierLevel m_profileTierLevel; bool m_alfEnabledFlag; - +#if JVET_Q0795_CCALF + bool m_ccalfEnabledFlag; +#endif bool m_wrapAroundEnabledFlag; unsigned m_wrapAroundOffset; unsigned m_IBCFlag; @@ -1323,6 +1335,10 @@ public: bool getALFEnabledFlag() const { return m_alfEnabledFlag; } void setALFEnabledFlag( bool b ) { m_alfEnabledFlag = b; } +#if JVET_Q0795_CCALF +bool getCCALFEnabledFlag() const { return m_ccalfEnabledFlag; } +void setCCALFEnabledFlag( bool b ) { m_ccalfEnabledFlag = b; } +#endif void setJointCbCrEnabledFlag(bool bVal) { m_JointCbCrEnabledFlag = bVal; } bool getJointCbCrEnabledFlag() const { return m_JointCbCrEnabledFlag; } @@ -1835,6 +1851,9 @@ private: AlfParam m_alfAPSParam; SliceReshapeInfo m_reshapeAPSInfo; ScalingList m_scalingListApsInfo; +#if JVET_Q0795_CCALF + CcAlfFilterParam m_ccAlfAPSParam; +#endif public: APS(); @@ -1857,6 +1876,10 @@ public: SliceReshapeInfo& getReshaperAPSInfo() { return m_reshapeAPSInfo; } void setScalingList( ScalingList& scalingListAPSInfo ) { m_scalingListApsInfo = scalingListAPSInfo; } ScalingList& getScalingList() { return m_scalingListApsInfo; } +#if JVET_Q0795_CCALF + void setCcAlfAPSParam(CcAlfFilterParam& ccAlfAPSParam) { m_ccAlfAPSParam = ccAlfAPSParam; } + CcAlfFilterParam& getCcAlfAPSParam() { return m_ccAlfAPSParam; } +#endif }; struct WPScalingParam { @@ -1933,6 +1956,11 @@ private: int m_numAlfAps; //!< number of alf aps active for the picture std::vector<int> m_alfApsId; //!< list of alf aps for the picture int m_alfChromaApsId; //!< chroma alf aps ID +#if JVET_Q0795_CCALF + bool m_ccalfEnabledFlag[MAX_NUM_COMPONENT]; + int m_ccalfCbApsId; + int m_ccalfCrApsId; +#endif bool m_depQuantEnabledFlag; //!< dependent quantization enabled flag bool m_signDataHidingEnabledFlag; //!< sign data hiding enabled flag bool m_deblockingFilterOverridePresentFlag; //!< deblocking filter override controls present in picture header @@ -2053,7 +2081,16 @@ public: void setNumAlfAps(int i) { m_numAlfAps = i; } int getNumAlfAps() const { return m_numAlfAps; } void setAlfApsIdChroma(int i) { m_alfChromaApsId = i; } - int getAlfApsIdChroma() const { return m_alfChromaApsId; } + int getAlfApsIdChroma() const { return m_alfChromaApsId; } +#if JVET_Q0795_CCALF + void setCcAlfEnabledFlag(ComponentID compId, bool b) { m_ccalfEnabledFlag[compId] = b; } + bool getCcAlfEnabledFlag(ComponentID compId) const { return m_ccalfEnabledFlag[compId]; } + + void setCcAlfCbApsId(int i) { m_ccalfCbApsId = i; } + int getCcAlfCbApsId() const { return m_ccalfCbApsId; } + void setCcAlfCrApsId(int i) { m_ccalfCrApsId = i; } + int getCcAlfCrApsId() const { return m_ccalfCrApsId; } +#endif void setDepQuantEnabledFlag( bool b ) { m_depQuantEnabledFlag = b; } bool getDepQuantEnabledFlag() const { return m_depQuantEnabledFlag; } void setSignDataHidingEnabledFlag( bool b ) { m_signDataHidingEnabledFlag = b; } @@ -2225,6 +2262,12 @@ private: int m_tileGroupNumAps; std::vector<int> m_tileGroupLumaApsId; int m_tileGroupChromaApsId; +#if JVET_Q0795_CCALF + bool m_tileGroupCcAlfCbEnabledFlag; + bool m_tileGroupCcAlfCrEnabledFlag; + int m_tileGroupCcAlfCbApsId; + int m_tileGroupCcAlfCrApsId; +#endif bool m_disableSATDForRd; public: Slice(); @@ -2468,6 +2511,20 @@ public: m_tileGroupLumaApsId[i] = ApsIDs[i]; } } +#if JVET_Q0795_CCALF + void resetTileGroupCcAlCbfEnabledFlag() { m_tileGroupCcAlfCbEnabledFlag = 0; } + void resetTileGroupCcAlCrfEnabledFlag() { m_tileGroupCcAlfCrEnabledFlag = 0; } + + void setTileGroupCcAlfCbEnabledFlag(bool b) { m_tileGroupCcAlfCbEnabledFlag = b; } + void setTileGroupCcAlfCrEnabledFlag(bool b) { m_tileGroupCcAlfCrEnabledFlag = b; } + void setTileGroupCcAlfCbApsId(int i) { m_tileGroupCcAlfCbApsId = i; } + void setTileGroupCcAlfCrApsId(int i) { m_tileGroupCcAlfCrApsId = i; } + + bool getTileGroupCcAlfCbEnabledFlag() { return m_tileGroupCcAlfCbEnabledFlag; } + bool getTileGroupCcAlfCrEnabledFlag() { return m_tileGroupCcAlfCrEnabledFlag; } + int getTileGroupCcAlfCbApsId() { return m_tileGroupCcAlfCbApsId; } + int getTileGroupCcAlfCrApsId() { return m_tileGroupCcAlfCrApsId; } +#endif void setDisableSATDForRD(bool b) { m_disableSATDForRd = b; } bool getDisableSATDForRD() { return m_disableSATDForRd; } void scaleRefPicList( Picture *scaledRefPic[ ], PicHeader *picHeader, APS** apss, APS* lmcsAps, APS* scalingListAps, const bool isDecoder ); @@ -2477,6 +2534,11 @@ public: void setNumEntryPoints( const PPS *pps ); uint32_t getNumEntryPoints( ) const { return m_numEntryPoints; } +#if JVET_Q0795_CCALF + CcAlfFilterParam m_ccAlfFilterParam; + uint8_t* m_ccAlfFilterControl[2]; +#endif + protected: Picture* xGetRefPic( PicList& rcListPic, int poc, const int layerId ); Picture* xGetLongTermRefPic( PicList& rcListPic, int poc, bool pocHasMsb, const int layerId ); diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index fbcf3e131..8f7d179c6 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -74,6 +74,7 @@ #define JVET_Q0249_ALF_CHROMA_CLIPFLAG 1 // JVET-Q0249: Cleanup of chroma clipping flags for ALF #define JVET_Q0150 1 // fix for ALF virtual horizontal CTU boundary processing #define JVET_Q0054 1 // fix for long luma deblocking decision +#define JVET_Q0795_CCALF 1 // Cross-component ALF #define JVET_Q0483_CLIP_TMVP 1 // JVET-Q0483: Clip TMVP when no scaling is applied diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index 2b6e06881..b8f20a7de 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -194,6 +194,25 @@ void CABACReader::coding_tree_unit( CodingStructure& cs, const UnitArea& area, i } } } +#if JVET_Q0795_CCALF + if (cs.sps->getCCALFEnabledFlag()) + { + for ( int compIdx = 1; compIdx < getNumberValidComponents( cs.pcv->chrFormat ); compIdx++ ) + { + if (cs.slice->m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1]) + { + const int filterCount = cs.slice->m_ccAlfFilterParam.ccAlfFilterCount[compIdx - 1]; + + const int ry = ctuRsAddr / cs.pcv->widthInCtus; + const int rx = ctuRsAddr % cs.pcv->widthInCtus; + const Position lumaPos(rx * cs.pcv->maxCUWidth, ry * cs.pcv->maxCUHeight); + + ccAlfFilterControlIdc(cs, ComponentID(compIdx), ctuRsAddr, cs.slice->m_ccAlfFilterControl[compIdx - 1], lumaPos, + filterCount); + } + } + } +#endif if ( CS::isDualITree(cs) && cs.pcv->chrFormat != CHROMA_400 && cs.pcv->maxCUWidth > 64 ) @@ -251,6 +270,45 @@ void CABACReader::readAlfCtuFilterIndex(CodingStructure& cs, unsigned ctuRsAddr) } alfCtbFilterSetIndex[ctuRsAddr] = filtIndex; } +#if JVET_Q0795_CCALF +void CABACReader::ccAlfFilterControlIdc(CodingStructure &cs, const ComponentID compID, const int curIdx, + uint8_t *filterControlIdc, Position lumaPos, int filterCount) +{ + RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__CROSS_COMPONENT_ALF_BLOCK_LEVEL_IDC ); + + Position leftLumaPos = lumaPos.offset(-(int)cs.pcv->maxCUWidth, 0); + Position aboveLumaPos = lumaPos.offset(0, -(int)cs.pcv->maxCUWidth); + const uint32_t curSliceIdx = cs.slice->getIndependentSliceIdx(); + const uint32_t curTileIdx = cs.pps->getTileIdx( lumaPos ); + bool leftAvail = cs.getCURestricted( leftLumaPos, lumaPos, curSliceIdx, curTileIdx, CH_L ) ? true : false; + bool aboveAvail = cs.getCURestricted( aboveLumaPos, lumaPos, curSliceIdx, curTileIdx, CH_L ) ? true : false; + int ctxt = 0; + + if (leftAvail) + { + ctxt += ( filterControlIdc[curIdx - 1] ) ? 1 : 0; + } + if (aboveAvail) + { + ctxt += ( filterControlIdc[curIdx - cs.pcv->widthInCtus] ) ? 1 : 0; + } + ctxt += ( compID == COMPONENT_Cr ) ? 3 : 0; + + int idcVal = m_BinDecoder.decodeBin( Ctx::CcAlfFilterControlFlag( ctxt ) ); + if ( idcVal ) + { + while ( ( idcVal != filterCount ) && m_BinDecoder.decodeBinEP() ) + { + idcVal++; + } + } + filterControlIdc[curIdx] = idcVal; + + DTRACE(g_trace_ctx, D_SYNTAX, "ccAlfFilterControlIdc() compID=%d pos=(%d,%d) ctxt=%d, filterCount=%d, idcVal=%d\n", + compID, lumaPos.x, lumaPos.y, ctxt, filterCount, idcVal); +} +#endif + //================================================================================ // clause 7.3.8.3 //-------------------------------------------------------------------------------- diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h index 132c50232..e1b33238b 100644 --- a/source/Lib/DecoderLib/CABACReader.h +++ b/source/Lib/DecoderLib/CABACReader.h @@ -70,6 +70,11 @@ public: void readAlfCtuFilterIndex(CodingStructure& cs, unsigned ctuRsAddr); +#if JVET_Q0795_CCALF + void ccAlfFilterControlIdc(CodingStructure &cs, const ComponentID compID, const int curIdx, uint8_t *filterControlIdc, + Position lumaPos, int filterCount); +#endif + // coding (quad)tree (clause 7.3.8.4) void coding_tree ( CodingStructure& cs, Partitioner& pm, CUCtx& cuCtx, Partitioner* pPartitionerChroma = nullptr, CUCtx* pCuCtxChroma = nullptr); PartSplit split_cu_mode ( CodingStructure& cs, Partitioner& pm ); diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp index 6b62da634..322ddce36 100644 --- a/source/Lib/DecoderLib/DecLib.cpp +++ b/source/Lib/DecoderLib/DecLib.cpp @@ -222,6 +222,12 @@ bool tryDecodePicture( Picture* pcEncPic, const int expectedPoc, const std::stri pcEncPic->slices[i]->setTileGroupAlfEnabledFlag(COMPONENT_Y, pic->slices[i]->getTileGroupAlfEnabledFlag(COMPONENT_Y)); pcEncPic->slices[i]->setTileGroupAlfEnabledFlag(COMPONENT_Cb, pic->slices[i]->getTileGroupAlfEnabledFlag(COMPONENT_Cb)); pcEncPic->slices[i]->setTileGroupAlfEnabledFlag(COMPONENT_Cr, pic->slices[i]->getTileGroupAlfEnabledFlag(COMPONENT_Cr)); +#if JVET_Q0795_CCALF + pcEncPic->slices[i]->setTileGroupCcAlfCbApsId(pic->slices[i]->getTileGroupCcAlfCbApsId()); + pcEncPic->slices[i]->setTileGroupCcAlfCbEnabledFlag(pic->slices[i]->getTileGroupCcAlfCbEnabledFlag()); + pcEncPic->slices[i]->setTileGroupCcAlfCrApsId(pic->slices[i]->getTileGroupCcAlfCrApsId()); + pcEncPic->slices[i]->setTileGroupCcAlfCrEnabledFlag(pic->slices[i]->getTileGroupCcAlfCrEnabledFlag()); +#endif } } @@ -579,11 +585,13 @@ void DecLib::executeLoopFilters() if( cs.sps->getALFEnabledFlag() ) { - // ALF decodes the differentially coded coefficients and stores them in the parameters structure. - // Code could be restructured to do directly after parsing. So far we just pass a fresh non-const - // copy in case the APS gets used more than once. - m_cALF.ALFProcess(cs); - +#if JVET_Q0795_CCALF + m_cALF.getCcAlfFilterParam() = cs.slice->m_ccAlfFilterParam; +#endif + // ALF decodes the differentially coded coefficients and stores them in the parameters structure. + // Code could be restructured to do directly after parsing. So far we just pass a fresh non-const + // copy in case the APS gets used more than once. + m_cALF.ALFProcess(cs); } #if SUBPIC_DECCHECK @@ -985,6 +993,60 @@ void DecLib::xActivateParameterSets( const int layerId ) APS* scalinglistAPS = nullptr; activateAPS(&m_picHeader, m_apcSlicePilot, m_parameterSetManager, apss, lmcsAPS, scalinglistAPS); +#if JVET_Q0795_CCALF + CcAlfFilterParam &filterParam = m_apcSlicePilot->m_ccAlfFilterParam; + + // CC ALF Cb APS + int apsCcAlfCbId = m_apcSlicePilot->getTileGroupCcAlfCbApsId(); + APS *apsCcAlfCb = m_parameterSetManager.getAPS(apsCcAlfCbId, ALF_APS); + if (apsCcAlfCb) + { + m_parameterSetManager.clearAPSChangedFlag(apsCcAlfCbId, ALF_APS); + apss[apsCcAlfCbId] = apsCcAlfCb; + if (false == m_parameterSetManager.activateAPS(apsCcAlfCbId, ALF_APS)) + { + THROW("APS activation failed!"); + } + // cleanup before copying + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + memset( filterParam.ccAlfCoeff[COMPONENT_Cb - 1][filterIdx], 0, sizeof(filterParam.ccAlfCoeff[COMPONENT_Cb - 1][filterIdx]) ); + } + memset( filterParam.ccAlfFilterIdxEnabled[COMPONENT_Cb - 1], false, sizeof(filterParam.ccAlfFilterIdxEnabled[COMPONENT_Cb - 1]) ); + filterParam.ccAlfFilterCount[COMPONENT_Cb - 1] = apsCcAlfCb->getCcAlfAPSParam().ccAlfFilterCount[COMPONENT_Cb - 1]; + for (int filterIdx=0; filterIdx < filterParam.ccAlfFilterCount[COMPONENT_Cb - 1]; filterIdx++ ) + { + filterParam.ccAlfFilterIdxEnabled[COMPONENT_Cb - 1][filterIdx] = apsCcAlfCb->getCcAlfAPSParam().ccAlfFilterIdxEnabled[COMPONENT_Cb - 1][filterIdx]; + memcpy(filterParam.ccAlfCoeff[COMPONENT_Cb - 1][filterIdx], apsCcAlfCb->getCcAlfAPSParam().ccAlfCoeff[COMPONENT_Cb - 1][filterIdx], sizeof(apsCcAlfCb->getCcAlfAPSParam().ccAlfCoeff[COMPONENT_Cb - 1][filterIdx])); + } + } + + // CC ALF Cr APS + int apsCcAlfCrId = m_apcSlicePilot->getTileGroupCcAlfCrApsId(); + APS *apsCcAlfCr = m_parameterSetManager.getAPS(apsCcAlfCrId, ALF_APS); + if (apsCcAlfCr) + { + m_parameterSetManager.clearAPSChangedFlag(apsCcAlfCrId, ALF_APS); + apss[apsCcAlfCrId] = apsCcAlfCr; + if (false == m_parameterSetManager.activateAPS(apsCcAlfCrId, ALF_APS)) + { + THROW("APS activation failed!"); + } + // cleanup before copying + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + memset( filterParam.ccAlfCoeff[COMPONENT_Cr - 1][filterIdx], 0, sizeof(filterParam.ccAlfCoeff[COMPONENT_Cr - 1][filterIdx]) ); + } + memset( filterParam.ccAlfFilterIdxEnabled[COMPONENT_Cr - 1], false, sizeof(filterParam.ccAlfFilterIdxEnabled[COMPONENT_Cr - 1]) ); + filterParam.ccAlfFilterCount[COMPONENT_Cr - 1] = apsCcAlfCr->getCcAlfAPSParam().ccAlfFilterCount[COMPONENT_Cr - 1]; + for (int filterIdx=0; filterIdx < filterParam.ccAlfFilterCount[COMPONENT_Cr - 1]; filterIdx++ ) + { + filterParam.ccAlfFilterIdxEnabled[COMPONENT_Cr - 1][filterIdx] = apsCcAlfCr->getCcAlfAPSParam().ccAlfFilterIdxEnabled[COMPONENT_Cr - 1][filterIdx]; + memcpy(filterParam.ccAlfCoeff[COMPONENT_Cr - 1][filterIdx], apsCcAlfCr->getCcAlfAPSParam().ccAlfCoeff[COMPONENT_Cr - 1][filterIdx], sizeof(apsCcAlfCr->getCcAlfAPSParam().ccAlfCoeff[COMPONENT_Cr - 1][filterIdx])); + } + } +#endif + xParsePrefixSEImessages(); #if RExt__HIGH_BIT_DEPTH_SUPPORT==0 @@ -1076,6 +1138,10 @@ void DecLib::xActivateParameterSets( const int layerId ) { m_cALF.create( pps->getPicWidthInLumaSamples(), pps->getPicHeightInLumaSamples(), sps->getChromaFormatIdc(), sps->getMaxCUWidth(), sps->getMaxCUHeight(), sps->getMaxCodingDepth(), sps->getBitDepths().recon ); } +#if JVET_Q0795_CCALF + pSlice->m_ccAlfFilterControl[0] = m_cALF.getCcAlfControlIdc(COMPONENT_Cb); + pSlice->m_ccAlfFilterControl[1] = m_cALF.getCcAlfControlIdc(COMPONENT_Cr); +#endif } else { @@ -1249,6 +1315,9 @@ bool DecLib::xDecodeSlice(InputNALUnit &nalu, int &iSkipFrame, int iPOCLastDispl CHECK(nalu.m_temporalId != 0, "Current GDR picture has TemporalId not equal to 0"); m_HLSReader.setBitstream( &nalu.getBitstream() ); +#if JVET_Q0795_CCALF + m_apcSlicePilot->m_ccAlfFilterParam = m_cALF.getCcAlfFilterParam(); +#endif m_HLSReader.parseSliceHeader( m_apcSlicePilot, &m_picHeader, &m_parameterSetManager, m_prevTid0POC ); // update independent slice index diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp index fd516bb03..7766f6052 100644 --- a/source/Lib/DecoderLib/VLCReader.cpp +++ b/source/Lib/DecoderLib/VLCReader.cpp @@ -825,8 +825,17 @@ void HLSyntaxReader::parseAlfAps( APS* aps ) READ_FLAG(code, "alf_chroma_new_filter"); param.newFilterFlag[CHANNEL_TYPE_CHROMA] = code; - CHECK(param.newFilterFlag[CHANNEL_TYPE_LUMA] == 0 && param.newFilterFlag[CHANNEL_TYPE_CHROMA] == 0, - "bitstream conformance error, alf_luma_filter_signal_flag and alf_chroma_filter_signal_flag shall not equal to zero at the same time"); +#if JVET_Q0795_CCALF + CcAlfFilterParam ccAlfParam = aps->getCcAlfAPSParam(); + READ_FLAG(code, "alf_cc_cb_filter_signal_flag"); + ccAlfParam.newCcAlfFilter[COMPONENT_Cb - 1] = code; + READ_FLAG(code, "alf_cc_cr_filter_signal_flag"); + ccAlfParam.newCcAlfFilter[COMPONENT_Cr - 1] = code; + CHECK(param.newFilterFlag[CHANNEL_TYPE_LUMA] == 0 && param.newFilterFlag[CHANNEL_TYPE_CHROMA] == 0 + && ccAlfParam.newCcAlfFilter[COMPONENT_Cb - 1] == 0 && ccAlfParam.newCcAlfFilter[COMPONENT_Cr - 1] == 0, + "bitstream conformance error: one of alf_luma_filter_signal_flag, alf_chroma_filter_signal_flag, " + "alf_cross_component_cb_filter_signal_flag, and alf_cross_component_cr_filter_signal_flag shall be nonzero"); +#endif if (param.newFilterFlag[CHANNEL_TYPE_LUMA]) { @@ -876,6 +885,62 @@ void HLSyntaxReader::parseAlfAps( APS* aps ) alfFilter( param, true, altIdx ); } } + +#if JVET_Q0795_CCALF + for (int ccIdx = 0; ccIdx < 2; ccIdx++) + { + if (ccAlfParam.newCcAlfFilter[ccIdx]) + { + if (MAX_NUM_CC_ALF_FILTERS > 1) + { + READ_UVLC(code, ccIdx == 0 ? "alf_cc_cb_filters_signalled_minus1" : "alf_cc_cr_filters_signalled_minus1"); + } + else + { + code = 0; + } + ccAlfParam.ccAlfFilterCount[ccIdx] = code + 1; + + for (int filterIdx = 0; filterIdx < ccAlfParam.ccAlfFilterCount[ccIdx]; filterIdx++) + { + ccAlfParam.ccAlfFilterIdxEnabled[ccIdx][filterIdx] = true; + AlfFilterShape alfShape(size_CC_ALF); + + short *coeff = ccAlfParam.ccAlfCoeff[ccIdx][filterIdx]; + // Filter coefficients + for (int i = 0; i < alfShape.numCoeff - 1; i++) + { + READ_CODE(CCALF_BITS_PER_COEFF_LEVEL, code, + ccIdx == 0 ? "alf_cc_cb_mapped_coeff_abs" : "alf_cc_cr_mapped_coeff_abs"); + if (code == 0) + { + coeff[i] = 0; + } + else + { + coeff[i] = 1 << (code - 1); + READ_FLAG(code, ccIdx == 0 ? "alf_cc_cb_coeff_sign" : "alf_cc_cr_coeff_sign"); + coeff[i] *= 1 - 2 * code; + } + } + + DTRACE(g_trace_ctx, D_SYNTAX, "%s coeff filterIdx %d: ", ccIdx == 0 ? "Cb" : "Cr", filterIdx); + for (int i = 0; i < alfShape.numCoeff; i++) + { + DTRACE(g_trace_ctx, D_SYNTAX, "%d ", coeff[i]); + } + DTRACE(g_trace_ctx, D_SYNTAX, "\n"); + } + + for (int filterIdx = ccAlfParam.ccAlfFilterCount[ccIdx]; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++) + { + ccAlfParam.ccAlfFilterIdxEnabled[ccIdx][filterIdx] = false; + } + } + } + aps->setCcAlfAPSParam(ccAlfParam); +#endif + aps->setAlfAPSParam(param); } @@ -1334,6 +1399,16 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS) READ_FLAG( uiCode, "sps_sao_enabled_flag" ); pcSPS->setSAOEnabledFlag ( uiCode ? true : false ); READ_FLAG( uiCode, "sps_alf_enabled_flag" ); pcSPS->setALFEnabledFlag ( uiCode ? true : false ); +#if JVET_Q0795_CCALF + if (pcSPS->getALFEnabledFlag() && pcSPS->getChromaFormatIdc() != CHROMA_400) + { + READ_FLAG( uiCode, "sps_ccalf_enabled_flag" ); pcSPS->setCCALFEnabledFlag ( uiCode ? true : false ); + } + else + { + pcSPS->setCCALFEnabledFlag(false); + } +#endif READ_FLAG(uiCode, "sps_transform_skip_enabled_flag"); pcSPS->setTransformSkipEnabledFlag(uiCode ? true : false); if (pcSPS->getTransformSkipEnabledFlag()) @@ -2211,6 +2286,10 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag } // alf enable flags and aps IDs +#if JVET_Q0795_CCALF + picHeader->setCcAlfEnabledFlag(COMPONENT_Cb, false); + picHeader->setCcAlfEnabledFlag(COMPONENT_Cr, false); +#endif if( sps->getALFEnabledFlag() ) { READ_FLAG(uiCode, "pic_alf_enabled_present_flag"); @@ -2250,6 +2329,30 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag READ_CODE(3, uiCode, "pic_alf_aps_id_chroma"); picHeader->setAlfApsIdChroma(uiCode); } +#if JVET_Q0795_CCALF + if (sps->getCCALFEnabledFlag()) + { + READ_FLAG(uiCode, "ph_cc_alf_cb_enabled_flag"); + picHeader->setCcAlfEnabledFlag(COMPONENT_Cb, uiCode != 0); + picHeader->setCcAlfCbApsId(-1); + if (picHeader->getCcAlfEnabledFlag(COMPONENT_Cb)) + { + // parse APS ID + READ_CODE(3, uiCode, "ph_cc_alf_cb_aps_id"); + picHeader->setCcAlfCbApsId(uiCode); + } + // Cr + READ_FLAG(uiCode, "ph_cc_alf_cr_enabled_flag"); + picHeader->setCcAlfEnabledFlag(COMPONENT_Cr, uiCode != 0); + picHeader->setCcAlfCrApsId(-1); + if (picHeader->getCcAlfEnabledFlag(COMPONENT_Cr)) + { + // parse APS ID + READ_CODE(3, uiCode, "ph_cc_alf_cr_aps_id"); + picHeader->setCcAlfCrApsId(uiCode); + } + } +#endif } else { @@ -2951,6 +3054,39 @@ void HLSyntaxReader::parseSliceHeader (Slice* pcSlice, PicHeader* picHeader, Par } pcSlice->setTileGroupAlfEnabledFlag(COMPONENT_Cb, alfChromaIdc & 1); pcSlice->setTileGroupAlfEnabledFlag(COMPONENT_Cr, alfChromaIdc >> 1); + +#if JVET_Q0795_CCALF + CcAlfFilterParam &filterParam = pcSlice->m_ccAlfFilterParam; + if (sps->getCCALFEnabledFlag() && pcSlice->getTileGroupAlfEnabledFlag(COMPONENT_Y)) + { + READ_FLAG(uiCode, "slice_cc_alf_cb_enabled_flag"); + filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1] = (uiCode == 1) ? true : false; + pcSlice->setTileGroupCcAlfCbApsId(-1); + if (filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1]) + { + // parse APS ID + READ_CODE(3, uiCode, "slice_cc_alf_cb_aps_id"); + pcSlice->setTileGroupCcAlfCbApsId(uiCode); + } + // Cr + READ_FLAG(uiCode, "slice_cc_alf_cr_enabled_flag"); + filterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1] = (uiCode == 1) ? true : false; + pcSlice->setTileGroupCcAlfCrApsId(-1); + if (filterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1]) + { + // parse APS ID + READ_CODE(3, uiCode, "slice_cc_alf_cr_aps_id"); + pcSlice->setTileGroupCcAlfCrApsId(uiCode); + } + } + else + { + filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1] = false; + filterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1] = false; + pcSlice->setTileGroupCcAlfCbApsId(-1); + pcSlice->setTileGroupCcAlfCrApsId(-1); + } +#endif } @@ -3130,6 +3266,9 @@ void HLSyntaxReader::parseConstraintInfo(ConstraintInfo *cinfo) READ_FLAG(symbol, "no_partition_constraints_override_constraint_flag"); cinfo->setNoPartitionConstraintsOverrideConstraintFlag(symbol > 0 ? true : false); READ_FLAG(symbol, "no_sao_constraint_flag"); cinfo->setNoSaoConstraintFlag(symbol > 0 ? true : false); READ_FLAG(symbol, "no_alf_constraint_flag"); cinfo->setNoAlfConstraintFlag(symbol > 0 ? true : false); +#if JVET_Q0795_CCALF + READ_FLAG(symbol, "no_ccalf_constraint_flag"); cinfo->setNoCCAlfConstraintFlag(symbol > 0 ? true : false); +#endif READ_FLAG(symbol, "no_joint_cbcr_constraint_flag"); cinfo->setNoJointCbCrConstraintFlag(symbol > 0 ? true : false); READ_FLAG(symbol, "no_ref_wraparound_constraint_flag"); cinfo->setNoRefWraparoundConstraintFlag(symbol > 0 ? true : false); diff --git a/source/Lib/DecoderLib/VLCReader.h b/source/Lib/DecoderLib/VLCReader.h index 69e3f479c..67eaee7ed 100644 --- a/source/Lib/DecoderLib/VLCReader.h +++ b/source/Lib/DecoderLib/VLCReader.h @@ -178,6 +178,9 @@ public: void decodeScalingList ( ScalingList *scalingList, uint32_t scalingListId, bool isPredictor); void parseReshaper ( SliceReshapeInfo& sliceReshaperInfo, const SPS* pcSPS, const bool isIntra ); void alfFilter( AlfParam& alfParam, const bool isChroma, const int altIdx ); +#if JVET_Q0795_CCALF + void ccAlfFilter( Slice *pcSlice ); +#endif private: int alfGolombDecode( const int k, const bool signed_val=true ); diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp index c814c521a..4044ba80e 100644 --- a/source/Lib/EncoderLib/CABACWriter.cpp +++ b/source/Lib/EncoderLib/CABACWriter.cpp @@ -187,6 +187,26 @@ void CABACWriter::coding_tree_unit( CodingStructure& cs, const UnitArea& area, i } } +#if JVET_Q0795_CCALF + if ( !skipAlf ) + { + for ( int compIdx = 1; compIdx < getNumberValidComponents( cs.pcv->chrFormat ); compIdx++ ) + { + if (cs.slice->m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1]) + { + const int filterCount = cs.slice->m_ccAlfFilterParam.ccAlfFilterCount[compIdx - 1]; + + const int ry = ctuRsAddr / cs.pcv->widthInCtus; + const int rx = ctuRsAddr % cs.pcv->widthInCtus; + const Position lumaPos(rx * cs.pcv->maxCUWidth, ry * cs.pcv->maxCUHeight); + + codeCcAlfFilterControlIdc(cs.slice->m_ccAlfFilterControl[compIdx - 1][ctuRsAddr], cs, ComponentID(compIdx), + ctuRsAddr, cs.slice->m_ccAlfFilterControl[compIdx - 1], lumaPos, filterCount); + } + } + } +#endif + if ( CS::isDualITree(cs) && cs.pcv->chrFormat != CHROMA_400 && cs.pcv->maxCUWidth > 64 ) { CUCtx chromaCuCtx(qps[CH_C]); @@ -3351,6 +3371,49 @@ void CABACWriter::codeAlfCtuEnableFlag( CodingStructure& cs, uint32_t ctuRsAddr, } } +#if JVET_Q0795_CCALF +void CABACWriter::codeCcAlfFilterControlIdc(uint8_t idcVal, CodingStructure &cs, const ComponentID compID, + const int curIdx, const uint8_t *filterControlIdc, Position lumaPos, + const int filterCount) +{ + CHECK(idcVal > filterCount, "Filter index is too large"); + + const uint32_t curSliceIdx = cs.slice->getIndependentSliceIdx(); + const uint32_t curTileIdx = cs.pps->getTileIdx( lumaPos ); + Position leftLumaPos = lumaPos.offset(-(int)cs.pcv->maxCUWidth, 0); + Position aboveLumaPos = lumaPos.offset(0, -(int)cs.pcv->maxCUWidth); + bool leftAvail = cs.getCURestricted( leftLumaPos, lumaPos, curSliceIdx, curTileIdx, CH_L ) ? true : false; + bool aboveAvail = cs.getCURestricted( aboveLumaPos, lumaPos, curSliceIdx, curTileIdx, CH_L ) ? true : false; + int ctxt = 0; + + if (leftAvail) + { + ctxt += ( filterControlIdc[curIdx - 1]) ? 1 : 0; + } + if (aboveAvail) + { + ctxt += (filterControlIdc[curIdx - cs.pcv->widthInCtus]) ? 1 : 0; + } + ctxt += ( compID == COMPONENT_Cr ) ? 3 : 0; + + m_BinEncoder.encodeBin( ( idcVal == 0 ) ? 0 : 1, Ctx::CcAlfFilterControlFlag( ctxt ) ); // ON/OFF flag is context coded + if ( idcVal > 0 ) + { + int val = (idcVal - 1); + while ( val ) + { + m_BinEncoder.encodeBinEP( 1 ); + val--; + } + if ( idcVal < filterCount ) + { + m_BinEncoder.encodeBinEP( 0 ); + } + } + DTRACE( g_trace_ctx, D_SYNTAX, "ccAlfFilterControlIdc() compID=%d pos=(%d,%d) ctxt=%d, filterCount=%d, idcVal=%d\n", compID, lumaPos.x, lumaPos.y, ctxt, filterCount, idcVal ); +} +#endif + void CABACWriter::code_unary_fixed( unsigned symbol, unsigned ctxId, unsigned unary_max, unsigned fixed ) { bool unary = (symbol <= unary_max); diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h index 940fa4c22..8f2124456 100644 --- a/source/Lib/EncoderLib/CABACWriter.h +++ b/source/Lib/EncoderLib/CABACWriter.h @@ -164,6 +164,10 @@ public: void codeAlfCtuAlternatives ( CodingStructure& cs, ChannelType channel, AlfParam* alfParam); void codeAlfCtuAlternatives ( CodingStructure& cs, ComponentID compID, AlfParam* alfParam); void codeAlfCtuAlternative ( CodingStructure& cs, uint32_t ctuRsAddr, const int compIdx, const AlfParam* alfParam = NULL ); +#if JVET_Q0795_CCALF + void codeCcAlfFilterControlIdc(uint8_t idcVal, CodingStructure &cs, const ComponentID compID, const int curIdx, + const uint8_t *filterControlIdc, Position lumaPos, const int filterCount); +#endif private: void unary_max_symbol ( unsigned symbol, unsigned ctxId0, unsigned ctxIdN, unsigned maxSymbol ); diff --git a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp index 6de3317a9..252cea9d5 100644 --- a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp +++ b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp @@ -42,6 +42,20 @@ #define AlfCtx(c) SubCtx( Ctx::Alf, c) std::vector<double> EncAdaptiveLoopFilter::m_lumaLevelToWeightPLUT; +#if JVET_Q0795_CCALF +#include <algorithm> + +#if MAX_NUM_CC_ALF_FILTERS>1 +struct FilterIdxCount +{ + uint64_t count; + uint8_t filterIdx; +}; + +bool compareCounts(FilterIdxCount a, FilterIdxCount b) { return a.count > b.count; } +#endif +#endif + void AlfCovariance::getClipMax(const AlfFilterShape& alfShape, int *clip_max) const { for( int k = 0; k < numCoeff-1; ++k ) @@ -241,6 +255,27 @@ double AlfCovariance::calcErrorForCoeffs( const int *clip, const int *coeff, con return error / factor; } +#if JVET_Q0795_CCALF +double AlfCovariance::calcErrorForCcAlfCoeffs(const int* coeff, const int numCoeff, const int bitDepth) const +{ + double factor = 1 << (bitDepth - 1); + double error = 0; + + for (int i = 0; i < numCoeff; i++) // diagonal + { + double sum = 0; + for (int j = i + 1; j < numCoeff; j++) + { + // E[j][i] = E[i][j], sum will be multiplied by 2 later + sum += E[0][0][i][j] * coeff[j]; + } + error += ((E[0][0][i][i] * coeff[i] + sum * 2) / factor - 2 * y[0][i]) * coeff[i]; + } + + return error / factor; +} +#endif + double AlfCovariance::calculateError( const int *clip, const double *coeff, const int numCoeff ) const { double sum = 0; @@ -417,6 +452,13 @@ EncAdaptiveLoopFilter::EncAdaptiveLoopFilter( int& apsIdStart ) m_diffFilterCoeff = nullptr; m_alfWSSD = 0; + +#if JVET_Q0795_CCALF + m_alfCovarianceCcAlf[0] = nullptr; + m_alfCovarianceCcAlf[1] = nullptr; + m_alfCovarianceFrameCcAlf[0] = nullptr; + m_alfCovarianceFrameCcAlf[1] = nullptr; +#endif } void EncAdaptiveLoopFilter::create( const EncCfg* encCfg, const int picWidth, const int picHeight, const ChromaFormat chromaFormatIDC, const int maxCUWidth, const int maxCUHeight, const int maxCUDepth, const int inputBitDepth[MAX_NUM_CHANNEL_TYPE], const int internalBitDepth[MAX_NUM_CHANNEL_TYPE] ) @@ -499,6 +541,51 @@ void EncAdaptiveLoopFilter::create( const EncCfg* encCfg, const int picWidth, co } m_alfCtbFilterSetIndexTmp.resize(m_numCTUsInPic); memset(m_clipDefaultEnc, 0, sizeof(m_clipDefaultEnc)); +#if JVET_Q0795_CCALF + m_apsIdCcAlfStart[0] = (int) MAX_NUM_APS; + m_apsIdCcAlfStart[1] = (int) MAX_NUM_APS; + for( int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + int numFilters = MAX_NUM_CC_ALF_FILTERS; + m_alfCovarianceCcAlf[compIdx-1] = new AlfCovariance**[m_filterShapesCcAlf[compIdx-1].size()]; + m_alfCovarianceFrameCcAlf[compIdx-1] = new AlfCovariance*[m_filterShapesCcAlf[compIdx-1].size()]; + for( int i = 0; i != m_filterShapesCcAlf[compIdx-1].size(); i++ ) + { + m_alfCovarianceFrameCcAlf[compIdx - 1][i] = new AlfCovariance[numFilters]; + for (int k = 0; k < numFilters; k++) + { + m_alfCovarianceFrameCcAlf[compIdx - 1][i][k].create(m_filterShapesCcAlf[compIdx - 1][i].numCoeff); + } + + m_alfCovarianceCcAlf[compIdx - 1][i] = new AlfCovariance *[numFilters]; + for (int j = 0; j < numFilters; j++) + { + m_alfCovarianceCcAlf[compIdx - 1][i][j] = new AlfCovariance[m_numCTUsInPic]; + for (int k = 0; k < m_numCTUsInPic; k++) + { + m_alfCovarianceCcAlf[compIdx - 1][i][j][k].create(m_filterShapesCcAlf[compIdx - 1][i].numCoeff); + } + } + } + } + m_trainingCovControl = new uint8_t[m_numCTUsInPic]; + m_unfilteredDistortion = new uint64_t *[1]; + for (int i = 0; i < 1; i++) + { + m_unfilteredDistortion[i] = new uint64_t[m_numCTUsInPic]; + } + for ( int i = 0; i < MAX_NUM_CC_ALF_FILTERS; i++ ) + { + m_trainingDistortion[i] = new uint64_t[m_numCTUsInPic]; + } + m_filterControl = new uint8_t[m_numCTUsInPic]; + m_bestFilterControl = new uint8_t[m_numCTUsInPic]; + uint32_t area = (picWidth >> getComponentScaleX(COMPONENT_Cb,chromaFormatIDC))*(picHeight >> getComponentScaleY(COMPONENT_Cb,chromaFormatIDC)); + m_bufOrigin = ( Pel* ) xMalloc( Pel, area ); + m_buf = new PelBuf( m_bufOrigin, picWidth >> getComponentScaleX(COMPONENT_Cb,chromaFormatIDC), picWidth >> getComponentScaleX(COMPONENT_Cb,chromaFormatIDC), picHeight >> getComponentScaleY(COMPONENT_Cb,chromaFormatIDC) ); + m_lumaSwingGreaterThanThresholdCount = new uint64_t[m_numCTUsInPic]; + m_chromaSampleCountNearMidPoint = new uint64_t[m_numCTUsInPic]; +#endif } void EncAdaptiveLoopFilter::destroy() @@ -622,6 +709,106 @@ void EncAdaptiveLoopFilter::destroy() delete[] m_ctbDistortionUnfilter[comp]; m_ctbDistortionUnfilter[comp] = nullptr; } + +#if JVET_Q0795_CCALF + for (int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++) + { + int numFilters = MAX_NUM_CC_ALF_FILTERS; + if (m_alfCovarianceFrameCcAlf[compIdx - 1]) + { + for (int i = 0; i != m_filterShapesCcAlf[compIdx - 1].size(); i++) + { + for (int k = 0; k < numFilters; k++) + { + m_alfCovarianceFrameCcAlf[compIdx - 1][i][k].destroy(); + } + delete[] m_alfCovarianceFrameCcAlf[compIdx - 1][i]; + } + delete[] m_alfCovarianceFrameCcAlf[compIdx - 1]; + m_alfCovarianceFrameCcAlf[compIdx - 1] = nullptr; + } + + if (m_alfCovarianceCcAlf[compIdx - 1]) + { + for (int i = 0; i != m_filterShapesCcAlf[compIdx - 1].size(); i++) + { + for (int j = 0; j < numFilters; j++) + { + for (int k = 0; k < m_numCTUsInPic; k++) + { + m_alfCovarianceCcAlf[compIdx - 1][i][j][k].destroy(); + } + delete[] m_alfCovarianceCcAlf[compIdx - 1][i][j]; + } + delete[] m_alfCovarianceCcAlf[compIdx - 1][i]; + } + delete[] m_alfCovarianceCcAlf[compIdx - 1]; + m_alfCovarianceCcAlf[compIdx - 1] = nullptr; + } + } + + if (m_trainingCovControl) + { + delete[] m_trainingCovControl; + m_trainingCovControl = nullptr; + } + + for (int i = 0; i < 1; i++) + { + if (m_unfilteredDistortion[i]) + { + delete[] m_unfilteredDistortion[i]; + m_unfilteredDistortion[i] = nullptr; + } + } + delete[] m_unfilteredDistortion; + m_unfilteredDistortion = nullptr; + + for ( int i = 0; i < MAX_NUM_CC_ALF_FILTERS; i++ ) + { + if (m_trainingDistortion[i]) + { + delete[] m_trainingDistortion[i]; + m_trainingDistortion[i] = nullptr; + } + } + + if (m_filterControl) + { + delete[] m_filterControl; + m_filterControl = nullptr; + } + + if (m_bestFilterControl) + { + delete[] m_bestFilterControl; + m_bestFilterControl = nullptr; + } + + if (m_bufOrigin) + { + xFree(m_bufOrigin); + m_bufOrigin = nullptr; + } + + if (m_buf) + { + delete m_buf; + m_buf = nullptr; + } + + if (m_lumaSwingGreaterThanThresholdCount) + { + delete[] m_lumaSwingGreaterThanThresholdCount; + m_lumaSwingGreaterThanThresholdCount = nullptr; + } + if (m_chromaSampleCountNearMidPoint) + { + delete[] m_chromaSampleCountNearMidPoint; + m_chromaSampleCountNearMidPoint = nullptr; + } +#endif + AdaptiveLoopFilter::destroy(); } void EncAdaptiveLoopFilter::initCABACEstimator( CABACEncoder* cabacEncoder, CtxCache* ctxCache, Slice* pcSlice @@ -634,6 +821,76 @@ void EncAdaptiveLoopFilter::initCABACEstimator( CABACEncoder* cabacEncoder, CtxC m_CABACEstimator->resetBits(); } +#if JVET_Q0795_CCALF +void EncAdaptiveLoopFilter::xSetupCcAlfAPS( CodingStructure &cs ) +{ + if (m_ccAlfFilterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1]) + { + int ccAlfCbApsId = cs.slice->getTileGroupCcAlfCbApsId(); + APS* aps = m_apsMap->getPS((cs.slice->getTileGroupCcAlfCbApsId() << NUM_APS_TYPE_LEN) + ALF_APS); + if (aps == NULL) + { + aps = m_apsMap->allocatePS((ccAlfCbApsId << NUM_APS_TYPE_LEN) + ALF_APS); + aps->setTemporalId(cs.slice->getTLayer()); + } + aps->getCcAlfAPSParam().ccAlfFilterEnabled[COMPONENT_Cb - 1] = 1; + aps->getCcAlfAPSParam().ccAlfFilterCount[COMPONENT_Cb - 1] = m_ccAlfFilterParam.ccAlfFilterCount[COMPONENT_Cb - 1]; + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + aps->getCcAlfAPSParam().ccAlfFilterIdxEnabled[COMPONENT_Cb - 1][filterIdx] = + m_ccAlfFilterParam.ccAlfFilterIdxEnabled[COMPONENT_Cb - 1][filterIdx]; + memcpy(aps->getCcAlfAPSParam().ccAlfCoeff[COMPONENT_Cb - 1][filterIdx], + m_ccAlfFilterParam.ccAlfCoeff[COMPONENT_Cb - 1][filterIdx], sizeof(short) * MAX_NUM_CC_ALF_CHROMA_COEFF); + } + aps->setAPSId(ccAlfCbApsId); + aps->setAPSType(ALF_APS); + if (m_reuseApsId[COMPONENT_Cb - 1] < 0) + { + aps->getCcAlfAPSParam().newCcAlfFilter[COMPONENT_Cb - 1] = 1; + m_apsMap->setChangedFlag((ccAlfCbApsId << NUM_APS_TYPE_LEN) + ALF_APS, true); + aps->setTemporalId(cs.slice->getTLayer()); + } + cs.slice->setTileGroupCcAlfCbEnabledFlag(true); + } + else + { + cs.slice->setTileGroupCcAlfCbEnabledFlag(false); + } + if (m_ccAlfFilterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1]) + { + int ccAlfCrApsId = cs.slice->getTileGroupCcAlfCrApsId(); + APS* aps = m_apsMap->getPS((cs.slice->getTileGroupCcAlfCrApsId() << NUM_APS_TYPE_LEN) + ALF_APS); + if (aps == NULL) + { + aps = m_apsMap->allocatePS((ccAlfCrApsId << NUM_APS_TYPE_LEN) + ALF_APS); + aps->setTemporalId(cs.slice->getTLayer()); + } + aps->getCcAlfAPSParam().ccAlfFilterEnabled[COMPONENT_Cr - 1] = 1; + aps->getCcAlfAPSParam().ccAlfFilterCount[COMPONENT_Cr - 1] = m_ccAlfFilterParam.ccAlfFilterCount[COMPONENT_Cr - 1]; + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + aps->getCcAlfAPSParam().ccAlfFilterIdxEnabled[COMPONENT_Cr - 1][filterIdx] = + m_ccAlfFilterParam.ccAlfFilterIdxEnabled[COMPONENT_Cr - 1][filterIdx]; + memcpy(aps->getCcAlfAPSParam().ccAlfCoeff[COMPONENT_Cr - 1][filterIdx], + m_ccAlfFilterParam.ccAlfCoeff[COMPONENT_Cr - 1][filterIdx], sizeof(short) * MAX_NUM_CC_ALF_CHROMA_COEFF); + } + aps->setAPSId(ccAlfCrApsId); + if (m_reuseApsId[COMPONENT_Cr - 1] < 0) + { + aps->getCcAlfAPSParam().newCcAlfFilter[COMPONENT_Cr - 1] = 1; + m_apsMap->setChangedFlag((ccAlfCrApsId << NUM_APS_TYPE_LEN) + ALF_APS, true); + aps->setTemporalId(cs.slice->getTLayer()); + } + aps->setAPSType(ALF_APS); + cs.slice->setTileGroupCcAlfCrEnabledFlag(true); + } + else + { + cs.slice->setTileGroupCcAlfCrEnabledFlag(false); + } +} +#endif + void EncAdaptiveLoopFilter::ALFProcess(CodingStructure& cs, const double *lambdas #if ENABLE_QPA , const double lambdaChromaWeight @@ -656,6 +913,9 @@ void EncAdaptiveLoopFilter::ALFProcess(CodingStructure& cs, const double *lambda if (alfAPS) { alfAPS->getAlfAPSParam().reset(); +#if JVET_Q0795_CCALF + alfAPS->getCcAlfAPSParam().reset(); +#endif alfAPS = nullptr; } } @@ -663,6 +923,11 @@ void EncAdaptiveLoopFilter::ALFProcess(CodingStructure& cs, const double *lambda AlfParam alfParam; alfParam.reset(); const TempCtx ctxStart(m_CtxCache, AlfCtx(m_CABACEstimator->getCtx())); + +#if JVET_Q0795_CCALF + const TempCtx ctxStartCcAlf(m_CtxCache, SubCtx(Ctx::CcAlfFilterControlFlag, m_CABACEstimator->getCtx())); +#endif + // set available filter shapes alfParam.filterShapes = m_filterShapes; @@ -796,6 +1061,55 @@ void EncAdaptiveLoopFilter::ALFProcess(CodingStructure& cs, const double *lambda ); alfReconstructor(cs, recYuv); + +#if JVET_Q0795_CCALF + // Do not transmit CC ALF if it is unchanged + if (cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Y)) + { + for (int32_t lumaAlfApsId : cs.slice->getTileGroupApsIdLuma()) + { + APS* aps = (lumaAlfApsId >= 0) ? m_apsMap->getPS((lumaAlfApsId << NUM_APS_TYPE_LEN) + ALF_APS) : nullptr; + if (aps && m_apsMap->getChangedFlag((lumaAlfApsId << NUM_APS_TYPE_LEN) + ALF_APS)) + { + aps->getCcAlfAPSParam().newCcAlfFilter[0] = false; + aps->getCcAlfAPSParam().newCcAlfFilter[1] = false; + } + } + } + int chromaAlfApsId = ( cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Cb) || cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Cr) ) ? cs.slice->getTileGroupApsIdChroma() : -1; + APS* aps = (chromaAlfApsId >= 0) ? m_apsMap->getPS((chromaAlfApsId << NUM_APS_TYPE_LEN) + ALF_APS) : nullptr; + if (aps && m_apsMap->getChangedFlag((chromaAlfApsId << NUM_APS_TYPE_LEN) + ALF_APS)) + { + aps->getCcAlfAPSParam().newCcAlfFilter[0] = false; + aps->getCcAlfAPSParam().newCcAlfFilter[1] = false; + } + + if (!cs.slice->getSPS()->getCCALFEnabledFlag()) + { + return; + } + + m_tempBuf.get(COMPONENT_Cb).copyFrom(cs.getRecoBuf().get(COMPONENT_Cb)); + m_tempBuf.get(COMPONENT_Cr).copyFrom(cs.getRecoBuf().get(COMPONENT_Cr)); + recYuv = m_tempBuf.getBuf(cs.area); + recYuv.extendBorderPel(MAX_ALF_FILTER_LENGTH >> 1); + m_CABACEstimator->getCtx() = SubCtx(Ctx::CcAlfFilterControlFlag, ctxStartCcAlf); + deriveCcAlfFilter(cs, COMPONENT_Cb, orgYuv, recYuv, cs.getRecoBuf()); + m_CABACEstimator->getCtx() = SubCtx(Ctx::CcAlfFilterControlFlag, ctxStartCcAlf); + deriveCcAlfFilter(cs, COMPONENT_Cr, orgYuv, recYuv, cs.getRecoBuf()); + + xSetupCcAlfAPS(cs); + + for (int compIdx = 1; compIdx < getNumberValidComponents(cs.pcv->chrFormat); compIdx++) + { + ComponentID compID = ComponentID(compIdx); + if (m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1]) + { + applyCcAlfFilter(cs, compID, cs.getRecoBuf().get(compID), recYuv, m_ccAlfFilterControl[compIdx - 1], + m_ccAlfFilterParam.ccAlfCoeff[compIdx - 1], -1); + } + } +#endif } double EncAdaptiveLoopFilter::deriveCtbAlfEnableFlags( CodingStructure& cs, const int iShapeIdx, ChannelType channel, @@ -1604,6 +1918,29 @@ void EncAdaptiveLoopFilter::roundFiltCoeff( int *filterCoeffQuant, double *filte } } +#if JVET_Q0795_CCALF +void EncAdaptiveLoopFilter::roundFiltCoeffCCALF( int *filterCoeffQuant, double *filterCoeff, const int numCoeff, const int factor ) +{ + for( int i = 0; i < numCoeff; i++ ) + { + int sign = filterCoeff[i] > 0 ? 1 : -1; + double best_err = 128.0*128.0; + int best_index = 0; + for(int k = 0; k < CCALF_CANDS_COEFF_NR; k++) + { + double err = (filterCoeff[i] * sign * factor - CCALF_SMALL_TAB[k]); + err = err*err; + if(err < best_err) + { + best_err = err; + best_index = k; + } + } + filterCoeffQuant[i] = CCALF_SMALL_TAB[best_index] * sign; + } +} +#endif + void EncAdaptiveLoopFilter::mergeClasses( const AlfFilterShape& alfShape, AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], const int numClasses, short filterIndices[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES] ) { static int tmpClip[MAX_NUM_ALF_LUMA_COEFF]; @@ -2538,6 +2875,11 @@ void EncAdaptiveLoopFilter::alfEncoderCtb(CodingStructure& cs, AlfParam& alfPar }// for (int numTemporalAps = 0; numTemporalAps < apsIds.size(); numTemporalAps++) }//for (int useNewFilter = 0; useNewFilter <= 1; useNewFilter++) +#if JVET_Q0795_CCALF + cs.slice->setTileGroupCcAlfCbApsId(newApsId); + cs.slice->setTileGroupCcAlfCrApsId(newApsId); +#endif + if (costOff <= costMin) { cs.slice->resetTileGroupAlfEnabledFlag(); @@ -2735,6 +3077,14 @@ void EncAdaptiveLoopFilter::alfEncoderCtb(CodingStructure& cs, AlfParam& alfPar copyCtuAlternativeChroma(m_ctuAlternativeTmp, m_ctuAlternative); } } + +#if JVET_Q0795_CCALF + if (newApsIdChroma >= 0) + { + cs.slice->setTileGroupCcAlfCbApsId(newApsIdChroma); + cs.slice->setTileGroupCcAlfCrApsId(newApsIdChroma); + } +#endif if (costOff < costMin) { cs.slice->setTileGroupAlfEnabledFlag(COMPONENT_Cb, false); @@ -2971,3 +3321,999 @@ int EncAdaptiveLoopFilter::getMaxNumAlternativesChroma( ) { return std::min<int>( m_numCTUsInPic * 2, m_encCfg->getMaxNumAlfAlternativesChroma() ); } + +#if JVET_Q0795_CCALF +int EncAdaptiveLoopFilter::getCoeffRateCcAlf(short chromaCoeff[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF], bool filterEnabled[MAX_NUM_CC_ALF_FILTERS], uint8_t filterCount, ComponentID compID) +{ + int bits = 0; + + if ( filterCount > 0 ) + { + bits += lengthUvlc(filterCount - 1); + int signaledFilterCount = 0; + for ( int filterIdx=0; filterIdx<MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + if (filterEnabled[filterIdx]) + { + AlfFilterShape alfShape(size_CC_ALF); + // Filter coefficients + for (int i = 0; i < alfShape.numCoeff - 1; i++) + { + bits += CCALF_BITS_PER_COEFF_LEVEL + (chromaCoeff[filterIdx][i] == 0 ? 0 : 1); + } + + signaledFilterCount++; + } + } + CHECK(signaledFilterCount != filterCount, "Number of filter signaled not same as indicated"); + } + + return bits; +} + +void EncAdaptiveLoopFilter::deriveCcAlfFilterCoeff( ComponentID compID, const PelUnitBuf& recYuv, const PelUnitBuf& recYuvExt, short filterCoeff[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF], const uint8_t filterIdx ) +{ + int forward_tab[CCALF_CANDS_COEFF_NR * 2 - 1] = {0}; + for (int i = 0; i < CCALF_CANDS_COEFF_NR; i++) + { + forward_tab[CCALF_CANDS_COEFF_NR - 1 + i] = CCALF_SMALL_TAB[i]; + forward_tab[CCALF_CANDS_COEFF_NR - 1 - i] = (-1) * CCALF_SMALL_TAB[i]; + } + using TE = double[MAX_NUM_ALF_LUMA_COEFF][MAX_NUM_ALF_LUMA_COEFF]; + using Ty = double[MAX_NUM_ALF_LUMA_COEFF]; + + static double filterCoeffDbl[MAX_NUM_CC_ALF_CHROMA_COEFF]; + static int filterCoeffInt[MAX_NUM_CC_ALF_CHROMA_COEFF]; + + TE kE; + Ty ky; + const int size = m_filterShapesCcAlf[compID - 1][0].numCoeff - 1; + + for (int k = 0; k < size; k++) + { + ky[k] = m_alfCovarianceFrameCcAlf[compID - 1][0][filterIdx].y[0][k]; + for (int l = 0; l < size; l++) + { + kE[k][l] = m_alfCovarianceFrameCcAlf[compID - 1][0][filterIdx].E[0][0][k][l]; + } + } + + m_alfCovarianceFrameCcAlf[compID - 1][0][filterIdx].gnsSolveByChol(kE, ky, filterCoeffDbl, size); + roundFiltCoeffCCALF(filterCoeffInt, filterCoeffDbl, size, (1 << m_scaleBits)); + + for (int k = 0; k < size; k++) + { + CHECK( filterCoeffInt[k] < -(1 << CCALF_DYNAMIC_RANGE), "this is not possible: filterCoeffInt[k] < -(1 << CCALF_DYNAMIC_RANGE)"); + CHECK( filterCoeffInt[k] > (1 << CCALF_DYNAMIC_RANGE), "this is not possible: filterCoeffInt[k] > (1 << CCALF_DYNAMIC_RANGE)"); + } + + // Refine quanitzation + int modified = 1; + double errRef = m_alfCovarianceFrameCcAlf[compID - 1][0][filterIdx].calcErrorForCcAlfCoeffs(filterCoeffInt, size, (m_scaleBits+1)); + while (modified) + { + modified = 0; + for (int delta : { 1, -1 }) + { + double errMin = MAX_DOUBLE; + int idxMin = -1; + int minIndex = -1; + + for (int k = 0; k < size; k++) + { + int org_idx = -1; + for (int i = 0; i < CCALF_CANDS_COEFF_NR * 2 - 1; i++) + { + if (forward_tab[i] == filterCoeffInt[k]) + { + org_idx = i; + break; + } + } + CHECK( org_idx < 0, "this is wrong, does not find coeff from forward_tab"); + if ( (org_idx - delta < 0) || (org_idx - delta >= CCALF_CANDS_COEFF_NR * 2 - 1) ) + continue; + + filterCoeffInt[k] = forward_tab[org_idx - delta]; + double error = m_alfCovarianceFrameCcAlf[compID - 1][0][filterIdx].calcErrorForCcAlfCoeffs(filterCoeffInt, size, (m_scaleBits+1)); + if( error < errMin ) + { + errMin = error; + idxMin = k; + minIndex = org_idx; + } + filterCoeffInt[k] = forward_tab[org_idx]; + } + if (errMin < errRef) + { + minIndex -= delta; + CHECK( minIndex < 0, "this is wrong, index - delta < 0"); + CHECK( minIndex >= CCALF_CANDS_COEFF_NR * 2 - 1, "this is wrong, index - delta >= CCALF_CANDS_COEFF_NR * 2 - 1"); + filterCoeffInt[idxMin] = forward_tab[minIndex]; + modified++; + errRef = errMin; + } + } + } + + + for (int k = 0; k < (size + 1); k++) + { + CHECK((filterCoeffInt[k] < -(1 << CCALF_DYNAMIC_RANGE)) || (filterCoeffInt[k] > (1 << CCALF_DYNAMIC_RANGE)), "Exceeded valid range for CC ALF coefficient"); + filterCoeff[filterIdx][k] = filterCoeffInt[k]; + } +} + + +void EncAdaptiveLoopFilter::computeLog2BlockSizeDistortion(const Pel *org, int orgStride, const Pel *dec, int decStride, + int height, int width, uint64_t *distortionBuf, + int distortionBufStride, int log2BlockWidth, + int log2BlockHeight, uint64_t& totalDistortion) +{ + totalDistortion = 0; + for (int y = 0; y < height; y += (1 << log2BlockHeight)) + { + for (int x = 0; x < width; x += (1 << log2BlockWidth)) + { + int err; + uint64_t ssd = 0; + + for (int yOff = 0; yOff < (1 << log2BlockHeight); yOff++) + { + for (int xOff = 0; xOff < (1 << log2BlockWidth); xOff++) + { + if ((y + yOff) >= height || (x + xOff) >= width) + { + continue; + } + + err = org[yOff * orgStride + x + xOff] - dec[yOff * decStride + x + xOff]; + ssd += err * err; + } + } + + distortionBuf[(y >> log2BlockHeight) * distortionBufStride + (x >> log2BlockWidth)] = ssd; + totalDistortion += ssd; + } + org += (orgStride << log2BlockHeight); + dec += (decStride << log2BlockHeight); + } +} + +void EncAdaptiveLoopFilter::determineControlIdcValues(CodingStructure &cs, const ComponentID compID, const PelBuf *buf, + const int ctuWidthC, const int ctuHeightC, const int picWidthC, + const int picHeightC, uint64_t **unfilteredDistortion, + uint64_t *trainingDistortion[MAX_NUM_CC_ALF_FILTERS], + uint64_t *lumaSwingGreaterThanThresholdCount, + uint64_t *chromaSampleCountNearMidPoint, + bool reuseTemporalFilterCoeff, uint8_t *trainingCovControl, + uint8_t *filterControl, uint64_t &curTotalDistortion, + double &curTotalRate, bool filterEnabled[MAX_NUM_CC_ALF_FILTERS], + uint8_t mapFilterIdxToFilterIdc[MAX_NUM_CC_ALF_FILTERS + 1], + uint8_t &ccAlfFilterCount) +{ + bool curFilterEnabled[MAX_NUM_CC_ALF_FILTERS]; + std::fill_n(curFilterEnabled, MAX_NUM_CC_ALF_FILTERS, false); + +#if MAX_NUM_CC_ALF_FILTERS>1 + FilterIdxCount filterIdxCount[MAX_NUM_CC_ALF_FILTERS]; + for (int i = 0; i < MAX_NUM_CC_ALF_FILTERS; i++) + { + filterIdxCount[i].count = 0; + filterIdxCount[i].filterIdx = i; + } + + double prevRate = curTotalRate; +#endif + + TempCtx ctxInitial(m_CtxCache); + TempCtx ctxBest(m_CtxCache); + TempCtx ctxStart(m_CtxCache); + ctxInitial = SubCtx(Ctx::CcAlfFilterControlFlag, m_CABACEstimator->getCtx()); + ctxBest = SubCtx(Ctx::CcAlfFilterControlFlag, m_CABACEstimator->getCtx()); + + int ctuIdx = 0; + for (int yCtu = 0; yCtu < buf->height; yCtu += ctuHeightC) + { + for (int xCtu = 0; xCtu < buf->width; xCtu += ctuWidthC) + { + uint64_t ssd; + double rate; + double cost; + + uint64_t bestSSD = MAX_UINT64; + double bestRate = MAX_DOUBLE; + double bestCost = MAX_DOUBLE; + uint8_t bestFilterIdc = 0; + uint8_t bestFilterIdx = 0; + const uint32_t thresholdS = std::min<int>(buf->height - yCtu, ctuHeightC) << getComponentScaleY(COMPONENT_Cb, m_chromaFormat); + const uint32_t numberOfChromaSamples = std::min<int>(buf->height - yCtu, ctuHeightC) * std::min<int>(buf->width - xCtu, ctuWidthC); + const uint32_t thresholdC = (numberOfChromaSamples >> 2); + + m_CABACEstimator->getCtx() = ctxBest; + ctxStart = SubCtx(Ctx::CcAlfFilterControlFlag, m_CABACEstimator->getCtx()); + + for (int filterIdx = 0; filterIdx <= MAX_NUM_CC_ALF_FILTERS; filterIdx++) + { + uint8_t filterIdc = mapFilterIdxToFilterIdc[filterIdx]; + if (filterIdx < MAX_NUM_CC_ALF_FILTERS && !filterEnabled[filterIdx]) + { + continue; + } + + if (filterIdx == MAX_NUM_CC_ALF_FILTERS) + { + ssd = unfilteredDistortion[0][ctuIdx]; // restore saved distortion computation + } + else + { + ssd = trainingDistortion[filterIdx][ctuIdx]; + } + m_CABACEstimator->getCtx() = ctxStart; + m_CABACEstimator->resetBits(); + const Position lumaPos = Position({ xCtu << getComponentScaleX(compID, cs.pcv->chrFormat), + yCtu << getComponentScaleY(compID, cs.pcv->chrFormat) }); + m_CABACEstimator->codeCcAlfFilterControlIdc(filterIdc, cs, compID, ctuIdx, filterControl, lumaPos, + ccAlfFilterCount); + rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); + cost = rate * m_lambda[compID] + ssd; + + bool limitationExceeded = false; + if (m_limitCcAlf && filterIdx < MAX_NUM_CC_ALF_FILTERS) + { + limitationExceeded = limitationExceeded || (lumaSwingGreaterThanThresholdCount[ctuIdx] >= thresholdS); + limitationExceeded = limitationExceeded || (chromaSampleCountNearMidPoint[ctuIdx] >= thresholdC); + } + if (cost < bestCost && !limitationExceeded) + { + bestCost = cost; + bestRate = rate; + bestSSD = ssd; + bestFilterIdc = filterIdc; + bestFilterIdx = filterIdx; + + ctxBest = SubCtx(Ctx::CcAlfFilterControlFlag, m_CABACEstimator->getCtx()); + + trainingCovControl[ctuIdx] = (filterIdx == MAX_NUM_CC_ALF_FILTERS) ? 0 : (filterIdx + 1); + filterControl[ctuIdx] = (filterIdx == MAX_NUM_CC_ALF_FILTERS) ? 0 : (filterIdx + 1); + } + } + if (bestFilterIdc != 0) + { + curFilterEnabled[bestFilterIdx] = true; +#if MAX_NUM_CC_ALF_FILTERS>1 + filterIdxCount[bestFilterIdx].count++; +#endif + } + curTotalRate += bestRate; + curTotalDistortion += bestSSD; + ctuIdx++; + } + } + +#if MAX_NUM_CC_ALF_FILTERS>1 + if (!reuseTemporalFilterCoeff) + { + std::copy_n(curFilterEnabled, MAX_NUM_CC_ALF_FILTERS, filterEnabled); + + std::sort(filterIdxCount, filterIdxCount + MAX_NUM_CC_ALF_FILTERS, compareCounts); + + int filterIdc = 1; + ccAlfFilterCount = 0; + for ( FilterIdxCount &s : filterIdxCount ) + { + const int filterIdx = s.filterIdx; + if (filterEnabled[filterIdx]) + { + mapFilterIdxToFilterIdc[filterIdx] = filterIdc; + filterIdc++; + ccAlfFilterCount++; + } + } + + curTotalRate = prevRate; + m_CABACEstimator->getCtx() = ctxInitial; + m_CABACEstimator->resetBits(); + int ctuIdx = 0; + for (int y = 0; y < buf->height; y += ctuHeightC) + { + for (int x = 0; x < buf->width; x += ctuWidthC) + { + const int filterIdxPlus1 = filterControl[ctuIdx]; + + const Position lumaPos = Position( + { x << getComponentScaleX(compID, cs.pcv->chrFormat), y << getComponentScaleY(compID, cs.pcv->chrFormat) }); + + m_CABACEstimator->codeCcAlfFilterControlIdc(filterIdxPlus1 == 0 ? 0 + : mapFilterIdxToFilterIdc[filterIdxPlus1 - 1], + cs, compID, ctuIdx, filterControl, lumaPos, ccAlfFilterCount); + + ctuIdx++; + } + } + curTotalRate += FRAC_BITS_SCALE*m_CABACEstimator->getEstFracBits(); + } +#endif + + // restore for next iteration + m_CABACEstimator->getCtx() = ctxInitial; +} + +std::vector<int> EncAdaptiveLoopFilter::getAvailableCcAlfApsIds(CodingStructure& cs, ComponentID compID) +{ + APS** apss = cs.slice->getAlfAPSs(); + for (int i = 0; i < ALF_CTB_MAX_NUM_APS; i++) + { + apss[i] = m_apsMap->getPS((i << NUM_APS_TYPE_LEN) + ALF_APS); + } + + std::vector<int> result; + int apsIdChecked = 0, curApsId = m_apsIdStart; + if (curApsId < ALF_CTB_MAX_NUM_APS) + { + while (apsIdChecked < ALF_CTB_MAX_NUM_APS && !cs.slice->isIntra() && result.size() < ALF_CTB_MAX_NUM_APS && !cs.slice->getPendingRasInit() && !cs.slice->isIDRorBLA()) + { + APS* curAPS = cs.slice->getAlfAPSs()[curApsId]; + if (curAPS && curAPS->getTemporalId() <= cs.slice->getTLayer() && curAPS->getCcAlfAPSParam().newCcAlfFilter[compID - 1]) + { + result.push_back(curApsId); + } + apsIdChecked++; + curApsId = (curApsId + 1) % ALF_CTB_MAX_NUM_APS; + } + } + return result; +} + +void EncAdaptiveLoopFilter::deriveCcAlfFilter( CodingStructure& cs, ComponentID compID, const PelUnitBuf& orgYuv, const PelUnitBuf& tempDecYuvBuf, const PelUnitBuf& dstYuv ) +{ + if (!cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Y)) + { + m_ccAlfFilterParam.ccAlfFilterEnabled[compID - 1] = false; + return; + } + + m_limitCcAlf = m_encCfg->getBaseQP() >= m_encCfg->getCCALFQpThreshold(); + if (m_limitCcAlf && cs.slice->getSliceQp() <= m_encCfg->getBaseQP() + 1) + { + m_ccAlfFilterParam.ccAlfFilterEnabled[compID - 1] = false; + return; + } + + uint8_t bestMapFilterIdxToFilterIdc[MAX_NUM_CC_ALF_FILTERS+1]; + const int scaleX = getComponentScaleX(compID, cs.pcv->chrFormat); + const int scaleY = getComponentScaleY(compID, cs.pcv->chrFormat); + const int ctuWidthC = cs.pcv->maxCUWidth >> scaleX; + const int ctuHeightC = cs.pcv->maxCUHeight >> scaleY; + const int picWidthC = cs.pcv->lumaWidth >> scaleX; + const int picHeightC = cs.pcv->lumaHeight >> scaleY; + const int maxTrainingIterCount = 15; + + if (m_limitCcAlf) + { + countLumaSwingGreaterThanThreshold(dstYuv.get(COMPONENT_Y).bufAt(0, 0), dstYuv.get(COMPONENT_Y).stride, dstYuv.get(COMPONENT_Y).height, dstYuv.get(COMPONENT_Y).width, cs.pcv->maxCUWidthLog2, cs.pcv->maxCUHeightLog2, m_lumaSwingGreaterThanThresholdCount, m_numCTUsInWidth); + } + if (m_limitCcAlf) + { + countChromaSampleValueNearMidPoint(dstYuv.get(compID).bufAt(0, 0), dstYuv.get(compID).stride, dstYuv.get(compID).height, dstYuv.get(compID).width, cs.pcv->maxCUWidthLog2 - scaleX, cs.pcv->maxCUHeightLog2 - scaleY, m_chromaSampleCountNearMidPoint, m_numCTUsInWidth); + } + + for ( int filterIdx = 0; filterIdx <= MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + if ( filterIdx < MAX_NUM_CC_ALF_FILTERS) + { + memset( m_bestFilterCoeffSet[filterIdx], 0, sizeof(m_bestFilterCoeffSet[filterIdx]) ); + bestMapFilterIdxToFilterIdc[filterIdx] = filterIdx + 1; + } + else + { + bestMapFilterIdxToFilterIdc[filterIdx] = 0; + } + } + memset(m_bestFilterControl, 0, sizeof(uint8_t) * m_numCTUsInPic); + int ccalfReuseApsId = -1; + m_reuseApsId[compID - 1] = -1; + + const TempCtx ctxStartCcAlfFilterControlFlag ( m_CtxCache, SubCtx( Ctx::CcAlfFilterControlFlag, m_CABACEstimator->getCtx() ) ); + + // compute cost of not filtering + const Pel *org = orgYuv.get( compID ).bufAt(0,0); + const Pel *unfiltered = dstYuv.get( compID ).bufAt(0,0); + const int orgStride = orgYuv.get( compID ).stride; + const int unfilteredStride = dstYuv.get( compID ).stride; + const Pel *filtered = m_buf->bufAt(0,0); + const int filteredStride = m_buf->stride; + uint64_t unfilteredDistortion = 0; + computeLog2BlockSizeDistortion(org, orgStride, unfiltered, unfilteredStride, m_buf->height, m_buf->width, + m_unfilteredDistortion[0], m_numCTUsInWidth, cs.pcv->maxCUWidthLog2 - scaleX, + cs.pcv->maxCUHeightLog2 - scaleY, unfilteredDistortion); + double bestUnfilteredTotalCost = 1 * m_lambda[compID] + unfilteredDistortion; // 1 bit is for gating flag + + static bool ccAlfFilterIdxEnabled[MAX_NUM_CC_ALF_FILTERS]; + static short ccAlfFilterCoeff[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF]; + static uint8_t ccAlfFilterCount = MAX_NUM_CC_ALF_FILTERS; + double bestFilteredTotalCost = MAX_DOUBLE; + bool bestreuseTemporalFilterCoeff = false; + std::vector<int> apsIds = getAvailableCcAlfApsIds(cs, compID); + + for (int testFilterIdx = 0; testFilterIdx < ( apsIds.size() + 1 ); testFilterIdx++ ) + { + bool referencingExistingAps = (testFilterIdx < apsIds.size()) ? true : false; + int maxNumberOfFiltersBeingTested = MAX_NUM_CC_ALF_FILTERS - (testFilterIdx - static_cast<int>(apsIds.size())); + + if (maxNumberOfFiltersBeingTested < 0) + { + maxNumberOfFiltersBeingTested = 1; + } + + { + // Instead of rewriting the control buffer for every training iteration just keep a mapping from filterIdx to filterIdc + uint8_t mapFilterIdxToFilterIdc[MAX_NUM_CC_ALF_FILTERS + 1]; + for (int filterIdx = 0; filterIdx <= MAX_NUM_CC_ALF_FILTERS; filterIdx++) + { + if (filterIdx == MAX_NUM_CC_ALF_FILTERS) + { + mapFilterIdxToFilterIdc[filterIdx] = 0; + } + else + { + mapFilterIdxToFilterIdc[filterIdx] = filterIdx + 1; + } + } + + // initialize filters + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + ccAlfFilterIdxEnabled[filterIdx] = false; + memset(ccAlfFilterCoeff[filterIdx], 0, sizeof(ccAlfFilterCoeff[filterIdx])); + } + if ( referencingExistingAps ) + { + maxNumberOfFiltersBeingTested = m_apsMap->getPS((apsIds[testFilterIdx] << NUM_APS_TYPE_LEN) + ALF_APS)->getCcAlfAPSParam().ccAlfFilterCount[compID - 1]; + ccAlfFilterCount = maxNumberOfFiltersBeingTested; + for (int filterIdx = 0; filterIdx < maxNumberOfFiltersBeingTested; filterIdx++) + { + ccAlfFilterIdxEnabled[filterIdx] = true; + memcpy(ccAlfFilterCoeff[filterIdx], m_ccAlfFilterParam.ccAlfCoeff[compID - 1][filterIdx], + sizeof(ccAlfFilterCoeff[filterIdx])); + } + memcpy( ccAlfFilterCoeff, m_apsMap->getPS((apsIds[testFilterIdx] << NUM_APS_TYPE_LEN) + ALF_APS)->getCcAlfAPSParam().ccAlfCoeff[compID - 1], sizeof(ccAlfFilterCoeff) ); + } + else + { + for (int i = 0; i < maxNumberOfFiltersBeingTested; i++) + { + ccAlfFilterIdxEnabled[i] = true; + } + ccAlfFilterCount = maxNumberOfFiltersBeingTested; + } + + // initialize + int controlIdx = 0; + const int columnSize = ( m_buf->width / maxNumberOfFiltersBeingTested); + for (int y = 0; y < m_buf->height; y += ctuHeightC) + { + for (int x = 0; x < m_buf->width; x += ctuWidthC) + { + m_trainingCovControl[controlIdx] = ( x / columnSize ) + 1; + controlIdx++; + } + } + + // compute cost of filtering + int trainingIterCount = 0; + bool keepTraining = true; + bool improvement = false; + double prevTotalCost = MAX_DOUBLE; + while (keepTraining) + { + improvement = false; + for (int filterIdx = 0; filterIdx < maxNumberOfFiltersBeingTested; filterIdx++) + { + if (ccAlfFilterIdxEnabled[filterIdx]) + { + if (!referencingExistingAps) + { + deriveStatsForCcAlfFiltering(orgYuv, tempDecYuvBuf, compID, m_numCTUsInWidth, (filterIdx + 1), cs); + deriveCcAlfFilterCoeff(compID, dstYuv, tempDecYuvBuf, ccAlfFilterCoeff, filterIdx); + } + m_buf->copyFrom(dstYuv.get(compID)); + applyCcAlfFilter(cs, compID, *m_buf, tempDecYuvBuf, nullptr, ccAlfFilterCoeff, filterIdx); + + uint64_t distortion = 0; + computeLog2BlockSizeDistortion( + org, orgStride, filtered, filteredStride, m_buf->height, m_buf->width, m_trainingDistortion[filterIdx], + m_numCTUsInWidth, cs.pcv->maxCUWidthLog2 - scaleX, cs.pcv->maxCUHeightLog2 - scaleY, distortion); + } + } + + m_CABACEstimator->getCtx() = ctxStartCcAlfFilterControlFlag; + + uint64_t curTotalDistortion = 0; + double curTotalRate = 0; + determineControlIdcValues(cs, compID, m_buf, ctuWidthC, ctuHeightC, picWidthC, picHeightC, + m_unfilteredDistortion, m_trainingDistortion, + m_lumaSwingGreaterThanThresholdCount, + m_chromaSampleCountNearMidPoint, + (referencingExistingAps == true), + m_trainingCovControl, m_filterControl, curTotalDistortion, curTotalRate, + ccAlfFilterIdxEnabled, mapFilterIdxToFilterIdc, ccAlfFilterCount); + + // compute coefficient coding bit cost + if (ccAlfFilterCount > 0) + { + if (referencingExistingAps) + { + curTotalRate += 1 + 3 + lengthUvlc(ccAlfFilterCount - 1); // +1 for enable flag, +3 APS ID in slice header + } + else + { + curTotalRate += getCoeffRateCcAlf(ccAlfFilterCoeff, ccAlfFilterIdxEnabled, ccAlfFilterCount, compID) + 1 + lengthUvlc(ccAlfFilterCount - 1) + + 7; // +1 for the enable flag, +7 two 3-bit APS ID, one in slice header/one in APS, a 1-bit + // new filter flags (ignore shared cost such as other new-filter flags/NALU header/RBSP + // terminating bit/byte alignment bits) + } + + double curTotalCost = curTotalRate * m_lambda[compID] + curTotalDistortion; + + if (curTotalCost < prevTotalCost) + { + prevTotalCost = curTotalCost; + improvement = true; + } + + if (curTotalCost < bestFilteredTotalCost) + { + bestFilteredTotalCost = curTotalCost; + memcpy(m_bestFilterIdxEnabled, ccAlfFilterIdxEnabled, sizeof(ccAlfFilterIdxEnabled)); + memcpy(m_bestFilterCoeffSet, ccAlfFilterCoeff, sizeof(ccAlfFilterCoeff)); + memcpy(m_bestFilterControl, m_filterControl, sizeof(uint8_t) * m_numCTUsInPic); + m_bestFilterCount = ccAlfFilterCount; + ccalfReuseApsId = referencingExistingAps ? apsIds[testFilterIdx] : -1; + memcpy(bestMapFilterIdxToFilterIdc, mapFilterIdxToFilterIdc, sizeof(mapFilterIdxToFilterIdc)); + } + } + + trainingIterCount++; + if (!improvement || trainingIterCount > maxTrainingIterCount || referencingExistingAps) + { + keepTraining = false; + } + } + } + } + + if (bestUnfilteredTotalCost < bestFilteredTotalCost) + { + memset(m_bestFilterControl, 0, sizeof(uint8_t) * m_numCTUsInPic); + } + + // save best coeff and control + bool atleastOneBlockUndergoesFitlering = false; + for (int controlIdx = 0; m_bestFilterCount > 0 && controlIdx < m_numCTUsInPic; controlIdx++) + { + if (m_bestFilterControl[controlIdx]) + { + atleastOneBlockUndergoesFitlering = true; + break; + } + } + m_ccAlfFilterParam.numberValidComponents = getNumberValidComponents(m_chromaFormat); + m_ccAlfFilterParam.ccAlfFilterEnabled[compID - 1] = atleastOneBlockUndergoesFitlering; + if (atleastOneBlockUndergoesFitlering) + { + // update the filter control indicators + if (bestreuseTemporalFilterCoeff!=1) + { + short storedBestFilterCoeffSet[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF]; + for (int filterIdx=0; filterIdx<MAX_NUM_CC_ALF_FILTERS; filterIdx++) + { + memcpy(storedBestFilterCoeffSet[filterIdx], m_bestFilterCoeffSet[filterIdx], sizeof(m_bestFilterCoeffSet[filterIdx])); + } + memcpy(m_filterControl, m_bestFilterControl, sizeof(uint8_t) * m_numCTUsInPic); + + int filterCount = 0; + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + uint8_t curFilterIdc = bestMapFilterIdxToFilterIdc[filterIdx]; + if (m_bestFilterIdxEnabled[filterIdx]) + { + for (int controlIdx = 0; controlIdx < m_numCTUsInPic; controlIdx++) + { + if (m_filterControl[controlIdx] == (filterIdx+1) ) + { + m_bestFilterControl[controlIdx] = curFilterIdc; + } + } + memcpy( m_bestFilterCoeffSet[curFilterIdc-1], storedBestFilterCoeffSet[filterIdx], sizeof(storedBestFilterCoeffSet[filterIdx]) ); + filterCount++; + } + m_bestFilterIdxEnabled[filterIdx] = ( filterIdx < m_bestFilterCount ) ? true : false; + } + CHECK( filterCount != m_bestFilterCount, "Number of filters enabled did not match the filter count"); + } + + m_ccAlfFilterParam.ccAlfFilterCount[compID - 1] = m_bestFilterCount; + // cleanup before copying + memset(m_ccAlfFilterControl[compID - 1], 0, sizeof(uint8_t) * m_numCTUsInPic); + for ( int filterIdx = 0; filterIdx < MAX_NUM_CC_ALF_FILTERS; filterIdx++ ) + { + memset(m_ccAlfFilterParam.ccAlfCoeff[compID - 1][filterIdx], 0, + sizeof(m_ccAlfFilterParam.ccAlfCoeff[compID - 1][filterIdx])); + } + memset(m_ccAlfFilterParam.ccAlfFilterIdxEnabled[compID - 1], false, + sizeof(m_ccAlfFilterParam.ccAlfFilterIdxEnabled[compID - 1])); + for ( int filterIdx = 0; filterIdx < m_bestFilterCount; filterIdx++ ) + { + m_ccAlfFilterParam.ccAlfFilterIdxEnabled[compID - 1][filterIdx] = m_bestFilterIdxEnabled[filterIdx]; + memcpy(m_ccAlfFilterParam.ccAlfCoeff[compID - 1][filterIdx], m_bestFilterCoeffSet[filterIdx], + sizeof(m_bestFilterCoeffSet[filterIdx])); + } + memcpy(m_ccAlfFilterControl[compID - 1], m_bestFilterControl, sizeof(uint8_t) * m_numCTUsInPic); + if ( ccalfReuseApsId >= 0 ) + { + m_reuseApsId[compID - 1] = ccalfReuseApsId; + if (compID == COMPONENT_Cb) + { + cs.slice->setTileGroupCcAlfCbApsId(ccalfReuseApsId); + } + else + { + cs.slice->setTileGroupCcAlfCrApsId(ccalfReuseApsId); + } + } + } +} + +void EncAdaptiveLoopFilter::deriveStatsForCcAlfFiltering(const PelUnitBuf &orgYuv, const PelUnitBuf &recYuv, + const int compIdx, const int maskStride, + const uint8_t filterIdc, CodingStructure &cs) +{ + const int filterIdx = filterIdc - 1; + + // init CTU stats buffers + for( int shape = 0; shape != m_filterShapesCcAlf[compIdx-1].size(); shape++ ) + { + for (int ctuIdx = 0; ctuIdx < m_numCTUsInPic; ctuIdx++) + { + m_alfCovarianceCcAlf[compIdx - 1][shape][filterIdx][ctuIdx].reset(); + } + } + + // init Frame stats buffers + for (int shape = 0; shape != m_filterShapesCcAlf[compIdx - 1].size(); shape++) + { + m_alfCovarianceFrameCcAlf[compIdx - 1][shape][filterIdx].reset(); + } + + int ctuRsAddr = 0; + const PreCalcValues &pcv = *cs.pcv; + bool clipTop = false, clipBottom = false, clipLeft = false, clipRight = false; + int numHorVirBndry = 0, numVerVirBndry = 0; + int horVirBndryPos[] = { 0, 0, 0 }; + int verVirBndryPos[] = { 0, 0, 0 }; + + for (int yPos = 0; yPos < m_picHeight; yPos += m_maxCUHeight) + { + for (int xPos = 0; xPos < m_picWidth; xPos += m_maxCUWidth) + { + if (m_trainingCovControl[ctuRsAddr] == filterIdc) + { + const int width = (xPos + m_maxCUWidth > m_picWidth) ? (m_picWidth - xPos) : m_maxCUWidth; + const int height = (yPos + m_maxCUHeight > m_picHeight) ? (m_picHeight - yPos) : m_maxCUHeight; + int rasterSliceAlfPad = 0; + if (isCrossedByVirtualBoundaries(cs, xPos, yPos, width, height, clipTop, clipBottom, clipLeft, clipRight, + numHorVirBndry, numVerVirBndry, horVirBndryPos, verVirBndryPos, + rasterSliceAlfPad)) + { + int yStart = yPos; + for (int i = 0; i <= numHorVirBndry; i++) + { + const int yEnd = i == numHorVirBndry ? yPos + height : horVirBndryPos[i]; + const int h = yEnd - yStart; + const bool clipT = (i == 0 && clipTop) || (i > 0) || (yStart == 0); + const bool clipB = (i == numHorVirBndry && clipBottom) || (i < numHorVirBndry) || (yEnd == pcv.lumaHeight); + int xStart = xPos; + for (int j = 0; j <= numVerVirBndry; j++) + { + const int xEnd = j == numVerVirBndry ? xPos + width : verVirBndryPos[j]; + const int w = xEnd - xStart; + const bool clipL = (j == 0 && clipLeft) || (j > 0) || (xStart == 0); + const bool clipR = (j == numVerVirBndry && clipRight) || (j < numVerVirBndry) || (xEnd == pcv.lumaWidth); + const int wBuf = w + (clipL ? 0 : MAX_ALF_PADDING_SIZE) + (clipR ? 0 : MAX_ALF_PADDING_SIZE); + const int hBuf = h + (clipT ? 0 : MAX_ALF_PADDING_SIZE) + (clipB ? 0 : MAX_ALF_PADDING_SIZE); + PelUnitBuf recBuf = m_tempBuf2.subBuf(UnitArea(cs.area.chromaFormat, Area(0, 0, wBuf, hBuf))); + recBuf.copyFrom(recYuv.subBuf( + UnitArea(cs.area.chromaFormat, Area(xStart - (clipL ? 0 : MAX_ALF_PADDING_SIZE), + yStart - (clipT ? 0 : MAX_ALF_PADDING_SIZE), wBuf, hBuf)))); + // pad top-left unavailable samples for raster slice + if (xStart == xPos && yStart == yPos && (rasterSliceAlfPad & 1)) + { + recBuf.padBorderPel(MAX_ALF_PADDING_SIZE, 1); + } + + // pad bottom-right unavailable samples for raster slice + if (xEnd == xPos + width && yEnd == yPos + height && (rasterSliceAlfPad & 2)) + { + recBuf.padBorderPel(MAX_ALF_PADDING_SIZE, 2); + } + recBuf.extendBorderPel(MAX_ALF_PADDING_SIZE); + recBuf = recBuf.subBuf(UnitArea( + cs.area.chromaFormat, Area(clipL ? 0 : MAX_ALF_PADDING_SIZE, clipT ? 0 : MAX_ALF_PADDING_SIZE, w, h))); + + const UnitArea area(m_chromaFormat, Area(0, 0, w, h)); + const UnitArea areaDst(m_chromaFormat, Area(xStart, yStart, w, h)); + + const ComponentID compID = ComponentID(compIdx); + + for (int shape = 0; shape != m_filterShapesCcAlf[compIdx - 1].size(); shape++) + { + getBlkStatsCcAlf(m_alfCovarianceCcAlf[compIdx - 1][0][filterIdx][ctuRsAddr], + m_filterShapesCcAlf[compIdx - 1][shape], orgYuv, recBuf, areaDst, area, compID, yPos); + m_alfCovarianceFrameCcAlf[compIdx - 1][shape][filterIdx] += + m_alfCovarianceCcAlf[compIdx - 1][shape][filterIdx][ctuRsAddr]; + } + + xStart = xEnd; + } + + yStart = yEnd; + } + } + else + { + const UnitArea area(m_chromaFormat, Area(xPos, yPos, width, height)); + + const ComponentID compID = ComponentID(compIdx); + + for (int shape = 0; shape != m_filterShapesCcAlf[compIdx - 1].size(); shape++) + { + getBlkStatsCcAlf(m_alfCovarianceCcAlf[compIdx - 1][0][filterIdx][ctuRsAddr], + m_filterShapesCcAlf[compIdx - 1][shape], orgYuv, recYuv, area, area, compID, yPos); + m_alfCovarianceFrameCcAlf[compIdx - 1][shape][filterIdx] += + m_alfCovarianceCcAlf[compIdx - 1][shape][filterIdx][ctuRsAddr]; + } + } + } + ctuRsAddr++; + } + } +} + +void EncAdaptiveLoopFilter::getBlkStatsCcAlf(AlfCovariance &alfCovariance, const AlfFilterShape &shape, + const PelUnitBuf &orgYuv, const PelUnitBuf &recYuv, + const UnitArea &areaDst, const UnitArea &area, const ComponentID compID, + const int yPos) +{ + const int numberOfComponents = getNumberValidComponents( m_chromaFormat ); + const CompArea &compArea = areaDst.block(compID); + int recStride[MAX_NUM_COMPONENT]; + const Pel* rec[MAX_NUM_COMPONENT]; + for ( int cIdx = 0; cIdx < numberOfComponents; cIdx++ ) + { + recStride[cIdx] = recYuv.get(ComponentID(cIdx)).stride; + rec[cIdx] = recYuv.get(ComponentID(cIdx)).bufAt(isLuma(ComponentID(cIdx)) ? area.lumaPos() : area.chromaPos()); + } + + int orgStride = orgYuv.get(compID).stride; + const Pel *org = orgYuv.get(compID).bufAt(compArea); + const int numBins = 1; + + int vbCTUHeight = m_alfVBLumaCTUHeight; + int vbPos = m_alfVBLumaPos; + if ((yPos + m_maxCUHeight) >= m_picHeight) + { + vbPos = m_picHeight; + } + + int ELocal[MAX_NUM_CC_ALF_CHROMA_COEFF][1]; + + for (int i = 0; i < compArea.height; i++) + { + int vbDistance = ((i << getComponentScaleX(compID, m_chromaFormat)) % vbCTUHeight) - vbPos; + for (int j = 0; j < compArea.width; j++) + { + std::memset(ELocal, 0, sizeof(ELocal)); + + double weight = 1.0; + if (m_alfWSSD) + { + weight = m_lumaLevelToWeightPLUT[org[j]]; + } + + int yLocal = org[j] - rec[compID][j]; + + calcCovarianceCcAlf( ELocal, rec[COMPONENT_Y] + ( j << getComponentScaleX(compID, m_chromaFormat)), recStride[COMPONENT_Y], shape, vbDistance ); + + for( int k = 0; k < (shape.numCoeff - 1); k++ ) + { + for( int l = k; l < (shape.numCoeff - 1); l++ ) + { + for( int b0 = 0; b0 < numBins; b0++ ) + { + for (int b1 = 0; b1 < numBins; b1++) + { + if (m_alfWSSD) + { + alfCovariance.E[b0][b1][k][l] += weight * (double) (ELocal[k][b0] * ELocal[l][b1]); + } + else + { + alfCovariance.E[b0][b1][k][l] += ELocal[k][b0] * ELocal[l][b1]; + } + } + } + } + for (int b = 0; b < numBins; b++) + { + if (m_alfWSSD) + { + alfCovariance.y[b][k] += weight * (double) (ELocal[k][b] * yLocal); + } + else + { + alfCovariance.y[b][k] += ELocal[k][b] * yLocal; + } + } + } + if (m_alfWSSD) + { + alfCovariance.pixAcc += weight * (double) (yLocal * yLocal); + } + else + { + alfCovariance.pixAcc += yLocal * yLocal; + } + } + org += orgStride; + for (int srcCIdx = 0; srcCIdx < numberOfComponents; srcCIdx++) + { + ComponentID srcCompID = ComponentID(srcCIdx); + if (toChannelType(srcCompID) == toChannelType(compID)) + { + rec[srcCIdx] += recStride[srcCIdx]; + } + else + { + if (isLuma(compID)) + { + rec[srcCIdx] += (recStride[srcCIdx] >> getComponentScaleY(srcCompID, m_chromaFormat)); + } + else + { + rec[srcCIdx] += (recStride[srcCIdx] << getComponentScaleY(compID, m_chromaFormat)); + } + } + } + } + + for (int k = 1; k < (MAX_NUM_CC_ALF_CHROMA_COEFF - 1); k++) + { + for (int l = 0; l < k; l++) + { + for (int b0 = 0; b0 < numBins; b0++) + { + for (int b1 = 0; b1 < numBins; b1++) + { + alfCovariance.E[b0][b1][k][l] = alfCovariance.E[b1][b0][l][k]; + } + } + } + } +} + +void EncAdaptiveLoopFilter::calcCovarianceCcAlf(int ELocal[MAX_NUM_CC_ALF_CHROMA_COEFF][1], const Pel *rec, const int stride, const AlfFilterShape& shape, int vbDistance) +{ + CHECK(shape.filterType != CC_ALF, "Bad CC ALF shape"); + + const Pel *recYM1 = rec - 1 * stride; + const Pel *recY0 = rec; + const Pel *recYP1 = rec + 1 * stride; + const Pel *recYP2 = rec + 2 * stride; + + if (vbDistance == -2 || vbDistance == +1) + { + recYP2 = recYP1; + } + else if (vbDistance == -1 || vbDistance == 0) + { + recYM1 = recY0; + recYP2 = recYP1 = recY0; + } + + for (int b = 0; b < 1; b++) + { + const Pel centerValue = recY0[+0]; + ELocal[0][b] += recYM1[+0] - centerValue; + ELocal[1][b] += recY0[-1] - centerValue; + ELocal[2][b] += recY0[+1] - centerValue; + ELocal[3][b] += recYP1[-1] - centerValue; + ELocal[4][b] += recYP1[+0] - centerValue; + ELocal[5][b] += recYP1[+1] - centerValue; + ELocal[6][b] += recYP2[+0] - centerValue; + } +} + +void EncAdaptiveLoopFilter::countLumaSwingGreaterThanThreshold(const Pel* luma, int lumaStride, int height, int width, int log2BlockWidth, int log2BlockHeight, uint64_t* lumaSwingGreaterThanThresholdCount, int lumaCountStride) +{ + const int lumaBitDepth = m_inputBitDepth[CH_L]; + const int threshold = (1 << ( m_inputBitDepth[CH_L] - 2 )) - 1; + + // 3x4 Diamond + int xSupport[] = { 0, -1, 0, 1, -1, 0, 1, 0 }; + int ySupport[] = { -1, 0, 0, 0, 1, 1, 1, 2 }; + + for (int y = 0; y < height; y += (1 << log2BlockHeight)) + { + for (int x = 0; x < width; x += (1 << log2BlockWidth)) + { + lumaSwingGreaterThanThresholdCount[(y >> log2BlockHeight) * lumaCountStride + (x >> log2BlockWidth)] = 0; + + for (int yOff = 0; yOff < (1 << log2BlockHeight); yOff++) + { + for (int xOff = 0; xOff < (1 << log2BlockWidth); xOff++) + { + if ((y + yOff) >= (height - 2) || (x + xOff) >= (width - 1) || (y + yOff) < 1 || (x + xOff) < 1) // only consider samples that are fully supported by picture + { + continue; + } + + int minVal = ((1 << lumaBitDepth) - 1); + int maxVal = 0; + for (int i = 0; i < 8; i++) + { + Pel p = luma[(yOff + ySupport[i]) * lumaStride + x + xOff + xSupport[i]]; + + if ( p < minVal ) + { + minVal = p; + } + if ( p > maxVal ) + { + maxVal = p; + } + } + + if ((maxVal - minVal) > threshold) + { + lumaSwingGreaterThanThresholdCount[(y >> log2BlockHeight) * lumaCountStride + (x >> log2BlockWidth)]++; + } + } + } + } + luma += (lumaStride << log2BlockHeight); + } +} + +void EncAdaptiveLoopFilter::countChromaSampleValueNearMidPoint(const Pel* chroma, int chromaStride, int height, int width, int log2BlockWidth, int log2BlockHeight, uint64_t* chromaSampleCountNearMidPoint, int chromaSampleCountNearMidPointStride) +{ + const int midPoint = (1 << m_inputBitDepth[CH_C]) >> 1; + const int threshold = 16; + + for (int y = 0; y < height; y += (1 << log2BlockHeight)) + { + for (int x = 0; x < width; x += (1 << log2BlockWidth)) + { + chromaSampleCountNearMidPoint[(y >> log2BlockHeight)* chromaSampleCountNearMidPointStride + (x >> log2BlockWidth)] = 0; + + for (int yOff = 0; yOff < (1 << log2BlockHeight); yOff++) + { + for (int xOff = 0; xOff < (1 << log2BlockWidth); xOff++) + { + if ((y + yOff) >= height || (x + xOff) >= width) + { + continue; + } + + int distanceToMidPoint = abs(chroma[yOff * chromaStride + x + xOff] - midPoint); + if (distanceToMidPoint < threshold) + { + chromaSampleCountNearMidPoint[(y >> log2BlockHeight)* chromaSampleCountNearMidPointStride + (x >> log2BlockWidth)]++; + } + } + } + } + chroma += (chromaStride << log2BlockHeight); + } +} +#endif diff --git a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h index d2ceb026f..5c620b053 100644 --- a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h +++ b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h @@ -202,15 +202,24 @@ struct AlfCovariance double calculateError( const int *clip, const double *coeff ) const { return calculateError(clip, coeff, numCoeff); } double calculateError( const int *clip, const double *coeff, const int numCoeff ) const; double calcErrorForCoeffs( const int *clip, const int *coeff, const int numCoeff, const int bitDepth ) const; +#if JVET_Q0795_CCALF + double calcErrorForCcAlfCoeffs(const int* coeff, const int numCoeff, const int bitDepth) const; +#endif void getClipMax(const AlfFilterShape& alfShape, int *clip_max) const; void reduceClipCost(const AlfFilterShape& alfShape, int *clip) const; +#if JVET_Q0795_CCALF + int gnsSolveByChol( TE LHS, double* rhs, double *x, int numEq ) const; +#endif + private: // Cholesky decomposition int gnsSolveByChol( const int *clip, double *x, int numEq ) const; +#if !JVET_Q0795_CCALF int gnsSolveByChol( TE LHS, double* rhs, double *x, int numEq ) const; +#endif void gnsBacksubstitution( TE R, double* z, int size, double* A ) const; void gnsTransposeBacksubstitution( TE U, double* rhs, double* x, int order ) const; int gnsCholeskyDec( TE inpMatr, TE outMatr, int numEq ) const; @@ -231,6 +240,10 @@ private: uint8_t* m_ctuEnableFlagTmp[MAX_NUM_COMPONENT]; uint8_t* m_ctuEnableFlagTmp2[MAX_NUM_COMPONENT]; uint8_t* m_ctuAlternativeTmp[MAX_NUM_COMPONENT]; +#if JVET_Q0795_CCALF + AlfCovariance*** m_alfCovarianceCcAlf[2]; // [compIdx-1][shapeIdx][ctbAddr][filterIdx] + AlfCovariance** m_alfCovarianceFrameCcAlf[2]; // [compIdx-1][shapeIdx][filterIdx] +#endif //for RDO AlfParam m_alfParamTemp; @@ -255,6 +268,25 @@ private: int m_filterTmp[MAX_NUM_ALF_LUMA_COEFF]; int m_clipTmp[MAX_NUM_ALF_LUMA_COEFF]; +#if JVET_Q0795_CCALF + int m_apsIdCcAlfStart[2]; + + short m_bestFilterCoeffSet[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF]; + bool m_bestFilterIdxEnabled[MAX_NUM_CC_ALF_FILTERS]; + uint8_t m_bestFilterCount; + uint8_t* m_trainingCovControl; + Pel* m_bufOrigin; + PelBuf* m_buf; + uint64_t** m_unfilteredDistortion; // for different block size + uint64_t* m_trainingDistortion[MAX_NUM_CC_ALF_FILTERS]; // for current block size + uint64_t* m_lumaSwingGreaterThanThresholdCount; + uint64_t* m_chromaSampleCountNearMidPoint; + uint8_t* m_filterControl; // current iterations filter control + uint8_t* m_bestFilterControl; // best saved filter control + int m_reuseApsId[2]; + bool m_limitCcAlf; +#endif + public: EncAdaptiveLoopFilter( int& apsIdStart ); virtual ~EncAdaptiveLoopFilter() {} @@ -271,6 +303,9 @@ public: , const double lambdaChromaWeight #endif ); +#if JVET_Q0795_CCALF + int getNewCcAlfApsId(CodingStructure &cs, int cIdx); +#endif void initCABACEstimator( CABACEncoder* cabacEncoder, CtxCache* ctxCache, Slice* pcSlice, ParameterSetMap<APS>* apsMap ); void create( const EncCfg* encCfg, const int picWidth, const int picHeight, const ChromaFormat chromaFormatIDC, const int maxCUWidth, const int maxCUHeight, const int maxCUDepth, const int inputBitDepth[MAX_NUM_CHANNEL_TYPE], const int internalBitDepth[MAX_NUM_CHANNEL_TYPE] ); void destroy(); @@ -292,6 +327,14 @@ private: void deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnitBuf& recYuv, CodingStructure& cs ); void getBlkStats(AlfCovariance* alfCovariace, const AlfFilterShape& shape, AlfClassifier** classifier, Pel* org, const int orgStride, Pel* rec, const int recStride, const CompArea& areaDst, const CompArea& area, const ChannelType channel, int vbCTUHeight, int vbPos); void calcCovariance(int ELocal[MAX_NUM_ALF_LUMA_COEFF][MaxAlfNumClippingValues], const Pel *rec, const int stride, const AlfFilterShape& shape, const int transposeIdx, const ChannelType channel, int vbDistance); +#if JVET_Q0795_CCALF + void deriveStatsForCcAlfFiltering(const PelUnitBuf &orgYuv, const PelUnitBuf &recYuv, const int compIdx, + const int maskStride, const uint8_t filterIdc, CodingStructure &cs); + void getBlkStatsCcAlf(AlfCovariance &alfCovariance, const AlfFilterShape &shape, const PelUnitBuf &orgYuv, + const PelUnitBuf &recYuv, const UnitArea &areaDst, const UnitArea &area, + const ComponentID compID, const int yPos); + void calcCovarianceCcAlf(int ELocal[MAX_NUM_CC_ALF_CHROMA_COEFF][1], const Pel* rec, const int stride, const AlfFilterShape& shape, int vbDistance); +#endif void mergeClasses(const AlfFilterShape& alfShape, AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], const int numClasses, short filterIndices[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES]); @@ -305,6 +348,9 @@ private: #endif const int numClasses, const int numCoeff, double& distUnfilter ); void roundFiltCoeff( int *filterCoeffQuant, double *filterCoeff, const int numCoeff, const int factor ); +#if JVET_Q0795_CCALF + void roundFiltCoeffCCALF( int *filterCoeffQuant, double *filterCoeff, const int numCoeff, const int factor ); +#endif double getDistCoeffForce0( bool* codedVarBins, double errorForce0CoeffTab[MAX_NUM_ALF_CLASSES][2], int* bitsVarBin, int zeroBitsVarBin, const int numFilters); int lengthUvlc( int uiCode ); @@ -329,6 +375,28 @@ private: void setCtuAlternativeChroma( uint8_t* ctuAlts[MAX_NUM_COMPONENT], uint8_t val ); void copyCtuAlternativeChroma( uint8_t* ctuAltsDst[MAX_NUM_COMPONENT], uint8_t* ctuAltsSrc[MAX_NUM_COMPONENT] ); int getMaxNumAlternativesChroma( ); +#if JVET_Q0795_CCALF + int getCoeffRateCcAlf(short chromaCoeff[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF], bool filterEnabled[MAX_NUM_CC_ALF_FILTERS], uint8_t filterCount, ComponentID compID); + void deriveCcAlfFilterCoeff( ComponentID compID, const PelUnitBuf& recYuv, const PelUnitBuf& recYuvExt, short filterCoeff[MAX_NUM_CC_ALF_FILTERS][MAX_NUM_CC_ALF_CHROMA_COEFF], const uint8_t filterIdx ); + void computeLog2BlockSizeDistortion(const Pel *org, int orgStride, const Pel *dec, int decStride, int height, + int width, uint64_t *distortionBuf, int distortionBufStride, int log2BlockWidth, + int log2BlockHeight, uint64_t &totalDistortion); + void determineControlIdcValues(CodingStructure &cs, const ComponentID compID, const PelBuf *buf, const int ctuWidthC, + const int ctuHeightC, const int picWidthC, const int picHeightC, + uint64_t **unfilteredDistortion, uint64_t *trainingDistortion[MAX_NUM_CC_ALF_FILTERS], + uint64_t *lumaSwingGreaterThanThresholdCount, + uint64_t *chromaSampleCountNearMidPoint, + bool reuseFilterCoeff, uint8_t *trainingCovControl, uint8_t *filterControl, + uint64_t &curTotalDistortion, double &curTotalRate, + bool filterEnabled[MAX_NUM_CC_ALF_FILTERS], + uint8_t mapFilterIdxToFilterIdc[MAX_NUM_CC_ALF_FILTERS + 1], + uint8_t &ccAlfFilterCount); + void deriveCcAlfFilter( CodingStructure& cs, ComponentID compID, const PelUnitBuf& orgYuv, const PelUnitBuf& tempDecYuvBuf, const PelUnitBuf& dstYuv ); + std::vector<int> getAvailableCcAlfApsIds(CodingStructure& cs, ComponentID compID); + void xSetupCcAlfAPS( CodingStructure& cs ); + void countLumaSwingGreaterThanThreshold(const Pel* luma, int lumaStride, int height, int width, int log2BlockWidth, int log2BlockHeight, uint64_t* lumaSwingGreaterThanThresholdCount, int lumaCountStride); + void countChromaSampleValueNearMidPoint(const Pel* chroma, int chromaStride, int height, int width, int log2BlockWidth, int log2BlockHeight, uint64_t* chromaSampleCountNearMidPoint, int chromaSampleCountNearMidPointStride); +#endif }; diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index f27bb3c10..ceadd3249 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -171,6 +171,9 @@ protected: bool m_noPartitionConstraintsOverrideConstraintFlag; bool m_bNoSaoConstraintFlag; bool m_bNoAlfConstraintFlag; +#if JVET_Q0795_CCALF + bool m_noCCAlfConstraintFlag; +#endif bool m_bNoRefWraparoundConstraintFlag; bool m_bNoTemporalMvpConstraintFlag; bool m_bNoSbtmvpConstraintFlag; @@ -683,6 +686,10 @@ protected: #endif bool m_alf; ///< Adaptive Loop Filter +#if JVET_Q0795_CCALF + bool m_ccalf; + int m_ccalfQpThreshold; +#endif #if JVET_O0756_CALCULATE_HDRMETRICS double m_whitePointDeltaE[hdrtoolslib::NB_REF_WHITE]; double m_maxSampleValue; @@ -732,6 +739,10 @@ public: void setNoSaoConstraintFlag(bool bVal) { m_bNoSaoConstraintFlag = bVal; } bool getNoAlfConstraintFlag() const { return m_bNoAlfConstraintFlag; } void setNoAlfConstraintFlag(bool bVal) { m_bNoAlfConstraintFlag = bVal; } +#if JVET_Q0795_CCALF + bool getNoCCAlfConstraintFlag() const { return m_noCCAlfConstraintFlag; } + void setNoCCAlfConstraintFlag(bool bVal) { m_noCCAlfConstraintFlag = bVal; } +#endif bool getNoRefWraparoundConstraintFlag() const { return m_bNoRefWraparoundConstraintFlag; } void setNoRefWraparoundConstraintFlag(bool bVal) { m_bNoRefWraparoundConstraintFlag = bVal; } bool getNoTemporalMvpConstraintFlag() const { return m_bNoTemporalMvpConstraintFlag; } @@ -1770,7 +1781,12 @@ public: #endif void setUseALF( bool b ) { m_alf = b; } bool getUseALF() const { return m_alf; } - +#if JVET_Q0795_CCALF + void setUseCCALF( bool b ) { m_ccalf = b; } + bool getUseCCALF() const { return m_ccalf; } + void setCCALFQpThreshold( int b ) { m_ccalfQpThreshold = b; } + int getCCALFQpThreshold() const { return m_ccalfQpThreshold; } +#endif #if JVET_O0756_CALCULATE_HDRMETRICS void setWhitePointDeltaE( uint32_t index, double value ) { m_whitePointDeltaE[ index ] = value; } double getWhitePointDeltaE( uint32_t index ) const { return m_whitePointDeltaE[ index ]; } diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index 0bc3ea911..62d57e16c 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -2788,7 +2788,9 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, m_pcLoopFilter->loopFilterPic( cs ); CS::setRefinedMotionField(cs); +#if !JVET_Q0795_CCALF DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "final", 1 ) ) ); +#endif if( pcSlice->getSPS()->getSAOEnabledFlag() ) { @@ -2842,8 +2844,15 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, } pcPic->slices[s]->setAlfAPSs(cs.slice->getAlfAPSs()); pcPic->slices[s]->setTileGroupApsIdChroma(cs.slice->getTileGroupApsIdChroma()); +#if JVET_Q0795_CCALF + pcPic->slices[s]->setTileGroupCcAlfCbApsId(cs.slice->getTileGroupCcAlfCbApsId()); + pcPic->slices[s]->setTileGroupCcAlfCrApsId(cs.slice->getTileGroupCcAlfCrApsId()); +#endif } } +#if JVET_Q0795_CCALF + DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "final", 1 ) ) ); +#endif if (m_pcCfg->getUseCompositeRef() && getPrepareLTRef()) { updateCompositeReference(pcSlice, rcListPic, pocCurr); @@ -2949,7 +2958,11 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, } } +#if JVET_Q0795_CCALF + if (pcSlice->getSPS()->getALFEnabledFlag() && (pcSlice->getTileGroupAlfEnabledFlag(COMPONENT_Y) || pcSlice->getTileGroupCcAlfCbEnabledFlag() || pcSlice->getTileGroupCcAlfCrEnabledFlag())) +#else if (pcSlice->getSPS()->getALFEnabledFlag() && pcSlice->getTileGroupAlfEnabledFlag(COMPONENT_Y)) +#endif { for (int apsId = 0; apsId < ALF_CTB_MAX_NUM_APS; apsId++) { @@ -2964,12 +2977,28 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, *apsMap->allocatePS(apsId) = *aps; //allocate and cpy m_pcALF->setApsIdStart( apsId ); } +#if JVET_Q0795_CCALF + else if (pcSlice->getTileGroupCcAlfCbEnabledFlag() && !aps && apsId == pcSlice->getTileGroupCcAlfCbApsId()) + { + writeAPS = true; + aps = apsMap->getPS((pcSlice->getTileGroupCcAlfCbApsId() << NUM_APS_TYPE_LEN) + ALF_APS); + } + else if (pcSlice->getTileGroupCcAlfCrEnabledFlag() && !aps && apsId == pcSlice->getTileGroupCcAlfCrApsId()) + { + writeAPS = true; + aps = apsMap->getPS((pcSlice->getTileGroupCcAlfCrApsId() << NUM_APS_TYPE_LEN) + ALF_APS); + } +#endif if (writeAPS ) { actualTotalBits += xWriteAPS( accessUnit, aps, m_pcEncLib->getLayerId(), true ); apsMap->clearChangedFlag((apsId << NUM_APS_TYPE_LEN) + ALF_APS); +#if JVET_Q0795_CCALF + CHECK(aps != pcSlice->getAlfAPSs()[apsId] && apsId != pcSlice->getTileGroupCcAlfCbApsId() && apsId != pcSlice->getTileGroupCcAlfCrApsId(), "Wrong APS pointer in compressGOP"); +#else CHECK(aps != pcSlice->getAlfAPSs()[apsId], "Wrong APS pointer in compressGOP"); +#endif } } } @@ -3067,6 +3096,12 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, picHeader->setNumAlfAps(pcSlice->getTileGroupNumAps()); picHeader->setAlfAPSs(pcSlice->getTileGroupApsIdLuma()); picHeader->setAlfApsIdChroma(pcSlice->getTileGroupApsIdChroma()); +#if JVET_Q0795_CCALF + picHeader->setCcAlfEnabledFlag(COMPONENT_Cb, pcSlice->getTileGroupCcAlfCbEnabledFlag()); + picHeader->setCcAlfEnabledFlag(COMPONENT_Cr, pcSlice->getTileGroupCcAlfCrEnabledFlag()); + picHeader->setCcAlfCbApsId(pcSlice->getTileGroupCcAlfCbApsId()); + picHeader->setCcAlfCrApsId(pcSlice->getTileGroupCcAlfCrApsId()); +#endif } else { picHeader->setAlfEnabledPresentFlag( false ); @@ -3089,6 +3124,11 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, tmpBitsBeforeWriting = m_HLSWriter->getNumberOfWrittenBits(); +#if JVET_Q0795_CCALF + pcSlice->m_ccAlfFilterParam = m_pcALF->getCcAlfFilterParam(); + pcSlice->m_ccAlfFilterControl[0] = m_pcALF->getCcAlfControlIdc(COMPONENT_Cb); + pcSlice->m_ccAlfFilterControl[1] = m_pcALF->getCcAlfControlIdc(COMPONENT_Cr); +#endif m_HLSWriter->codeSliceHeader( pcSlice ); actualHeadBits += ( m_HLSWriter->getNumberOfWrittenBits() - tmpBitsBeforeWriting ); diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 3eb5c0846..834b3e98b 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -976,6 +976,9 @@ void EncLib::xInitSPS( SPS& sps, VPS& vps ) cinfo->setNoPartitionConstraintsOverrideConstraintFlag(m_noPartitionConstraintsOverrideConstraintFlag); cinfo->setNoSaoConstraintFlag(m_bNoSaoConstraintFlag); cinfo->setNoAlfConstraintFlag(m_bNoAlfConstraintFlag); +#if JVET_Q0795_CCALF + cinfo->setNoCCAlfConstraintFlag(m_noCCAlfConstraintFlag); +#endif cinfo->setNoRefWraparoundConstraintFlag(m_bNoRefWraparoundConstraintFlag); cinfo->setNoTemporalMvpConstraintFlag(m_bNoTemporalMvpConstraintFlag); cinfo->setNoSbtmvpConstraintFlag(m_bNoSbtmvpConstraintFlag); @@ -1146,6 +1149,9 @@ void EncLib::xInitSPS( SPS& sps, VPS& vps ) sps.setScalingListFlag ( (m_useScalingListId == SCALING_LIST_OFF) ? 0 : 1 ); sps.setALFEnabledFlag( m_alf ); +#if JVET_Q0795_CCALF + sps.setCCALFEnabledFlag( m_ccalf ); +#endif sps.setVuiParametersPresentFlag(getVuiParametersPresentFlag()); if (sps.getVuiParametersPresentFlag()) diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp index 4a1fdcc80..2c97b4d44 100644 --- a/source/Lib/EncoderLib/VLCWriter.cpp +++ b/source/Lib/EncoderLib/VLCWriter.cpp @@ -515,6 +515,12 @@ void HLSWriter::codeAlfAps( APS* pcAPS ) WRITE_FLAG(param.newFilterFlag[CHANNEL_TYPE_LUMA], "alf_luma_new_filter"); WRITE_FLAG(param.newFilterFlag[CHANNEL_TYPE_CHROMA], "alf_chroma_new_filter"); +#if JVET_Q0795_CCALF + CcAlfFilterParam paramCcAlf = pcAPS->getCcAlfAPSParam(); + WRITE_FLAG(paramCcAlf.newCcAlfFilter[COMPONENT_Cb - 1], "alf_cc_cb_filter_signal_flag"); + WRITE_FLAG(paramCcAlf.newCcAlfFilter[COMPONENT_Cr - 1], "alf_cc_cr_filter_signal_flag"); +#endif + if (param.newFilterFlag[CHANNEL_TYPE_LUMA]) { #if JVET_Q0249_ALF_CHROMA_CLIPFLAG @@ -550,6 +556,52 @@ void HLSWriter::codeAlfAps( APS* pcAPS ) alfFilter(param, true, altIdx); } } +#if JVET_Q0795_CCALF + for (int ccIdx = 0; ccIdx < 2; ccIdx++) + { + if (paramCcAlf.newCcAlfFilter[ccIdx]) + { + const int filterCount = paramCcAlf.ccAlfFilterCount[ccIdx]; + CHECK(filterCount > MAX_NUM_CC_ALF_FILTERS, "CC ALF Filter count is too large"); + CHECK(filterCount == 0, "CC ALF Filter count is too small"); + + if (MAX_NUM_CC_ALF_FILTERS > 1) + { + WRITE_UVLC(filterCount - 1, + ccIdx == 0 ? "alf_cc_cb_filters_signalled_minus1" : "alf_cc_cr_filters_signalled_minus1"); + } + + for (int filterIdx = 0; filterIdx < filterCount; filterIdx++) + { + AlfFilterShape alfShape(size_CC_ALF); + + const short *coeff = paramCcAlf.ccAlfCoeff[ccIdx][filterIdx]; + // Filter coefficients + for (int i = 0; i < alfShape.numCoeff - 1; i++) + { + if (coeff[i] == 0) + { + WRITE_CODE(0, CCALF_BITS_PER_COEFF_LEVEL, + ccIdx == 0 ? "alf_cc_cb_mapped_coeff_abs" : "alf_cc_cr_mapped_coeff_abs"); + } + else + { + WRITE_CODE(1 + floorLog2(abs(coeff[i])), CCALF_BITS_PER_COEFF_LEVEL, + ccIdx == 0 ? "alf_cc_cb_mapped_coeff_abs" : "alf_cc_cr_mapped_coeff_abs"); + WRITE_FLAG(coeff[i] < 0 ? 1 : 0, ccIdx == 0 ? "alf_cc_cb_coeff_sign" : "alf_cc_cr_coeff_sign"); + } + } + + DTRACE(g_trace_ctx, D_SYNTAX, "%s coeff filterIdx %d: ", ccIdx == 0 ? "Cb" : "Cr", filterIdx); + for (int i = 0; i < alfShape.numCoeff; i++) + { + DTRACE(g_trace_ctx, D_SYNTAX, "%d ", coeff[i]); + } + DTRACE(g_trace_ctx, D_SYNTAX, "\n"); + } + } + } +#endif } void HLSWriter::codeLmcsAps( APS* pcAPS ) @@ -864,6 +916,12 @@ void HLSWriter::codeSPS( const SPS* pcSPS ) WRITE_FLAG( pcSPS->getSAOEnabledFlag(), "sps_sao_enabled_flag"); WRITE_FLAG( pcSPS->getALFEnabledFlag(), "sps_alf_enabled_flag" ); +#if JVET_Q0795_CCALF + if (pcSPS->getALFEnabledFlag() && pcSPS->getChromaFormatIdc() != CHROMA_400) + { + WRITE_FLAG( pcSPS->getCCALFEnabledFlag(), "sps_ccalf_enabled_flag" ); + } +#endif WRITE_FLAG(pcSPS->getTransformSkipEnabledFlag() ? 1 : 0, "sps_transform_skip_enabled_flag"); if (pcSPS->getTransformSkipEnabledFlag()) @@ -1597,6 +1655,21 @@ void HLSWriter::codePictureHeader( PicHeader* picHeader ) { WRITE_CODE(picHeader->getAlfApsIdChroma(), 3, "pic_alf_aps_id_chroma"); } +#if JVET_Q0795_CCALF + if (sps->getCCALFEnabledFlag()) + { + WRITE_FLAG(picHeader->getCcAlfEnabledFlag(COMPONENT_Cb), "ph_cc_alf_cb_enabled_flag"); + if (picHeader->getCcAlfEnabledFlag(COMPONENT_Cb)) + { + WRITE_CODE(picHeader->getCcAlfCbApsId(), 3, "ph_cc_alf_cb_aps_id"); + } + WRITE_FLAG(picHeader->getCcAlfEnabledFlag(COMPONENT_Cr), "ph_cc_alf_cr_enabled_flag"); + if (picHeader->getCcAlfEnabledFlag(COMPONENT_Cr)) + { + WRITE_CODE(picHeader->getCcAlfCrApsId(), 3, "ph_cc_alf_cr_aps_id"); + } + } +#endif } } else @@ -1604,6 +1677,10 @@ void HLSWriter::codePictureHeader( PicHeader* picHeader ) picHeader->setAlfEnabledFlag(COMPONENT_Y, true); picHeader->setAlfEnabledFlag(COMPONENT_Cb, true); picHeader->setAlfEnabledFlag(COMPONENT_Cr, true); +#if JVET_Q0795_CCALF + picHeader->setCcAlfEnabledFlag(COMPONENT_Cb, sps->getCCALFEnabledFlag()); + picHeader->setCcAlfEnabledFlag(COMPONENT_Cr, sps->getCCALFEnabledFlag()); +#endif } } else @@ -1611,6 +1688,10 @@ void HLSWriter::codePictureHeader( PicHeader* picHeader ) picHeader->setAlfEnabledFlag(COMPONENT_Y, false); picHeader->setAlfEnabledFlag(COMPONENT_Cb, false); picHeader->setAlfEnabledFlag(COMPONENT_Cr, false); +#if JVET_Q0795_CCALF + picHeader->setCcAlfEnabledFlag(COMPONENT_Cb, false); + picHeader->setCcAlfEnabledFlag(COMPONENT_Cr, false); +#endif } // dependent quantization @@ -2030,6 +2111,26 @@ void HLSWriter::codeSliceHeader ( Slice* pcSlice ) { WRITE_CODE(pcSlice->getTileGroupApsIdChroma(), 3, "slice_alf_aps_id_chroma"); } + +#if JVET_Q0795_CCALF + if (pcSlice->getSPS()->getCCALFEnabledFlag()) + { + CcAlfFilterParam &filterParam = pcSlice->m_ccAlfFilterParam; + WRITE_FLAG(filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1] ? 1 : 0, "slice_cc_alf_cb_enabled_flag"); + if (filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1]) + { + // write CC ALF Cb APS ID + WRITE_CODE(pcSlice->getTileGroupCcAlfCbApsId(), 3, "slice_cc_alf_cb_aps_id"); + } + // Cr + WRITE_FLAG(filterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1] ? 1 : 0, "slice_cc_alf_cr_enabled_flag"); + if (filterParam.ccAlfFilterEnabled[COMPONENT_Cr - 1]) + { + // write CC ALF Cr APS ID + WRITE_CODE(pcSlice->getTileGroupCcAlfCrApsId(), 3, "slice_cc_alf_cr_aps_id"); + } + } +#endif } } @@ -2088,6 +2189,9 @@ void HLSWriter::codeConstraintInfo ( const ConstraintInfo* cinfo ) WRITE_FLAG(cinfo->getNoPartitionConstraintsOverrideConstraintFlag() ? 1 : 0, "no_partition_constraints_override_constraint_flag"); WRITE_FLAG(cinfo->getNoSaoConstraintFlag() ? 1 : 0, "no_sao_constraint_flag"); WRITE_FLAG(cinfo->getNoAlfConstraintFlag() ? 1 : 0, "no_alf_constraint_flag"); +#if JVET_Q0795_CCALF + WRITE_FLAG(cinfo->getNoCCAlfConstraintFlag() ? 1 : 0, "no_ccalf_constraint_flag"); +#endif WRITE_FLAG(cinfo->getNoJointCbCrConstraintFlag() ? 1 : 0, "no_joint_cbcr_constraint_flag"); WRITE_FLAG(cinfo->getNoRefWraparoundConstraintFlag() ? 1 : 0, "no_ref_wraparound_constraint_flag"); WRITE_FLAG(cinfo->getNoTemporalMvpConstraintFlag() ? 1 : 0, "no_temporal_mvp_constraint_flag"); -- GitLab