diff --git a/source/Lib/EncoderLib/AQp.cpp b/source/Lib/EncoderLib/AQp.cpp index a0a248eb1cfe86c8f5393c347d39b205969a6f9b..917e6b2db725fb945b70c84d350d238ec6c60586 100644 --- a/source/Lib/EncoderLib/AQp.cpp +++ b/source/Lib/EncoderLib/AQp.cpp @@ -36,126 +36,112 @@ */ #include "AQp.h" -#include <float.h> -//! \ingroup EncoderLib -//! \{ - -/** Constructor - */ -AQpLayer::AQpLayer(int width, int height, uint32_t uiAQPartWidth, uint32_t uiAQPartHeight) - : m_uiAQPartWidth(uiAQPartWidth) - , m_uiAQPartHeight(uiAQPartHeight) - , m_uiNumAQPartInWidth((width + uiAQPartWidth - 1) / uiAQPartWidth) - , m_uiNumAQPartInHeight((height + uiAQPartHeight - 1) / uiAQPartHeight) - , m_dAvgActivity(0.0) - , m_acEncAQU(m_uiNumAQPartInWidth * m_uiNumAQPartInHeight, 0.0) +// Constructor +AQpLayer::AQpLayer(int width, int height, uint32_t partWidth, uint32_t partHeight) + : m_partWidth(partWidth) + , m_partHeight(partHeight) + , m_widthInParts((width + partWidth - 1) / partWidth) + , m_heightInParts((height + partHeight - 1) / partHeight) + , m_avgActivity(0.0) { + m_activities.reserve(m_widthInParts * m_heightInParts); // allocate memory } -/** Destructor - */ +// Destructor AQpLayer::~AQpLayer() { } +// Analyze source picture and compute local image characteristics used for QP adaptation +void AQpPreanalyzer::preanalyze(Picture *pic) +{ + const CPelBuf lumaPlane = pic->getOrigBuf().Y(); + const int width = lumaPlane.width; + const int height = lumaPlane.height; + const ptrdiff_t stride = lumaPlane.stride; -/** Analyze source picture and compute local image characteristics used for QP adaptation - * \param pcEPic Picture object to be analyzed - * \return void - */ + for (auto aqLayer: pic->aqlayer) + { + const uint32_t partWidth = aqLayer->getAQPartWidth(); + const uint32_t partHeight = aqLayer->getAQPartHeight(); -void AQpPreanalyzer::preanalyze( Picture* pcEPic ) -{ - const CPelBuf lumaPlane = pcEPic->getOrigBuf().Y(); - const int width = lumaPlane.width; - const int height = lumaPlane.height; - const ptrdiff_t stride = lumaPlane.stride; + std::vector<double> &activities = aqLayer->getQPAdaptationUnit(); - for ( uint32_t d = 0; d < pcEPic->aqlayer.size(); d++ ) - { - const Pel* pLineY = lumaPlane.bufAt( 0, 0); - AQpLayer* pcAQLayer = pcEPic->aqlayer[d]; - const uint32_t uiAQPartWidth = pcAQLayer->getAQPartWidth(); - const uint32_t uiAQPartHeight = pcAQLayer->getAQPartHeight(); - double* pcAQU = &pcAQLayer->getQPAdaptationUnit()[0]; - - double dSumAct = 0.0; - for (uint32_t y = 0; y < height; y += uiAQPartHeight) + double activitySum = 0.0; + for (uint32_t y = 0; y < height; y += partHeight) { - const uint32_t uiCurrAQPartHeight = std::min(uiAQPartHeight, height - y); - for (uint32_t x = 0; x < width; x += uiAQPartWidth, pcAQU++) + const uint32_t curPartHeight = std::min(partHeight, height - y); + CHECK((curPartHeight & 1) != 0, "Odd part height unsupported"); + + for (uint32_t x = 0; x < width; x += partWidth) { - const uint32_t uiCurrAQPartWidth = std::min(uiAQPartWidth, width - x); - const Pel* pBlkY = &pLineY[x]; - uint64_t sum[4] = { 0, 0, 0, 0 }; - uint64_t sumSq[4] = { 0, 0, 0, 0 }; - uint32_t by = 0; - for ( ; by < uiCurrAQPartHeight>>1; by++ ) - { - uint32_t bx = 0; - for ( ; bx < uiCurrAQPartWidth>>1; bx++ ) - { - sum[0] += pBlkY[bx]; - sumSq[0] += pBlkY[bx] * pBlkY[bx]; - } - for ( ; bx < uiCurrAQPartWidth; bx++ ) - { - sum[1] += pBlkY[bx]; - sumSq[1] += pBlkY[bx] * pBlkY[bx]; - } - pBlkY += stride; - } - for ( ; by < uiCurrAQPartHeight; by++ ) + const uint32_t curPartWidth = std::min(partWidth, width - x); + CHECK((curPartWidth & 1) != 0, "Odd part width unsupported"); + + std::array<uint64_t, 4> sum; + std::array<uint64_t, 4> sumSq; + + sum.fill(0); + sumSq.fill(0); + + const uint32_t quadrantWidth = curPartWidth >> 1; + const uint32_t quadrantHeight = curPartHeight >> 1; + + const Pel *pBlkY0 = lumaPlane.bufAt(x, y); + const Pel *pBlkY1 = pBlkY0 + quadrantWidth; + const Pel *pBlkY2 = pBlkY0 + quadrantHeight * stride; + const Pel *pBlkY3 = pBlkY2 + quadrantWidth; + + for (ptrdiff_t by = 0; by < quadrantHeight; by++) { - uint32_t bx = 0; - for ( ; bx < uiCurrAQPartWidth>>1; bx++ ) - { - sum[2] += pBlkY[bx]; - sumSq[2] += pBlkY[bx] * pBlkY[bx]; - } - for ( ; bx < uiCurrAQPartWidth; bx++ ) + for (ptrdiff_t bx = 0; bx < quadrantWidth; bx++) { - sum[3] += pBlkY[bx]; - sumSq[3] += pBlkY[bx] * pBlkY[bx]; + const ptrdiff_t k = bx + by * stride; + + sum[0] += pBlkY0[k]; + sumSq[0] += pBlkY0[k] * pBlkY0[k]; + + sum[1] += pBlkY1[k]; + sumSq[1] += pBlkY1[k] * pBlkY1[k]; + + sum[2] += pBlkY2[k]; + sumSq[2] += pBlkY2[k] * pBlkY2[k]; + + sum[3] += pBlkY3[k]; + sumSq[3] += pBlkY3[k] * pBlkY3[k]; } - pBlkY += stride; } - CHECK((uiCurrAQPartWidth&1)!=0, "Odd part width unsupported"); - CHECK((uiCurrAQPartHeight&1)!=0, "Odd part height unsupported"); - const uint32_t pixelWidthOfQuadrants = uiCurrAQPartWidth >>1; - const uint32_t pixelHeightOfQuadrants = uiCurrAQPartHeight>>1; - const uint32_t numPixInAQPart = pixelWidthOfQuadrants * pixelHeightOfQuadrants; + const uint32_t quadrantSize = quadrantWidth * quadrantHeight; - double dMinVar = DBL_MAX; - if (numPixInAQPart!=0) + double minVariance = MAX_DOUBLE; + if (quadrantSize != 0) { - for ( int i=0; i<4; i++) + for (int i = 0; i < sum.size(); i++) { - const double dAverage = double(sum[i]) / numPixInAQPart; - const double dVariance = double(sumSq[i]) / numPixInAQPart - dAverage * dAverage; - dMinVar = std::min(dMinVar, dVariance); + const double average = double(sum[i]) / quadrantSize; + const double variance = double(sumSq[i]) / quadrantSize - average * average; + + minVariance = std::min(minVariance, variance); } } else { - dMinVar = 0.0; + minVariance = 0.0; } - const double dActivity = 1.0 + dMinVar; - *pcAQU = dActivity; - dSumAct += dActivity; + + // activity is 1 + the lowest variance amongst the 4 quadrants in the part + const double activity = 1.0 + minVariance; + + activities.push_back(activity); + activitySum += activity; } - pLineY += stride * uiCurrAQPartHeight; } - const double dAvgAct = dSumAct / (pcAQLayer->getNumAQPartInWidth() * pcAQLayer->getNumAQPartInHeight()); - pcAQLayer->setAvgActivity( dAvgAct ); + const double activityAvg = activitySum / (aqLayer->getNumAQPartInWidth() * aqLayer->getNumAQPartInHeight()); + + aqLayer->setAvgActivity(activityAvg); } } - - - -//! \} - diff --git a/source/Lib/EncoderLib/AQp.h b/source/Lib/EncoderLib/AQp.h index e9264e63444e0ae05da47d3f7085635d97ca6378..64ec82197aa60fd695ac0ce01478343a8faba8d9 100644 --- a/source/Lib/EncoderLib/AQp.h +++ b/source/Lib/EncoderLib/AQp.h @@ -35,62 +35,57 @@ \brief class of picture which includes side information for encoder (header) */ -#ifndef __AQP__ -#define __AQP__ +#pragma once #include "CommonLib/CommonDef.h" #include "CommonLib/Picture.h" -//! \ingroup EncoderLib -//! \{ - // ==================================================================================================================== // Class definition // ==================================================================================================================== -/// Local image characteristics for CUs on a specific depth +// Local image characteristics for CUs on a specific depth class AQpLayer { private: - uint32_t m_uiAQPartWidth; - uint32_t m_uiAQPartHeight; - uint32_t m_uiNumAQPartInWidth; - uint32_t m_uiNumAQPartInHeight; - double m_dAvgActivity; - std::vector<double> m_acEncAQU; + uint32_t m_partWidth; + uint32_t m_partHeight; + uint32_t m_widthInParts; + uint32_t m_heightInParts; + double m_avgActivity; + + std::vector<double> m_activities; public: - AQpLayer(int width, int height, uint32_t uiAQPartWidth, uint32_t uiAQPartHeight); + AQpLayer(int width, int height, uint32_t partWidth, uint32_t partHeight); virtual ~AQpLayer(); - uint32_t getAQPartWidth() { return m_uiAQPartWidth; } - uint32_t getAQPartHeight() { return m_uiAQPartHeight; } - uint32_t getNumAQPartInWidth() { return m_uiNumAQPartInWidth; } - uint32_t getNumAQPartInHeight() { return m_uiNumAQPartInHeight; } - uint32_t getAQPartStride() { return m_uiNumAQPartInWidth; } - std::vector<double>& getQPAdaptationUnit() { return m_acEncAQU; } - double getActivity( const Position& pos) + uint32_t getAQPartWidth() const { return m_partWidth; } + uint32_t getAQPartHeight() const { return m_partHeight; } + uint32_t getNumAQPartInWidth() const { return m_widthInParts; } + uint32_t getNumAQPartInHeight() const { return m_heightInParts; } + uint32_t getAQPartStride() const { return m_widthInParts; } + + std::vector<double> &getQPAdaptationUnit() { return m_activities; } + + double getActivity(const Position &pos) const { - uint32_t uiAQUPosX = pos.x / m_uiAQPartWidth; - uint32_t uiAQUPosY = pos.y / m_uiAQPartHeight; - return m_acEncAQU[uiAQUPosY * m_uiNumAQPartInWidth + uiAQUPosX]; + const uint32_t x = pos.x / m_partWidth; + const uint32_t y = pos.y / m_partHeight; + return m_activities[y * m_widthInParts + x]; } - double getAvgActivity() { return m_dAvgActivity; } + double getAvgActivity() const { return m_avgActivity; } - void setAvgActivity( double d ) { m_dAvgActivity = d; } + void setAvgActivity(double d) { m_avgActivity = d; } }; -/// Source picture analyzer class +// Source picture analyzer class class AQpPreanalyzer { protected: AQpPreanalyzer() {} virtual ~AQpPreanalyzer() {} public: - static void preanalyze( Picture* picture ); + static void preanalyze(Picture *pic); }; - -//! \} - -#endif // __ENCPIC__ diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp index d66fe0a1f3462c9c9eb8c2ea6ab45f2c7bf055f2..7319a32010ef055cd93629ae09029673ad3b5aa9 100644 --- a/source/Lib/EncoderLib/EncModeCtrl.cpp +++ b/source/Lib/EncoderLib/EncModeCtrl.cpp @@ -168,17 +168,18 @@ void EncModeCtrl::xGetMinMaxQP( int& minQP, int& maxQP, const CodingStructure& c int EncModeCtrl::xComputeDQP( const CodingStructure &cs, const Partitioner &partitioner ) { - Picture* picture = cs.picture; - unsigned uiAQDepth = std::min( partitioner.currSubdiv/2, ( uint32_t ) picture->aqlayer.size() - 1 ); - AQpLayer* pcAQLayer = picture->aqlayer[uiAQDepth]; - - double dMaxQScale = pow( 2.0, m_pcEncCfg->getQPAdaptationRange() / 6.0 ); - double dAvgAct = pcAQLayer->getAvgActivity(); - double dCUAct = pcAQLayer->getActivity( cs.area.Y().topLeft() ); - double dNormAct = ( dMaxQScale*dCUAct + dAvgAct ) / ( dCUAct + dMaxQScale*dAvgAct ); - double dQpOffset = log( dNormAct ) / log( 2.0 ) * 6.0; - int iQpOffset = int( floor( dQpOffset + 0.49999 ) ); - return iQpOffset; + const Picture *picture = cs.picture; + + const unsigned aqDepth = std::min(partitioner.currSubdiv / 2, (uint32_t) picture->aqlayer.size() - 1); + const AQpLayer *aqLayer = picture->aqlayer[aqDepth]; + + const double maxQpScale = pow(2.0, m_pcEncCfg->getQPAdaptationRange() / 6.0); + const double avgActivity = aqLayer->getAvgActivity(); + const double cuActivity = aqLayer->getActivity(cs.area.Y().topLeft()); + const double normActivity = (maxQpScale * cuActivity + avgActivity) / (cuActivity + maxQpScale * avgActivity); + const double qpOffset = std::log2(normActivity) * 6.0; + + return int(floor(qpOffset + 0.49999)); }