[x265] [PATCH] rc: extract final average QP from the coded CTU structure
Steve Borho
steve at borho.org
Mon Apr 27 21:01:38 CEST 2015
# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1430161148 18000
# Mon Apr 27 13:59:08 2015 -0500
# Node ID ed448198ce3d22c95a78c2e4b68070abca577822
# Parent 2f5b57e5de1f77886041b5321bcff7c4fdc4c48d
rc: extract final average QP from the coded CTU structure
With --qg-size, the average QP per CTU can be different from the CTU QP. This
removes calcQpForCu(), which was both redundant and out-of-date, and either
collects the total QP within collectCTUStatistics() or it uses a much simpler
calcCTUQP() function.
The average AQ QP is only used in two places:
1) frame logging
2) two-pass stats
It should have only a minor effect on both, but the result should be both more
accurate and more compute efficient
diff -r 2f5b57e5de1f -r ed448198ce3d source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp Mon Apr 27 13:08:57 2015 -0500
+++ b/source/encoder/frameencoder.cpp Mon Apr 27 13:59:08 2015 -0500
@@ -849,14 +849,6 @@
else
curEncData.m_cuStat[cuAddr].baseQp = curEncData.m_avgQpRc;
- if (m_param->rc.aqMode || bIsVbv)
- {
- X265_CHECK(slice->m_pps->bUseDQP, "adaptive quant in use without DQP\n");
- int qp = calcQpForCu(cuAddr, curEncData.m_cuStat[cuAddr].baseQp);
- qp = x265_clip3(QP_MIN, QP_MAX_SPEC, qp);
- curEncData.m_rowStat[row].sumQpAq += qp;
- }
-
if (m_param->bEnableWavefront && !col && row)
{
// Load SBAC coder context from previous row and initialize row state.
@@ -883,7 +875,9 @@
curRow.completed++;
if (m_param->bLogCuStats || m_param->rc.bStatWrite)
- collectCTUStatistics(*ctu);
+ curEncData.m_rowStat[row].sumQpAq += collectCTUStatistics(*ctu);
+ else if (m_param->rc.aqMode)
+ curEncData.m_rowStat[row].sumQpAq += calcCTUQP(*ctu);
// copy no. of intra, inter Cu cnt per row into frame stats for 2 pass
if (m_param->rc.bStatWrite)
@@ -1078,9 +1072,11 @@
m_completionEvent.trigger();
}
-void FrameEncoder::collectCTUStatistics(CUData& ctu)
+/* collect statistics about CU coding decisions, return total QP */
+int FrameEncoder::collectCTUStatistics(const CUData& ctu)
{
StatisticLog* log = &m_sliceTypeLog[ctu.m_slice->m_sliceType];
+ int totQP = 0;
if (ctu.m_slice->m_sliceType == I_SLICE)
{
@@ -1092,6 +1088,7 @@
log->totalCu++;
log->cntIntra[depth]++;
log->qTreeIntraCnt[depth]++;
+ totQP += ctu.m_qp[absPartIdx] * (ctu.m_numPartitions >> (depth * 2));
if (ctu.m_predMode[absPartIdx] == MODE_NONE)
{
@@ -1121,6 +1118,7 @@
log->totalCu++;
log->cntTotalCu[depth]++;
+ totQP += ctu.m_qp[absPartIdx] * (ctu.m_numPartitions >> (depth * 2));
if (ctu.m_predMode[absPartIdx] == MODE_NONE)
{
@@ -1152,6 +1150,7 @@
{
X265_CHECK(ctu.m_log2CUSize[absPartIdx] == 3 && ctu.m_slice->m_sps->quadtreeTULog2MinSize < 3, "Intra NxN found at improbable depth\n");
log->cntIntraNxN++;
+ log->cntIntra[depth]--;
/* TODO: log intra modes at absPartIdx +0 to +3 */
}
else if (ctu.m_lumaIntraDir[absPartIdx] > 1)
@@ -1161,6 +1160,23 @@
}
}
}
+
+ return totQP;
+}
+
+/* iterate over coded CUs and determine total QP */
+int FrameEncoder::calcCTUQP(const CUData& ctu)
+{
+ int totQP = 0;
+ uint32_t depth = 0, numParts = ctu.m_numPartitions;
+
+ for (uint32_t absPartIdx = 0; absPartIdx < ctu.m_numPartitions; absPartIdx += numParts)
+ {
+ depth = ctu.m_cuDepth[absPartIdx];
+ numParts = ctu.m_numPartitions >> (depth * 2);
+ totQP += ctu.m_qp[absPartIdx] * numParts;
+ }
+ return totQP;
}
/* DCT-domain noise reduction / adaptive deadzone from libavcodec */
@@ -1195,55 +1211,6 @@
}
}
-int FrameEncoder::calcQpForCu(uint32_t ctuAddr, double baseQp)
-{
- x265_emms();
- double qp = baseQp;
-
- FrameData& curEncData = *m_frame->m_encData;
- /* clear cuCostsForVbv from when vbv row reset was triggered */
- bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0;
- if (bIsVbv)
- {
- curEncData.m_cuStat[ctuAddr].vbvCost = 0;
- curEncData.m_cuStat[ctuAddr].intraVbvCost = 0;
- }
-
- /* Derive qpOffet for each CU by averaging offsets for all 16x16 blocks in the cu. */
- double qp_offset = 0;
- uint32_t maxBlockCols = (m_frame->m_fencPic->m_picWidth + (16 - 1)) / 16;
- uint32_t maxBlockRows = (m_frame->m_fencPic->m_picHeight + (16 - 1)) / 16;
- uint32_t noOfBlocks = g_maxCUSize / 16;
- uint32_t block_y = (ctuAddr / curEncData.m_slice->m_sps->numCuInWidth) * noOfBlocks;
- uint32_t block_x = (ctuAddr * noOfBlocks) - block_y * curEncData.m_slice->m_sps->numCuInWidth;
-
- /* Use cuTree offsets if cuTree enabled and frame is referenced, else use AQ offsets */
- bool isReferenced = IS_REFERENCED(m_frame);
- double *qpoffs = (isReferenced && m_param->rc.cuTree) ? m_frame->m_lowres.qpCuTreeOffset : m_frame->m_lowres.qpAqOffset;
-
- uint32_t cnt = 0, idx = 0;
- for (uint32_t h = 0; h < noOfBlocks && block_y < maxBlockRows; h++, block_y++)
- {
- for (uint32_t w = 0; w < noOfBlocks && (block_x + w) < maxBlockCols; w++)
- {
- idx = block_x + w + (block_y * maxBlockCols);
- if (m_param->rc.aqMode)
- qp_offset += qpoffs[idx];
- if (bIsVbv)
- {
- curEncData.m_cuStat[ctuAddr].vbvCost += m_frame->m_lowres.lowresCostForRc[idx] & LOWRES_COST_MASK;
- curEncData.m_cuStat[ctuAddr].intraVbvCost += m_frame->m_lowres.intraCost[idx];
- }
- cnt++;
- }
- }
-
- qp_offset /= cnt;
- qp += qp_offset;
-
- return x265_clip3(QP_MIN, QP_MAX_MAX, (int)(qp + 0.5));
-}
-
Frame *FrameEncoder::getEncodedPicture(NALList& output)
{
if (m_frame)
diff -r 2f5b57e5de1f -r ed448198ce3d source/encoder/frameencoder.h
--- a/source/encoder/frameencoder.h Mon Apr 27 13:08:57 2015 -0500
+++ b/source/encoder/frameencoder.h Mon Apr 27 13:59:08 2015 -0500
@@ -226,8 +226,8 @@
void encodeSlice();
void threadMain();
- int calcQpForCu(uint32_t cuAddr, double baseQp);
- void collectCTUStatistics(CUData& ctu);
+ int collectCTUStatistics(const CUData& ctu);
+ int calcCTUQP(const CUData& ctu);
void noiseReductionUpdate();
/* Called by WaveFront::findJob() */
diff -r 2f5b57e5de1f -r ed448198ce3d source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp Mon Apr 27 13:08:57 2015 -0500
+++ b/source/encoder/ratecontrol.cpp Mon Apr 27 13:59:08 2015 -0500
@@ -2187,23 +2187,24 @@
{
if (m_isVbv)
{
+ /* determine avg QP decided by VBV rate control */
for (uint32_t i = 0; i < slice->m_sps->numCuInHeight; i++)
curEncData.m_avgQpRc += curEncData.m_rowStat[i].sumQpRc;
curEncData.m_avgQpRc /= slice->m_sps->numCUsInFrame;
rce->qpaRc = curEncData.m_avgQpRc;
-
- // copy avg RC qp to m_avgQpAq. To print out the correct qp when aq/cutree is disabled.
- curEncData.m_avgQpAq = curEncData.m_avgQpRc;
}
if (m_param->rc.aqMode)
{
+ /* determine actual avg encoded QP, after AQ/cutree adjustments */
for (uint32_t i = 0; i < slice->m_sps->numCuInHeight; i++)
curEncData.m_avgQpAq += curEncData.m_rowStat[i].sumQpAq;
- curEncData.m_avgQpAq /= slice->m_sps->numCUsInFrame;
+ curEncData.m_avgQpAq /= (slice->m_sps->numCUsInFrame * NUM_4x4_PARTITIONS);
}
+ else
+ curEncData.m_avgQpAq = curEncData.m_avgQpRc;
}
// Write frame stats into the stats file if 2 pass is enabled.
More information about the x265-devel
mailing list