Rearrange things a bit. No longer do fallback mode

This commit is contained in:
Tamius Han 2022-06-28 01:33:21 +02:00
parent 78f350b72b
commit 556722693c
2 changed files with 132 additions and 329 deletions

View File

@ -35,7 +35,6 @@ class ArDetector {
sampleLines sampleLines
canFallback: boolean = true; canFallback: boolean = true;
fallbackMode: boolean = false;
blackLevel: number; blackLevel: number;
@ -49,7 +48,6 @@ class ArDetector {
private manualTickEnabled: boolean; private manualTickEnabled: boolean;
_nextTick: boolean; _nextTick: boolean;
canDoFallbackMode: boolean = false;
// helper objects // helper objects
private animationFrameHandle: any; private animationFrameHandle: any;
@ -249,28 +247,7 @@ class ArDetector {
} }
// //
// [3] detect if we're in the fallback mode and reset guardline // [3] do other things setup needs to do
//
if (this.fallbackMode) {
this.logger.log('warn', 'debug', `[ArDetect::setup] <@${this.arid}> WARNING: CANVAS RESET DETECTED/we're in fallback mode - recalculating guardLine`, "background: #000; color: #ff2");
// blackbar, imagebar
this.guardLine.reset();
}
//
// [4] see if browser supports "fallback mode" by drawing a small portion of our window
//
try {
(this.blackframeContext as any).drawWindow(window,0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height, "rgba(0,0,128,1)");
this.canDoFallbackMode = true;
} catch (e) {
this.canDoFallbackMode = false;
}
//
// [5] do other things setup needs to do
// //
this.detectionTimeoutEventCount = 0; this.detectionTimeoutEventCount = 0;
@ -302,12 +279,7 @@ class ArDetector {
//#region AARD control //#region AARD control
start() { start() {
// if (this.settings.canStartAutoAr()) {
// this.logger.log('info', 'debug', `"%c[ArDetect::start] <@${this.arid}> Starting automatic aspect ratio detection`, _ard_console_start);
// } else {
// this.logger.log('warn', 'debug', `"%c[ArDetect::start] <@${this.arid}> Wanted to start automatic aspect ratio detection, but settings don't allow that. Aard won't be started.`, _ard_console_change);
// return;
// }
if (!this._ready) { if (!this._ready) {
this.init(); this.init();
return; // we will return to this function once initialization is complete return; // we will return to this function once initialization is complete
@ -318,10 +290,6 @@ class ArDetector {
this.conf.resizer.setLastAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); this.conf.resizer.setLastAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
} }
this._paused = false;
this._halted = false;
this._exited = false;
// start autodetection // start autodetection
this.startLoop(); this.startLoop();
@ -353,19 +321,12 @@ class ArDetector {
} }
pause() { pause() {
// pause only if we were running before. Don't pause if we aren't running
// (we are running when _halted is neither true nor undefined)
if (this._halted === false) {
this._paused = true;
}
this.stop(); this.stop();
} }
halt(){ halt(){
this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Halting automatic aspect ratio detection`, _ard_console_stop); this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Halting automatic aspect ratio detection`, _ard_console_stop);
this.stop(); this.stop();
this._halted = true;
// this.conf.resizer.setArLastAr();
} }
setManualTick(manualTick) { setManualTick(manualTick) {
@ -438,15 +399,13 @@ class ArDetector {
clearTimeout(this.setupTimer); clearTimeout(this.setupTimer);
} }
let ths = this; this.setupTimer = setTimeout( () => {
this.setupTimer = setTimeout(function(){ this.setupTimer = null;
ths.setupTimer = null; try {
try{ this.start();
ths.start();
} catch(e) { } catch(e) {
this.logger('error', 'debug', `[ArDetector::scheduleInitRestart] <@${this.arid}> Failed to start main(). Error:`,e); this.logger.log('error', 'debug', `[ArDetector::scheduleInitRestart] <@${this.arid}> Failed to start main(). Error:`, e);
} }
ths = null;
}, },
timeout timeout
); );
@ -569,56 +528,23 @@ class ArDetector {
let letterbox = edges.top + edges.bottom; let letterbox = edges.top + edges.bottom;
// Since video is stretched to fit the canvas, we need to take that into account when calculating target
// aspect ratio and correct our calculations to account for that
if (! this.fallbackMode) { const fileAr = this.video.videoWidth / this.video.videoHeight;
// Since video is stretched to fit the canvas, we need to take that into account when calculating target const canvasAr = this.canvas.width / this.canvas.height;
// aspect ratio and correct our calculations to account for that let widthCorrected;
const fileAr = this.video.videoWidth / this.video.videoHeight; if (edges.top && edges.bottom) {
const canvasAr = this.canvas.width / this.canvas.height; // in case of letterbox, we take canvas height as canon and assume width got stretched or squished
let widthCorrected;
if (edges.top && edges.bottom) { if (fileAr != canvasAr) {
// in case of letterbox, we take canvas height as canon and assume width got stretched or squished widthCorrected = this.canvas.height * fileAr;
} else {
if (fileAr != canvasAr) { widthCorrected = this.canvas.width;
widthCorrected = this.canvas.height * fileAr;
} else {
widthCorrected = this.canvas.width;
}
return widthCorrected / (this.canvas.height - letterbox);
}
} else {
// fallback mode behaves a wee bit differently
let zoomFactor = 1;
// there's stuff missing from the canvas. We need to assume canvas' actual height is bigger by a factor x, where
// x = [video.zoomedHeight] / [video.unzoomedHeight]
//
// letterbox also needs to be corrected:
// letterbox += [video.zoomedHeight] - [video.unzoomedHeight]
let vbr = this.video.getBoundingClientRect();
zoomFactor = vbr.height / this.video.clientHeight;
letterbox += vbr.height - this.video.clientHeight;
let trueHeight = this.canvas.height * zoomFactor - letterbox;
if(edges.top > 1 && edges.top <= this.settings.active.arDetect.fallbackMode.noTriggerZonePx ){
this.logger.log('info', 'arDetect', `%c[ArDetect::calculateArFromEdges] <@${this.arid}> Edge is in the no-trigger zone. Aspect ratio change is not triggered.`)
return;
} }
// varnostno območje, ki naj ostane črno (da lahko v fallback načinu odkrijemo ožanje razmerja stranic). return widthCorrected / (this.canvas.height - letterbox);
// x2, ker je safetyBorderPx definiran za eno stran.
// safety border so we can detect aspect ratio narrowing (21:9 -> 16:9).
// x2 because safetyBorderPx is for one side.
trueHeight += (this.settings.active.arDetect.fallbackMode.safetyBorderPx << 1);
return this.canvas.width * zoomFactor / trueHeight;
} }
} }
@ -667,10 +593,12 @@ class ArDetector {
id = undefined; id = undefined;
} }
async frameCheck(){ async frameCheck(){
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::processAr] <@${this.arid}> Starting frame check.`); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::processAr] <@${this.arid}> Starting frame check.`);
if(! this.video){ if (!this.video) {
this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`); this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`);
this.conf.destroy(); this.conf.destroy();
return; return;
@ -687,101 +615,24 @@ class ArDetector {
// //
// [0] blackframe tests (they also determine whether we need fallback mode) // [0] blackframe tests (they also determine whether we need fallback mode)
// //
try { const bfAnalysis = await this.blackframeTest();
startTime = performance.now(); if (bfAnalysis.isBlack) {
// do it in ghetto async. This way, other javascript function should be able to
// get a chance to do something _before_ we process our data
await new Promise<void>(
resolve => {
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
resolve();
}
);
partialDrawImageTime += performance.now() - startTime;
this.fallbackMode = false;
// If we detected DRM and if we're here, this means we're also using Google Chrome.
// And if we're here while DRM is detected, we know we'll be looking at black frames.
// We won't be able to do anything useful, therefore we're just gonna call it quits.
if (this.conf.hasDrm) {
this.logger.log('info', 'debug', 'we have drm, doing nothing.', this.conf.hasDrm);
return;
}
} catch (e) {
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] <@${this.arid}> %c[ArDetect::frameCheck] can't draw image on canvas. ${this.canDoFallbackMode ? 'Trying canvas.drawWindow instead' : 'Doing nothing as browser doesn\'t support fallback mode.'}`, "color:#000; backgroud:#f51;", e);
if (! this.canvasReadyForDrawWindow()) {
// this means canvas needs to be resized, so we'll just re-run setup with all those new parameters
this.halt();
let newCanvasWidth = window.innerHeight * (this.video.videoWidth / this.video.videoHeight);
let newCanvasHeight = window.innerHeight;
if (this.conf.resizer.videoAlignment.x === VideoAlignmentType.Center) {
this.canvasDrawWindowHOffset = Math.round((window.innerWidth - newCanvasWidth) * 0.5);
} else if (this.conf.resizer.videoAlignment.x === VideoAlignmentType.Left) {
this.canvasDrawWindowHOffset = 0;
} else {
this.canvasDrawWindowHOffset = window.innerWidth - newCanvasWidth;
}
this.setup(newCanvasWidth, newCanvasHeight);
return;
}
// if this is the case, we'll first draw on canvas, as we'll need intermediate canvas if we want to get a
// smaller sample for blackframe check
this.fallbackMode = true;
startTime = performance.now();
try {
(this.context as any).drawWindow(window, this.canvasDrawWindowHOffset, 0, this.canvas.width, this.canvas.height);
} catch (e) {
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] can't draw image on canvas with fallback mode either. This error is prolly only temporary.`, "color:#000; backgroud:#f51;", e);
return; // it's prolly just a fluke, so we do nothing special here
}
// draw blackframe sample from our main sample:
await new Promise<void>(
resolve => {
this.blackframeContext.drawImage(this.canvas, this.blackframeCanvas.width, this.blackframeCanvas.height);
resolve();
}
);
partialDrawImageTime += performance.now() - startTime;
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] canvas.drawImage seems to have worked`, "color:#000; backgroud:#2f5;");
}
const bfanalysis = this.blackframeTest();
if (bfanalysis.isBlack) {
// we don't do any corrections on frames confirmed black // we don't do any corrections on frames confirmed black
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Black frame analysis suggests this frame is black or too dark. Doing nothing.`, "color: #fa3", bfanalysis); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Black frame analysis suggests this frame is black or too dark. Doing nothing.`, "color: #fa3", bfAnalysis);
return; return;
} }
// if we are in fallback mode, then frame has already been drawn to the main canvas.
// if we are in normal mode though, the frame has yet to be drawn
if (!this.fallbackMode) {
startTime = performance.now();
await new Promise<void>(
resolve => {
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
resolve();
}
)
partialDrawImageTime += performance.now() - startTime;
}
this.addPerformanceTimeMeasure(this.performance.drawImage, partialDrawImageTime);
startTime = performance.now(); startTime = performance.now();
await new Promise<void>(
resolve => {
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
resolve();
}
)
const imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height).data; const imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height).data;
this.addPerformanceTimeMeasure(this.performance.getImageData, performance.now() - startTime); const imageDrawTime = performance.now() - startTime;
if (! this.fastLetterboxPresenceTest(imageData, sampleCols) ) { if (! this.fastLetterboxPresenceTest(imageData, sampleCols) ) {
// Če ne zaznamo letterboxa, kličemo reset. Lahko, da je bilo razmerje stranic popravljeno na roke. Možno je tudi,
// da je letterbox izginil.
// If we don't detect letterbox, we reset aspect ratio to aspect ratio of the video file. The aspect ratio could // If we don't detect letterbox, we reset aspect ratio to aspect ratio of the video file. The aspect ratio could
// have been corrected manually. It's also possible that letterbox (that was there before) disappeared. // have been corrected manually. It's also possible that letterbox (that was there before) disappeared.
this.conf.resizer.updateAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); this.conf.resizer.updateAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
@ -794,18 +645,13 @@ class ArDetector {
return; return;
} }
// Če preverjamo naprej, potem moramo postaviti to vrednost nazaj na 'false'. V nasprotnem primeru se bo
// css resetiral enkrat na video/pageload namesto vsakič, ko so za nekaj časa obrobe odstranejene
// if we look further we need to reset this value back to false. Otherwise we'll only get CSS reset once // if we look further we need to reset this value back to false. Otherwise we'll only get CSS reset once
// per video/pageload instead of every time letterbox goes away (this can happen more than once per vid) // per video/pageload instead of every time letterbox goes away (this can happen more than once per vid)
this.noLetterboxCanvasReset = false; this.noLetterboxCanvasReset = false;
// poglejmo, če obrežemo preveč.
// let's check if we're cropping too much // let's check if we're cropping too much
const guardLineOut = this.guardLine.check(imageData, this.fallbackMode); const guardLineOut = this.guardLine.check(imageData);
// če ni padla nobena izmed funkcij, potem se razmerje stranic ni spremenilo
// if both succeed, then aspect ratio hasn't changed. // if both succeed, then aspect ratio hasn't changed.
if (!guardLineOut.imageFail && !guardLineOut.blackbarFail) { if (!guardLineOut.imageFail && !guardLineOut.blackbarFail) {
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] guardLine tests were successful. (no imagefail and no blackbarfail)\n`, "color: #afa", guardLineOut); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] guardLine tests were successful. (no imagefail and no blackbarfail)\n`, "color: #afa", guardLineOut);
@ -813,30 +659,13 @@ class ArDetector {
return; return;
} }
// drugače nadaljujemo, našemu vzorcu stolpcev pa dodamo tiste stolpce, ki so // otherwise we continue. We add blackbar violations to the list of the cols we'll sample and sort them
// kršili blackbar (če obstajajo) ter jih razvrstimo
// otherwise we continue. We add blackbar violations to the list of the cols
// we'll sample and sort them
if (guardLineOut.blackbarFail) { if (guardLineOut.blackbarFail) {
sampleCols.concat(guardLineOut.offenders).sort( sampleCols.concat(guardLineOut.offenders).sort(
(a: number, b: number) => a - b (a: number, b: number) => a - b
); );
} }
// if we're in fallback mode and blackbar test failed, we restore CSS and quit
// (since the new letterbox edge isn't present in our sample due to technical
// limitations)
if (this.fallbackMode && guardLineOut.blackbarFail) {
this.logger.log('warn', 'arDetect_verbose', `%c[ArDetect::frameCheck] <@${this.arid}> We are in fallback mode and blackbar failed. Reverting to initial aspect ratio.`);
this.conf.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
this.guardLine.reset();
this.noLetterboxCanvasReset = true;
this.clearImageData(imageData);
return;
}
// If aspect ratio changes from narrower to wider, we first check for presence of pillarbox. Presence of pillarbox indicates // If aspect ratio changes from narrower to wider, we first check for presence of pillarbox. Presence of pillarbox indicates
// a chance of a logo on black background. We could cut easily cut too much. Because there's a somewhat significant chance // a chance of a logo on black background. We could cut easily cut too much. Because there's a somewhat significant chance
// that we will cut too much, we rather avoid doing anything at all. There's gonna be a next chance. // that we will cut too much, we rather avoid doing anything at all. There's gonna be a next chance.
@ -859,13 +688,12 @@ class ArDetector {
this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] something went wrong while checking for pillarbox. Error:\n`, e); this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] something went wrong while checking for pillarbox. Error:\n`, e);
} }
// pa poglejmo, kje se končajo črne letvice na vrhu in na dnu videa.
// let's see where black bars end. // let's see where black bars end.
this.sampleCols_current = sampleCols.length; this.sampleCols_current = sampleCols.length;
// blackSamples -> {res_top, res_bottom} // blackSamples -> {res_top, res_bottom}
let edgePost = this.edgeDetector.findBars(imageData, sampleCols, EdgeDetectPrimaryDirection.Vertical, EdgeDetectQuality.Improved, guardLineOut, bfanalysis); let edgePost = this.edgeDetector.findBars(imageData, sampleCols, EdgeDetectPrimaryDirection.Vertical, EdgeDetectQuality.Improved, guardLineOut, bfAnalysis);
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] edgeDetector returned this\n`, "color: #aaf", edgePost); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] edgeDetector returned this\n`, "color: #aaf", edgePost);
@ -882,26 +710,12 @@ class ArDetector {
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Triggering aspect ration change! new ar: ${newAr}`, "color: #aaf"); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Triggering aspect ration change! new ar: ${newAr}`, "color: #aaf");
// we also know edges for guardline, so set them. // we also know edges for guardline, so set them. If edges are okay and not invalid, we also
// we need to be mindful of fallbackMode though
// if edges are okay and not invalid, we also
// allow automatic aspect ratio correction. If edges // allow automatic aspect ratio correction. If edges
// are bogus, we remain aspect ratio unchanged. // are bogus, we keep aspect ratio unchanged.
try { try {
if (!this.fallbackMode) { // throws error if top/bottom are invalid
// throws error if top/bottom are invalid this.guardLine.setBlackbar({top: edgePost.guardLineTop, bottom: edgePost.guardLineBottom});
this.guardLine.setBlackbar({top: edgePost.guardLineTop, bottom: edgePost.guardLineBottom});
} else {
if (this.conf.player.dimensions){
this.guardLine.setBlackbarManual({
top: this.settings.active.arDetect.fallbackMode.noTriggerZonePx,
bottom: this.conf.player.dimensions.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1
},{
top: edgePost.guardLineTop + this.settings.active.arDetect.guardLine.edgeTolerancePx,
bottom: edgePost.guardLineBottom - this.settings.active.arDetect.guardLine.edgeTolerancePx
})
}
}
this.processAr(newAr); this.processAr(newAr);
} catch (e) { } catch (e) {
@ -931,14 +745,30 @@ class ArDetector {
} }
blackframeTest() { async blackframeTest() {
if (this.blackLevel === undefined) { if (this.blackLevel === undefined) {
this.logger.log('info', 'arDetect_verbose', "[ArDetect::blackframeTest] black level undefined, resetting"); this.logger.log('info', 'arDetect_verbose', "[ArDetect::blackframeTest] black level undefined, resetting");
this.resetBlackLevel(); this.resetBlackLevel();
} }
/**
* Performs a quick black frame test
*/
const bfDrawStartTime = performance.now();
await new Promise<void>(
resolve => {
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
resolve();
}
);
const rows = this.blackframeCanvas.height; const rows = this.blackframeCanvas.height;
const cols = this.blackframeCanvas.width; const cols = this.blackframeCanvas.width;
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data;
const blackFrameDraw = performance.now() - bfDrawStartTime;
const bfProcessStartTime = performance.now();
const pixels = rows * cols; const pixels = rows * cols;
let cumulative_r = 0, cumulative_g = 0, cumulative_b = 0; let cumulative_r = 0, cumulative_g = 0, cumulative_b = 0;
let max_r = 0, max_g = 0, max_b = 0; let max_r = 0, max_g = 0, max_b = 0;
@ -948,8 +778,7 @@ class ArDetector {
let pixelMax = 0; let pixelMax = 0;
let cumulativeValue = 0; let cumulativeValue = 0;
let blackPixelCount = 0; let blackPixelCount = 0;
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data; const blackThreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameThreshold;
const blackTreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameThreshold;
// we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists // we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists
@ -964,7 +793,7 @@ class ArDetector {
pixelMax = Math.max(bfImageData[i], bfImageData[i+1], bfImageData[i+2]); pixelMax = Math.max(bfImageData[i], bfImageData[i+1], bfImageData[i+2]);
bfImageData[i+3] = pixelMax; bfImageData[i+3] = pixelMax;
if (pixelMax < blackTreshold) { if (pixelMax < blackThreshold) {
if (pixelMax < this.blackLevel) { if (pixelMax < this.blackLevel) {
this.blackLevel = pixelMax; this.blackLevel = pixelMax;
} }
@ -1053,6 +882,10 @@ class ArDetector {
colMax: colMax, colMax: colMax,
}; };
} }
const blackFrameProcessTime = performance.now() - bfProcessStartTime;
// TODO: update processing time statistics
return { return {
isBlack: isBlack, isBlack: isBlack,
rowMax: rowMax, rowMax: rowMax,

View File

@ -26,7 +26,7 @@ class GuardLine {
constructor (ardConf){ constructor (ardConf){
this.blackbar = {top: undefined, bottom: undefined}; this.blackbar = {top: undefined, bottom: undefined};
this.imageBar = {top: undefined, bottom: undefined}; this.imageBar = {top: undefined, bottom: undefined};
this.aard = ardConf; this.aard = ardConf;
this.settings = ardConf.settings; this.settings = ardConf.settings;
} }
@ -36,17 +36,6 @@ class GuardLine {
this.imageBar = {top: undefined, bottom: undefined}; this.imageBar = {top: undefined, bottom: undefined};
} }
setBlackbarManual(blackbarConf, imagebarConf){
// ni lepo uporabljat tega, ampak pri fallback mode nastavljamo blackbar stuff na roke
// it's not nice to use this, but we're setting these values manually in fallbackMode
if (blackbarConf) {
this.blackbar = blackbarConf;
}
if (imagebarConf) {
this.imageBar = imagebarConf;
}
}
setBlackbar(bbconf){ setBlackbar(bbconf){
let bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx; let bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx;
let bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx; let bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx;
@ -68,7 +57,7 @@ class GuardLine {
} }
} }
check(image, fallbackMode){ check(image){
// izračunaj enkrat in shrani na objekt // izračunaj enkrat in shrani na objekt
// calculate once and save object-instance-wide // calculate once and save object-instance-wide
this.blackbarThreshold = this.aard.blackLevel + this.settings.active.arDetect.blackbar.threshold; this.blackbarThreshold = this.aard.blackLevel + this.settings.active.arDetect.blackbar.threshold;
@ -76,7 +65,7 @@ class GuardLine {
// dejansko testiranje // dejansko testiranje
// actual checks // actual checks
let guardLineResult = this.guardLineCheck(image, fallbackMode); let guardLineResult = this.guardLineCheck(image);
// Zaznali smo kršitev črnega dela, zato nam ni treba preveriti, ali je slika // Zaznali smo kršitev črnega dela, zato nam ni treba preveriti, ali je slika
// prisotna. Vemo namreč, da se je razmerje stranic zmanjšalo. // prisotna. Vemo namreč, da se je razmerje stranic zmanjšalo.
@ -91,7 +80,7 @@ class GuardLine {
} }
} }
let imageCheckResult = this.imageCheck(image, fallbackMode); let imageCheckResult = this.imageCheck(image);
return { return {
blackbarFail: false, blackbarFail: false,
@ -99,142 +88,123 @@ class GuardLine {
} }
} }
// don't use methods below this line outside this class // don't use methods below this line outside this class
guardLineCheck(image, fallbackMode){ guardLineCheck(image){
// this test tests for whether we crop too aggressively // this test tests for whether we crop too aggressively
// if this test is passed, then aspect ratio probably didn't change from wider to narrower. However, further // if this test is passed, then aspect ratio probably didn't change from wider to narrower. However, further
// checks are needed to determine whether aspect ratio got wider. // checks are needed to determine whether aspect ratio got wider.
// if this test fails, it returns a list of offending points. // if this test fails, it returns a list of offending points.
// if the upper edge is null, then edge hasn't been detected before. This test is pointless, therefore it // if the upper edge is null, then edge hasn't been detected before. This test is pointless, therefore it
// should succeed by default. Also need to check bottom, for cases where only one edge is known // should succeed by default. Also need to check bottom, for cases where only one edge is known
if(! fallbackMode && (! this.blackbar.top || ! this.blackbar.bottom)) { if (!this.blackbar.top || !this.blackbar.bottom) {
return { success: true }; return { success: true };
} }
let offset = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2; let offset = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
let offenders = []; let offenders = [];
let offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index. let offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index.
// TODO: implement logo check. // TODO: implement logo check.
// preglejmo obe vrstici // preglejmo obe vrstici
// check both rows // check both rows
let edge_lower, edge_upper; let edge_lower, edge_upper;
if(! fallbackMode){ edge_upper = this.blackbar.top;
edge_upper = this.blackbar.top; edge_lower = this.blackbar.bottom;
edge_lower = this.blackbar.bottom;
}
else {
// fallback mode is a bit different
edge_upper = 0;
edge_lower = this.aard.canvas.height - 1;
}
let rowStart, rowEnd; let rowStart, rowEnd;
// <<<=======| checking upper row |========>>> // <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset; rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2); rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) { if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
// offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount); // offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} else { } else {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount); offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} }
// <<<=======| checking lower row |========>>> // <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset; rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2); rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) { if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
// offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount); // offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} else { } else {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount); offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} }
// če nismo našli nobenih prekrškarjev, vrnemo uspeh. Drugače vrnemo seznam prekrškarjev // če nismo našli nobenih prekrškarjev, vrnemo uspeh. Drugače vrnemo seznam prekrškarjev
// vrnemo tabelo, ki vsebuje sredinsko točko vsakega prekrškarja (x + width*0.5) // vrnemo tabelo, ki vsebuje sredinsko točko vsakega prekrškarja (x + width*0.5)
// //
// if we haven't found any offenders, we return success. Else we return list of offenders // if we haven't found any offenders, we return success. Else we return list of offenders
// we return array of middle points of offenders (x + (width * 0.5) for every offender) // we return array of middle points of offenders (x + (width * 0.5) for every offender)
if(offenderCount == -1){ if(offenderCount == -1){
return {success: true}; return {success: true};
} }
let ret = new Array(offenders.length); let ret = new Array(offenders.length);
for(let o in offenders){ for(let o in offenders){
ret[o] = offenders[o].x + (offenders[o].width * 0.25); ret[o] = offenders[o].x + (offenders[o].width * 0.25);
} }
return {success: false, offenders: ret}; return {success: false, offenders: ret};
} }
imageCheck(image, fallbackMode?: boolean): ImageCheckResult { imageCheck(image): ImageCheckResult {
if(!this.imageBar.top || !this.imageBar.bottom) if(!this.imageBar.top || !this.imageBar.bottom)
return { success: false }; return { success: false };
let offset = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2; let offset = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
// TODO: implement logo check. // TODO: implement logo check.
let edge_upper = this.imageBar.top;
// preglejmo obe vrstici - tukaj po pravilih ne bi smeli iti prek mej platna. ne rabimo preverjati let edge_lower = this.imageBar.bottom;
// check both rows - by the rules and definitions, we shouldn't go out of bounds here. no need to check, then
// if(fallbackMode){
// let edge_upper = this.settings.active.arDetect.fallbackMode.noTriggerZonePx;
// let edge_lower = this.conf.canvas.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1;
// }
// else{
let edge_upper = this.imageBar.top;
let edge_lower = this.imageBar.bottom;
// }
// koliko pikslov rabimo zaznati, da je ta funkcija uspe. Tu dovoljujemo tudi, da so vsi piksli na enem
// robu (eden izmed robov je lahko v celoti črn)
// how many non-black pixels we need to consider this check a success. We only need to detect enough pixels // how many non-black pixels we need to consider this check a success. We only need to detect enough pixels
// on one edge (one of the edges can be black as long as both aren't) // on one edge (one of the edges can be black as long as both aren't)
let successThreshold = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold); let successThreshold = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
let rowStart, rowEnd; let rowStart, rowEnd;
// <<<=======| checking upper row |========>>> // <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset; rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2); rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
let res = false; let res = false;
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
if (res) {
return {success: true};
}
// <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset;
// rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){ if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold); // res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else { } else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold); res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
} }
if (res) {
return {success: true};
}
// <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset;
// rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
return {success: res}; return {success: res};
} }
@ -245,8 +215,8 @@ class GuardLine {
_gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){ _gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){
let firstOffender = -1; let firstOffender = -1;
for(let i = rowStart; i < rowEnd; i+=4){ for(let i = rowStart; i < rowEnd; i+=4){
// we track sections that go over what's supposed to be a black line, so we can suggest more // we track sections that go over what's supposed to be a black line, so we can suggest more
// columns to sample // columns to sample
if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){ if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
if(firstOffender < 0){ if(firstOffender < 0){
@ -259,22 +229,22 @@ class GuardLine {
} }
} }
else{ else{
// is that a black pixel again? Let's reset the 'first offender' // is that a black pixel again? Let's reset the 'first offender'
firstOffender = -1; firstOffender = -1;
} }
} }
return offenderCount; return offenderCount;
} }
// _gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){ // _gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){
// let firstOffender = -1; // let firstOffender = -1;
// for(let i = rowStart; i < rowEnd; i+=4){ // for(let i = rowStart; i < rowEnd; i+=4){
// // we track sections that go over what's supposed to be a black line, so we can suggest more // // we track sections that go over what's supposed to be a black line, so we can suggest more
// // columns to sample // // columns to sample
// if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){ // if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION); // this.aard.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION);
// if(firstOffender < 0){ // if(firstOffender < 0){
// firstOffender = (i - rowStart) >> 2; // firstOffender = (i - rowStart) >> 2;
// offenderCount++; // offenderCount++;
@ -285,13 +255,13 @@ class GuardLine {
// } // }
// } // }
// else{ // else{
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR); // this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR);
// // is that a black pixel again? Let's reset the 'first offender' // // is that a black pixel again? Let's reset the 'first offender'
// firstOffender = -1; // firstOffender = -1;
// } // }
// } // }
// return offenderCount; // return offenderCount;
// } // }
@ -301,9 +271,9 @@ class GuardLine {
if(successThreshold --<= 0){ if(successThreshold --<= 0){
return true; return true;
} }
} }
} }
return false; return false;
} }
@ -316,9 +286,9 @@ class GuardLine {
// } // }
// } else { // } else {
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.WARN); // this.aard.debugCanvas.trace(i, DebugCanvasClasses.WARN);
// } // }
// } // }
// return false; // return false;
// } // }
} }