Kodi Documentation 23.0
Kodi is an open source media player and entertainment hub.
Loading...
Searching...
No Matches
TestContentBarDetector.cpp File Reference
#include "SyntheticFrame.h"
#include "video/geometry/ContentBarDetector.h"
#include "video/geometry/ContentBarHistogram.h"
#include "video/geometry/test/GeometryTestHelpers.h"
#include <algorithm>
#include <array>
#include <utility>
#include <vector>
#include <gtest/gtest.h>

Functions

 TEST (TestContentBarDetector, CleanBars)
 
 TEST (TestContentBarDetector, SparseOutliersDoNotDefeatABar)
 
 TEST (TestContentBarDetector, DitheredBarsAreStillBars)
 
 TEST (TestContentBarDetector, HeavilyGrainedBarsAreReportedWideNotNarrow)
 
 TEST (TestContentBarDetector, FullRangeContentFlaggedAsLimited)
 
 TEST (TestContentBarDetector, UnspecifiedRangeAssumesLimitedAndCostsConfidence)
 
 TEST (TestContentBarDetector, DarkSaturatedPictureIsNotABar)
 
 TEST (TestContentBarDetector, FlatShadedDarkPictureIsIndistinguishableFromABar)
 
 TEST (TestContentBarDetector, SubtitleInBarDoesNotMoveTheRect)
 
 TEST (TestContentBarDetector, LogoInBarDoesNotMoveTheRect)
 
 TEST (TestContentBarDetector, OverWideOverlayFailsWide)
 
 TEST (TestContentBarDetector, CutToBlackIsDegenerate)
 
 TEST (TestContentBarDetector, FadeToBlackCollapsesConfidence)
 
 TEST (TestContentBarDetector, ContentAtItsTrueAspectGetsNoInventedBars)
 
 TEST (TestContentBarDetector, DarkButStructuredFirstRowIsNotABar)
 
 TEST (TestContentBarDetector, PillarboxOnly)
 
 TEST (TestContentBarDetector, WindowboxNeedsColumnsJudgedOverPictureRowsOnly)
 
 TEST (TestContentBarDetector, OffCentreBarsAreReportedNotCorrected)
 
 TEST (TestContentBarDetector, SubFloorEdgeIsNotABar)
 
 TEST (TestContentBarDetector, SingleBlackLineOnCorrectlyCodedContentIsIgnored)
 
 TEST (TestContentBarDetector, OneMarginalBarLineDoesNotZeroSeparation)
 
 TEST (TestContentBarDetector, OddBoundaryOn420LosesOneLineWide)
 
 TEST (TestContentBarDetector, MissingChromaPlanesSkipTheNeutralityTest)
 
 TEST (TestContentBarDetector, RoiSelectsOneViewOfSideBySide)
 
 TEST (TestContentBarDetector, OddDimensions)
 
 TEST (TestContentBarDetector, TinyFrameDoesNotMisbehave)
 
 TEST (TestContentBarDetector, ASampleAboveTheDeclaredDepthIsStillPicture)
 
 TEST (TestContentBarDetector, EmptyFrameIsRejected)
 
 TEST (TestContentBarHistogram, BothWaysOfAskingAgree)
 
 TEST (TestContentBarHistogram, AnEmptyHistogramAnswersZero)
 An empty histogram has no quantile to give, and says so the same way whichever is asked.
 
 TEST (TestContentBarHistogram, ResetEmptiesIt)
 Reset returns it to that state rather than to a stale one - it is reused line to line.
 
 TEST (TestContentBarHistogram, ResetLeavesNoCountsBehind)
 

Function Documentation

◆ TEST() [1/32]

TEST ( TestContentBarDetector ,
ASampleAboveTheDeclaredDepthIsStillPicture  )

◆ TEST() [2/32]

TEST ( TestContentBarDetector ,
CleanBars  )

Also expect a confident, non-degenerate reading

◆ TEST() [3/32]

TEST ( TestContentBarDetector ,
ContentAtItsTrueAspectGetsNoInventedBars  )

