Coverage Report

Created: 2026-10-28 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/vvenc/source/Lib/EncoderLib/BitAllocation.cpp
Line
Count
Source
1
/* -----------------------------------------------------------------------------
2
The copyright in this software is being made available under the Clear BSD
3
License, included below. No patent rights, trademark rights and/or
4
other Intellectual Property Rights other than the copyrights concerning
5
the Software are granted under this license.
6
7
The Clear BSD License
8
9
Copyright (c) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC Authors.
10
All rights reserved.
11
12
Redistribution and use in source and binary forms, with or without modification,
13
are permitted (subject to the limitations in the disclaimer below) provided that
14
the following conditions are met:
15
16
     * Redistributions of source code must retain the above copyright notice,
17
     this list of conditions and the following disclaimer.
18
19
     * Redistributions in binary form must reproduce the above copyright
20
     notice, this list of conditions and the following disclaimer in the
21
     documentation and/or other materials provided with the distribution.
22
23
     * Neither the name of the copyright holder nor the names of its
24
     contributors may be used to endorse or promote products derived from this
25
     software without specific prior written permission.
26
27
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
28
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
29
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
31
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
32
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
36
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
POSSIBILITY OF SUCH DAMAGE.
39
40
41
------------------------------------------------------------------------------------------- */
42
43
44
/** \file     BitAllocation.cpp
45
\brief    Bit allocation class for QP adaptation and, possibly, rate control
46
*/
47
48
#include "BitAllocation.h"
49
#include "EncStage.h"
50
#include "CommonLib/Picture.h"
51
#include "CommonLib/UnitTools.h"
52
#include <math.h>
53
54
#include "vvenc/vvencCfg.h"
55
56
57
//! \ingroup EncoderLib
58
//! \{
59
60
namespace vvenc {
61
62
// static functions
63
64
static inline int apprI3Log2 (const double d, const bool isSccStrong) // rounded 2*log2(d) or 3*log2(d)
65
14.0k
{
66
14.0k
  const double weight = (isSccStrong ? 2.0 : 3.0);
67
14.0k
  const double dLimit = (isSccStrong ? 5.5e-20 : 1.5e-13);
68
69
14.0k
  return d < dLimit ? -128 : int (floor (weight * log (d) / log (2.0) + 0.5));
70
14.0k
}
71
72
// Activity scale that adds the picture's fractional QP to the rounded QP offsets apprI3Log2 derives from activities, so
73
// that the blocks whose unrounded QP lies within the fraction below the next integer move up to that integer QP.
74
static inline double fractionalQPActivityScale (const double picQPFraction, const bool isSccStrong)
75
4.60k
{
76
4.60k
  return picQPFraction > 0.0 ? pow (2.0, picQPFraction / (isSccStrong ? 2.0 : 3.0)) : 1.0;
77
4.60k
}
78
79
static inline int lumaDQPOffset (const uint32_t avgLumaValue, const uint32_t bitDepth)
80
0
{
81
0
  if (bitDepth > 16 || avgLumaValue >= (1u << bitDepth)) return 0;
82
#if 0
83
  // mapping for peak luminance of ca. 3*400 = 1200 nits
84
  return (2 - int ((9 * uint64_t (avgLumaValue * avgLumaValue)) >> uint64_t (2 * bitDepth)));
85
#else
86
  // mapping for peak luminance of ca. 2*400 =  800 nits
87
0
  return (1 - int ((6 * uint64_t (avgLumaValue * avgLumaValue)) >> uint64_t (2 * bitDepth)));
88
0
#endif
89
0
}
90
91
void calcSpatialVisAct ( const Pel* pSrc,
92
                         const int iSrcStride,
93
                         const int height,
94
                         const int width,
95
                         const uint32_t bitDepth,
96
                         const bool isUHD,
97
                         VisAct& va )
98
14.0k
{
99
14.0k
  CHECK( pSrc == nullptr, "no source buffer given to calculate temporal visual activity" );
100
101
14.0k
  uint64_t saAct;  // spatial absolute activity sum
102
103
  // skip first row as there may be a black border frame
104
14.0k
  pSrc += iSrcStride;
105
106
  // center rows
107
14.0k
  if (isUHD) // high-pass with downsampling
108
0
  {
109
0
    pSrc += iSrcStride;
110
111
0
    saAct = g_pelBufOP.AvgHighPassWithDownsampling (width, height, pSrc, iSrcStride);
112
113
0
    va.hpSpatAct = double (saAct) / double ((width - 4) * (height - 4));
114
0
  }
115
14.0k
  else // HD high-pass without downsampling
116
14.0k
  {
117
14.0k
    saAct = g_pelBufOP.AvgHighPass (width, height, pSrc, iSrcStride);
118
119
14.0k
    va.hpSpatAct = double (saAct) / double ((width - 2) * (height - 2));
120
14.0k
  }
121
122
  // spatial in 12 bit
123
14.0k
  va.spatAct = unsigned (0.5 + va.hpSpatAct * double (bitDepth < 12 ? 1 << (12 - bitDepth) : 1));
124
14.0k
}
125
126
void calcTemporalVisAct ( const Pel* pSrc,
127
                          const int iSrcStride,
128
                          const int height,
129
                          const int width,
130
                          const Pel* pSM1,
131
                          const int iSM1Stride,
132
                          const Pel* pSM2,
133
                          const int iSM2Stride,
134
                          uint32_t frameRate,
135
                          const uint32_t bitDepth,
136
                          const bool isUHD,
137
                          VisAct& va )
138
12.5k
{
139
12.5k
  CHECK( pSrc == nullptr, "no source buffer given to calculate temporal visual activity" );
140
12.5k
  CHECK( pSM1 == nullptr, "no compare buffer given to calculate temporal visual activity" );
141
142
12.5k
  const Pel* pS0 = pSrc;
143
12.5k
  uint64_t taAct;  // temporal absolute activity sum
144
145
  // force 1st-order delta if only prev. frame available
146
12.5k
  if (pSM2 == nullptr || iSM2Stride <= 0) frameRate = 24;
147
148
  // skip first row as there may be a black border frame
149
12.5k
  pSrc += iSrcStride;
150
151
  // center rows
152
12.5k
  if (pS0 == pSM1 && frameRate <= 31)
153
12.5k
  {
154
12.5k
    va.hpTempAct = 0; // bypass high-pass, result will be zero
155
12.5k
  }
156
0
  else if (isUHD)  // downsampled high-pass
157
0
  {
158
0
    const int i2M1Stride = iSM1Stride * 2;
159
160
0
    CHECK (pSM1 == nullptr || iSM1Stride <= 0 || iSM1Stride < width, "Pel buffer pointer pSM1 must not be null!");
161
162
0
    pSrc += iSrcStride;
163
0
    pSM1 += i2M1Stride;
164
0
    if (frameRate <= 31) // 1st-order delta
165
0
    {
166
0
      taAct = g_pelBufOP.AvgHighPassWithDownsamplingDiff1st (width, height, pSrc, pSM1, iSrcStride, iSM1Stride);
167
0
    }
168
0
    else // 2nd-order delta (diff of diffs)
169
0
    {
170
0
      const int i2M2Stride = iSM2Stride * 2;
171
172
0
      CHECK (pSM2 == nullptr || iSM2Stride <= 0 || iSM2Stride < width, "Pel buffer pointer pSM2 must not be null!");
173
174
0
      pSM2 += i2M2Stride;
175
0
      taAct = g_pelBufOP.AvgHighPassWithDownsamplingDiff2nd (width, height, pSrc, pSM1, pSM2, iSrcStride, iSM1Stride, iSM2Stride);
176
0
    }
177
178
0
    va.hpTempAct = double (taAct) / double ((width - 4) * (height - 4));
179
0
  }
180
0
  else // HD high-pass without downsampling
181
0
  {
182
0
    CHECK (pSM1 == nullptr || iSM1Stride <= 0 || iSM1Stride < width, "Pel buffer pointer pSM1 must not be null!");
183
184
0
    pSM1 += iSM1Stride;
185
0
    if (frameRate <= 31) // 1st-order delta
186
0
    {
187
0
      taAct = g_pelBufOP.HDHighPass (width, height, pSrc, pSM1, iSrcStride, iSM1Stride);
188
0
    }
189
0
    else // 2nd-order delta (diff of diffs)
190
0
    {
191
0
      CHECK (pSM2 == nullptr || iSM2Stride <= 0 || iSM2Stride < width, "Pel buffer pointer pSM2 must not be null!");
192
193
0
      pSM2 += iSM2Stride;
194
0
      taAct = g_pelBufOP.HDHighPass2 (width, height, pSrc, pSM1, pSM2, iSrcStride, iSM1Stride, iSM2Stride);
195
0
    }
196
197
0
    va.hpTempAct = double (taAct) / double ((width - 2) * (height - 2));
198
0
  }
199
200
  // temporal in 12 bit
201
12.5k
  va.tempAct = unsigned (0.5 + va.hpTempAct * double (bitDepth < 12 ? 1 << (12 - bitDepth) : 1) * (frameRate <= 31 ? 1.15625 : 1.0));
202
12.5k
}
203
204
void updateVisAct ( VisAct& va, const uint32_t bitDepth )
205
12.5k
{
206
  // minimum part in 12 bit
207
12.5k
  va.minAct = std::min( va.tempAct, va.spatAct );
208
  // lower limit, compensate for high-pass amplification
209
12.5k
  va.hpVisAct = std::max (double (1 << (bitDepth - 6)), va.hpSpatAct + 2.0 * va.hpTempAct);
210
12.5k
  va.visAct   = ClipBD( uint16_t( 0.5 + va.hpVisAct ), bitDepth );
211
12.5k
}
212
213
double filterAndCalculateAverageActivity ( const Pel* pSrc,
214
                                           const int iSrcStride,
215
                                           const int height,
216
                                           const int width,
217
                                           const Pel* pSM1,
218
                                           const int iSM1Stride,
219
                                           const Pel* pSM2,
220
                                           const int iSM2Stride,
221
                                           uint32_t frameRate,
222
                                           const uint32_t bitDepth,
223
                                           const bool isUHD,
224
                                           unsigned* minVisAct = nullptr,
225
                                           unsigned* spVisAct  = nullptr )
226
12.5k
{
227
12.5k
  VisAct va;
228
229
  // spatial activity
230
12.5k
  calcSpatialVisAct( pSrc, iSrcStride, height, width, bitDepth, isUHD, va );
231
232
  // temporal activity
233
12.5k
  calcTemporalVisAct( pSrc, iSrcStride, height, width, pSM1, iSM1Stride, pSM2, iSM2Stride,
234
12.5k
                     frameRate, bitDepth, isUHD, va );
235
236
  // minimum and visual activity
237
12.5k
  updateVisAct( va, bitDepth );
238
239
12.5k
  if( minVisAct )
240
4.97k
  {
241
4.97k
    *minVisAct = va.minAct;
242
4.97k
  }
243
12.5k
  if( spVisAct )
244
4.97k
  {
245
4.97k
    *spVisAct = va.spatAct;
246
4.97k
  }
247
248
12.5k
  return va.hpVisAct;
249
12.5k
}
250
251
static double getAveragePictureActivity (const uint32_t picWidth,  const uint32_t picHeight,
252
                                         const int scaledAverageGopActivity,
253
                                         const bool tempFiltering, const uint32_t bitDepth)
254
6.09k
{
255
6.09k
  if (scaledAverageGopActivity > 0)
256
0
  {
257
0
    return (double (scaledAverageGopActivity) / double (1 << (24 - bitDepth)));
258
0
  }
259
6.09k
  const double hpEnerPic = (tempFiltering ? 32.0 : 16.0) * double (1 << (2 * bitDepth - 10)) * sqrt ((3840.0 * 2160.0) / double (picWidth * picHeight));
260
261
6.09k
  return sqrt (hpEnerPic); // square-root of a_pic value
262
6.09k
}
263
264
static int getGlaringColorQPOffset (Picture* const pic, const int ctuAddr, const int bitDepth, uint32_t &avgLumaValue)
265
6.46k
{
266
6.46k
  const PreCalcValues& pcv  = *pic->cs->pcv;
267
6.46k
  const ChromaFormat chrFmt = pic->chromaFormat;
268
6.46k
  const SizeType chrWidth   = pcv.maxCUSize >> getChannelTypeScaleX (CH_C, chrFmt);
269
6.46k
  const SizeType chrHeight  = pcv.maxCUSize >> getChannelTypeScaleY (CH_C, chrFmt);
270
6.46k
  const unsigned w          = pcv.widthInCtus;
271
6.46k
  const int      midLevel   = 1 << (bitDepth - 1);
272
6.46k
  int chrValue = MAX_INT;
273
274
6.46k
  avgLumaValue = uint32_t ((ctuAddr >= 0) ? pic->ctuAdaptedQP[ctuAddr] : pic->getOrigBuf().Y().getAvg());
275
276
19.4k
  for (uint32_t comp = COMP_Cb; comp < MAX_NUM_COMP; comp++)
277
12.9k
  {
278
12.9k
    const ComponentID compID = (ComponentID) comp;
279
12.9k
    int avgCompValue;
280
281
12.9k
    if (ctuAddr >= 0) // chroma
282
9.95k
    {
283
9.95k
      const CompArea chrArea = clipArea (CompArea (compID, chrFmt, Area ((ctuAddr % w) * chrWidth, (ctuAddr / w) * chrHeight, chrWidth, chrHeight)), pic->block (compID));
284
285
9.95k
      avgCompValue = pic->getOrigBuf (chrArea).getAvg();
286
9.95k
    }
287
2.98k
    else avgCompValue = pic->getOrigBuf (pic->block (compID)).getAvg();
288
289
12.9k
    if (chrValue > avgCompValue) chrValue = avgCompValue; // minimum of the DC offsets
290
12.9k
  }
291
6.46k
  CHECK (chrValue < 0, "mean chroma value cannot be negative!");
292
293
6.46k
  chrValue = (int) avgLumaValue - chrValue;
294
295
6.46k
  if (chrValue > midLevel) return apprI3Log2 (double (chrValue * chrValue) / double (midLevel * midLevel), pic->isSccStrong);
296
297
6.46k
  return 0;
298
6.46k
}
299
300
static int getGlaringColorQPOffsetSubCtu (Picture* const pic, const CompArea& lumaArea, const int bitDepth, uint32_t &avgLumaValue)
301
4.60k
{
302
4.60k
  const ChromaFormat chrFmt = pic->chromaFormat;
303
4.60k
  const SizeType chrWidth   = lumaArea.width  >> getChannelTypeScaleX (CH_C, chrFmt);
304
4.60k
  const SizeType chrHeight  = lumaArea.height >> getChannelTypeScaleY (CH_C, chrFmt);
305
4.60k
  const PosType  chrPosX    = lumaArea.x >> getChannelTypeScaleX (CH_C, chrFmt);
306
4.60k
  const PosType  chrPosY    = lumaArea.y >> getChannelTypeScaleY (CH_C, chrFmt);
307
4.60k
  const int      midLevel   = 1 << (bitDepth - 1);
308
4.60k
  int chrValue = MAX_INT;
309
310
4.60k
  avgLumaValue = pic->getOrigBuf (lumaArea).getAvg();
311
312
13.8k
  for (uint32_t comp = COMP_Cb; comp < MAX_NUM_COMP; comp++)
313
9.20k
  {
314
9.20k
    const ComponentID compID = (ComponentID) comp;
315
9.20k
    const CompArea   chrArea = clipArea (CompArea (compID, chrFmt, Area (chrPosX, chrPosY, chrWidth, chrHeight)), pic->block (compID));
316
317
9.20k
    int avgCompValue = pic->getOrigBuf (chrArea).getAvg();
318
319
9.20k
    if (chrValue > avgCompValue) chrValue = avgCompValue; // minimum of the DC offsets
320
9.20k
  }
321
4.60k
  CHECK (chrValue < 0, "mean chroma value cannot be negative!");
322
323
4.60k
  chrValue = (int) avgLumaValue - chrValue;
324
325
4.60k
  if (chrValue > midLevel) return apprI3Log2 (double (chrValue * chrValue) / double (midLevel * midLevel), pic->isSccStrong);
326
327
4.60k
  return 0;
328
4.60k
}
329
330
static void updateMinNoiseLevelsPic (uint8_t* const minNoiseLevels, const int bitDepth, const unsigned avgValue, const unsigned noise)
331
0
{
332
0
  const unsigned avgIndex = avgValue >> (bitDepth - 3); // one of 8 mean level regions
333
334
0
  CHECK (avgIndex >= QPA_MAX_NOISE_LEVELS, "array index out of bounds");
335
336
0
  if (noise < (unsigned) minNoiseLevels[avgIndex])
337
0
  {
338
0
    minNoiseLevels[avgIndex] = (uint8_t) noise;
339
0
  }
340
0
}
341
342
static void clipQPValToEstimatedMinimStats (const uint8_t* minNoiseLevels, const int bitDepth, const unsigned avgValue,
343
                                            const double resFac, const int extraQPOffset, int& QP) // output QP
344
0
{
345
0
  const unsigned avgIndex = avgValue >> (bitDepth - 3); // one of 8 mean level regions
346
0
  const unsigned x = (1 << 3) - 1;
347
0
  const int32_t dQPOffset = -15;
348
349
0
  CHECK (avgIndex >= QPA_MAX_NOISE_LEVELS, "array index out of bounds");
350
351
0
  int i = minNoiseLevels[avgIndex];
352
353
  // try to "fill in the blanks" in luma range (also results in peak smoothing, as described in PCS 2022 paper)
354
0
  if (avgIndex == 0 && i > minNoiseLevels[0 + 1]) i = minNoiseLevels[0 + 1];
355
0
  if (avgIndex == x && i > minNoiseLevels[x - 1]) i = minNoiseLevels[x - 1];
356
357
0
  if (avgIndex > 0 && avgIndex < x)
358
0
  {
359
0
    const uint8_t maxNeighborNoiseLevel = std::max (minNoiseLevels[avgIndex - 1], minNoiseLevels[avgIndex + 1]);
360
361
0
    if (i > maxNeighborNoiseLevel) i = maxNeighborNoiseLevel;
362
0
  }
363
0
  if (i >= 255)
364
0
  {
365
0
    return;
366
0
  }
367
368
0
  i = std::max (0, apprI3Log2 (std::min (1.0, resFac) * i * i, false) + dQPOffset + extraQPOffset); // = 6*log2
369
0
  if (QP < i)
370
0
  {
371
0
    QP = i;
372
0
  }
373
0
}
374
375
static int applyDeltaQpPeakSmoothing (Picture* const pic, const VVEncCfg* encCfg, const uint32_t startAddr, const uint32_t endAddr)
376
986
{
377
986
  const uint32_t ctuWdt = pic->cs->pcv->widthInCtus;
378
986
  const uint32_t ctuEnd = endAddr - ctuWdt;
379
986
  int deltaQpSum = 0, preSmoothQP = 0;
380
986
  std::vector<int> prevQP;
381
382
986
  if (ctuWdt == 0 || endAddr <= startAddr + 3u * ctuWdt) return 0;
383
384
0
  prevQP.resize (ctuWdt);
385
386
0
  for (uint32_t ctuRsAddr = startAddr; ctuRsAddr < ctuEnd; ctuRsAddr++)
387
0
  {
388
0
    const int32_t idx = ctuRsAddr % ctuWdt;
389
390
0
    if (ctuRsAddr < ctuWdt) prevQP[idx] = pic->ctuAdaptedQP[ctuRsAddr];
391
0
    else if (idx == 0)      preSmoothQP = pic->ctuAdaptedQP[ctuRsAddr];
392
0
    else if (idx == ctuWdt - 1)
393
0
    {
394
0
      prevQP[idx - 1] = preSmoothQP;
395
0
      prevQP[idx] = pic->ctuAdaptedQP[ctuRsAddr];
396
0
    }
397
0
    else // no boundary CTU
398
0
    {
399
0
      const int32_t nextIdx = ctuRsAddr + ctuWdt; // max. and min. in 3x3 neighborhood
400
0
      int qpMax = std::max (preSmoothQP, pic->ctuAdaptedQP[ctuRsAddr + 1]);
401
0
      int qpMin = std::min (preSmoothQP, pic->ctuAdaptedQP[ctuRsAddr + 1]);
402
403
0
      for (int32_t i = -1; i <= 1; i++)
404
0
      {
405
0
        qpMax = std::max (qpMax, std::max (prevQP[idx + i], pic->ctuAdaptedQP[nextIdx + i]));
406
0
        qpMin = std::min (qpMin, std::min (prevQP[idx + i], pic->ctuAdaptedQP[nextIdx + i]));
407
0
      }
408
0
      prevQP[idx - 1] = preSmoothQP;
409
0
      preSmoothQP = pic->ctuAdaptedQP[ctuRsAddr];
410
411
0
      if (preSmoothQP > qpMax)
412
0
      {
413
0
        pic->ctuQpaLambda[ctuRsAddr] *= 0.793701; // peak: decrease adapted lambda, QP
414
0
        pic->ctuAdaptedQP[ctuRsAddr]--;
415
0
        deltaQpSum--;
416
0
      }
417
0
      if (preSmoothQP < qpMin)
418
0
      {
419
0
        pic->ctuQpaLambda[ctuRsAddr] *= 1.259921; // hole: increase adapted lambda, QP
420
0
        pic->ctuAdaptedQP[ctuRsAddr]++;
421
0
        deltaQpSum++;
422
0
      }
423
0
    }
424
0
  }
425
426
0
  prevQP.clear();
427
428
0
  return deltaQpSum;
429
986
}
430
431
static int refineDeltaQpDistribution (Picture* const pic, const VVEncCfg* encCfg,   const int sliceQP,
432
                                      const double sliceLambda, const int rcQpDiff, const int bitDepth,
433
                                      const uint32_t startAddr, const uint32_t endAddr, const int qpSum,
434
                                      const uint32_t tempLayer, const bool isIntra, const bool isEncPass,
435
                                      const uint8_t* minNoiseLevels, std::vector<int>& ctuAvgLuma)
436
0
{
437
0
  const double resRatio = (isEncPass ? sqrt (double (encCfg->m_SourceWidth * encCfg->m_SourceHeight) / (3840.0 * 2160.0)) : 0.0);
438
0
  const int ctusInSlice = int (endAddr - startAddr);
439
0
  const int targetQpSum = (encCfg->m_RCTargetBitrate > 0 ? sliceQP * ctusInSlice : qpSum);
440
0
  int blockQpSum = 0, tempLumaQP;
441
0
  double blockLambda;
442
0
  bool isLimited = false;
443
444
0
  for (uint32_t ctuRsAddr = startAddr; ctuRsAddr < endAddr; ctuRsAddr++)
445
0
  {
446
0
    int clippedLumaQP = std::max (0, pic->ctuAdaptedQP[ctuRsAddr] + rcQpDiff);
447
448
0
    if (isEncPass)
449
0
    {
450
0
      tempLumaQP = clippedLumaQP; // CTU QP before clipping for diff calculation below
451
452
0
      clipQPValToEstimatedMinimStats (minNoiseLevels, bitDepth, ctuAvgLuma[ctuRsAddr - startAddr], resRatio, (isIntra ? encCfg->m_intraQPOffset >> 1 : std::min (4, (int) tempLayer)), clippedLumaQP);
453
0
      if (clippedLumaQP > tempLumaQP)
454
0
      {
455
0
        ctuAvgLuma[ctuRsAddr - startAddr] = -1; // mark CTU as being processed already
456
0
        isLimited = isEncPass;
457
0
      }
458
0
    }
459
460
0
    clippedLumaQP = std::min (MAX_QP, clippedLumaQP);
461
462
0
    blockLambda = sliceLambda * pow (2.0, double (clippedLumaQP - sliceQP) / 3.0);
463
0
    blockQpSum += clippedLumaQP;
464
465
0
    pic->ctuQpaLambda[ctuRsAddr] = blockLambda;  // store modified CTU lambdas and QPs
466
0
    pic->ctuAdaptedQP[ctuRsAddr] = clippedLumaQP;
467
0
  }
468
469
0
  if (blockQpSum > targetQpSum && isLimited) // CTU QPs limited, so distribute saved rate among nonlimited CTUs
470
0
  {
471
0
    int maxCtuQP = 0, minCtuQP = MAX_QP;
472
473
0
    for (uint32_t ctuRsAddr = startAddr; ctuRsAddr < endAddr; ctuRsAddr++) // find max
474
0
    {
475
0
      if (ctuAvgLuma[ctuRsAddr - startAddr] >= 0 && pic->ctuAdaptedQP[ctuRsAddr] > maxCtuQP) // nonlimited CTUs
476
0
      {
477
0
        maxCtuQP = pic->ctuAdaptedQP[ctuRsAddr];
478
0
      }
479
0
      if (pic->ctuAdaptedQP[ctuRsAddr] < minCtuQP)
480
0
      {
481
0
        minCtuQP = pic->ctuAdaptedQP[ctuRsAddr];
482
0
      }
483
0
    }
484
485
0
    minCtuQP = std::max (0, minCtuQP);
486
487
0
    while (maxCtuQP > minCtuQP && blockQpSum > targetQpSum) // spend rate starting at max QPs, then go downward
488
0
    {
489
0
      for (uint32_t ctuRsAddr = startAddr; ctuRsAddr < endAddr; ctuRsAddr++) // reduce
490
0
      {
491
0
        if (ctuAvgLuma[ctuRsAddr - startAddr] >= 0 && pic->ctuAdaptedQP[ctuRsAddr] == maxCtuQP)
492
0
        {
493
0
          tempLumaQP = std::max (0, pic->ctuAdaptedQP[ctuRsAddr] - 1);
494
495
0
          ctuAvgLuma[ctuRsAddr - startAddr] = -1; // mark CTU as being reduced already
496
0
          blockLambda = sliceLambda * pow (2.0, double (tempLumaQP - sliceQP) / 3.0);
497
0
          if (tempLumaQP < pic->ctuAdaptedQP[ctuRsAddr]) blockQpSum--;
498
499
0
          pic->ctuQpaLambda[ctuRsAddr] = blockLambda; // store reduced lambdas and QPs
500
0
          pic->ctuAdaptedQP[ctuRsAddr] = tempLumaQP;
501
0
        }
502
503
0
        if (blockQpSum <= targetQpSum) break;
504
0
      }
505
506
0
      maxCtuQP--;
507
0
    }
508
0
  }
509
510
0
  return (blockQpSum + (ctusInSlice >> 1)) / ctusInSlice;
511
0
}
512
513
// public functions
514
515
int BitAllocation::applyQPAdaptationSlice (const Slice* slice, const VVEncCfg* encCfg, const int sliceQP,
516
                                           const double sliceLambda, uint16_t* const picVisActLuma,
517
                                           std::vector<int>& ctuPumpRedQP, std::vector<uint8_t>* ctuRCQPMemory,
518
                                           int* const optChromaQPOffsets, const uint8_t* minNoiseLevels,
519
                                           const uint32_t ctuStartAddr, const uint32_t ctuBoundingAddr)
520
1.49k
{
521
1.49k
  Picture* const pic          = (slice != nullptr ? slice->pic : nullptr);
522
1.49k
  double hpEner[MAX_NUM_COMP] = {0.0, 0.0, 0.0};
523
1.49k
  double averageAdaptedLambda = 0.0;
524
1.49k
  int    averageAdaptedLumaQP = -1;
525
1.49k
  uint32_t meanLuma           = MAX_UINT;
526
1.49k
  std::vector<int> ctuAvgLuma;
527
528
1.49k
  if (pic == nullptr || pic->cs == nullptr || encCfg == nullptr || ctuStartAddr >= ctuBoundingAddr)
529
0
  {
530
0
    return -1;
531
0
  }
532
533
1.49k
  const bool isEncPass        = (encCfg->m_LookAhead > 0 && !pic->isPreAnalysis);
534
1.49k
  const bool isHDR            = encCfg->m_HdrMode != vvencHDRMode::VVENC_HDR_OFF;
535
1.49k
  const bool isBIM            = (encCfg->hasBlockQpOffsets() && !pic->m_picShared->m_ctuBimQpOffset.empty());
536
1.49k
  const bool isSccStrongRC    = ((encCfg->m_LookAhead > 0 || encCfg->m_RCNumPasses == 2) && pic->isSccStrong);
537
1.49k
  const bool isHighResolution = (std::min (encCfg->m_SourceWidth, encCfg->m_SourceHeight) > 1280);
538
1.49k
  const bool useFrameWiseQPA  = (encCfg->m_QP > MAX_QP_PERCEPT_QPA) && (encCfg->m_framesToBeEncoded != 1) && (slice->TLayer > 0);
539
1.49k
  const uint32_t hpFrameRate  = (pic->force2ndOrder ? 32 : encCfg->m_FrameRate / encCfg->m_FrameScale);
540
1.49k
  const int  bitDepth         = slice->sps->bitDepths[CH_L];
541
1.49k
  double hpEnerPicNorm        = 1.0 / getAveragePictureActivity (encCfg->m_SourceWidth, encCfg->m_SourceHeight, (encCfg->m_RCNumPasses == 2 ? 0 : ctuPumpRedQP.back()),
542
1.49k
                                                                 (encCfg->m_internalUsePerceptQPATempFiltISlice || !slice->isIntra()), bitDepth);
543
1.49k
  if (pic->picQPFraction > 0.0)
544
0
  {
545
0
    hpEnerPicNorm *= fractionalQPActivityScale (pic->picQPFraction, isSccStrongRC);
546
0
  }
547
1.49k
  const PreCalcValues& pcv    = *pic->cs->pcv;
548
549
1.49k
  pic->picInitialQP = sliceQP;  // modified below and used in applyQPAdaptationSubCtu
550
1.49k
  if ((encCfg->m_RCTargetBitrate > 0) && useFrameWiseQPA)
551
0
  {
552
0
    averageAdaptedLumaQP = Clip3 (0, MAX_QP, sliceQP - 1); // one will be added again
553
0
  }
554
555
5.97k
  for (uint32_t comp = 0; comp < getNumberValidComponents (pic->chromaFormat); comp++)
556
4.47k
  {
557
4.47k
    const ComponentID compID  = (ComponentID) comp;
558
559
4.47k
    if (isLuma (compID)) // luma: CTU-wise QPA operation
560
1.49k
    {
561
1.49k
      const PosType guardSize = (isHighResolution ? 2 : 1);
562
1.49k
      unsigned zeroMinActCTUs = 0, picSpVisAct = 0;
563
564
6.46k
      for (uint32_t ctuRsAddr = ctuStartAddr; ctuRsAddr < ctuBoundingAddr; ctuRsAddr++)
565
4.97k
      {
566
4.97k
        const Position pos ((ctuRsAddr % pcv.widthInCtus) * pcv.maxCUSize, (ctuRsAddr / pcv.widthInCtus) * pcv.maxCUSize);
567
4.97k
        const CompArea ctuArea   = clipArea (CompArea (COMP_Y, pic->chromaFormat, Area (pos.x, pos.y, pcv.maxCUSize, pcv.maxCUSize)), pic->Y());
568
4.97k
        const SizeType fltWidth  = pcv.maxCUSize + guardSize * (pos.x > 0 ? 2 : 1);
569
4.97k
        const SizeType fltHeight = pcv.maxCUSize + guardSize * (pos.y > 0 ? 2 : 1);
570
4.97k
        const CompArea fltArea   = clipArea (CompArea (COMP_Y, pic->chromaFormat, Area (pos.x > 0 ? pos.x - guardSize : 0, pos.y > 0 ? pos.y - guardSize : 0, fltWidth, fltHeight)), pic->Y());
571
4.97k
        const CPelBuf  picOrig   = pic->getOrigBuf (fltArea);
572
4.97k
        const CPelBuf  picPrv1   = pic->getOrigBufPrev (fltArea, PREV_FRAME_1);
573
4.97k
        const CPelBuf  picPrv2   = pic->getOrigBufPrev (fltArea, PREV_FRAME_2);
574
4.97k
        unsigned minActivityPart = 0, spVisActCTU = 0;
575
576
4.97k
        hpEner[1] = filterAndCalculateAverageActivity (picOrig.buf, picOrig.stride, picOrig.height, picOrig.width,
577
4.97k
                                                       picPrv1.buf, picPrv1.stride, picPrv2.buf, picPrv2.stride, hpFrameRate,
578
4.97k
                                                       bitDepth, isHighResolution, &minActivityPart, &spVisActCTU);
579
580
4.97k
        if (minActivityPart == 0) zeroMinActCTUs++;
581
582
4.97k
        hpEner[comp] += hpEner[1] * double (ctuArea.width * ctuArea.height);
583
4.97k
        pic->ctuQpaLambda[ctuRsAddr] = hpEner[1]; // temporary backup of CTU mean visual activity
584
4.97k
        pic->ctuAdaptedQP[ctuRsAddr] = (int) pic->getOrigBuf (ctuArea).getAvg(); // and mean luma
585
586
4.97k
        if (picOrig.buf == picPrv1.buf) // replace temporal visual activity with min motion error
587
4.97k
        {
588
4.97k
          hpEner[1] = pic->m_picShared->m_minNoiseLevels[pic->ctuAdaptedQP[ctuRsAddr] >> (bitDepth - 3)] * (bitDepth >= 10 ? 1.5 : 0.375);
589
590
4.97k
          if (hpEner[1] < (bitDepth >= 10 ? 382.5 : 95.625)) // levels in first frame
591
0
          {
592
0
            hpEner[comp] += hpEner[1] * double (ctuArea.width * ctuArea.height);
593
0
            pic->ctuQpaLambda[ctuRsAddr] += hpEner[1]; // add noise level to mean visual activity
594
0
          }
595
4.97k
        }
596
0
        else if (!isEncPass && (encCfg->m_RCNumPasses == 2 || pic->gopEntry->m_mctfIndex < 0 || !pic->gopEntry->m_isStartOfGop))
597
0
        {
598
0
          updateMinNoiseLevelsPic (pic->m_picShared->m_minNoiseLevels, bitDepth, pic->ctuAdaptedQP[ctuRsAddr], minActivityPart);
599
0
        }
600
4.97k
        picSpVisAct += spVisActCTU;
601
4.97k
      }
602
603
1.49k
      hpEner[comp] /= double (encCfg->m_SourceWidth * encCfg->m_SourceHeight);
604
1.49k
      if (picVisActLuma != nullptr)
605
1.49k
      {
606
1.49k
        *picVisActLuma = ClipBD (uint16_t (0.5 + hpEner[comp]), bitDepth);
607
1.49k
      }
608
1.49k
      if (ctuBoundingAddr > ctuStartAddr)
609
1.49k
      {
610
1.49k
        const uint32_t nCtu = ctuBoundingAddr - ctuStartAddr;
611
612
1.49k
        pic->picVA.spatAct[ CH_L ] = ClipBD (uint16_t ((picSpVisAct + (nCtu >> 1)) / nCtu), 12);
613
1.49k
      }
614
1.49k
      if (encCfg->m_internalUsePerceptQPATempFiltISlice && slice->isIntra() && pic->getOrigBuf (compID).buf != pic->getOrigBufPrev (compID, PREV_FRAME_1).buf && zeroMinActCTUs * 2 > ctuBoundingAddr - ctuStartAddr)
615
0
      {
616
0
        hpEnerPicNorm *= sqrt (zeroMinActCTUs * 2.0 / float (ctuBoundingAddr - ctuStartAddr)); // frozen-image mode
617
0
      }
618
1.49k
    }
619
2.98k
    else // chroma: only picture-wise operation required
620
2.98k
    {
621
2.98k
      const CPelBuf picOrig = pic->getOrigBuf (compID);
622
2.98k
      const CPelBuf picPrv1 = pic->getOrigBufPrev (compID, PREV_FRAME_1);
623
2.98k
      const CPelBuf picPrv2 = pic->getOrigBufPrev (compID, PREV_FRAME_2);
624
625
2.98k
      hpEner[comp] = filterAndCalculateAverageActivity (picOrig.buf, picOrig.stride, picOrig.height, picOrig.width,
626
2.98k
                                                        picPrv1.buf, picPrv1.stride, picPrv2.buf, picPrv2.stride, hpFrameRate,
627
2.98k
                                                        bitDepth, isHighResolution && (pic->chromaFormat == CHROMA_444));
628
629
2.98k
      const int adaptChromaQPOffset = 1.5 * hpEner[comp] <= hpEner[0] ? 0 : apprI3Log2 (1.5 * hpEner[comp] / hpEner[0], pic->isSccStrong);
630
631
2.98k
      if (averageAdaptedLumaQP < 0) // YUV is not 4:0:0!
632
1.49k
      {
633
1.49k
        averageAdaptedLumaQP = Clip3 (0, MAX_QP, sliceQP + apprI3Log2 (hpEner[0] * hpEnerPicNorm, isSccStrongRC));
634
635
1.49k
        if (isChromaEnabled (pic->chromaFormat) && (averageAdaptedLumaQP < MAX_QP))
636
1.49k
        {
637
1.49k
          averageAdaptedLumaQP += getGlaringColorQPOffset (pic, -1 /*ctuAddr*/, slice->sps->bitDepths[CH_C], meanLuma);
638
639
1.49k
          if ((averageAdaptedLumaQP > MAX_QP) && !isHDR) averageAdaptedLumaQP = MAX_QP;
640
1.49k
        }
641
        // change mean picture QP index based on picture's average luma value (Sharp)
642
1.49k
        if (isHDR)
643
0
        {
644
0
          if (meanLuma == MAX_UINT) meanLuma = pic->getOrigBuf().Y().getAvg();
645
646
0
          averageAdaptedLumaQP = Clip3 (0, MAX_QP, averageAdaptedLumaQP + lumaDQPOffset (meanLuma, bitDepth));
647
0
        }
648
1.49k
      }
649
650
2.98k
      if (optChromaQPOffsets != nullptr) // adapts sliceChromaQpOffsetIntraOrPeriodic
651
2.98k
      {
652
        GCC_WARNING_DISABLE_maybe_uninitialized // probably spurious warning, when building with -fsanitize=undefined: "error: 'encCfg.33' may be used uninitialized in this function"
653
2.98k
        const int lumaChromaMappingDQP = (averageAdaptedLumaQP - slice->sps->chromaQpMappingTable.getMappedChromaQpValue (compID, averageAdaptedLumaQP)) >> (encCfg->m_HdrMode == vvencHDRMode::VVENC_HDR_OFF ? 1 : 2);
654
2.98k
        GCC_WARNING_RESET
655
2.98k
        optChromaQPOffsets[comp - 1] = std::min (3 + lumaChromaMappingDQP, adaptChromaQPOffset + lumaChromaMappingDQP);
656
2.98k
      }
657
2.98k
    } // isLuma or isChroma
658
4.47k
  }
659
660
1.49k
  if (averageAdaptedLumaQP < 0) // only if YUV is 4:0:0!
661
0
  {
662
0
    averageAdaptedLumaQP = Clip3 (0, MAX_QP, sliceQP + apprI3Log2 (hpEner[0] * hpEnerPicNorm, isSccStrongRC));
663
664
    // change mean picture QP index based on the picture's average luma value (Sharp)
665
0
    if (isHDR)
666
0
    {
667
0
      if (meanLuma == MAX_UINT) meanLuma = pic->getOrigBuf().Y().getAvg();
668
669
0
      averageAdaptedLumaQP = Clip3 (0, MAX_QP, averageAdaptedLumaQP + lumaDQPOffset (meanLuma, bitDepth));
670
0
    }
671
0
  }
672
673
1.49k
  if (encCfg->m_RCNumPasses == 2 && (encCfg->m_RCTargetBitrate > 0) && (ctuRCQPMemory != nullptr) && slice->pps->useDQP && (encCfg->m_internalUsePerceptQPATempFiltISlice == 2) && slice->isIntra())
674
0
  {
675
0
    const int nCtu = int (ctuBoundingAddr - ctuStartAddr);
676
0
    const int offs = (slice->poc / encCfg->m_IntraPeriod) * ((nCtu + 1) >> 1);
677
0
    std::vector<uint8_t>& ctuQPMem = *ctuRCQPMemory; // unpack 1st-pass reduction QPs
678
679
0
    if ((ctuPumpRedQP.size() >= nCtu) && (ctuQPMem.size() >= offs + ((nCtu + 1) >> 1)))
680
0
    {
681
0
      for (uint32_t ctuRsAddr = ctuStartAddr; ctuRsAddr < ctuBoundingAddr; ctuRsAddr++)
682
0
      {
683
0
        ctuPumpRedQP[ctuRsAddr] = int ((ctuRsAddr & 1) ? ctuQPMem[offs + (ctuRsAddr >> 1)] >> 4 : ctuQPMem[offs + (ctuRsAddr >> 1)] & 15) - 8;
684
0
      }
685
0
    }
686
0
  }
687
688
1.49k
  if (useFrameWiseQPA || (averageAdaptedLumaQP >= MAX_QP)) // store the CTU-wise QP/lambda values
689
0
  {
690
0
    averageAdaptedLumaQP = std::min (MAX_QP, averageAdaptedLumaQP + 1);
691
0
    averageAdaptedLambda = sliceLambda * pow (2.0, double (averageAdaptedLumaQP - sliceQP) / 3.0);
692
693
0
    for (uint32_t ctuRsAddr = ctuStartAddr; ctuRsAddr < ctuBoundingAddr; ctuRsAddr++)
694
0
    {
695
0
      pic->ctuQpaLambda[ctuRsAddr] = averageAdaptedLambda; // save adapted lambda, QP
696
0
      pic->ctuAdaptedQP[ctuRsAddr] = averageAdaptedLumaQP;
697
0
    }
698
0
  }
699
1.49k
  else // use CTU-level QPA
700
1.49k
  {
701
1.49k
    const int nCtu = int (ctuBoundingAddr - ctuStartAddr);
702
1.49k
    const int dvsr = encCfg->m_IntraPeriod - encCfg->m_GOPSize;
703
1.49k
    const int aaQP = averageAdaptedLumaQP; // backup of initial average QP from above
704
1.49k
    const bool rcIsFirstPassOf2 = ((encCfg->m_RCTargetBitrate == 0) && (ctuRCQPMemory != nullptr) && slice->pps->useDQP && (slice->poc > 0) ? encCfg->m_RCNumPasses == 2 : false);
705
706
1.49k
    if (isEncPass) ctuAvgLuma.resize (nCtu);
707
708
1.49k
    averageAdaptedLumaQP = 0;
709
6.46k
    for (uint32_t ctuRsAddr = ctuStartAddr; ctuRsAddr < ctuBoundingAddr; ctuRsAddr++)
710
4.97k
    {
711
4.97k
      const double hpEnerCTU = pic->ctuQpaLambda[ctuRsAddr];
712
4.97k
      int adaptedLumaQP = Clip3 (0, MAX_QP, sliceQP + apprI3Log2 (hpEnerCTU * hpEnerPicNorm, isSccStrongRC));
713
714
4.97k
      if ((encCfg->m_internalUsePerceptQPATempFiltISlice == 2) && slice->isIntra() && (ctuPumpRedQP.size() > ctuRsAddr))
715
0
      {
716
0
        if (rcIsFirstPassOf2) // backup 1st-pass I-frame QP for 2nd rate control pass
717
0
        {
718
0
          if (ctuRsAddr & 1) ctuRCQPMemory->back() |= (Clip3 (-8, 7, ctuPumpRedQP[ctuRsAddr]) + 8) << 4;
719
0
          else /*even addr*/ ctuRCQPMemory->push_back (Clip3 (-8, 7, ctuPumpRedQP[ctuRsAddr]) + 8);
720
0
          if (adaptedLumaQP > 0)
721
0
          {
722
0
            adaptedLumaQP -= (aaQP >> 4); // some first-pass tuning for stabilization
723
0
          }
724
0
        }
725
0
        if (ctuPumpRedQP[ctuRsAddr] < 0) adaptedLumaQP = Clip3 (0, MAX_QP, adaptedLumaQP + (ctuPumpRedQP[ctuRsAddr] * encCfg->m_GOPSize - (dvsr >> 1)) / dvsr);
726
0
        else /*ctuPumpRedQP[addr] >= 0*/ adaptedLumaQP = Clip3 (0, MAX_QP, adaptedLumaQP + (ctuPumpRedQP[ctuRsAddr] * encCfg->m_GOPSize + (dvsr >> 1)) / dvsr);
727
728
0
        ctuPumpRedQP[ctuRsAddr] = 0; // reset QP memory for temporal pumping analysis
729
0
      }
730
4.97k
      if ((encCfg->m_internalUsePerceptQPATempFiltISlice == 2) && !slice->isIntra() && (slice->TLayer == 0) && rcIsFirstPassOf2 && (adaptedLumaQP < MAX_QP))
731
0
      {
732
0
        adaptedLumaQP++; // this is a first-pass tuning to stabilize the rate control
733
0
      }
734
4.97k
      meanLuma = MAX_UINT;
735
4.97k
      if (isChromaEnabled (pic->chromaFormat) && (adaptedLumaQP < MAX_QP))
736
4.97k
      {
737
4.97k
        adaptedLumaQP += getGlaringColorQPOffset (pic, (int) ctuRsAddr, slice->sps->bitDepths[CH_C], meanLuma);
738
739
4.97k
        if ((adaptedLumaQP > MAX_QP) && !isHDR) adaptedLumaQP = MAX_QP;
740
4.97k
      }
741
      // change the CTU-level QP index based on CTU area's average luma value (Sharp)
742
4.97k
      if (isHDR)
743
0
      {
744
0
        if (meanLuma == MAX_UINT) meanLuma = pic->ctuAdaptedQP[ctuRsAddr];
745
746
0
        adaptedLumaQP = Clip3 (0, MAX_QP, adaptedLumaQP + lumaDQPOffset (meanLuma, bitDepth));
747
0
      }
748
      // add further delta-QP of block importance mapping (BIM) detector if available
749
4.97k
      if (isBIM)
750
0
      {
751
0
        adaptedLumaQP = Clip3 (-slice->sps->qpBDOffset[CH_L], MAX_QP, adaptedLumaQP + pic->m_picShared->m_ctuBimQpOffset[ctuRsAddr]);
752
0
      }
753
      // reduce delta-QP variance, avoid wasting precious bit budget at low bit-rates
754
4.97k
      if ((encCfg->m_RCTargetBitrate == 0) && (3 + encCfg->m_QP > MAX_QP_PERCEPT_QPA) && (encCfg->m_framesToBeEncoded != 1))
755
837
      {
756
837
        const int retunedAdLumaQP = adaptedLumaQP + 1;
757
758
837
        adaptedLumaQP = (std::max (0, 1 + MAX_QP_PERCEPT_QPA - encCfg->m_QP) * adaptedLumaQP + std::min (4, 3 + encCfg->m_QP - MAX_QP_PERCEPT_QPA) * aaQP + 2) >> 2;
759
837
        if (adaptedLumaQP > retunedAdLumaQP) adaptedLumaQP = retunedAdLumaQP;
760
837
        if (adaptedLumaQP < MAX_QP && encCfg->m_QP == MAX_QP_PERCEPT_QPA && slice->TLayer > 1) adaptedLumaQP++; // a fine-tuning
761
837
      }
762
4.97k
      if (isEncPass) ctuAvgLuma[ctuRsAddr - ctuStartAddr] = pic->ctuAdaptedQP[ctuRsAddr];
763
764
4.97k
      averageAdaptedLambda = sliceLambda * pow (2.0, double (adaptedLumaQP - sliceQP) / 3.0);
765
4.97k
      averageAdaptedLumaQP += adaptedLumaQP;
766
767
4.97k
      pic->ctuQpaLambda[ctuRsAddr] = averageAdaptedLambda; // save adapted lambda, QP
768
4.97k
      pic->ctuAdaptedQP[ctuRsAddr] = adaptedLumaQP;
769
4.97k
    }
770
771
1.49k
    if (encCfg->m_cuQpDeltaSubdiv == 0 || !slice->isIntra()) averageAdaptedLumaQP += applyDeltaQpPeakSmoothing (pic, encCfg, ctuStartAddr, ctuBoundingAddr);
772
773
1.49k
    meanLuma = std::max (0, averageAdaptedLumaQP);
774
1.49k
    averageAdaptedLumaQP = (meanLuma + (nCtu >> 1)) / nCtu;
775
776
1.49k
    if ((encCfg->m_RCTargetBitrate > 0 && averageAdaptedLumaQP != sliceQP) || (isEncPass) || (encCfg->m_LookAhead > 0 && pic->isPreAnalysis) ) // QP/rate control
777
0
    {
778
0
      const int rcQpDiff = (encCfg->m_RCTargetBitrate > 0 || (encCfg->m_LookAhead > 0 && pic->isPreAnalysis) ? sliceQP - averageAdaptedLumaQP : 0);
779
780
0
      averageAdaptedLumaQP = refineDeltaQpDistribution (pic, encCfg, sliceQP, sliceLambda, rcQpDiff, bitDepth, ctuStartAddr, ctuBoundingAddr,
781
0
                                                        meanLuma, slice->TLayer, slice->isIntra(), isEncPass, minNoiseLevels, ctuAvgLuma);
782
783
0
      pic->picInitialQP = Clip3 (0, MAX_QP, pic->picInitialQP + rcQpDiff); // used in applyQPAdaptationSubCtu
784
0
      pic->isMeanQPLimited = (encCfg->m_RCTargetBitrate > 0) && isEncPass && (averageAdaptedLumaQP > sliceQP);
785
0
    }
786
1.49k
    else if (encCfg->m_rateCap && (pic->gopAdaptedQP > 0) && (averageAdaptedLumaQP < aaQP)) // capped CQF
787
0
    {
788
0
      const int rcQpDiff = aaQP - averageAdaptedLumaQP;
789
790
0
      averageAdaptedLambda = pow (2.0, double (rcQpDiff) / 3.0);
791
0
      for (uint32_t ctuRsAddr = ctuStartAddr; ctuRsAddr < ctuBoundingAddr; ctuRsAddr++)
792
0
      {
793
0
        pic->ctuQpaLambda[ctuRsAddr] *= averageAdaptedLambda; // scale adapted lambda
794
0
        pic->ctuAdaptedQP[ctuRsAddr] = std::min (MAX_QP, pic->ctuAdaptedQP[ctuRsAddr] + rcQpDiff);
795
0
      }
796
797
0
      pic->picInitialQP = Clip3 (0, MAX_QP, pic->picInitialQP + rcQpDiff); // used in applyQPAdaptationSubCtu
798
0
      averageAdaptedLumaQP = aaQP;
799
0
    }
800
1.49k
    else if ((encCfg->m_RCTargetBitrate == 0) && (3 + encCfg->m_QP > MAX_QP_PERCEPT_QPA) && (encCfg->m_framesToBeEncoded != 1) && (averageAdaptedLumaQP + 1 < aaQP))
801
0
    {
802
0
      const int lrQpDiff = (aaQP - averageAdaptedLumaQP) >> (encCfg->m_QP <= MAX_QP_PERCEPT_QPA ? 2 : 1); // for monotonous rate change at low rates
803
804
0
      averageAdaptedLambda = pow (2.0, double (lrQpDiff) / 3.0);
805
0
      for (uint32_t ctuRsAddr = ctuStartAddr; ctuRsAddr < ctuBoundingAddr; ctuRsAddr++)
806
0
      {
807
0
        pic->ctuQpaLambda[ctuRsAddr] *= averageAdaptedLambda; // scale adapted lambda
808
0
        pic->ctuAdaptedQP[ctuRsAddr] = std::min (MAX_QP, pic->ctuAdaptedQP[ctuRsAddr] + lrQpDiff);
809
0
      }
810
811
0
      pic->picInitialQP = Clip3 (0, MAX_QP, pic->picInitialQP + lrQpDiff); // used in applyQPAdaptationSubCtu
812
0
      averageAdaptedLumaQP = aaQP; // TODO hlm: += lrQpDiff?
813
814
0
      pic->isMeanQPLimited = false;
815
0
    }
816
817
1.49k
    if (isEncPass) ctuAvgLuma.clear();
818
1.49k
  } // CTU-/frame-level QPA
819
820
1.49k
  return averageAdaptedLumaQP;
821
1.49k
}
822
823
int BitAllocation::applyQPAdaptationSubCtu (const Slice* slice, const VVEncCfg* encCfg, const Area& lumaArea, const uint8_t* minNoiseLevels)
824
4.60k
{
825
4.60k
  Picture* const pic          = (slice != nullptr ? slice->pic : nullptr);
826
4.60k
  uint32_t meanLuma           = MAX_UINT;
827
828
4.60k
  if (pic == nullptr || encCfg == nullptr)
829
0
  {
830
0
    return -1;
831
0
  }
832
833
4.60k
  const bool isEncPass        = (encCfg->m_LookAhead > 0 && !pic->isPreAnalysis);
834
4.60k
  const bool isHDR            = encCfg->m_HdrMode != vvencHDRMode::VVENC_HDR_OFF;
835
4.60k
  const bool isBIM            = (encCfg->hasBlockQpOffsets() && !pic->m_picShared->m_ctuBimQpOffset.empty());
836
4.60k
  const bool isSccStrongRC    = ((encCfg->m_LookAhead > 0 || encCfg->m_RCNumPasses == 2) && pic->isSccStrong);
837
4.60k
  const bool isHighResolution = (std::min (encCfg->m_SourceWidth, encCfg->m_SourceHeight) > 1280);
838
4.60k
  const uint32_t hpFrameRate  = (pic->force2ndOrder ? 32 : encCfg->m_FrameRate / encCfg->m_FrameScale);
839
4.60k
  const int  bitDepth         = slice->sps->bitDepths[CH_L];
840
4.60k
  const PosType     guardSize = (isHighResolution ? 2 : 1);
841
4.60k
  const Position    pos       = lumaArea.pos();
842
4.60k
  const CompArea    subArea   = clipArea (CompArea (COMP_Y, pic->chromaFormat, Area (pos.x, pos.y, lumaArea.width, lumaArea.height)), pic->Y());
843
4.60k
  const SizeType    fltWidth  = lumaArea.width  + guardSize * (pos.x > 0 ? 2 : 1);
844
4.60k
  const SizeType    fltHeight = lumaArea.height + guardSize * (pos.y > 0 ? 2 : 1);
845
4.60k
  const CompArea    fltArea   = clipArea (CompArea (COMP_Y, pic->chromaFormat, Area (pos.x > 0 ? pos.x - guardSize : 0, pos.y > 0 ? pos.y - guardSize : 0, fltWidth, fltHeight)), pic->Y());
846
4.60k
  const CPelBuf     picOrig   = pic->getOrigBuf (fltArea);
847
4.60k
  const CPelBuf     picPrv1   = pic->getOrigBufPrev (fltArea, PREV_FRAME_1);
848
4.60k
  const CPelBuf     picPrv2   = pic->getOrigBufPrev (fltArea, PREV_FRAME_2);
849
4.60k
  const double hpEnerSubCTU   = filterAndCalculateAverageActivity (picOrig.buf, picOrig.stride, picOrig.height, picOrig.width,
850
4.60k
                                                                   picPrv1.buf, picPrv1.stride, picPrv2.buf, picPrv2.stride, hpFrameRate,
851
4.60k
                                                                   bitDepth, isHighResolution);
852
4.60k
  const double hpEnerPicNorm  = fractionalQPActivityScale (pic->picQPFraction, isSccStrongRC)
853
4.60k
                              / getAveragePictureActivity (encCfg->m_SourceWidth, encCfg->m_SourceHeight, 0,
854
4.60k
                                                           (encCfg->m_internalUsePerceptQPATempFiltISlice || !slice->isIntra()), bitDepth);
855
4.60k
  int adaptedSubCtuQP = Clip3 (0, MAX_QP, pic->picInitialQP + apprI3Log2 (hpEnerSubCTU * hpEnerPicNorm, isSccStrongRC));
856
857
4.60k
  if (isChromaEnabled (pic->chromaFormat) && (adaptedSubCtuQP < MAX_QP))
858
4.60k
  {
859
4.60k
    adaptedSubCtuQP += getGlaringColorQPOffsetSubCtu (pic, subArea, slice->sps->bitDepths[CH_C], meanLuma);
860
861
4.60k
    if ((adaptedSubCtuQP > MAX_QP) && !isHDR) adaptedSubCtuQP = MAX_QP;
862
4.60k
  }
863
  // change the sub-CTU-level QP index based on sub-area's average luma value (Sharp)
864
4.60k
  if (isHDR)
865
0
  {
866
0
    if (meanLuma == MAX_UINT) meanLuma = pic->getOrigBuf (subArea).getAvg();
867
868
0
    adaptedSubCtuQP = Clip3 (0, MAX_QP, adaptedSubCtuQP + lumaDQPOffset (meanLuma, bitDepth));
869
0
  }
870
  // add additional delta-QP of block importance mapping (BIM) detection if available
871
4.60k
  if (isBIM)
872
0
  {
873
0
    adaptedSubCtuQP = Clip3 (-slice->sps->qpBDOffset[CH_L], MAX_QP, adaptedSubCtuQP + pic->m_picShared->m_ctuBimQpOffset[getCtuAddr (pos, *pic->cs->pcv)]);
874
0
  }
875
  // reduce the delta-QP variance, avoid wasting precious bit budget at low bit-rates
876
4.60k
  if ((encCfg->m_RCTargetBitrate == 0) && (3 + encCfg->m_QP > MAX_QP_PERCEPT_QPA) && (slice->sliceQp >= 0) && (encCfg->m_framesToBeEncoded != 1))
877
0
  {
878
0
    const int retunedAdLumaQP = adaptedSubCtuQP + 1;
879
880
0
    adaptedSubCtuQP = (std::max (0, 1 + MAX_QP_PERCEPT_QPA - encCfg->m_QP) * adaptedSubCtuQP + std::min (4, 3 + encCfg->m_QP - MAX_QP_PERCEPT_QPA) * slice->sliceQp + 2) >> 2;
881
0
    if (adaptedSubCtuQP > retunedAdLumaQP) adaptedSubCtuQP = retunedAdLumaQP;
882
0
    if (adaptedSubCtuQP < MAX_QP && encCfg->m_QP >= MAX_QP_PERCEPT_QPA) adaptedSubCtuQP++; // for monotonous rate change, l. 507
883
0
  }
884
4.60k
  if (isEncPass)
885
0
  {
886
0
    const double resRatio = sqrt (double (encCfg->m_SourceWidth * encCfg->m_SourceHeight) / (3840.0 * 2160.0));
887
888
0
    if (meanLuma == MAX_UINT) meanLuma = pic->getOrigBuf (subArea).getAvg();
889
0
    clipQPValToEstimatedMinimStats (minNoiseLevels, bitDepth, meanLuma, resRatio, (slice->isIntra() ? encCfg->m_intraQPOffset >> 1 : std::min (4, (int) slice->TLayer)), adaptedSubCtuQP);
890
0
  }
891
892
4.60k
  return adaptedSubCtuQP;
893
4.60k
}
894
895
int BitAllocation::getCtuPumpingReducingQP (const Slice* slice, const CPelBuf& origY, const Distortion uiSadBestForQPA,
896
                                            std::vector<int>& ctuPumpRedQP, const uint32_t ctuRsAddr, const int baseQP,
897
                                            const bool isBIM)
898
0
{
899
0
  if (slice == nullptr || !slice->pps->useDQP || ctuPumpRedQP.size() <= ctuRsAddr) return 0;
900
901
0
  const int32_t avgOrig = origY.getAvg();
902
  // sum up the zero-mean absolute values
903
0
  const uint32_t sumAbsZmOrig = g_pelBufOP.sumAbsDeviation (origY.buf, origY.stride, origY.width, origY.height, avgOrig);
904
905
0
  const double sumAbsRatio = double (uiSadBestForQPA * 3 /*TODO: or 4? fine-tune!*/) / double (sumAbsZmOrig == 0 ? 1 : sumAbsZmOrig);
906
0
  const int pumpingReducQP = ((isBIM ? -1 : 0) + int (log (Clip3 (0.25, 4.0, sumAbsRatio)) / log (2.0) + (sumAbsRatio < 1.0 ? -0.5 : 0.5))) >> (baseQP >= 38/*MAX_QP_PERCEPT_QPA*/ ? 1 : 0);
907
908
0
  ctuPumpRedQP[ctuRsAddr] += pumpingReducQP;
909
910
0
  return pumpingReducQP;
911
0
}
912
913
} // namespace vvenc
914
915
//! \}