/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 | | //! \} |