Re-add legacy AR detect

This commit is contained in:
Tamius Han 2025-10-14 18:52:22 +02:00
parent ddffbe92a0
commit c0c29759fa
13 changed files with 2765 additions and 176 deletions

View File

@ -8,6 +8,7 @@
* Setting inheritance/overriding is not thoroughly tested and may be full of edge cases.
* Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.
* Autodetection can now scan for subtitles.
* New experimental autodetection (which is secretly just slightly modified subtitle check)
### v6.3.0
* Added zoom segment to in-player UI and popup.

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "ultrawidify",
"version": "6.3.97",
"version": "6.3.98",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "ultrawidify",
"version": "6.3.97",
"version": "6.3.98",
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
"author": "Tamius Han <tamius.han@gmail.com>",
"scripts": {

View File

@ -90,6 +90,7 @@ export interface AardSubtitleScanOptions {
export interface AardSettings {
aardType: 'webgl' | 'legacy' | 'auto';
useLegacy: boolean,
earlyStopOptions: {
stopAfterFirstDetection: boolean;
@ -183,6 +184,131 @@ export interface AardSettings {
}
}
export interface AardLegacySettings {
aardType: 'webgl' | 'legacy' | 'auto';
earlyStopOptions: {
stopAfterFirstDetection: boolean;
stopAfterTimeout: boolean;
stopTimeout: number;
},
polling: {
runInBackgroundTabs: AardPollingOptions;
runOnSmallVideos: AardPollingOptions;
}
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar.
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency
playing: number, // while playing
playingReduced: number, // while video/player element has insufficient size
paused: number, // while paused
error: number, // after error
minimumTimeout: number,
tickrate: number, // 1 tick every this many milliseconds
},
subtitles: AardSubtitleScanOptions,
autoDisable: { // settings for automatically disabling the extension
onFirstChange: boolean,
ifNotChanged: boolean,
ifNotChangedTimeout: number,
ifSubtitles: boolean;
},
canvasDimensions: {
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
// it's not used to detect aspect ratio by itself, so it can be tiny af
width: number,
height: number,
},
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
// at the expense of performance
width: number,
height: number,
},
},
blackLevels: {
defaultBlack: number, // By default, pixels darker than this are considered black.
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
// It is not considered a valid image detection if gradient detection is enabled
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
}
sampling: {
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
randomCols: number, // we add this many randomly selected columns to the static columns
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
},
// pls deprecate and move things used
edgeDetection: {
slopeTestWidth: number,
gradientTestSamples: number, // we check this many pixels below (or above) the suspected edge to check for gradient
gradientTestBlackThreshold: number, // if pixel in test sample is brighter than that, we aren't looking at gradient
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
thresholds: {
edgeDetectionLimit: number, // during scanning of the edge, quit after edge gets detected at this many points
minQualitySingleEdge: number, // At least one of the detected must reach this quality
minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
}
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
// Previous iteration variables VVVV
sampleWidth: number, // we take a sample this wide for edge detection
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
confirmationThreshold: number, //
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
// to confirm an edge in case there's no edges on top or bottom (other
// than logo, of course)
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
// or watermark.
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
// continue with search. It's pointless, because black edge is higher/lower than we
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
edgeMismatchTolerancePx: number,// corners and center are considered equal if they differ by at most this many px
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable
minEdgeSegmentSize: number,
averageEdgeThreshold: number, // average edge must be this many px
},
letterboxOrientationScan: {
letterboxLimit: number, // how many non-black pixels we can detect before ruling out letterbox
pillarboxLimit: number, // how many non-black pixels we can detect before ruling out pillarbox
}
pillarTest: {
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
allowMisaligned: number // left and right edge can vary this much (%)
},
textLineTest: {
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
// with text. This value is relative to canvas width (%)
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
// is over 50% of the canvas width
testRowOffset: number // we test this % of height from detected edge
}
}
interface DevSettings {
loadFromSnapshot: boolean,
}
@ -194,7 +320,8 @@ interface SettingsInterface {
}
dev: DevSettings,
arDetect: AardSettings,
aardLegacy: AardLegacySettings,
aard: AardSettings,
ui: {
inPlayer: {

View File

@ -11,19 +11,29 @@
<div class="settings-segment">
<div class="field">
<div class="label">Autodetection mode (requires page reload)</div>
<div class="select">
<select v-model="settings.active.aard.useLegacy" @change="aardLegacyModeChanged">
<option :value="true">Legacy</option>
<option :value="false">Experimental</option>
</select>
</div>
</div>
<div class="field">
<div class="label">Autodetection frequency (time between samples)</div>
<div class="range-input">
<input
type="range"
:value="Math.log(settings.active.arDetect.timers.playing)"
:value="Math.log(aardSettings.timers.playing)"
@change="setArCheckFrequency($event.target.value)"
min="2.3"
max="9.3"
step="0.01"
/>
<input
v-model="settings.active.arDetect.timers.playing"
v-model="aardSettings.timers.playing"
@change="setArCheckFrequency($event.target.value)"
class="input"
type="text"
@ -37,14 +47,14 @@
<div class="range-input">
<input
type="range"
:value="Math.log(settings.active.arDetect.timers.playingReduced)"
:value="Math.log(aardSettings.timers.playingReduced)"
@change="setArCheckFrequency($event.target.value, 'playingReduced')"
min="2.3"
max="9.3"
step="0.01"
/>
<input
v-model="settings.active.arDetect.timers.playingReduced"
v-model="aardSettings.timers.playingReduced"
@change="setArCheckFrequency($event.target.value, 'playingReduced')"
class="input"
type="text"
@ -56,64 +66,64 @@
<div class="field">
<div class="label">Poll for aspect ratio changes in background tabs:</div>
<div class="select">
<select v-model="settings.active.arDetect.polling.runInBackgroundTabs" @change="settings.saveWithoutReload">
<select v-model="aardSettings.polling.runInBackgroundTabs" @change="settings.saveWithoutReload">
<option :value="AardPollingOptions.No">Never</option>
<option :value="AardPollingOptions.Reduced">Use reduced polling rate</option>
<option :value="AardPollingOptions.Full">Use normal polling rate</option>
</select>
</div>
</div>
<div v-if="settings.active.arDetect.polling.runInBackgroundTabs === AardPollingOptions.Full" class="hint warn">
<div v-if="aardSettings.polling.runInBackgroundTabs === AardPollingOptions.Full" class="hint warn">
Using normal polling rate in background tabs is NOT recommended.
</div>
<div class="field">
<div class="label">Poll for aspect ratio changes in small players:</div>
<div class="select">
<select v-model="settings.active.arDetect.polling.runOnSmallVideos" @change="settings.saveWithoutReload">
<select v-model="aardSettings.polling.runOnSmallVideos" @change="settings.saveWithoutReload">
<option :value="AardPollingOptions.No">Never</option>
<option :value="AardPollingOptions.Reduced">Use reduced polling rate</option>
<option :value="AardPollingOptions.Full">Use normal polling rate</option>
</select>
</div>
</div>
<div v-if="settings.active.arDetect.polling.runOnSmallVideos === AardPollingOptions.Full" class="hint warn">
<div v-if="aardSettings.polling.runOnSmallVideos === AardPollingOptions.Full" class="hint warn">
Using normal polling rate on small videos is NOT recommended.
</div>
<div class="field">
<div class="label">Stop autodetection after first detection:</div>
<div class="select">
<select v-model="settings.active.arDetect.autoDisable.onFirstChange" @change="settings.saveWithoutReload">
<select v-model="aardSettings.autoDisable.onFirstChange" @change="settings.saveWithoutReload">
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
</div>
<div class="field" :class="{disabled: settings.active.arDetect.autoDisable.onFirstChange}">
<div class="field" :class="{disabled: aardSettings.autoDisable.onFirstChange}">
<div class="label">Stop autodetection if aspect ratio doesn't change for some time:</div>
<div class="select">
<select v-model="settings.active.arDetect.autoDisable.ifNotChanged" @change="settings.saveWithoutReload">
<select v-model="aardSettings.autoDisable.ifNotChanged" @change="settings.saveWithoutReload">
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
</div>
<div v-if="settings.active.arDetect.autoDisable.ifNotChanged" class="field">
<div v-if="aardSettings.autoDisable.ifNotChanged" class="field">
<div class="label">Stop autodetection if aspect ratio doesn't change for:</div>
<div class="range-input">
<input
type="range"
:value="Math.log(settings.active.arDetect.autoDisable.ifNotChangedTimeout / 10)"
:value="Math.log(aardSettings.autoDisable.ifNotChangedTimeout / 10)"
@change="setAutoDisableTimeout($event.target.value, 10)"
min="2.3"
max="9.3"
step="0.01"
/>
<input
:value="settings.active.arDetect.autoDisable.ifNotChangedTimeout / 1000"
:value="aardSettings.autoDisable.ifNotChangedTimeout / 1000"
@change="setAutoDisableTimeout($event.target.value, 1000)"
class="input"
type="text"
@ -125,7 +135,7 @@
<div class="field">
<div class="label">Autodetection canvas type:</div>
<div class="select">
<select v-model="settings.active.arDetect.aardType" @change="settings.saveWithoutReload">
<select v-model="aardSettings.aardType" @change="settings.saveWithoutReload">
<option value="auto">Automatic</option>
<option value="webgl">WebGL only</option>
<option value="legacy">Legacy / fallback</option>
@ -136,7 +146,7 @@
<div class="field">
<div class="label">Maximum allowed vertical video misalignment:</div>
<div class="input">
<input v-model="settings.active.arDetect.allowedMisaligned" />
<input v-model="aardSettings.allowedMisaligned" />
</div>
</div>
<div class="hint">
@ -154,7 +164,7 @@
<div class="field">
<div class="label">Subtitle detection:</div>
<div class="select">
<select v-model="settings.active.arDetect.subtitles.subtitleCropMode" @change="settings.saveWithoutReload">
<select v-model="aardSettings.subtitles.subtitleCropMode" @change="settings.saveWithoutReload">
<option :value="AardSubtitleCropMode.DisableScan">Do not detect subtitles</option>
<option :value="AardSubtitleCropMode.ResetAR">Do not crop while subtitles are on screen</option>
<option :value="AardSubtitleCropMode.ResetAndDisable">Stop autodetection if subtitles are detected</option>
@ -163,19 +173,19 @@
</div>
</div>
<div v-if="settings.active.arDetect.subtitles.subtitleCropMode === AardSubtitleCropMode.ResetAR" class="field">
<div v-if="aardSettings.subtitles.subtitleCropMode === AardSubtitleCropMode.ResetAR" class="field">
<div class="label">Wait before resuming detection:</div>
<div class="range-input">
<input
type="range"
:value="Math.log(settings.active.arDetect.subtitles.resumeAfter / 10)"
:value="Math.log(aardSettings.subtitles.resumeAfter / 10)"
@change="setSubtitleTimeout($event.target.value, 10)"
min="2.3"
max="9.3"
step="0.01"
/>
<input
:value="settings.active.arDetect.subtitles.resumeAfter / 1000"
:value="aardSettings.subtitles.resumeAfter / 1000"
@change="setSubtitleTimeout($event.target.value, 1000)"
class="input"
type="text"
@ -261,6 +271,9 @@ export default {
}
},
computed: {
aardSettings() {
return this.settings.active.aard.useLegacy ? this.settings.active.aardLegacy : this.settings.active.aard;
}
},
created() {
this.eventBus.subscribe(
@ -285,15 +298,15 @@ export default {
BrowserDetect.runtime.openOptionsPage();
},
setArCheckFrequency(event, timer) {
this.settings.active.arDetect.timers[timer ?? 'playing'] = Math.floor(Math.pow(Math.E, event));
this.aardSettings.timers[timer ?? 'playing'] = Math.floor(Math.pow(Math.E, event));
this.settings.saveWithoutReload();
},
setAutoDisableTimeout(event, multiplier) {
this.settings.active.arDetect.autoDisable.ifNotChangedTimeout = Math.floor(Math.pow(Math.E, event)) * multiplier;
this.aardSettings.autoDisable.ifNotChangedTimeout = Math.floor(Math.pow(Math.E, event)) * multiplier;
this.settings.saveWithoutReload();
},
setSubtitleTimeout(event, multiplier) {
this.settings.active.arDetect.subtitles.resumeAfter = Math.floor(Math.pow(Math.E, event)) * multiplier;
this.aardSettings.subtitles.resumeAfter = Math.floor(Math.pow(Math.E, event)) * multiplier;
this.settings.saveWithoutReload();
},
refreshGraph() {
@ -311,6 +324,9 @@ export default {
saveDebugUiSettings() {
this.settings.active.ui.dev.aardDebugOverlay = JSON.parse(JSON.stringify(this.settingsJson));
this.settings.saveWithoutReload();
},
aardLegacyModeChanged() {
this.settings.save();
}
},

View File

@ -11,6 +11,10 @@
<li>Embedded sites now inherit settings of the parent frame. <small>However, this hasn't been tested for all edge cases and may contain bugs.</small></li>
<li>Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.</li>
<li>Autodetection can be set to stop after first aspect ratio detection, or after a period of no changes.</li>
<li>
There is a new, experiental mode for autodetection. At this point, it can be manually enabled in the autodetection settings. It will become the default option in 2026.<br/>
If you enable experimental mode, please consider reporting problems <a href="https://github.com/tamius-han/ultrawidify/issues/291" target="_blank">in this thread</a> on Github.
</li>
</ul>
</div>

View File

@ -255,17 +255,25 @@ const ExtensionConfPatch = Object.freeze([
{
forVersion: '6.3.93',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.arDetect.polling = defaultOptions.arDetect.polling;
userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles;
userOptions.arDetect.autoDisable = defaultOptions.arDetect.autoDisable;
(userOptions as any).arDetect.polling = defaultOptions.aard.polling;
(userOptions as any).arDetect.subtitles = defaultOptions.aard.subtitles;
(userOptions as any).arDetect.autoDisable = defaultOptions.aard.autoDisable;
}
},
{
forVersion: '6.3.97',
forVersion: '6.3.98',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.arDetect.letterboxOrientationScan = defaultOptions.arDetect.letterboxOrientationScan;
userOptions.arDetect.edgeDetection = defaultOptions.arDetect.edgeDetection;
userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles;
(userOptions as any).arDetect.letterboxOrientationScan = defaultOptions.aard.letterboxOrientationScan;
(userOptions as any).arDetect.edgeDetection = defaultOptions.aard.edgeDetection;
(userOptions as any).arDetect.subtitles = defaultOptions.aard.subtitles;
}
},
{
forVersion: '6.3.98',
upgradeFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.aard = defaultOptions.aard;
userOptions.aardLegacy = defaultOptions.aardLegacy;
delete (userOptions as any).arDetect;
}
}

View File

@ -21,7 +21,7 @@ const ExtensionConf: SettingsInterface = {
loadFromSnapshot: false,
},
arDetect: {
aardLegacy: {
aardType: 'auto',
polling: {
@ -88,6 +88,140 @@ const ExtensionConf: SettingsInterface = {
},
},
blackLevels: {
defaultBlack: 16,
blackTolerance: 4,
imageDelta: 16,
},
sampling: {
edgePosition: 0.25,
staticCols: 16, // we take a column at [0-n]/n-th parts along the width and sample it
randomCols: 0, // we add this many randomly selected columns to the static columns
staticRows: 9, // forms grid with staticSampleCols. Determined in the same way. For black frame checks
},
edgeDetection: {
slopeTestWidth: 8,
gradientTestSamples: 8,
gradientTestBlackThreshold: 16,
gradientTestDeltaThreshold: 32,
gradientTestMinDelta: 8,
thresholds: {
edgeDetectionLimit: 12,
minQualitySingleEdge: 6,
minQualitySecondEdge: 3,
},
maxLetterboxOffset: 0.1,
sampleWidth: 8, // we take a sample this wide for edge detection
detectionThreshold: 4, // sample needs to have this many non-black pixels to be a valid edge
confirmationThreshold: 1, //
singleSideConfirmationThreshold: 3, // we need this much edges (out of all samples, not just edges) in order
// to confirm an edge in case there's no edges on top or bottom (other
// than logo, of course)
logoThreshold: 0.15, // if edge candidate sits with count greater than this*all_samples, it can't be logo
// or watermark.
edgeTolerancePx: 1, // we check for black edge violation this far from detection point
edgeTolerancePercent: null, // we check for black edge detection this % of height from detection point. unused
middleIgnoredArea: 0.2, // we ignore this % of canvas height towards edges while detecting aspect ratios
minColsForSearch: 0.5, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
// continue with search. It's pointless, because black edge is higher/lower than we
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
edgeMismatchTolerancePx: 3, // corners and center are considered equal if they differ by at most this many px
gradientThreshold: 0.5, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
gradientTestMinDeltaAfter: 2, // if difference between test row and after row is LESS than this -> not gradient
gradientTestMaxDeltaAfter: 12, // if difference between test row and after row is MORE than this -> not gradient
minValidImage: 0.7, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
maxEdgeSegments: 8, // if edge has more than this many segments, we consider it unreliable
minEdgeSegmentSize: 2,
averageEdgeThreshold: 16, // average(ish) edge must be this many px
},
pillarTest: {
ignoreThinPillarsPx: 5, // ignore pillars that are less than this many pixels thick.
allowMisaligned: 0.05 // left and right edge can vary this much (%)
},
textLineTest: {
nonTextPulse: 0.10, // if a single continuous pulse has this many non-black pixels, we aren't dealing
// with text. This value is relative to canvas width (%)
pulsesToConfirm: 10, // this is a threshold to confirm we're seeing text.
pulsesToConfirmIfHalfBlack: 5, // this is the threshold to confirm we're seeing text if longest black pulse
// is over 50% of the canvas width
testRowOffset: 0.02 // we test this % of height from detected edge
}
},
aard: {
aardType: 'auto',
useLegacy: true,
polling: {
runInBackgroundTabs: AardPollingOptions.Reduced,
runOnSmallVideos: AardPollingOptions.Reduced
},
letterboxOrientationScan: {
letterboxLimit: 8,
pillarboxLimit: 8
},
subtitles: {
subtitleCropMode: AardSubtitleCropMode.ResetAR,
resumeAfter: 5000,
scanSpacing: 5,
scanMargin: 0.25,
maxValidLetter: 24,
subtitleSubpixelThresholdOff: 8,
subtitleSubpixelThresholdOn: 192,
minDetections: 8,
minImageLineDetections: 8,
refiningScanSpacing: 8,
refiningScanInitialIterations: 12,
maxPotentialSubtitleMisalignment: 32,
},
earlyStopOptions: {
stopAfterFirstDetection: false,
stopAfterTimeout: false,
stopTimeout: 30,
},
disabledReason: "", // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: 0.05, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar.
allowedArVariance: 0.0125,// amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency
playing: 333, // while playing
playingReduced: 5000, // while playing at small sizes
paused: 3000, // while paused
error: 3000, // after error
minimumTimeout: 5,
tickrate: 10, // 1 tick every this many milliseconds
},
autoDisable: { // settings for automatically disabling the extension
onFirstChange: false, // disable once we have a stable aspect ratio
ifNotChanged: false, // disable if Ar hasn't changed for this long
ifNotChangedTimeout: 20000, // if user enables ifNotChangedTimeout, we default to 20s
ifSubtitles: false, // disable if subtitles are detected
},
canvasDimensions: {
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
// it's not used to detect aspect ratio by itself, so it can be tiny af
width: 16,
height: 9,
},
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
// at the expense of performance
width: 640,
height: 360,
},
},
blackLevels: {
defaultBlack: 16,
blackTolerance: 4,

View File

@ -152,20 +152,20 @@ export class Aard {
this.canvasSamples = {
top: generateSampleArray(
this.settings.active.arDetect.sampling.staticCols,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width
this.settings.active.aard.sampling.staticCols,
this.settings.active.aard.canvasDimensions.sampleCanvas.width
),
bottom: generateSampleArray(
this.settings.active.arDetect.sampling.staticCols,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width
this.settings.active.aard.sampling.staticCols,
this.settings.active.aard.canvasDimensions.sampleCanvas.width
),
left: generateSampleArray(
this.settings.active.arDetect.sampling.staticCols,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
this.settings.active.aard.sampling.staticCols,
this.settings.active.aard.canvasDimensions.sampleCanvas.height
),
right: generateSampleArray(
this.settings.active.arDetect.sampling.staticCols,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
this.settings.active.aard.sampling.staticCols,
this.settings.active.aard.canvasDimensions.sampleCanvas.height
)
};
@ -188,14 +188,14 @@ export class Aard {
}
private createCanvas(canvasId: string, canvasType?: 'webgl' | 'legacy') {
ROW_SIZE = this.settings.active.arDetect.canvasDimensions.sampleCanvas.width * PIXEL_SIZE;
ROW_SIZE = this.settings.active.aard.canvasDimensions.sampleCanvas.width * PIXEL_SIZE;
if (canvasType) {
if (canvasType === this.settings.active.arDetect.aardType || this.settings.active.arDetect.aardType === 'auto') {
if (canvasType === this.settings.active.aard.aardType || this.settings.active.aard.aardType === 'auto') {
if (canvasType === 'webgl') {
return new GlCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-gl'});
return new GlCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-gl'});
} else if (canvasType === 'legacy') {
return new FallbackCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-legacy'});
return new FallbackCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-legacy'});
} else {
// TODO: throw error
}
@ -205,21 +205,21 @@ export class Aard {
}
if (['auto', 'webgl'].includes(this.settings.active.arDetect.aardType)) {
if (['auto', 'webgl'].includes(this.settings.active.aard.aardType)) {
try {
return new GlCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-gl'});
return new GlCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-gl'});
} catch (e) {
if (this.settings.active.arDetect.aardType !== 'webgl') {
return new FallbackCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-legacy'});
if (this.settings.active.aard.aardType !== 'webgl') {
return new FallbackCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-legacy'});
}
this.logger.error('createCanvas', 'could not create webgl canvas:', e);
this.eventBus.send('uw-config-broadcast', {type: 'aard-error', aardErrors: {webglError: true}});
throw e;
}
} else if (this.settings.active.arDetect.aardType === 'legacy') {
return new FallbackCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-legacy'});
} else if (this.settings.active.aard.aardType === 'legacy') {
return new FallbackCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-legacy'});
} else {
this.logger.error('createCanvas', 'invalid value in settings.arDetect.aardType:', this.settings.active.arDetect.aardType);
this.logger.error('createCanvas', 'invalid value in settings.arDetect.aardType:', this.settings.active.aard.aardType);
this.eventBus.send('uw-config-broadcast', {type: 'aard-error', aardErrors: {invalidSettings: true}});
throw 'AARD_INVALID_SETTINGS';
}
@ -231,7 +231,7 @@ export class Aard {
*/
private showDebugCanvas() {
if (!this.canvasStore.debug) {
this.canvasStore.debug = new GlDebugCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'uw-debug-gl'});
this.canvasStore.debug = new GlDebugCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'uw-debug-gl'});
}
this.canvasStore.debug.enableFx();
if (!this.debugConfig.debugUi) {
@ -290,8 +290,8 @@ export class Aard {
}
// do full reset of test samples
this.testResults = initAardTestResults(this.settings.active.arDetect);
this.verticalTestResults = initAardTestResults(this.settings.active.arDetect);
this.testResults = initAardTestResults(this.settings.active.aard);
this.verticalTestResults = initAardTestResults(this.settings.active.aard);
if (this.animationFrame) {
window.cancelAnimationFrame(this.animationFrame);
@ -301,8 +301,8 @@ export class Aard {
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
// set auto-disable timer if detection timeout is set
if (this.settings.active.arDetect.autoDisable.ifNotChanged) {
this.timers.autoDisableAt = Date.now() + this.settings.active.arDetect.autoDisable.ifNotChangedTimeout;
if (this.settings.active.aard.autoDisable.ifNotChanged) {
this.timers.autoDisableAt = Date.now() + this.settings.active.aard.autoDisable.ifNotChangedTimeout;
}
}
@ -314,8 +314,8 @@ export class Aard {
this.stop();
if (options?.noCache) {
this.testResults = initAardTestResults(this.settings.active.arDetect);
this.verticalTestResults = initAardTestResults(this.settings.active.arDetect);
this.testResults = initAardTestResults(this.settings.active.aard);
this.verticalTestResults = initAardTestResults(this.settings.active.aard);
}
this.main();
@ -342,7 +342,7 @@ export class Aard {
// if video was paused & we know that we already checked that frame,
// we will not check it again.
const videoState = this.getVideoPlaybackState();
const polling = this.settings.active.arDetect.polling;
const polling = this.settings.active.aard.polling;
const now = Date.now();
if (videoState !== VideoPlaybackState.Playing) {
@ -375,8 +375,8 @@ export class Aard {
return false;
}
this.timers.nextFrameCheckTime = now + this.settings.active.arDetect.timers.playing;
this.timers.reducedPollingNextCheckTime = now + this.settings.active.arDetect.timers.playingReduced;
this.timers.nextFrameCheckTime = now + this.settings.active.aard.timers.playing;
this.timers.reducedPollingNextCheckTime = now + this.settings.active.aard.timers.playingReduced;
return true;
}
@ -403,7 +403,7 @@ export class Aard {
* Main loop for scanning aspect ratio changes
*/
private async main() {
const arConf = this.settings.active.arDetect;
const arConf = this.settings.active.aard;
try {
this.timer.next();
@ -745,8 +745,8 @@ export class Aard {
const lastPixelOffset = ROW_SIZE - PIXEL_SIZE;
const imageSize = ROW_SIZE * height;
const xLimit = this.settings.active.arDetect.letterboxOrientationScan.letterboxLimit;
const yLimit = this.settings.active.arDetect.letterboxOrientationScan.pillarboxLimit;
const xLimit = this.settings.active.aard.letterboxOrientationScan.letterboxLimit;
const yLimit = this.settings.active.aard.letterboxOrientationScan.pillarboxLimit;
let letterbox = true, pillarbox = true;
let xCount = 0, yCount = 0;
@ -844,7 +844,7 @@ export class Aard {
*/
private updateLetterboxEdgeCandidates(crossDimension: number, topCandidate: number, bottomCandidate: number) {
const bottomDistance = (crossDimension - bottomCandidate);
const maxOffset = ~~(crossDimension * this.settings.active.arDetect.edgeDetection.maxLetterboxOffset);
const maxOffset = ~~(crossDimension * this.settings.active.aard.edgeDetection.maxLetterboxOffset);
const diff = Math.abs(topCandidate - bottomDistance);
const candidateAvg = ~~((topCandidate + bottomDistance) * 0.5);
@ -869,7 +869,7 @@ export class Aard {
* @returns
*/
private subtitleScan(imageData: Uint8Array, width: number, height: number, skipAdvancedScan: boolean) {
const scanConf = this.settings.active.arDetect.subtitles;
const scanConf = this.settings.active.aard.subtitles;
this.testResults.subtitleDetected = false;
@ -931,8 +931,8 @@ export class Aard {
) {
results.uncertain = false;
const scanConf = this.settings.active.arDetect.subtitles;
const arConf = this.settings.active.arDetect;
const scanConf = this.settings.active.aard.subtitles;
const arConf = this.settings.active.aard;
let letterCount, imageSegmentCount, potentialFadedLetterCount, potentialFadedLetterCountInvalidated, nonGradientPixelCount, letterSize, imageSize, imageSegmentSize, imageWeightedSize, segmentWeights, imageSegmentAlignment, imageSegmentAlignmentSamples,
isOnLetter, isOnImage, isBlank,
@ -940,7 +940,7 @@ export class Aard {
let rowStart, rowEnd, rowMid, rowGTA, rowGTB; // GT = gradient test
let imageConfirmPass = false, subtitleConfirmPass = false;
let innerIteration, outerIteration = 0;
let outerIteration = 0;
const rowMargin = Math.floor(scanConf.scanMargin * ROW_SIZE);
const imageThreshold = Math.floor((ROW_SIZE - (rowMargin * 2)) * arConf.edgeDetection.minValidImage * PIXEL_SIZE_FRACTION);
@ -953,8 +953,11 @@ export class Aard {
(scanSpacing > 0 && searchRow < endRow) || (scanSpacing < 0 && searchRow > endRow);
searchRow += scanSpacing
) {
innerIteration = 0;
outerIteration++;
if (++outerIteration > height) {
console.warn('[ultrawidify|aard::subtitleScanRegionLinear] — scan got stuck in an infinite loop. This shouldn\'t happen.');
results.uncertain;
break outerLoop;
}
letterCount = 0;
potentialFadedLetterCount = 0;
@ -1019,8 +1022,6 @@ export class Aard {
*/
while (rowStart < rowEnd) {
innerIteration++;
const r = imageData[rowStart], g = imageData[rowStart + 1], b = imageData[rowStart + 2];
const on = r > scanConf.subtitleSubpixelThresholdOn
@ -1316,7 +1317,7 @@ export class Aard {
const maxRatio = Math.max(ar, this.testResults.activeAspectRatio);
const diff = Math.abs(ar - this.testResults.activeAspectRatio);
if ((diff / maxRatio) > this.settings.active.arDetect.allowedArVariance || options?.forceReset) {
if ((diff / maxRatio) > this.settings.active.aard.allowedArVariance || options?.forceReset) {
this.videoData.resizer.updateAr({
type: AspectRatioType.AutomaticUpdate,
ratio: ar,
@ -1326,11 +1327,11 @@ export class Aard {
this.testResults.activeAspectRatio = ar;
if (!options?.uncertainDetection) {
if (this.settings.active.arDetect.autoDisable.onFirstChange) {
if (this.settings.active.aard.autoDisable.onFirstChange) {
this.status.autoDisabled = true;
}
if (this.settings.active.arDetect.autoDisable.ifNotChanged) {
this.timers.autoDisableAt = Date.now() + this.settings.active.arDetect.autoDisable.ifNotChangedTimeout;
if (this.settings.active.aard.autoDisable.ifNotChanged) {
this.timers.autoDisableAt = Date.now() + this.settings.active.aard.autoDisable.ifNotChangedTimeout;
}
}

View File

@ -1,4 +1,7 @@
import { Aard } from './Aard';
import { AardLegacy } from './AardLegacy';
import { AardPerformanceData } from './AardTimers';
import { FallbackCanvas } from './gl/FallbackCanvas';
export class AardDebugUi {
@ -76,37 +79,11 @@ export class AardDebugUi {
</div>
<div id="uw-aard-debug-ui_body" style="display: flex; flex-direction: row; width: 100%">
<div id="uw-aard-debug-ui_body" style="display: flex; flex-direction: row; width: 100%; margin-top: 8rem;">
<div style="">
<div id="uw-aard-debug_aard-sample-canvas" style="min-width: 640px"></div>
<div style="background: black; color: #fff"; font-size: 24px;">AARD IN</div>
<div style="background: black; color: #ccc; padding: 1rem">
<div>
<span style="color: rgb(0.1, 0.1, 0.35)"></span>
Black level sample
</div>
<div>
<span style="color: rgb(0.3, 1.0, 0.6)"></span>
<span style="color: rgb(0.1, 0.5, 0.3)"></span>
Guard line (middle/corner) OK
</div>
<div>
<span style="color: rgb(1.0, 0.1, 0.1)"></span>
<span style="color: rgb(0.5, 0.0, 0.0)"></span>
Guard line (middle/corner) violation
</div>
<div>
Image line <span style="color: rgb(0.7, 0.7, 0.7)"></span> image, <span style="color: rgb(0.2, 0.2, 0.6)"></span> no image
</div>
<div>
Edge scan <span style="color: rgb(0.1, 0.1, 0.4)"></span> probe, <span style="color: rgb(0.4, 0.4, 1.0)"></span> hit
</div>
<div>
Slope test <span style="color: rgb(0.4, 0.4, 1.0)"></span> ok, <span style="color: rgb(1.0, 0.0, 0.0)"></span> fail
</div>
</div>
<div style="pointer-events: all">
<button id="uw-aard-debug-ui_enable-stop-on-change" style="">Pause video on aspect ratio change</button>
<button id="uw-aard-debug-ui_disable-stop-on-change" style="display: none">Stop pausing video on aspect ratio change</button>
@ -125,10 +102,9 @@ export class AardDebugUi {
<pre id="uw-aard-results"></pre>
</div>
</div>
<div style="width: 1920px">
<div style="width: 1920px; border: 2px dotted #142; margin-right:2rem;">
<div id="uw-aard-debug_aard-output" style="zoom: 3; image-rendering: pixelated;"></div>
<div style="background: black; color: #fff; font-size: 24px;">AARD RESULT</div>
</div>
</div>
</div>
@ -194,9 +170,19 @@ export class AardDebugUi {
const popupContent = `
<h2 style="color: #fa6; margin-bottom: 1rem">Detailed performance analysis:</h2>
<div>
<span style="color: #fff">About:</span><br/>
Aard version: <span style="color: #fa6">${this.aard instanceof AardLegacy ? 'legacy' : this.aard instanceof Aard ? 'experimental' : 'unknown'}</span> <small>(instanceof AardLegacy? ${this.aard instanceof AardLegacy}, Aard? ${this.aard instanceof Aard})</small><br/>
canvas type: <span style="color: #fa6">${!this.aard.canvasStore.main ? '<canvas not initialized>' : this.aard.canvasStore.main instanceof FallbackCanvas ? '2dCanvas' : 'webgl'}</span> |
legacy default: <span style="color: #fa6">${this.aard.settings.active.aardLegacy.aardType}</span>,
experimental default: <span style="color: #fa6">${this.aard.settings.active.aard.aardType}</span>
</div>
<br/>
<div style="width: 100%; display: flex; flex-direction: column">
<div style="margin-bottom: 1rem;">
${this.generateRawTimes(this.aard.timer.average)}
${this.generateRawTimes(this.aard.timer.current)}
</div>
<div style="color: #fff">Stage times (not cumulative):</div>
<div style="display: flex; flex-direction: row; width: 100%; height: 150px">
@ -214,12 +200,14 @@ export class AardDebugUi {
</div>
<div style="margin-bottom: 1rem;">
${this.generateRawTimes(this.aard.timer.average)}
${this.generateRawTimes(this.aard.timer.lastChange)}
</div>
<div style="display: flex; flex-direction: row; width: 100%; height: 150px">
<div style="width: 160px; text-align: right; padding-right: 4px;">Last change:</div>
<div style="flex-grow: 1;">${this.generateMiniGraphBar(this.aard.timer.lastChange, true)}</div>
</div>
<!-- <pre>${JSON.stringify({current: this.aard.timer.current, average: this.aard.timer.average, lastChange: this.aard.timer.lastChange}, null, 2)}</pre> -->
</div>
`
@ -298,23 +286,23 @@ export class AardDebugUi {
total += fastBlackLevel;
const guardLineStart = fastBlackLevelStart + fastBlackLevel;
const guardLine = Math.max(perf.guardLine - total, 0);
const guardLine = (perf.guardLine !== undefined && perf.guardLine !== -1) ? Math.max(perf.guardLine - total, 0) : 0;
total += guardLine;
const edgeScanStart = guardLineStart + guardLine;
const edgeScan = Math.max(perf.edgeScan - total, 0);
const edgeScan = (perf.edgeScan !== undefined && perf.edgeScan !== -1) ? Math.max(perf.edgeScan - total, 0) : 0;
total += edgeScan;
const gradientStart = edgeScanStart + edgeScan;
const gradient = Math.max(perf.gradient - total, 0);
const gradient = (perf.gradient !== undefined && perf.gradient !== -1) ? Math.max(perf.gradient - total, 0) : 0;
total += gradient;
const scanResultsStart = gradientStart + gradient;
const scanResults = Math.max(perf.scanResults - total, 0);
const scanResults = (perf.scanResults !== undefined && perf.scanResults !== -1) ? Math.max(perf.scanResults - total, 0) : 0;
total += scanResults;
const subtitleScanStart = scanResultsStart + scanResults;
const subtitleScan = Math.max(perf.scanResults - total, 0);
const subtitleScan = Math.max(perf.subtitleScan - total, 0);
total += subtitleScan;
return `
@ -344,12 +332,10 @@ export class AardDebugUi {
<div style="position: absolute; top: ${detailed ? '74px' : '2px'}; left: ${scanResultsStart}%; min-width: 1px; width: ${scanResults}%; background: #80f; height: 12px;"></div>
${this.getBarLabel(scanResults, scanResultsStart, 74, `scan results processing: ${scanResults.toFixed(2)} ms`, detailed)}
${this.getBarLabel(0, scanResults + scanResultsStart, 88, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
<div style="position: absolute; top: ${detailed ? '86px' : '2px'}; left: ${subtitleScanStart}%; min-width: 1px; width: ${subtitleScan}%; background: rgba(234, 204, 84, 1); height: 12px;"></div>
${this.getBarLabel(subtitleScan, subtitleScanStart, 86, `subtitle scan: ${subtitleScan.toFixed(2)} ms`, detailed)}
<div style="position: absolute; top: ${detailed ? '74px' : '2px'}; left: ${subtitleScanStart}%; min-width: 1px; width: ${subtitleScan}%; background: rgba(234, 204, 84, 1); height: 12px;"></div>
${this.getBarLabel(scanResults, scanResultsStart, 74, `scan results processing: ${subtitleScan.toFixed(2)} ms`, detailed)}
${this.getBarLabel(0, scanResults + scanResultsStart, 88, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
${this.getBarLabel(0, subtitleScan + subtitleScanStart, 98, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
<!-- 60/30 fps markers -->
<div style="position: absolute; top: ${detailed ? '-12px' : '0'}; left: 16.666%; width: 1px; border-left: 1px dashed #4f9; height: ${detailed ? '112px' : '12px'}; padding-left: 2px; background-color: rgba(0,0,0,0.5); z-index: ${detailed ? '5' : '2'}000;">60fps</div>
@ -359,7 +345,7 @@ export class AardDebugUi {
}
_lastAr: undefined;
updateTestResults(testResults) {
updateTestResults(testResults, timers) {
this.updatePerformanceResults();
if (testResults.aspectRatioUpdated && this.pauseOnArCheck) {
@ -378,52 +364,13 @@ export class AardDebugUi {
Active: ${ar}, changed since last check? ${testResults.aspectRatioUpdated} letterbox width: ${testResults.letterboxWidth} offset ${testResults.letterboxOffset}<br/>
<sup>(last: ${this._lastAr})</sup>
Paused until? ${Date.now() < timers.pauseUntil ? ((timers.pauseUntil - Date.now()) / 1000) + 's' : 'not paused'};
<sup>now: ${Date.now()}ms; until:${timers.pauseUntil}ms; diff: ${(+timers.pauseUntil - Date.now())} </sup>
image in black level probe (aka "not letterbox"): ${testResults.notLetterbox}
`;
this._lastAr = ar;
if (testResults.notLetterbox) {
resultsDiv.innerHTML = out;
return;
}
out = `${out}
-- UNCERTAIN FLAGS
AR: ${testResults.aspectRatioUncertain} (reason: ${testResults.aspectRatioUncertainReason ?? 'n/a'}); top row: ${testResults.topRowUncertain}; bottom row: ${testResults.bottomRowUncertain}${
testResults.aspectRatioInvalid ? `\nINVALID_AR (reason: ${testResults.aspectRatioInvalidReason ?? 'n/a'})` : ''}
-- GUARD & IMAGE LINE
bottom guard: ${testResults.guardLine.bottom} image: ${testResults.guardLine.invalidated ? 'n/a' : testResults.imageLine.bottom}
top guard: ${testResults.guardLine.top} image: ${testResults.guardLine.invalidated ? 'n/a' : testResults.imageLine.top}
guard line ${testResults.guardLine.invalidated ? 'INVALIDATED' : 'valid'} image line ${testResults.guardLine.invalidated ? '<skipped test>' : testResults.imageLine.invalidated ? 'INVALIDATED' : 'valid'}
corner invalidations (invalid pixels -> verdict)
LEFT CENTER RIGHT
bottom: ${testResults.guardLine.cornerPixelsViolated[0]} ${testResults.guardLine.cornerViolated[0] ? '❌' : '◽'} ${testResults.guardLine.cornerPixelsViolated[1]} ${testResults.guardLine.cornerViolated[1] ? '❌' : '◽'}
top: ${testResults.guardLine.cornerPixelsViolated[2]} ${testResults.guardLine.cornerViolated[2] ? '❌' : '◽'} ${testResults.guardLine.cornerPixelsViolated[3]} ${testResults.guardLine.cornerViolated[3] ? '❌' : '◽'}
-- AR SCAN ${testResults.lastStage < 1 ? `
DID NOT RUN THIS FRAME` : `
LEFT CENTER RIGHT CANDIDATE
BOTTOM
distance: ${testResults.aspectRatioCheck.bottomRows[0]} ${testResults.aspectRatioCheck.bottomRows[1]} ${testResults.aspectRatioCheck.bottomRows[2]} ${testResults.aspectRatioCheck.bottomCandidate}
quality: ${testResults.aspectRatioCheck.bottomQuality[0]} ${testResults.aspectRatioCheck.bottomQuality[1]} ${testResults.aspectRatioCheck.bottomQuality[2]} ${testResults.aspectRatioCheck.bottomCandidateQuality}
TOP
distance: ${testResults.aspectRatioCheck.topRows[0]} ${testResults.aspectRatioCheck.topRows[1]} ${testResults.aspectRatioCheck.topRows[2]} ${testResults.aspectRatioCheck.topCandidate}
quality: ${testResults.aspectRatioCheck.topQuality[0]} ${testResults.aspectRatioCheck.topQuality[1]} ${testResults.aspectRatioCheck.topQuality[2]} ${testResults.aspectRatioCheck.topCandidateQuality}
Diff matrix:
R-L C-R C-L
bottom: ${testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[0]} ${testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[1]} ${testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[2]}
top: ${testResults.aspectRatioCheck.topRowsDifferenceMatrix[0]} ${testResults.aspectRatioCheck.topRowsDifferenceMatrix[1]} ${testResults.aspectRatioCheck.topRowsDifferenceMatrix[2]}
`}
`;
resultsDiv.innerHTML = out;
}

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,13 @@ export interface AardPerformanceData {
getImage: number;
fastBlackLevel: number;
guardLine: number; // actually times both guard line and image line checks
edgeScan: number; // includes validation step
gradient: number;
// optional ones are only available in legacy aard
guardLine?: number; // actually times both guard line and image line checks
edgeScan?: number; // includes validation step
gradient?: number;
scanResults?: number;
subtitleScan: number;
scanResults: number;
}
@ -82,36 +84,52 @@ export class AardTimer {
getAverage() {
for (let i = 0; i < this.aardPerformanceDataBuffer.length; i++) {
const sample = this.aardPerformanceDataBuffer[i];
if (sample.draw !== -1) {
this.average.draw += sample.draw;
}
if (sample.getImage !== -1) {
this.average.getImage += sample.getImage;
}
if (sample.fastBlackLevel !== -1) {
if (sample.fastBlackLevel !== -1 && this.average.fastBlackLevel !== null) {
this.average.fastBlackLevel += sample.fastBlackLevel;
} else {
this.average.fastBlackLevel = null;
}
if (sample.guardLine !== -1) {
if (sample.guardLine !== -1 && this.average.guardLine !== null) {
this.average.guardLine += sample.guardLine;
} else {
this.average.guardLine = null;
}
if (sample.edgeScan !== -1) {
if (sample.edgeScan !== -1 && this.average.edgeScan !== null) {
this.average.edgeScan += sample.edgeScan;
} else {
this.average.edgeScan = null;
}
if (sample.gradient !== -1) {
if (sample.gradient !== -1 && this.average.gradient !== null) {
this.average.gradient += sample.gradient;
} else {
this.average.edgeScan = null;
}
if (sample.scanResults !== -1) {
if (sample.scanResults !== -1 && this.average.scanResults !== null) {
this.average.scanResults += sample.scanResults;
} else {
this.average.scanResults = null;
}
if (sample.subtitleScan !== -1 && this.average.subtitleScan !== null) {
this.average.subtitleScan += sample.subtitleScan;
} else {
this.average.subtitleScan = null;
}
}
this.average.draw /= this.aardPerformanceDataBuffer.length;
this.average.getImage /= this.aardPerformanceDataBuffer.length;
this.average.fastBlackLevel /= this.aardPerformanceDataBuffer.length;
this.average.guardLine /= this.aardPerformanceDataBuffer.length;
this.average.edgeScan /= this.aardPerformanceDataBuffer.length;
this.average.gradient /= this.aardPerformanceDataBuffer.length;
this.average.scanResults /= this.aardPerformanceDataBuffer.length;
this.average.subtitleScan /= this.aardPerformanceDataBuffer.length;
this.average.guardLine = this.average.guardLine === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
this.average.edgeScan = this.average.edgeScan === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
this.average.gradient = this.average.gradient === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
this.average.scanResults = this.average.scanResults === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
this.average.subtitleScan = this.average.subtitleScan === null ? -1 : (this.average.subtitleScan /this.aardPerformanceDataBuffer.length);
}
}

View File

@ -0,0 +1,189 @@
import { AardLegacySettings, AardSettings } from '../../../../common/interfaces/SettingsInterface'
import { AardUncertainReason } from '../enums/aard-letterbox-uncertain-reason.enum'
import { LetterboxOrientation } from '../enums/letterbox-orientation.enum'
export interface AardTestResult_SubtitleRegion {
firstBlank: number,
lastBlank: number,
firstSubtitle: number,
lastSubtitle: number,
firstImage: number,
lastImage: number,
uncertain: boolean,
}
export interface AardLegacyTestResults {
isFinished: boolean,
lastStage: number,
letterboxOrientation: LetterboxOrientation,
lastValidLetterboxOrientation: LetterboxOrientation,
subtitleDetected: boolean,
blackLevel: number, // is cumulative
blackThreshold: number, // is cumulative
guardLine: {
top: number, // is cumulative
bottom: number, // is cumulative
invalidated: boolean,
cornerViolated: [boolean, boolean, boolean, boolean],
cornerPixelsViolated: [0,0,0,0],
front?: number,
back?: number,
},
imageLine: {
top: number, // is cumulative
bottom: number, // is cumulative
invalidated: boolean
}
aspectRatioCheck: {
topRows: [number, number, number],
topQuality: [number, number, number],
bottomRows: [number, number, number],
bottomQuality: [number, number, number],
topCandidate: number,
topCandidateQuality: number,
bottomCandidate: number,
bottomCandidateDistance: number,
bottomCandidateQuality: number,
topRowsDifferenceMatrix: [number, number, number],
bottomRowsDifferenceMatrix: [number, number, number],
frontCandidate: number,
backCandidate: number,
},
aspectRatioUncertain: boolean,
topRowUncertain: boolean,
bottomRowUncertain: boolean,
aspectRatioUpdated: boolean,
activeAspectRatio: number, // is cumulative
letterboxSize: number,
letterboxOffset: number,
logoDetected: [boolean, boolean, boolean, boolean],
aspectRatioInvalid: boolean,
subtitleScan: {
top: number,
bottom: number,
regions: {
top: AardTestResult_SubtitleRegion,
bottom: AardTestResult_SubtitleRegion
}
},
notLetterbox: boolean,
aspectRatioUncertainEdges: number,
aspectRatioUncertainReason?: AardUncertainReason,
aspectRatioInvalidReason?: string,
}
export function initAardTestResults(settings: AardLegacySettings): AardLegacyTestResults {
return {
isFinished: true,
lastStage: 0,
letterboxOrientation: LetterboxOrientation.NotKnown,
lastValidLetterboxOrientation: LetterboxOrientation.NotKnown,
blackLevel: settings.blackLevels.defaultBlack,
blackThreshold: 16,
guardLine: {
top: -1,
bottom: -1,
invalidated: false,
cornerViolated: [false, false, false, false],
cornerPixelsViolated: [0,0,0,0],
front: -1,
back: -1,
},
imageLine: {
top: -1,
bottom: -1,
invalidated: false,
},
aspectRatioCheck: {
topRows: [-1, -1, -1],
topQuality: [0, 0, 0],
bottomRows: [-1, -1, -1],
bottomQuality: [0, 0, 0],
topCandidate: 0,
topCandidateQuality: 0,
bottomCandidate: 0,
bottomCandidateDistance: 0,
bottomCandidateQuality: 0,
topRowsDifferenceMatrix: [0, 0, 0],
bottomRowsDifferenceMatrix: [0, 0, 0],
frontCandidate: 0,
backCandidate: 0,
},
aspectRatioUncertain: false,
aspectRatioUncertainEdges: 0,
topRowUncertain: false,
bottomRowUncertain: false,
subtitleDetected: false,
subtitleScan: {
top: -1,
bottom: -1,
regions: {
top: {
firstBlank: -1,
lastBlank: -1,
firstSubtitle: -1,
lastSubtitle: -1,
firstImage: -1,
lastImage: -1,
uncertain: false,
},
bottom: {
firstBlank: -1,
lastBlank: -1,
firstSubtitle: -1,
lastSubtitle: -1,
firstImage: -1,
lastImage: -1,
uncertain: false,
}
}
},
aspectRatioUpdated: false,
activeAspectRatio: 0,
letterboxSize: 0,
letterboxOffset: 0,
logoDetected: [false, false, false, false],
aspectRatioInvalid: false,
notLetterbox: false,
}
}
export function resetGuardLine(results: AardLegacyTestResults) {
results.guardLine.front = -1;
results.guardLine.back = -1;
}
export function resetAardTestResults(results: AardLegacyTestResults): void {
results.isFinished = false;
results.lastStage = 0;
results.aspectRatioUpdated = false;
results.aspectRatioUncertainReason = null;
results.aspectRatioInvalid = false;
results.letterboxOrientation = LetterboxOrientation.NotKnown;
}
export function resetSubtitleScanResults(results: AardLegacyTestResults): void {
results.subtitleScan.top = -1;
results.subtitleScan.bottom = -1;
results.subtitleScan.regions.top.firstBlank = -1;
results.subtitleScan.regions.top.lastBlank = -1;
results.subtitleScan.regions.top.firstSubtitle = -1;
results.subtitleScan.regions.top.lastSubtitle = -1;
results.subtitleScan.regions.top.firstImage = -1;
results.subtitleScan.regions.top.lastImage = -1;
results.subtitleScan.regions.bottom.firstBlank = -1;
results.subtitleScan.regions.bottom.lastBlank = -1;
results.subtitleScan.regions.bottom.firstSubtitle = -1;
results.subtitleScan.regions.bottom.lastSubtitle = -1;
results.subtitleScan.regions.bottom.firstImage = -1;
results.subtitleScan.regions.bottom.lastImage = -1;
}