◆ TEST() [4/32]

TEST ( TestContentBarDetector ,
CutToBlackIsDegenerate  )

A cut to black finds no picture at all. This must never be treated as a very narrow reading; the rect stays at the full frame so that even a caller which ignores the flag fails wide rather than closing a mask onto picture.

◆ TEST() [5/32]

TEST ( TestContentBarDetector ,
DarkButStructuredFirstRowIsNotABar  )

A dark first row is not a bar unless it is also flat and achromatic. Real picture that happens to be dark still has range in it, and that is the whole discriminator.

◆ TEST() [6/32]

TEST ( TestContentBarDetector ,
DarkSaturatedPictureIsNotABar  )

Dark saturated picture - a night sky, sodium haze - is dark and flat in luma, so on PQ the luma tests barely discriminate. A true bar is achromatic; this is not.

◆ TEST() [7/32]

TEST ( TestContentBarDetector ,
DitheredBarsAreStillBars  )

Dither and lightly retained grain in the bar, at a level that is actually seen in delivered encodes. Note that a maximum-deviation test survives this too: the robust statistic's advantage is outlier tolerance, not noise tolerance, and claiming otherwise would overstate it.

◆ TEST() [8/32]

TEST ( TestContentBarDetector ,
EmptyFrameIsRejected  )

◆ TEST() [9/32]

TEST ( TestContentBarDetector ,
FadeToBlackCollapsesConfidence  )

A fade does not trip the degenerate flag - a few lines stay marginally above black - so the boolean alone is not enough to discard it. The boundary is soft, and confidence has to collapse instead.

◆ TEST() [10/32]

TEST ( TestContentBarDetector ,
FlatShadedDarkPictureIsIndistinguishableFromABar  )

The acknowledged hard case, pinned so a later change cannot quietly claim to have solved it. Flat-shaded animation can be genuinely dark, genuinely flat and genuinely achromatic, so it is indistinguishable from a bar in a single frame. The detector over-crops by the depth of the flat region and reports it with high confidence; only evidence from other frames can resolve it, which is the sampler's problem.

◆ TEST() [11/32]

TEST ( TestContentBarDetector ,
FullRangeContentFlaggedAsLimited  )

Content whose black really is 0 but whose flag says limited range. The limited threshold is the more generous of the two, so the reading is still correct - which is why an unknown range resolves to limited rather than to full.

◆ TEST() [12/32]

TEST ( TestContentBarDetector ,
HeavilyGrainedBarsAreReportedWideNotNarrow  )

The limit, pinned deliberately. No flatness band that is any use can accept a bar carrying sigma 8 of noise, and no threshold derivation should be widened until it can. The detector declines to call it a bar and reports the full frame - too wide, which is the harmless direction.

◆ TEST() [13/32]

TEST ( TestContentBarDetector ,
LogoInBarDoesNotMoveTheRect  )

◆ TEST() [14/32]

TEST ( TestContentBarDetector ,
MissingChromaPlanesSkipTheNeutralityTest  )

◆ TEST() [15/32]

TEST ( TestContentBarDetector ,
OddBoundaryOn420LosesOneLineWide  )

On 4:2:0 the chroma line straddling the boundary carries picture chroma, so the last bar line fails the neutrality test and the walk stops one line early. One row of bar is kept - the wider, safe direction - and that is asserted rather than left to be discovered later.

◆ TEST() [16/32]

TEST ( TestContentBarDetector ,
OddDimensions  )

◆ TEST() [17/32]

TEST ( TestContentBarDetector ,
OffCentreBarsAreReportedNotCorrected  )

Off-centre bars are real, and reporting a rect rather than a ratio exists precisely so they can be expressed. Asymmetry therefore costs confidence and must never alter the rectangle.

◆ TEST() [18/32]

TEST ( TestContentBarDetector ,
OneMarginalBarLineDoesNotZeroSeparation  )

