Coverage Report

Created: 2026-08-13 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/vvenc/source/Lib/EncoderLib/PreProcess.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     PreProcess.cpp
45
    \brief    
46
*/
47
48
49
#include "PreProcess.h"
50
#include "BitAllocation.h"
51
52
//! \ingroup EncoderLib
53
//! \{
54
55
namespace vvenc {
56
57
58
PreProcess::PreProcess( MsgLog& _m )
59
1.20k
  : m_encCfg     ( nullptr )
60
1.20k
  , m_gopCfg     ( _m )
61
1.20k
  , m_lastPoc    ( 0 )
62
1.20k
  , m_isHighRes  ( false )
63
1.20k
  , m_doSTA      ( false )
64
1.20k
  , m_doTempDown ( false )
65
1.20k
  , m_doVisAct   ( false )
66
1.20k
  , m_doVisActQpa( false )
67
1.20k
  , m_cappedCQF  ( false )
68
1.20k
{
69
1.20k
}
70
71
72
PreProcess::~PreProcess()
73
1.20k
{
74
1.20k
}
75
76
77
void PreProcess::init( const VVEncCfg& encCfg, bool isFinalPass )
78
1.20k
{
79
1.20k
  m_gopCfg.initGopList( encCfg.m_DecodingRefreshType, encCfg.m_poc0idr, encCfg.m_IntraPeriod, encCfg.m_GOPSize, encCfg.m_leadFrames, encCfg.m_picReordering, encCfg.m_GOPList, encCfg.m_vvencMCTF, encCfg.m_FirstPassMode, encCfg.m_minIntraDist );
80
1.20k
  CHECK( m_gopCfg.getMaxTLayer() != encCfg.m_maxTLayer, "max temporal layer of gop configuration does not match pre-configured value" );
81
82
1.20k
  m_encCfg             = &encCfg;
83
84
1.20k
  m_lastPoc            = std::numeric_limits<int>::min();
85
1.20k
  m_isHighRes          = (std::min (m_encCfg->m_SourceWidth, m_encCfg->m_SourceHeight) > 1280);
86
87
1.20k
  m_doSTA              = m_encCfg->m_sliceTypeAdapt > 0;
88
1.20k
  m_cappedCQF          = m_encCfg->m_RCNumPasses != 2 && m_encCfg->m_rateCap;
89
1.20k
  m_doTempDown         = m_encCfg->m_FirstPassMode == 2 || m_encCfg->m_FirstPassMode == 4;
90
1.20k
  m_doVisAct           = m_encCfg->m_usePerceptQPA
91
0
                         || (m_encCfg->m_LookAhead && m_encCfg->m_RCTargetBitrate > 0)
92
0
                         || (m_encCfg->m_RCNumPasses > 1 && (!isFinalPass));
93
1.20k
  m_doVisActQpa        = m_encCfg->m_usePerceptQPA;
94
1.20k
}
95
96
97
void PreProcess::initPicture( Picture* pic )
98
1.20k
{
99
1.20k
}
100
101
102
void PreProcess::processPictures( const PicList& picList, AccessUnitList& auList, PicList& doneList, PicList& freeList )
103
2.40k
{
104
  // continue with next poc
105
2.40k
  if( ! picList.empty() && picList.back()->poc > m_lastPoc )
106
1.20k
  {
107
1.20k
    auto pic = picList.back();
108
109
    // set gop entry
110
1.20k
    m_gopCfg.getNextGopEntry( pic->m_picShared->m_gopEntry );
111
1.20k
    CHECK( pic->m_picShared->m_gopEntry.m_POC != pic->poc, "invalid state" );
112
113
1.20k
    if( ! pic->m_picShared->isLeadTrail() )
114
1.20k
    {
115
      // link previous frames
116
1.20k
      xLinkPrevQpaBufs( pic, picList );
117
118
      // compute visual activity
119
1.20k
      xGetVisualActivity( pic, picList );
120
121
      // detect scc
122
1.20k
      xDetectScc( pic );
123
124
      // slice type adaptation
125
1.20k
      if( m_doSTA && pic->gopEntry->m_temporalId == 0 )
126
0
      {
127
        // detect scene cut in gop and adapt slice type
128
0
        xDetectSTA( pic, picList );
129
130
        // disable temporal downsampling for gop with scene cut
131
0
        if( m_doTempDown && pic->gopEntry->m_scType == SCT_TL0_SCENE_CUT )
132
0
        {
133
0
          xDisableTempDown( pic, picList );
134
0
        }
135
0
      }
136
137
      // disable temporal downsampling in lowest 3 temp. layers
138
      // with one-pass RC and QPA; this stabilizes the RC a bit
139
1.20k
      if( m_doTempDown && m_doVisActQpa && pic->gopEntry->m_temporalId <= 2 && m_encCfg->m_LookAhead && m_encCfg->m_RCTargetBitrate > 0 )
140
0
      {
141
0
        xDisableTempDown( pic, picList, 0 /*for faster - TODO: 2 for fast*/ );
142
0
      }
143
1.20k
    }
144
0
    else if( pic->gopEntry->m_temporalId == 0 )
145
0
    {
146
0
      xGetVisualActivity( pic, picList );
147
0
    }
148
149
    // cleanup pic list
150
1.20k
    xFreeUnused( pic, picList, doneList, freeList );
151
152
1.20k
    m_lastPoc = picList.back()->poc;
153
1.20k
  }
154
1.20k
  else if( ! picList.empty() && picList.back()->isFlush  )
155
1.20k
  {
156
    // first flush call, fix start of last gop
157
1.20k
    if( ! picList.empty() )
158
1.20k
    {
159
1.20k
      Picture* pic = xGetStartOfLastGop( picList );
160
1.20k
      if( pic )
161
1.20k
      {
162
1.20k
        m_gopCfg.fixStartOfLastGop( pic->m_picShared->m_gopEntry );
163
        // compute visual activity for start of last GOP
164
        // this works in combination with xUpdateVAStartOfLastGop()
165
1.20k
        xGetVisualActivity( pic, picList );
166
1.20k
      }
167
1.20k
    }
168
169
    // cleanup when flush is set and all pics are done
170
1.20k
    for( auto pic : picList )
171
1.20k
    {
172
1.20k
      freeList.push_back( pic );
173
1.20k
    }
174
1.20k
  }
175
2.40k
}
176
177
178
void PreProcess::xFreeUnused( Picture* pic, const PicList& picList, PicList& doneList, PicList& freeList ) const
179
1.20k
{
180
  // current picture is done
181
1.20k
  doneList.push_back( pic );
182
183
  // free unused previous frames
184
1.20k
  bool foundTl0       = ! m_doSTA; // is sta is off, not need to keep previous tl0 pic
185
1.20k
  int idx             = 0;
186
1.20k
  Picture* startOfGop = xGetStartOfLastGop( picList );
187
2.40k
  for( auto itr = picList.rbegin(); itr != picList.rend(); itr++, idx++ )
188
1.20k
  {
189
1.20k
    Picture* tp  = *itr;
190
1.20k
    bool keepPic = false;
191
192
    // keep previous frames for visual activity
193
1.20k
    keepPic  |= ( m_doVisAct || m_cappedCQF || m_encCfg->m_GOPQPA ) && idx < NUM_QPA_PREV_FRAMES;
194
    // keep previous (first) tl0 pic for sta
195
1.20k
    keepPic  |= ! foundTl0 && ( tp->gopEntry->m_temporalId == 0 || m_doTempDown );
196
1.20k
    foundTl0 |=                 tp->gopEntry->m_temporalId == 0;  // update found tl0
197
    // keep start of last gop
198
1.20k
    keepPic  |= ( tp == startOfGop );
199
200
1.20k
    if( ! keepPic )
201
0
    {
202
0
      freeList.push_back( tp );
203
0
    }
204
1.20k
  }
205
1.20k
}
206
207
208
void PreProcess::xGetPrevPics( const Picture* pic, const PicList& picList, const Picture* prevPics[ NUM_QPA_PREV_FRAMES ] ) const
209
1.20k
{
210
1.20k
  std::fill_n( prevPics, NUM_QPA_PREV_FRAMES, nullptr );
211
212
  // find previous pics
213
1.20k
  int prevPoc = pic->poc;
214
1.20k
  int prevIdx = 0;
215
2.40k
  for( auto itr = picList.rbegin(); itr != picList.rend() && prevIdx < NUM_QPA_PREV_FRAMES; itr++ )
216
1.20k
  {
217
1.20k
    Picture* tp = *itr;
218
1.20k
    if( tp->poc >= pic->poc )
219
1.20k
      continue;
220
0
    if( tp->poc != prevPoc - 1 )
221
0
      break;
222
0
    prevPics[ prevIdx ] = tp;
223
0
    prevPoc -= 1;
224
0
    prevIdx += 1;
225
0
  }
226
  // if first prev not found, set link to picture itself
227
1.20k
  if( prevPics[ 0 ] == nullptr )
228
1.20k
  {
229
1.20k
    prevPics[ 0 ] = pic;
230
1.20k
  }
231
1.20k
}
232
233
234
Picture* PreProcess::xGetPrevTL0Pic( const Picture* pic, const PicList& picList ) const
235
2.40k
{
236
  // find previous tl0 picture
237
2.40k
  Picture* prevTL0 = nullptr;
238
4.80k
  for( auto itr = picList.rbegin(); itr != picList.rend(); itr++ )
239
2.40k
  {
240
2.40k
    Picture* tp = *itr;
241
2.40k
    if( tp == pic )
242
2.40k
      continue;
243
0
    if( tp->gopEntry->m_temporalId == 0 )
244
0
    {
245
0
      prevTL0 = tp;
246
0
      break;
247
0
    }
248
0
  }
249
2.40k
  return prevTL0;
250
2.40k
}
251
252
253
Picture* PreProcess::xGetStartOfLastGop( const PicList& picList ) const
254
2.40k
{
255
  // use only non lead trail pics
256
2.40k
  std::vector<Picture*> cnList;
257
2.40k
  cnList.reserve( picList.size() );
258
2.40k
  for( auto pic : picList )
259
2.40k
  {
260
2.40k
    if( ! pic->m_picShared->isLeadTrail() )
261
2.40k
    {
262
2.40k
      cnList.push_back( pic );
263
2.40k
    }
264
2.40k
  }
265
266
2.40k
  if( cnList.empty() )
267
0
  {
268
0
    return nullptr;
269
0
  }
270
271
  // sort pics by coding number
272
2.40k
  std::sort( cnList.begin(), cnList.end(), []( auto& a, auto& b ){ return a->gopEntry->m_codingNum < b->gopEntry->m_codingNum; } );
273
274
  // find start of current gop
275
2.40k
  Picture* pic = cnList.back();
276
2.40k
  const int poc0Offset = (m_encCfg->m_poc0idr ? -1 : 0); // place leading poc 0 idr in GOP -1
277
2.40k
  const int lastGopNum = pic->gopEntry->m_gopNum + (pic->gopEntry->m_POC == 0 ? poc0Offset : 0);
278
4.80k
  for( auto itr = cnList.rbegin(); itr != cnList.rend(); itr++ )
279
2.40k
  {
280
2.40k
    Picture* tp = *itr;
281
2.40k
    const int tpGopNum = tp->gopEntry->m_gopNum + (tp->gopEntry->m_POC == 0 ? poc0Offset : 0);
282
2.40k
    if( tpGopNum != lastGopNum )
283
0
    {
284
0
      return pic;
285
0
    }
286
2.40k
    pic = tp;
287
2.40k
  }
288
2.40k
  return pic;
289
2.40k
}
290
291
292
void PreProcess::xLinkPrevQpaBufs( Picture* pic, const PicList& picList ) const
293
1.20k
{
294
1.20k
  PicShared* picShared = pic->m_picShared;
295
296
  // find max previous pictures
297
1.20k
  if( m_doVisAct )
298
1.20k
  {
299
1.20k
    const Picture* prevPics[ NUM_QPA_PREV_FRAMES ];
300
1.20k
    xGetPrevPics( pic, picList, prevPics );
301
3.60k
    for( int i = 0; i < NUM_QPA_PREV_FRAMES; i++ )
302
2.40k
    {
303
2.40k
      if( prevPics[ i ] )
304
1.20k
      {
305
1.20k
        picShared->m_prevShared[ i ] = prevPics[ i ]->m_picShared;
306
1.20k
      }
307
2.40k
    }
308
1.20k
  }
309
1.20k
}
310
311
312
void PreProcess::xGetVisualActivity( Picture* pic, const PicList& picList ) const
313
2.40k
{
314
2.40k
  VisAct va[ MAX_NUM_CH ];
315
2.40k
  VisAct vaTL0;
316
317
2.40k
  const bool doChroma           = m_cappedCQF && pic->gopEntry->m_isStartOfGop;
318
  // for the time being qpa activity done on ctu basis in applyQPAdaptationSlice(), which for now sums up luma activity
319
2.40k
  const bool doVisAct           = m_doVisAct && !m_doVisActQpa;
320
2.40k
  const bool doSpatAct          = pic->gopEntry->m_isStartOfGop && m_encCfg->m_GOPQPA;
321
2.40k
  const bool doVisActStartOfGOP = pic->gopEntry->m_isStartOfGop && m_cappedCQF;
322
2.40k
  const bool doVisActTL0        = pic->gopEntry->m_temporalId == 0 && ( m_doSTA || m_cappedCQF || !doVisAct );
323
324
  // spatial activity
325
2.40k
  if( doSpatAct || doVisAct || doVisActStartOfGOP || doVisActTL0 )
326
1.20k
  {
327
1.20k
    xGetSpatialActivity( pic, true, doChroma, va );
328
    // copy luma spatial activity for prev TL0
329
1.20k
    if( doVisActTL0 )
330
1.20k
    {
331
1.20k
      vaTL0 = va[ CH_L ];
332
1.20k
    }
333
1.20k
  }
334
335
  // visual activity to previous pics (luma only)
336
2.40k
  if( doVisAct || doVisActStartOfGOP )
337
0
  {
338
    // get temporal activity for picture
339
0
    const Picture* prevPics[ NUM_QPA_PREV_FRAMES ];
340
0
    xGetPrevPics( pic, picList, prevPics );
341
0
    xGetTemporalActivity( pic, prevPics[ 0 ], prevPics[ 1 ], va[ CH_L ] );
342
    // visual activity for picture
343
0
    updateVisAct( va[ CH_L ], m_encCfg->m_internalBitDepth[ CH_L ] );
344
0
  }
345
346
  // visual activity to previous TL0 pic (luma only)
347
2.40k
  const Picture* prevTL0 = xGetPrevTL0Pic( pic, picList );
348
2.40k
  if( doVisActTL0 && prevTL0 )
349
0
  {
350
    // get temporal activity for picture
351
0
    xGetTemporalActivity( pic, prevTL0, nullptr, vaTL0 );
352
    // visual activity for picture
353
0
    updateVisAct( vaTL0, m_encCfg->m_internalBitDepth[ CH_L ] );
354
0
  }
355
356
  // store visual activity
357
2.40k
  PicShared* picShared               = pic->m_picShared;
358
2.40k
  picShared->m_picVA.spatAct[ CH_L ] = ClipBD( (uint16_t)va[ CH_L ].spatAct, 12 );
359
2.40k
  picShared->m_picVA.spatAct[ CH_C ] = ClipBD( (uint16_t)va[ CH_C ].spatAct, 12 );
360
2.40k
  picShared->m_picVA.visAct          = va[ CH_L ].visAct;
361
2.40k
  picShared->m_picVA.visActTL0       = vaTL0.visAct;
362
2.40k
  if( prevTL0 )
363
0
  {
364
0
    picShared->m_picVA.prevTL0spatAct[ CH_L ] = prevTL0->m_picShared->m_picVA.spatAct[ CH_L ];
365
0
    picShared->m_picVA.prevTL0spatAct[ CH_C ] = prevTL0->m_picShared->m_picVA.spatAct[ CH_C ];
366
0
  }
367
  // update visual activity in pic, this is needed for STA detection in this stage
368
2.40k
  pic->picVA = picShared->m_picVA;
369
2.40k
}
370
371
372
void PreProcess::xGetSpatialActivity( Picture* pic, bool doLuma, bool doChroma, VisAct va[ MAX_NUM_CH ] ) const
373
1.20k
{
374
  // luma part
375
1.20k
  if( doLuma )
376
1.20k
  {
377
1.20k
    const int bitDepth = m_encCfg->m_internalBitDepth[ CH_L ];
378
1.20k
    CPelBuf origBuf    = pic->getOrigBuf( COMP_Y );
379
1.20k
    calcSpatialVisAct( origBuf.buf, origBuf.stride, origBuf.height, origBuf.width, bitDepth, m_isHighRes, va[ CH_L ] );
380
1.20k
  }
381
382
  // chroma part
383
1.20k
  if( doChroma )
384
0
  {
385
0
    const int bitDepth = m_encCfg->m_internalBitDepth[ CH_C ];
386
0
    const bool isUHD   = m_isHighRes && ( pic->chromaFormat == CHROMA_444 );
387
0
    const int numComp  = getNumberValidComponents( pic->chromaFormat );
388
    // accumulate spatial activity over chroma components
389
0
    for( int comp = 1; comp < numComp; comp++ )
390
0
    {
391
0
      VisAct chVA;
392
0
      const ComponentID compID = (ComponentID) comp;
393
0
      CPelBuf origBuf          = pic->getOrigBuf( compID );
394
0
      calcSpatialVisAct( origBuf.buf, origBuf.stride, origBuf.height, origBuf.width, bitDepth, isUHD, chVA );
395
0
      va[ CH_C ].hpSpatAct += chVA.hpSpatAct;
396
0
    }
397
    // mean value over chroma components
398
0
    va[ CH_C ].hpSpatAct = va[ CH_C ].hpSpatAct / (double)( numComp - 1 );
399
    // spatial in 12 bit
400
0
    va[ CH_C ].spatAct   = unsigned (0.5 + va[ CH_C ].hpSpatAct * double (bitDepth < 12 ? 1 << (12 - bitDepth) : 1));
401
0
  }
402
1.20k
}
403
404
405
void PreProcess::xGetTemporalActivity( Picture* curPic, const Picture* refPic1, const Picture* refPic2, VisAct& va ) const
406
0
{
407
0
  CHECK( curPic == nullptr || refPic1 == nullptr, "no pictures given to compute visual activity" );
408
409
0
  const int bitDepth = m_encCfg->m_internalBitDepth[ CH_L ];
410
411
0
  CPelBuf origBufs[ 3 ];
412
0
  origBufs[ 0 ] = curPic->getOrigBuf( COMP_Y );
413
0
  origBufs[ 1 ] = refPic1->getOrigBuf( COMP_Y );
414
0
  if( refPic2 )
415
0
  {
416
0
    origBufs[ 2 ] = refPic2->getOrigBuf( COMP_Y );
417
0
  }
418
419
0
  calcTemporalVisAct( origBufs[0].buf, origBufs[0].stride, origBufs[0].height, origBufs[0].width,
420
0
                      origBufs[1].buf, origBufs[1].stride,
421
0
                      origBufs[2].buf, origBufs[2].stride,
422
0
                      m_encCfg->m_FrameRate / m_encCfg->m_FrameScale,
423
0
                      bitDepth,
424
0
                      m_isHighRes,
425
0
                      va
426
0
                    );
427
0
}
428
429
430
void PreProcess::xDetectSTA( Picture* pic, const PicList& picList )
431
0
{
432
0
  const Picture* prevTL0 = xGetPrevTL0Pic( pic, picList );
433
434
0
  int picMemorySTA  = 0;
435
0
  bool isSta        = false;
436
0
  bool intraAllowed = m_gopCfg.isSTAallowed( pic->poc );
437
438
0
  if( prevTL0 && prevTL0->picVA.visActTL0 > 0 && intraAllowed )
439
0
  {
440
0
    const int scThreshold = ( ( pic->isSccStrong ? 6 : ( pic->isSccWeak ? 5 : 4 ) ) * ( m_isHighRes ? 19 : 15 ) ) >> 2;
441
442
0
    if(        pic->picVA.visActTL0 * 11 > prevTL0->picVA.visActTL0 * scThreshold
443
0
        || prevTL0->picVA.visActTL0 * 11 > pic->picVA.visActTL0     * ( scThreshold + 1 ) )
444
0
    {
445
0
      const int dir = pic->picVA.visActTL0 < prevTL0->picVA.visActTL0 ? -1 : 1;
446
0
      picMemorySTA  = prevTL0->picVA.visActTL0 * dir;
447
0
      isSta         = ( picMemorySTA * prevTL0->picMemorySTA ) >= 0;
448
0
    }
449
0
  }
450
451
0
  if( isSta )
452
0
  {
453
0
    PicShared* picShared              = pic->m_picShared;
454
0
    pic->picMemorySTA                 = picMemorySTA;
455
0
    picShared->m_picMemorySTA         = picMemorySTA;
456
0
    picShared->m_gopEntry.m_sliceType = 'I';
457
0
    picShared->m_gopEntry.m_scType    = SCT_TL0_SCENE_CUT;
458
0
    m_gopCfg.setLastIntraSTA( pic->poc );
459
460
0
    if( m_encCfg->m_sliceTypeAdapt == 2 )
461
0
    {
462
0
      m_gopCfg.startIntraPeriod( picShared->m_gopEntry );
463
0
    }
464
0
  }
465
0
}
466
467
468
void PreProcess::xDisableTempDown( Picture* pic, const PicList& picList, const int thresh /*= INT32_MAX*/ )
469
0
{
470
0
  for( auto itr = picList.rbegin(); itr != picList.rend(); itr++ )
471
0
  {
472
0
    Picture* tp = *itr;
473
0
    if( pic->gopEntry->m_gopNum != tp->gopEntry->m_gopNum )
474
0
      break;
475
0
    if( tp->gopEntry->m_temporalId <= thresh )
476
0
      tp->m_picShared->m_gopEntry.m_skipFirstPass = false;
477
0
  }
478
0
}
479
480
481
#if FIX_FOR_TEMPORARY_COMPILER_ISSUES_ENABLED && defined( __GNUC__ ) && __GNUC__ == 5
482
#pragma GCC diagnostic push
483
#pragma GCC diagnostic ignored "-Wstrict-overflow"
484
#endif
485
486
487
void PreProcess::xDetectScc( Picture* pic ) const
488
1.20k
{
489
1.20k
  if( m_encCfg->m_forceScc > 0 )
490
0
  {
491
0
    pic->isSccStrong = pic->m_picShared->m_isSccStrong = m_encCfg->m_forceScc >= 3;
492
0
    pic->isSccWeak   = pic->m_picShared->m_isSccWeak   = m_encCfg->m_forceScc >= 2;
493
0
    return;
494
0
  }
495
496
1.20k
  CPelBuf yuvOrgBuf = pic->getOrigBuf().Y();
497
498
  // blocksize and threshold
499
1.20k
  static constexpr int SIZE_BL =  4;
500
1.20k
  static constexpr int K_SC    = 23;
501
1.20k
  static constexpr int K_noSC  =  8;
502
503
  // mean and variance fixed point accuracy
504
1.20k
  static constexpr int accM = 4;
505
1.20k
  static constexpr int accV = 2;
506
507
1.20k
  static_assert( accM <= 4 && accV <= 4, "Maximum Mean and Variance accuracy of 4 allowed!" );
508
1.20k
  static constexpr int shfM = 4 - accM;
509
1.20k
  static constexpr int shfV = 4 + accM - accV;
510
1.20k
  static constexpr int addM = 1 << shfM >> 1;
511
1.20k
  static constexpr int addV = 1 << shfV >> 1;
512
513
1.20k
  static constexpr int SizeS = SIZE_BL << 1;
514
515
1.20k
  const int minLevel = 1 << ( m_encCfg->m_internalBitDepth[CH_L] - ( m_encCfg->m_videoFullRangeFlag ? 6 : 4 ) ); // 1/16th or 1/64th of range
516
517
1.20k
  const Pel*     piSrc    = yuvOrgBuf.buf;
518
1.20k
  const uint32_t uiStride = yuvOrgBuf.stride;
519
1.20k
  const uint32_t uiWidth  = yuvOrgBuf.width;
520
1.20k
  const uint32_t uiHeight = yuvOrgBuf.height;
521
522
1.20k
  CHECK( ( uiWidth & 7 ) != 0 || ( uiHeight & 7 ) != 0, "Width and height have to be multiples of 8!" );
523
524
1.20k
  const int amountBlock = ( uiWidth >> 2 ) * ( uiHeight >> 2 );
525
526
1.20k
  int sR[4] = { 0, 0, 0, 0 }; // strong SCC data
527
1.20k
  int zR[4] = { 0, 0, 0, 0 }; // zero input data
528
529
24.3k
  for( int hh = 0; hh < uiHeight; hh += SizeS )
530
23.1k
  {
531
489k
    for( int ww = 0; ww < uiWidth; ww += SizeS )
532
466k
    {
533
466k
      int Rx = ww >= ( uiWidth  >> 1 ) ? 1 : 0;
534
466k
      int Ry = hh >= ( uiHeight >> 1 ) ? 2 : 0;
535
466k
      Ry = Ry | Rx;
536
537
466k
      int n = 0;
538
466k
      int Var[4];
539
540
1.39M
      for( int j = hh; j < hh + SizeS; j += SIZE_BL )
541
933k
      {
542
2.79M
        for( int i = ww; i < ww + SizeS; i += SIZE_BL )
543
1.86M
        {
544
1.86M
          const Pel *p0 = &piSrc[j * uiStride + i];
545
546
1.86M
          int Mit = 0;
547
1.86M
          int V   = 0;
548
549
9.33M
          for( int h = 0; h < SIZE_BL; h++, p0 += uiStride )
550
7.46M
          {
551
37.3M
            for( int w = 0; w < SIZE_BL; w++ )
552
29.8M
            {
553
29.8M
              Mit += p0[w];
554
29.8M
            }
555
7.46M
          }
556
557
1.86M
          Mit = ( Mit + addM ) >> shfM;
558
559
1.86M
          p0 = &piSrc[j * uiStride + i];
560
561
9.33M
          for( int h = 0; h < SIZE_BL; h++, p0 += uiStride )
562
7.46M
          {
563
37.3M
            for( int w = 0; w < SIZE_BL; w++ )
564
29.8M
            {
565
29.8M
              V += abs( Mit - ( int( p0[w] ) << accM ) );
566
29.8M
            }
567
7.46M
          }
568
569
          // if variance is lower than 1 and mean is lower/equal to minLevel
570
1.86M
          if( V < ( 1 << ( accM + 4 ) ) && Mit <= ( minLevel << accM ) )
571
1.86M
          {
572
1.86M
            Var[n] = -1;
573
1.86M
          }
574
0
          else
575
0
          {
576
0
            Var[n] = ( V + addV ) >> shfV;
577
0
          }
578
579
1.86M
          n++;
580
1.86M
        }
581
933k
      }
582
583
1.39M
      for( int i = 0; i < 2; i++ )
584
933k
      {
585
933k
        const int var0 = Var[ i];
586
933k
        const int var1 = Var[ i + 2];
587
933k
        const int var2 = Var[ i << 1];
588
933k
        const int var3 = Var[(i << 1) + 1];
589
590
933k
        if( var0 < 0 && var1 < 0 && zR[Ry] * 20 < amountBlock )
591
188k
        {
592
188k
          zR[Ry]++;
593
188k
        }
594
744k
        else if( var0 == var1 )
595
744k
        {
596
744k
          sR[Ry]++;
597
744k
        }
598
599
933k
        if( var2 < 0 && var3 < 0 && zR[Ry] * 20 < amountBlock )
600
186k
        {
601
186k
          zR[Ry]++;
602
186k
        }
603
746k
        else if( var2 == var3 )
604
746k
        {
605
746k
          sR[Ry]++;
606
746k
        }
607
933k
      }
608
466k
    }
609
23.1k
  }
610
611
1.20k
  bool isSccWeak     = false;
612
1.20k
  bool isSccStrong   = false;
613
1.20k
  bool isNoSccStrong = false;
614
615
1.20k
  int numAll   = 0;
616
1.20k
  int numMin   = amountBlock, numMax = 0;
617
1.20k
  int numBelow = 0;
618
619
6.00k
  for( int r = 0; r < 4; r++ )
620
4.80k
  {
621
4.80k
    numAll   += sR[r];
622
4.80k
    numMax    = std::max( numMax, sR[r] );
623
4.80k
    numMin    = std::min( numMin, sR[r] );
624
4.80k
    numBelow += sR[r] * 100 <= K_SC * ( amountBlock >> 2 ) ? 1 : 0;
625
4.80k
  }
626
627
  // lowest quarter is above K_SC threshold
628
1.20k
  isSccStrong   = numMin * 100 >  K_SC *   ( amountBlock >> 2 );
629
  // lowest quarter is below K_noSC threshold and theres more than one quarter below K_SC threshold
630
1.20k
  isNoSccStrong = numMin * 100 <= K_noSC * ( amountBlock >> 2 ) && numBelow > 1;
631
  // overall is above K_SC threshold
632
1.20k
  isSccWeak     = numAll * 100 >  K_SC *     amountBlock;
633
  // peak quarter is above 2.15*K_SC threshold
634
1.20k
  isSccStrong  |= isSccWeak && !isNoSccStrong && numMax * 186 > K_SC * amountBlock;
635
636
1.20k
  PicShared* picShared     = pic->m_picShared;
637
1.20k
  pic->isSccWeak           = isSccWeak;
638
1.20k
  pic->isSccStrong         = isSccStrong;
639
1.20k
  picShared->m_isSccWeak   = isSccWeak;
640
1.20k
  picShared->m_isSccStrong = isSccStrong;
641
1.20k
}
642
643
644
#if FIX_FOR_TEMPORARY_COMPILER_ISSUES_ENABLED && defined( __GNUC__ ) && __GNUC__ == 5
645
#pragma GCC diagnostic pop
646
#endif
647
648
649
} // namespace vvenc
650
651
//! \}
652