/* The copyright in this software is being made available under the BSD * License, included below. This software may be subject to other third party * and contributor rights, including patent rights, and no such rights are * granted under this license. * * Copyright (c) 2010-2023, ITU/ISO/IEC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /** \file UnitTool.cpp * \brief defines operations for basic units */ #include "UnitTools.h" #include "dtrace_next.h" #include "Unit.h" #include "Slice.h" #include "Picture.h" #include #include #if GREEN_METADATA_SEI_ENABLED #include #endif // CS tools bool CS::isDualITree( const CodingStructure &cs ) { return cs.slice->isIntra() && !cs.pcv->ISingleTree; } UnitArea CS::getArea( const CodingStructure &cs, const UnitArea &area, const ChannelType chType ) { return isDualITree( cs ) || cs.treeType != TREE_D ? area.singleChan( chType ) : area; } void CS::setRefinedMotionField(CodingStructure &cs) { for (CodingUnit *cu : cs.cus) { for (auto &pu : CU::traversePUs(*cu)) { if (PU::checkDMVRCondition(pu)) { PredictionUnit subPu = pu; const int dy = std::min(pu.lumaSize().height, DMVR_SUBCU_HEIGHT); const int dx = std::min(pu.lumaSize().width, DMVR_SUBCU_WIDTH); Position puPos = pu.lumaPos(); int num = 0; for (int y = puPos.y; y < (puPos.y + pu.lumaSize().height); y = y + dy) { for (int x = puPos.x; x < (puPos.x + pu.lumaSize().width); x = x + dx) { subPu.UnitArea::operator=(UnitArea(pu.chromaFormat, Area(x, y, dx, dy))); subPu.mv[0] = pu.mv[0]; subPu.mv[1] = pu.mv[1]; subPu.mv[REF_PIC_LIST_0] += pu.mvdL0SubPu[num]; subPu.mv[REF_PIC_LIST_1] -= pu.mvdL0SubPu[num]; subPu.mv[REF_PIC_LIST_0].clipToStorageBitDepth(); subPu.mv[REF_PIC_LIST_1].clipToStorageBitDepth(); pu.mvdL0SubPu[num].setZero(); num++; PU::spanMotionInfo(subPu); } } } } } } // CU tools bool CU::getRprScaling(const SPS *sps, const PPS *curPPS, Picture *refPic, ScalingRatio &scalingRatio) { const int subWidthC = SPS::getWinUnitX(sps->getChromaFormatIdc()); const int subHeightC = SPS::getWinUnitY(sps->getChromaFormatIdc()); const Window& curScalingWindow = curPPS->getScalingWindow(); const int curLeftOffset = subWidthC * curScalingWindow.getWindowLeftOffset(); const int curRightOffset = subWidthC * curScalingWindow.getWindowRightOffset(); const int curTopOffset = subHeightC * curScalingWindow.getWindowTopOffset(); const int curBottomOffset = subHeightC * curScalingWindow.getWindowBottomOffset(); // Note: 64-bit integers are used for sizes such as to avoid possible overflows in corner cases const int64_t curPicScalWinWidth = curPPS->getPicWidthInLumaSamples() - (curLeftOffset + curRightOffset); const int64_t curPicScalWinHeight = curPPS->getPicHeightInLumaSamples() - (curTopOffset + curBottomOffset); const Window& refScalingWindow = refPic->getScalingWindow(); const int refLeftOffset = subWidthC * refScalingWindow.getWindowLeftOffset(); const int refRightOffset = subWidthC * refScalingWindow.getWindowRightOffset(); const int refTopOffset = subHeightC * refScalingWindow.getWindowTopOffset(); const int refBottomOffset = subHeightC * refScalingWindow.getWindowBottomOffset(); const int64_t refPicScalWinWidth = refPic->getPicWidthInLumaSamples() - (refLeftOffset + refRightOffset); const int64_t refPicScalWinHeight = refPic->getPicHeightInLumaSamples() - (refTopOffset + refBottomOffset); CHECK(curPicScalWinWidth * 2 < refPicScalWinWidth, "curPicScalWinWidth * 2 shall be greater than or equal to refPicScalWinWidth"); CHECK(curPicScalWinHeight * 2 < refPicScalWinHeight, "curPicScalWinHeight * 2 shall be greater than or equal to refPicScalWinHeight"); CHECK(curPicScalWinWidth > refPicScalWinWidth * 8, "curPicScalWinWidth shall be less than or equal to refPicScalWinWidth * 8"); CHECK(curPicScalWinHeight > refPicScalWinHeight * 8, "curPicScalWinHeight shall be less than or equal to refPicScalWinHeight * 8"); scalingRatio.x = (int) (((refPicScalWinWidth << ScalingRatio::BITS) + (curPicScalWinWidth >> 1)) / curPicScalWinWidth); scalingRatio.y = (int) (((refPicScalWinHeight << ScalingRatio::BITS) + (curPicScalWinHeight >> 1)) / curPicScalWinHeight); const int maxPicWidth = sps->getMaxPicWidthInLumaSamples(); // sps_pic_width_max_in_luma_samples const int maxPicHeight = sps->getMaxPicHeightInLumaSamples(); // sps_pic_height_max_in_luma_samples const int curPicWidth = curPPS->getPicWidthInLumaSamples(); // pps_pic_width_in_luma_samples const int curPicHeight = curPPS->getPicHeightInLumaSamples(); // pps_pic_height_in_luma_samples const int picSizeIncrement = std::max((int) 8, (1 << sps->getLog2MinCodingBlockSize())); // Max(8, MinCbSizeY) CHECK((curPicScalWinWidth * maxPicWidth) < refPicScalWinWidth * (curPicWidth - picSizeIncrement), "(curPicScalWinWidth * maxPicWidth) should be greater than or equal to refPicScalWinWidth * (curPicWidth - " "picSizeIncrement))"); CHECK((curPicScalWinHeight * maxPicHeight) < refPicScalWinHeight * (curPicHeight - picSizeIncrement), "(curPicScalWinHeight * maxPicHeight) should be greater than or equal to refPicScalWinHeight * (curPicHeight - " "picSizeIncrement))"); CHECK(curLeftOffset < -curPicWidth * 15, "The value of SubWidthC * pps_scaling_win_left_offset shall be greater " "than or equal to -pps_pic_width_in_luma_samples * 15"); CHECK(curLeftOffset >= curPicWidth, "The value of SubWidthC * pps_scaling_win_left_offset shall be less than pps_pic_width_in_luma_samples"); CHECK(curRightOffset < -curPicWidth * 15, "The value of SubWidthC * pps_scaling_win_right_offset shall be greater " "than or equal to -pps_pic_width_in_luma_samples * 15"); CHECK(curRightOffset >= curPicWidth, "The value of SubWidthC * pps_scaling_win_right_offset shall be less than pps_pic_width_in_luma_samples"); CHECK(curTopOffset < -curPicHeight * 15, "The value of SubHeightC * pps_scaling_win_top_offset shall be greater " "than or equal to -pps_pic_height_in_luma_samples * 15"); CHECK(curTopOffset >= curPicHeight, "The value of SubHeightC * pps_scaling_win_top_offset shall be less than pps_pic_height_in_luma_samples"); CHECK(curBottomOffset < (-curPicHeight) * 15, "The value of SubHeightC * pps_scaling_win_bottom_offset shall be " "greater than or equal to -pps_pic_height_in_luma_samples * 15"); CHECK(curBottomOffset >= curPicHeight, "The value of SubHeightC * pps_scaling_win_bottom_offset shall be less than pps_pic_height_in_luma_samples"); CHECK(curLeftOffset + curRightOffset < -curPicWidth * 15, "The value of SubWidthC * ( pps_scaling_win_left_offset + pps_scaling_win_right_offset ) shall be greater than " "or equal to -pps_pic_width_in_luma_samples * 15"); CHECK(curLeftOffset + curRightOffset >= curPicWidth, "The value of SubWidthC * ( pps_scaling_win_left_offset + pps_scaling_win_right_offset ) shall be less than " "pps_pic_width_in_luma_samples"); CHECK(curTopOffset + curBottomOffset < -curPicHeight * 15, "The value of SubHeightC * ( pps_scaling_win_top_offset + pps_scaling_win_bottom_offset ) shall be greater " "than or equal to -pps_pic_height_in_luma_samples * 15"); CHECK(curTopOffset + curBottomOffset >= curPicHeight, "The value of SubHeightC * ( pps_scaling_win_top_offset + pps_scaling_win_bottom_offset ) shall be less than " "pps_pic_height_in_luma_samples"); return refPic->isRefScaled( curPPS ); } void CU::checkConformanceILRP(Slice *slice) { const int numRefList = slice->isInterB() ? 2 : 1; int currentSubPicIdx = NOT_VALID; // derive sub-picture index for the current slice for( int subPicIdx = 0; subPicIdx < slice->getPic()->cs->sps->getNumSubPics(); subPicIdx++ ) { if( slice->getPic()->cs->pps->getSubPic( subPicIdx ).getSubPicID() == slice->getSliceSubPicId() ) { currentSubPicIdx = subPicIdx; break; } } CHECK( currentSubPicIdx == NOT_VALID, "Sub-picture was not found" ); if( !slice->getPic()->cs->sps->getSubPicTreatedAsPicFlag( currentSubPicIdx ) ) { return; } //constraint 1: The picture referred to by each active entry in RefPicList[ 0 ] or RefPicList[ 1 ] has the same subpicture layout as the current picture bool isAllRefSameSubpicLayout = true; for (int refList = 0; refList < numRefList; refList++) // loop over l0 and l1 { RefPicList eRefPicList = (refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0); for (int refIdx = 0; refIdx < slice->getNumRefIdx(eRefPicList); refIdx++) { const Picture* refPic = slice->getRefPic( eRefPicList, refIdx ); if( refPic->subPictures.size() != slice->getPic()->cs->pps->getNumSubPics() ) { isAllRefSameSubpicLayout = false; refList = numRefList; break; } else { for( int i = 0; i < refPic->subPictures.size(); i++ ) { const SubPic& refSubPic = refPic->subPictures[i]; const SubPic& curSubPic = slice->getPic()->cs->pps->getSubPic( i ); if( refSubPic.getSubPicWidthInCTUs() != curSubPic.getSubPicWidthInCTUs() || refSubPic.getSubPicHeightInCTUs() != curSubPic.getSubPicHeightInCTUs() || refSubPic.getSubPicCtuTopLeftX() != curSubPic.getSubPicCtuTopLeftX() || refSubPic.getSubPicCtuTopLeftY() != curSubPic.getSubPicCtuTopLeftY() || ( refPic->layerId != slice->getPic()->layerId && refSubPic.getSubPicID() != curSubPic.getSubPicID() ) || refSubPic.getTreatedAsPicFlag() != curSubPic.getTreatedAsPicFlag()) { isAllRefSameSubpicLayout = false; refIdx = slice->getNumRefIdx(eRefPicList); refList = numRefList; break; } } // A picture with different sub-picture ID of the collocated sub-picture cannot be used as an active reference picture in the same layer if( refPic->layerId == slice->getPic()->layerId ) { isAllRefSameSubpicLayout = isAllRefSameSubpicLayout && refPic->subPictures[currentSubPicIdx].getSubPicID() == slice->getSliceSubPicId(); } } } } //constraint 2: The picture referred to by each active entry in RefPicList[ 0 ] or RefPicList[ 1 ] is an ILRP for which the value of sps_num_subpics_minus1 is equal to 0 if (!isAllRefSameSubpicLayout) { for (int refList = 0; refList < numRefList; refList++) // loop over l0 and l1 { const RefPicList eRefPicList = refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0; for (int refIdx = 0; refIdx < slice->getNumRefIdx(eRefPicList); refIdx++) { const Picture* refPic = slice->getRefPic( eRefPicList, refIdx ); CHECK( refPic->layerId == slice->getPic()->layerId || refPic->subPictures.size() > 1, "The inter-layer reference shall contain a single subpicture or have same subpicture layout with the current picture" ); } } } return; } bool CU::isSameSlice(const CodingUnit& cu, const CodingUnit& cu2) { return cu.slice->getIndependentSliceIdx() == cu2.slice->getIndependentSliceIdx(); } bool CU::isSameTile(const CodingUnit& cu, const CodingUnit& cu2) { return cu.tileIdx == cu2.tileIdx; } bool CU::isSameSliceAndTile(const CodingUnit& cu, const CodingUnit& cu2) { return ( cu.slice->getIndependentSliceIdx() == cu2.slice->getIndependentSliceIdx() ) && ( cu.tileIdx == cu2.tileIdx ); } bool CU::isSameSubPic(const CodingUnit& cu, const CodingUnit& cu2) { return (cu.slice->getPPS()->getSubPicFromCU(cu).getSubPicIdx() == cu2.slice->getPPS()->getSubPicFromCU(cu2).getSubPicIdx()) ; } bool CU::isSameCtu(const CodingUnit& cu, const CodingUnit& cu2) { const uint32_t ctuSizeBit = floorLog2(cu.cs->sps->getMaxCUWidth()); Position pos1Ctu(cu.lumaPos().x >> ctuSizeBit, cu.lumaPos().y >> ctuSizeBit); Position pos2Ctu(cu2.lumaPos().x >> ctuSizeBit, cu2.lumaPos().y >> ctuSizeBit); return pos1Ctu.x == pos2Ctu.x && pos1Ctu.y == pos2Ctu.y; } bool CU::isLastSubCUOfCtu( const CodingUnit &cu ) { const Area cuAreaY = cu.isSepTree() ? Area(recalcPosition(cu.chromaFormat, cu.chType, ChannelType::LUMA, cu.block(cu.chType).pos()), recalcSize(cu.chromaFormat, cu.chType, ChannelType::LUMA, cu.block(cu.chType).size())) : (const Area &) cu.Y(); return ( ( ( ( cuAreaY.x + cuAreaY.width ) & cu.cs->pcv->maxCUWidthMask ) == 0 || cuAreaY.x + cuAreaY.width == cu.cs->pps->getPicWidthInLumaSamples() ) && ( ( ( cuAreaY.y + cuAreaY.height ) & cu.cs->pcv->maxCUHeightMask ) == 0 || cuAreaY.y + cuAreaY.height == cu.cs->pps->getPicHeightInLumaSamples() ) ); } uint32_t CU::getCtuAddr(const CodingUnit &cu) { return getCtuAddr(cu.block(cu.chType).lumaPos(), *cu.cs->pcv); } int CU::predictQP( const CodingUnit& cu, const int prevQP ) { const CodingStructure &cs = *cu.cs; const uint32_t ctuRsAddr = getCtuAddr(cu); const uint32_t ctuXPosInCtus = ctuRsAddr % cs.pcv->widthInCtus; const uint32_t tileColIdx = cu.slice->getPPS()->ctuToTileCol(ctuXPosInCtus); const uint32_t tileXPosInCtus = cu.slice->getPPS()->getTileColumnBd(tileColIdx); const CompArea &area = cu.block(cu.chType); if (ctuXPosInCtus == tileXPosInCtus && !(area.x & (cs.pcv->maxCUWidthMask >> getChannelTypeScaleX(cu.chType, cu.chromaFormat))) && !(area.y & (cs.pcv->maxCUHeightMask >> getChannelTypeScaleY(cu.chType, cu.chromaFormat))) && (cs.getCU(area.pos().offset(0, -1), cu.chType) != nullptr) && CU::isSameSliceAndTile(*cs.getCU(area.pos().offset(0, -1), cu.chType), cu)) { return ((cs.getCU(area.pos().offset(0, -1), cu.chType))->qp); } else { const int a = (area.y & (cs.pcv->maxCUHeightMask >> getChannelTypeScaleY(cu.chType, cu.chromaFormat))) ? (cs.getCU(area.pos().offset(0, -1), cu.chType))->qp : prevQP; const int b = (area.x & (cs.pcv->maxCUWidthMask >> getChannelTypeScaleX(cu.chType, cu.chromaFormat))) ? (cs.getCU(area.pos().offset(-1, 0), cu.chType))->qp : prevQP; return ( a + b + 1 ) >> 1; } } uint32_t CU::getNumPUs( const CodingUnit& cu ) { uint32_t cnt = 0; PredictionUnit *pu = cu.firstPU; do { cnt++; } while( ( pu != cu.lastPU ) && ( pu = pu->next ) ); return cnt; } void CU::addPUs( CodingUnit& cu ) { cu.cs->addPU( CS::getArea( *cu.cs, cu, cu.chType ), cu.chType ); } void CU::saveMotionForHmvp(const CodingUnit &cu) { if (!cu.geoFlag && !cu.affine && !(CU::isIBC(cu) && cu.lwidth() * cu.lheight() <= 16)) { const PredictionUnit &pu = *cu.firstPU; MotionInfo mi = pu.getMotionInfo(); #if GDR_ENABLED mi.sourcePos = pu.lumaPos(); mi.sourceClean = pu.cs->isClean(mi.sourcePos, ChannelType::LUMA); #endif mi.bcwIdx = mi.interDir == 3 ? cu.bcwIdx : BCW_DEFAULT; if (CU::isIBC(cu)) { cu.cs->addMiToLut(cu.cs->motionLut.lutIbc, mi); } else { const uint32_t mask = ~0u << (pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2); const CompArea &area = pu.cu->Y(); if ((((area.x + area.width) ^ area.x) & mask) != 0 && (((area.y + area.height) ^ area.y) & mask) != 0) { cu.cs->addMiToLut(cu.cs->motionLut.lut, mi); } } } } PartSplit CU::getSplitAtDepth( const CodingUnit& cu, const unsigned depth ) { if (depth >= cu.depth) { return CU_DONT_SPLIT; } const PartSplit cuSplitType = PartSplit( ( cu.splitSeries >> ( depth * SPLIT_DMULT ) ) & SPLIT_MASK ); if (cuSplitType == CU_QUAD_SPLIT) { return CU_QUAD_SPLIT; } else if (cuSplitType == CU_HORZ_SPLIT) { return CU_HORZ_SPLIT; } else if (cuSplitType == CU_VERT_SPLIT) { return CU_VERT_SPLIT; } else if (cuSplitType == CU_TRIH_SPLIT) { return CU_TRIH_SPLIT; } else if (cuSplitType == CU_TRIV_SPLIT) { return CU_TRIV_SPLIT; } else { THROW("Unknown split mode"); return CU_QUAD_SPLIT; } } ModeType CU::getModeTypeAtDepth( const CodingUnit& cu, const unsigned depth ) { ModeType modeType = ModeType( (cu.modeTypeSeries >> (depth * 3)) & 0x07 ); CHECK( depth > cu.depth, " depth is wrong" ); return modeType; } bool CU::divideTuInRows( const CodingUnit &cu ) { CHECK(cu.ispMode != ISPType::HOR && cu.ispMode != ISPType::VER, "Intra Subpartitions type not recognized!"); return cu.ispMode == ISPType::HOR; } PartSplit CU::getISPType( const CodingUnit &cu, const ComponentID compID ) { if (cu.ispMode != ISPType::NONE && isLuma(compID)) { const bool tuIsDividedInRows = CU::divideTuInRows( cu ); return tuIsDividedInRows ? TU_1D_HORZ_SPLIT : TU_1D_VERT_SPLIT; } return TU_NO_ISP; } bool CU::isISPLast( const CodingUnit &cu, const CompArea &tuArea, const ComponentID compID ) { const PartSplit partitionType = CU::getISPType(cu, compID); const Area originalArea = cu.blocks[compID]; switch( partitionType ) { case TU_1D_HORZ_SPLIT: return tuArea.y + tuArea.height == originalArea.y + originalArea.height; case TU_1D_VERT_SPLIT: return tuArea.x + tuArea.width == originalArea.x + originalArea.width; default: THROW( "Unknown ISP processing order type!" ); return false; } } bool CU::isISPFirst( const CodingUnit &cu, const CompArea &tuArea, const ComponentID compID ) { return tuArea == cu.firstTU->blocks[compID]; } bool CU::canUseISP( const CodingUnit &cu, const ComponentID compID ) { const int width = cu.blocks[compID].width; const int height = cu.blocks[compID].height; const int maxTrSize = cu.cs->sps->getMaxTbSize(); return CU::canUseISP( width, height, maxTrSize ); } bool CU::canUseISP( const int width, const int height, const int maxTrSize ) { const bool notEnoughSamplesToSplit = (floorLog2(width) + floorLog2(height) <= (floorLog2(MIN_TB_SIZEY) << 1)); const bool cuSizeLargerThanMaxTrSize = width > maxTrSize || height > maxTrSize; if ( notEnoughSamplesToSplit || cuSizeLargerThanMaxTrSize ) { return false; } return true; } bool CU::canUseLfnstWithISP( const CompArea& cuArea, const ISPType ispSplitType ) { if (ispSplitType == ISPType::NONE) { return false; } const Size tuSize = (ispSplitType == ISPType::HOR) ? Size(cuArea.width, CU::getISPSplitDim(cuArea.width, cuArea.height, TU_1D_HORZ_SPLIT)) : Size(CU::getISPSplitDim(cuArea.width, cuArea.height, TU_1D_VERT_SPLIT), cuArea.height); if( !( tuSize.width >= MIN_TB_SIZEY && tuSize.height >= MIN_TB_SIZEY ) ) { return false; } return true; } bool CU::canUseLfnstWithISP( const CodingUnit& cu, const ChannelType chType ) { CHECK( !isLuma( chType ), "Wrong ISP mode!" ); return CU::canUseLfnstWithISP(cu.blocks[isLuma(chType) ? 0 : 1], cu.ispMode); } uint32_t CU::getISPSplitDim( const int width, const int height, const PartSplit ispType ) { const bool divideTuInRows = ispType == TU_1D_HORZ_SPLIT; uint32_t splitDimensionSize; uint32_t nonSplitDimensionSize; if( divideTuInRows ) { splitDimensionSize = height; nonSplitDimensionSize = width; } else { splitDimensionSize = width; nonSplitDimensionSize = height; } const int divShift = 2; const int minNumberOfSamplesPerCu = 1 << (2 * floorLog2(MIN_TB_SIZEY)); const int factorToMinSamples = nonSplitDimensionSize < minNumberOfSamplesPerCu ? minNumberOfSamplesPerCu >> floorLog2(nonSplitDimensionSize) : 1; const int partitionSize = (splitDimensionSize >> divShift) < factorToMinSamples ? factorToMinSamples : (splitDimensionSize >> divShift); CHECK( floorLog2(partitionSize) + floorLog2(nonSplitDimensionSize) < floorLog2(minNumberOfSamplesPerCu), "A partition has less than the minimum amount of samples!" ); return partitionSize; } bool CU::allLumaCBFsAreZero(const CodingUnit& cu) { if (cu.ispMode == ISPType::NONE) { return TU::getCbf(*cu.firstTU, COMPONENT_Y) == false; } else { const int numTotalTUs = cu.ispMode == ISPType::HOR ? cu.lheight() >> floorLog2(cu.firstTU->lheight()) : cu.lwidth() >> floorLog2(cu.firstTU->lwidth()); TransformUnit* tuPtr = cu.firstTU; for (int tuIdx = 0; tuIdx < numTotalTUs; tuIdx++) { if (TU::getCbf(*tuPtr, COMPONENT_Y) == true) { return false; } tuPtr = tuPtr->next; } return true; } } PUTraverser CU::traversePUs( CodingUnit& cu ) { return PUTraverser( cu.firstPU, cu.lastPU->next ); } TUTraverser CU::traverseTUs( CodingUnit& cu ) { return TUTraverser( cu.firstTU, cu.lastTU->next ); } cPUTraverser CU::traversePUs( const CodingUnit& cu ) { return cPUTraverser( cu.firstPU, cu.lastPU->next ); } cTUTraverser CU::traverseTUs( const CodingUnit& cu ) { return cTUTraverser( cu.firstTU, cu.lastTU->next ); } // PU tools int PU::getIntraMPMs(const PredictionUnit &pu, unsigned *mpm) { const ChannelType channelType = ChannelType::LUMA; int leftIntraDir = PLANAR_IDX; int aboveIntraDir = PLANAR_IDX; const CompArea &area = pu.block(getFirstComponentOfChannel(channelType)); const Position posRT = area.topRight(); const Position posLB = area.bottomLeft(); // Get intra direction of left PU const PredictionUnit *puLeft = pu.cs->getPURestricted(posLB.offset(-1, 0), pu, channelType); if (puLeft && CU::isIntra(*puLeft->cu)) { leftIntraDir = PU::getIntraDirLuma(*puLeft); } // Get intra direction of above PU const PredictionUnit *puAbove = pu.cs->getPURestricted(posRT.offset(0, -1), pu, channelType); if (puAbove && CU::isIntra(*puAbove->cu) && CU::isSameCtu(*pu.cu, *puAbove->cu)) { aboveIntraDir = PU::getIntraDirLuma(*puAbove); } auto wrap = [](const int m) { const int mod = NUM_INTRA_ANGULAR_MODES - 1; return (m - ANGULAR_BASE + mod) % mod + ANGULAR_BASE; }; const int numCands = leftIntraDir == aboveIntraDir ? 1 : 2; const int minCandVal = std::min(leftIntraDir, aboveIntraDir); const int maxCandVal = std::max(leftIntraDir, aboveIntraDir); mpm[0] = PLANAR_IDX; if (maxCandVal < ANGULAR_BASE) { mpm[1] = DC_IDX; mpm[2] = VER_IDX; mpm[3] = HOR_IDX; mpm[4] = VER_IDX - 4; mpm[5] = VER_IDX + 4; } else if (numCands == 1 || minCandVal < ANGULAR_BASE) { mpm[1] = maxCandVal; mpm[2] = wrap(maxCandVal - 1); mpm[3] = wrap(maxCandVal + 1); mpm[4] = wrap(maxCandVal - 2); mpm[5] = wrap(maxCandVal + 2); } else // L!=A { mpm[1] = leftIntraDir; mpm[2] = aboveIntraDir; if (maxCandVal - minCandVal == 1) { mpm[3] = wrap(minCandVal - 1); mpm[4] = wrap(maxCandVal + 1); mpm[5] = wrap(minCandVal - 2); } else if (maxCandVal - minCandVal >= NUM_INTRA_ANGULAR_MODES - 3) { mpm[3] = wrap(minCandVal + 1); mpm[4] = wrap(maxCandVal - 1); mpm[5] = wrap(minCandVal + 2); } else if (maxCandVal - minCandVal == 2) { mpm[3] = wrap(minCandVal + 1); mpm[4] = wrap(minCandVal - 1); mpm[5] = wrap(maxCandVal + 1); } else { mpm[3] = wrap(minCandVal - 1); mpm[4] = wrap(minCandVal + 1); mpm[5] = wrap(maxCandVal - 1); } } for (int i = 0; i < NUM_MOST_PROBABLE_MODES; i++) { CHECK(mpm[i] >= NUM_LUMA_MODE, "Invalid MPM"); } return numCands; } bool PU::isMIP(const PredictionUnit &pu, const ChannelType chType) { if (isLuma(chType)) { // Default case if chType is omitted. return pu.cu->mipFlag; } else { return isDMChromaMIP(pu) && (pu.intraDir[ChannelType::CHROMA] == DM_CHROMA_IDX); } } bool PU::isDMChromaMIP(const PredictionUnit &pu) { return !pu.cu->isSepTree() && (pu.chromaFormat == CHROMA_444) && getCoLocatedLumaPU(pu).cu->mipFlag; } uint32_t PU::getIntraDirLuma( const PredictionUnit &pu ) { if (isMIP(pu)) { return PLANAR_IDX; } else { return pu.intraDir[ChannelType::LUMA]; } } void PU::getIntraChromaCandModes( const PredictionUnit &pu, unsigned modeList[NUM_CHROMA_MODE] ) { modeList[0] = PLANAR_IDX; modeList[1] = VER_IDX; modeList[2] = HOR_IDX; modeList[3] = DC_IDX; modeList[4] = LM_CHROMA_IDX; modeList[5] = MDLM_L_IDX; modeList[6] = MDLM_T_IDX; modeList[7] = DM_CHROMA_IDX; // If Direct Mode is MIP, mode cannot be already in the list. if (isDMChromaMIP(pu)) { return; } const uint32_t lumaMode = getCoLocatedIntraLumaMode(pu); for (int i = 0; i < 4; i++) { if (lumaMode == modeList[i]) { modeList[i] = VDIA_IDX; break; } } } bool PU::isLMCMode(unsigned mode) { return (mode >= LM_CHROMA_IDX && mode <= MDLM_T_IDX); } bool PU::isLMCModeEnabled(const PredictionUnit &pu, unsigned mode) { if ( pu.cs->sps->getUseLMChroma() && pu.cu->checkCCLMAllowed() ) { return true; } return false; } int PU::getLMSymbolList(const PredictionUnit &pu, int *modeList) { int idx = 0; modeList[idx++] = LM_CHROMA_IDX; modeList[idx++] = MDLM_L_IDX; modeList[idx++] = MDLM_T_IDX; return idx; } uint32_t PU::getFinalIntraMode( const PredictionUnit &pu, const ChannelType &chType ) { uint32_t intraMode = pu.intraDir[chType]; if (intraMode == DM_CHROMA_IDX && !isLuma(chType)) { intraMode = getCoLocatedIntraLumaMode(pu); } if (pu.chromaFormat == CHROMA_422 && !isLuma(chType) && intraMode < NUM_LUMA_MODE) // map directional, planar and dc { intraMode = g_chroma422IntraAngleMappingTable[intraMode]; } return intraMode; } const PredictionUnit &PU::getCoLocatedLumaPU(const PredictionUnit &pu) { Position topLeftPos = pu.block(pu.chType).lumaPos(); Position refPos = topLeftPos.offset(pu.block(pu.chType).lumaSize().width >> 1, pu.block(pu.chType).lumaSize().height >> 1); const PredictionUnit &lumaPU = pu.cu->isSepTree() ? *pu.cs->picture->cs->getPU(refPos, ChannelType::LUMA) : *pu.cs->getPU(topLeftPos, ChannelType::LUMA); return lumaPU; } uint32_t PU::getCoLocatedIntraLumaMode(const PredictionUnit &pu) { return PU::getIntraDirLuma(PU::getCoLocatedLumaPU(pu)); } int PU::getWideAngle( const TransformUnit &tu, const uint32_t dirMode, const ComponentID compID ) { //This function returns a wide angle index taking into account that the values 0 and 1 are reserved //for Planar and DC respectively, as defined in the Spec. Text. if( dirMode < 2 ) { return ( int ) dirMode; } const CompArea &area = tu.cu->ispMode != ISPType::NONE && isLuma(compID) ? tu.cu->blocks[compID] : tu.blocks[compID]; int width = area.width; int height = area.height; int modeShift[ ] = { 0, 6, 10, 12, 14, 15 }; int deltaSize = abs( floorLog2( width ) - floorLog2( height ) ); int predMode = dirMode; if( width > height && dirMode < 2 + modeShift[ deltaSize ] ) { predMode += ( VDIA_IDX - 1 ); } else if( height > width && predMode > VDIA_IDX - modeShift[ deltaSize ] ) { predMode -= ( VDIA_IDX + 1 ); } return predMode; } bool PU::addMergeHmvpCand(const CodingStructure &cs, MergeCtx &mrgCtx, const int &mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const bool isAvailableA1, const MotionInfo &miLeft, const bool isAvailableB1, const MotionInfo &miAbove, const bool ibcFlag, const bool isGt4x4 #if GDR_ENABLED , const PredictionUnit &pu, bool &allCandSolidInAbove #endif ) { const Slice &slice = *cs.slice; const auto &lut = ibcFlag ? cs.motionLut.lutIbc : cs.motionLut.lut; const int numAvailCandInLut = (int) lut.size(); #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || cs.picture->gdrParam.verBoundary == -1); bool vbOnCtuBoundary = true; if (isEncodeGdrClean) { vbOnCtuBoundary = (pu.cs->picture->gdrParam.verBoundary == -1) || (pu.cs->picture->gdrParam.verBoundary % pu.cs->sps->getMaxCUWidth() == 0); allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary; } #endif for (int mrgIdx = 0; mrgIdx < numAvailCandInLut; mrgIdx++) { const MotionInfo &miNeighbor = lut[numAvailCandInLut - 1 - mrgIdx]; #if GDR_ENABLED Position sourcePos = Position(0, 0); if (isEncodeGdrClean) { sourcePos = miNeighbor.sourcePos; } #endif if (mrgIdx > 1 || ((mrgIdx > 0 || !isGt4x4) && ibcFlag) || ((!isAvailableA1 || miLeft != miNeighbor) && (!isAvailableB1 || miAbove != miNeighbor))) { mrgCtx.interDirNeighbours[cnt] = miNeighbor.interDir; mrgCtx.useAltHpelIf[cnt] = !ibcFlag && miNeighbor.useAltHpelIf; mrgCtx.bcwIdx[cnt] = miNeighbor.interDir == 3 ? miNeighbor.bcwIdx : BCW_DEFAULT; for (const auto l: { REF_PIC_LIST_0, REF_PIC_LIST_1 }) { if (l != REF_PIC_LIST_0 && !slice.isInterB()) { continue; } mrgCtx.mvFieldNeighbours[cnt][l].setMvField(miNeighbor.mv[l], miNeighbor.refIdx[l]); #if GDR_ENABLED if (isEncodeGdrClean) { // note : cannot guarantee the order/value in the lut if any of the lut is in dirty area mrgCtx.mvPos[cnt][l] = sourcePos; mrgCtx.mvSolid[cnt][l] = allCandSolidInAbove & vbOnCtuBoundary; mrgCtx.mvValid[cnt][l] = cs.isClean(pu.Y().bottomRight(), miNeighbor.mv[l], l, miNeighbor.refIdx[l]); allCandSolidInAbove &= vbOnCtuBoundary; } #endif } if (mrgCandIdx == cnt) { return true; } if (++cnt == maxNumMergeCandMin1) { break; } } } if (cnt < maxNumMergeCandMin1) { mrgCtx.useAltHpelIf[cnt] = false; } return false; } void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx) { const CodingStructure &cs = *pu.cs; const uint32_t maxNumMergeCand = pu.cs->sps->getMaxNumIBCMergeCand(); #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA); bool allCandSolidInAbove = true; #endif for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui) { mrgCtx.bcwIdx[ui] = BCW_DEFAULT; mrgCtx.interDirNeighbours[ui] = 0; mrgCtx.mvFieldNeighbours[ui][0].refIdx = NOT_VALID; mrgCtx.mvFieldNeighbours[ui][1].refIdx = NOT_VALID; #if GDR_ENABLED if (isEncodeGdrClean) { mrgCtx.mvSolid[ui][0] = true; mrgCtx.mvSolid[ui][1] = true; mrgCtx.mvValid[ui][0] = true; mrgCtx.mvValid[ui][1] = true; } #endif mrgCtx.useAltHpelIf[ui] = false; } mrgCtx.numValidMergeCand = maxNumMergeCand; // compute the location of the current PU int cnt = 0; const Position posRT = pu.Y().topRight(); const Position posLB = pu.Y().bottomLeft(); MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft; //left const PredictionUnit* puLeft = cs.getPURestricted(posLB.offset(-1, 0), pu, pu.chType); bool isGt4x4 = pu.lwidth() * pu.lheight() > 16; const bool isAvailableA1 = puLeft && pu.cu != puLeft->cu && CU::isIBC(*puLeft->cu); if (isGt4x4 && isAvailableA1) { miLeft = puLeft->getMotionInfo(posLB.offset(-1, 0)); // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miLeft.interDir; // get Mv from Left mrgCtx.mvFieldNeighbours[cnt][0].setMvField(miLeft.mv[0], miLeft.refIdx[0]); #if GDR_ENABLED if (isEncodeGdrClean) { mrgCtx.mvSolid[cnt][0] = cs.isClean(posLB.offset(-1, 0), pu.chType); } #endif if (mrgCandIdx == cnt) { return; } cnt++; } // early termination if (cnt == maxNumMergeCand) { return; } // above const PredictionUnit *puAbove = cs.getPURestricted(posRT.offset(0, -1), pu, pu.chType); bool isAvailableB1 = puAbove && pu.cu != puAbove->cu && CU::isIBC(*puAbove->cu); if (isGt4x4 && isAvailableB1) { miAbove = puAbove->getMotionInfo(posRT.offset(0, -1)); if (!isAvailableA1 || (miAbove != miLeft)) { // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miAbove.interDir; // get Mv from Above mrgCtx.mvFieldNeighbours[cnt][0].setMvField(miAbove.mv[0], miAbove.refIdx[0]); #if GDR_ENABLED if (isEncodeGdrClean) { mrgCtx.mvSolid[cnt][0] = cs.isClean(posRT.offset(0, -1), pu.chType); } #endif if (mrgCandIdx == cnt) { return; } cnt++; } } // early termination if (cnt == maxNumMergeCand) { return; } if (cnt != maxNumMergeCand) { #if GDR_ENABLED bool allCandSolidInAbove = true; bool found = addMergeHmvpCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCand, cnt, isAvailableA1, miLeft, isAvailableB1, miAbove, true, isGt4x4, pu, allCandSolidInAbove); #else bool found = addMergeHmvpCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCand, cnt, isAvailableA1, miLeft, isAvailableB1, miAbove, true, isGt4x4); #endif if (found) { return; } } while (cnt < maxNumMergeCand) { mrgCtx.mvFieldNeighbours[cnt][0].setMvField(Mv(0, 0), IBC_REF_IDX); mrgCtx.interDirNeighbours[cnt] = 1; #if GDR_ENABLED // GDR: zero mv(0,0) if (isEncodeGdrClean) { mrgCtx.mvSolid[cnt][0] = true && allCandSolidInAbove; allCandSolidInAbove = true && allCandSolidInAbove; } #endif if (mrgCandIdx == cnt) { return; } cnt++; } mrgCtx.numValidMergeCand = cnt; } void PU::getInterMergeCandidates(const PredictionUnit &pu, MergeCtx &mrgCtx, int mmvdList, const int &mrgCandIdx) { const unsigned plevel = pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2; const CodingStructure &cs = *pu.cs; const Slice &slice = *pu.cs->slice; const uint32_t maxNumMergeCand = pu.cs->sps->getMaxNumMergeCand(); CHECK (maxNumMergeCand > MRG_MAX_NUM_CANDS, "selected maximum number of merge candidate exceeds global limit"); #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); bool allCandSolidInAbove = true; #endif for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui) { mrgCtx.bcwIdx[ui] = BCW_DEFAULT; mrgCtx.interDirNeighbours[ui] = 0; mrgCtx.mvFieldNeighbours[ui][0].refIdx = NOT_VALID; mrgCtx.mvFieldNeighbours[ui][1].refIdx = NOT_VALID; #if GDR_ENABLED if (isEncodeGdrClean) { mrgCtx.mvSolid[ui][0] = true; mrgCtx.mvSolid[ui][1] = true; mrgCtx.mvValid[ui][0] = true; mrgCtx.mvValid[ui][1] = true; mrgCtx.mvPos[ui][0] = Position(0, 0); mrgCtx.mvPos[ui][1] = Position(0, 0); } #endif mrgCtx.useAltHpelIf[ui] = false; } mrgCtx.numValidMergeCand = maxNumMergeCand; // compute the location of the current PU int cnt = 0; const Position posLT = pu.Y().topLeft(); const Position posRT = pu.Y().topRight(); const Position posLB = pu.Y().bottomLeft(); MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft; // above const PredictionUnit *puAbove = cs.getPURestricted(posRT.offset(0, -1), pu, pu.chType); bool isAvailableB1 = puAbove && isDiffMER(pu.lumaPos(), posRT.offset(0, -1), plevel) && pu.cu != puAbove->cu && CU::isInter(*puAbove->cu); if (isAvailableB1) { miAbove = puAbove->getMotionInfo(posRT.offset(0, -1)); // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miAbove.interDir; mrgCtx.useAltHpelIf[cnt] = miAbove.useAltHpelIf; // get Mv from Above mrgCtx.bcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAbove->cu->bcwIdx : BCW_DEFAULT; for (const auto l: { REF_PIC_LIST_0, REF_PIC_LIST_1 }) { if (l != REF_PIC_LIST_0 && !slice.isInterB()) { continue; } mrgCtx.mvFieldNeighbours[cnt][l].setMvField(miAbove.mv[l], miAbove.refIdx[l]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puAbove->lumaPos(); mrgCtx.mvPos[cnt][l] = pos; mrgCtx.mvSolid[cnt][l] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][l] = cs.isClean(pu.Y().bottomRight(), miAbove.mv[l], l, miAbove.refIdx[l]); } #endif } if (mrgCandIdx == cnt) { return; } cnt++; } // early termination if (cnt == maxNumMergeCand) { return; } //left const PredictionUnit* puLeft = cs.getPURestricted(posLB.offset(-1, 0), pu, pu.chType); const bool isAvailableA1 = puLeft && isDiffMER(pu.lumaPos(), posLB.offset(-1, 0), plevel) && pu.cu != puLeft->cu && CU::isInter(*puLeft->cu); if (isAvailableA1) { miLeft = puLeft->getMotionInfo(posLB.offset(-1, 0)); if (!isAvailableB1 || (miAbove != miLeft)) { // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miLeft.interDir; mrgCtx.useAltHpelIf[cnt] = miLeft.useAltHpelIf; mrgCtx.bcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeft->cu->bcwIdx : BCW_DEFAULT; // get Mv from Left mrgCtx.mvFieldNeighbours[cnt][0].setMvField(miLeft.mv[0], miLeft.refIdx[0]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puLeft->lumaPos(); mrgCtx.mvPos[cnt][0] = pos; mrgCtx.mvSolid[cnt][0] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][0] = cs.isClean(pu.Y().bottomRight(), miLeft.mv[0], REF_PIC_LIST_0, miLeft.refIdx[0]); } #endif if (slice.isInterB()) { mrgCtx.mvFieldNeighbours[cnt][1].setMvField(miLeft.mv[1], miLeft.refIdx[1]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puLeft->lumaPos(); mrgCtx.mvPos[cnt][1] = pos; mrgCtx.mvSolid[cnt][1] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][1] = cs.isClean(pu.Y().bottomRight(), miLeft.mv[1], REF_PIC_LIST_1, miLeft.refIdx[1]); } #endif } if (mrgCandIdx == cnt) { return; } cnt++; } } // early termination if( cnt == maxNumMergeCand ) { return; } // above right const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType ); bool isAvailableB0 = puAboveRight && isDiffMER( pu.lumaPos(), posRT.offset(1, -1), plevel) && CU::isInter( *puAboveRight->cu ); if( isAvailableB0 ) { miAboveRight = puAboveRight->getMotionInfo( posRT.offset( 1, -1 ) ); if( !isAvailableB1 || ( miAbove != miAboveRight ) ) { // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miAboveRight.interDir; mrgCtx.useAltHpelIf[cnt] = miAboveRight.useAltHpelIf; // get Mv from Above-right mrgCtx.bcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveRight->cu->bcwIdx : BCW_DEFAULT; mrgCtx.mvFieldNeighbours[cnt][0].setMvField(miAboveRight.mv[0], miAboveRight.refIdx[0]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puAboveRight->lumaPos(); mrgCtx.mvPos[cnt][0] = pos; mrgCtx.mvSolid[cnt][0] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][0] = cs.isClean(pu.Y().bottomRight(), miAboveRight.mv[0], REF_PIC_LIST_0, miAboveRight.refIdx[0]); } #endif if( slice.isInterB() ) { mrgCtx.mvFieldNeighbours[cnt][1].setMvField(miAboveRight.mv[1], miAboveRight.refIdx[1]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puAboveRight->lumaPos(); mrgCtx.mvPos[cnt][1] = pos; mrgCtx.mvSolid[cnt][1] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][1] = cs.isClean(pu.Y().bottomRight(), miAboveRight.mv[1], REF_PIC_LIST_1, miAboveRight.refIdx[1]); } #endif } if (mrgCandIdx == cnt) { return; } cnt++; } } // early termination if( cnt == maxNumMergeCand ) { return; } //left bottom const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType ); bool isAvailableA0 = puLeftBottom && isDiffMER( pu.lumaPos(), posLB.offset(-1, 1), plevel) && CU::isInter( *puLeftBottom->cu ); if( isAvailableA0 ) { miBelowLeft = puLeftBottom->getMotionInfo( posLB.offset( -1, 1 ) ); if( !isAvailableA1 || ( miBelowLeft != miLeft ) ) { // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miBelowLeft.interDir; mrgCtx.useAltHpelIf[cnt] = miBelowLeft.useAltHpelIf; mrgCtx.bcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeftBottom->cu->bcwIdx : BCW_DEFAULT; // get Mv from Bottom-Left mrgCtx.mvFieldNeighbours[cnt][0].setMvField(miBelowLeft.mv[0], miBelowLeft.refIdx[0]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puLeftBottom->lumaPos(); mrgCtx.mvPos[cnt][0] = pos; mrgCtx.mvSolid[cnt][0] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][0] = cs.isClean(pu.Y().bottomRight(), miBelowLeft.mv[0], REF_PIC_LIST_0, miBelowLeft.refIdx[0]); } #endif if( slice.isInterB() ) { mrgCtx.mvFieldNeighbours[cnt][1].setMvField(miBelowLeft.mv[1], miBelowLeft.refIdx[1]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puLeftBottom->lumaPos(); mrgCtx.mvPos[cnt][1] = pos; mrgCtx.mvSolid[cnt][1] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][1] = cs.isClean(pu.Y().bottomRight(), miBelowLeft.mv[1], REF_PIC_LIST_1, miBelowLeft.refIdx[1]); } #endif } if (mrgCandIdx == cnt) { return; } cnt++; } } // early termination if( cnt == maxNumMergeCand ) { return; } // above left if ( cnt < 4 ) { const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType ); bool isAvailableB2 = puAboveLeft && isDiffMER( pu.lumaPos(), posLT.offset(-1, -1), plevel ) && CU::isInter( *puAboveLeft->cu ); if( isAvailableB2 ) { miAboveLeft = puAboveLeft->getMotionInfo( posLT.offset( -1, -1 ) ); if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) ) { // get Inter Dir mrgCtx.interDirNeighbours[cnt] = miAboveLeft.interDir; mrgCtx.useAltHpelIf[cnt] = miAboveLeft.useAltHpelIf; mrgCtx.bcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveLeft->cu->bcwIdx : BCW_DEFAULT; // get Mv from Above-Left mrgCtx.mvFieldNeighbours[cnt][0].setMvField(miAboveLeft.mv[0], miAboveLeft.refIdx[0]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puAboveLeft->lumaPos(); mrgCtx.mvPos[cnt][0] = pos; mrgCtx.mvSolid[cnt][0] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][0] = cs.isClean(pu.Y().bottomRight(), miAboveLeft.mv[0], REF_PIC_LIST_0, miAboveLeft.refIdx[0]); } #endif if( slice.isInterB() ) { mrgCtx.mvFieldNeighbours[cnt][1].setMvField(miAboveLeft.mv[1], miAboveLeft.refIdx[1]); #if GDR_ENABLED if (isEncodeGdrClean) { Position pos = puAboveLeft->lumaPos(); mrgCtx.mvPos[cnt][1] = pos; mrgCtx.mvSolid[cnt][1] = cs.isClean(pos, pu.chType); mrgCtx.mvValid[cnt][1] = cs.isClean(pu.Y().bottomRight(), miAboveLeft.mv[1], REF_PIC_LIST_1, miAboveLeft.refIdx[1]); } #endif } if (mrgCandIdx == cnt) { return; } cnt++; } } } // early termination if (cnt == maxNumMergeCand) { return; } if (slice.getPicHeader()->getEnableTMVPFlag() && (pu.lumaSize().width + pu.lumaSize().height > 12)) { //>> MTK colocated-RightBottom // offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to Position posRB = pu.Y().bottomRight().offset( -3, -3 ); const PreCalcValues& pcv = *cs.pcv; #if GDR_ENABLED bool posC0inCurPicSolid = true; bool posC1inCurPicSolid = true; bool posC0inRefPicSolid = true; bool posC1inRefPicSolid = true; #endif Position posC0; Position posC1 = pu.Y().center(); bool C0Avail = false; bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight); const SubPic& curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos()); if (curSubPic.getTreatedAsPicFlag()) { boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() && (posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom()); } if (boundaryCond) { int posYInCtu = posRB.y & pcv.maxCUHeightMask; if (posYInCtu + 4 < pcv.maxCUHeight) { posC0 = posRB.offset(4, 4); C0Avail = true; } } Mv cColMv; int refIdx = 0; int dir = 0; unsigned arrayAddr = cnt; bool existMV = (C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, refIdx, false)) || getColocatedMVP(pu, REF_PIC_LIST_0, posC1, cColMv, refIdx, false); if (existMV) { dir |= 1; mrgCtx.mvFieldNeighbours[arrayAddr][0].setMvField(cColMv, refIdx); #if GDR_ENABLED if (isEncodeGdrClean) { Mv ccMv; posC0inCurPicSolid = cs.isClean(posC0, ChannelType::LUMA); posC1inCurPicSolid = cs.isClean(posC1, ChannelType::LUMA); posC0inRefPicSolid = cs.isClean(posC0, REF_PIC_LIST_0, refIdx); posC1inRefPicSolid = cs.isClean(posC1, REF_PIC_LIST_0, refIdx); bool isMVP0exist = C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, ccMv, refIdx, false); Position pos = isMVP0exist ? posC0 : posC1; mrgCtx.mvPos[arrayAddr][0] = pos; mrgCtx.mvSolid[arrayAddr][0] = isMVP0exist ? (posC0inCurPicSolid && posC0inRefPicSolid) : (posC1inCurPicSolid && posC1inRefPicSolid); mrgCtx.mvValid[arrayAddr][0] = cs.isClean(pu.Y().bottomRight(), ccMv, REF_PIC_LIST_0, refIdx); } #endif } if (slice.isInterB()) { existMV = (C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, cColMv, refIdx, false)) || getColocatedMVP(pu, REF_PIC_LIST_1, posC1, cColMv, refIdx, false); if (existMV) { dir |= 2; mrgCtx.mvFieldNeighbours[arrayAddr][1].setMvField(cColMv, refIdx); #if GDR_ENABLED if (isEncodeGdrClean) { Mv ccMv; posC0inCurPicSolid = cs.isClean(posC0, ChannelType::LUMA); posC1inCurPicSolid = cs.isClean(posC1, ChannelType::LUMA); posC0inRefPicSolid = cs.isClean(posC0, REF_PIC_LIST_1, refIdx); posC1inRefPicSolid = cs.isClean(posC1, REF_PIC_LIST_1, refIdx); bool isMVP0exist = C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, ccMv, refIdx, false); Position pos = isMVP0exist ? posC0 : posC1; mrgCtx.mvPos[arrayAddr][1] = pos; mrgCtx.mvSolid[arrayAddr][1] = isMVP0exist ? (posC0inCurPicSolid && posC0inRefPicSolid) : (posC1inCurPicSolid && posC1inRefPicSolid); mrgCtx.mvValid[arrayAddr][1] = cs.isClean(pu.Y().bottomRight(), ccMv, REF_PIC_LIST_1, refIdx); } #endif } } if( dir != 0 ) { bool addTMvp = true; if( addTMvp ) { mrgCtx.interDirNeighbours[arrayAddr] = dir; mrgCtx.bcwIdx[arrayAddr] = BCW_DEFAULT; mrgCtx.useAltHpelIf[arrayAddr] = false; if (mrgCandIdx == cnt) { return; } cnt++; } } } // early termination if (cnt == maxNumMergeCand) { return; } int maxNumMergeCandMin1 = maxNumMergeCand - 1; if (cnt != maxNumMergeCandMin1) { bool isGt4x4 = true; #if GDR_ENABLED allCandSolidInAbove = true; #endif #if GDR_ENABLED bool found = addMergeHmvpCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCandMin1, cnt, isAvailableA1, miLeft, isAvailableB1, miAbove, CU::isIBC(*pu.cu), isGt4x4, pu, allCandSolidInAbove); #else bool found = addMergeHmvpCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCandMin1, cnt, isAvailableA1, miLeft, isAvailableB1, miAbove, CU::isIBC(*pu.cu), isGt4x4); #endif if (found) { return; } } // pairwise-average candidates { if (cnt > 1 && cnt < maxNumMergeCand) { mrgCtx.mvFieldNeighbours[cnt][0].setMvField(Mv(0, 0), NOT_VALID); mrgCtx.mvFieldNeighbours[cnt][1].setMvField(Mv(0, 0), NOT_VALID); // calculate average MV for L0 and L1 seperately unsigned char interDir = 0; mrgCtx.useAltHpelIf[cnt] = (mrgCtx.useAltHpelIf[0] == mrgCtx.useAltHpelIf[1]) ? mrgCtx.useAltHpelIf[0] : false; for( int refListId = 0; refListId < (slice.isInterB() ? 2 : 1); refListId++ ) { const short refIdxI = mrgCtx.mvFieldNeighbours[0][refListId].refIdx; const short refIdxJ = mrgCtx.mvFieldNeighbours[1][refListId].refIdx; #if GDR_ENABLED // GDR: Pairwise average candidate bool mvISolid = isEncodeGdrClean ? mrgCtx.mvSolid[0][refListId] : true; bool mvJSolid = isEncodeGdrClean ? mrgCtx.mvSolid[1][refListId] : true; bool mvSolid = true; #endif // both MVs are invalid, skip if( (refIdxI == NOT_VALID) && (refIdxJ == NOT_VALID) ) { continue; } interDir += 1 << refListId; // both MVs are valid, average these two MVs if( (refIdxI != NOT_VALID) && (refIdxJ != NOT_VALID) ) { const Mv &mvI = mrgCtx.mvFieldNeighbours[0][refListId].mv; const Mv &mvJ = mrgCtx.mvFieldNeighbours[1][refListId].mv; // average two MVs Mv avgMv = mvI; avgMv += mvJ; avgMv >>= 1; mrgCtx.mvFieldNeighbours[cnt][refListId].setMvField(avgMv, refIdxI); #if GDR_ENABLED // GDR: Pairwise single I,J candidate if (isEncodeGdrClean) { mvSolid = mvISolid & mvJSolid & allCandSolidInAbove; mrgCtx.mvPos[cnt][refListId] = Position(0, 0); mrgCtx.mvSolid[cnt][refListId] = mvSolid; mrgCtx.mvValid[cnt][refListId] = cs.isClean(pu.Y().bottomRight(), avgMv, (RefPicList) refListId, refIdxI); allCandSolidInAbove &= mvSolid; } #endif } // only one MV is valid, take the only one MV else if( refIdxI != NOT_VALID ) { Mv singleMv = mrgCtx.mvFieldNeighbours[0][refListId].mv; mrgCtx.mvFieldNeighbours[cnt][refListId].setMvField(singleMv, refIdxI); #if GDR_ENABLED // GDR: Pairwise single I,J candidate if (isEncodeGdrClean) { mvSolid = mvISolid && allCandSolidInAbove; mrgCtx.mvPos[cnt][refListId] = Position(0, 0); mrgCtx.mvSolid[cnt][refListId] = mvSolid; mrgCtx.mvValid[cnt][refListId] = cs.isClean(pu.Y().bottomRight(), singleMv, (RefPicList) refListId, refIdxI); allCandSolidInAbove &= mvSolid; } #endif } else if( refIdxJ != NOT_VALID ) { Mv singleMv = mrgCtx.mvFieldNeighbours[1][refListId].mv; mrgCtx.mvFieldNeighbours[cnt][refListId].setMvField(singleMv, refIdxJ); #if GDR_ENABLED // GDR: Pairwise single I,J candidate if (isEncodeGdrClean) { mvSolid = mvJSolid && allCandSolidInAbove; mrgCtx.mvPos[cnt][refListId] = Position(0, 0); mrgCtx.mvSolid[cnt][refListId] = mvSolid; mrgCtx.mvValid[cnt][refListId] = cs.isClean(pu.Y().bottomRight(), singleMv, (RefPicList) refListId, refIdxJ); allCandSolidInAbove &= mvSolid; } #endif } } mrgCtx.interDirNeighbours[cnt] = interDir; if( interDir > 0 ) { cnt++; } } // early termination if( cnt == maxNumMergeCand ) { return; } } uint32_t arrayAddr = cnt; int numRefIdx = slice.isInterB() ? std::min(slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1)) : slice.getNumRefIdx(REF_PIC_LIST_0); int r = 0; int refcnt = 0; while (arrayAddr < maxNumMergeCand) { mrgCtx.interDirNeighbours[arrayAddr] = 1; mrgCtx.bcwIdx[arrayAddr] = BCW_DEFAULT; mrgCtx.mvFieldNeighbours[arrayAddr][0].setMvField(Mv(0, 0), r); mrgCtx.useAltHpelIf[arrayAddr] = false; #if GDR_ENABLED // GDR: zero mv(0,0) if (isEncodeGdrClean) { mrgCtx.mvPos[arrayAddr][0] = Position(0, 0); mrgCtx.mvSolid[arrayAddr][0] = allCandSolidInAbove; mrgCtx.mvValid[arrayAddr][0] = cs.isClean(pu.Y().bottomRight(), Mv(0, 0), REF_PIC_LIST_0, r); } #endif if (slice.isInterB()) { mrgCtx.interDirNeighbours[arrayAddr] = 3; mrgCtx.mvFieldNeighbours[arrayAddr][1].setMvField(Mv(0, 0), r); #if GDR_ENABLED // GDR: zero mv(0,0) if (isEncodeGdrClean) { mrgCtx.mvPos[arrayAddr][1] = Position(0, 0); mrgCtx.mvSolid[arrayAddr][1] = allCandSolidInAbove; mrgCtx.mvValid[arrayAddr][1] = cs.isClean(pu.Y().bottomRight(), Mv(0, 0), (RefPicList) REF_PIC_LIST_1, r); } #endif } arrayAddr++; if (refcnt == numRefIdx - 1) { r = 0; } else { ++r; ++refcnt; } } mrgCtx.numValidMergeCand = arrayAddr; } bool PU::checkDMVRCondition(const PredictionUnit& pu) { if (pu.cs->sps->getUseDMVR() && !pu.cs->picHeader->getDmvrDisabledFlag()) { const int refIdx0 = pu.refIdx[REF_PIC_LIST_0]; const int refIdx1 = pu.refIdx[REF_PIC_LIST_1]; const bool ref0IsScaled = refIdx0 < 0 || refIdx0 >= MAX_NUM_ACTIVE_REF ? false : pu.cu->slice->getRefPic(REF_PIC_LIST_0, refIdx0)->isRefScaled(pu.cs->pps); const bool ref1IsScaled = refIdx1 < 0 || refIdx1 >= MAX_NUM_ACTIVE_REF ? false : pu.cu->slice->getRefPic(REF_PIC_LIST_1, refIdx1)->isRefScaled(pu.cs->pps); return pu.mergeFlag && pu.mergeType == MergeType::DEFAULT_N && !pu.ciipFlag && !pu.cu->affine && !pu.mmvdMergeFlag && !pu.cu->mmvdSkip && PU::isSimpleSymmetricBiPred(pu) && PU::dmvrBdofSizeCheck(pu) && !ref0IsScaled && !ref1IsScaled; } else { return false; } } static int xGetDistScaleFactor(const int &currPoc, const int &currRefPoc, const int &colPoc, const int &colRefPoc) { const int diffPocD = colPoc - colRefPoc; const int diffPocB = currPoc - currRefPoc; if (diffPocD == diffPocB) { return 4096; } else { const int tdB = Clip3(-128, 127, diffPocB); const int tdD = Clip3(-128, 127, diffPocD); const int x = (0x4000 + abs(tdD / 2)) / tdD; const int scale = Clip3(-4096, 4095, (tdB * x + 32) >> 6); return scale; } } int convertMvFixedToFloat(int32_t val) { const int sign = val >> 31; const int scale = floorLog2((val ^ sign) | MV_MANTISSA_UPPER_LIMIT) - (MV_MANTISSA_BITCOUNT - 1); int exponent; int mantissa; if (scale >= 0) { int round = (1 << scale) >> 1; int n = (val + round) >> scale; exponent = scale + ((n ^ sign) >> (MV_MANTISSA_BITCOUNT - 1)); mantissa = (n & MV_MANTISSA_UPPER_LIMIT) | (sign * (1 << (MV_MANTISSA_BITCOUNT - 1))); } else { exponent = 0; mantissa = val; } return exponent | (mantissa * (1 << MV_EXPONENT_BITCOUNT)); } int convertMvFloatToFixed(int val) { const int exponent = val & MV_EXPONENT_MASK; const int mantissa = val >> MV_EXPONENT_BITCOUNT; return exponent == 0 ? mantissa : (mantissa ^ MV_MANTISSA_LIMIT) * (1 << (exponent - 1)); } int roundMvComp(int x) { return convertMvFloatToFixed(convertMvFixedToFloat(x)); } int PU::getDistScaleFactor(const int &currPOC, const int &currRefPOC, const int &colPOC, const int &colRefPOC) { return xGetDistScaleFactor(currPOC, currRefPOC, colPOC, colRefPOC); } void PU::getInterMMVDMergeCandidates(const PredictionUnit &pu, MergeCtx &mrgCtx) { int k; int currBaseNum = 0; const uint16_t maxNumMergeCand = mrgCtx.numValidMergeCand; #if GDR_ENABLED const CodingStructure &cs = *pu.cs; const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); #endif #if GDR_ENABLED for (int k = 0; k < MmvdIdx::BASE_MV_NUM; k++) { mrgCtx.mmvdSolid[k][0] = true; mrgCtx.mmvdSolid[k][1] = true; mrgCtx.mmvdValid[k][0] = true; mrgCtx.mmvdValid[k][1] = true; } #endif for (k = 0; k < maxNumMergeCand; k++) { for (const auto l: { REF_PIC_LIST_0, REF_PIC_LIST_1 }) { const int refIdx = mrgCtx.mvFieldNeighbours[k][l].refIdx; mrgCtx.mmvdBaseMv[currBaseNum][l] = refIdx >= 0 ? mrgCtx.mvFieldNeighbours[k][l] : MvField(Mv(0, 0), -1); #if GDR_ENABLED if (isEncodeGdrClean) { mrgCtx.mmvdSolid[currBaseNum][l] = refIdx < 0 || mrgCtx.mvSolid[k][l]; } #endif } mrgCtx.mmvdUseAltHpelIf[currBaseNum] = mrgCtx.useAltHpelIf[k]; currBaseNum++; if (currBaseNum == MmvdIdx::BASE_MV_NUM) { break; } } } bool PU::getColocatedMVP(const PredictionUnit &pu, const RefPicList &eRefPicList, const Position &_pos, Mv& rcMv, const int &refIdx, bool sbFlag) { // don't perform MV compression when generally disabled or subPuMvp is used const unsigned scale = 4 * std::max(1, 4 * AMVP_DECIMATION_FACTOR / 4); const unsigned mask = ~( scale - 1 ); const Position pos = Position{ PosType( _pos.x & mask ), PosType( _pos.y & mask ) }; const Slice &slice = *pu.cs->slice; // use coldir. const Picture* const pColPic = slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx()); if( !pColPic ) { return false; } // Check the position of colocated block is within a subpicture const SubPic &curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos()); if (curSubPic.getTreatedAsPicFlag()) { if (!curSubPic.isContainingPos(pos)) { return false; } } RefPicList eColRefPicList = slice.getCheckLDC() ? eRefPicList : RefPicList(slice.getColFromL0Flag()); const MotionInfo& mi = pColPic->cs->getMotionInfo( pos ); if( !mi.isInter ) { return false; } if (mi.isIBCmot) { return false; } if (CU::isIBC(*pu.cu)) { return false; } int colRefIdx = mi.refIdx[eColRefPicList]; if (sbFlag && !slice.getCheckLDC()) { eColRefPicList = eRefPicList; colRefIdx = mi.refIdx[eColRefPicList]; if (colRefIdx < 0) { return false; } } else { if (colRefIdx < 0) { eColRefPicList = RefPicList(1 - eColRefPicList); colRefIdx = mi.refIdx[eColRefPicList]; if (colRefIdx < 0) { return false; } } } const Slice *pColSlice = nullptr; for( const auto s : pColPic->slices ) { if( s->getIndependentSliceIdx() == mi.sliceIdx ) { pColSlice = s; break; } } CHECK( pColSlice == nullptr, "Slice segment not found" ); const Slice &colSlice = *pColSlice; const bool isCurrRefLongTerm = slice.getRefPic(eRefPicList, refIdx)->longTerm; const bool isColRefLongTerm = colSlice.getIsUsedAsLongTerm(eColRefPicList, colRefIdx); if (isCurrRefLongTerm != isColRefLongTerm) { return false; } // Scale the vector. Mv cColMv = mi.mv[eColRefPicList]; cColMv.setHor(roundMvComp(cColMv.getHor())); cColMv.setVer(roundMvComp(cColMv.getVer())); if (isCurrRefLongTerm /*|| isColRefLongTerm*/) { rcMv = cColMv; rcMv.clipToStorageBitDepth(); } else { const int currPOC = slice.getPOC(); const int colPOC = colSlice.getPOC(); const int colRefPOC = colSlice.getRefPOC(eColRefPicList, colRefIdx); const int currRefPOC = slice.getRefPic(eRefPicList, refIdx)->getPOC(); const int distscale = xGetDistScaleFactor(currPOC, currRefPOC, colPOC, colRefPOC); if (distscale == 4096) { rcMv = cColMv; rcMv.clipToStorageBitDepth(); } else { rcMv = cColMv.getScaledMv(distscale); } } return true; } bool PU::isDiffMER(const Position &pos1, const Position &pos2, const unsigned plevel) { const unsigned xN = pos1.x; const unsigned yN = pos1.y; const unsigned xP = pos2.x; const unsigned yP = pos2.y; if ((xN >> plevel) != (xP >> plevel)) { return true; } if ((yN >> plevel) != (yP >> plevel)) { return true; } return false; } bool PU::addNeighborMv(const Mv& currMv, static_vector& neighborMvs) { for (const auto& cand : neighborMvs) { if (currMv == cand) { return false; } } neighborMvs.push_back(currMv); return true; } void PU::getIbcMVPsEncOnly(PredictionUnit &pu, static_vector& mvPred) { const PreCalcValues &pcv = *pu.cs->pcv; const int cuWidth = pu.blocks[COMPONENT_Y].width; const int cuHeight = pu.blocks[COMPONENT_Y].height; const int log2UnitWidth = floorLog2(pcv.minCUWidth); const int log2UnitHeight = floorLog2(pcv.minCUHeight); const int totalAboveUnits = (cuWidth >> log2UnitWidth) + 1; const int totalLeftUnits = (cuHeight >> log2UnitHeight) + 1; Position posLT = pu.Y().topLeft(); // above-left const PredictionUnit *aboveLeftPU = pu.cs->getPURestricted(posLT.offset(-1, -1), pu, ChannelType::LUMA); if (aboveLeftPU && CU::isIBC(*aboveLeftPU->cu)) { addNeighborMv(aboveLeftPU->bv, mvPred); } // above neighbors for (uint32_t dx = 0; dx < totalAboveUnits && mvPred.size() < mvPred.max_size(); dx++) { const PredictionUnit *tmpPU = pu.cs->getPURestricted(posLT.offset((dx << log2UnitWidth), -1), pu, ChannelType::LUMA); if (tmpPU && CU::isIBC(*tmpPU->cu)) { addNeighborMv(tmpPU->bv, mvPred); } } // left neighbors for (uint32_t dy = 0; dy < totalLeftUnits && mvPred.size() < mvPred.max_size(); dy++) { const PredictionUnit *tmpPU = pu.cs->getPURestricted(posLT.offset(-1, (dy << log2UnitHeight)), pu, ChannelType::LUMA); if (tmpPU && CU::isIBC(*tmpPU->cu)) { addNeighborMv(tmpPU->bv, mvPred); } } size_t numAvaiCandInLUT = pu.cs->motionLut.lutIbc.size(); for (uint32_t cand = 0; cand < numAvaiCandInLUT && mvPred.size() < mvPred.max_size(); cand++) { MotionInfo neibMi = pu.cs->motionLut.lutIbc[cand]; addNeighborMv(neibMi.bv, mvPred); } std::array isBvCandDerived; isBvCandDerived.fill(false); auto curNbPred = mvPred.size(); if (curNbPred < mvPred.max_size()) { do { curNbPred = mvPred.size(); for (uint32_t idx = 0; idx < curNbPred && mvPred.size() < mvPred.max_size(); idx++) { if (!isBvCandDerived[idx]) { Mv derivedBv; if (getDerivedBV(pu, mvPred[idx], derivedBv)) { addNeighborMv(derivedBv, mvPred); } isBvCandDerived[idx] = true; } } } while (mvPred.size() > curNbPred && mvPred.size() < mvPred.max_size()); } } bool PU::getDerivedBV(PredictionUnit &pu, const Mv& currentMv, Mv& derivedMv) { int cuPelX = pu.lumaPos().x; int cuPelY = pu.lumaPos().y; int rX = cuPelX + currentMv.getHor(); int rY = cuPelY + currentMv.getVer(); int offsetX = currentMv.getHor(); int offsetY = currentMv.getVer(); if( rX < 0 || rY < 0 || rX >= pu.cs->slice->getPPS()->getPicWidthInLumaSamples() || rY >= pu.cs->slice->getPPS()->getPicHeightInLumaSamples() ) { return false; } const PredictionUnit *neibRefPU = nullptr; neibRefPU = pu.cs->getPURestricted(pu.lumaPos().offset(offsetX, offsetY), pu, ChannelType::LUMA); bool isIBC = (neibRefPU) ? CU::isIBC(*neibRefPU->cu) : 0; if (isIBC) { derivedMv = neibRefPU->bv; derivedMv += currentMv; } return isIBC; } /** * Constructs a list of candidates for IBC AMVP (See specification, section "Derivation process for motion vector predictor candidates") */ void PU::fillIBCMvpCand(PredictionUnit &pu, AMVPInfo &amvpInfo) { AMVPInfo *pInfo = &amvpInfo; pInfo->numCand = 0; MergeCtx mergeCtx; PU::getIBCMergeCandidates(pu, mergeCtx, AMVP_MAX_NUM_CANDS - 1); int candIdx = 0; while (pInfo->numCand < AMVP_MAX_NUM_CANDS) { pInfo->mvCand[pInfo->numCand] = mergeCtx.mvFieldNeighbours[candIdx][0].mv; ; pInfo->numCand++; candIdx++; } for (Mv &mv : pInfo->mvCand) { mv.roundIbcPrecInternal2Amvr(pu.cu->imv); } } /** Constructs a list of candidates for AMVP (See specification, section "Derivation process for motion vector predictor * candidates") \param uiPartIdx \param uiPartAddr \param eRefPicList \param refIdx \param pInfo */ void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AMVPInfo &amvpInfo) { CodingStructure &cs = *pu.cs; AMVPInfo *pInfo = &amvpInfo; pInfo->numCand = 0; if (refIdx < 0) { return; } //-- Get Spatial MV Position posLT = pu.Y().topLeft(); Position posRT = pu.Y().topRight(); Position posLB = pu.Y().bottomLeft(); #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); bool &allCandSolidInAbove = amvpInfo.allCandSolidInAbove; #endif { bool added = addMVPCandUnscaled(pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, *pInfo); if (!added) { added = addMVPCandUnscaled(pu, eRefPicList, refIdx, posLB, MD_LEFT, *pInfo); } } // Above predictor search { bool added = addMVPCandUnscaled(pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, *pInfo); if (!added) { added = addMVPCandUnscaled(pu, eRefPicList, refIdx, posRT, MD_ABOVE, *pInfo); if (!added) { addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, *pInfo ); } } } for( int i = 0; i < pInfo->numCand; i++ ) { pInfo->mvCand[i].roundTransPrecInternal2Amvr(pu.cu->imv); } if( pInfo->numCand == 2 ) { if( pInfo->mvCand[0] == pInfo->mvCand[1] ) { pInfo->numCand = 1; } } if (cs.picHeader->getEnableTMVPFlag() && pInfo->numCand < AMVP_MAX_NUM_CANDS && (pu.lumaSize().width + pu.lumaSize().height > 12)) { // Get Temporal Motion Predictor const int refIdxCol = refIdx; Position posRB = pu.Y().bottomRight().offset(-3, -3); const PreCalcValues& pcv = *cs.pcv; Position posC0; bool C0Avail = false; Position posC1 = pu.Y().center(); Mv cColMv; bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight); const SubPic &curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos()); if (curSubPic.getTreatedAsPicFlag()) { boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() && (posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom()); } if (boundaryCond) { int posYInCtu = posRB.y & pcv.maxCUHeightMask; if (posYInCtu + 4 < pcv.maxCUHeight) { posC0 = posRB.offset(4, 4); C0Avail = true; } } if ((C0Avail && getColocatedMVP(pu, eRefPicList, posC0, cColMv, refIdxCol, false)) || getColocatedMVP(pu, eRefPicList, posC1, cColMv, refIdxCol, false)) { #if GDR_ENABLED if (isEncodeGdrClean) { Mv ccMv; bool posC0inCurPicSolid = cs.isClean(posC0, ChannelType::LUMA); bool posC1inCurPicSolid = cs.isClean(posC1, ChannelType::LUMA); bool posC0inRefPicSolid = cs.isClean(posC0, REF_PIC_LIST_0, refIdxCol); bool posC1inRefPicSolid = cs.isClean(posC1, REF_PIC_LIST_0, refIdxCol); bool isMVP0exist = C0Avail && getColocatedMVP(pu, eRefPicList, posC0, ccMv, refIdxCol, false); Position pos = isMVP0exist ? posC0 : posC1; pInfo->mvPos[pInfo->numCand] = pos; pInfo->mvType[pInfo->numCand] = isMVP0exist ? MVP_TMVP_C0 : MVP_TMVP_C1; pInfo->mvSolid[pInfo->numCand] = allCandSolidInAbove && (isMVP0exist ? (posC0inCurPicSolid && posC0inRefPicSolid) : (posC1inCurPicSolid && posC1inRefPicSolid)); allCandSolidInAbove = allCandSolidInAbove && pInfo->mvSolid[pInfo->numCand]; } #endif cColMv.roundTransPrecInternal2Amvr(pu.cu->imv); pInfo->mvCand[pInfo->numCand++] = cColMv; } } if (pInfo->numCand < AMVP_MAX_NUM_CANDS) { const int currRefPOC = cs.slice->getRefPic(eRefPicList, refIdx)->getPOC(); addAMVPHMVPCand(pu, eRefPicList, currRefPOC, *pInfo); } if (pInfo->numCand > AMVP_MAX_NUM_CANDS) { pInfo->numCand = AMVP_MAX_NUM_CANDS; } while (pInfo->numCand < AMVP_MAX_NUM_CANDS) { #if GDR_ENABLED if (isEncodeGdrClean) { pInfo->mvType[pInfo->numCand] = MVP_ZERO; allCandSolidInAbove = pInfo->mvSolid[pInfo->numCand] = true && allCandSolidInAbove; } #endif pInfo->mvCand[pInfo->numCand] = Mv( 0, 0 ); pInfo->numCand++; } for (Mv &mv : pInfo->mvCand) { mv.roundTransPrecInternal2Amvr(pu.cu->imv); } } bool PU::addAffineMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &refPicList, const int &refIdx, const Position &pos, const MvpDir &dir, AffineAMVPInfo &affiAMVPInfo ) { CodingStructure &cs = *pu.cs; const PredictionUnit *neibPU = nullptr; Position neibPos; #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); #endif switch ( dir ) { case MD_LEFT: neibPos = pos.offset( -1, 0 ); break; case MD_ABOVE: neibPos = pos.offset( 0, -1 ); break; case MD_ABOVE_RIGHT: neibPos = pos.offset( 1, -1 ); break; case MD_BELOW_LEFT: neibPos = pos.offset( -1, 1 ); break; case MD_ABOVE_LEFT: neibPos = pos.offset( -1, -1 ); break; default: break; } neibPU = cs.getPURestricted( neibPos, pu, pu.chType ); if (neibPU == nullptr || !neibPU->isAffineBlock()) { return false; } Mv outputAffineMv[3]; #if GDR_ENABLED bool outputAffineMvSolid[3]; MvpType outputAffineMvType[3]; Position outputAffineMvPos[3]; #endif const MotionInfo& neibMi = neibPU->getMotionInfo( neibPos ); const int currRefPOC = cs.slice->getRefPic( refPicList, refIdx )->getPOC(); const RefPicList refPicList2nd = (refPicList == REF_PIC_LIST_0) ? REF_PIC_LIST_1 : REF_PIC_LIST_0; for ( int predictorSource = 0; predictorSource < 2; predictorSource++ ) // examine the indicated reference picture list, then if not available, examine the other list. { const RefPicList eRefPicListIndex = (predictorSource == 0) ? refPicList : refPicList2nd; const int neibRefIdx = neibMi.refIdx[eRefPicListIndex]; if ( ((neibPU->interDir & (eRefPicListIndex + 1)) == 0) || pu.cu->slice->getRefPOC( eRefPicListIndex, neibRefIdx ) != currRefPOC ) { continue; } #if GDR_ENABLED // note : get MV from neihgbor of neibPu (LB, RB) and save to outputAffineMv if (isEncodeGdrClean) { xInheritedAffineMv(pu, neibPU, eRefPicListIndex, outputAffineMv, outputAffineMvSolid, outputAffineMvType, outputAffineMvPos); } else { xInheritedAffineMv(pu, neibPU, eRefPicListIndex, outputAffineMv); } #else xInheritedAffineMv( pu, neibPU, eRefPicListIndex, outputAffineMv ); #endif outputAffineMv[0].roundAffinePrecInternal2Amvr(pu.cu->imv); outputAffineMv[1].roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0]; affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1]; #if GDR_ENABLED bool neighClean = true; if (isEncodeGdrClean) { neighClean = cs.isClean(neibPU->Y().pos(), ChannelType::LUMA); affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = neighClean && outputAffineMvSolid[0]; affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = neighClean && outputAffineMvSolid[1]; affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = outputAffineMvType[0]; affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = outputAffineMvType[1]; affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = outputAffineMvPos[0]; affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = outputAffineMvPos[1]; } #endif if (pu.cu->affineType == AffineModel::_6_PARAMS) { outputAffineMv[2].roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2]; #if GDR_ENABLED if (isEncodeGdrClean) { neighClean = cs.isClean(neibPU->Y().pos(), ChannelType::LUMA); affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = neighClean && outputAffineMvSolid[2]; affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = outputAffineMvType[2]; affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = outputAffineMvPos[2]; } #endif } affiAMVPInfo.numCand++; return true; } return false; } #if GDR_ENABLED void PU::xInheritedAffineMv(const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3], bool rcMvSolid[3], MvpType rcMvType[3], Position rcMvPos[3]) { const CodingStructure &cs = *pu.cs; const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); int posNeiX = puNeighbour->Y().pos().x; int posNeiY = puNeighbour->Y().pos().y; int posCurX = pu.Y().pos().x; int posCurY = pu.Y().pos().y; int neiW = puNeighbour->Y().width; int curW = pu.Y().width; int neiH = puNeighbour->Y().height; int curH = pu.Y().height; Mv mvLT, mvRT, mvLB; mvLT = puNeighbour->mvAffi[eRefPicList][0]; mvRT = puNeighbour->mvAffi[eRefPicList][1]; mvLB = puNeighbour->mvAffi[eRefPicList][2]; #if GDR_ENABLED bool neighClean = true; if (isEncodeGdrClean) { neighClean = cs.isClean(puNeighbour->Y().pos(), ChannelType::LUMA); rcMvSolid[0] = neighClean; rcMvSolid[1] = neighClean; rcMvSolid[2] = neighClean; rcMvType[0] = AFFINE_INHERIT; rcMvType[1] = AFFINE_INHERIT; rcMvType[2] = AFFINE_INHERIT; rcMvPos[0] = puNeighbour->Y().pos(); rcMvPos[1] = puNeighbour->Y().pos(); rcMvPos[2] = puNeighbour->Y().pos(); } #endif bool isTopCtuBoundary = false; if ((posNeiY + neiH) % pu.cs->sps->getCTUSize() == 0 && (posNeiY + neiH) == posCurY) { // use bottom-left and bottom-right sub-block MVs for inheritance const Position posRB = puNeighbour->Y().bottomRight(); const Position posLB = puNeighbour->Y().bottomLeft(); mvLT = puNeighbour->getMotionInfo(posLB).mv[eRefPicList]; mvRT = puNeighbour->getMotionInfo(posRB).mv[eRefPicList]; #if GDR_ENABLED if (isEncodeGdrClean) { neighClean = cs.isClean(puNeighbour->Y().pos(), ChannelType::LUMA); rcMvSolid[0] = cs.isClean(posLB, ChannelType::LUMA); rcMvSolid[1] = cs.isClean(posRB, ChannelType::LUMA); rcMvSolid[2] = neighClean; rcMvType[0] = AFFINE_INHERIT_LB_RB; rcMvType[1] = AFFINE_INHERIT_LB_RB; rcMvType[2] = AFFINE_INHERIT_LB_RB; rcMvPos[0] = posLB; rcMvPos[1] = posRB; rcMvPos[2] = puNeighbour->Y().pos(); } #endif posNeiY += neiH; isTopCtuBoundary = true; } int shift = MAX_CU_DEPTH; int dmvHorX, dmvHorY, dmvVerX, dmvVerY; dmvHorX = (mvRT - mvLT).getHor() << (shift - floorLog2(neiW)); dmvHorY = (mvRT - mvLT).getVer() << (shift - floorLog2(neiW)); if (puNeighbour->cu->affineType == AffineModel::_6_PARAMS && !isTopCtuBoundary) { dmvVerX = (mvLB - mvLT).getHor() << (shift - floorLog2(neiH)); dmvVerY = (mvLB - mvLT).getVer() << (shift - floorLog2(neiH)); } else { dmvVerX = -dmvHorY; dmvVerY = dmvHorX; } int mvScaleHor = mvLT.getHor() << shift; int mvScaleVer = mvLT.getVer() << shift; // v0 rcMv[0].hor = mvScaleHor + dmvHorX * (posCurX - posNeiX) + dmvVerX * (posCurY - posNeiY); rcMv[0].ver = mvScaleVer + dmvHorY * (posCurX - posNeiX) + dmvVerY * (posCurY - posNeiY); rcMv[0] >>= shift; rcMv[0].clipToStorageBitDepth(); // v1 rcMv[1].hor = mvScaleHor + dmvHorX * (posCurX + curW - posNeiX) + dmvVerX * (posCurY - posNeiY); rcMv[1].ver = mvScaleVer + dmvHorY * (posCurX + curW - posNeiX) + dmvVerY * (posCurY - posNeiY); rcMv[1] >>= shift; rcMv[1].clipToStorageBitDepth(); // v2 if (pu.cu->affineType == AffineModel::_6_PARAMS) { rcMv[2].hor = mvScaleHor + dmvHorX * (posCurX - posNeiX) + dmvVerX * (posCurY + curH - posNeiY); rcMv[2].ver = mvScaleVer + dmvHorY * (posCurX - posNeiX) + dmvVerY * (posCurY + curH - posNeiY); rcMv[2] >>= shift; rcMv[2].clipToStorageBitDepth(); } } #endif void PU::xInheritedAffineMv( const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3] ) { int posNeiX = puNeighbour->Y().pos().x; int posNeiY = puNeighbour->Y().pos().y; int posCurX = pu.Y().pos().x; int posCurY = pu.Y().pos().y; int neiW = puNeighbour->Y().width; int curW = pu.Y().width; int neiH = puNeighbour->Y().height; int curH = pu.Y().height; Mv mvLT, mvRT, mvLB; mvLT = puNeighbour->mvAffi[eRefPicList][0]; mvRT = puNeighbour->mvAffi[eRefPicList][1]; mvLB = puNeighbour->mvAffi[eRefPicList][2]; bool isTopCtuBoundary = false; if ( (posNeiY + neiH) % pu.cs->sps->getCTUSize() == 0 && (posNeiY + neiH) == posCurY ) { // use bottom-left and bottom-right sub-block MVs for inheritance const Position posRB = puNeighbour->Y().bottomRight(); const Position posLB = puNeighbour->Y().bottomLeft(); mvLT = puNeighbour->getMotionInfo( posLB ).mv[eRefPicList]; mvRT = puNeighbour->getMotionInfo( posRB ).mv[eRefPicList]; posNeiY += neiH; isTopCtuBoundary = true; } int shift = MAX_CU_DEPTH; int dmvHorX, dmvHorY, dmvVerX, dmvVerY; dmvHorX = (mvRT - mvLT).getHor() * (1 << (shift - floorLog2(neiW))); dmvHorY = (mvRT - mvLT).getVer() * (1 << (shift - floorLog2(neiW))); if (puNeighbour->cu->affineType == AffineModel::_6_PARAMS && !isTopCtuBoundary) { dmvVerX = (mvLB - mvLT).getHor() * (1 << (shift - floorLog2(neiH))); dmvVerY = (mvLB - mvLT).getVer() * (1 << (shift - floorLog2(neiH))); } else { dmvVerX = -dmvHorY; dmvVerY = dmvHorX; } int mvScaleHor = mvLT.getHor() * (1 << shift); int mvScaleVer = mvLT.getVer() * (1 << shift); // v0 rcMv[0].hor = mvScaleHor + dmvHorX * (posCurX - posNeiX) + dmvVerX * (posCurY - posNeiY); rcMv[0].ver = mvScaleVer + dmvHorY * (posCurX - posNeiX) + dmvVerY * (posCurY - posNeiY); rcMv[0] >>= shift; rcMv[0].clipToStorageBitDepth(); // v1 rcMv[1].hor = mvScaleHor + dmvHorX * (posCurX + curW - posNeiX) + dmvVerX * (posCurY - posNeiY); rcMv[1].ver = mvScaleVer + dmvHorY * (posCurX + curW - posNeiX) + dmvVerY * (posCurY - posNeiY); rcMv[1] >>= shift; rcMv[1].clipToStorageBitDepth(); // v2 if (pu.cu->affineType == AffineModel::_6_PARAMS) { rcMv[2].hor = mvScaleHor + dmvHorX * (posCurX - posNeiX) + dmvVerX * (posCurY + curH - posNeiY); rcMv[2].ver = mvScaleVer + dmvHorY * (posCurX - posNeiX) + dmvVerY * (posCurY + curH - posNeiY); rcMv[2] >>= shift; rcMv[2].clipToStorageBitDepth(); } } void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AffineAMVPInfo &affiAMVPInfo) { affiAMVPInfo.numCand = 0; if (refIdx < 0) { return; } #if GDR_ENABLED const CodingStructure &cs = *pu.cs; const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); bool &allCandSolidInAbove = affiAMVPInfo.allCandSolidInAbove; if (isEncodeGdrClean) { allCandSolidInAbove = true; affiAMVPInfo.allCandSolidInAbove = true; for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) { affiAMVPInfo.mvSolidLT[i] = true; affiAMVPInfo.mvSolidRT[i] = true; affiAMVPInfo.mvSolidLB[i] = true; } } #endif // insert inherited affine candidates Mv outputAffineMv[3]; #if GDR_ENABLED bool outputAffineMvSolid[3]; MvpType outputAffineMvType[3]; Position outputAffineMvPos[3]; #endif Position posLT = pu.Y().topLeft(); Position posRT = pu.Y().topRight(); Position posLB = pu.Y().bottomLeft(); // check left neighbor if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, affiAMVPInfo ) ) { addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, affiAMVPInfo ); } // check above neighbor if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, affiAMVPInfo ) ) { if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, affiAMVPInfo ) ) { addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, affiAMVPInfo ); } } if ( affiAMVPInfo.numCand >= AMVP_MAX_NUM_CANDS ) { for (int i = 0; i < affiAMVPInfo.numCand; i++) { affiAMVPInfo.mvCandLT[i].roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandRT[i].roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandLB[i].roundAffinePrecInternal2Amvr(pu.cu->imv); } return; } // insert constructed affine candidates int cornerMVPattern = 0; //------------------- V0 (START) -------------------// AMVPInfo amvpInfo0; amvpInfo0.numCand = 0; #if GDR_ENABLED if (isEncodeGdrClean) { amvpInfo0.allCandSolidInAbove = true; for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) { amvpInfo0.mvSolid[i] = true; amvpInfo0.mvValid[i] = true; } } #endif // A->C: Above Left, Above, Left addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, amvpInfo0 ); if ( amvpInfo0.numCand < 1 ) { addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE, amvpInfo0 ); } if ( amvpInfo0.numCand < 1 ) { addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_LEFT, amvpInfo0 ); } cornerMVPattern = cornerMVPattern | amvpInfo0.numCand; //------------------- V1 (START) -------------------// AMVPInfo amvpInfo1; amvpInfo1.numCand = 0; #if GDR_ENABLED if (isEncodeGdrClean) { amvpInfo1.allCandSolidInAbove = true; for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) { amvpInfo1.mvSolid[i] = true; amvpInfo1.mvValid[i] = true; } } #endif // D->E: Above, Above Right addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, amvpInfo1 ); if ( amvpInfo1.numCand < 1 ) { addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, amvpInfo1 ); } cornerMVPattern = cornerMVPattern | (amvpInfo1.numCand << 1); //------------------- V2 (START) -------------------// AMVPInfo amvpInfo2; amvpInfo2.numCand = 0; #if GDR_ENABLED if (isEncodeGdrClean) { amvpInfo2.allCandSolidInAbove = true; for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) { amvpInfo2.mvSolid[i] = true; amvpInfo2.mvValid[i] = true; } } #endif // F->G: Left, Below Left addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, amvpInfo2 ); if ( amvpInfo2.numCand < 1 ) { addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, amvpInfo2 ); } cornerMVPattern = cornerMVPattern | (amvpInfo2.numCand << 2); outputAffineMv[0] = amvpInfo0.mvCand[0]; outputAffineMv[1] = amvpInfo1.mvCand[0]; outputAffineMv[2] = amvpInfo2.mvCand[0]; #if GDR_ENABLED if (isEncodeGdrClean) { outputAffineMvSolid[0] = amvpInfo0.mvSolid[0] && allCandSolidInAbove; outputAffineMvSolid[1] = amvpInfo1.mvSolid[0] && allCandSolidInAbove; outputAffineMvSolid[2] = amvpInfo2.mvSolid[0] && allCandSolidInAbove; outputAffineMvPos[0] = amvpInfo0.mvPos[0]; outputAffineMvPos[1] = amvpInfo1.mvPos[0]; outputAffineMvPos[2] = amvpInfo2.mvPos[0]; outputAffineMvType[0] = amvpInfo0.mvType[0]; outputAffineMvType[1] = amvpInfo1.mvType[0]; outputAffineMvType[2] = amvpInfo2.mvType[0]; allCandSolidInAbove = allCandSolidInAbove && outputAffineMvSolid[0] && outputAffineMvSolid[1] && outputAffineMvSolid[2]; } #endif outputAffineMv[0].roundAffinePrecInternal2Amvr(pu.cu->imv); outputAffineMv[1].roundAffinePrecInternal2Amvr(pu.cu->imv); outputAffineMv[2].roundAffinePrecInternal2Amvr(pu.cu->imv); if (cornerMVPattern == 7 || (cornerMVPattern == 3 && pu.cu->affineType == AffineModel::_4_PARAMS)) { affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0]; affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1]; affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2]; #if GDR_ENABLED if (isEncodeGdrClean) { affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = outputAffineMvSolid[0] && allCandSolidInAbove; affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = outputAffineMvSolid[1] && allCandSolidInAbove; affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = outputAffineMvSolid[2] && allCandSolidInAbove; affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = outputAffineMvPos[0]; affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = outputAffineMvPos[1]; affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = outputAffineMvPos[2]; affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = outputAffineMvType[0]; affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = outputAffineMvType[1]; affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = outputAffineMvType[2]; allCandSolidInAbove = allCandSolidInAbove && outputAffineMvSolid[0] && outputAffineMvSolid[1] && outputAffineMvSolid[2]; } #endif affiAMVPInfo.numCand++; } if ( affiAMVPInfo.numCand < 2 ) { // check corner MVs for ( int i = 2; i >= 0 && affiAMVPInfo.numCand < AMVP_MAX_NUM_CANDS; i-- ) { if ( cornerMVPattern & (1 << i) ) // MV i exist { affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[i]; affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[i]; affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[i]; #if GDR_ENABLED if (isEncodeGdrClean) { affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = outputAffineMvSolid[i] && allCandSolidInAbove; affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = outputAffineMvSolid[i] && allCandSolidInAbove; affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = outputAffineMvSolid[i] && allCandSolidInAbove; affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = outputAffineMvPos[i]; affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = outputAffineMvPos[i]; affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = outputAffineMvPos[i]; affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = outputAffineMvType[i]; affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = outputAffineMvType[i]; affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = outputAffineMvType[i]; allCandSolidInAbove = allCandSolidInAbove && outputAffineMvSolid[i]; } #endif affiAMVPInfo.numCand++; } } // Get Temporal Motion Predictor if ( affiAMVPInfo.numCand < 2 && pu.cs->picHeader->getEnableTMVPFlag() ) { const int refIdxCol = refIdx; Position posRB = pu.Y().bottomRight().offset( -3, -3 ); const PreCalcValues& pcv = *pu.cs->pcv; Position posC0; bool C0Avail = false; Position posC1 = pu.Y().center(); Mv cColMv; bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight); const SubPic &curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos()); if (curSubPic.getTreatedAsPicFlag()) { boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() && (posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom()); } if (boundaryCond) { int posYInCtu = posRB.y & pcv.maxCUHeightMask; if (posYInCtu + 4 < pcv.maxCUHeight) { posC0 = posRB.offset(4, 4); C0Avail = true; } } if ( ( C0Avail && getColocatedMVP( pu, eRefPicList, posC0, cColMv, refIdxCol, false ) ) || getColocatedMVP( pu, eRefPicList, posC1, cColMv, refIdxCol, false ) ) { cColMv.roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = cColMv; affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = cColMv; affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = cColMv; #if GDR_ENABLED if (isEncodeGdrClean) { Mv ccMv; bool posC0inCurPicSolid = cs.isClean(posC0, ChannelType::LUMA); bool posC1inCurPicSolid = cs.isClean(posC1, ChannelType::LUMA); bool posC0inRefPicSolid = cs.isClean(posC0, eRefPicList, refIdxCol); bool posC1inRefPicSolid = cs.isClean(posC1, eRefPicList, refIdxCol); bool isMVP0exist = C0Avail && getColocatedMVP(pu, eRefPicList, posC0, ccMv, refIdxCol, false); if (isMVP0exist) { affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = posC0inCurPicSolid && posC0inRefPicSolid && allCandSolidInAbove; affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = posC0inCurPicSolid && posC0inRefPicSolid && allCandSolidInAbove; affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = posC0inCurPicSolid && posC0inRefPicSolid && allCandSolidInAbove; affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = posC0; affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = posC0; affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = posC0; affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = MVP_TMVP_C0; affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = MVP_TMVP_C0; affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = MVP_TMVP_C0; allCandSolidInAbove = allCandSolidInAbove && affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand]; } else { affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = posC1inCurPicSolid && posC1inRefPicSolid && allCandSolidInAbove; affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = posC1inCurPicSolid && posC1inRefPicSolid && allCandSolidInAbove; affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = posC1inCurPicSolid && posC1inRefPicSolid && allCandSolidInAbove; affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = posC1; affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = posC1; affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = posC1; affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = MVP_TMVP_C1; affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = MVP_TMVP_C1; affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = MVP_TMVP_C1; allCandSolidInAbove = allCandSolidInAbove && affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand]; } } #endif affiAMVPInfo.numCand++; } } if ( affiAMVPInfo.numCand < 2 ) { // add zero MV for ( int i = affiAMVPInfo.numCand; i < AMVP_MAX_NUM_CANDS; i++ ) { affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand].setZero(); affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand].setZero(); affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand].setZero(); #if GDR_ENABLED if (isEncodeGdrClean) { affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = true && allCandSolidInAbove; affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = true && allCandSolidInAbove; affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = true && allCandSolidInAbove; affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = Position(0, 0); affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = Position(0, 0); affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = Position(0, 0); affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = MVP_ZERO; affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = MVP_ZERO; affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = MVP_ZERO; allCandSolidInAbove = allCandSolidInAbove && affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand]; } #endif affiAMVPInfo.numCand++; } } } for (int i = 0; i < affiAMVPInfo.numCand; i++) { affiAMVPInfo.mvCandLT[i].roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandRT[i].roundAffinePrecInternal2Amvr(pu.cu->imv); affiAMVPInfo.mvCandLB[i].roundAffinePrecInternal2Amvr(pu.cu->imv); } } bool PU::addMVPCandUnscaled(const PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, const Position &pos, const MvpDir &eDir, AMVPInfo &info) { CodingStructure &cs = *pu.cs; const PredictionUnit *neibPU = nullptr; Position neibPos; #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); bool &allCandSolidInAbove = info.allCandSolidInAbove; #endif switch (eDir) { case MD_LEFT: neibPos = pos.offset( -1, 0 ); break; case MD_ABOVE: neibPos = pos.offset( 0, -1 ); break; case MD_ABOVE_RIGHT: neibPos = pos.offset( 1, -1 ); break; case MD_BELOW_LEFT: neibPos = pos.offset( -1, 1 ); break; case MD_ABOVE_LEFT: neibPos = pos.offset( -1, -1 ); break; default: break; } neibPU = cs.getPURestricted( neibPos, pu, pu.chType ); if (neibPU == nullptr || !CU::isInter(*neibPU->cu)) { return false; } const MotionInfo& neibMi = neibPU->getMotionInfo( neibPos ); const int currRefPOC = cs.slice->getRefPic(eRefPicList, refIdx)->getPOC(); const RefPicList eRefPicList2nd = ( eRefPicList == REF_PIC_LIST_0 ) ? REF_PIC_LIST_1 : REF_PIC_LIST_0; for( int predictorSource = 0; predictorSource < 2; predictorSource++ ) // examine the indicated reference picture list, then if not available, examine the other list. { const RefPicList eRefPicListIndex = ( predictorSource == 0 ) ? eRefPicList : eRefPicList2nd; const int neibRefIdx = neibMi.refIdx[eRefPicListIndex]; if( neibRefIdx >= 0 && currRefPOC == cs.slice->getRefPOC( eRefPicListIndex, neibRefIdx ) ) { #if GDR_ENABLED if (isEncodeGdrClean) { bool isSolid = cs.isClean(neibPos, ChannelType::LUMA); info.mvSolid[info.numCand] = isSolid && allCandSolidInAbove; info.mvType[info.numCand] = (MvpType)eDir; info.mvPos[info.numCand] = neibPos; allCandSolidInAbove = isSolid && allCandSolidInAbove; } #endif info.mvCand[info.numCand++] = neibMi.mv[eRefPicListIndex]; return true; } } return false; } void PU::addAMVPHMVPCand(const PredictionUnit &pu, const RefPicList eRefPicList, const int currRefPOC, AMVPInfo &info) { const Slice &slice = *(*pu.cs).slice; MotionInfo neibMi; auto &lut = CU::isIBC(*pu.cu) ? pu.cs->motionLut.lutIbc : pu.cs->motionLut.lut; int numAvailCandInLut = (int) lut.size(); int numAllowedCand = std::min(MAX_NUM_HMVP_AVMPCANDS, numAvailCandInLut); const RefPicList eRefPicList2nd = (eRefPicList == REF_PIC_LIST_0) ? REF_PIC_LIST_1 : REF_PIC_LIST_0; #if GDR_ENABLED CodingStructure &cs = *pu.cs; const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); bool &allCandSolidInAbove = info.allCandSolidInAbove; #endif #if GDR_ENABLED bool vbOnCtuBoundary = true; if (isEncodeGdrClean) { vbOnCtuBoundary = (pu.cs->picture->gdrParam.verBoundary == -1) || (pu.cs->picture->gdrParam.verBoundary % pu.cs->sps->getMaxCUWidth() == 0); allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary; } #endif for (int mrgIdx = 1; mrgIdx <= numAllowedCand; mrgIdx++) { if (info.numCand >= AMVP_MAX_NUM_CANDS) { return; } neibMi = lut[mrgIdx - 1]; for (int predictorSource = 0; predictorSource < 2; predictorSource++) { const RefPicList eRefPicListIndex = (predictorSource == 0) ? eRefPicList : eRefPicList2nd; const int neibRefIdx = neibMi.refIdx[eRefPicListIndex]; if (neibRefIdx >= 0 && (CU::isIBC(*pu.cu) || (currRefPOC == slice.getRefPOC(eRefPicListIndex, neibRefIdx)))) { Mv pmv = neibMi.mv[eRefPicListIndex]; pmv.roundTransPrecInternal2Amvr(pu.cu->imv); #if GDR_ENABLED if (isEncodeGdrClean) { info.mvPos[info.numCand] = neibMi.sourcePos; info.mvType[info.numCand] = MVP_HMVP; info.mvSolid[info.numCand] = allCandSolidInAbove && vbOnCtuBoundary; // cs.isClean(neibMi.soPos,ChannelType::LUMA); allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary; } #endif info.mvCand[info.numCand++] = pmv; if (info.numCand >= AMVP_MAX_NUM_CANDS) { return; } } } } } bool PU::isBipredRestriction(const PredictionUnit &pu) { if(pu.cu->lumaSize().width == 4 && pu.cu->lumaSize().height ==4 ) { return true; } /* disable bi-prediction for 4x8/8x4 */ if ( pu.cu->lumaSize().width + pu.cu->lumaSize().height == 12 ) { return true; } return false; } #if GDR_ENABLED void PU::getAffineControlPointCand(const PredictionUnit &pu, MotionInfo mi[4], bool isAvailable[4], int verIdx[4], int8_t bcwIdx, int modelIdx, int verNum, AffineMergeCtx& affMrgType, bool isEncodeGdrClean, bool modelSolid[6]) #else void PU::getAffineControlPointCand(const PredictionUnit& pu, MotionInfo mi[4], bool isAvailable[4], int verIdx[4], int8_t bcwIdx, int modelIdx, int verNum, AffineMergeCtx& affMrgType) #endif { int cuW = pu.Y().width; int cuH = pu.Y().height; int vx, vy; int shift = MAX_CU_DEPTH; int shiftHtoW = shift + floorLog2(cuW) - floorLog2(cuH); // motion info Mv cMv[2][4]; int refIdx[2] = { -1, -1 }; int dir = 0; AffineModel curType = (verNum == 2) ? AffineModel::_4_PARAMS : AffineModel::_6_PARAMS; if ( verNum == 2 ) { int idx0 = verIdx[0], idx1 = verIdx[1]; if ( !isAvailable[idx0] || !isAvailable[idx1] ) { return; } for ( int l = 0; l < 2; l++ ) { if ( mi[idx0].refIdx[l] >= 0 && mi[idx1].refIdx[l] >= 0 ) { // check same refidx and different mv if ( mi[idx0].refIdx[l] == mi[idx1].refIdx[l]) { dir |= (l + 1); refIdx[l] = mi[idx0].refIdx[l]; } } } } else if ( verNum == 3 ) { int idx0 = verIdx[0], idx1 = verIdx[1], idx2 = verIdx[2]; if ( !isAvailable[idx0] || !isAvailable[idx1] || !isAvailable[idx2] ) { return; } for ( int l = 0; l < 2; l++ ) { if ( mi[idx0].refIdx[l] >= 0 && mi[idx1].refIdx[l] >= 0 && mi[idx2].refIdx[l] >= 0 ) { // check same refidx and different mv if ( mi[idx0].refIdx[l] == mi[idx1].refIdx[l] && mi[idx0].refIdx[l] == mi[idx2].refIdx[l]) { dir |= (l + 1); refIdx[l] = mi[idx0].refIdx[l]; } } } } if ( dir == 0 ) { return; } for ( int l = 0; l < 2; l++ ) { if ( dir & (l + 1) ) { for ( int i = 0; i < verNum; i++ ) { cMv[l][verIdx[i]] = mi[verIdx[i]].mv[l]; } // convert to LT, RT[, [LB]] switch ( modelIdx ) { case 0: // 0 : LT, RT, LB break; case 1: // 1 : LT, RT, RB cMv[l][2].hor = cMv[l][3].hor + cMv[l][0].hor - cMv[l][1].hor; cMv[l][2].ver = cMv[l][3].ver + cMv[l][0].ver - cMv[l][1].ver; cMv[l][2].clipToStorageBitDepth(); break; case 2: // 2 : LT, LB, RB cMv[l][1].hor = cMv[l][3].hor + cMv[l][0].hor - cMv[l][2].hor; cMv[l][1].ver = cMv[l][3].ver + cMv[l][0].ver - cMv[l][2].ver; cMv[l][1].clipToStorageBitDepth(); break; case 3: // 3 : RT, LB, RB cMv[l][0].hor = cMv[l][1].hor + cMv[l][2].hor - cMv[l][3].hor; cMv[l][0].ver = cMv[l][1].ver + cMv[l][2].ver - cMv[l][3].ver; cMv[l][0].clipToStorageBitDepth(); break; case 4: // 4 : LT, RT break; case 5: // 5 : LT, LB vx = (cMv[l][0].hor * (1 << shift)) + ((cMv[l][2].ver - cMv[l][0].ver) * (1 << shiftHtoW)); vy = (cMv[l][0].ver * (1 << shift)) - ((cMv[l][2].hor - cMv[l][0].hor) * (1 << shiftHtoW)); cMv[l][1].set( vx, vy ); cMv[l][1] >>= shift; cMv[l][1].clipToStorageBitDepth(); break; default: THROW("Invalid model index!"); break; } } else { for ( int i = 0; i < 4; i++ ) { cMv[l][i].hor = 0; cMv[l][i].ver = 0; } } } for ( int i = 0; i < 3; i++ ) { affMrgType.mvFieldNeighbours[affMrgType.numValidMergeCand][i][0].mv = cMv[0][i]; affMrgType.mvFieldNeighbours[affMrgType.numValidMergeCand][i][0].refIdx = refIdx[0]; affMrgType.mvFieldNeighbours[affMrgType.numValidMergeCand][i][1].mv = cMv[1][i]; affMrgType.mvFieldNeighbours[affMrgType.numValidMergeCand][i][1].refIdx = refIdx[1]; #if GDR_ENABLED if (isEncodeGdrClean) { affMrgType.mvSolid[affMrgType.numValidMergeCand][i][0] = modelSolid[modelIdx]; affMrgType.mvSolid[affMrgType.numValidMergeCand][i][1] = modelSolid[modelIdx]; } #endif } affMrgType.interDirNeighbours[affMrgType.numValidMergeCand] = dir; affMrgType.affineType[affMrgType.numValidMergeCand] = curType; affMrgType.bcwIdx[affMrgType.numValidMergeCand] = (dir == 3) ? bcwIdx : BCW_DEFAULT; affMrgType.numValidMergeCand++; return; } const int getAvailableAffineNeighboursForLeftPredictor( const PredictionUnit &pu, const PredictionUnit* npu[] ) { const Position posLB = pu.Y().bottomLeft(); int num = 0; const unsigned plevel = pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2; const PredictionUnit *puLeftBottom = pu.cs->getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType ); if (puLeftBottom && puLeftBottom->isAffineBlock() && PU::isDiffMER(pu.lumaPos(), posLB.offset(-1, 1), plevel)) { npu[num++] = puLeftBottom; return num; } const PredictionUnit* puLeft = pu.cs->getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType ); if (puLeft && puLeft->isAffineBlock() && PU::isDiffMER(pu.lumaPos(), posLB.offset(-1, 0), plevel)) { npu[num++] = puLeft; return num; } return num; } const int getAvailableAffineNeighboursForAbovePredictor( const PredictionUnit &pu, const PredictionUnit* npu[], int numAffNeighLeft ) { const Position posLT = pu.Y().topLeft(); const Position posRT = pu.Y().topRight(); const unsigned plevel = pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2; int num = numAffNeighLeft; const PredictionUnit* puAboveRight = pu.cs->getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType ); if (puAboveRight && puAboveRight->isAffineBlock() && PU::isDiffMER(pu.lumaPos(), posRT.offset(1, -1), plevel)) { npu[num++] = puAboveRight; return num; } const PredictionUnit* puAbove = pu.cs->getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType ); if (puAbove && puAbove->isAffineBlock() && PU::isDiffMER(pu.lumaPos(), posRT.offset(0, -1), plevel)) { npu[num++] = puAbove; return num; } const PredictionUnit *puAboveLeft = pu.cs->getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType ); if (puAboveLeft && puAboveLeft->isAffineBlock() && PU::isDiffMER(pu.lumaPos(), posLT.offset(-1, -1), plevel)) { npu[num++] = puAboveLeft; return num; } return num; } void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx, const int mrgCandIdx ) { const CodingStructure &cs = *pu.cs; const Slice &slice = *pu.cs->slice; const uint32_t maxNumAffineMergeCand = slice.getPicHeader()->getMaxNumAffineMergeCand(); const unsigned plevel = pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2; #if GDR_ENABLED const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); #endif for ( int i = 0; i < maxNumAffineMergeCand; i++ ) { for ( int mvNum = 0; mvNum < 3; mvNum++ ) { affMrgCtx.mvFieldNeighbours[i][mvNum][0].setMvField(Mv(), -1); affMrgCtx.mvFieldNeighbours[i][mvNum][1].setMvField(Mv(), -1); #if GDR_ENABLED if (isEncodeGdrClean) { affMrgCtx.mvSolid[i][mvNum][0] = true; affMrgCtx.mvSolid[i][mvNum][1] = true; affMrgCtx.mvValid[i][mvNum][0] = true; affMrgCtx.mvValid[i][mvNum][1] = true; } #endif } affMrgCtx.interDirNeighbours[i] = 0; affMrgCtx.affineType[i] = AffineModel::_4_PARAMS; affMrgCtx.mergeType[i] = MergeType::DEFAULT_N; affMrgCtx.bcwIdx[i] = BCW_DEFAULT; } #if GDR_ENABLED if (isEncodeGdrClean) { MergeCtx &mrgCtx = *affMrgCtx.mrgCtx; for (int i = 0; i < MRG_MAX_NUM_CANDS; i++) { mrgCtx.mvSolid[i][0] = true; mrgCtx.mvSolid[i][1] = true; mrgCtx.mvValid[i][0] = true; mrgCtx.mvValid[i][1] = true; } } #endif affMrgCtx.numValidMergeCand = 0; affMrgCtx.maxNumMergeCand = maxNumAffineMergeCand; bool sbTmvpEnableFlag = slice.getSPS()->getSbTMVPEnabledFlag() && !(slice.getPOC() == slice.getRefPic(REF_PIC_LIST_0, 0)->getPOC() && slice.isIRAP()); bool isAvailableSubPu = false; if (sbTmvpEnableFlag && slice.getPicHeader()->getEnableTMVPFlag()) { MergeCtx mrgCtx = *affMrgCtx.mrgCtx; CHECK( mrgCtx.subPuMvpMiBuf.area() == 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized" ); mrgCtx.subPuMvpMiBuf.fill( MotionInfo() ); int pos = 0; // Get spatial MV const Position posCurLB = pu.Y().bottomLeft(); MotionInfo miLeft; //left const PredictionUnit* puLeft = cs.getPURestricted( posCurLB.offset( -1, 0 ), pu, pu.chType ); const bool isAvailableA1 = puLeft && isDiffMER(pu.lumaPos(), posCurLB.offset(-1, 0), plevel) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu ); if ( isAvailableA1 ) { miLeft = puLeft->getMotionInfo( posCurLB.offset( -1, 0 ) ); // get Inter Dir mrgCtx.interDirNeighbours[pos] = miLeft.interDir; // get Mv from Left mrgCtx.mvFieldNeighbours[pos][0].setMvField(miLeft.mv[0], miLeft.refIdx[0]); if ( slice.isInterB() ) { mrgCtx.mvFieldNeighbours[pos][1].setMvField(miLeft.mv[1], miLeft.refIdx[1]); } #if GDR_ENABLED // check if the (puLeft) is in clean area if (isEncodeGdrClean) { mrgCtx.mvSolid[pos][0] = cs.isClean(puLeft->Y().bottomRight(), ChannelType::LUMA); mrgCtx.mvSolid[pos][1] = cs.isClean(puLeft->Y().bottomRight(), ChannelType::LUMA); } #endif pos++; } mrgCtx.numValidMergeCand = pos; isAvailableSubPu = getInterMergeSubPuMvpCand(pu, mrgCtx, pos, 0); if ( isAvailableSubPu ) { for ( int mvNum = 0; mvNum < 3; mvNum++ ) { affMrgCtx.mvFieldNeighbours[affMrgCtx.numValidMergeCand][mvNum][0].setMvField( mrgCtx.mvFieldNeighbours[pos][0].mv, mrgCtx.mvFieldNeighbours[pos][0].refIdx); affMrgCtx.mvFieldNeighbours[affMrgCtx.numValidMergeCand][mvNum][1].setMvField( mrgCtx.mvFieldNeighbours[pos][1].mv, mrgCtx.mvFieldNeighbours[pos][1].refIdx); #if GDR_ENABLED if (isEncodeGdrClean) { affMrgCtx.mvSolid[affMrgCtx.numValidMergeCand][mvNum][0] = mrgCtx.mvSolid[pos][0]; affMrgCtx.mvSolid[affMrgCtx.numValidMergeCand][mvNum][1] = mrgCtx.mvSolid[pos][1]; } #endif } affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = mrgCtx.interDirNeighbours[pos]; affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = AffineModel::NUM; affMrgCtx.mergeType[affMrgCtx.numValidMergeCand] = MergeType::SUBPU_ATMVP; if ( affMrgCtx.numValidMergeCand == mrgCandIdx ) { return; } affMrgCtx.numValidMergeCand++; // early termination if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand ) { return; } } } if ( slice.getSPS()->getUseAffine() ) { ///> Start: inherited affine candidates const PredictionUnit* npu[5]; int numAffNeighLeft = getAvailableAffineNeighboursForLeftPredictor( pu, npu ); int numAffNeigh = getAvailableAffineNeighboursForAbovePredictor( pu, npu, numAffNeighLeft ); for ( int idx = 0; idx < numAffNeigh; idx++ ) { // derive Mv from Neigh affine PU Mv cMv[2][3]; #if GDR_ENABLED bool cMvSolid[2][3] = { {true, true, true}, {true, true, true} }; MvpType cMvType[2][3]; Position cMvPos[2][3]; #endif const PredictionUnit* puNeigh = npu[idx]; pu.cu->affineType = puNeigh->cu->affineType; if ( puNeigh->interDir != 2 ) { #if GDR_ENABLED if (isEncodeGdrClean) { xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_0, cMv[0], cMvSolid[0], cMvType[0], cMvPos[0]); } else { xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_0, cMv[0]); } #else xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_0, cMv[0]); #endif } if ( slice.isInterB() ) { if ( puNeigh->interDir != 1 ) { #if GDR_ENABLED if (isEncodeGdrClean) { xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_1, cMv[1], cMvSolid[1], cMvType[1], cMvPos[1]); } else { xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_1, cMv[1]); } #else xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_1, cMv[1]); #endif } } for ( int mvNum = 0; mvNum < 3; mvNum++ ) { affMrgCtx.mvFieldNeighbours[affMrgCtx.numValidMergeCand][mvNum][0].setMvField(cMv[0][mvNum], puNeigh->refIdx[0]); affMrgCtx.mvFieldNeighbours[affMrgCtx.numValidMergeCand][mvNum][1].setMvField(cMv[1][mvNum], puNeigh->refIdx[1]); #if GDR_ENABLED if (isEncodeGdrClean) { affMrgCtx.mvSolid[affMrgCtx.numValidMergeCand][mvNum][0] = cMvSolid[0][mvNum]; affMrgCtx.mvSolid[affMrgCtx.numValidMergeCand][mvNum][1] = cMvSolid[0][mvNum]; } #endif } affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = puNeigh->interDir; affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = puNeigh->cu->affineType; affMrgCtx.bcwIdx[affMrgCtx.numValidMergeCand] = puNeigh->cu->bcwIdx; if ( affMrgCtx.numValidMergeCand == mrgCandIdx ) { return; } // early termination affMrgCtx.numValidMergeCand++; if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand ) { return; } } ///> End: inherited affine candidates ///> Start: Constructed affine candidates { MotionInfo mi[4]; bool isAvailable[4] = { false }; int8_t neighBcw[2] = { BCW_DEFAULT, BCW_DEFAULT }; // control point: LT B2->B3->A2 const Position posLT[3] = { pu.Y().topLeft().offset( -1, -1 ), pu.Y().topLeft().offset( 0, -1 ), pu.Y().topLeft().offset( -1, 0 ) }; #if GDR_ENABLED bool miSolid[4] = { false, false, false, false }; #endif for ( int i = 0; i < 3; i++ ) { const Position pos = posLT[i]; const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType ); if (puNeigh && CU::isInter(*puNeigh->cu) && PU::isDiffMER(pu.lumaPos(), pos, plevel)) { isAvailable[0] = true; mi[0] = puNeigh->getMotionInfo(pos); neighBcw[0] = puNeigh->cu->bcwIdx; #if GDR_ENABLED if (isEncodeGdrClean) { miSolid[0] = cs.isClean(puNeigh->Y().topRight(), ChannelType::LUMA); } #endif break; } } // control point: RT B1->B0 const Position posRT[2] = { pu.Y().topRight().offset( 0, -1 ), pu.Y().topRight().offset( 1, -1 ) }; for ( int i = 0; i < 2; i++ ) { const Position pos = posRT[i]; const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType ); if (puNeigh && CU::isInter(*puNeigh->cu) && PU::isDiffMER(pu.lumaPos(), pos, plevel)) { isAvailable[1] = true; mi[1] = puNeigh->getMotionInfo(pos); neighBcw[1] = puNeigh->cu->bcwIdx; #if GDR_ENABLED if (isEncodeGdrClean) { miSolid[1] = cs.isClean(puNeigh->Y().topRight(), ChannelType::LUMA); } #endif break; } } // control point: LB A1->A0 const Position posLB[2] = { pu.Y().bottomLeft().offset( -1, 0 ), pu.Y().bottomLeft().offset( -1, 1 ) }; for ( int i = 0; i < 2; i++ ) { const Position pos = posLB[i]; const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType ); if (puNeigh && CU::isInter(*puNeigh->cu) && PU::isDiffMER(pu.lumaPos(), pos, plevel)) { isAvailable[2] = true; mi[2] = puNeigh->getMotionInfo( pos ); #if GDR_ENABLED if (isEncodeGdrClean) { miSolid[2] = cs.isClean(puNeigh->Y().topRight(), ChannelType::LUMA); } #endif break; } } // control point: RB if ( slice.getPicHeader()->getEnableTMVPFlag() ) { //>> MTK colocated-RightBottom // offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to Position posRB = pu.Y().bottomRight().offset( -3, -3 ); const PreCalcValues& pcv = *cs.pcv; Position posC0; bool C0Avail = false; bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight); const SubPic &curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos()); if (curSubPic.getTreatedAsPicFlag()) { boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() && (posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom()); } if (boundaryCond) { int posYInCtu = posRB.y & pcv.maxCUHeightMask; if (posYInCtu + 4 < pcv.maxCUHeight) { posC0 = posRB.offset(4, 4); C0Avail = true; } } Mv cColMv; int refIdx = 0; bool existMV = C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, refIdx, false); if (existMV) { mi[3].mv[0] = cColMv; mi[3].refIdx[0] = refIdx; mi[3].interDir = 1; isAvailable[3] = true; #if GDR_ENABLED if (isEncodeGdrClean) { bool posL0inCurPicSolid = cs.isClean(posC0, ChannelType::LUMA); bool posL0inRefPicSolid = cs.isClean(posC0, REF_PIC_LIST_0, refIdx); miSolid[3] = posL0inCurPicSolid && posL0inRefPicSolid; } #endif } if ( slice.isInterB() ) { existMV = C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, cColMv, refIdx, false); if (existMV) { mi[3].mv[1] = cColMv; mi[3].refIdx[1] = refIdx; mi[3].interDir |= 2; isAvailable[3] = true; #if GDR_ENABLED if (isEncodeGdrClean) { bool posL1inCurPicSolid = cs.isClean(posC0, ChannelType::LUMA); bool posL1inRefPicSolid = cs.isClean(posC0, REF_PIC_LIST_1, refIdx); miSolid[3] = (mi[3].interDir & 1) ? (miSolid[3] && posL1inCurPicSolid && posL1inRefPicSolid) : (posL1inCurPicSolid && posL1inRefPicSolid); } #endif } } } //------------------- insert model -------------------// int order[6] = { 0, 1, 2, 3, 4, 5 }; int modelNum = 6; int model[6][4] = { { 0, 1, 2 }, // 0: LT, RT, LB { 0, 1, 3 }, // 1: LT, RT, RB { 0, 2, 3 }, // 2: LT, LB, RB { 1, 2, 3 }, // 3: RT, LB, RB { 0, 1 }, // 4: LT, RT { 0, 2 }, // 5: LT, LB }; #if GDR_ENABLED bool modelSolid[6] = { miSolid[0] && miSolid[1] && miSolid[2], miSolid[0] && miSolid[1] && miSolid[3], miSolid[0] && miSolid[2] && miSolid[3], miSolid[1] && miSolid[2] && miSolid[3], miSolid[0] && miSolid[1], miSolid[0] && miSolid[2] }; #endif int verNum[6] = { 3, 3, 3, 3, 2, 2 }; int startIdx = pu.cs->sps->getUseAffineType() ? 0 : 4; for ( int idx = startIdx; idx < modelNum; idx++ ) { int modelIdx = order[idx]; #if GDR_ENABLED getAffineControlPointCand(pu, mi, isAvailable, model[modelIdx], ((modelIdx == 3) ? neighBcw[1] : neighBcw[0]), modelIdx, verNum[modelIdx], affMrgCtx, isEncodeGdrClean, modelSolid); #else getAffineControlPointCand(pu, mi, isAvailable, model[modelIdx], ((modelIdx == 3) ? neighBcw[1] : neighBcw[0]), modelIdx, verNum[modelIdx], affMrgCtx); #endif if ( affMrgCtx.numValidMergeCand != 0 && affMrgCtx.numValidMergeCand - 1 == mrgCandIdx ) { return; } // early termination if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand ) { return; } } } ///> End: Constructed affine candidates } ///> zero padding int cnt = affMrgCtx.numValidMergeCand; while ( cnt < maxNumAffineMergeCand ) { for ( int mvNum = 0; mvNum < 3; mvNum++ ) { affMrgCtx.mvFieldNeighbours[cnt][mvNum][0].setMvField(Mv(0, 0), 0); #if GDR_ENABLED if (isEncodeGdrClean) { affMrgCtx.mvSolid[cnt][mvNum][0] = true; } #endif } affMrgCtx.interDirNeighbours[cnt] = 1; if ( slice.isInterB() ) { for ( int mvNum = 0; mvNum < 3; mvNum++ ) { affMrgCtx.mvFieldNeighbours[cnt][mvNum][1].setMvField(Mv(0, 0), 0); #if GDR_ENABLED if (isEncodeGdrClean) { affMrgCtx.mvSolid[cnt][mvNum][1] = true; } #endif } affMrgCtx.interDirNeighbours[cnt] = 3; } affMrgCtx.affineType[cnt] = AffineModel::_4_PARAMS; if ( cnt == mrgCandIdx ) { return; } cnt++; affMrgCtx.numValidMergeCand++; } } void PU::setAllAffineMvField(PredictionUnit &pu, std::array &mvField, RefPicList eRefList) { // Set Mv std::array mv; for (int i = 0; i < mv.size(); i++) { mv[i] = mvField[i][eRefList].mv; } setAllAffineMv( pu, mv[0], mv[1], mv[2], eRefList ); // Set RefIdx CHECK(mvField[0][eRefList].refIdx != mvField[1][eRefList].refIdx || mvField[0][eRefList].refIdx != mvField[2][eRefList].refIdx, "Affine mv corners don't have the same refIdx."); pu.refIdx[eRefList] = mvField[0][eRefList].refIdx; } void PU::setAllAffineMv(PredictionUnit& pu, Mv affLT, Mv affRT, Mv affLB, RefPicList eRefList, bool clipCPMVs) { int width = pu.Y().width; int shift = MAX_CU_DEPTH; if (clipCPMVs) { affLT.foldToStorageBitDepth(); affRT.foldToStorageBitDepth(); if (pu.cu->affineType == AffineModel::_6_PARAMS) { affLB.foldToStorageBitDepth(); } } int deltaMvHorX, deltaMvHorY, deltaMvVerX, deltaMvVerY; deltaMvHorX = (affRT - affLT).getHor() * (1 << (shift - floorLog2(width))); deltaMvHorY = (affRT - affLT).getVer() * (1 << (shift - floorLog2(width))); int height = pu.Y().height; if (pu.cu->affineType == AffineModel::_6_PARAMS) { deltaMvVerX = (affLB - affLT).getHor() * (1 << (shift - floorLog2(height))); deltaMvVerY = (affLB - affLT).getVer() * (1 << (shift - floorLog2(height))); } else { deltaMvVerX = -deltaMvHorY; deltaMvVerY = deltaMvHorX; } const int mvScaleHor = affLT.getHor() * (1 << shift); const int mvScaleVer = affLT.getVer() * (1 << shift); int blockWidth = AFFINE_SUBBLOCK_SIZE; int blockHeight = AFFINE_SUBBLOCK_SIZE; const int halfBW = blockWidth >> 1; const int halfBH = blockHeight >> 1; MotionBuf mb = pu.getMotionBuf(); int mvScaleTmpHor, mvScaleTmpVer; const bool subblkMVSpreadOverLimit = InterPrediction::isSubblockVectorSpreadOverLimit( deltaMvHorX, deltaMvHorY, deltaMvVerX, deltaMvVerY, pu.interDir ); for ( int h = 0; h < pu.Y().height; h += blockHeight ) { for ( int w = 0; w < pu.Y().width; w += blockWidth ) { if ( !subblkMVSpreadOverLimit ) { mvScaleTmpHor = mvScaleHor + deltaMvHorX * (halfBW + w) + deltaMvVerX * (halfBH + h); mvScaleTmpVer = mvScaleVer + deltaMvHorY * (halfBW + w) + deltaMvVerY * (halfBH + h); } else { mvScaleTmpHor = mvScaleHor + deltaMvHorX * ( pu.Y().width >> 1 ) + deltaMvVerX * ( pu.Y().height >> 1 ); mvScaleTmpVer = mvScaleVer + deltaMvHorY * ( pu.Y().width >> 1 ) + deltaMvVerY * ( pu.Y().height >> 1 ); } Mv curMv(mvScaleTmpHor, mvScaleTmpVer); curMv >>= shift; curMv.clipToStorageBitDepth(); for ( int y = (h >> MIN_CU_LOG2); y < ((h + blockHeight) >> MIN_CU_LOG2); y++ ) { for ( int x = (w >> MIN_CU_LOG2); x < ((w + blockWidth) >> MIN_CU_LOG2); x++ ) { mb.at(x, y).mv[eRefList] = curMv; } } } } pu.mvAffi[eRefList][0] = affLT; pu.mvAffi[eRefList][1] = affRT; pu.mvAffi[eRefList][2] = affLB; } void clipColPos(int& posX, int& posY, const PredictionUnit& pu) { Position puPos = pu.lumaPos(); int log2CtuSize = floorLog2(pu.cs->sps->getCTUSize()); int ctuX = ((puPos.x >> log2CtuSize) << log2CtuSize); int ctuY = ((puPos.y >> log2CtuSize) << log2CtuSize); int horMax; const SubPic &curSubPic = pu.cu->slice->getPPS()->getSubPicFromPos(puPos); if (curSubPic.getTreatedAsPicFlag()) { horMax = std::min((int)curSubPic.getSubPicRight(), ctuX + (int)pu.cs->sps->getCTUSize() + 3); } else { horMax = std::min((int)pu.cs->pps->getPicWidthInLumaSamples() - 1, ctuX + (int)pu.cs->sps->getCTUSize() + 3); } int horMin = std::max((int)0, ctuX); int verMax = std::min( (int)pu.cs->pps->getPicHeightInLumaSamples() - 1, ctuY + (int)pu.cs->sps->getCTUSize() - 1 ); int verMin = std::max((int)0, ctuY); posX = std::min(horMax, std::max(horMin, posX)); posY = std::min(verMax, std::max(verMin, posY)); } bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx &mrgCtx, const int count, int mmvdList) { const Slice &slice = *pu.cs->slice; const unsigned scale = 4 * std::max(1, 4 * AMVP_DECIMATION_FACTOR / 4); const unsigned mask = ~(scale - 1); const Picture *pColPic = slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx()); Mv cTMv; #if GDR_ENABLED const CodingStructure& cs = *pu.cs; const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); bool isSubPuSolid[2] = { true, true }; #endif if ( count ) { if ((mrgCtx.interDirNeighbours[0] & (1 << REF_PIC_LIST_0)) && slice.getRefPic(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[0][REF_PIC_LIST_0].refIdx) == pColPic) { cTMv = mrgCtx.mvFieldNeighbours[0][REF_PIC_LIST_0].mv; #if GDR_ENABLED if (isEncodeGdrClean) { isSubPuSolid[REF_PIC_LIST_0] = mrgCtx.mvSolid[0][REF_PIC_LIST_0]; } #endif } else if (slice.isInterB() && (mrgCtx.interDirNeighbours[0] & (1 << REF_PIC_LIST_1)) && slice.getRefPic(REF_PIC_LIST_1, mrgCtx.mvFieldNeighbours[0][REF_PIC_LIST_1].refIdx) == pColPic) { cTMv = mrgCtx.mvFieldNeighbours[0][REF_PIC_LIST_1].mv; #if GDR_ENABLED if (isEncodeGdrClean) { isSubPuSolid[REF_PIC_LIST_1] = mrgCtx.mvSolid[0][REF_PIC_LIST_1]; } #endif } } /////////////////////////////////////////////////////////////////////// //////// GET Initial Temporal Vector //////// /////////////////////////////////////////////////////////////////////// Mv cTempVector = cTMv; // compute the location of the current PU Position puPos = pu.lumaPos(); Size puSize = pu.lumaSize(); int numPartLine = std::max(puSize.width >> ATMVP_SUB_BLOCK_SIZE, 1u); int numPartCol = std::max(puSize.height >> ATMVP_SUB_BLOCK_SIZE, 1u); int puHeight = numPartCol == 1 ? puSize.height : 1 << ATMVP_SUB_BLOCK_SIZE; int puWidth = numPartLine == 1 ? puSize.width : 1 << ATMVP_SUB_BLOCK_SIZE; Mv cColMv; int refIdx = 0; // use coldir. const bool isBSlice = slice.isInterB(); Position centerPos; bool found = false; cTempVector = cTMv; cTempVector.changePrecision(MvPrecision::SIXTEENTH, MvPrecision::ONE); int tempX = cTempVector.getHor(); int tempY = cTempVector.getVer(); centerPos.x = puPos.x + (puSize.width >> 1) + tempX; centerPos.y = puPos.y + (puSize.height >> 1) + tempY; clipColPos(centerPos.x, centerPos.y, pu); centerPos = Position{ PosType(centerPos.x & mask), PosType(centerPos.y & mask) }; // derivation of center motion parameters from the collocated CU const MotionInfo &mi = pColPic->cs->getMotionInfo(centerPos); if (mi.isInter && mi.isIBCmot == false) { mrgCtx.interDirNeighbours[count] = 0; for (unsigned currRefListId = 0; currRefListId < (isBSlice ? 2 : 1); currRefListId++) { RefPicList currRefPicList = RefPicList(currRefListId); if (getColocatedMVP(pu, currRefPicList, centerPos, cColMv, refIdx, true)) { // set as default, for further motion vector field spanning mrgCtx.mvFieldNeighbours[count][currRefListId].setMvField(cColMv, 0); mrgCtx.interDirNeighbours[count] |= (1 << currRefListId); #if GDR_ENABLED if (isEncodeGdrClean) { mrgCtx.mvSolid[count][currRefListId] = cs.isClean(centerPos, currRefPicList, refIdx); } #endif mrgCtx.bcwIdx[count] = BCW_DEFAULT; found = true; } else { mrgCtx.mvFieldNeighbours[count][currRefListId].setMvField(Mv(), NOT_VALID); mrgCtx.interDirNeighbours[count] &= ~(1 << currRefListId); } } } if (!found) { return false; } if (mmvdList != 1) { int xOff = (puWidth >> 1) + tempX; int yOff = (puHeight >> 1) + tempY; MotionBuf &mb = mrgCtx.subPuMvpMiBuf; const bool isBiPred = isBipredRestriction(pu); for (int y = puPos.y; y < puPos.y + puSize.height; y += puHeight) { for (int x = puPos.x; x < puPos.x + puSize.width; x += puWidth) { Position colPos{ x + xOff, y + yOff }; clipColPos(colPos.x, colPos.y, pu); colPos = Position{ PosType(colPos.x & mask), PosType(colPos.y & mask) }; const MotionInfo &colMi = pColPic->cs->getMotionInfo(colPos); MotionInfo mi; found = false; mi.isInter = true; mi.sliceIdx = slice.getIndependentSliceIdx(); mi.isIBCmot = false; if (colMi.isInter && colMi.isIBCmot == false) { for (unsigned currRefListId = 0; currRefListId < (isBSlice ? 2 : 1); currRefListId++) { RefPicList currRefPicList = RefPicList(currRefListId); if (getColocatedMVP(pu, currRefPicList, colPos, cColMv, refIdx, true)) { mi.refIdx[currRefListId] = 0; mi.mv[currRefListId] = cColMv; found = true; #if GDR_ENABLED if (isEncodeGdrClean) { isSubPuSolid[currRefPicList] = isSubPuSolid[currRefPicList] && cs.isClean(colPos, currRefPicList, refIdx); } #endif } } } if (!found) { mi.mv[0] = mrgCtx.mvFieldNeighbours[count][0].mv; mi.mv[1] = mrgCtx.mvFieldNeighbours[count][1].mv; mi.refIdx[0] = mrgCtx.mvFieldNeighbours[count][0].refIdx; mi.refIdx[1] = mrgCtx.mvFieldNeighbours[count][1].refIdx; } mi.interDir = (mi.refIdx[0] != -1 ? 1 : 0) + (mi.refIdx[1] != -1 ? 2 : 0); if (isBiPred && mi.interDir == 3) { mi.interDir = 1; mi.mv[1] = Mv(); mi.refIdx[1] = NOT_VALID; } mb.subBuf(g_miScaling.scale(Position{ x, y } - pu.lumaPos()), g_miScaling.scale(Size(puWidth, puHeight))) .fill(mi); } } } #if GDR_ENABLED if (isEncodeGdrClean) { // the final if it is solid mrgCtx.mvSolid[count][0] &= isSubPuSolid[0]; mrgCtx.mvSolid[count][1] &= isSubPuSolid[1]; } #endif return true; } #if JVET_AC0139_UNIFIED_MERGE void PU::spanMotionInfo(PredictionUnit &pu, const MergeCtx &mrgCtx) { spanMotionInfo(pu, mrgCtx.subPuMvpMiBuf); } void PU::spanMotionInfo(PredictionUnit &pu, const MotionBuf& subPuMvpMiBuf) { #else void PU::spanMotionInfo( PredictionUnit &pu, const MergeCtx &mrgCtx ) { #endif MotionBuf mb = pu.getMotionBuf(); if (!pu.mergeFlag || pu.mergeType == MergeType::DEFAULT_N || pu.mergeType == MergeType::IBC) { MotionInfo mi; mi.isInter = !CU::isIntra(*pu.cu); mi.isIBCmot = CU::isIBC(*pu.cu); mi.sliceIdx = pu.cu->slice->getIndependentSliceIdx(); if( mi.isInter ) { mi.interDir = pu.interDir; mi.useAltHpelIf = pu.cu->imv == IMV_HPEL; for( int i = 0; i < NUM_REF_PIC_LIST_01; i++ ) { mi.mv[i] = pu.mv[i]; mi.refIdx[i] = pu.refIdx[i]; } if (mi.isIBCmot) { mi.bv = pu.bv; } } if( pu.cu->affine ) { for( int y = 0; y < mb.height; y++ ) { for( int x = 0; x < mb.width; x++ ) { MotionInfo &dest = mb.at( x, y ); dest.isInter = mi.isInter; dest.isIBCmot = false; dest.interDir = mi.interDir; dest.sliceIdx = mi.sliceIdx; for( int i = 0; i < NUM_REF_PIC_LIST_01; i++ ) { if( mi.refIdx[i] == -1 ) { dest.mv[i] = Mv(); } dest.refIdx[i] = mi.refIdx[i]; } } } } else { mb.fill( mi ); } } else if (pu.mergeType == MergeType::SUBPU_ATMVP) { #if JVET_AC0139_UNIFIED_MERGE CHECK(subPuMvpMiBuf.area() == 0 || !subPuMvpMiBuf.buf, "Buffer not initialized"); mb.copyFrom(subPuMvpMiBuf); #else CHECK(mrgCtx.subPuMvpMiBuf.area() == 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized"); mb.copyFrom(mrgCtx.subPuMvpMiBuf); #endif } else { if( isBipredRestriction( pu ) ) { for( int y = 0; y < mb.height; y++ ) { for( int x = 0; x < mb.width; x++ ) { MotionInfo &mi = mb.at( x, y ); if( mi.interDir == 3 ) { mi.interDir = 1; mi.mv [1] = Mv(); mi.refIdx[1] = NOT_VALID; } } } } } } void PU::applyImv( PredictionUnit& pu, MergeCtx &mrgCtx, InterPrediction *interPred ) { if( !pu.mergeFlag ) { if( pu.interDir != 2 /* PRED_L1 */ ) { pu.mvd[0].changeTransPrecAmvr2Internal(pu.cu->imv); unsigned mvpIdx = pu.mvpIdx[0]; AMVPInfo amvpInfo; if (CU::isIBC(*pu.cu)) { PU::fillIBCMvpCand(pu, amvpInfo); } else PU::fillMvpCand(pu, REF_PIC_LIST_0, pu.refIdx[0], amvpInfo); pu.mvpNum[0] = amvpInfo.numCand; pu.mvpIdx[0] = mvpIdx; pu.mv[0] = amvpInfo.mvCand[mvpIdx] + pu.mvd[0]; pu.mv[0].foldToStorageBitDepth(); } if (pu.interDir != 1 /* PRED_L0 */) { if( !( pu.cu->cs->picHeader->getMvdL1ZeroFlag() && pu.interDir == 3 ) && pu.cu->imv )/* PRED_BI */ { pu.mvd[1].changeTransPrecAmvr2Internal(pu.cu->imv); } unsigned mvpIdx = pu.mvpIdx[1]; AMVPInfo amvpInfo; PU::fillMvpCand(pu, REF_PIC_LIST_1, pu.refIdx[1], amvpInfo); pu.mvpNum[1] = amvpInfo.numCand; pu.mvpIdx[1] = mvpIdx; pu.mv[1] = amvpInfo.mvCand[mvpIdx] + pu.mvd[1]; pu.mv[1].foldToStorageBitDepth(); } } else { // this function is never called for merge THROW("unexpected"); PU::getInterMergeCandidates(pu, mrgCtx, 0); mrgCtx.setMergeInfo( pu, pu.mergeIdx ); } PU::spanMotionInfo( pu, mrgCtx ); } bool PU::isSimpleSymmetricBiPred(const PredictionUnit &pu) { const int refIdx0 = pu.refIdx[REF_PIC_LIST_0]; const int refIdx1 = pu.refIdx[REF_PIC_LIST_1]; if (refIdx0 >= 0 && refIdx1 >= 0) { const Slice *slice = pu.cu->slice; if (slice->getRefPic(REF_PIC_LIST_0, refIdx0)->longTerm || slice->getRefPic(REF_PIC_LIST_1, refIdx1)->longTerm) { return false; } if (pu.cu->bcwIdx != BCW_DEFAULT) { return false; } if (WPScalingParam::isWeighted(slice->getWpScaling(REF_PIC_LIST_0, refIdx0)) || WPScalingParam::isWeighted(slice->getWpScaling(REF_PIC_LIST_1, refIdx1))) { return false; } const int poc0 = slice->getRefPOC(REF_PIC_LIST_0, refIdx0); const int poc1 = slice->getRefPOC(REF_PIC_LIST_1, refIdx1); const int poc = slice->getPOC(); return poc - poc0 == poc1 - poc; } return false; } void PU::restrictBiPredMergeCandsOne(PredictionUnit &pu) { if (PU::isBipredRestriction(pu)) { if (pu.interDir == 3) { pu.interDir = 1; pu.refIdx[1] = -1; pu.mv[1] = Mv(0, 0); pu.cu->bcwIdx = BCW_DEFAULT; } } } void PU::getGeoMergeCandidates( const PredictionUnit &pu, MergeCtx& geoMrgCtx ) { MergeCtx tmpMergeCtx; const uint32_t maxNumMergeCand = pu.cs->sps->getMaxNumMergeCand(); geoMrgCtx.numValidMergeCand = 0; #if GDR_ENABLED CodingStructure &cs = *pu.cs; const bool isEncodeGdrClean = cs.sps->getGDREnabledFlag() && cs.pcv->isEncoder && ((cs.picture->gdrParam.inGdrInterval && cs.isClean(pu.Y().topRight(), ChannelType::LUMA)) || (cs.picture->gdrParam.verBoundary == -1)); #endif for (int32_t i = 0; i < GEO_MAX_NUM_UNI_CANDS; i++) { geoMrgCtx.bcwIdx[i] = BCW_DEFAULT; geoMrgCtx.interDirNeighbours[i] = 0; geoMrgCtx.mvFieldNeighbours[i][0].refIdx = NOT_VALID; geoMrgCtx.mvFieldNeighbours[i][1].refIdx = NOT_VALID; geoMrgCtx.mvFieldNeighbours[i][0].mv = Mv(); geoMrgCtx.mvFieldNeighbours[i][1].mv = Mv(); #if GDR_ENABLED if (isEncodeGdrClean) { geoMrgCtx.mvSolid[i][0] = true; geoMrgCtx.mvSolid[i][1] = true; } #endif geoMrgCtx.useAltHpelIf[i] = false; } PU::getInterMergeCandidates(pu, tmpMergeCtx, 0); for (int32_t i = 0; i < maxNumMergeCand; i++) { int parity = i & 1; if( tmpMergeCtx.interDirNeighbours[i] & (0x01 + parity) ) { geoMrgCtx.interDirNeighbours[geoMrgCtx.numValidMergeCand] = 1 + parity; geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][!parity].mv = Mv(0, 0); geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][parity].mv = tmpMergeCtx.mvFieldNeighbours[i][parity].mv; geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][!parity].refIdx = -1; geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][parity].refIdx = tmpMergeCtx.mvFieldNeighbours[i][parity].refIdx; #if GDR_ENABLED if (isEncodeGdrClean) { Mv mv = tmpMergeCtx.mvFieldNeighbours[i][parity].mv; int refIdx = tmpMergeCtx.mvFieldNeighbours[i][parity].refIdx; RefPicList refPicList = parity ? REF_PIC_LIST_1 : REF_PIC_LIST_0; geoMrgCtx.mvSolid[geoMrgCtx.numValidMergeCand][!parity] = true; geoMrgCtx.mvSolid[geoMrgCtx.numValidMergeCand][parity] = tmpMergeCtx.mvSolid[i][parity]; geoMrgCtx.mvValid[geoMrgCtx.numValidMergeCand][!parity] = true; geoMrgCtx.mvValid[geoMrgCtx.numValidMergeCand][parity] = cs.isClean(pu.Y().bottomRight(), mv, refPicList, refIdx); } #endif geoMrgCtx.numValidMergeCand++; if (geoMrgCtx.numValidMergeCand == GEO_MAX_NUM_UNI_CANDS) { return; } continue; } if (tmpMergeCtx.interDirNeighbours[i] & (0x02 - parity)) { geoMrgCtx.interDirNeighbours[geoMrgCtx.numValidMergeCand] = 2 - parity; geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][!parity].mv = tmpMergeCtx.mvFieldNeighbours[i][!parity].mv; geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][parity].mv = Mv(0, 0); geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][!parity].refIdx = tmpMergeCtx.mvFieldNeighbours[i][!parity].refIdx; geoMrgCtx.mvFieldNeighbours[geoMrgCtx.numValidMergeCand][parity].refIdx = -1; #if GDR_ENABLED if (isEncodeGdrClean) { Mv mv = tmpMergeCtx.mvFieldNeighbours[i][!parity].mv; int refIdx = tmpMergeCtx.mvFieldNeighbours[i][!parity].refIdx; RefPicList refPicList = !parity ? REF_PIC_LIST_1 : REF_PIC_LIST_0; geoMrgCtx.mvSolid[geoMrgCtx.numValidMergeCand][!parity] = tmpMergeCtx.mvSolid[i][!parity]; geoMrgCtx.mvSolid[geoMrgCtx.numValidMergeCand][parity] = true; geoMrgCtx.mvValid[geoMrgCtx.numValidMergeCand][!parity] = cs.isClean(pu.Y().bottomRight(), mv, refPicList, refIdx); geoMrgCtx.mvValid[geoMrgCtx.numValidMergeCand][parity] = true; } #endif geoMrgCtx.numValidMergeCand++; if (geoMrgCtx.numValidMergeCand == GEO_MAX_NUM_UNI_CANDS) { return; } } } } void PU::spanGeoMotionInfo(PredictionUnit &pu, const MergeCtx &geoMrgCtx, const uint8_t splitDir, const MergeIdxPair &candIdx) { pu.geoSplitDir = splitDir; pu.geoMergeIdx = candIdx; MotionBuf mb = pu.getMotionBuf(); MotionInfo biMv; biMv.isInter = true; biMv.sliceIdx = pu.cs->slice->getIndependentSliceIdx(); const uint8_t &candIdx0 = candIdx[0]; const uint8_t &candIdx1 = candIdx[1]; if( geoMrgCtx.interDirNeighbours[candIdx0] == 1 && geoMrgCtx.interDirNeighbours[candIdx1] == 2 ) { biMv.interDir = 3; biMv.mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx0][0].mv; biMv.mv[1] = geoMrgCtx.mvFieldNeighbours[candIdx1][1].mv; biMv.refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx0][0].refIdx; biMv.refIdx[1] = geoMrgCtx.mvFieldNeighbours[candIdx1][1].refIdx; } else if( geoMrgCtx.interDirNeighbours[candIdx0] == 2 && geoMrgCtx.interDirNeighbours[candIdx1] == 1 ) { biMv.interDir = 3; biMv.mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx1][0].mv; biMv.mv[1] = geoMrgCtx.mvFieldNeighbours[candIdx0][1].mv; biMv.refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx1][0].refIdx; biMv.refIdx[1] = geoMrgCtx.mvFieldNeighbours[candIdx0][1].refIdx; } else if( geoMrgCtx.interDirNeighbours[candIdx0] == 1 && geoMrgCtx.interDirNeighbours[candIdx1] == 1 ) { biMv.interDir = 1; biMv.mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx1][0].mv; biMv.mv[1] = Mv(0, 0); biMv.refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx1][0].refIdx; biMv.refIdx[1] = -1; } else if( geoMrgCtx.interDirNeighbours[candIdx0] == 2 && geoMrgCtx.interDirNeighbours[candIdx1] == 2 ) { biMv.interDir = 2; biMv.mv[0] = Mv(0, 0); biMv.mv[1] = geoMrgCtx.mvFieldNeighbours[candIdx1][1].mv; biMv.refIdx[0] = -1; biMv.refIdx[1] = geoMrgCtx.mvFieldNeighbours[candIdx1][1].refIdx; } const int angle = g_geoParams[splitDir].angleIdx; const int distanceIdx = g_geoParams[splitDir].distanceIdx; int tpmMask = 0; int lookUpY = 0, motionIdx = 0; bool isFlip = angle >= 13 && angle <= 27; int distanceX = angle; int distanceY = (distanceX + (GEO_NUM_ANGLES >> 2)) % GEO_NUM_ANGLES; int offsetX = (-(int)pu.lwidth()) >> 1; int offsetY = (-(int)pu.lheight()) >> 1; if (distanceIdx > 0) { if (angle % 16 == 8 || (angle % 16 != 0 && pu.lheight() >= pu.lwidth())) { offsetY += angle < 16 ? ((distanceIdx * pu.lheight()) >> 3) : -(int)((distanceIdx * pu.lheight()) >> 3); } else { offsetX += angle < 16 ? ((distanceIdx * pu.lwidth()) >> 3) : -(int)((distanceIdx * pu.lwidth()) >> 3); } } for (int y = 0; y < mb.height; y++) { lookUpY = (2 * (4 * y + offsetY) + 5) * g_dis[distanceY]; for (int x = 0; x < mb.width; x++) { motionIdx = (2 * (4 * x + offsetX) + 5) * g_dis[distanceX] + lookUpY; tpmMask = abs(motionIdx) < 32 ? 2 : (motionIdx <= 0 ? (1 - isFlip) : isFlip); if (tpmMask == 2) { mb.at(x, y).isInter = true; mb.at(x, y).interDir = biMv.interDir; mb.at(x, y).refIdx[0] = biMv.refIdx[0]; mb.at(x, y).refIdx[1] = biMv.refIdx[1]; mb.at(x, y).mv[0] = biMv.mv[0]; mb.at(x, y).mv[1] = biMv.mv[1]; mb.at(x, y).sliceIdx = biMv.sliceIdx; } else if (tpmMask == 0) { mb.at(x, y).isInter = true; mb.at(x, y).interDir = geoMrgCtx.interDirNeighbours[candIdx0]; mb.at(x, y).refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx0][0].refIdx; mb.at(x, y).refIdx[1] = geoMrgCtx.mvFieldNeighbours[candIdx0][1].refIdx; mb.at(x, y).mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx0][0].mv; mb.at(x, y).mv[1] = geoMrgCtx.mvFieldNeighbours[candIdx0][1].mv; mb.at(x, y).sliceIdx = biMv.sliceIdx; } else { mb.at(x, y).isInter = true; mb.at(x, y).interDir = geoMrgCtx.interDirNeighbours[candIdx1]; mb.at(x, y).refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx1][0].refIdx; mb.at(x, y).refIdx[1] = geoMrgCtx.mvFieldNeighbours[candIdx1][1].refIdx; mb.at(x, y).mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx1][0].mv; mb.at(x, y).mv[1] = geoMrgCtx.mvFieldNeighbours[candIdx1][1].mv; mb.at(x, y).sliceIdx = biMv.sliceIdx; } } } } void PU::getNeighborAffineInfo(const PredictionUnit& pu, int& numNeighborAvai, int& numNeighborAffine) { const Position& posLT = pu.Y().topLeft(); const Position& posRT = pu.Y().topRight(); const Position& posLB = pu.Y().bottomLeft(); const int neighborNum = 5; const PredictionUnit* neighbor[neighborNum]; neighbor[0] = pu.cs->getPURestricted(posRT.offset(0, -1), pu, pu.chType); // above neighbor[1] = pu.cs->getPURestricted(posLB.offset(-1, 0), pu, pu.chType); // left neighbor[2] = pu.cs->getPURestricted(posRT.offset(1, -1), pu, pu.chType); // above-right neighbor[3] = pu.cs->getPURestricted(posLB.offset(-1, 1), pu, pu.chType); // left-bottom neighbor[4] = pu.cs->getPURestricted(posLT.offset(-1, -1), pu, pu.chType); // above-left numNeighborAvai = 0; numNeighborAffine = 0; for (int i = 0; i < neighborNum; i++) { if (neighbor[i] != nullptr) { numNeighborAvai++; numNeighborAffine += neighbor[i]->cu->affine; } } } bool CU::hasSubCUNonZeroMVd( const CodingUnit& cu ) { bool nonZeroMvd = false; for( const auto &pu : CU::traversePUs( cu ) ) { if( ( !pu.mergeFlag ) && ( !cu.skip ) ) { if( pu.interDir != 2 /* PRED_L1 */ ) { nonZeroMvd |= pu.mvd[REF_PIC_LIST_0].getHor() != 0; nonZeroMvd |= pu.mvd[REF_PIC_LIST_0].getVer() != 0; } if( pu.interDir != 1 /* PRED_L0 */ ) { if( !pu.cu->cs->picHeader->getMvdL1ZeroFlag() || pu.interDir != 3 /* PRED_BI */ ) { nonZeroMvd |= pu.mvd[REF_PIC_LIST_1].getHor() != 0; nonZeroMvd |= pu.mvd[REF_PIC_LIST_1].getVer() != 0; } } } } return nonZeroMvd; } bool CU::hasSubCUNonZeroAffineMVd( const CodingUnit& cu ) { bool nonZeroAffineMvd = false; if ( !cu.affine || cu.firstPU->mergeFlag ) { return false; } for ( const auto &pu : CU::traversePUs( cu ) ) { if ( ( !pu.mergeFlag ) && ( !cu.skip ) ) { if ( pu.interDir != 2 /* PRED_L1 */ ) { for (int i = 0; i < cu.getNumAffineMvs(); i++) { nonZeroAffineMvd |= pu.mvdAffi[REF_PIC_LIST_0][i].getHor() != 0; nonZeroAffineMvd |= pu.mvdAffi[REF_PIC_LIST_0][i].getVer() != 0; } } if ( pu.interDir != 1 /* PRED_L0 */ ) { if ( !pu.cu->cs->picHeader->getMvdL1ZeroFlag() || pu.interDir != 3 /* PRED_BI */ ) { for (int i = 0; i < cu.getNumAffineMvs(); i++) { nonZeroAffineMvd |= pu.mvdAffi[REF_PIC_LIST_1][i].getHor() != 0; nonZeroAffineMvd |= pu.mvdAffi[REF_PIC_LIST_1][i].getVer() != 0; } } } } } return nonZeroAffineMvd; } uint8_t CU::getSbtInfo( uint8_t idx, uint8_t pos ) { return ( pos << 4 ) + ( idx << 0 ); } uint8_t CU::getSbtIdx( const uint8_t sbtInfo ) { return ( sbtInfo >> 0 ) & 0xf; } uint8_t CU::getSbtPos( const uint8_t sbtInfo ) { return ( sbtInfo >> 4 ) & 0x3; } uint8_t CU::getSbtMode( uint8_t sbtIdx, uint8_t sbtPos ) { uint8_t sbtMode = 0; switch( sbtIdx ) { case SBT_VER_HALF: sbtMode = sbtPos + SBT_VER_H0; break; case SBT_HOR_HALF: sbtMode = sbtPos + SBT_HOR_H0; break; case SBT_VER_QUAD: sbtMode = sbtPos + SBT_VER_Q0; break; case SBT_HOR_QUAD: sbtMode = sbtPos + SBT_HOR_Q0; break; default: assert( 0 ); } assert( sbtMode < NUMBER_SBT_MODE ); return sbtMode; } uint8_t CU::getSbtIdxFromSbtMode( uint8_t sbtMode ) { if( sbtMode <= SBT_VER_H1 ) { return SBT_VER_HALF; } else if( sbtMode <= SBT_HOR_H1 ) { return SBT_HOR_HALF; } else if( sbtMode <= SBT_VER_Q1 ) { return SBT_VER_QUAD; } else if( sbtMode <= SBT_HOR_Q1 ) { return SBT_HOR_QUAD; } else { assert( 0 ); return 0; } } uint8_t CU::getSbtPosFromSbtMode( uint8_t sbtMode ) { if( sbtMode <= SBT_VER_H1 ) { return sbtMode - SBT_VER_H0; } else if( sbtMode <= SBT_HOR_H1 ) { return sbtMode - SBT_HOR_H0; } else if( sbtMode <= SBT_VER_Q1 ) { return sbtMode - SBT_VER_Q0; } else if( sbtMode <= SBT_HOR_Q1 ) { return sbtMode - SBT_HOR_Q0; } else { assert( 0 ); return 0; } } uint8_t CU::targetSbtAllowed( uint8_t sbtIdx, uint8_t sbtAllowed ) { uint8_t val = 0; switch( sbtIdx ) { case SBT_VER_HALF: val = ( ( sbtAllowed >> SBT_VER_HALF ) & 0x1 ); break; case SBT_HOR_HALF: val = ( ( sbtAllowed >> SBT_HOR_HALF ) & 0x1 ); break; case SBT_VER_QUAD: val = ( ( sbtAllowed >> SBT_VER_QUAD ) & 0x1 ); break; case SBT_HOR_QUAD: val = ( ( sbtAllowed >> SBT_HOR_QUAD ) & 0x1 ); break; default: THROW("unknown SBT type"); } return val; } uint8_t CU::numSbtModeRdo( uint8_t sbtAllowed ) { uint8_t num = 0; uint8_t sum = 0; num = targetSbtAllowed( SBT_VER_HALF, sbtAllowed ) + targetSbtAllowed( SBT_HOR_HALF, sbtAllowed ); sum += std::min( SBT_NUM_RDO, ( num << 1 ) ); num = targetSbtAllowed( SBT_VER_QUAD, sbtAllowed ) + targetSbtAllowed( SBT_HOR_QUAD, sbtAllowed ); sum += std::min( SBT_NUM_RDO, ( num << 1 ) ); return sum; } bool CU::isSbtMode( const uint8_t sbtInfo ) { const uint8_t sbtIdx = getSbtIdx(sbtInfo); return sbtIdx >= SBT_VER_HALF && sbtIdx <= SBT_HOR_QUAD; } bool CU::isSameSbtSize( const uint8_t sbtInfo1, const uint8_t sbtInfo2 ) { const uint8_t sbtIdx1 = getSbtIdxFromSbtMode(sbtInfo1); const uint8_t sbtIdx2 = getSbtIdxFromSbtMode(sbtInfo2); if( sbtIdx1 == SBT_HOR_HALF || sbtIdx1 == SBT_VER_HALF ) { return sbtIdx2 == SBT_HOR_HALF || sbtIdx2 == SBT_VER_HALF; } else if( sbtIdx1 == SBT_HOR_QUAD || sbtIdx1 == SBT_VER_QUAD ) { return sbtIdx2 == SBT_HOR_QUAD || sbtIdx2 == SBT_VER_QUAD; } else { return false; } } bool CU::isPredRegDiffFromTB(const CodingUnit &cu, const ComponentID compID) { return (compID == COMPONENT_Y) && (cu.ispMode == ISPType::VER && CU::isMinWidthPredEnabledForBlkSize(cu.blocks[compID].width, cu.blocks[compID].height)); } bool CU::isMinWidthPredEnabledForBlkSize(const int w, const int h) { return ((w == 8 && h > 4) || w == 4); } bool CU::isFirstTBInPredReg(const CodingUnit& cu, const ComponentID compID, const CompArea &area) { return compID == COMPONENT_Y && cu.ispMode != ISPType::NONE && ((area.topLeft().x - cu.Y().topLeft().x) % PRED_REG_MIN_WIDTH == 0); } void CU::adjustPredArea(CompArea &area) { area.width = std::max(PRED_REG_MIN_WIDTH, area.width); } bool CU::isBcwIdxCoded( const CodingUnit &cu ) { if( cu.cs->sps->getUseBcw() == false ) { CHECK(cu.bcwIdx != BCW_DEFAULT, "Error: cu.bcwIdx != BCW_DEFAULT"); return false; } if (CU::isIBC(cu)) { return false; } if (CU::isIntra(cu) || cu.cs->slice->isInterP()) { return false; } if( cu.lwidth() * cu.lheight() < BCW_SIZE_CONSTRAINT ) { return false; } if( !cu.firstPU->mergeFlag ) { if( cu.firstPU->interDir == 3 ) { const int refIdx0 = cu.firstPU->refIdx[REF_PIC_LIST_0]; const int refIdx1 = cu.firstPU->refIdx[REF_PIC_LIST_1]; const WPScalingParam *wp0 = cu.cs->slice->getWpScaling(REF_PIC_LIST_0, refIdx0); const WPScalingParam *wp1 = cu.cs->slice->getWpScaling(REF_PIC_LIST_1, refIdx1); return !(WPScalingParam::isWeighted(wp0) || WPScalingParam::isWeighted(wp1)); } } return false; } uint8_t CU::getValidBcwIdx( const CodingUnit &cu ) { if( cu.firstPU->interDir == 3 && !cu.firstPU->mergeFlag ) { return cu.bcwIdx; } else if (cu.firstPU->interDir == 3 && cu.firstPU->mergeFlag && cu.firstPU->mergeType == MergeType::DEFAULT_N) { // This is intended to do nothing here. } else if (cu.firstPU->mergeFlag && cu.firstPU->mergeType == MergeType::SUBPU_ATMVP) { CHECK(cu.bcwIdx != BCW_DEFAULT, " cu.bcwIdx != BCW_DEFAULT "); } else { CHECK(cu.bcwIdx != BCW_DEFAULT, " cu.bcwIdx != BCW_DEFAULT "); } return BCW_DEFAULT; } bool CU::bdpcmAllowed( const CodingUnit& cu, const ComponentID compID ) { SizeType transformSkipMaxSize = 1 << cu.cs->sps->getLog2MaxTransformSkipBlockSize(); bool bdpcmAllowed = cu.cs->sps->getBDPCMEnabledFlag() && CU::isIntra(cu); if (isLuma(compID)) { bdpcmAllowed &= (cu.lwidth() <= transformSkipMaxSize && cu.lheight() <= transformSkipMaxSize); } else { bdpcmAllowed &= (cu.chromaSize().width <= transformSkipMaxSize && cu.chromaSize().height <= transformSkipMaxSize) && !cu.colorTransform; } return bdpcmAllowed; } bool CU::isMTSAllowed(const CodingUnit &cu, const ComponentID compID) { SizeType tsMaxSize = 1 << cu.cs->sps->getLog2MaxTransformSkipBlockSize(); const int maxSize = CU::isIntra( cu ) ? MTS_INTRA_MAX_CU_SIZE : MTS_INTER_MAX_CU_SIZE; const int cuWidth = cu.blocks[0].lumaSize().width; const int cuHeight = cu.blocks[0].lumaSize().height; bool mtsAllowed = isLuma(cu.chType) && compID == COMPONENT_Y; mtsAllowed &= CU::isIntra(cu) ? cu.cs->sps->getExplicitMtsIntraEnabled() : cu.cs->sps->getExplicitMtsInterEnabled() && CU::isInter(cu); mtsAllowed &= cuWidth <= maxSize && cuHeight <= maxSize; mtsAllowed &= cu.ispMode == ISPType::NONE; mtsAllowed &= !cu.sbtInfo; mtsAllowed &= !(cu.bdpcmMode != BdpcmMode::NONE && cuWidth <= tsMaxSize && cuHeight <= tsMaxSize); return mtsAllowed; } // TU tools bool TU::isNonTransformedResidualRotated(const TransformUnit &tu, const ComponentID &compID) { return tu.cs->sps->getSpsRangeExtension().getTransformSkipRotationEnabledFlag() && tu.blocks[compID].width == 4 && CU::isIntra(*tu.cu); } bool TU::getCbf( const TransformUnit &tu, const ComponentID &compID ) { return getCbfAtDepth( tu, compID, tu.depth ); } bool TU::getCbfAtDepth(const TransformUnit &tu, const ComponentID &compID, const unsigned &depth) { if( !tu.blocks[compID].valid() ) { CHECK(tu.cbf[compID] != 0, "cbf must be 0 if the component is not available"); } return ((tu.cbf[compID] >> depth) & 1) == 1; } void TU::setCbfAtDepth(TransformUnit &tu, const ComponentID &compID, const unsigned &depth, const bool &cbf) { // first clear the CBF at the depth tu.cbf[compID] &= ~(1 << depth); // then set the CBF tu.cbf[compID] |= ((cbf ? 1 : 0) << depth); } bool TU::isTSAllowed(const TransformUnit &tu, const ComponentID compID) { const int maxSize = tu.cs->sps->getLog2MaxTransformSkipBlockSize(); bool tsAllowed = tu.cs->sps->getTransformSkipEnabledFlag(); tsAllowed &= (tu.cu->ispMode == ISPType::NONE || !isLuma(compID)); SizeType transformSkipMaxSize = 1 << maxSize; tsAllowed &= tu.cu->getBdpcmMode(compID) == BdpcmMode::NONE; tsAllowed &= tu.blocks[compID].width <= transformSkipMaxSize && tu.blocks[compID].height <= transformSkipMaxSize; tsAllowed &= !tu.cu->sbtInfo; return tsAllowed; } int TU::getICTMode( const TransformUnit& tu, int jointCbCr ) { if( jointCbCr < 0 ) { jointCbCr = tu.jointCbCr; } return g_ictModes[ tu.cs->picHeader->getJointCbCrSignFlag() ][ jointCbCr ]; } bool TU::needsSqrt2Scale( const TransformUnit &tu, const ComponentID &compID ) { const Size &size=tu.blocks[compID]; const bool isTransformSkip = tu.mtsIdx[compID] == MtsType::SKIP; return (!isTransformSkip) && (((floorLog2(size.width) + floorLog2(size.height)) & 1) == 1); } bool TU::needsBlockSizeTrafoScale( const TransformUnit &tu, const ComponentID &compID ) { return needsSqrt2Scale( tu, compID ) || isNonLog2BlockSize( tu.blocks[compID] ); } TransformUnit* TU::getPrevTU( const TransformUnit &tu, const ComponentID compID ) { TransformUnit* prevTU = tu.prev; if( prevTU != nullptr && ( prevTU->cu != tu.cu || !prevTU->blocks[compID].valid() ) ) { prevTU = nullptr; } return prevTU; } bool TU::getPrevTuCbfAtDepth( const TransformUnit ¤tTu, const ComponentID compID, const int trDepth ) { const TransformUnit* prevTU = getPrevTU( currentTu, compID ); return ( prevTU != nullptr ) ? TU::getCbfAtDepth( *prevTU, compID, trDepth ) : false; } // other tools uint32_t getCtuAddr( const Position& pos, const PreCalcValues& pcv ) { return ( pos.x >> pcv.maxCUWidthLog2 ) + ( pos.y >> pcv.maxCUHeightLog2 ) * pcv.widthInCtus; } bool allowLfnstWithMip(const Size& block) { if (block.width >= 16 && block.height >= 16) { return true; } return false; } #if GREEN_METADATA_SEI_ENABLED void writeGMFAOutput(FeatureCounterStruct& featureCounterUpdated, FeatureCounterStruct& featureCounterOld, std::string GMFAFile,bool lastFrame ) { std::string fileName = std::string(""); std::string matlabFile = std::string(""); if (GMFAFile.length() == 0) { return; } fileName = GMFAFile; size_t strStart = 0; size_t strEnd = 0; int64_t codedFrames = featureCounterUpdated.iSlices + featureCounterUpdated.bSlices + featureCounterUpdated.pSlices; strEnd = fileName.rfind('.'); strEnd = strEnd == fileName.length() ? 0 : strEnd; if (!lastFrame) { fileName = fileName.substr(0, strEnd) + "_" + std::to_string(codedFrames) + fileName.substr(strEnd, fileName.length()); } strStart = fileName.find_last_of("/\\"); strStart = strStart == fileName.length() ? 0 : strStart+1; matlabFile = fileName.substr(strStart, strEnd-strStart); std::ofstream featureFile(fileName); featureFile << "function [n]="; featureFile << matlabFile.c_str(); featureFile << "() \n"; //General features featureFile << "\tn.EO = 1;\n"; featureFile << "\tn.ISlice = " << featureCounterUpdated.iSlices-featureCounterOld.iSlices << ";\n"; featureFile << "\tn.PSlice = " << featureCounterUpdated.pSlices-featureCounterOld.pSlices << ";\n"; featureFile << "\tn.BSlice = " << featureCounterUpdated.bSlices-featureCounterOld.bSlices << ";\n"; featureFile << "\tn.width = " << featureCounterUpdated.width << " ; \n"; featureFile << "\tn.height = " << featureCounterUpdated.height << " ; \n"; featureFile << "\tn.baseQP = [...\n\t"; for (int iter = 0; iter < 64; iter++) { featureFile << " " << featureCounterUpdated.baseQP[iter]-featureCounterOld.baseQP[iter] << " "; if (iter % 8 == 7 && iter != 63) { featureFile << " ...\n\t"; } } featureFile << "]; \n "; featureFile << "\tn.bytes = " << featureCounterUpdated.bytes << " ; \n"; featureFile << "\tn.is8bit = " << featureCounterUpdated.is8bit << " ; \n"; featureFile << "\tn.is10bit = " << featureCounterUpdated.is10bit << " ; \n"; featureFile << "\tn.is12bit = " << featureCounterUpdated.is12bit << " ; \n"; featureFile << "\tn.isYUV400 = " << featureCounterUpdated.isYUV400 << " ; \n"; featureFile << "\tn.isYUV420 = " << featureCounterUpdated.isYUV420 << " ; \n"; featureFile << "\tn.isYUV422 = " << featureCounterUpdated.isYUV422 << " ; \n"; featureFile << "\tn.isYUV444 = " << featureCounterUpdated.isYUV444 << " ; \n"; //Intra Feature featureToFile(featureFile, featureCounterUpdated.intraBlockSizes, "intraBlocks",true,featureCounterOld.intraBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaPlaBlockSizes, "intraPlaLuma",true,featureCounterOld.intraLumaPlaBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaDcBlockSizes, "intraDCLuma",true,featureCounterOld.intraLumaDcBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaHvdBlockSizes, "intraHVDLuma",true,featureCounterOld.intraLumaHvdBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaHvBlockSizes, "intraHVLuma",true,featureCounterOld.intraLumaHvBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaAngBlockSizes, "intraAngLuma",true,featureCounterOld.intraLumaAngBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaPlaBlockSizes, "intraPlaChroma",true,featureCounterOld.intraChromaPlaBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaDcBlockSizes, "intraDCChroma",true,featureCounterOld.intraChromaDcBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaHvdBlockSizes, "intraHVDChroma",true,featureCounterOld.intraChromaHvdBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaHvBlockSizes, "intraHVChroma",true,featureCounterOld.intraChromaHvBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaAngBlockSizes, "intraAngChroma",true,featureCounterOld.intraChromaAngBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaCrossCompBlockSizes, "intraCrossComp",true,featureCounterOld.intraChromaCrossCompBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraPDPCBlockSizes, "intraPDPC",true,featureCounterOld.intraPDPCBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaPDPCBlockSizes, "intraLumaPDPC",true,featureCounterOld.intraLumaPDPCBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaPDPCBlockSizes, "intraChromaPDPC",true,featureCounterOld.intraChromaPDPCBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraMIPBlockSizes, "intraMIP",true,featureCounterOld.intraMIPBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraLumaMIPBlockSizes, "intraLumaMIP",true,featureCounterOld.intraLumaMIPBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraChromaMIPBlockSizes, "intraChromaMIP",true,featureCounterOld.intraChromaMIPBlockSizes); featureToFile(featureFile, featureCounterUpdated.IBCBlockSizes, "intraIBC",true,featureCounterOld.IBCBlockSizes); featureToFile(featureFile, featureCounterUpdated.IBCLumaBlockSizes, "intraLumaIBC",true,featureCounterOld.IBCLumaBlockSizes); featureToFile(featureFile, featureCounterUpdated.IBCChromaBlockSizes, "intraChromaIBC",true,featureCounterOld.IBCChromaBlockSizes); featureToFile(featureFile, featureCounterUpdated.intraSubPartitionsHorizontal, "intraSubPartitionsHorizontal",true,featureCounterOld.intraSubPartitionsHorizontal); featureToFile(featureFile, featureCounterUpdated.intraLumaSubPartitionsHorizontal, "intraLumaSubPartitionsHorizontal",true,featureCounterOld.intraLumaSubPartitionsHorizontal); featureToFile(featureFile, featureCounterUpdated.intraChromaSubPartitionsHorizontal,"intraChromaSubPartitionsHorizontal",true,featureCounterOld.intraChromaSubPartitionsHorizontal); featureToFile(featureFile, featureCounterUpdated.intraSubPartitionsVertical, "intraSubPartitionsVertical",true,featureCounterOld.intraSubPartitionsVertical); featureToFile(featureFile, featureCounterUpdated.intraLumaSubPartitionsVertical, "intraLumaSubPartitionsVertical",true,featureCounterOld.intraLumaSubPartitionsVertical); featureToFile(featureFile, featureCounterUpdated.intraChromaSubPartitionsVertical, "intraChromaSubPartitionsVertical",true,featureCounterOld.intraChromaSubPartitionsVertical); //Inter featureToFile(featureFile, featureCounterUpdated.interBlockSizes, "interBlocks",true,featureCounterOld.interBlockSizes); featureToFile(featureFile, featureCounterUpdated.interLumaBlockSizes, "interLumaBlocks",true,featureCounterOld.interLumaBlockSizes); featureToFile(featureFile, featureCounterUpdated.interChromaBlockSizes, "interChromaBlocks",true,featureCounterOld.interChromaBlockSizes); featureToFile(featureFile, featureCounterUpdated.interInterBlocks, "interInter",true,featureCounterOld.interInterBlocks); featureToFile(featureFile, featureCounterUpdated.interLumaInterBlocks, "interLumaInter",true,featureCounterOld.interLumaInterBlocks); featureToFile(featureFile, featureCounterUpdated.interChromaInterBlocks, "interChromaInter",true,featureCounterOld.interChromaInterBlocks); featureToFile(featureFile, featureCounterUpdated.interMergeBlocks, "interMerge",true,featureCounterOld.interMergeBlocks); featureToFile(featureFile, featureCounterUpdated.interLumaMergeBlocks, "interLumaMerge",true,featureCounterOld.interLumaMergeBlocks); featureToFile(featureFile, featureCounterUpdated.interChromaMergeBlocks, "interChromaMerge",true,featureCounterOld.interChromaMergeBlocks); featureToFile(featureFile, featureCounterUpdated.interSkipBlocks, "interSkip",true,featureCounterOld.interSkipBlocks); featureToFile(featureFile, featureCounterUpdated.interLumaSkipBlocks, "interLumaSkip",true,featureCounterOld.interLumaSkipBlocks); featureToFile(featureFile, featureCounterUpdated.interChromaSkipBlocks, "interChromaSkip",true,featureCounterOld.interChromaSkipBlocks); featureToFile(featureFile, featureCounterUpdated.affine, "interAffine",true,featureCounterOld.affine); featureToFile(featureFile, featureCounterUpdated.affineLuma, "interLumaAffine",true,featureCounterOld.affineLuma); featureToFile(featureFile, featureCounterUpdated.affineChroma, "interChromaAffine",true,featureCounterOld.affineChroma); featureToFile(featureFile, featureCounterUpdated.affineInter, "interAffineInter",true,featureCounterOld.affineInter); featureToFile(featureFile, featureCounterUpdated.affineLumaInter, "interLumaAffineInter",true,featureCounterOld.affineLumaInter); featureToFile(featureFile, featureCounterUpdated.affineChromaInter, "interChromaAffineInter",true,featureCounterOld.affineChromaInter); featureToFile(featureFile, featureCounterUpdated.affineMerge, "interAffineMerge",true,featureCounterOld.affineMerge); featureToFile(featureFile, featureCounterUpdated.affineLumaMerge, "interLumaAffineMerge",true,featureCounterOld.affineLumaMerge); featureToFile(featureFile, featureCounterUpdated.affineChromaMerge, "interChromaAffineMerge",true,featureCounterOld.affineChromaMerge); featureToFile(featureFile, featureCounterUpdated.affineSkip, "interAffineSkip",true,featureCounterOld.affineSkip); featureToFile(featureFile, featureCounterUpdated.affineLumaSkip, "interLumaAffineSkip",true,featureCounterOld.affineLumaSkip); featureToFile(featureFile, featureCounterUpdated.affineChromaSkip, "interChromaAffineSkip",true,featureCounterOld.affineChromaSkip); featureToFile(featureFile, featureCounterUpdated.geo, "geo",true,featureCounterOld.geo); featureToFile(featureFile, featureCounterUpdated.geoLuma, "geoLuma",true,featureCounterOld.geoLuma); featureToFile(featureFile, featureCounterUpdated.geoChroma, "geoChroma",true,featureCounterOld.geoChroma); featureToFile(featureFile, featureCounterUpdated.dmvrBlocks, "dmvrBlocks",true,featureCounterOld.dmvrBlocks); featureToFile(featureFile, featureCounterUpdated.bdofBlocks, "bdofBlocks",true,featureCounterOld.bdofBlocks); featureFile << "\tn.uniPredPel = " << featureCounterUpdated.uniPredPel-featureCounterOld.uniPredPel << " ; \n"; featureFile << "\tn.biPredPel = " << featureCounterUpdated.biPredPel-featureCounterOld.biPredPel << " ; \n"; featureFile << "\tn.fracPelHor = " << featureCounterUpdated.fracPelHor-featureCounterOld.fracPelHor << " ; \n"; featureFile << "\tn.fracPelVer = " << featureCounterUpdated.fracPelVer-featureCounterOld.fracPelVer << " ; \n"; featureFile << "\tn.fracPelBoth = " << featureCounterUpdated.fracPelBoth-featureCounterOld.fracPelBoth << " ; \n"; featureFile << "\tn.copyCUPel = " << featureCounterUpdated.copyCUPel-featureCounterOld.copyCUPel << " ; \n"; featureFile << "\tn.affineFracPelHor = " << featureCounterUpdated.affineFracPelHor-featureCounterOld.affineFracPelHor << " ; \n"; featureFile << "\tn.affineFracPelVer = " << featureCounterUpdated.affineFracPelVer-featureCounterOld.affineFracPelVer << " ; \n"; featureFile << "\tn.affineFracPelBoth = " << featureCounterUpdated.affineFracPelBoth-featureCounterOld.affineFracPelBoth << " ; \n"; featureFile << "\tn.affineCopyCUPel = " << featureCounterUpdated.affineCopyCUPel-featureCounterOld.affineCopyCUPel << " ; \n"; //Transform featureToFile(featureFile, featureCounterUpdated.transformBlocks, "transform",true,featureCounterOld.transformBlocks); featureToFile(featureFile, featureCounterUpdated.transformLumaBlocks, "transformLuma",true,featureCounterOld.transformLumaBlocks); featureToFile(featureFile, featureCounterUpdated.transformChromaBlocks, "transformChroma",true,featureCounterOld.transformChromaBlocks); featureToFile(featureFile, featureCounterUpdated.transformSkipBlocks, "transformSkip",true,featureCounterOld.transformSkipBlocks); featureToFile(featureFile, featureCounterUpdated.transformLumaSkipBlocks, "transformLumaSkip",true,featureCounterOld.transformLumaSkipBlocks); featureToFile(featureFile, featureCounterUpdated.transformChromaSkipBlocks, "transformChromaSkip",true,featureCounterOld.transformChromaSkipBlocks); featureFile << "\tn.transformLFNST4 = " << featureCounterUpdated.transformLFNST4-featureCounterOld.transformLFNST4 << " ; \n"; featureFile << "\tn.transformLFNST8 = " << featureCounterUpdated.transformLFNST8-featureCounterOld.transformLFNST8 << " ; \n"; featureFile << "\tn.nrOfCoeff = " << featureCounterUpdated.nrOfCoeff-featureCounterOld.nrOfCoeff << " ; \n"; featureFile << "\tn.coeffG1 = " << featureCounterUpdated.coeffG1-featureCounterOld.coeffG1 << " ; \n"; featureFile << "\tn.valueOfCoeff = " << featureCounterUpdated.valueOfCoeff-featureCounterOld.valueOfCoeff << " ; \n"; //In-Loop featureFile << "\tn.BS0 = " << featureCounterUpdated.boundaryStrength[0]-featureCounterOld.boundaryStrength[0] << " ; \n"; featureFile << "\tn.BS1 = " << featureCounterUpdated.boundaryStrength[1]-featureCounterOld.boundaryStrength[1] << " ; \n"; featureFile << "\tn.BS2 = " << featureCounterUpdated.boundaryStrength[2]-featureCounterOld.boundaryStrength[2] << " ; \n"; featureFile << "\tn.BSPel0 = " << featureCounterUpdated.boundaryStrengthPel[0]-featureCounterOld.boundaryStrengthPel[0] << " ; \n"; featureFile << "\tn.BSPel1 = " << featureCounterUpdated.boundaryStrengthPel[1]-featureCounterOld.boundaryStrengthPel[1] << " ; \n"; featureFile << "\tn.BSPel2 = " << featureCounterUpdated.boundaryStrengthPel[2]-featureCounterOld.boundaryStrengthPel[2] << " ; \n"; featureFile << "\tn.saoLumaBO = " << featureCounterUpdated.saoLumaBO-featureCounterOld.saoLumaBO << " ; \n"; featureFile << "\tn.saoLumaEO = " << featureCounterUpdated.saoLumaEO-featureCounterOld.saoLumaEO << " ; \n"; featureFile << "\tn.saoChromaBO = " << featureCounterUpdated.saoChromaBO-featureCounterOld.saoChromaBO << " ; \n"; featureFile << "\tn.saoChromaEO = " << featureCounterUpdated.saoChromaEO-featureCounterOld.saoChromaEO << " ; \n"; featureFile << "\tn.saoLumaPels = " << featureCounterUpdated.saoLumaPels << " ; \n"; featureFile << "\tn.saoChromaPels = " << featureCounterUpdated.saoChromaPels << " ; \n"; featureFile << "\tn.alfLumaType7 = " << featureCounterUpdated.alfLumaType7-featureCounterOld.alfLumaType7 << " ; \n"; featureFile << "\tn.alfChromaType5 = " << featureCounterUpdated.alfChromaType5-featureCounterOld.alfChromaType5 << " ; \n"; featureFile << "\tn.alfLumaPels = " << featureCounterUpdated.alfLumaPels << " ; \n"; featureFile << "\tn.alfChromaPels = " << featureCounterUpdated.alfChromaPels << " ; \n"; featureFile << "\tn.ccalf = " << featureCounterUpdated.ccalf-featureCounterOld.ccalf << " ; \n"; featureFile << "end\n"; featureFile.close(); } void featureToFile(std::ofstream& featureFile,int featureCounterReference[MAX_CU_DEPTH+1][MAX_CU_DEPTH+1], std::string featureName,bool calcDifference,int featureCounter[MAX_CU_DEPTH+1][MAX_CU_DEPTH+1]) { featureFile << "\tn." << featureName << " = [...\n\t"; for (size_t i = 0; i < MAX_CU_DEPTH+1; i++) { for (size_t j = 0; j < MAX_CU_DEPTH+1; j++) { if (calcDifference) { featureFile << " " << featureCounterReference[j][i] - featureCounter[j][i] << " "; } else { featureFile << " " << featureCounterReference[j][i] << " "; } } if (i != MAX_CU_DEPTH) { featureFile << ";... \n\t"; } } featureFile << "]; \n "; } void countFeatures(FeatureCounterStruct& featureCounter, CodingStructure& cs, const UnitArea& ctuArea) { SizeType cuWidthIdx = MAX_UINT; SizeType cuHeightIdx = MAX_UINT; for (auto &currCU: cs.traverseCUs(ctuArea, ChannelType::LUMA)) { cuWidthIdx = floorLog2(currCU.lwidth()); cuHeightIdx = floorLog2(currCU.lheight()); if ((cuWidthIdx <= MAX_CU_DEPTH+1) && (cuHeightIdx <= MAX_CU_DEPTH+1)) { if (currCU.predMode == MODE_INTRA) // Intra-Mode { for (auto &currPU: CU::traversePUs(currCU)) { SizeType puWidthIdx = floorLog2(currPU.Y().width); SizeType puHeightIdx = floorLog2(currPU.Y().height); if ((puWidthIdx <= MAX_CU_DEPTH+1) && (puHeightIdx <= MAX_CU_DEPTH+1)) { featureCounter.intraBlockSizes[puWidthIdx][puHeightIdx]++; SizeType lumaPredDir = currPU.intraDir[ChannelType::LUMA]; if (currCU.mipFlag) { featureCounter.intraMIPBlockSizes[puWidthIdx][puHeightIdx]++; featureCounter.intraLumaMIPBlockSizes[puWidthIdx][puHeightIdx]++; } else { if (lumaPredDir == PLANAR_IDX) { featureCounter.intraLumaPlaBlockSizes[puWidthIdx][puHeightIdx]++; } else if (lumaPredDir == DC_IDX) { featureCounter.intraLumaDcBlockSizes[puWidthIdx][puHeightIdx]++; } else if (lumaPredDir == HOR_IDX || lumaPredDir == VER_IDX || lumaPredDir == DIA_IDX) { featureCounter.intraLumaHvdBlockSizes[puWidthIdx][puHeightIdx]++; } else { featureCounter.intraLumaAngBlockSizes[puWidthIdx][puHeightIdx]++; } } bool tempPDPC = (currPU.Y().width >= MIN_TB_SIZEY && currPU.Y().height >= MIN_TB_SIZEY) && isLuma(COMPONENT_Y) ? currPU.multiRefIdx : false; if (tempPDPC) { featureCounter.intraPDPCBlockSizes[puWidthIdx][puHeightIdx]++; featureCounter.intraLumaPDPCBlockSizes[puWidthIdx][puHeightIdx]++; } } } if (currCU.ispMode > ISPType::NONE) { if (currCU.ispMode == ISPType::VER) { featureCounter.intraSubPartitionsVertical[cuWidthIdx][cuHeightIdx]++; featureCounter.intraLumaSubPartitionsVertical[cuWidthIdx][cuHeightIdx]++; } if (currCU.ispMode == ISPType::HOR) { featureCounter.intraSubPartitionsHorizontal[cuWidthIdx][cuHeightIdx]++; featureCounter.intraLumaSubPartitionsHorizontal[cuWidthIdx][cuHeightIdx]++; } } } else if (currCU.predMode == MODE_IBC) // IBC-Mode { featureCounter.IBCBlockSizes[cuWidthIdx][cuHeightIdx]++; featureCounter.IBCLumaBlockSizes[cuWidthIdx][cuHeightIdx]++; } else if (currCU.predMode == MODE_INTER) // Inter-Mode { featureCounter.interBlockSizes[cuWidthIdx][cuHeightIdx]++; featureCounter.interLumaBlockSizes[cuWidthIdx][cuHeightIdx]++; featureCounter.interBlockSizes[cuWidthIdx-1][cuHeightIdx-1] += 2; featureCounter.interChromaBlockSizes[cuWidthIdx-1][cuHeightIdx-1] += 2; if (currCU.skip) { featureCounter.interSkipBlocks[cuWidthIdx][cuHeightIdx]++; featureCounter.interLumaSkipBlocks[cuWidthIdx][cuHeightIdx]++; featureCounter.interSkipBlocks[cuWidthIdx-1][cuHeightIdx-1] += 2; featureCounter.interChromaSkipBlocks[cuWidthIdx-1][cuHeightIdx-1] += 2; if (currCU.affine) { featureCounter.affine[cuWidthIdx][cuHeightIdx]++; featureCounter.affineLuma[cuWidthIdx][cuHeightIdx]++; featureCounter.affineSkip[cuWidthIdx][cuHeightIdx]++; featureCounter.affineLumaSkip[cuWidthIdx][cuHeightIdx]++; featureCounter.affine[cuWidthIdx-1][cuHeightIdx-1] += 2; featureCounter.affineChroma[cuWidthIdx-1][cuHeightIdx-1] += 2; featureCounter.affineSkip[cuWidthIdx-1][cuHeightIdx-1] += 2; featureCounter.affineChromaSkip[cuWidthIdx-1][cuHeightIdx-1] += 2; } } else { for (auto &currPU: CU::traversePUs(currCU)) { SizeType puWidthIdx = floorLog2(currPU.lwidth()); SizeType puHeightIdx = floorLog2(currPU.lheight()); if ((puWidthIdx <= MAX_CU_DEPTH+1) && (puHeightIdx <= MAX_CU_DEPTH+1)) { if (currPU.mergeFlag) { featureCounter.interMergeBlocks[puWidthIdx][puHeightIdx]++; featureCounter.interLumaMergeBlocks[puWidthIdx][puHeightIdx]++; featureCounter.interMergeBlocks[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.interChromaMergeBlocks[puWidthIdx-1][puHeightIdx-1] += 2; if (currCU.affine) { featureCounter.affine[puWidthIdx][puHeightIdx]++; featureCounter.affineLuma[puWidthIdx][puHeightIdx]++; featureCounter.affineMerge[puWidthIdx][puHeightIdx]++; featureCounter.affineLumaMerge[puWidthIdx][puHeightIdx]++; featureCounter.affine[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.affineChroma[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.affineMerge[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.affineChromaMerge[puWidthIdx-1][puHeightIdx-1] += 2; } } else // InterInter { featureCounter.interInterBlocks[puWidthIdx][puHeightIdx]++; featureCounter.interLumaInterBlocks[puWidthIdx][puHeightIdx]++; featureCounter.interInterBlocks[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.interChromaInterBlocks[puWidthIdx-1][puHeightIdx-1] += 2; if (currCU.affine) { featureCounter.affine[puWidthIdx][puHeightIdx]++; featureCounter.affineLuma[puWidthIdx][puHeightIdx]++; featureCounter.affineInter[puWidthIdx][puHeightIdx]++; featureCounter.affineLumaInter[puWidthIdx][puHeightIdx]++; featureCounter.affine[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.affineChroma[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.affineInter[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.affineChromaInter[puWidthIdx-1][puHeightIdx-1] += 2; } } if (currPU.cu->geoFlag) { featureCounter.geo[puWidthIdx][puHeightIdx]++; featureCounter.geoLuma[puWidthIdx][puHeightIdx]++; featureCounter.geo[puWidthIdx-1][puHeightIdx-1] += 2; featureCounter.geoChroma[puWidthIdx-1][puHeightIdx-1] += 2; } } } } for (auto &currPU: CU::traversePUs(currCU)) // Check whether BDOF or DMVR are used in the current PU { bool m_DMVRapplied = false; bool m_BDOFapplied = false; PredictionUnit &pu = currPU; SizeType puWidthIdx = floorLog2(currPU.lwidth()); SizeType puHeightIdx = floorLog2(currPU.lheight()); if ((puWidthIdx <= MAX_CU_DEPTH + 1) && (puHeightIdx <= MAX_CU_DEPTH + 1)) { const PPS &pps = *pu.cs->pps; const Slice &localSlice = *pu.cs->slice; CHECK(!pu.cu->affine && pu.refIdx[0] >= 0 && pu.refIdx[1] >= 0 && (pu.lwidth() + pu.lheight() == 12), "invalid 4x8/8x4 bi-predicted blocks"); int refIdx0 = pu.refIdx[REF_PIC_LIST_0]; int refIdx1 = pu.refIdx[REF_PIC_LIST_1]; const WPScalingParam *wp0 = pu.cs->slice->getWpScaling(REF_PIC_LIST_0, refIdx0); const WPScalingParam *wp1 = pu.cs->slice->getWpScaling(REF_PIC_LIST_1, refIdx1); bool bioApplied = false; if (pu.cs->sps->getBDOFEnabledFlag() && (!pu.cs->picHeader->getBdofDisabledFlag())) { if (pu.cu->affine) { bioApplied = false; } else { const bool biocheck0 = !((WPScalingParam::isWeighted(wp0) || WPScalingParam::isWeighted(wp1)) && localSlice.getSliceType() == B_SLICE); const bool biocheck1 = !(pps.getUseWP() && localSlice.getSliceType() == P_SLICE); if (biocheck0 && biocheck1 && PU::isSimpleSymmetricBiPred(pu) && (pu.Y().height >= 8) && (pu.Y().width >= 8) && ((pu.Y().height * pu.Y().width) >= 128)) { bioApplied = true; } } if (bioApplied && pu.ciipFlag) { bioApplied = false; } if (bioApplied && pu.cu->smvdMode) { bioApplied = false; } if (pu.cu->cs->sps->getUseBcw() && bioApplied && pu.cu->bcwIdx != BCW_DEFAULT) { bioApplied = false; } } if (pu.mmvdEncOptMode == 2 && pu.mmvdMergeFlag) { bioApplied = false; } bool dmvrApplied = false; dmvrApplied = PU::checkDMVRCondition(pu); bool refIsScaled = (refIdx0 < 0 ? false : pu.cu->slice->getRefPic(REF_PIC_LIST_0, refIdx0)->isRefScaled(pu.cs->pps)) || (refIdx1 < 0 ? false : pu.cu->slice->getRefPic(REF_PIC_LIST_1, refIdx1)->isRefScaled(pu.cs->pps)); dmvrApplied = dmvrApplied && !refIsScaled; bioApplied = bioApplied && !refIsScaled; for (uint32_t refList = 0; refList < NUM_REF_PIC_LIST_01; refList++) { if (pu.refIdx[refList] < 0) { continue; } RefPicList eRefPicList = (refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0); CHECK(CU::isIBC(*pu.cu) && eRefPicList != REF_PIC_LIST_0, "Invalid interdir for ibc mode"); CHECK(CU::isIBC(*pu.cu) && pu.refIdx[refList] != IBC_REF_IDX, "Invalid reference index for ibc mode"); CHECK((CU::isInter(*pu.cu) && pu.refIdx[refList] >= localSlice.getNumRefIdx(eRefPicList)), "Invalid reference index"); } m_BDOFapplied = bioApplied; if (!pu.cu->geoFlag && (!dmvrApplied) && (!bioApplied) && pps.getWPBiPred() && localSlice.getSliceType() == B_SLICE && pu.cu->bcwIdx == BCW_DEFAULT) { m_DMVRapplied = false; } else if (!pu.cu->geoFlag && pps.getUseWP() && localSlice.getSliceType() == P_SLICE) { m_DMVRapplied = false; } else { m_DMVRapplied = dmvrApplied; } if (m_DMVRapplied) { featureCounter.dmvrBlocks[puWidthIdx][puHeightIdx]++; } if (m_BDOFapplied) { featureCounter.bdofBlocks[puWidthIdx][puHeightIdx]++; } } } } if (currCU.predMode == MODE_INTER) { const int nShift = MV_FRACTIONAL_BITS_DIFF; const int nOffset = 1 << (nShift - 1); for (auto &currPU: CU::traversePUs(currCU)) { int numberOfPels = currPU.lwidth() * currPU.lheight(); if (!currPU.cu->affine && !currPU.cu->geoFlag) { if (currPU.interDir != MODE_TYPE_INTRA /* PRED_L1 */) { Mv mv = currPU.mv[REF_PIC_LIST_0]; Mv mvd = currPU.mvd[REF_PIC_LIST_0]; mv.hor = mv.hor >= 0 ? (mv.hor + nOffset) >> nShift : -((-mv.hor + nOffset) >> nShift); mv.ver = mv.ver >= 0 ? (mv.ver + nOffset) >> nShift : -((-mv.ver + nOffset) >> nShift); mvd.hor = mvd.hor >= 0 ? (mvd.hor + nOffset) >> nShift : -((-mvd.hor + nOffset) >> nShift); mvd.ver = mvd.ver >= 0 ? (mvd.ver + nOffset) >> nShift : -((-mvd.ver + nOffset) >> nShift); if (currPU.interDir == 3) { featureCounter.biPredPel += numberOfPels; } else { featureCounter.uniPredPel += numberOfPels; } if (mv.hor == 0 && mv.ver == 0) { featureCounter.copyCUPel += numberOfPels; } if (mv.hor != 0 && mv.ver == 0) { featureCounter.fracPelHor += numberOfPels; } if (mv.hor == 0 && mv.ver != 0) { featureCounter.fracPelVer += numberOfPels; } if (mv.hor != 0 && mv.ver != 0) { featureCounter.fracPelBoth += numberOfPels; } } if (currPU.interDir != 1 /* PRED_L1 */) { Mv mv = currPU.mv[REF_PIC_LIST_1]; Mv mvd = currPU.mvd[REF_PIC_LIST_1]; mv.hor = mv.hor >= 0 ? (mv.hor + nOffset) >> nShift : -((-mv.hor + nOffset) >> nShift); mv.ver = mv.ver >= 0 ? (mv.ver + nOffset) >> nShift : -((-mv.ver + nOffset) >> nShift); mvd.hor = mvd.hor >= 0 ? (mvd.hor + nOffset) >> nShift : -((-mvd.hor + nOffset) >> nShift); mvd.ver = mvd.ver >= 0 ? (mvd.ver + nOffset) >> nShift : -((-mvd.ver + nOffset) >> nShift); if (currPU.interDir != 3) { featureCounter.uniPredPel += numberOfPels; } if (mv.hor == 0 && mv.ver == 0) { featureCounter.copyCUPel += numberOfPels; } if (mv.hor != 0 && mv.ver == 0) { featureCounter.fracPelHor += numberOfPels; } if (mv.hor == 0 && mv.ver != 0) { featureCounter.fracPelVer += numberOfPels; } if (mv.hor != 0 && mv.ver != 0) { featureCounter.fracPelBoth += numberOfPels; } } } else if (currPU.cu->affine) { if (currPU.interDir != 2 /* PRED_L1 */) { Mv mv[3]; const CMotionBuf &mb = currPU.getMotionBuf(); mv[0] = mb.at(0, 0).mv[REF_PIC_LIST_0]; mv[1] = mb.at(mb.width - 1, 0).mv[REF_PIC_LIST_0]; mv[2] = mb.at(0, mb.height - 1).mv[REF_PIC_LIST_0]; mv[0].hor = mv[0].hor >= 0 ? (mv[0].hor + nOffset) >> nShift : -((-mv[0].hor + nOffset) >> nShift); mv[0].ver = mv[0].ver >= 0 ? (mv[0].ver + nOffset) >> nShift : -((-mv[0].ver + nOffset) >> nShift); mv[1].hor = mv[1].hor >= 0 ? (mv[1].hor + nOffset) >> nShift : -((-mv[1].hor + nOffset) >> nShift); mv[1].ver = mv[1].ver >= 0 ? (mv[1].ver + nOffset) >> nShift : -((-mv[1].ver + nOffset) >> nShift); mv[2].hor = mv[2].hor >= 0 ? (mv[2].hor + nOffset) >> nShift : -((-mv[2].hor + nOffset) >> nShift); mv[2].ver = mv[2].ver >= 0 ? (mv[2].ver + nOffset) >> nShift : -((-mv[2].ver + nOffset) >> nShift); if (currPU.interDir == 3) { featureCounter.biPredPel += numberOfPels; } else { featureCounter.uniPredPel += numberOfPels; } for (int iter = 0; iter < 3; iter++) { if (mv[iter].hor == 0 && mv[iter].ver == 0) { featureCounter.affineCopyCUPel += numberOfPels; } if (mv[iter].hor != 0 && mv[iter].ver == 0) { featureCounter.affineFracPelHor += numberOfPels; } if (mv[iter].hor == 0 && mv[iter].ver != 0) { featureCounter.affineFracPelVer += numberOfPels; } if (mv[iter].hor != 0 && mv[iter].ver != 0) { featureCounter.affineFracPelBoth += numberOfPels; } } } if (currPU.interDir != 1 /* PRED_L1 */) { Mv mv[3]; const CMotionBuf &mb = currPU.getMotionBuf(); mv[0] = mb.at(0, 0).mv[REF_PIC_LIST_1]; mv[1] = mb.at(mb.width - 1, 0).mv[REF_PIC_LIST_1]; mv[2] = mb.at(0, mb.height - 1).mv[REF_PIC_LIST_1]; mv[0].hor = mv[0].hor >= 0 ? (mv[0].hor + nOffset) >> nShift : -((-mv[0].hor + nOffset) >> nShift); mv[0].ver = mv[0].ver >= 0 ? (mv[0].ver + nOffset) >> nShift : -((-mv[0].ver + nOffset) >> nShift); mv[1].hor = mv[1].hor >= 0 ? (mv[1].hor + nOffset) >> nShift : -((-mv[1].hor + nOffset) >> nShift); mv[1].ver = mv[1].ver >= 0 ? (mv[1].ver + nOffset) >> nShift : -((-mv[1].ver + nOffset) >> nShift); mv[2].hor = mv[2].hor >= 0 ? (mv[2].hor + nOffset) >> nShift : -((-mv[2].hor + nOffset) >> nShift); mv[2].ver = mv[2].ver >= 0 ? (mv[2].ver + nOffset) >> nShift : -((-mv[2].ver + nOffset) >> nShift); if (currPU.interDir != 3) { featureCounter.uniPredPel += numberOfPels; } for (int iter = 0; iter < 3; iter++) { if (mv[iter].hor == 0 && mv[iter].ver == 0) { featureCounter.affineCopyCUPel += numberOfPels; } if (mv[iter].hor != 0 && mv[iter].ver == 0) { featureCounter.affineFracPelHor += numberOfPels; } if (mv[iter].hor == 0 && mv[iter].ver != 0) { featureCounter.affineFracPelVer += numberOfPels; } if (mv[iter].hor != 0 && mv[iter].ver != 0) { featureCounter.affineFracPelBoth += numberOfPels; } } } } } } for (auto &currTU: CU::traverseTUs(currCU)) { bool m_notOnCounterGridWidth = false; bool m_notOnCounterGridHeight = false; SizeType currTUWIdx = MAX_UINT; SizeType currTUHIdx = MAX_UINT; currTUWIdx = floorLog2(currTU.Y().width); currTUHIdx = floorLog2(currTU.Y().height); if ((currTUWIdx <= MAX_CU_DEPTH + 1) && (currTUHIdx <= MAX_CU_DEPTH + 1)) { if (currTU.Y().width != int(pow(2, currTUWIdx))) { m_notOnCounterGridWidth = true; } if (currTU.Y().height != int(pow(2, currTUHIdx))) { m_notOnCounterGridHeight = true; } if (currTU.cu->skip) { featureCounter.transformSkipBlocks[currTUWIdx][currTUHIdx]++; featureCounter.transformLumaSkipBlocks[currTUWIdx][currTUHIdx]++; if (m_notOnCounterGridHeight || m_notOnCounterGridWidth) { if (m_notOnCounterGridHeight) { featureCounter.transformSkipBlocks[currTUWIdx][currTUHIdx - 1]++; featureCounter.transformLumaSkipBlocks[currTUWIdx][currTUHIdx - 1]++; } if (m_notOnCounterGridWidth) { featureCounter.transformSkipBlocks[currTUWIdx - 1][currTUHIdx]++; featureCounter.transformLumaSkipBlocks[currTUWIdx - 1][currTUHIdx]++; } } } else { featureCounter.transformBlocks[currTUWIdx][currTUHIdx]++; featureCounter.transformLumaBlocks[currTUWIdx][currTUHIdx]++; if (m_notOnCounterGridHeight || m_notOnCounterGridWidth) { if (m_notOnCounterGridHeight) { featureCounter.transformBlocks[currTUWIdx][currTUHIdx - 1]++; featureCounter.transformLumaBlocks[currTUWIdx][currTUHIdx - 1]++; } if (m_notOnCounterGridWidth) { featureCounter.transformBlocks[currTUWIdx - 1][currTUHIdx]++; featureCounter.transformLumaBlocks[currTUWIdx - 1][currTUHIdx]++; } } } if (currTU.cu->lfnstIdx && currTU.mtsIdx[COMPONENT_Y] != MtsType::SKIP && (currTU.cu->isSepTree() ? true : isLuma(COMPONENT_Y))) { bool significantCoeff = false; for (int bufferScan = 0; bufferScan < currTU.lwidth() * currTU.lheight() && !significantCoeff; bufferScan++) { if (currTU.getCoeffs(ComponentID(0)).buf[bufferScan] != 0) { significantCoeff = true; } } if (significantCoeff) { if (currTU.Y().width >= 8 && currTU.Y().height >= 8) { featureCounter.transformLFNST8++; } else { featureCounter.transformLFNST4++; } } } int maxNumberOfCoeffs = 0; maxNumberOfCoeffs = currTU.Y().width * currTU.Y().height; if (currTU.cbf[COMPONENT_Y] == 0) { maxNumberOfCoeffs = 0; } for (int i = 0; i < maxNumberOfCoeffs; i++) { int counterCoeff = currTU.getCoeffs(COMPONENT_Y).buf[i]; if (counterCoeff != 0) { featureCounter.nrOfCoeff++; if (counterCoeff < 0) { counterCoeff *= -1; } if (counterCoeff > 1) { featureCounter.coeffG1++; } counterCoeff--; // taking account of the fact that n_coeffg1 has already been counted double ldVal = counterCoeff < 2 ? 0.0 : floorLog2(counterCoeff) + (2 * counterCoeff >= (3 << floorLog2(counterCoeff)) ? 0.585 : 0.0); featureCounter.valueOfCoeff += ldVal; } } } } } } for (auto &currCU: cs.traverseCUs(ctuArea, ChannelType::CHROMA)) { SizeType cuCbWidthIdx = floorLog2(currCU.Cb().width); SizeType cuCbHeightIdx = floorLog2(currCU.Cb().height); SizeType cuCrWidthIdx = floorLog2(currCU.Cr().width); SizeType cuCrHeightIdx = floorLog2(currCU.Cr().height); if ((cuCbWidthIdx <= MAX_CU_DEPTH+1) && (cuCbHeightIdx <= MAX_CU_DEPTH+1) && (cuCrWidthIdx <= MAX_CU_DEPTH+1) && (cuCrHeightIdx <= MAX_CU_DEPTH+1)) { if (currCU.predMode == MODE_INTRA) // Intra-Mode { for (auto &currPU: CU::traversePUs(currCU)) { SizeType chromaWidthIdx = floorLog2(currPU.Cb().width); SizeType chromaHeightIdx = floorLog2(currPU.Cb().height); if ((chromaWidthIdx <= MAX_CU_DEPTH+1) && (chromaHeightIdx <= MAX_CU_DEPTH+1)) { featureCounter.intraBlockSizes[chromaWidthIdx][chromaHeightIdx]++; SizeType chromaPredDir = currPU.intraDir[ChannelType::CHROMA]; // Intra Prediction Direction if (currCU.mipFlag) { featureCounter.intraMIPBlockSizes[chromaWidthIdx][chromaHeightIdx]++; featureCounter.intraChromaMIPBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else { if (chromaPredDir == PLANAR_IDX) { featureCounter.intraChromaPlaBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else if (chromaPredDir == DC_IDX) { featureCounter.intraChromaDcBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else if (chromaPredDir == HOR_IDX || chromaPredDir == VER_IDX || chromaPredDir == DIA_IDX) { featureCounter.intraChromaHvdBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else if (chromaPredDir < LM_CHROMA_IDX) { featureCounter.intraChromaAngBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } if (PU::isLMCMode(chromaPredDir)) { featureCounter.intraChromaCrossCompBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } } bool tempPDPC = (currPU.Cb().width >= MIN_TB_SIZEY && currPU.Cb().height >= MIN_TB_SIZEY) && isLuma(COMPONENT_Cb) ? currPU.multiRefIdx : false; if (tempPDPC) { featureCounter.intraPDPCBlockSizes[chromaWidthIdx][chromaHeightIdx]++; featureCounter.intraChromaPDPCBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } } } if (currCU.ispMode > ISPType::NONE) { if (currCU.ispMode == ISPType::VER) { featureCounter.intraSubPartitionsVertical[cuCbWidthIdx][cuCbHeightIdx]++; featureCounter.intraChromaSubPartitionsVertical[cuCbWidthIdx][cuCbHeightIdx]++; } if (currCU.ispMode == ISPType::HOR) { featureCounter.intraSubPartitionsHorizontal[cuCbWidthIdx][cuCbHeightIdx]++; featureCounter.intraChromaSubPartitionsHorizontal[cuCbWidthIdx][cuCbHeightIdx]++; } } } if (currCU.predMode == MODE_INTRA) // Intra-Mode { for (auto &currPU: CU::traversePUs(currCU)) { SizeType chromaWidthIdx = floorLog2(currPU.Cr().width); SizeType chromaHeightIdx = floorLog2(currPU.Cr().height); featureCounter.intraBlockSizes[chromaWidthIdx][chromaHeightIdx]++; SizeType chromaPredDir = currPU.intraDir[ChannelType::CHROMA]; if ((chromaWidthIdx <= MAX_CU_DEPTH + 1) && (chromaHeightIdx <= MAX_CU_DEPTH + 1)) { if (currCU.mipFlag) { featureCounter.intraMIPBlockSizes[chromaWidthIdx][chromaHeightIdx]++; featureCounter.intraChromaMIPBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else { if (chromaPredDir == PLANAR_IDX) { featureCounter.intraChromaPlaBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else if (chromaPredDir == DC_IDX) { featureCounter.intraChromaDcBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else if (chromaPredDir == HOR_IDX || chromaPredDir == VER_IDX || chromaPredDir == DIA_IDX) { featureCounter.intraChromaHvdBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } else if (chromaPredDir < NUM_CHROMA_MODE) { featureCounter.intraChromaAngBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } if (PU::isLMCMode(chromaPredDir)) { featureCounter.intraChromaCrossCompBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } } bool tempPDPC = (currPU.Cr().width >= MIN_TB_SIZEY && currPU.Cr().height >= MIN_TB_SIZEY) && isLuma(COMPONENT_Cr) ? currPU.multiRefIdx : false; if (tempPDPC) { featureCounter.intraPDPCBlockSizes[chromaWidthIdx][chromaHeightIdx]++; featureCounter.intraChromaPDPCBlockSizes[chromaWidthIdx][chromaHeightIdx]++; } } } if (currCU.ispMode > ISPType::NONE) { if (currCU.ispMode == ISPType::VER) { featureCounter.intraSubPartitionsVertical[cuCrWidthIdx][cuCrHeightIdx]++; featureCounter.intraChromaSubPartitionsVertical[cuCrWidthIdx][cuCrHeightIdx]++; } if (currCU.ispMode == ISPType::HOR) { featureCounter.intraSubPartitionsHorizontal[cuCrWidthIdx][cuCrHeightIdx]++; featureCounter.intraSubPartitionsHorizontal[cuCrWidthIdx][cuCrHeightIdx]++; } } } for (auto &currTU: CU::traverseTUs(currCU)) { for (int m_compID = 1; m_compID < getNumberValidComponents(currTU.chromaFormat); m_compID++) { bool m_notOnCounterGridWidth = false; bool m_notOnCounterGridHeight = false; SizeType currTUWIdx = MAX_UINT; SizeType currTUHIdx = MAX_UINT; if ((currTUWIdx <= MAX_CU_DEPTH+1) && (currTUHIdx <= MAX_CU_DEPTH+1)) { if (m_compID == COMPONENT_Cb) { currTUWIdx = floorLog2(currTU.Cb().width); currTUHIdx = floorLog2(currTU.Cb().height); if (currTU.Cb().width != int(pow(2, currTUWIdx))) { m_notOnCounterGridWidth = true; } if (currTU.Cb().height != int(pow(2, currTUHIdx))) { m_notOnCounterGridHeight = true; } } else { currTUWIdx = floorLog2(currTU.Cr().width); currTUHIdx = floorLog2(currTU.Cr().height); if (currTU.Cr().width != int(pow(2, currTUWIdx))) { m_notOnCounterGridWidth = true; } if (currTU.Cr().height != int(pow(2, currTUHIdx))) { m_notOnCounterGridHeight = true; } } if (currTU.cu->skip) { featureCounter.transformSkipBlocks[currTUWIdx][currTUHIdx]++; featureCounter.transformChromaSkipBlocks[currTUWIdx][currTUHIdx]++; if (m_notOnCounterGridHeight || m_notOnCounterGridWidth) { if (m_notOnCounterGridHeight) { featureCounter.transformSkipBlocks[currTUWIdx][currTUHIdx - 1]++; featureCounter.transformChromaSkipBlocks[currTUWIdx][currTUHIdx - 1]++; } if (m_notOnCounterGridWidth) { featureCounter.transformSkipBlocks[currTUWIdx - 1][currTUHIdx]++; featureCounter.transformChromaSkipBlocks[currTUWIdx - 1][currTUHIdx]++; } } } else { featureCounter.transformBlocks[currTUWIdx][currTUHIdx]++; featureCounter.transformChromaBlocks[currTUWIdx][currTUHIdx]++; if (m_notOnCounterGridHeight || m_notOnCounterGridWidth) { if (m_notOnCounterGridHeight) { featureCounter.transformBlocks[currTUWIdx][currTUHIdx - 1]++; featureCounter.transformChromaBlocks[currTUWIdx][currTUHIdx - 1]++; } if (m_notOnCounterGridWidth) { featureCounter.transformBlocks[currTUWIdx - 1][currTUHIdx]++; featureCounter.transformChromaBlocks[currTUWIdx - 1][currTUHIdx]++; } } } if (currTU.cu->lfnstIdx && currTU.mtsIdx[m_compID] != MtsType::SKIP && (currTU.cu->isSepTree() ? true : isLuma(COMPONENT_Cr))) { bool significantCoeff = false; if (m_compID == COMPONENT_Cb) { for (int bufferScan = 0; bufferScan < currTU.Cb().width * currTU.Cb().height && significantCoeff == false; bufferScan++) { if (currTU.getCoeffs(ComponentID(m_compID)).buf[bufferScan] != 0) { significantCoeff = true; } } } else { for (int bufferScan = 0; bufferScan < currTU.Cr().width * currTU.Cr().height && significantCoeff == false; bufferScan++) { if (currTU.getCoeffs(ComponentID(m_compID)).buf[bufferScan] != 0) { significantCoeff = true; } } } if (significantCoeff) { if (currTU.Cb().width >= 8 && currTU.Cb().height >= 8) { featureCounter.transformLFNST8++; } else { featureCounter.transformLFNST4++; } } } int maxNumberOfCoeffs = 0; if (m_compID == COMPONENT_Y) { maxNumberOfCoeffs = currTU.Y().width * currTU.Y().height; if (currTU.cbf[COMPONENT_Y] == 0) { maxNumberOfCoeffs = 0; } } else if (m_compID == COMPONENT_Cb) { maxNumberOfCoeffs = currTU.Cb().width * currTU.Cb().height; if (currTU.cbf[COMPONENT_Cb] == 0) { maxNumberOfCoeffs = 0; } } else { maxNumberOfCoeffs = currTU.Cr().width * currTU.Cr().height; if (currTU.cbf[COMPONENT_Cr] == 0) { maxNumberOfCoeffs = 0; } } for (int i = 0; i < maxNumberOfCoeffs; i++) { int counterCoeff = currTU.getCoeffs((ComponentID) m_compID).buf[i]; if (counterCoeff != 0) { featureCounter.nrOfCoeff++; if (counterCoeff < 0) // abs val { counterCoeff *= -1; } if (counterCoeff > 1) { featureCounter.coeffG1++; } counterCoeff--; // taking account of the fact that n_coeffg1 has already been counted double ldVal = counterCoeff < 2 ? 0.0 : floorLog2(counterCoeff) + (2 * counterCoeff >= (3 << floorLog2(counterCoeff)) ? 0.585 : 0.0); featureCounter.valueOfCoeff += ldVal; } } } } } } } } #endif