[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