diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp index 007ffd3cb2d64a0480042285f535bd886c75b280..a10bd3829b7e9344dbf5a8c9654cf10d59d7d720 100644 --- a/source/Lib/CommonLib/CodingStructure.cpp +++ b/source/Lib/CommonLib/CodingStructure.cpp @@ -274,6 +274,19 @@ void CodingStructure::clearCuPuTuIdxMap( const UnitArea &_area, uint32_t numCu, } #endif +#if JVET_O0050_LOCAL_DUAL_TREE +CodingUnit* CodingStructure::getLumaCU( const Position &pos ) +{ + const ChannelType effChType = CHANNEL_TYPE_LUMA; + const CompArea &_blk = area.blocks[effChType]; + CHECK( !_blk.contains( pos ), "must contain the pos" ); + + const unsigned idx = m_cuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )]; + + if( idx != 0 ) return cus[idx - 1]; + else return nullptr; +} +#endif CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType effChType ) { @@ -285,20 +298,16 @@ CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType effCh if( !_blk.contains( pos ) ) #endif { - if (parent) - { #if JVET_O0050_LOCAL_DUAL_TREE - if (treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA) - { - CHECK(parent->treeType != TREE_D, "wrong parent treeType "); - } -#endif - return parent->getCU(pos, effChType); - } - else + //keep this check, which is helpful to identify bugs + if( treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA ) { - return nullptr; + CHECK( parent == nullptr, "parent shall be valid; consider using function getLumaCU()" ); + CHECK( parent->treeType != TREE_D, "wrong parent treeType " ); } +#endif + if( parent ) return parent->getCU( pos, effChType ); + else return nullptr; } else { @@ -321,7 +330,10 @@ const CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType { #if JVET_O0050_LOCAL_DUAL_TREE if( treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA ) + { + CHECK( parent == nullptr, "parent shall be valid; consider using function getLumaCU()" ); CHECK( parent->treeType != TREE_D, "wrong parent treeType" ); + } #endif if( parent ) return parent->getCU( pos, effChType ); else return nullptr; diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h index 17acbeb2a141c62672ebaf39bf749e6beedb5eab..8a11a3befa0e2b2fa667cf7d69c1df1cdec1c51a 100644 --- a/source/Lib/CommonLib/CodingStructure.h +++ b/source/Lib/CommonLib/CodingStructure.h @@ -132,6 +132,9 @@ public: const TransformUnit *getTU(const Position &pos, const ChannelType _chType, const int subTuIdx = -1) const; CodingUnit *getCU(const Position &pos, const ChannelType _chType); +#if JVET_O0050_LOCAL_DUAL_TREE + CodingUnit *getLumaCU( const Position &pos ); +#endif PredictionUnit *getPU(const Position &pos, const ChannelType _chType); TransformUnit *getTU(const Position &pos, const ChannelType _chType, const int subTuIdx = -1); diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index 18ae062abc939b5c2351b1f3cef785aaaa8ef1d9..254b5690a5f7b54f962404b12da958d4bf0a7c0c 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -442,7 +442,12 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU bool lastSegment = false; // Reset delta QP coding flag and ChromaQPAdjustemt coding flag +#if JVET_O0050_LOCAL_DUAL_TREE + //Note: do not reset qg at chroma CU + if( pps.getUseDQP() && partitioner.currQgEnable() && !isChroma(partitioner.chType) ) +#else if( pps.getUseDQP() && partitioner.currQgEnable() ) +#endif { cuCtx.qgStart = true; cuCtx.isDQPCoded = false; @@ -453,11 +458,7 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU } // Reset delta QP coding flag and ChromaQPAdjustemt coding flag -#if JVET_O0050_LOCAL_DUAL_TREE - if (partitioner.isSepTree(cs) && pPartitionerChroma != nullptr) -#else if (CS::isDualITree(cs) && pPartitionerChroma != nullptr) -#endif { if (pps.getUseDQP() && pPartitionerChroma->currQgEnable()) { @@ -635,6 +636,7 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU cu.tileIdx = cs.picture->brickMap->getBrickIdxRsMap( currArea.lumaPos() ); #if JVET_O0050_LOCAL_DUAL_TREE CHECK( cu.cs->treeType != partitioner.treeType, "treeType mismatch" ); + int lumaQPinLocalDualTree = -1; #endif // Predict QP on start of quantization group @@ -652,7 +654,15 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU { const Position chromaCentral(cu.chromaPos().offset(cu.chromaSize().width >> 1, cu.chromaSize().height >> 1)); const Position lumaRefPos(chromaCentral.x << getComponentScaleX(COMPONENT_Cb, cu.chromaFormat), chromaCentral.y << getComponentScaleY(COMPONENT_Cb, cu.chromaFormat)); +#if JVET_O0050_LOCAL_DUAL_TREE + //derive chroma qp, but the chroma qp is saved in cuCtx.qp which is used for luma qp + //therefore, after decoding the chroma CU, the cuCtx.qp shall be recovered to luma qp in order to decode next luma cu qp + const CodingUnit* colLumaCu = cs.getLumaCU( lumaRefPos ); + CHECK( colLumaCu == nullptr, "colLumaCU shall exist" ); + lumaQPinLocalDualTree = cuCtx.qp; +#else const CodingUnit* colLumaCu = cs.getCU(lumaRefPos, CHANNEL_TYPE_LUMA); +#endif if (colLumaCu) cuCtx.qp = colLumaCu->qp; } @@ -665,6 +675,13 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU cu.shareParentSize = (shareStateDec == SHARING) ? shareParentSize : partitioner.currArea().lumaSize(); bool isLastCtu = coding_unit( cu, partitioner, cuCtx ); +#if JVET_O0050_LOCAL_DUAL_TREE + //recover cuCtx.qp to luma qp after decoding the chroma CU + if( pps.getUseDQP() && partitioner.isSepTree( cs ) && isChroma( cu.chType ) ) + { + cuCtx.qp = lumaQPinLocalDualTree; + } +#endif #if JVET_O0119_BASE_PALETTE_444 uint32_t compBegin; @@ -697,8 +714,19 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU { cs.reorderPrevPLT(cs.prevPLT, cu.curPLTSize, cu.curPLT, cu.reuseflag, compBegin, numComp, jointPLT); } +#endif +#if JVET_O0050_LOCAL_DUAL_TREE + if( cu.chType == CHANNEL_TYPE_CHROMA ) + { + DTRACE( g_trace_ctx, D_QP, "[chroma CU]x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Cb().x, cu.Cb().y, cu.Cb().width, cu.Cb().height, cu.qp ); + } + else + { #endif DTRACE( g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp ); +#if JVET_O0050_LOCAL_DUAL_TREE + } +#endif if (startShareThisLevel == 1) shareStateDec = NO_SHARE; return isLastCtu; diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp index 73b7b329e59bb17f2e873eeee931e4738fe9ac3b..f1701bb927fe18dab0d23bed430b5faf92c8ddf9 100644 --- a/source/Lib/EncoderLib/CABACWriter.cpp +++ b/source/Lib/EncoderLib/CABACWriter.cpp @@ -386,7 +386,12 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione const CodingUnit &cu = *cs.getCU( currArea.blocks[partitioner.chType], partitioner.chType ); // Reset delta QP coding flag and ChromaQPAdjustemt coding flag +#if JVET_O0050_LOCAL_DUAL_TREE + //Note: do not reset qg at chroma CU + if( pps.getUseDQP() && partitioner.currQgEnable() && !isChroma( partitioner.chType ) ) +#else if( pps.getUseDQP() && partitioner.currQgEnable() ) +#endif { cuCtx.qgStart = true; cuCtx.isDQPCoded = false; @@ -396,11 +401,7 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione cuCtx.isChromaQpAdjCoded = false; } // Reset delta QP coding flag and ChromaQPAdjustemt coding flag -#if JVET_O0050_LOCAL_DUAL_TREE - if (partitioner.isSepTree(cs) && pPartitionerChroma != nullptr) -#else if (CS::isDualITree(cs) && pPartitionerChroma != nullptr) -#endif { if (pps.getUseDQP() && pPartitionerChroma->currQgEnable()) { @@ -525,7 +526,18 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione // coding unit coding_unit( cu, partitioner, cuCtx ); +#if JVET_O0050_LOCAL_DUAL_TREE + if( cu.chType == CHANNEL_TYPE_CHROMA ) + { + DTRACE_COND( (isEncoding()), g_trace_ctx, D_QP, "[chroma CU]x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Cb().x, cu.Cb().y, cu.Cb().width, cu.Cb().height, cu.qp ); + } + else + { +#endif DTRACE_COND( ( isEncoding() ), g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp ); +#if JVET_O0050_LOCAL_DUAL_TREE + } +#endif DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode ); } @@ -1765,7 +1777,11 @@ void CABACWriter::cu_palette_info(const CodingUnit& cu, ComponentID compBegin, u if (cu.useEscape[compBegin] && cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded) { +#if JVET_O0050_LOCAL_DUAL_TREE + if (!cu.isSepTree() || isLuma(tu.chType)) +#else if (!CS::isDualITree(*tu.cs) || isLuma(tu.chType)) +#endif { cu_qp_delta(cu, cuCtx.qp, cu.qp); cuCtx.qp = cu.qp; diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp index b32a868382234ffd2bbf947dea7849274c6cf45d..cb8c2c4d2088ba85bbbe39ed60bd3db4e0144131 100644 --- a/source/Lib/EncoderLib/EncCu.cpp +++ b/source/Lib/EncoderLib/EncCu.cpp @@ -995,7 +995,21 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par m_CABACEstimator->getCtx() = m_CurrCtx->best; // QP from last processed CU for further processing +#if JVET_O0050_LOCAL_DUAL_TREE + //copy the qp of the last non-chroma CU + int numCUInThisNode = (int)bestCS->cus.size(); + if( numCUInThisNode > 1 && bestCS->cus.back()->chType == CHANNEL_TYPE_CHROMA && !CS::isDualITree( *bestCS ) ) + { + CHECK( bestCS->cus[numCUInThisNode-2]->chType != CHANNEL_TYPE_LUMA, "wrong chType" ); + bestCS->prevQP[partitioner.chType] = bestCS->cus[numCUInThisNode-2]->qp; + } + else + { +#endif bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp; +#if JVET_O0050_LOCAL_DUAL_TREE + } +#endif if ((!slice.isIntra() || slice.getSPS()->getIBCFlag()) && partitioner.chType == CHANNEL_TYPE_LUMA && bestCS->cus.size() == 1 && (bestCS->cus.back()->predMode == MODE_INTER || bestCS->cus.back()->predMode == MODE_IBC) @@ -1566,14 +1580,14 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, { for( int i = 0; i < bestSubCS->cus.size(); i++ ) { - CHECK( bestSubCS->cus[i]->predMode != MODE_INTER, "all CUs must be inter mode in an Intra coding region (SCIPU)" ); + CHECK( bestSubCS->cus[i]->predMode != MODE_INTER, "all CUs must be inter mode in an Inter coding region (SCIPU)" ); } } else if( partitioner.isConsIntra() ) { for( int i = 0; i < bestSubCS->cus.size(); i++ ) { - CHECK( bestSubCS->cus[i]->predMode != MODE_INTRA && bestSubCS->cus[i]->predMode != MODE_IBC, "all CUs must be intra/ibc mode in an Intra coding region (SCIPU)" ); + CHECK( bestSubCS->cus[i]->predMode == MODE_INTER, "all CUs must not be inter mode in an Intra coding region (SCIPU)" ); } } #endif @@ -1622,6 +1636,69 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, #if JVET_O0050_LOCAL_DUAL_TREE if( chromaNotSplit ) { +#if JVET_O0050_LOCAL_DUAL_TREE + //Note: In local dual tree region, the chroma CU refers to the central luma CU's QP. + //If the luma CU QP shall be predQP (no residual in it and before it in the QG), it must be revised to predQP before encoding the chroma CU + //Otherwise, the chroma CU uses predQP+deltaQP in encoding but is decoded as using predQP, thus causing encoder-decoded mismatch on chroma qp. + if( tempCS->pps->getUseDQP() ) + { + //find parent CS that including all coded CUs in the QG before this node + CodingStructure* qgCS = tempCS; + bool deltaQpCodedBeforeThisNode = false; + if( partitioner.currArea().lumaPos() != partitioner.currQgPos ) + { + int numParentNodeToQgCS = 0; + while( qgCS->area.lumaPos() != partitioner.currQgPos ) + { + CHECK( qgCS->parent == nullptr, "parent of qgCS shall exsit" ); + qgCS = qgCS->parent; + numParentNodeToQgCS++; + } + + //check whether deltaQP has been coded (in luma CU or luma&chroma CU) before this node + CodingStructure* parentCS = tempCS->parent; + for( int i = 0; i < numParentNodeToQgCS; i++ ) + { + //checking each parent + CHECK( parentCS == nullptr, "parentCS shall exsit" ); + for( const auto &cu : parentCS->cus ) + { + if( cu->rootCbf && !isChroma( cu->chType ) ) + { + deltaQpCodedBeforeThisNode = true; + break; + } + } + parentCS = parentCS->parent; + } + } + + //revise luma CU qp before the first luma CU with residual in the SCIPU to predQP + if( !deltaQpCodedBeforeThisNode ) + { + //get pred QP of the QG + const CodingUnit* cuFirst = qgCS->getCU( CHANNEL_TYPE_LUMA ); + CHECK( cuFirst->lumaPos() != partitioner.currQgPos, "First cu of the Qg is wrong" ); + int predQp = CU::predictQP( *cuFirst, qgCS->prevQP[CHANNEL_TYPE_LUMA] ); + + //revise to predQP + int firstCuHasResidual = (int)tempCS->cus.size(); + for( int i = 0; i < tempCS->cus.size(); i++ ) + { + if( tempCS->cus[i]->rootCbf ) + { + firstCuHasResidual = i; + break; + } + } + + for( int i = 0; i < firstCuHasResidual; i++ ) + { + tempCS->cus[i]->qp = predQp; + } + } + } +#endif assert( tempCS->treeType == TREE_L ); uint32_t numCuPuTu[6]; tempCS->picture->cs->getNumCuPuTuOffset( numCuPuTu ); @@ -2333,7 +2410,12 @@ void EncCu::xCheckDQP( CodingStructure& cs, Partitioner& partitioner, bool bKeep bool hasResidual = false; for( const auto &cu : cs.cus ) { +#if JVET_O0050_LOCAL_DUAL_TREE + //not include the chroma CU because chroma CU is decided based on corresponding luma QP and deltaQP is not signaled at chroma CU + if( cu->rootCbf && !isChroma( cu->chType )) +#else if( cu->rootCbf ) +#endif { hasResidual = true; break; @@ -2359,7 +2441,12 @@ void EncCu::xCheckDQP( CodingStructure& cs, Partitioner& partitioner, bool bKeep // NOTE: reset QPs for CUs without residuals up to first coded CU for( const auto &cu : cs.cus ) { +#if JVET_O0050_LOCAL_DUAL_TREE + //not include the chroma CU because chroma CU is decided based on corresponding luma QP and deltaQP is not signaled at chroma CU + if( cu->rootCbf && !isChroma( cu->chType )) +#else if( cu->rootCbf ) +#endif { break; }