/* 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-2013, 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 TEncSampleAdaptiveOffset.cpp \brief estimation part of sample adaptive offset class */ #include "TEncSampleAdaptiveOffset.h" #include #include #include #include //! \ingroup TLibEncoder //! \{ TEncSampleAdaptiveOffset::TEncSampleAdaptiveOffset() { m_pcEntropyCoder = NULL; m_pppcRDSbacCoder = NULL; m_pcRDGoOnSbacCoder = NULL; m_pppcBinCoderCABAC = NULL; m_iCount = NULL; m_iOffset = NULL; m_iOffsetOrg = NULL; m_iRate = NULL; m_iDist = NULL; m_dCost = NULL; m_dCostPartBest = NULL; m_iDistOrg = NULL; m_iTypePartBest = NULL; #if SAO_ENCODING_CHOICE_CHROMA m_depthSaoRate[0][0] = 0; m_depthSaoRate[0][1] = 0; m_depthSaoRate[0][2] = 0; m_depthSaoRate[0][3] = 0; m_depthSaoRate[1][0] = 0; m_depthSaoRate[1][1] = 0; m_depthSaoRate[1][2] = 0; m_depthSaoRate[1][3] = 0; #endif } TEncSampleAdaptiveOffset::~TEncSampleAdaptiveOffset() { } // ==================================================================================================================== // Constants // ==================================================================================================================== // ==================================================================================================================== // Tables // ==================================================================================================================== inline Double xRoundIbdi2(Int bitDepth, Double x) { return ((x)>0) ? (Int)(((Int)(x)+(1<<(bitDepth-8-1)))/(1<<(bitDepth-8))) : ((Int)(((Int)(x)-(1<<(bitDepth-8-1)))/(1<<(bitDepth-8)))); } /** rounding with IBDI * \param x */ inline Double xRoundIbdi(Int bitDepth, Double x) { return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((Int)((x)+0.5)) : ((Int)((x)-0.5)))) ; } /** process SAO for one partition * \param *psQTPart, iPartIdx, dLambda */ Void TEncSampleAdaptiveOffset::rdoSaoOnePart(SAOQTPart *psQTPart, Int iPartIdx, Double dLambda, Int yCbCr) { Int iTypeIdx; Int iNumTotalType = MAX_NUM_SAO_TYPE; SAOQTPart* pOnePart = &(psQTPart[iPartIdx]); Int64 iEstDist; Int iClassIdx; Int uiShift = 2 * DISTORTION_PRECISION_ADJUSTMENT((yCbCr == 0 ? g_bitDepthY : g_bitDepthC)-8); UInt uiDepth = pOnePart->PartLevel; m_iDistOrg [iPartIdx] = 0; Double bestRDCostTableBo = MAX_DOUBLE; Int bestClassTableBo = 0; Int currentDistortionTableBo[MAX_NUM_SAO_CLASS]; Double currentRdCostTableBo[MAX_NUM_SAO_CLASS]; Int addr; Int allowMergeLeft; Int allowMergeUp; Int frameWidthInCU = m_pcPic->getFrameWidthInCU(); SaoLcuParam saoLcuParamRdo; for (iTypeIdx=-1; iTypeIdxload(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]); m_pcRDGoOnSbacCoder->resetBits(); } else { m_pcEntropyCoder->resetEntropy(); m_pcEntropyCoder->resetBits(); } iEstDist = 0; if (iTypeIdx == -1) { for (Int ry = pOnePart->StartCUY; ry<= pOnePart->EndCUY; ry++) { for (Int rx = pOnePart->StartCUX; rx <= pOnePart->EndCUX; rx++) { addr = ry * frameWidthInCU + rx; // get bits for iTypeIdx = -1 allowMergeLeft = 1; allowMergeUp = 1; if (rx != 0) { // check tile id and slice id if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-1) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-1)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx())) { allowMergeLeft = 0; } } if (ry!=0) { if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-m_iNumCuInWidth) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-m_iNumCuInWidth)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx())) { allowMergeUp = 0; } } // reset resetSaoUnit(&saoLcuParamRdo); // set merge flag saoLcuParamRdo.mergeUpFlag = 1; saoLcuParamRdo.mergeLeftFlag = 1; if (ry == pOnePart->StartCUY) { saoLcuParamRdo.mergeUpFlag = 0; } if (rx == pOnePart->StartCUX) { saoLcuParamRdo.mergeLeftFlag = 0; } m_pcEntropyCoder->encodeSaoUnitInterleaving(yCbCr, 1, rx, ry, &saoLcuParamRdo, 1, 1, allowMergeLeft, allowMergeUp); } } } if (iTypeIdx>=0) { iEstDist = estSaoTypeDist(iPartIdx, iTypeIdx, uiShift, dLambda, currentDistortionTableBo, currentRdCostTableBo); if( iTypeIdx == SAO_BO ) { // Estimate Best Position Double currentRDCost = 0.0; for(Int i=0; i< SAO_MAX_BO_CLASSES -SAO_BO_LEN +1; i++) { currentRDCost = 0.0; for(UInt uj = i; uj < i+SAO_BO_LEN; uj++) { currentRDCost += currentRdCostTableBo[uj]; } if( currentRDCost < bestRDCostTableBo) { bestRDCostTableBo = currentRDCost; bestClassTableBo = i; } } // Re code all Offsets // Code Center for(iClassIdx = bestClassTableBo; iClassIdx < bestClassTableBo+SAO_BO_LEN; iClassIdx++) { iEstDist += currentDistortionTableBo[iClassIdx]; } } for (Int ry = pOnePart->StartCUY; ry<= pOnePart->EndCUY; ry++) { for (Int rx = pOnePart->StartCUX; rx <= pOnePart->EndCUX; rx++) { addr = ry * frameWidthInCU + rx; // get bits for iTypeIdx = -1 allowMergeLeft = 1; allowMergeUp = 1; if (rx != 0) { // check tile id and slice id if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-1) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-1)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx())) { allowMergeLeft = 0; } } if (ry!=0) { if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-m_iNumCuInWidth) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-m_iNumCuInWidth)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx())) { allowMergeUp = 0; } } // reset resetSaoUnit(&saoLcuParamRdo); // set merge flag saoLcuParamRdo.mergeUpFlag = 1; saoLcuParamRdo.mergeLeftFlag = 1; if (ry == pOnePart->StartCUY) { saoLcuParamRdo.mergeUpFlag = 0; } if (rx == pOnePart->StartCUX) { saoLcuParamRdo.mergeLeftFlag = 0; } // set type and offsets saoLcuParamRdo.typeIdx = iTypeIdx; saoLcuParamRdo.subTypeIdx = (iTypeIdx==SAO_BO)?bestClassTableBo:0; saoLcuParamRdo.length = m_iNumClass[iTypeIdx]; for (iClassIdx = 0; iClassIdx < saoLcuParamRdo.length; iClassIdx++) { saoLcuParamRdo.offset[iClassIdx] = (Int)m_iOffset[iPartIdx][iTypeIdx][iClassIdx+saoLcuParamRdo.subTypeIdx+1]; } m_pcEntropyCoder->encodeSaoUnitInterleaving(yCbCr, 1, rx, ry, &saoLcuParamRdo, 1, 1, allowMergeLeft, allowMergeUp); } } m_iDist[iPartIdx][iTypeIdx] = iEstDist; m_iRate[iPartIdx][iTypeIdx] = m_pcEntropyCoder->getNumberOfWrittenBits(); m_dCost[iPartIdx][iTypeIdx] = (Double)((Double)m_iDist[iPartIdx][iTypeIdx] + dLambda * (Double) m_iRate[iPartIdx][iTypeIdx]); if(m_dCost[iPartIdx][iTypeIdx] < m_dCostPartBest[iPartIdx]) { m_iDistOrg [iPartIdx] = 0; m_dCostPartBest[iPartIdx] = m_dCost[iPartIdx][iTypeIdx]; m_iTypePartBest[iPartIdx] = iTypeIdx; if( m_bUseSBACRD ) m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[pOnePart->PartLevel][CI_TEMP_BEST] ); } } else { if(m_iDistOrg[iPartIdx] < m_dCostPartBest[iPartIdx] ) { m_dCostPartBest[iPartIdx] = (Double) m_iDistOrg[iPartIdx] + m_pcEntropyCoder->getNumberOfWrittenBits()*dLambda ; m_iTypePartBest[iPartIdx] = -1; if( m_bUseSBACRD ) { m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[pOnePart->PartLevel][CI_TEMP_BEST] ); } } } } pOnePart->bProcessed = true; pOnePart->bSplit = false; pOnePart->iMinDist = m_iTypePartBest[iPartIdx] >= 0 ? m_iDist[iPartIdx][m_iTypePartBest[iPartIdx]] : m_iDistOrg[iPartIdx]; pOnePart->iMinRate = (Int) (m_iTypePartBest[iPartIdx] >= 0 ? m_iRate[iPartIdx][m_iTypePartBest[iPartIdx]] : 0); pOnePart->dMinCost = pOnePart->iMinDist + dLambda * pOnePart->iMinRate; pOnePart->iBestType = m_iTypePartBest[iPartIdx]; if (pOnePart->iBestType != -1) { // pOnePart->bEnableFlag = 1; pOnePart->iLength = m_iNumClass[pOnePart->iBestType]; Int minIndex = 0; if( pOnePart->iBestType == SAO_BO ) { pOnePart->subTypeIdx = bestClassTableBo; minIndex = pOnePart->subTypeIdx; } for (Int i=0; i< pOnePart->iLength ; i++) { pOnePart->iOffset[i] = (Int) m_iOffset[iPartIdx][pOnePart->iBestType][minIndex+i+1]; } } else { // pOnePart->bEnableFlag = 0; pOnePart->iLength = 0; } } /** Run partition tree disable */ Void TEncSampleAdaptiveOffset::disablePartTree(SAOQTPart *psQTPart, Int iPartIdx) { SAOQTPart* pOnePart= &(psQTPart[iPartIdx]); pOnePart->bSplit = false; pOnePart->iLength = 0; pOnePart->iBestType = -1; if (pOnePart->PartLevel < m_uiMaxSplitLevel) { for (Int i=0; iDownPartsIdx[i]); } } } /** Run quadtree decision function * \param iPartIdx, pcPicOrg, pcPicDec, pcPicRest, &dCostFinal */ Void TEncSampleAdaptiveOffset::runQuadTreeDecision(SAOQTPart *psQTPart, Int iPartIdx, Double &dCostFinal, Int iMaxLevel, Double dLambda, Int yCbCr) { SAOQTPart* pOnePart = &(psQTPart[iPartIdx]); UInt uiDepth = pOnePart->PartLevel; UInt uhNextDepth = uiDepth+1; if (iPartIdx == 0) { dCostFinal = 0; } //SAO for this part if(!pOnePart->bProcessed) { rdoSaoOnePart (psQTPart, iPartIdx, dLambda, yCbCr); } //SAO for sub 4 parts if (pOnePart->PartLevel < iMaxLevel) { Double dCostNotSplit = dLambda + pOnePart->dMinCost; Double dCostSplit = dLambda; for (Int i=0; i< NUM_DOWN_PART ;i++) { if( m_bUseSBACRD ) { if ( 0 == i) //initialize RD with previous depth buffer { m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]); } else { m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); } } runQuadTreeDecision(psQTPart, pOnePart->DownPartsIdx[i], dCostFinal, iMaxLevel, dLambda, yCbCr); dCostSplit += dCostFinal; if( m_bUseSBACRD ) { m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_TEMP_BEST]); } } if(dCostSplit < dCostNotSplit) { dCostFinal = dCostSplit; pOnePart->bSplit = true; pOnePart->iLength = 0; pOnePart->iBestType = -1; if( m_bUseSBACRD ) { m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); } } else { dCostFinal = dCostNotSplit; pOnePart->bSplit = false; for (Int i=0; iDownPartsIdx[i]); } if( m_bUseSBACRD ) { m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]); } } } else { dCostFinal = pOnePart->dMinCost; } } /** delete allocated memory of TEncSampleAdaptiveOffset class. */ Void TEncSampleAdaptiveOffset::destroyEncBuffer() { for (Int i=0;iinit( m_pppcBinCoderCABAC [iDepth][iCIIdx] ); } } } /** Start SAO encoder * \param pcPic, pcEntropyCoder, pppcRDSbacCoder, pcRDGoOnSbacCoder */ Void TEncSampleAdaptiveOffset::startSaoEnc( TComPic* pcPic, TEncEntropy* pcEntropyCoder, TEncSbac*** pppcRDSbacCoder, TEncSbac* pcRDGoOnSbacCoder) { m_bUseSBACRD = true; m_pcPic = pcPic; m_pcEntropyCoder = pcEntropyCoder; m_pcRDGoOnSbacCoder = pcRDGoOnSbacCoder; m_pcEntropyCoder->setEntropyCoder(m_pcRDGoOnSbacCoder, pcPic->getSlice(0)); m_pcEntropyCoder->resetEntropy(); m_pcEntropyCoder->resetBits(); if( m_bUseSBACRD ) { m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[0][CI_NEXT_BEST]); m_pppcRDSbacCoder[0][CI_CURR_BEST]->load( m_pppcRDSbacCoder[0][CI_NEXT_BEST]); } } /** End SAO encoder */ Void TEncSampleAdaptiveOffset::endSaoEnc() { m_pcPic = NULL; m_pcEntropyCoder = NULL; } inline Int xSign(Int x) { return ((x >> 31) | ((Int)( (((UInt) -x)) >> 31))); } /** Calculate SAO statistics for non-cross-slice or non-cross-tile processing * \param pRecStart to-be-filtered block buffer pointer * \param pOrgStart original block buffer pointer * \param stride picture buffer stride * \param ppStat statistics buffer * \param ppCount counter buffer * \param width block width * \param height block height * \param pbBorderAvail availabilities of block border pixels */ Void TEncSampleAdaptiveOffset::calcSaoStatsBlock( Pel* pRecStart, Pel* pOrgStart, Int stride, Int64** ppStats, Int64** ppCount, UInt width, UInt height, Bool* pbBorderAvail, Int iYCbCr) { Int64 *stats, *count; Int classIdx, posShift, startX, endX, startY, endY, signLeft,signRight,signDown,signDown1; Pel *pOrg, *pRec; UInt edgeType; Int x, y; Pel *pTableBo = (iYCbCr==0)?m_lumaTableBo:m_chromaTableBo; //--------- Band offset-----------// stats = ppStats[SAO_BO]; count = ppCount[SAO_BO]; pOrg = pOrgStart; pRec = pRecStart; for (y=0; y< height; y++) { for (x=0; x< width; x++) { classIdx = pTableBo[pRec[x]]; if (classIdx) { stats[classIdx] += (pOrg[x] - pRec[x]); count[classIdx] ++; } } pOrg += stride; pRec += stride; } //---------- Edge offset 0--------------// stats = ppStats[SAO_EO_0]; count = ppCount[SAO_EO_0]; pOrg = pOrgStart; pRec = pRecStart; startX = (pbBorderAvail[SGU_L]) ? 0 : 1; endX = (pbBorderAvail[SGU_R]) ? width : (width -1); for (y=0; y< height; y++) { signLeft = xSign(pRec[startX] - pRec[startX-1]); for (x=startX; x< endX; x++) { signRight = xSign(pRec[x] - pRec[x+1]); edgeType = signRight + signLeft + 2; signLeft = -signRight; stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]); count[m_auiEoTable[edgeType]] ++; } pRec += stride; pOrg += stride; } //---------- Edge offset 1--------------// stats = ppStats[SAO_EO_1]; count = ppCount[SAO_EO_1]; pOrg = pOrgStart; pRec = pRecStart; startY = (pbBorderAvail[SGU_T]) ? 0 : 1; endY = (pbBorderAvail[SGU_B]) ? height : height-1; if (!pbBorderAvail[SGU_T]) { pRec += stride; pOrg += stride; } for (x=0; x< width; x++) { m_iUpBuff1[x] = xSign(pRec[x] - pRec[x-stride]); } for (y=startY; ygetCStride()):(m_pcPic->getStride()); Pel* pPicOrg = getPicYuvAddr (m_pcPic->getPicYuvOrg(), iYCbCr); Pel* pPicRec = getPicYuvAddr(m_pcYuvTmp, iYCbCr); std::vector& vFilterBlocks = *(m_pcPic->getCU(iAddr)->getNDBFilterBlocks()); //variables UInt xPos, yPos, width, height; Bool* pbBorderAvail; UInt posOffset; for(Int i=0; i< vFilterBlocks.size(); i++) { xPos = vFilterBlocks[i].posX >> isChroma; yPos = vFilterBlocks[i].posY >> isChroma; width = vFilterBlocks[i].width >> isChroma; height = vFilterBlocks[i].height >> isChroma; pbBorderAvail = vFilterBlocks[i].isBorderAvailable; posOffset = (yPos* stride) + xPos; calcSaoStatsBlock(pPicRec+ posOffset, pPicOrg+ posOffset, stride, ppStats, ppCount,width, height, pbBorderAvail, iYCbCr); } } } /** Calculate SAO statistics for current LCU without non-crossing slice * \param iAddr, iPartIdx, iYCbCr */ Void TEncSampleAdaptiveOffset::calcSaoStatsCuOrg(Int iAddr, Int iPartIdx, Int iYCbCr) { Int x,y; TComDataCU *pTmpCu = m_pcPic->getCU(iAddr); TComSPS *pTmpSPS = m_pcPic->getSlice(0)->getSPS(); Pel* pOrg; Pel* pRec; Int iStride; Int iLcuHeight = pTmpSPS->getMaxCUHeight(); Int iLcuWidth = pTmpSPS->getMaxCUWidth(); UInt uiLPelX = pTmpCu->getCUPelX(); UInt uiTPelY = pTmpCu->getCUPelY(); UInt uiRPelX; UInt uiBPelY; Int64* iStats; Int64* iCount; Int iClassIdx; Int iPicWidthTmp; Int iPicHeightTmp; Int iStartX; Int iStartY; Int iEndX; Int iEndY; Pel* pTableBo = (iYCbCr==0)?m_lumaTableBo:m_chromaTableBo; Int iIsChroma = (iYCbCr!=0)? 1:0; Int numSkipLine = iIsChroma? 2:4; if (m_saoLcuBasedOptimization == 0) { numSkipLine = 0; } Int numSkipLineRight = iIsChroma? 3:5; if (m_saoLcuBasedOptimization == 0) { numSkipLineRight = 0; } iPicWidthTmp = m_iPicWidth >> iIsChroma; iPicHeightTmp = m_iPicHeight >> iIsChroma; iLcuWidth = iLcuWidth >> iIsChroma; iLcuHeight = iLcuHeight >> iIsChroma; uiLPelX = uiLPelX >> iIsChroma; uiTPelY = uiTPelY >> iIsChroma; uiRPelX = uiLPelX + iLcuWidth ; uiBPelY = uiTPelY + iLcuHeight ; uiRPelX = uiRPelX > iPicWidthTmp ? iPicWidthTmp : uiRPelX; uiBPelY = uiBPelY > iPicHeightTmp ? iPicHeightTmp : uiBPelY; iLcuWidth = uiRPelX - uiLPelX; iLcuHeight = uiBPelY - uiTPelY; iStride = (iYCbCr == 0)? m_pcPic->getStride(): m_pcPic->getCStride(); //if(iSaoType == BO_0 || iSaoType == BO_1) { if( m_saoLcuBasedOptimization && m_saoLcuBoundary ) { numSkipLine = iIsChroma? 1:3; numSkipLineRight = iIsChroma? 2:4; } iStats = m_iOffsetOrg[iPartIdx][SAO_BO]; iCount = m_iCount [iPartIdx][SAO_BO]; pOrg = getPicYuvAddr(m_pcPic->getPicYuvOrg(), iYCbCr, iAddr); pRec = getPicYuvAddr(m_pcPic->getPicYuvRec(), iYCbCr, iAddr); iEndX = (uiRPelX == iPicWidthTmp) ? iLcuWidth : iLcuWidth-numSkipLineRight; iEndY = (uiBPelY == iPicHeightTmp) ? iLcuHeight : iLcuHeight-numSkipLine; for (y=0; ygetPicYuvOrg(), iYCbCr, iAddr); pRec = getPicYuvAddr(m_pcPic->getPicYuvRec(), iYCbCr, iAddr); iStartX = (uiLPelX == 0) ? 1 : 0; iEndX = (uiRPelX == iPicWidthTmp) ? iLcuWidth-1 : iLcuWidth-numSkipLineRight; for (y=0; ygetPicYuvOrg(), iYCbCr, iAddr); pRec = getPicYuvAddr(m_pcPic->getPicYuvRec(), iYCbCr, iAddr); iStartY = (uiTPelY == 0) ? 1 : 0; iEndX = (uiRPelX == iPicWidthTmp) ? iLcuWidth : iLcuWidth-numSkipLineRight; iEndY = (uiBPelY == iPicHeightTmp) ? iLcuHeight-1 : iLcuHeight-numSkipLine; if (uiTPelY == 0) { pOrg += iStride; pRec += iStride; } for (x=0; x< iLcuWidth; x++) { m_iUpBuff1[x] = xSign(pRec[x] - pRec[x-iStride]); } for (y=iStartY; ygetPicYuvOrg(), iYCbCr, iAddr); pRec = getPicYuvAddr(m_pcPic->getPicYuvRec(), iYCbCr, iAddr); iStartX = (uiLPelX == 0) ? 1 : 0; iEndX = (uiRPelX == iPicWidthTmp) ? iLcuWidth-1 : iLcuWidth-numSkipLineRight; iStartY = (uiTPelY == 0) ? 1 : 0; iEndY = (uiBPelY == iPicHeightTmp) ? iLcuHeight-1 : iLcuHeight-numSkipLine; if (uiTPelY == 0) { pOrg += iStride; pRec += iStride; } for (x=iStartX; xgetPicYuvOrg(), iYCbCr, iAddr); pRec = getPicYuvAddr(m_pcPic->getPicYuvRec(), iYCbCr, iAddr); iStartX = (uiLPelX == 0) ? 1 : 0; iEndX = (uiRPelX == iPicWidthTmp) ? iLcuWidth-1 : iLcuWidth-numSkipLineRight; iStartY = (uiTPelY == 0) ? 1 : 0; iEndY = (uiBPelY == iPicHeightTmp) ? iLcuHeight-1 : iLcuHeight-numSkipLine; if (iStartY == 1) { pOrg += iStride; pRec += iStride; } for (x=iStartX-1; xgetSlice(0)->getSPS(); Pel* pOrg; Pel* pRec; Int stride; Int lcuHeight = pTmpSPS->getMaxCUHeight(); Int lcuWidth = pTmpSPS->getMaxCUWidth(); UInt rPelX; UInt bPelY; Int64* stats; Int64* count; Int classIdx; Int picWidthTmp = 0; Int picHeightTmp = 0; Int startX; Int startY; Int endX; Int endY; Int firstX, firstY; Int idxY; Int idxX; Int frameHeightInCU = m_iNumCuInHeight; Int frameWidthInCU = m_iNumCuInWidth; Int j, k; Int isChroma; Int numSkipLine, numSkipLineRight; UInt lPelX, tPelY; TComDataCU *pTmpCu; Pel* pTableBo; for (idxY = 0; idxY< frameHeightInCU; idxY++) { for (idxX = 0; idxX< frameWidthInCU; idxX++) { lcuHeight = pTmpSPS->getMaxCUHeight(); lcuWidth = pTmpSPS->getMaxCUWidth(); addr = idxX + frameWidthInCU*idxY; pTmpCu = pcPic->getCU(addr); lPelX = pTmpCu->getCUPelX(); tPelY = pTmpCu->getCUPelY(); for( yCbCr = 0; yCbCr < 3; yCbCr++ ) { isChroma = (yCbCr!=0)? 1:0; for ( j=0;j> isChroma; picHeightTmp = m_iPicHeight >> isChroma; lcuWidth = lcuWidth >> isChroma; lcuHeight = lcuHeight >> isChroma; lPelX = lPelX >> isChroma; tPelY = tPelY >> isChroma; } rPelX = lPelX + lcuWidth ; bPelY = tPelY + lcuHeight ; rPelX = rPelX > picWidthTmp ? picWidthTmp : rPelX; bPelY = bPelY > picHeightTmp ? picHeightTmp : bPelY; lcuWidth = rPelX - lPelX; lcuHeight = bPelY - tPelY; stride = (yCbCr == 0)? pcPic->getStride(): pcPic->getCStride(); pTableBo = (yCbCr==0)?m_lumaTableBo:m_chromaTableBo; //if(iSaoType == BO) numSkipLine = isChroma? 1:3; numSkipLineRight = isChroma? 2:4; stats = m_offsetOrg_PreDblk[addr][yCbCr][SAO_BO]; count = m_count_PreDblk[addr][yCbCr][SAO_BO]; pOrg = getPicYuvAddr(pcPic->getPicYuvOrg(), yCbCr, addr); pRec = getPicYuvAddr(pcPic->getPicYuvRec(), yCbCr, addr); startX = (rPelX == picWidthTmp) ? lcuWidth : lcuWidth-numSkipLineRight; startY = (bPelY == picHeightTmp) ? lcuHeight : lcuHeight-numSkipLine; for (y=0; ygetPicYuvOrg(), yCbCr, addr); pRec = getPicYuvAddr(pcPic->getPicYuvRec(), yCbCr, addr); startX = (rPelX == picWidthTmp) ? lcuWidth-1 : lcuWidth-numSkipLineRight; startY = (bPelY == picHeightTmp) ? lcuHeight : lcuHeight-numSkipLine; firstX = (lPelX == 0) ? 1 : 0; endX = (rPelX == picWidthTmp) ? lcuWidth-1 : lcuWidth; for (y=0; ygetPicYuvOrg(), yCbCr, addr); pRec = getPicYuvAddr(pcPic->getPicYuvRec(), yCbCr, addr); startX = (rPelX == picWidthTmp) ? lcuWidth : lcuWidth-numSkipLineRight; startY = (bPelY == picHeightTmp) ? lcuHeight-1 : lcuHeight-numSkipLine; firstY = (tPelY == 0) ? 1 : 0; endY = (bPelY == picHeightTmp) ? lcuHeight-1 : lcuHeight; if (firstY == 1) { pOrg += stride; pRec += stride; } for (x=0; x< lcuWidth; x++) { m_iUpBuff1[x] = xSign(pRec[x] - pRec[x-stride]); } for (y=firstY; ygetPicYuvOrg(), yCbCr, addr); pRec = getPicYuvAddr(pcPic->getPicYuvRec(), yCbCr, addr); startX = (rPelX == picWidthTmp) ? lcuWidth-1 : lcuWidth-numSkipLineRight; startY = (bPelY == picHeightTmp) ? lcuHeight-1 : lcuHeight-numSkipLine; firstX = (lPelX == 0) ? 1 : 0; firstY = (tPelY == 0) ? 1 : 0; endX = (rPelX == picWidthTmp) ? lcuWidth-1 : lcuWidth; endY = (bPelY == picHeightTmp) ? lcuHeight-1 : lcuHeight; if (firstY == 1) { pOrg += stride; pRec += stride; } for (x=firstX; xgetPicYuvOrg(), yCbCr, addr); pRec = getPicYuvAddr(pcPic->getPicYuvRec(), yCbCr, addr); startX = (rPelX == picWidthTmp) ? lcuWidth-1 : lcuWidth-numSkipLineRight; startY = (bPelY == picHeightTmp) ? lcuHeight-1 : lcuHeight-numSkipLine; firstX = (lPelX == 0) ? 1 : 0; firstY = (tPelY == 0) ? 1 : 0; endX = (rPelX == picWidthTmp) ? lcuWidth-1 : lcuWidth; endY = (bPelY == picHeightTmp) ? lcuHeight-1 : lcuHeight; if (firstY == 1) { pOrg += stride; pRec += stride; } for (x=firstX-1; xgetFrameWidthInCU(); Int iDownPartIdx; Int iPartStart; Int iPartEnd; SAOQTPart* pOnePart; if (m_uiMaxSplitLevel == 0) { iPartIdx = 0; pOnePart = &(psQTPart[iPartIdx]); for (LcuIdxY = pOnePart->StartCUY; LcuIdxY<= pOnePart->EndCUY; LcuIdxY++) { for (LcuIdxX = pOnePart->StartCUX; LcuIdxX<= pOnePart->EndCUX; LcuIdxX++) { iAddr = LcuIdxY*iFrameWidthInCU + LcuIdxX; calcSaoStatsCu(iAddr, iPartIdx, iYCbCr); } } } else { for(iPartIdx=m_aiNumCulPartsLevel[m_uiMaxSplitLevel-1]; iPartIdxStartCUY; LcuIdxY<= pOnePart->EndCUY; LcuIdxY++) { for (LcuIdxX = pOnePart->StartCUX; LcuIdxX<= pOnePart->EndCUX; LcuIdxX++) { iAddr = LcuIdxY*iFrameWidthInCU + LcuIdxX; calcSaoStatsCu(iAddr, iPartIdx, iYCbCr); } } } for (iLevelIdx = m_uiMaxSplitLevel-1; iLevelIdx>=0; iLevelIdx-- ) { iPartStart = (iLevelIdx > 0) ? m_aiNumCulPartsLevel[iLevelIdx-1] : 0; iPartEnd = m_aiNumCulPartsLevel[iLevelIdx]; for(iPartIdx = iPartStart; iPartIdx < iPartEnd; iPartIdx++) { pOnePart = &(psQTPart[iPartIdx]); for (i=0; i< NUM_DOWN_PART; i++) { iDownPartIdx = pOnePart->DownPartsIdx[i]; for (iTypeIdx=0; iTypeIdxgetPicYuvRec()->copyToPic(m_pcYuvTmp); } m_uiSaoBitIncreaseY = max(g_bitDepthY - 10, 0); m_uiSaoBitIncreaseC = max(g_bitDepthC - 10, 0); m_iOffsetThY = 1 << min(g_bitDepthY - 5, 5); m_iOffsetThC = 1 << min(g_bitDepthC - 5, 5); resetSAOParam(pcSaoParam); if( !m_saoLcuBasedOptimization || !m_saoLcuBoundary ) { resetStats(); } Double dCostFinal = 0; if ( m_saoLcuBasedOptimization) { #if SAO_ENCODING_CHOICE rdoSaoUnitAll(pcSaoParam, dLambdaLuma, dLambdaChroma, depth); #else rdoSaoUnitAll(pcSaoParam, dLambdaLuma, dLambdaChroma); #endif } else { pcSaoParam->bSaoFlag[0] = 1; pcSaoParam->bSaoFlag[1] = 0; dCostFinal = 0; Double lambdaRdo = dLambdaLuma; resetStats(); getSaoStats(pcSaoParam->psSaoPart[0], 0); runQuadTreeDecision(pcSaoParam->psSaoPart[0], 0, dCostFinal, m_uiMaxSplitLevel, lambdaRdo, 0); pcSaoParam->bSaoFlag[0] = dCostFinal < 0 ? 1:0; if(pcSaoParam->bSaoFlag[0]) { convertQT2SaoUnit(pcSaoParam, 0, 0); assignSaoUnitSyntax(pcSaoParam->saoLcuParam[0], pcSaoParam->psSaoPart[0], pcSaoParam->oneUnitFlag[0], 0); } } if (pcSaoParam->bSaoFlag[0]) { processSaoUnitAll( pcSaoParam->saoLcuParam[0], pcSaoParam->oneUnitFlag[0], 0); } if (pcSaoParam->bSaoFlag[1]) { processSaoUnitAll( pcSaoParam->saoLcuParam[1], pcSaoParam->oneUnitFlag[1], 1); processSaoUnitAll( pcSaoParam->saoLcuParam[2], pcSaoParam->oneUnitFlag[2], 2); } } /** Check merge SAO unit * \param saoUnitCurr current SAO unit * \param saoUnitCheck SAO unit tobe check * \param dir direction */ Void TEncSampleAdaptiveOffset::checkMerge(SaoLcuParam * saoUnitCurr, SaoLcuParam * saoUnitCheck, Int dir) { Int i ; Int countDiff = 0; if (saoUnitCurr->partIdx != saoUnitCheck->partIdx) { if (saoUnitCurr->typeIdx !=-1) { if (saoUnitCurr->typeIdx == saoUnitCheck->typeIdx) { for (i=0;ilength;i++) { countDiff += (saoUnitCurr->offset[i] != saoUnitCheck->offset[i]); } countDiff += (saoUnitCurr->subTypeIdx != saoUnitCheck->subTypeIdx); if (countDiff ==0) { saoUnitCurr->partIdx = saoUnitCheck->partIdx; if (dir == 1) { saoUnitCurr->mergeUpFlag = 1; saoUnitCurr->mergeLeftFlag = 0; } else { saoUnitCurr->mergeUpFlag = 0; saoUnitCurr->mergeLeftFlag = 1; } } } } else { if (saoUnitCurr->typeIdx == saoUnitCheck->typeIdx) { saoUnitCurr->partIdx = saoUnitCheck->partIdx; if (dir == 1) { saoUnitCurr->mergeUpFlag = 1; saoUnitCurr->mergeLeftFlag = 0; } else { saoUnitCurr->mergeUpFlag = 0; saoUnitCurr->mergeLeftFlag = 1; } } } } } /** Assign SAO unit syntax from picture-based algorithm * \param saoLcuParam SAO LCU parameters * \param saoPart SAO part * \param oneUnitFlag SAO one unit flag * \param iYCbCr color component Index */ Void TEncSampleAdaptiveOffset::assignSaoUnitSyntax(SaoLcuParam* saoLcuParam, SAOQTPart* saoPart, Bool &oneUnitFlag, Int yCbCr) { if (saoPart->bSplit == 0) { oneUnitFlag = 1; } else { Int i,j, addr, addrUp, addrLeft, idx, idxUp, idxLeft, idxCount; oneUnitFlag = 0; idxCount = -1; saoLcuParam[0].mergeUpFlag = 0; saoLcuParam[0].mergeLeftFlag = 0; for (j=0;jnumCuInHeight; Int frameWidthInCU = saoParam->numCuInWidth; Int j, k; Int addr = 0; Int addrUp = -1; Int addrLeft = -1; Int compIdx = 0; SaoLcuParam mergeSaoParam[3][2]; Double compDistortion[3]; saoParam->bSaoFlag[0] = true; saoParam->bSaoFlag[1] = true; saoParam->oneUnitFlag[0] = false; saoParam->oneUnitFlag[1] = false; saoParam->oneUnitFlag[2] = false; #if SAO_ENCODING_CHOICE #if SAO_ENCODING_CHOICE_CHROMA Int numNoSao[2]; numNoSao[0] = 0;// Luma numNoSao[1] = 0;// Chroma if( depth > 0 && m_depthSaoRate[0][depth-1] > SAO_ENCODING_RATE ) { saoParam->bSaoFlag[0] = false; } if( depth > 0 && m_depthSaoRate[1][depth-1] > SAO_ENCODING_RATE_CHROMA ) { saoParam->bSaoFlag[1] = false; } #else Int numNoSao = 0; if( depth > 0 && m_depth0SaoRate > SAO_ENCODING_RATE ) { saoParam->bSaoFlag[0] = false; saoParam->bSaoFlag[1] = false; } #endif #endif for (idxY = 0; idxY< frameHeightInCU; idxY++) { for (idxX = 0; idxX< frameWidthInCU; idxX++) { addr = idxX + frameWidthInCU*idxY; addrUp = addr < frameWidthInCU ? -1:idxX + frameWidthInCU*(idxY-1); addrLeft = idxX == 0 ? -1:idxX-1 + frameWidthInCU*idxY; Int allowMergeLeft = 1; Int allowMergeUp = 1; UInt rate; Double bestCost, mergeCost; if (idxX!=0) { // check tile id and slice id if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-1) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-1)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx())) { allowMergeLeft = 0; } } else { allowMergeLeft = 0; } if (idxY!=0) { if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-m_iNumCuInWidth) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-m_iNumCuInWidth)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx())) { allowMergeUp = 0; } } else { allowMergeUp = 0; } compDistortion[0] = 0; compDistortion[1] = 0; compDistortion[2] = 0; m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]); if (allowMergeLeft) { m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0); } if (allowMergeUp) { m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0); } m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[0][CI_TEMP_BEST] ); // reset stats Y, Cb, Cr for ( compIdx=0;compIdx<3;compIdx++) { for ( j=0;jsaoLcuParam[compIdx][addr].typeIdx = -1; saoParam->saoLcuParam[compIdx][addr].mergeUpFlag = 0; saoParam->saoLcuParam[compIdx][addr].mergeLeftFlag = 0; saoParam->saoLcuParam[compIdx][addr].subTypeIdx = 0; #if SAO_ENCODING_CHOICE if( (compIdx ==0 && saoParam->bSaoFlag[0])|| (compIdx >0 && saoParam->bSaoFlag[1]) ) #endif { calcSaoStatsCu(addr, compIdx, compIdx); } } saoComponentParamDist(allowMergeLeft, allowMergeUp, saoParam, addr, addrUp, addrLeft, 0, lambda, &mergeSaoParam[0][0], &compDistortion[0]); sao2ChromaParamDist(allowMergeLeft, allowMergeUp, saoParam, addr, addrUp, addrLeft, lambdaChroma, &mergeSaoParam[1][0], &mergeSaoParam[2][0], &compDistortion[0]); if( saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1] ) { // Cost of new SAO_params m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]); m_pcRDGoOnSbacCoder->resetBits(); if (allowMergeLeft) { m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0); } if (allowMergeUp) { m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0); } for ( compIdx=0;compIdx<3;compIdx++) { if( (compIdx ==0 && saoParam->bSaoFlag[0]) || (compIdx >0 && saoParam->bSaoFlag[1])) { m_pcEntropyCoder->encodeSaoOffset(&saoParam->saoLcuParam[compIdx][addr], compIdx); } } rate = m_pcEntropyCoder->getNumberOfWrittenBits(); bestCost = compDistortion[0] + (Double)rate; m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); // Cost of Merge for(Int mergeUp=0; mergeUp<2; ++mergeUp) { if ( (allowMergeLeft && (mergeUp==0)) || (allowMergeUp && (mergeUp==1)) ) { m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]); m_pcRDGoOnSbacCoder->resetBits(); if (allowMergeLeft) { m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(1-mergeUp); } if ( allowMergeUp && (mergeUp==1) ) { m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(1); } rate = m_pcEntropyCoder->getNumberOfWrittenBits(); mergeCost = compDistortion[mergeUp+1] + (Double)rate; if (mergeCost < bestCost) { bestCost = mergeCost; m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); for ( compIdx=0;compIdx<3;compIdx++) { mergeSaoParam[compIdx][mergeUp].mergeLeftFlag = 1-mergeUp; mergeSaoParam[compIdx][mergeUp].mergeUpFlag = mergeUp; if( (compIdx==0 && saoParam->bSaoFlag[0]) || (compIdx>0 && saoParam->bSaoFlag[1])) { copySaoUnit(&saoParam->saoLcuParam[compIdx][addr], &mergeSaoParam[compIdx][mergeUp] ); } } } } } #if SAO_ENCODING_CHOICE #if SAO_ENCODING_CHOICE_CHROMA if( saoParam->saoLcuParam[0][addr].typeIdx == -1) { numNoSao[0]++; } if( saoParam->saoLcuParam[1][addr].typeIdx == -1) { numNoSao[1]+=2; } #else for ( compIdx=0;compIdx<3;compIdx++) { if( depth == 0 && saoParam->saoLcuParam[compIdx][addr].typeIdx == -1) { numNoSao++; } } #endif #endif m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[0][CI_CURR_BEST]); } } } #if SAO_ENCODING_CHOICE #if SAO_ENCODING_CHOICE_CHROMA if( !saoParam->bSaoFlag[0]) { m_depthSaoRate[0][depth] = 1.0; } else { m_depthSaoRate[0][depth] = numNoSao[0]/((Double) frameHeightInCU*frameWidthInCU); } if( !saoParam->bSaoFlag[1]) { m_depthSaoRate[1][depth] = 1.0; } else { m_depthSaoRate[1][depth] = numNoSao[1]/((Double) frameHeightInCU*frameWidthInCU*2); } #else if( depth == 0) { // update SAO Rate m_depth0SaoRate = numNoSao/((Double) frameHeightInCU*frameWidthInCU*3); } #endif #endif } /** rate distortion optimization of SAO unit * \param saoParam SAO parameters * \param addr address * \param addrUp above address * \param addrLeft left address * \param yCbCr color component index * \param lambda */ inline Int64 TEncSampleAdaptiveOffset::estSaoTypeDist(Int compIdx, Int typeIdx, Int shift, Double lambda, Int *currentDistortionTableBo, Double *currentRdCostTableBo) { Int64 estDist = 0; Int classIdx; Int bitDepth = (compIdx==0) ? g_bitDepthY : g_bitDepthC; Int saoBitIncrease = (compIdx==0) ? m_uiSaoBitIncreaseY : m_uiSaoBitIncreaseC; Int saoOffsetTh = (compIdx==0) ? m_iOffsetThY : m_iOffsetThC; for(classIdx=1; classIdx < ( (typeIdx < SAO_BO) ? m_iNumClass[typeIdx]+1 : SAO_MAX_BO_CLASSES+1); classIdx++) { if( typeIdx == SAO_BO) { currentDistortionTableBo[classIdx-1] = 0; currentRdCostTableBo[classIdx-1] = lambda; } if(m_iCount [compIdx][typeIdx][classIdx]) { m_iOffset[compIdx][typeIdx][classIdx] = (Int64) xRoundIbdi(bitDepth, (Double)(m_iOffsetOrg[compIdx][typeIdx][classIdx]<<(bitDepth-8)) / (Double)(m_iCount [compIdx][typeIdx][classIdx]<0 && classIdx>=3) { m_iOffset[compIdx][typeIdx][classIdx] = 0; } } m_iOffset[compIdx][typeIdx][classIdx] = estIterOffset( typeIdx, classIdx, lambda, m_iOffset[compIdx][typeIdx][classIdx], m_iCount [compIdx][typeIdx][classIdx], m_iOffsetOrg[compIdx][typeIdx][classIdx], shift, saoBitIncrease, currentDistortionTableBo, currentRdCostTableBo, saoOffsetTh ); } else { m_iOffsetOrg[compIdx][typeIdx][classIdx] = 0; m_iOffset[compIdx][typeIdx][classIdx] = 0; } if( typeIdx != SAO_BO ) { estDist += estSaoDist( m_iCount [compIdx][typeIdx][classIdx], m_iOffset[compIdx][typeIdx][classIdx] << saoBitIncrease, m_iOffsetOrg[compIdx][typeIdx][classIdx], shift); } } return estDist; } inline Int64 TEncSampleAdaptiveOffset::estSaoDist(Int64 count, Int64 offset, Int64 offsetOrg, Int shift) { return (( count*offset*offset-offsetOrg*offset*2 ) >> shift); } inline Int64 TEncSampleAdaptiveOffset::estIterOffset(Int typeIdx, Int classIdx, Double lambda, Int64 offsetInput, Int64 count, Int64 offsetOrg, Int shift, Int bitIncrease, Int *currentDistortionTableBo, Double *currentRdCostTableBo, Int offsetTh ) { //Clean up, best_q_offset. Int64 iterOffset, tempOffset; Int64 tempDist, tempRate; Double tempCost, tempMinCost; Int64 offsetOutput = 0; iterOffset = offsetInput; // Assuming sending quantized value 0 results in zero offset and sending the value zero needs 1 bit. entropy coder can be used to measure the exact rate here. tempMinCost = lambda; while (iterOffset != 0) { // Calculate the bits required for signalling the offset tempRate = (typeIdx == SAO_BO) ? (abs((Int)iterOffset)+2) : (abs((Int)iterOffset)+1); if (abs((Int)iterOffset)==offsetTh-1) { tempRate --; } // Do the dequntization before distorion calculation tempOffset = iterOffset << bitIncrease; tempDist = estSaoDist( count, tempOffset, offsetOrg, shift); tempCost = ((Double)tempDist + lambda * (Double) tempRate); if(tempCost < tempMinCost) { tempMinCost = tempCost; offsetOutput = iterOffset; if(typeIdx == SAO_BO) { currentDistortionTableBo[classIdx-1] = (Int) tempDist; currentRdCostTableBo[classIdx-1] = tempCost; } } iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1); } return offsetOutput; } Void TEncSampleAdaptiveOffset::saoComponentParamDist(Int allowMergeLeft, Int allowMergeUp, SAOParam *saoParam, Int addr, Int addrUp, Int addrLeft, Int yCbCr, Double lambda, SaoLcuParam *compSaoParam, Double *compDistortion) { Int typeIdx; Int64 estDist; Int classIdx; Int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(((yCbCr==0)?g_bitDepthY:g_bitDepthC)-8); Int64 bestDist; SaoLcuParam* saoLcuParam = &(saoParam->saoLcuParam[yCbCr][addr]); SaoLcuParam* saoLcuParamNeighbor = NULL; resetSaoUnit(saoLcuParam); resetSaoUnit(&compSaoParam[0]); resetSaoUnit(&compSaoParam[1]); Double dCostPartBest = MAX_DOUBLE; Double bestRDCostTableBo = MAX_DOUBLE; Int bestClassTableBo = 0; Int currentDistortionTableBo[MAX_NUM_SAO_CLASS]; Double currentRdCostTableBo[MAX_NUM_SAO_CLASS]; SaoLcuParam saoLcuParamRdo; Double estRate = 0; resetSaoUnit(&saoLcuParamRdo); m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcRDGoOnSbacCoder->resetBits(); m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo, yCbCr); dCostPartBest = m_pcEntropyCoder->getNumberOfWrittenBits()*lambda ; copySaoUnit(saoLcuParam, &saoLcuParamRdo ); bestDist = 0; for (typeIdx=0; typeIdxload(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcRDGoOnSbacCoder->resetBits(); m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo, yCbCr); estRate = m_pcEntropyCoder->getNumberOfWrittenBits(); m_dCost[yCbCr][typeIdx] = (Double)((Double)estDist + lambda * (Double) estRate); if(m_dCost[yCbCr][typeIdx] < dCostPartBest) { dCostPartBest = m_dCost[yCbCr][typeIdx]; copySaoUnit(saoLcuParam, &saoLcuParamRdo ); bestDist = estDist; } } compDistortion[0] += ((Double)bestDist/lambda); m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcEntropyCoder->encodeSaoOffset(saoLcuParam, yCbCr); m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[0][CI_TEMP_BEST] ); // merge left or merge up for (Int idxNeighbor=0;idxNeighbor<2;idxNeighbor++) { saoLcuParamNeighbor = NULL; if (allowMergeLeft && addrLeft>=0 && idxNeighbor ==0) { saoLcuParamNeighbor = &(saoParam->saoLcuParam[yCbCr][addrLeft]); } else if (allowMergeUp && addrUp>=0 && idxNeighbor ==1) { saoLcuParamNeighbor = &(saoParam->saoLcuParam[yCbCr][addrUp]); } if (saoLcuParamNeighbor!=NULL) { estDist = 0; typeIdx = saoLcuParamNeighbor->typeIdx; if (typeIdx>=0) { Int mergeBandPosition = (typeIdx == SAO_BO)?saoLcuParamNeighbor->subTypeIdx:0; Int merge_iOffset; for(classIdx = 0; classIdx < m_iNumClass[typeIdx]; classIdx++) { merge_iOffset = saoLcuParamNeighbor->offset[classIdx]; estDist += estSaoDist(m_iCount [yCbCr][typeIdx][classIdx+mergeBandPosition+1], merge_iOffset, m_iOffsetOrg[yCbCr][typeIdx][classIdx+mergeBandPosition+1], shift); } } else { estDist = 0; } copySaoUnit(&compSaoParam[idxNeighbor], saoLcuParamNeighbor ); compSaoParam[idxNeighbor].mergeUpFlag = idxNeighbor; compSaoParam[idxNeighbor].mergeLeftFlag = !idxNeighbor; compDistortion[idxNeighbor+1] += ((Double)estDist/lambda); } } } Void TEncSampleAdaptiveOffset::sao2ChromaParamDist(Int allowMergeLeft, Int allowMergeUp, SAOParam *saoParam, Int addr, Int addrUp, Int addrLeft, Double lambda, SaoLcuParam *crSaoParam, SaoLcuParam *cbSaoParam, Double *distortion) { Int typeIdx; Int64 estDist[2]; Int classIdx; Int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(g_bitDepthC-8); Int64 bestDist = 0; SaoLcuParam* saoLcuParam[2] = {&(saoParam->saoLcuParam[1][addr]), &(saoParam->saoLcuParam[2][addr])}; SaoLcuParam* saoLcuParamNeighbor[2] = {NULL, NULL}; SaoLcuParam* saoMergeParam[2][2]; saoMergeParam[0][0] = &crSaoParam[0]; saoMergeParam[0][1] = &crSaoParam[1]; saoMergeParam[1][0] = &cbSaoParam[0]; saoMergeParam[1][1] = &cbSaoParam[1]; resetSaoUnit(saoLcuParam[0]); resetSaoUnit(saoLcuParam[1]); resetSaoUnit(saoMergeParam[0][0]); resetSaoUnit(saoMergeParam[0][1]); resetSaoUnit(saoMergeParam[1][0]); resetSaoUnit(saoMergeParam[1][1]); Double costPartBest = MAX_DOUBLE; Double bestRDCostTableBo; Int bestClassTableBo[2] = {0, 0}; Int currentDistortionTableBo[MAX_NUM_SAO_CLASS]; Double currentRdCostTableBo[MAX_NUM_SAO_CLASS]; SaoLcuParam saoLcuParamRdo[2]; Double estRate = 0; resetSaoUnit(&saoLcuParamRdo[0]); resetSaoUnit(&saoLcuParamRdo[1]); m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcRDGoOnSbacCoder->resetBits(); m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo[0], 1); m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo[1], 2); costPartBest = m_pcEntropyCoder->getNumberOfWrittenBits()*lambda ; copySaoUnit(saoLcuParam[0], &saoLcuParamRdo[0] ); copySaoUnit(saoLcuParam[1], &saoLcuParamRdo[1] ); for (typeIdx=0; typeIdxload(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcRDGoOnSbacCoder->resetBits(); for(Int compIdx = 0; compIdx < 2; compIdx++) { resetSaoUnit(&saoLcuParamRdo[compIdx]); saoLcuParamRdo[compIdx].length = m_iNumClass[typeIdx]; saoLcuParamRdo[compIdx].typeIdx = typeIdx; saoLcuParamRdo[compIdx].mergeLeftFlag = 0; saoLcuParamRdo[compIdx].mergeUpFlag = 0; saoLcuParamRdo[compIdx].subTypeIdx = (typeIdx == SAO_BO) ? bestClassTableBo[compIdx] : 0; for (classIdx = 0; classIdx < saoLcuParamRdo[compIdx].length; classIdx++) { saoLcuParamRdo[compIdx].offset[classIdx] = (Int)m_iOffset[compIdx+1][typeIdx][classIdx+saoLcuParamRdo[compIdx].subTypeIdx+1]; } m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo[compIdx], compIdx+1); } estRate = m_pcEntropyCoder->getNumberOfWrittenBits(); m_dCost[1][typeIdx] = (Double)((Double)(estDist[0] + estDist[1]) + lambda * (Double) estRate); if(m_dCost[1][typeIdx] < costPartBest) { costPartBest = m_dCost[1][typeIdx]; copySaoUnit(saoLcuParam[0], &saoLcuParamRdo[0] ); copySaoUnit(saoLcuParam[1], &saoLcuParamRdo[1] ); bestDist = (estDist[0]+estDist[1]); } } distortion[0] += ((Double)bestDist/lambda); m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]); m_pcEntropyCoder->encodeSaoOffset(saoLcuParam[0], 1); m_pcEntropyCoder->encodeSaoOffset(saoLcuParam[1], 2); m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[0][CI_TEMP_BEST] ); // merge left or merge up for (Int idxNeighbor=0;idxNeighbor<2;idxNeighbor++) { for(Int compIdx = 0; compIdx < 2; compIdx++) { saoLcuParamNeighbor[compIdx] = NULL; if (allowMergeLeft && addrLeft>=0 && idxNeighbor ==0) { saoLcuParamNeighbor[compIdx] = &(saoParam->saoLcuParam[compIdx+1][addrLeft]); } else if (allowMergeUp && addrUp>=0 && idxNeighbor ==1) { saoLcuParamNeighbor[compIdx] = &(saoParam->saoLcuParam[compIdx+1][addrUp]); } if (saoLcuParamNeighbor[compIdx]!=NULL) { estDist[compIdx] = 0; typeIdx = saoLcuParamNeighbor[compIdx]->typeIdx; if (typeIdx>=0) { Int mergeBandPosition = (typeIdx == SAO_BO)?saoLcuParamNeighbor[compIdx]->subTypeIdx:0; Int merge_iOffset; for(classIdx = 0; classIdx < m_iNumClass[typeIdx]; classIdx++) { merge_iOffset = saoLcuParamNeighbor[compIdx]->offset[classIdx]; estDist[compIdx] += estSaoDist(m_iCount [compIdx+1][typeIdx][classIdx+mergeBandPosition+1], merge_iOffset, m_iOffsetOrg[compIdx+1][typeIdx][classIdx+mergeBandPosition+1], shift); } } else { estDist[compIdx] = 0; } copySaoUnit(saoMergeParam[compIdx][idxNeighbor], saoLcuParamNeighbor[compIdx] ); saoMergeParam[compIdx][idxNeighbor]->mergeUpFlag = idxNeighbor; saoMergeParam[compIdx][idxNeighbor]->mergeLeftFlag = !idxNeighbor; distortion[idxNeighbor+1] += ((Double)estDist[compIdx]/lambda); } } } } //! \}