/* 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 MCTS.cpp \brief Motion Constrained Tile Sets class */ #include "MCTS.h" #include "Buffer.h" #include "UnitTools.h" #include #include //! \ingroup CommonLib //! \{ // ==================================================================================================================== // Constructor / destructor / initialize // ==================================================================================================================== void MCTSInfo::init( CodingStructure* cs, int ctuAddr ) { // TODO: Tile area calculation can be a part of TileMap or some other high-level-part where tiles are managed m_tileArea = MCTSHelper::getTileArea( cs, ctuAddr ); } Area MCTSInfo::getTileAreaSubPelRestricted( const PredictionUnit &pu ) { const int offLT = 3; const int offRB = 4; return MCTSHelper::getTileAreaRestricted( m_tileArea, offLT, offRB ); } Area MCTSInfo::getTileAreaIntPelRestricted( const PredictionUnit &pu ) { const int offLT = 0; const int offRB = 0; return MCTSHelper::getTileAreaRestricted( m_tileArea, offLT, offRB ); } Area MCTSHelper::getTileAreaRestricted( const Area& tileArea, const int offLT, const int offRB ) { return Area( tileArea.x + offLT, tileArea.y + offLT, tileArea.width - ( offLT + offRB ), tileArea.height - ( offLT + offRB ) ); } void MCTSHelper::clipMvToArea( Mv& rcMv, const Area& block, const Area& clipArea, const SPS& sps, const int mvFracBits ) { const int horMax = (clipArea.x + clipArea.width - (int) block.x - (int) block.width) << mvFracBits; const int horMin = (clipArea.x - (int) block.x) << mvFracBits; const int verMax = (clipArea.y + clipArea.height - (int) block.y - (int) block.height) << mvFracBits; const int verMin = (clipArea.y - (int) block.y) << mvFracBits; rcMv.setHor(Clip3(horMin, horMax, rcMv.getHor())); rcMv.setVer(Clip3(verMin, verMax, rcMv.getVer())); } Area MCTSHelper::getTileArea( const CodingStructure* cs, const int ctuAddr ) { const PPS *pps = cs->pps; const int maxCUWidth = cs->pcv->maxCUWidth; const int maxCUHeight = cs->pcv->maxCUHeight; const uint32_t tileIdx = pps->getTileIdx( (uint32_t)ctuAddr ); const uint32_t tileX = tileIdx % pps->getNumTileColumns(); const uint32_t tileY = tileIdx / pps->getNumTileColumns(); const int tileWidthtInCtus = pps->getTileColumnWidth( tileX ); const int tileHeightInCtus = pps->getTileRowHeight ( tileY ); const int tileXPosInCtus = pps->getTileColumnBd( tileX ); const int tileYPosInCtus = pps->getTileRowBd( tileY ); const int tileLeftTopPelPosX = maxCUWidth * tileXPosInCtus; const int tileLeftTopPelPosY = maxCUHeight * tileYPosInCtus; const int tileRightBottomPelPosX = std::min( ( ( tileWidthtInCtus + tileXPosInCtus ) * maxCUWidth ), (int)cs->picture->lwidth() ) - 1; const int tileRightBottomPelPosY = std::min( ( ( tileHeightInCtus + tileYPosInCtus ) * maxCUHeight ), (int)cs->picture->lheight() ) - 1; return Area( tileLeftTopPelPosX, tileLeftTopPelPosY, tileRightBottomPelPosX - tileLeftTopPelPosX + 1, tileRightBottomPelPosY - tileLeftTopPelPosY + 1 ); } bool MCTSHelper::isRefBlockAtRestrictedTileBoundary( const PredictionUnit &pu ) { const Area& tileArea = pu.cs->picture->mctsInfo.getTileArea(); const int mvPrecBits = MV_FRACTIONAL_BITS_INTERNAL; for( int refList = 0; refList < NUM_REF_PIC_LIST_01; refList++ ) { if( pu.refIdx[refList] >= 0 ) { const Mv& mv = pu.mv[refList]; Area targetBlock( pu.Y().offset( mv.getHor() >> mvPrecBits, mv.getVer() >> mvPrecBits ), pu.Y().size() ); // NOTE: at boundary take sub-pel filter samples into account if( targetBlock.x < tileArea.x + 3 || targetBlock.y < tileArea.y + 3 || targetBlock.bottomRight().x > tileArea.bottomRight().x - 4 || targetBlock.bottomRight().y > tileArea.bottomRight().y - 4 ) { return true; } } } return false; } static void getMotInfoBlockPartPos( const PredictionUnit& pu, int xOff, int yOff, const Mv& mv, int& ruiPredXLeft, int& ruiPredYTop, int& ruiPredXRight, int& ruiPredYBottom ) { const int mvFracBits = MV_FRACTIONAL_BITS_INTERNAL; ruiPredXLeft = ( mv.getHor() >> mvFracBits ) + pu.lx() + xOff; ruiPredYTop = ( mv.getVer() >> mvFracBits ) + pu.ly() + yOff; ruiPredXRight = ruiPredXLeft + ( 1 << g_miScaling.posx ) - 1; ruiPredYBottom = ruiPredYTop + ( 1 << g_miScaling.posy ) - 1; } static bool checkMVRange( const Mv& mv, const Area& tileArea, int predXLeft, int predXRight, int predYTop, int predYBottom, ChromaFormat chromaFormat, bool isLuma, bool msgFlag ) { // Filter length of sub-sample generation filter to be considered int sampleOffsetLT, sampleOffsetRB, fracDiv; if( isLuma ) { sampleOffsetLT = 3; sampleOffsetRB = 4; fracDiv = 1 << MV_FRACTIONAL_BITS_INTERNAL; } else { sampleOffsetLT = 2; //1; // 2 in luma coordinates! sampleOffsetRB = 3; //2; // 3 in luma coordinates! fracDiv = 1 << ( MV_FRACTIONAL_BITS_INTERNAL + 1 ); } const int leftTopPelPosX = tileArea.topLeft().x; const int leftTopPelPosY = tileArea.topLeft().y; const int rightBottomPelPosX = tileArea.bottomRight().x; const int rightBottomPelPosY = tileArea.bottomRight().y; const bool isFullPelHor = ( mv.getHor() % fracDiv == 0 ); const bool isFullPelVer = ( mv.getVer() % fracDiv == 0 ); const int rangeXLeft = leftTopPelPosX + ( isFullPelHor ? 0 : sampleOffsetLT ); const int rangeYTop = leftTopPelPosY + ( isFullPelVer ? 0 : sampleOffsetLT ); const int rangeXRight = rightBottomPelPosX - ( isFullPelHor ? 0 : sampleOffsetRB ); const int rangeYBottom = rightBottomPelPosY - ( isFullPelVer ? 0 : sampleOffsetRB ); if( !( predXLeft >= rangeXLeft && predXLeft <= rangeXRight ) || !( predXRight >= rangeXLeft && predXRight <= rangeXRight ) ) { if( msgFlag ) { msg( WARNING, "%s: pu motion vector across tile boundaries MV(%d,%d) RangeLR(%d,%d) PredLR(%d,%d)\n", (isLuma ? "LUMA": "CHROMA"), mv.getHor(), mv.getVer(), rangeXLeft, rangeXRight, predXLeft, predXRight ); } return false; } else if( !( predYTop >= rangeYTop && predYTop <= rangeYBottom ) || !( predYBottom >= rangeYTop && predYBottom <= rangeYBottom ) ) { if( msgFlag ) { msg( WARNING, "%s: pu motion vector across tile boundaries MV(%d,%d) RangeTB(%d,%d) PredTB(%d,%d)\n", (isLuma ? "LUMA" : "CHROMA"), mv.getHor(), mv.getVer(), rangeYTop, rangeYBottom, predYTop, predYBottom ); } return false; } return true; } bool MCTSHelper::checkMvBufferForMCTSConstraint( const PredictionUnit &pu, bool msgFlag ) { const Area& tileArea = pu.cs->picture->mctsInfo.getTileArea(); const ChromaFormat chromaFormat = pu.cs->sps->getChromaFormatIdc(); CMotionBuf mb = pu.getMotionBuf(); const int blkW = 1 << ( g_miScaling.posx ); const int blkH = 1 << ( g_miScaling.posy ); for( int y = 0; y < mb.height; y++ ) { for( int x = 0; x < mb.width; x++ ) { const MotionInfo &mi = mb.at( x, y ); int xOff = x << g_miScaling.posx; int yOff = y << g_miScaling.posy; for( int refList = 0; refList < NUM_REF_PIC_LIST_01; refList++ ) { if( pu.refIdx[refList] >= 0 || mi.refIdx[refList] >= 0 ) { const Mv& mv = mi.mv[refList]; int predXLeft, predYTop, predXRight, predYBottom; getMotInfoBlockPartPos( pu, xOff, yOff, mv, predXLeft, predYTop, predXRight, predYBottom ); if( !checkMVRange( mv, tileArea, predXLeft, predXRight, predYTop, predYBottom, chromaFormat, true, msgFlag ) ) { return false; } // Chroma if( !pu.cu->affine ) { if( !checkMVRange( mv, tileArea, predXLeft, predXRight, predYTop, predYBottom, chromaFormat, false, msgFlag ) ) { return false; } } else { if( x % 2 == 0 && y % 2 == 0 ) { const MotionInfo &miA = mi; const MotionInfo &miB = mb.at( x + 1, y + 1 ); Mv mvAff = miA.mv[refList] + miB.mv[refList]; mvAff >>= 1; getMotInfoBlockPartPos( pu, xOff, yOff, mvAff, predXLeft, predYTop, predXRight, predYBottom ); if( !checkMVRange( mvAff, tileArea, predXLeft, predXRight + blkW, predYTop, predYBottom + blkH, chromaFormat, false, msgFlag ) ) { return false; } } } } } } } return true; } bool MCTSHelper::checkMvIsNotInRestrictedArea( const PredictionUnit &pu, const Mv& mv, const Area& restrArea, const MvPrecision mvPrec ) { CHECKD(mvPrec < MvPrecision::ONE, "Wrong MV precision!"); Mv testMv = mv; testMv >>= mvPrec - MvPrecision::ONE; Area targetArea = pu.Y(); targetArea.repositionTo( targetArea.offset( testMv.getHor(), testMv.getVer() ) ); if( !restrArea.contains( targetArea ) ) { // Skip this pos return false; } return true; } bool MCTSHelper::checkMvForMCTSConstraint( const PredictionUnit &pu, const Mv& mv, const MvPrecision mvPrec ) { return checkMvIsNotInRestrictedArea( pu, mv, pu.cs->picture->mctsInfo.getTileAreaSubPelRestricted( pu ), mvPrec ); } //! \}