/* 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-2014, 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 TComSampleAdaptiveOffset.cpp \brief sample adaptive offset class */ #include "TComSampleAdaptiveOffset.h" #include #include #include #include //! \ingroup TLibCommon //! \{ UInt g_saoMaxOffsetQVal[MAX_NUM_COMPONENT]; SAOOffset::SAOOffset() { reset(); } SAOOffset::~SAOOffset() { } Void SAOOffset::reset() { modeIdc = SAO_MODE_OFF; typeIdc = -1; typeAuxInfo = -1; ::memset(offset, 0, sizeof(Int)* MAX_NUM_SAO_CLASSES); } const SAOOffset& SAOOffset::operator= (const SAOOffset& src) { modeIdc = src.modeIdc; typeIdc = src.typeIdc; typeAuxInfo = src.typeAuxInfo; ::memcpy(offset, src.offset, sizeof(Int)* MAX_NUM_SAO_CLASSES); return *this; } SAOBlkParam::SAOBlkParam() { reset(); } SAOBlkParam::~SAOBlkParam() { } Void SAOBlkParam::reset() { for(Int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++) { offsetParam[compIdx].reset(); } } const SAOBlkParam& SAOBlkParam::operator= (const SAOBlkParam& src) { for(Int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++) { offsetParam[compIdx] = src.offsetParam[compIdx]; } return *this; } TComSampleAdaptiveOffset::TComSampleAdaptiveOffset() { m_tempPicYuv = NULL; m_lineBufWidth = 0; m_signLineBuf1 = NULL; m_signLineBuf2 = NULL; } TComSampleAdaptiveOffset::~TComSampleAdaptiveOffset() { destroy(); if (m_signLineBuf1) delete[] m_signLineBuf1; m_signLineBuf1 = NULL; if (m_signLineBuf2) delete[] m_signLineBuf2; m_signLineBuf2 = NULL; } Void TComSampleAdaptiveOffset::create( Int picWidth, Int picHeight, ChromaFormat format, UInt maxCUWidth, UInt maxCUHeight, UInt maxCUDepth, UInt lumaBitShift, UInt chromaBitShift ) { destroy(); m_picWidth = picWidth; m_picHeight = picHeight; m_chromaFormatIDC = format; m_maxCUWidth = maxCUWidth; m_maxCUHeight = maxCUHeight; m_numCTUInWidth = (m_picWidth/m_maxCUWidth) + ((m_picWidth % m_maxCUWidth)?1:0); m_numCTUInHeight = (m_picHeight/m_maxCUHeight) + ((m_picHeight % m_maxCUHeight)?1:0); m_numCTUsPic = m_numCTUInHeight*m_numCTUInWidth; //temporary picture buffer if ( !m_tempPicYuv ) { m_tempPicYuv = new TComPicYuv; m_tempPicYuv->create( m_picWidth, m_picHeight, m_chromaFormatIDC, m_maxCUWidth, m_maxCUHeight, maxCUDepth ); } //bit-depth related for(Int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++) { Int bitDepthSample = g_bitDepth[toChannelType(ComponentID(compIdx))]; m_offsetStepLog2 [compIdx] = isLuma(ComponentID(compIdx))? lumaBitShift : chromaBitShift; g_saoMaxOffsetQVal[compIdx] = (1<<(min(bitDepthSample,MAX_SAO_TRUNCATED_BITDEPTH)-5))-1; //Table 9-32, inclusive } } Void TComSampleAdaptiveOffset::destroy() { if ( m_tempPicYuv ) { m_tempPicYuv->destroy(); delete m_tempPicYuv; m_tempPicYuv = NULL; } } Void TComSampleAdaptiveOffset::invertQuantOffsets(ComponentID compIdx, Int typeIdc, Int typeAuxInfo, Int* dstOffsets, Int* srcOffsets) { Int codedOffset[MAX_NUM_SAO_CLASSES]; ::memcpy(codedOffset, srcOffsets, sizeof(Int)*MAX_NUM_SAO_CLASSES); ::memset(dstOffsets, 0, sizeof(Int)*MAX_NUM_SAO_CLASSES); if(typeIdc == SAO_TYPE_START_BO) { for(Int i=0; i< 4; i++) { dstOffsets[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES] = codedOffset[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES]*(1< 0) { mergedCTUPos = ctuRsAddr- m_numCTUInWidth; if( pic->getSAOMergeAvailability(ctuRsAddr, mergedCTUPos) ) { mergeCandidate = &(blkParams[mergedCTUPos]); } } } break; case SAO_MERGE_LEFT: { if(ctuX > 0) { mergedCTUPos = ctuRsAddr- 1; if( pic->getSAOMergeAvailability(ctuRsAddr, mergedCTUPos) ) { mergeCandidate = &(blkParams[mergedCTUPos]); } } } break; default: { printf("not a supported merge type"); assert(0); exit(-1); } } mergeList[mergeType]=mergeCandidate; if (mergeCandidate != NULL) { numValidMergeCandidates++; } } return numValidMergeCandidates; } Void TComSampleAdaptiveOffset::reconstructBlkSAOParam(SAOBlkParam& recParam, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]) { const Int numberOfComponents = getNumberValidComponents(m_chromaFormatIDC); for(Int compIdx = 0; compIdx < numberOfComponents; compIdx++) { const ComponentID component = ComponentID(compIdx); SAOOffset& offsetParam = recParam[component]; if(offsetParam.modeIdc == SAO_MODE_OFF) { continue; } switch(offsetParam.modeIdc) { case SAO_MODE_NEW: { invertQuantOffsets(component, offsetParam.typeIdc, offsetParam.typeAuxInfo, offsetParam.offset, offsetParam.offset); } break; case SAO_MODE_MERGE: { SAOBlkParam* mergeTarget = mergeList[offsetParam.typeIdc]; assert(mergeTarget != NULL); offsetParam = (*mergeTarget)[component]; } break; default: { printf("Not a supported mode"); assert(0); exit(-1); } } } } Void TComSampleAdaptiveOffset::reconstructBlkSAOParams(TComPic* pic, SAOBlkParam* saoBlkParams) { for(Int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++) { m_picSAOEnabled[compIdx] = false; } const Int numberOfComponents = getNumberValidComponents(m_chromaFormatIDC); for(Int ctuRsAddr=0; ctuRsAddr< m_numCTUsPic; ctuRsAddr++) { SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES] = { NULL }; getMergeList(pic, ctuRsAddr, saoBlkParams, mergeList); reconstructBlkSAOParam(saoBlkParams[ctuRsAddr], mergeList); for(Int compIdx = 0; compIdx < numberOfComponents; compIdx++) { if(saoBlkParams[ctuRsAddr][compIdx].modeIdc != SAO_MODE_OFF) { m_picSAOEnabled[compIdx] = true; } } } } Void TComSampleAdaptiveOffset::offsetBlock(ComponentID compIdx, Int typeIdx, Int* offset , Pel* srcBlk, Pel* resBlk, Int srcStride, Int resStride, Int width, Int height , Bool isLeftAvail, Bool isRightAvail, Bool isAboveAvail, Bool isBelowAvail, Bool isAboveLeftAvail, Bool isAboveRightAvail, Bool isBelowLeftAvail, Bool isBelowRightAvail) { if(m_lineBufWidth != m_maxCUWidth) { m_lineBufWidth = m_maxCUWidth; if (m_signLineBuf1) delete[] m_signLineBuf1; m_signLineBuf1 = NULL; m_signLineBuf1 = new Char[m_lineBufWidth+1]; if (m_signLineBuf2) delete[] m_signLineBuf2; m_signLineBuf2 = NULL; m_signLineBuf2 = new Char[m_lineBufWidth+1]; } const Int maxSampleValueIncl = (1<< g_bitDepth[toChannelType(compIdx)] )-1; Int x,y, startX, startY, endX, endY, edgeType; Int firstLineStartX, firstLineEndX, lastLineStartX, lastLineEndX; Char signLeft, signRight, signDown; Pel* srcLine = srcBlk; Pel* resLine = resBlk; switch(typeIdx) { case SAO_TYPE_EO_0: { offset += 2; startX = isLeftAvail ? 0 : 1; endX = isRightAvail ? width : (width -1); for (y=0; y< height; y++) { signLeft = (Char)sgn(srcLine[startX] - srcLine[startX-1]); for (x=startX; x< endX; x++) { signRight = (Char)sgn(srcLine[x] - srcLine[x+1]); edgeType = signRight + signLeft; signLeft = -signRight; resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); } srcLine += srcStride; resLine += resStride; } } break; case SAO_TYPE_EO_90: { offset += 2; Char *signUpLine = m_signLineBuf1; startY = isAboveAvail ? 0 : 1; endY = isBelowAvail ? height : height-1; if (!isAboveAvail) { srcLine += srcStride; resLine += resStride; } Pel* srcLineAbove= srcLine- srcStride; for (x=0; x< width; x++) { signUpLine[x] = (Char)sgn(srcLine[x] - srcLineAbove[x]); } Pel* srcLineBelow; for (y=startY; y(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); } srcLine += srcStride; resLine += resStride; } } break; case SAO_TYPE_EO_135: { offset += 2; Char *signUpLine, *signDownLine, *signTmpLine; signUpLine = m_signLineBuf1; signDownLine= m_signLineBuf2; startX = isLeftAvail ? 0 : 1 ; endX = isRightAvail ? width : (width-1); //prepare 2nd line's upper sign Pel* srcLineBelow= srcLine+ srcStride; for (x=startX; x< endX+1; x++) { signUpLine[x] = (Char)sgn(srcLineBelow[x] - srcLine[x- 1]); } //1st line Pel* srcLineAbove= srcLine- srcStride; firstLineStartX = isAboveLeftAvail ? 0 : 1; firstLineEndX = isAboveAvail? endX: 1; for(x= firstLineStartX; x< firstLineEndX; x++) { edgeType = sgn(srcLine[x] - srcLineAbove[x- 1]) - signUpLine[x+1]; resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); } srcLine += srcStride; resLine += resStride; //middle lines for (y= 1; y< height-1; y++) { srcLineBelow= srcLine+ srcStride; for (x=startX; x(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); signDownLine[x+1] = -signDown; } signDownLine[startX] = (Char)sgn(srcLineBelow[startX] - srcLine[startX-1]); signTmpLine = signUpLine; signUpLine = signDownLine; signDownLine = signTmpLine; srcLine += srcStride; resLine += resStride; } //last line srcLineBelow= srcLine+ srcStride; lastLineStartX = isBelowAvail ? startX : (width -1); lastLineEndX = isBelowRightAvail ? width : (width -1); for(x= lastLineStartX; x< lastLineEndX; x++) { edgeType = sgn(srcLine[x] - srcLineBelow[x+ 1]) + signUpLine[x]; resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); } } break; case SAO_TYPE_EO_45: { offset += 2; Char *signUpLine = m_signLineBuf1+1; startX = isLeftAvail ? 0 : 1; endX = isRightAvail ? width : (width -1); //prepare 2nd line upper sign Pel* srcLineBelow= srcLine+ srcStride; for (x=startX-1; x< endX; x++) { signUpLine[x] = (Char)sgn(srcLineBelow[x] - srcLine[x+1]); } //first line Pel* srcLineAbove= srcLine- srcStride; firstLineStartX = isAboveAvail ? startX : (width -1 ); firstLineEndX = isAboveRightAvail ? width : (width-1); for(x= firstLineStartX; x< firstLineEndX; x++) { edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) -signUpLine[x-1]; resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); } srcLine += srcStride; resLine += resStride; //middle lines for (y= 1; y< height-1; y++) { srcLineBelow= srcLine+ srcStride; for(x= startX; x< endX; x++) { signDown = (Char)sgn(srcLine[x] - srcLineBelow[x-1]); edgeType = signDown + signUpLine[x]; resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); signUpLine[x-1] = -signDown; } signUpLine[endX-1] = (Char)sgn(srcLineBelow[endX-1] - srcLine[endX]); srcLine += srcStride; resLine += resStride; } //last line srcLineBelow= srcLine+ srcStride; lastLineStartX = isBelowLeftAvail ? 0 : 1; lastLineEndX = isBelowAvail ? endX : 1; for(x= lastLineStartX; x< lastLineEndX; x++) { edgeType = sgn(srcLine[x] - srcLineBelow[x-1]) + signUpLine[x]; resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[edgeType]); } } break; case SAO_TYPE_BO: { const Int shiftBits = g_bitDepth[toChannelType(compIdx)] - NUM_SAO_BO_CLASSES_LOG2; for (y=0; y< height; y++) { for (x=0; x< width; x++) { resLine[x] = Clip3(0, maxSampleValueIncl, srcLine[x] + offset[srcLine[x] >> shiftBits] ); } srcLine += srcStride; resLine += resStride; } } break; default: { printf("Not a supported SAO types\n"); assert(0); exit(-1); } } } Void TComSampleAdaptiveOffset::offsetCTU(Int ctuRsAddr, TComPicYuv* srcYuv, TComPicYuv* resYuv, SAOBlkParam& saoblkParam, TComPic* pPic) { Bool isLeftAvail,isRightAvail,isAboveAvail,isBelowAvail,isAboveLeftAvail,isAboveRightAvail,isBelowLeftAvail,isBelowRightAvail; const Int numberOfComponents = getNumberValidComponents(m_chromaFormatIDC); Bool bAllOff=true; for(Int compIdx = 0; compIdx < numberOfComponents; compIdx++) { if (saoblkParam[compIdx].modeIdc != SAO_MODE_OFF) bAllOff=false; } if (bAllOff) return; //block boundary availability pPic->getPicSym()->deriveLoopFilterBoundaryAvailibility(ctuRsAddr, isLeftAvail,isRightAvail,isAboveAvail,isBelowAvail,isAboveLeftAvail,isAboveRightAvail,isBelowLeftAvail,isBelowRightAvail); Int yPos = (ctuRsAddr / m_numCTUInWidth)*m_maxCUHeight; Int xPos = (ctuRsAddr % m_numCTUInWidth)*m_maxCUWidth; Int height = (yPos + m_maxCUHeight > m_picHeight)?(m_picHeight- yPos):m_maxCUHeight; Int width = (xPos + m_maxCUWidth > m_picWidth )?(m_picWidth - xPos):m_maxCUWidth; for(Int compIdx = 0; compIdx < numberOfComponents; compIdx++) { const ComponentID component = ComponentID(compIdx); SAOOffset& ctbOffset = saoblkParam[compIdx]; if(ctbOffset.modeIdc != SAO_MODE_OFF) { const UInt componentScaleX = getComponentScaleX(component, pPic->getChromaFormat()); const UInt componentScaleY = getComponentScaleY(component, pPic->getChromaFormat()); Int blkWidth = (width >> componentScaleX); Int blkHeight = (height >> componentScaleY); Int blkXPos = (xPos >> componentScaleX); Int blkYPos = (yPos >> componentScaleY); Int srcStride = srcYuv->getStride(component); Pel* srcBlk = srcYuv->getAddr(component) + blkYPos*srcStride + blkXPos; Int resStride = resYuv->getStride(component); Pel* resBlk = resYuv->getAddr(component) + blkYPos*resStride + blkXPos; offsetBlock( component, ctbOffset.typeIdc, ctbOffset.offset , srcBlk, resBlk, srcStride, resStride, blkWidth, blkHeight , isLeftAvail, isRightAvail , isAboveAvail, isBelowAvail , isAboveLeftAvail, isAboveRightAvail , isBelowLeftAvail, isBelowRightAvail ); } } //compIdx } Void TComSampleAdaptiveOffset::SAOProcess(TComPic* pDecPic) { const Int numberOfComponents = getNumberValidComponents(m_chromaFormatIDC); Bool bAllDisabled=true; for(Int compIdx = 0; compIdx < numberOfComponents; compIdx++) { if (m_picSAOEnabled[compIdx]) bAllDisabled=false; } if (bAllDisabled) return; TComPicYuv* resYuv = pDecPic->getPicYuvRec(); TComPicYuv* srcYuv = m_tempPicYuv; resYuv->copyToPic(srcYuv); for(Int ctuRsAddr= 0; ctuRsAddr < m_numCTUsPic; ctuRsAddr++) { offsetCTU(ctuRsAddr, srcYuv, resYuv, (pDecPic->getPicSym()->getSAOBlkParam())[ctuRsAddr], pDecPic); } //ctu } /** PCM LF disable process. * \param pcPic picture (TComPic) pointer * \returns Void * * \note Replace filtered sample values of PCM mode blocks with the transmitted and reconstructed ones. */ Void TComSampleAdaptiveOffset::PCMLFDisableProcess (TComPic* pcPic) { xPCMRestoration(pcPic); } /** Picture-level PCM restoration. * \param pcPic picture (TComPic) pointer * \returns Void */ Void TComSampleAdaptiveOffset::xPCMRestoration(TComPic* pcPic) { Bool bPCMFilter = (pcPic->getSlice(0)->getSPS()->getUsePCM() && pcPic->getSlice(0)->getSPS()->getPCMFilterDisableFlag())? true : false; if(bPCMFilter || pcPic->getSlice(0)->getPPS()->getTransquantBypassEnableFlag()) { for( UInt ctuRsAddr = 0; ctuRsAddr < pcPic->getNumberOfCtusInFrame() ; ctuRsAddr++ ) { TComDataCU* pcCU = pcPic->getCtu(ctuRsAddr); xPCMCURestoration(pcCU, 0, 0); } } } /** PCM CU restoration. * \param pcCU pointer to current CU * \param uiAbsPartIdx part index * \param uiDepth CU depth * \returns Void */ Void TComSampleAdaptiveOffset::xPCMCURestoration ( TComDataCU* pcCU, UInt uiAbsZorderIdx, UInt uiDepth ) { TComPic* pcPic = pcCU->getPic(); UInt uiCurNumParts = pcPic->getNumPartitionsInCtu() >> (uiDepth<<1); UInt uiQNumParts = uiCurNumParts>>2; // go to sub-CU if( pcCU->getDepth(uiAbsZorderIdx) > uiDepth ) { for ( UInt uiPartIdx = 0; uiPartIdx < 4; uiPartIdx++, uiAbsZorderIdx+=uiQNumParts ) { UInt uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsZorderIdx] ]; UInt uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsZorderIdx] ]; if( ( uiLPelX < pcCU->getSlice()->getSPS()->getPicWidthInLumaSamples() ) && ( uiTPelY < pcCU->getSlice()->getSPS()->getPicHeightInLumaSamples() ) ) xPCMCURestoration( pcCU, uiAbsZorderIdx, uiDepth+1 ); } return; } // restore PCM samples if ((pcCU->getIPCMFlag(uiAbsZorderIdx)&& pcPic->getSlice(0)->getSPS()->getPCMFilterDisableFlag()) || pcCU->isLosslessCoded( uiAbsZorderIdx)) { const UInt numComponents=pcPic->getNumberValidComponents(); for(UInt comp=0; compgetPic()->getPicYuvRec(); UInt uiPcmLeftShiftBit; const UInt uiMinCoeffSize = pcCU->getPic()->getMinCUWidth()*pcCU->getPic()->getMinCUHeight(); const UInt csx=pcPicYuvRec->getComponentScaleX(compID); const UInt csy=pcPicYuvRec->getComponentScaleY(compID); const UInt uiOffset = (uiMinCoeffSize*uiAbsZorderIdx)>>(csx+csy); Pel *piSrc = pcPicYuvRec->getAddr(compID, pcCU->getCtuRsAddr(), uiAbsZorderIdx); const Pel *piPcm = pcCU->getPCMSample(compID) + uiOffset; const UInt uiStride = pcPicYuvRec->getStride(compID); const UInt uiWidth = ((g_uiMaxCUWidth >> uiDepth) >> csx); const UInt uiHeight = ((g_uiMaxCUWidth >> uiDepth) >> csy); if ( pcCU->isLosslessCoded(uiAbsZorderIdx) && !pcCU->getIPCMFlag(uiAbsZorderIdx) ) { uiPcmLeftShiftBit = 0; } else { uiPcmLeftShiftBit = g_bitDepth[toChannelType(compID)] - pcCU->getSlice()->getSPS()->getPCMBitDepth(toChannelType(compID)); } for(UInt uiY = 0; uiY < uiHeight; uiY++ ) { for(UInt uiX = 0; uiX < uiWidth; uiX++ ) { piSrc[uiX] = (piPcm[uiX] << uiPcmLeftShiftBit); } piPcm += uiWidth; piSrc += uiStride; } } //! \}