On real content, one marginal line at the transition, which is where ringing lives, zeroed separation across a 263-line bar. Taking the worst line is the maximum-deviation mistake one level up; the bar as a whole is what the score is about.

◆ TEST() [19/32]

TEST ( TestContentBarDetector ,
OverWideOverlayFailsWide  )

An overlay wider than half the line is not admitted, so the walk stops and the bar is kept. That is the wider, safe direction, and it is exactly what would have happened without an overlay rule at all - the rule can only improve on the baseline, never make it worse. The flag lets the sampler down-weight the frame.

◆ TEST() [20/32]

TEST ( TestContentBarDetector ,
PillarboxOnly  )

◆ TEST() [21/32]

TEST ( TestContentBarDetector ,
RoiSelectsOneViewOfSideBySide  )

How a side-by-side frame is handled without the detector knowing what stereoscopy is: the caller, which can see stereo_mode, hands over one view. The rectangle comes back in full-frame coordinates.

◆ TEST() [22/32]

TEST ( TestContentBarDetector ,
SingleBlackLineOnCorrectlyCodedContentIsIgnored  )

The case this is really for: content already coded at its true aspect, where a single boundary line reads as black. It must come back exactly full-frame, not one line short.

◆ TEST() [23/32]

TEST ( TestContentBarDetector ,
SparseOutliersDoNotDefeatABar  )

The decisive case for a robust statistic. HandBrake's test is "every pixel within +/-16 of the row average", which a handful of ringing or stuck pixels in a 3840-wide row defeats outright. A high percentile tolerates half a percent of the line, so ten outliers cost nothing, and the row is still read as the bar it plainly is.

◆ TEST() [24/32]

TEST ( TestContentBarDetector ,
SubFloorEdgeIsNotABar  )

A sliver is not a bar. From real content (2026-08-06): a two-pixel edge alongside a 263-line letterbox scored zero on its own boundary and took the whole frame's confidence with it, on a reading otherwise correct to within a row; and on already-cropped content every spurious finding across 135 samples was exactly one line. Real letterboxing is tens to hundreds of lines. Sub-floor findings are dropped and flagged.

◆ TEST() [25/32]

TEST ( TestContentBarDetector ,
SubtitleInBarDoesNotMoveTheRect  )

◆ TEST() [26/32]

TEST ( TestContentBarDetector ,
TinyFrameDoesNotMisbehave  )

◆ TEST() [27/32]

TEST ( TestContentBarDetector ,
UnspecifiedRangeAssumesLimitedAndCostsConfidence  )

◆ TEST() [28/32]

TEST ( TestContentBarDetector ,
WindowboxNeedsColumnsJudgedOverPictureRowsOnly  )

On a letterboxed frame every column contains the bars, so a column walk run independently of the row walk is dark and flat all the way across and consumes the whole frame. Columns must be judged only over the rows that survived the row walk.

◆ TEST() [29/32]

TEST ( TestContentBarHistogram ,
AnEmptyHistogramAnswersZero  )

An empty histogram has no quantile to give, and says so the same way whichever is asked.

◆ TEST() [30/32]

TEST ( TestContentBarHistogram ,
BothWaysOfAskingAgree  )

The bulk answer exists only to save three walks of the buckets, so the one thing it owes is agreeing with the single one everywhere. Its loop advances through the quantiles while the single one restarts for each, and the two decide "past this target" in separate places - which is a divergence that would surface as a content edge in the wrong row and nothing else.

◆ TEST() [31/32]

TEST ( TestContentBarHistogram ,
ResetEmptiesIt  )

Reset returns it to that state rather than to a stale one - it is reused line to line.

◆ TEST() [32/32]

TEST ( TestContentBarHistogram ,
ResetLeavesNoCountsBehind  )

Reset clears only the buckets that were written, so what it leaves behind has to be checked by refilling rather than by asking an empty histogram - which answers zero without reading a bucket at all, and so passes whatever the bins hold.

A bar line is read, then a picture line, then a bar line again: counts surviving the first reset would put the second bar line's high percentile up among the picture values and stop the walk on a row that is a bar.