diff --git a/src/ext/lib/ar-detect/ArDetector.ts b/src/ext/lib/ar-detect/ArDetector.ts deleted file mode 100644 index 6a34bf3..0000000 --- a/src/ext/lib/ar-detect/ArDetector.ts +++ /dev/null @@ -1,1141 +0,0 @@ - -import Debug from '../../conf/Debug'; -import EdgeDetect from './edge-detect/EdgeDetect'; -import EdgeStatus from './edge-detect/enums/EdgeStatusEnum'; -import EdgeDetectPrimaryDirection from './edge-detect/enums/EdgeDetectPrimaryDirectionEnum'; -import EdgeDetectQuality from './edge-detect/enums/EdgeDetectQualityEnum'; -import GuardLine from './GuardLine'; -// import DebugCanvas from './DebugCanvas'; -import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum'; -import AspectRatioType from '../../../common/enums/AspectRatioType.enum'; -import {sleep} from '../Util'; -import BrowserDetect from '../../conf/BrowserDetect'; -import Logger from '../Logger'; -import VideoData from '../video-data/VideoData'; -import Settings from '../Settings'; -import EventBus from '../EventBus'; -import { GlCanvas } from '../aard/gl/GlCanvas'; - -enum VideoPlaybackState { - Playing, - Paused, - Ended, - Error -} - -export interface AardPerformanceMeasurement { - sampleCount: number, - averageTime: number, - worstTime: number, - stDev: number, -} - -export interface AardPerformanceData { - total: AardPerformanceMeasurement, - theoretical: AardPerformanceMeasurement, - imageDraw: AardPerformanceMeasurement - blackFrameDraw: AardPerformanceMeasurement, - blackFrame: AardPerformanceMeasurement, - fastLetterbox: AardPerformanceMeasurement, - edgeDetect: AardPerformanceMeasurement, - - imageDrawCount: number, - blackFrameDrawCount: number, - blackFrameCount: number, - fastLetterboxCount: number, - edgeDetectCount: number, - - aardActive: boolean, // whether autodetection is currently running or not -} - -class ArDetector { - - //#region helper objects - logger: Logger; - conf: VideoData; - video: HTMLVideoElement; - settings: Settings; - eventBus: EventBus; - - guardLine: GuardLine; - edgeDetector: EdgeDetect; - //#endregion - - private eventBusCommands = { - 'get-aard-timing': [{ - function: () => this.handlePerformanceDataRequest() - }] - } - - setupTimer: any; - sampleCols: any[]; - sampleLines - - canFallback: boolean = true; - - blackLevel: number; - - arid: string; - - - - private manualTickEnabled: boolean; - _nextTick: boolean; - - private animationFrameHandle: any; - canvasImageDataRowLength: number; - - glCanvas: GlCanvas; - - private timers = { - nextFrameCheckTime: Date.now() - } - private status = { - lastVideoStatus: VideoPlaybackState.Playing, - aardActive: false, - } - - //#region debug variables - private performance = { - samples: [], - currentIndex: 0, - maxSamples: 64, - - lastMeasurements: { - fastLetterbox: { - samples: [], - currentIndex: 0, - }, - edgeDetect: { - samples: [], - currentIndex: 0 - } - } - }; - - //#endregion - - //#region getters - get defaultAr() { - const ratio = this.video.videoWidth / this.video.videoHeight; - if (isNaN(ratio)) { - return undefined; - } - return ratio; - } - //#endregion getters - - //#region debug getters - - //#endregion - - //#region lifecycle - constructor(videoData){ - this.logger = videoData.logger; - this.conf = videoData; - this.video = videoData.video; - this.settings = videoData.settings; - this.eventBus = videoData.eventBus; - - this.initEventBus(); - - this.sampleCols = []; - - this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel; - - this.arid = (Math.random()*100).toFixed(); - - // we can tick manually, for debugging - this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`); - } - - private initEventBus() { - for (const action in this.eventBusCommands) { - for (const command of this.eventBusCommands[action]) { - this.eventBus.subscribe(action, command); - } - } - } - - init(){ - this.logger.log('info', 'init', `[ArDetect::init] <@${this.arid}> Initializing autodetection.`); - this.setup(); - } - - setup(cwidth?: number, cheight?: number){ - this.logger.log('info', 'init', `[ArDetect::setup] <@${this.arid}> Starting autodetection setup.`); - // - // [-1] check for zero-width and zero-height videos. If we detect this, we kick the proverbial - // can some distance down the road. This problem will prolly fix itself soon. We'll also - // not do any other setup until this issue is fixed - // - if (this.video.videoWidth === 0 || this.video.videoHeight === 0 ){ - this.logger.log('warn', 'debug', `[ArDetect::setup] <@${this.arid}> This video has zero width or zero height. Dimensions: ${this.video.videoWidth} × ${this.video.videoHeight}`); - - this.scheduleInitRestart(); - return; - } - - // - // [0] initiate "dependencies" first - // - - this.guardLine = new GuardLine(this); - this.edgeDetector = new EdgeDetect(this); - // this.debugCanvas = new DebugCanvas(this); - - - // - // [1] initiate canvases - // - if (this.glCanvas) { - this.glCanvas.destroy(); - } - this.glCanvas = new GlCanvas(this.settings.active.arDetect.canvasDimensions.sampleCanvas); - - // - // [2] determine places we'll use to sample our main frame - // - let ncol = this.settings.active.arDetect.sampling.staticCols; - let nrow = this.settings.active.arDetect.sampling.staticRows; - - let colSpacing = this.glCanvas.width / ncol; - let rowSpacing = (this.glCanvas.height << 2) / nrow; - - this.sampleLines = []; - this.sampleCols = []; - - for (let i = 0; i < ncol; i++){ - if(i < ncol - 1) - this.sampleCols.push(Math.round(colSpacing * i)); - else{ - this.sampleCols.push(Math.round(colSpacing * i) - 1); - } - } - - for (let i = 0; i < nrow; i++){ - if(i < ncol - 5) - this.sampleLines.push(Math.round(rowSpacing * i)); - else{ - this.sampleLines.push(Math.round(rowSpacing * i) - 4); - } - } - - // - // [3] do other things setup needs to do - // - this.resetBlackLevel(); - - // if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio - this.conf.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}; - - this.canvasImageDataRowLength = cwidth << 2; - - this.start(); - // if(Debug.debugCanvas.enabled){ - // this.debugCanvas.init({width: cwidth, height: cheight}); - // DebugCanvas.draw("test marker","test","rect", {x:5, y:5}, {width: 5, height: 5}); - // } - - this.conf.arSetupComplete = true; - } - - destroy(){ - this.logger.log('info', 'init', `%c[ArDetect::destroy] <@${this.arid}> Destroying aard.`, _ard_console_stop); - // this.debugCanvas.destroy(); - this.stop(); - } - //#endregion lifecycle - - //#region AARD control - start() { - if (this.conf.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) { - // ensure first autodetection will run in any case - this.conf.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}; - } - - // start autodetection - this.startLoop(); - } - - startLoop() { - if (this.animationFrameHandle) { - window.cancelAnimationFrame(this.animationFrameHandle); - } - - this.status.aardActive = true; - this.animationFrameHandle = window.requestAnimationFrame( (ts) => this.animationFrameBootstrap(ts)); - this.logger.log('info', 'debug', `"%c[ArDetect::startLoop] <@${this.arid}> AARD loop started.`, _ard_console_start); - } - - stop() { - this.status.aardActive = false; - - if (this.animationFrameHandle) { - this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Stopping AnimationFrame loop.`, _ard_console_stop); - window.cancelAnimationFrame(this.animationFrameHandle); - } else { - this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> AnimationFrame loop is already paused (due to an earlier call of this function).`); - } - } - - unpause() { - this.startLoop(); - } - - pause() { - this.stop(); - } - - setManualTick(manualTick) { - this.manualTickEnabled = manualTick; - } - - tick() { - this._nextTick = true; - } - //#endregion - - //#region helper functions (general) - - isRunning(){ - return this.status.aardActive; - } - - private getVideoPlaybackState(): VideoPlaybackState { - - try { - if (this.video.ended) { - return VideoPlaybackState.Ended; - } else if (this.video.paused) { - return VideoPlaybackState.Paused; - } else if (this.video.error) { - return VideoPlaybackState.Error; - } else { - return VideoPlaybackState.Playing; - } - } catch (e) { - this.logger.log('warn', 'debug', `[ArDetect::getVideoPlaybackState] There was an error while determining video playback state.`, e); - return VideoPlaybackState.Error; - } - } - - /** - * Checks whether conditions for granting a frame check are fulfilled - * @returns - */ - private canTriggerFrameCheck() { - // if (this._paused || this._halted || this._exited) { - // return false; - // } - - // if video was paused & we know that we already checked that frame, - // we will not check it again. - const videoState = this.getVideoPlaybackState(); - - if (videoState !== VideoPlaybackState.Playing) { - if (this.status.lastVideoStatus === videoState) { - return false; - } - } - this.status.lastVideoStatus = videoState; - - if (Date.now() < this.timers.nextFrameCheckTime) { - return false; - } - - this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing; - return true; - } - - private scheduleInitRestart(timeout?: number, force_reset?: boolean){ - if(! timeout){ - timeout = 100; - } - // don't allow more than 1 instance - if(this.setupTimer){ - clearTimeout(this.setupTimer); - } - - this.setupTimer = setTimeout( () => { - this.setupTimer = null; - try { - this.start(); - } catch(e) { - this.logger.log('error', 'debug', `[ArDetector::scheduleInitRestart] <@${this.arid}> Failed to start main(). Error:`, e); - } - }, - timeout - ); - } - - /** - * Adds execution time sample for performance metrics - * @param performanceObject - * @param executionTime - */ - private addPerformanceMeasurement(performanceObject) { - this.performance.samples[this.performance.currentIndex] = performanceObject; - this.performance.currentIndex = (this.performance.currentIndex + 1) % this.performance.maxSamples; - - if (performanceObject.fastLetterboxTime !== null) { - const lastMeasurements = this.performance.lastMeasurements.fastLetterbox; - lastMeasurements.samples[lastMeasurements.currentIndex] = performanceObject.fastLetterboxTime; - lastMeasurements.currentIndex = (lastMeasurements.currentIndex + 1) % this.performance.maxSamples; - } - if (performanceObject.edgeDetectTime !== null) { - const lastMeasurements = this.performance.lastMeasurements.edgeDetect; - lastMeasurements.samples[lastMeasurements.currentIndex] = performanceObject.edgeDetectTime; - lastMeasurements.currentIndex = (lastMeasurements.currentIndex + 1) & this.performance.maxSamples; - } - } - - //#endregion - - //#region helper functions (performance measurements) - - /** - * Returns time ultrawidify spends on certain aspects of autodetection. - * - * The returned object contains the following: - * - * eyeballedTimeBudget — a very inaccurate time budget - * fps — framerate at which we run - * aardTime — time spent on average frameCheck loop. - * It's a nearly useless metric, because - * frameCheck can exit very early. - * drawWindowTime — how much time browser spends on executing - * drawWindow() calls. - * getImageData — how much time browser spends on executing - * getImageData() calls. - * - * Most of these are on "per frame" basis and averaged. - */ - handlePerformanceDataRequest() { - let imageDrawCount = 0; - let blackFrameDrawCount = 0; - let blackFrameProcessCount = 0; - let fastLetterboxCount = 0; - let edgeDetectCount = 0; - - let fastLetterboxExecCount = 0; - let edgeDetectExecCount = 0; - - let imageDrawAverage = 0; - let blackFrameDrawAverage = 0; - let blackFrameProcessAverage = 0; - let fastLetterboxAverage = 0; - let edgeDetectAverage = 0; - - let imageDrawWorst = 0; - let blackFrameDrawWorst = 0; - let blackFrameProcessWorst = 0; - let fastLetterboxWorst = 0; - let edgeDetectWorst = 0; - - let imageDrawStDev = 0; - let blackFrameDrawStDev = 0; - let blackFrameProcessStDev = 0; - let fastLetterboxStDev = 0; - let edgeDetectStDev = 0; - - let totalAverage = 0; - let totalWorst = 0; - let totalStDev = 0; - - let theoreticalAverage = 0; - let theoreticalWorst = 0; - let theoreticalStDev = 0; - - for (const sample of this.performance.samples) { - if (sample.imageDrawTime) { - imageDrawCount++; - imageDrawAverage += sample.imageDrawTime; - if (sample.imageDrawTime > imageDrawWorst) { - imageDrawWorst = sample.imageDrawTime; - } - } - if (sample.blackFrameDrawTime) { - blackFrameDrawCount++; - blackFrameDrawAverage += sample.blackFrameDrawTime; - if (sample.blackFrameDrawTime > blackFrameDrawWorst) { - blackFrameDrawWorst = sample.blackFrameDrawTime; - } - } - if (sample.blackFrameProcessTime) { - blackFrameProcessCount++; - blackFrameProcessAverage += sample.blackFrameProcessTime; - if (sample.blackFrameProcessTime > blackFrameProcessWorst) { - blackFrameProcessWorst = sample.blackFrameProcessTime; - } - } - if (sample.fastLetterboxTime) { - fastLetterboxExecCount++; - } - if (sample.edgeDetectTime) { - edgeDetectExecCount++; - } - - const execTime = - sample.imageDrawTime ?? 0 - + sample.blackFrameDrawTime ?? 0 - + sample.blackFrameProcessTime ?? 0 - + sample.fastLetterboxTime ?? 0 - + sample.edgeDetectTime ?? 0; - - totalAverage += execTime; - if (execTime > totalWorst) { - totalWorst = execTime; - } - - const partialExecTime = - sample.imageDrawTime ?? 0 - + sample.blackFrameDrawTime ?? 0 - + sample.blackFrameProcessTime ?? 0; - - if (partialExecTime > theoreticalWorst) { - theoreticalWorst = partialExecTime; - } - } - - if (imageDrawCount) { - imageDrawAverage /= imageDrawCount; - } else { - imageDrawAverage = 0; - } - if (blackFrameDrawCount) { - blackFrameDrawAverage /= blackFrameDrawCount; - } else { - blackFrameDrawAverage = 0; - } - if (blackFrameProcessCount) { - blackFrameProcessAverage /= blackFrameProcessCount; - } else { - blackFrameProcessAverage = 0; - } - if (this.performance.samples.length) { - totalAverage /= this.performance.samples.length; - } else { - totalAverage = 0; - } - - theoreticalAverage = imageDrawAverage + blackFrameDrawAverage + blackFrameProcessAverage; - - for (const sample of this.performance.lastMeasurements.fastLetterbox.samples) { - fastLetterboxAverage += sample; - if (sample > fastLetterboxWorst) { - fastLetterboxWorst = sample; - } - } - for (const sample of this.performance.lastMeasurements.edgeDetect.samples) { - edgeDetectAverage += sample; - if (sample > edgeDetectWorst) { - edgeDetectWorst = sample; - } - } - fastLetterboxCount = this.performance.lastMeasurements.fastLetterbox.samples.length; - edgeDetectCount = this.performance.lastMeasurements.edgeDetect.samples.length; - - if (fastLetterboxCount) { - fastLetterboxAverage /= fastLetterboxCount; - } else { - fastLetterboxAverage = 0; - } - if (edgeDetectCount) { - edgeDetectAverage /= edgeDetectCount; - } else { - edgeDetectAverage = 0; - } - - theoreticalWorst += fastLetterboxWorst + edgeDetectWorst; - theoreticalAverage += fastLetterboxAverage + edgeDetectAverage; - - for (const sample of this.performance.samples) { - if (sample.imageDrawTime) { - imageDrawStDev += Math.pow((sample.imageDrawTime - imageDrawAverage), 2); - } - if (sample.blackFrameDrawTime) { - blackFrameDrawStDev += Math.pow((sample.blackFrameDrawTime - blackFrameDrawAverage), 2); - } - if (sample.blackFrameProcessTime) { - blackFrameProcessStDev += Math.pow((sample.blackFrameProcessTime - blackFrameProcessAverage), 2); - } - - const execTime = - sample.imageDrawTime ?? 0 - + sample.blackFrameDrawTime ?? 0 - + sample.blackFrameProcessTime ?? 0 - + sample.fastLetterboxTime ?? 0 - + sample.edgeDetectTime ?? 0; - - totalStDev += Math.pow((execTime - totalAverage), 2); - } - for (const sample of this.performance.lastMeasurements.fastLetterbox.samples) { - fastLetterboxStDev += Math.pow((sample - fastLetterboxAverage), 2); - } - for (const sample of this.performance.lastMeasurements.edgeDetect.samples) { - edgeDetectStDev += Math.pow((sample - edgeDetectAverage), 2); - } - - if (imageDrawCount < 2) { - imageDrawStDev = 0; - } else { - imageDrawStDev = Math.sqrt(imageDrawStDev / (imageDrawCount - 1)); - } - - if (blackFrameDrawCount < 2) { - blackFrameDrawStDev = 0; - } else { - blackFrameDrawStDev = Math.sqrt(blackFrameDrawStDev / (blackFrameDrawCount - 1)); - } - - if (blackFrameProcessCount < 2) { - blackFrameProcessStDev = 0; - } else { - blackFrameProcessStDev = Math.sqrt(blackFrameProcessStDev / (blackFrameProcessCount - 1)); - } - - if (fastLetterboxCount < 2) { - fastLetterboxStDev = 0; - } else { - fastLetterboxStDev = Math.sqrt(fastLetterboxStDev / (fastLetterboxCount - 1)); - } - - if (edgeDetectCount < 2) { - edgeDetectStDev = 0; - } else { - edgeDetectStDev = Math.sqrt(edgeDetectStDev / (edgeDetectCount - 1)); - } - - if (this.performance.samples.length < 2) { - totalStDev = 0; - } else { - totalStDev = Math.sqrt(totalStDev / (this.performance.samples.length - 1)); - } - - - const res: AardPerformanceData = { - total: { - sampleCount: this.performance.samples.length, - averageTime: totalAverage, - worstTime: totalWorst, - stDev: totalStDev, - }, - theoretical: { - sampleCount: -1, - averageTime: theoreticalAverage, - worstTime: theoreticalWorst, - stDev: theoreticalStDev - }, - imageDraw: { - sampleCount: imageDrawCount, - averageTime: imageDrawAverage, - worstTime: imageDrawWorst, - stDev: imageDrawStDev - }, - blackFrameDraw: { - sampleCount: blackFrameDrawCount, - averageTime: blackFrameDrawAverage, - worstTime: blackFrameDrawWorst, - stDev: blackFrameDrawStDev, - }, - blackFrame: { - sampleCount: blackFrameProcessCount, - averageTime: blackFrameProcessAverage, - worstTime: blackFrameProcessWorst, - stDev: blackFrameProcessStDev - }, - fastLetterbox: { - sampleCount: fastLetterboxCount, - averageTime: fastLetterboxAverage, - worstTime: fastLetterboxWorst, - stDev: fastLetterboxStDev - }, - edgeDetect: { - sampleCount: edgeDetectCount, - averageTime: edgeDetectAverage, - worstTime: edgeDetectWorst, - stDev: edgeDetectStDev - }, - - imageDrawCount, - blackFrameDrawCount, - blackFrameCount: blackFrameProcessCount, - fastLetterboxCount, - edgeDetectCount, - aardActive: this.status.aardActive, - } - - this.eventBus.send('uw-config-broadcast', {type: 'aard-performance-data', performanceData: res}); - } - //#endregion - - /** - * This is the "main loop" for aspect ratio autodetection - */ - private async animationFrameBootstrap(timestamp: number) { - // this.logger.log('info', 'arDetect_verbose', `[ArDetect::animationFrameBootstrap] <@${this.arid}> New animation frame.\nmanualTickEnabled: ${!this.manualTickEnabled}\ncan trigger frame check? ${this.canTriggerFrameCheck()}\nnext tick? ${this._nextTick}\n => (a&b | c) => Can we do tick? ${ (!this.manualTickEnabled && this.canTriggerFrameCheck()) || this._nextTick}\n\ncan we continue running? ${this && !this._halted && !this._paused}`); - - // do timekeeping first - - // trigger frame check, if we're allowed to - if ( (!this.manualTickEnabled && this.canTriggerFrameCheck()) || this._nextTick) { - this.logger.log('info', 'arDetect_verbose', `[ArDetect::animationFrameBootstrap] <@${this.arid}> Processing next tick.`); - - this._nextTick = false; - - try { - await this.frameCheck(); - } catch (e) { - this.logger.log('error', 'debug', `%c[ArDetect::animationFrameBootstrap] <@${this.arid}> Frame check failed:`, "color: #000, background: #f00", e); - } - } - - // if (this && !this._halted && !this._paused) { - this.animationFrameHandle = window.requestAnimationFrame( (ts) => this.animationFrameBootstrap(ts)); - // } else { - // this.logger.log('info', 'debug', `[ArDetect::animationFrameBootstrap] <@${this.arid}> Not renewing animation frame for some reason. Paused? ${this._paused}; Halted?: ${this._halted}, Exited?: ${this._exited}`); - // } - } - - calculateArFromEdges(edges) { - // if we don't specify these things, they'll have some default values. - if(edges.top === undefined){ - edges.top = 0; - edges.bottom = 0; - edges.left = 0; // RESERVED FOR FUTURE — CURRENTLY UNUSED - edges.right = 0; // THIS FUNCTION CAN PRESENTLY ONLY HANDLE LETTERBOX - } - - 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 - - const fileAr = this.video.videoWidth / this.video.videoHeight; - const canvasAr = this.glCanvas.width / this.glCanvas.height; - let widthCorrected; - - if (edges.top && edges.bottom) { - // in case of letterbox, we take canvas height as canon and assume width got stretched or squished - - if (fileAr != canvasAr) { - widthCorrected = this.glCanvas.height * fileAr; - } else { - widthCorrected = this.glCanvas.width; - } - - return widthCorrected / (this.glCanvas.height - letterbox); - } - } - - processAr(trueAr){ - if (!this.isRunning()) { - this.logger.log('warn', 'debug', `[ArDetect::processAr] <@${this.arid}> Trying to change aspect ratio while AARD is paused.`); - return; - } - - // check if aspect ratio is changed: - let lastAr = this.conf.resizer.lastAr; - if (lastAr.type === AspectRatioType.AutomaticUpdate && lastAr.ratio !== null && lastAr.ratio !== undefined){ - // we can only deny aspect ratio changes if we use automatic mode and if aspect ratio was set from here. - - let arDiff = trueAr - lastAr.ratio; - - if (arDiff < 0) - arDiff = -arDiff; - - const arDiff_percent = arDiff / trueAr; - - // is ar variance within acceptable levels? If yes -> we done - this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> New aspect ratio varies from the old one by this much:\n`,"color: #aaf","old Ar", lastAr.ratio, "current ar", trueAr, "arDiff (absolute):",arDiff,"ar diff (relative to new ar)", arDiff_percent); - - if (arDiff < trueAr * this.settings.active.arDetect.allowedArVariance){ - this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> Aspect ratio change denied — diff %: ${arDiff_percent}`, "background: #740; color: #fa2"); - return; - } - this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> aspect ratio change accepted — diff %: ${arDiff_percent}`, "background: #153; color: #4f9"); - } - this.logger.log('info', 'debug', `%c[ArDetect::processAr] <@${this.arid}> Triggering aspect ratio change. New aspect ratio: ${trueAr}`, _ard_console_change); - - this.conf.resizer.updateAr({type: AspectRatioType.AutomaticUpdate, ratio: trueAr}); - } - - clearImageData(id) { - if ((ArrayBuffer as any).transfer) { - (ArrayBuffer as any).transfer(id, 0); - } - id = undefined; - } - - - async frameCheck(){ - this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::processAr] <@${this.arid}> Starting frame check.`); - - const timerResults = { - imageDrawTime: null, - blackFrameDrawTime: null, - blackFrameProcessTime: null, - fastLetterboxTime: null, - edgeDetectTime: null - } - - if (!this.video) { - this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`); - this.conf.destroy(); - return; - } - - if (!this.glCanvas) { - this.init(); - } - - let sampleCols = this.sampleCols.slice(0); - - let startTime = performance.now(); - const imageData = await new Promise( - resolve => { - this.glCanvas.drawVideoFrame(this.video); - resolve(this.glCanvas.getImageData()); - } - ) - console.log('image data received ...') - timerResults.imageDrawTime = performance.now() - startTime; - - startTime = performance.now(); - const bfAnalysis = await this.blackframeTest(imageData); - timerResults.blackFrameProcessTime = performance.now() - startTime; - - if (bfAnalysis.isBlack) { - // 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.addPerformanceMeasurement(timerResults); - return; - } - - const fastLetterboxTestRes = this.fastLetterboxPresenceTest(imageData, sampleCols); - timerResults.fastLetterboxTime = performance.now() - startTime; - - if (! fastLetterboxTestRes) { - // 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. - this.conf.resizer.updateAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); - this.guardLine.reset(); - - this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Letterbox not detected in fast test. Letterbox is either gone or we manually corrected aspect ratio. Nothing will be done.`, "color: #fa3"); - - this.clearImageData(imageData); - this.addPerformanceMeasurement(timerResults); - return; - } - - // let's check if we're cropping too much - const guardLineOut = this.guardLine.check(imageData); - - // if both succeed, then aspect ratio hasn't changed. - // otherwise we continue. We add blackbar violations to the list of the cols we'll sample and sort them - 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.clearImageData(imageData); - this.addPerformanceMeasurement(timerResults); - return; - } else { - if (guardLineOut.blackbarFail) { - sampleCols.concat(guardLineOut.offenders).sort( - (a: number, b: number) => a - b - ); - } - } - - // 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 - // that we will cut too much, we rather avoid doing anything at all. There's gonna be a next chance. - try{ - if (guardLineOut.blackbarFail || guardLineOut.imageFail) { - startTime = performance.now(); - const edgeDetectRes = this.edgeDetector.findBars(imageData, null, EdgeDetectPrimaryDirection.Horizontal); - timerResults.edgeDetectTime = performance.now() - startTime; - - if(edgeDetectRes.status === 'ar_known'){ - if(guardLineOut.blackbarFail){ - this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] Detected blackbar violation and pillarbox. Resetting to default aspect ratio.`); - this.conf.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); - this.guardLine.reset(); - } else { - this.logger.log('info', 'arDetect_verbose', `[ArDetect::frameCheck] Guardline failed, blackbar didn't, and we got pillarbox. Doing nothing.`); - } - - this.clearImageData(imageData); - this.addPerformanceMeasurement(timerResults); - return; - } - } - } catch(e) { - this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] something went wrong while checking for pillarbox. Error:\n`, e); - } - - startTime = performance.now(); - let edgePost = this.edgeDetector.findBars(imageData, sampleCols, EdgeDetectPrimaryDirection.Vertical, EdgeDetectQuality.Improved, guardLineOut, bfAnalysis); - timerResults.edgeDetectTime = performance.now() - startTime; - - this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] edgeDetector returned this\n`, "color: #aaf", edgePost); - - if (edgePost.status !== EdgeStatus.ARKnown){ - // no edge was detected. Let's leave things as they were - this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.ARKnown); - - this.clearImageData(imageData); - this.addPerformanceMeasurement(timerResults); - return; - } - - let newAr = this.calculateArFromEdges(edgePost); - - 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. If edges are okay and not invalid, we also - // allow automatic aspect ratio correction. If edges - // are bogus, we keep aspect ratio unchanged. - try { - // throws error if top/bottom are invalid - this.guardLine.setBlackbar({top: edgePost.guardLineTop, bottom: edgePost.guardLineBottom}); - - this.processAr(newAr); - } catch (e) { - // edges weren't gucci, so we'll just reset - // the aspect ratio to defaults - this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] There was a problem setting blackbar. Doing nothing. Error:`, e); - - try { - this.guardLine.reset(); - } catch (e) { - // no guardline, no biggie - } - // WE DO NOT RESET ASPECT RATIO HERE IN CASE OF PROBLEMS, CAUSES UNWARRANTED RESETS: - // (eg. here: https://www.youtube.com/watch?v=nw5Z93Yt-UQ&t=410) - // - // this.conf.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); - } - - this.addPerformanceMeasurement(timerResults); - this.clearImageData(imageData); - } - - resetBlackLevel(){ - this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel; - } - - async blackframeTest(imageData) { - if (this.blackLevel === undefined) { - this.logger.log('info', 'arDetect_verbose', "[ArDetect::blackframeTest] black level undefined, resetting"); - this.resetBlackLevel(); - } - - /** - * Performs a quick black frame test - */ - const bfDrawStartTime = performance.now(); - - const rows = this.settings.active.arDetect.canvasDimensions.blackframeCanvas.width; - const cols = this.settings.active.arDetect.canvasDimensions.blackframeCanvas.height; - const samples = rows * cols; - - const blackFrameDrawTime = performance.now() - bfDrawStartTime; - const bfProcessStartTime = performance.now(); - - const pixels = rows * cols; - let cumulative_r = 0, cumulative_g = 0, cumulative_b = 0; - let max_r = 0, max_g = 0, max_b = 0; - let avg_r, avg_g, avg_b; - let var_r = 0, var_g = 0, var_b = 0; - - let pixelMax = 0; - let cumulativeValue = 0; - let blackPixelCount = 0; - const blackThreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameThreshold; - - const actualPixels = imageData.length / 4; - - // generate some random points that we'll use for sampling black frame. - const sampleArray = new Array(samples); - for (let i = 0; i < samples; i++) { - sampleArray[i] = Math.round(Math.random() * actualPixels); - } - - // we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists - // with sufficient level of certainty due to small sample resolution, it can still give us some hints for later - let rowMax = new Array(rows).fill(0); - let colMax = new Array(cols).fill(0); - - let r: number, c: number; - - for (const i of sampleArray) { - pixelMax = Math.max(imageData[i], imageData[i+1], imageData[i+2]); - imageData[i+3] = pixelMax; - - if (pixelMax < blackThreshold) { - if (pixelMax < this.blackLevel) { - this.blackLevel = pixelMax; - } - blackPixelCount++; - } else { - cumulativeValue += pixelMax; - cumulative_r += imageData[i]; - cumulative_g += imageData[i+1]; - cumulative_b += imageData[i+2]; - - max_r = max_r > imageData[i] ? max_r : imageData[i]; - max_g = max_g > imageData[i+1] ? max_g : imageData[i+1]; - max_b = max_b > imageData[i+2] ? max_b : imageData[i+2]; - } - - r = ~~(i/rows); - c = i % cols; - - if (pixelMax > rowMax[r]) { - rowMax[r] = pixelMax; - } - if (pixelMax > colMax[c]) { - colMax[c] = colMax; - } - } - - max_r = 1 / (max_r || 1); - max_g = 1 / (max_g || 1); - max_b = 1 / (max_b || 1); - - const imagePixels = pixels - blackPixelCount; - // calculate averages and normalize them - avg_r = (cumulative_r / imagePixels) * max_r; - avg_g = (cumulative_g / imagePixels) * max_g; - avg_b = (cumulative_b / imagePixels) * max_b; - - // second pass for color variance - for (let i = 0; i < imageData.length; i+= 4) { - if (imageData[i+3] >= this.blackLevel) { - var_r += Math.abs(avg_r - imageData[i] * max_r); - var_g += Math.abs(avg_g - imageData[i+1] * max_g); - var_b += Math.abs(avg_b - imageData[i+1] * max_b); - } - } - - const hasSufficientVariance = Math.abs(var_r - var_g) / Math.max(var_r, var_g, 1) > this.settings.active.arDetect.blackframe.sufficientColorVariance - || Math.abs(var_r - var_b) / Math.max(var_r, var_b, 1) > this.settings.active.arDetect.blackframe.sufficientColorVariance - || Math.abs(var_b - var_g) / Math.max(var_b, var_g, 1) > this.settings.active.arDetect.blackframe.sufficientColorVariance - - let isBlack = (blackPixelCount/(cols * rows) > this.settings.active.arDetect.blackframe.blackPixelsCondition); - - if (! isBlack) { - if (hasSufficientVariance) { - isBlack = cumulativeValue < this.settings.active.arDetect.blackframe.cumulativeThresholdLax; - } else { - isBlack = cumulativeValue < this.settings.active.arDetect.blackframe.cumulativeThresholdStrict; - } - } - - if (Debug.debug) { - return { - isBlack: isBlack, - blackPixelCount: blackPixelCount, - blackPixelRatio: (blackPixelCount/(cols * rows)), - cumulativeValue: cumulativeValue, - hasSufficientVariance: hasSufficientVariance, - blackLevel: this.blackLevel, - variances: { - raw: { - r: var_r, g: var_g, b: var_b - }, - relative: { - rg: Math.abs(var_r - var_g) / Math.max(var_r, var_g, 1), - rb: Math.abs(var_r - var_b) / Math.max(var_r, var_b, 1), - gb: Math.abs(var_b - var_g) / Math.max(var_b, var_g, 1), - }, - relativePercent: { - rg: Math.abs(var_r - var_g) / Math.max(var_r, var_g, 1) / this.settings.active.arDetect.blackframe.sufficientColorVariance, - rb: Math.abs(var_r - var_b) / Math.max(var_r, var_b, 1) / this.settings.active.arDetect.blackframe.sufficientColorVariance, - gb: Math.abs(var_b - var_g) / Math.max(var_b, var_g, 1) / this.settings.active.arDetect.blackframe.sufficientColorVariance, - }, - varianceLimit: this.settings.active.arDetect.blackframe.sufficientColorVariance, - }, - cumulativeValuePercent: cumulativeValue / (hasSufficientVariance ? this.settings.active.arDetect.blackframe.cumulativeThresholdLax : this.settings.active.arDetect.blackframe.cumulativeThresholdStrict), - rowMax: rowMax, - colMax: colMax, - }; - } - - const blackFrameProcessTime = performance.now() - bfProcessStartTime; - // TODO: update processing time statistics - - return { - isBlack: isBlack, - rowMax: rowMax, - colMax: colMax, - processingTime: { - blackFrameDrawTime, - blackFrameProcessTime, - } - }; - } - - /** - * Does a quick test to see if the aspect ratio is correct - * Returns 'true' if there's a chance of letterbox existing, false if not. - * @param imageData - * @param sampleCols - * @returns - */ - fastLetterboxPresenceTest(imageData, sampleCols) { - // fast test to see if aspect ratio is correct. - // returns 'true' if presence of letterbox is possible. - // returns 'false' if we found a non-black edge pixel. - - // If we detect anything darker than blackLevel, we modify blackLevel to the new lowest value - const rowOffset = this.glCanvas.width * (this.glCanvas.height - 1); - let currentMin = 255, currentMax = 0, colOffset_r, colOffset_g, colOffset_b, colOffset_rb, colOffset_gb, colOffset_bb, blthreshold = this.settings.active.arDetect.blackbar.threshold; - - // detect black level. if currentMax comes above blackbar + blackbar threshold, we know we aren't letterboxed - - for (let i = 0; i < sampleCols.length; ++i){ - colOffset_r = sampleCols[i] << 2; - colOffset_g = colOffset_r + 1; - colOffset_b = colOffset_r + 2; - colOffset_rb = colOffset_r + rowOffset; - colOffset_gb = colOffset_g + rowOffset; - colOffset_bb = colOffset_b + rowOffset; - - currentMax = Math.max( - imageData[colOffset_r], imageData[colOffset_g], imageData[colOffset_b], - // imageData[colOffset_rb], imageData[colOffset_gb], imageData[colOffset_bb], - currentMax - ); - - if (currentMax > this.blackLevel + blthreshold) { - // we search no further - if (currentMin < this.blackLevel) { - this.blackLevel = currentMin; - } - return false; - } - - currentMin = Math.min( - currentMax, - currentMin - ); - } - - - - if (currentMin < this.blackLevel) { - this.blackLevel = currentMin - } - - return true; - } - -} - -let _ard_console_stop = "background: #000; color: #f41"; -let _ard_console_start = "background: #000; color: #00c399"; -let _ard_console_change = "background: #000; color: #ff8"; - -export default ArDetector; diff --git a/src/ext/lib/ar-detect/DebugCanvas.js b/src/ext/lib/ar-detect/DebugCanvas.js deleted file mode 100644 index fbad560..0000000 --- a/src/ext/lib/ar-detect/DebugCanvas.js +++ /dev/null @@ -1,129 +0,0 @@ -class DebugCanvas { - constructor(ardConf){ - this.conf = ardConf; - this.targetWidth = 1280; - this.targetHeight = 720; - this.targetOffsetTop = 1080; - this.targetOffsetLeft = 100; - } - - init(canvasSize, canvasPosition, targetCanvasSize) { - console.log("initiating DebugCanvas") - - var body = document.getElementsByTagName('body')[0]; - - if(!canvasPosition){ - canvasPosition = { - top: 1200, - left: 800 - } - } - if(!this.canvas){ - this.canvas = document.createElement("canvas"); - body.appendChild(this.canvas); - } - - if(targetCanvasSize){ - this.targetWidth = targetCanvasSize.width; - this.targetHeight = targetCanvasSize.height; - } - - this.canvas.style.position = "absolute"; - this.canvas.style.left = `${canvasPosition.left}px`; - this.canvas.style.top = `${canvasPosition.top}px`; - this.canvas.style.zIndex = 10002; - // this.canvas.id = "uw_debug_canvas"; - - this.context = this.canvas.getContext("2d"); - - this.canvas.width = canvasSize.width; - this.canvas.height = canvasSize.height; - - this.calculateCanvasZoom(); - - console.log("debug canvas is:", this.canvas, "context:", this.context) - } - - calculateCanvasZoom(){ - var canvasZoom = this.targetWidth / this.canvas.width; - var translateX = (this.canvas.width - this.targetWidth)/2; - var translateY = (this.canvas.height - this.targetHeight)/2; - - this.canvas.style.transform = `scale(${canvasZoom},${canvasZoom}) translateX(${translateX}px) translateY(${translateY}px)`; - } - - destroy(){ - if(this.canvas) - this.canvas.remove(); - } - - setBuffer(buffer) { - // this.imageBuffer = buffer.splice(0); - this.imageBuffer = new Uint8ClampedArray(buffer); - } - - trace(arrayIndex, colorClass) { - this.imageBuffer[arrayIndex ] = colorClass.colorRgb[0]; - this.imageBuffer[arrayIndex+1] = colorClass.colorRgb[1]; - this.imageBuffer[arrayIndex+2] = colorClass.colorRgb[2]; - } - - update() { - var start = performance.now(); - try{ - if(this.context && this.imageBuffer instanceof Uint8ClampedArray){ - try{ - var idata = new ImageData(this.imageBuffer, this.canvas.width, this.canvas.height); - } catch (ee) { - console.log("[DebugCanvas.js::update] can't create image data. Trying to correct canvas size. Error was:", ee); - this.canvas.width = this.conf.canvas.width; - this.canvas.height = this.conf.canvas.height; - - this.calculateCanvasZoom(); - // this.context = this.canvas.getContext("2d"); - var idata = new ImageData(this.imageBuffer, this.canvas.width, this.canvas.height); - } - this.putImageData(this.context, idata, 0, 0, 0, 0, this.canvas.width, this.canvas.height); - } - } catch(e) { - console.log("[DebugCanvas.js::update] updating canvas failed.", e); - } - console.log("[DebugCanvas.js::update] update took", (performance.now() - start), "ms."); - } - - - putImageData(ctx, imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) { - var data = imageData.data; - var height = imageData.height; - var width = imageData.width; - dirtyX = dirtyX || 0; - dirtyY = dirtyY || 0; - dirtyWidth = dirtyWidth !== undefined? dirtyWidth: width; - dirtyHeight = dirtyHeight !== undefined? dirtyHeight: height; - var limitBottom = Math.min(dirtyHeight, height); - var limitRight = Math.min(dirtyWidth, width); - for (var y = dirtyY; y < limitBottom; y++) { - for (var x = dirtyX; x < limitRight; x++) { - var pos = y * width + x; - ctx.fillStyle = 'rgba(' + data[pos*4+0] - + ',' + data[pos*4+1] - + ',' + data[pos*4+2] - + ',' + (data[pos*4+3]/255) + ')'; - ctx.fillRect(x + dx, y + dy, 1, 1); - } - } - } -} - -DebugCanvasClasses = { - VIOLATION: {color: '#ff0000', colorRgb: [255, 0, 0], text: 'violation (general)'}, - WARN: {color: '#d0d039', colorRgb: [208, 208, 57], text: 'lesser violation (general)'}, - GUARDLINE_BLACKBAR: {color: '#3333FF', colorRgb: [51, 51, 255], text: 'guardline/blackbar (expected value)'}, - GUARDLINE_IMAGE: {color: '#000088', colorRgb: [0, 0, 136], text: 'guardline/image (expected value)'}, - - EDGEDETECT_ONBLACK: {color: '#444444', colorRgb: [68, 68, 68], text: 'edge detect - perpendicular (to edge)'}, - EDGEDETECT_CANDIDATE: {color: '#FFFFFF', colorRgb: [255, 255, 255], text: 'edge detect - edge candidate'}, - EDGEDETECT_CANDIDATE_SECONDARY: {color: '#OOOOOO', colorRgb: [0, 0, 0], text: 'edge detect - edge candidate, but for when candidate is really bright'}, - EDGEDETECT_BLACKBAR: {color: '#07ac93', colorRgb: [7, 172, 147], text: 'edge detect - parallel, black test'}, - EDGEDETECT_IMAGE: {color: '#046c5c', colorRgb: [4, 108, 92], text: 'edge detect - parallel, image test'} -} \ No newline at end of file diff --git a/src/ext/lib/ar-detect/GuardLine.ts b/src/ext/lib/ar-detect/GuardLine.ts deleted file mode 100644 index e635021..0000000 --- a/src/ext/lib/ar-detect/GuardLine.ts +++ /dev/null @@ -1,291 +0,0 @@ -import Debug from '../../conf/Debug'; -import Settings from '../Settings'; -import ArDetector from './ArDetector'; - -export type GuardLineBar = { - top?: number; - bottom?: number; -} - -export type ImageCheckResult = { - success: boolean; -} - -class GuardLine { - blackbar: GuardLineBar; - imageBar: GuardLineBar; - - aard: ArDetector; - settings: Settings; - - blackbarThreshold: number; - imageThreshold: number; - - - // ardConf — reference to ArDetector that has current GuardLine instance - constructor (ardConf){ - this.blackbar = {top: undefined, bottom: undefined}; - this.imageBar = {top: undefined, bottom: undefined}; - - this.aard = ardConf; - this.settings = ardConf.settings; - } - - reset() { - this.blackbar = {top: undefined, bottom: undefined}; - this.imageBar = {top: undefined, bottom: undefined}; - } - - setBlackbar(bbconf){ - let bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx; - let bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx; - - // to odstrani vse neveljavne nastavitve in vse možnosti, ki niso smiselne - // this removes any configs with invalid values or values that dont make sense - if (bbTop < 0 || bbBottom >= this.aard.glCanvas.height ){ - throw {error: "INVALID_SETTINGS_IN_GUARDLINE", bbTop, bbBottom} - } - - this.blackbar = { - top: bbTop, - bottom: bbBottom - } - - this.imageBar = { - top: bbconf.top + 1 + this.settings.active.arDetect.guardLine.edgeTolerancePx, - bottom: bbconf.bottom - 1 - this.settings.active.arDetect.guardLine.edgeTolerancePx - } - } - - check(image){ - // calculate once and save object-instance-wide - this.blackbarThreshold = this.aard.blackLevel + this.settings.active.arDetect.blackbar.threshold; - this.imageThreshold = this.blackbarThreshold + this.settings.active.arDetect.blackbar.imageThreshold; - - // actual checks - let guardLineResult = this.guardLineCheck(image); - - // blackbar violation detected. We don't even need to check for presence of image - // as aspect ratio just decreased - if(! guardLineResult.success) { - return { - blackbarFail: true, - offenders: guardLineResult.offenders, - imageFail: false - } - } - - let imageCheckResult = this.imageCheck(image); - - return { - blackbarFail: false, - imageFail: ! imageCheckResult.success - } - } - - - // don't use methods below this line outside this class - guardLineCheck(image){ - // 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 - // checks are needed to determine whether aspect ratio got wider. - // 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 - // should succeed by default. Also need to check bottom, for cases where only one edge is known - - if (!this.blackbar.top || !this.blackbar.bottom) { - return { success: true }; - } - - let offset = (this.aard.glCanvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2; - - let offenders = []; - let offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index. - - // TODO: implement logo check. - - // preglejmo obe vrstici - // check both rows - let edge_lower, edge_upper; - - edge_upper = this.blackbar.top; - edge_lower = this.blackbar.bottom; - - let rowStart, rowEnd; - - // <<<=======| checking upper row |========>>> - - rowStart = ((edge_upper * this.aard.glCanvas.width) << 2) + offset; - rowEnd = rowStart + ( this.aard.glCanvas.width << 2 ) - (offset * 2); - - if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) { - // offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount); - } else { - offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount); - } - // <<<=======| checking lower row |========>>> - - rowStart = ((edge_lower * this.aard.glCanvas.width) << 2) + offset; - rowEnd = rowStart + ( this.aard.glCanvas.width << 2 ) - (offset * 2); - - if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) { - // offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount); - } else { - offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount); - } - - // č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) - // - // 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) - - if(offenderCount == -1){ - return {success: true}; - } - - let ret = new Array(offenders.length); - for(let o in offenders){ - ret[o] = offenders[o].x + (offenders[o].width * 0.25); - } - - return {success: false, offenders: ret}; - } - - imageCheck(image): ImageCheckResult { - if(!this.imageBar.top || !this.imageBar.bottom) - return { success: false }; - - let offset = (this.aard.glCanvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2; - - // TODO: implement logo check. - - let edge_upper = this.imageBar.top; - let edge_lower = this.imageBar.bottom; - - // 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) - let successThreshold = (this.aard.glCanvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold); - let rowStart, rowEnd; - - - // <<<=======| checking upper row |========>>> - - rowStart = ((edge_upper * this.aard.glCanvas.width) << 2) + offset; - rowEnd = rowStart + ( this.aard.glCanvas.width << 2 ) - (offset * 2); - - 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.glCanvas.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}; - } - - // pomožne metode - // helper methods - - - _gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){ - let firstOffender = -1; - 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 - // columns to sample - if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){ - if(firstOffender < 0){ - firstOffender = (i - rowStart) >> 2; - offenderCount++; - offenders.push({x: firstOffender, width: 1}); - } - else{ - offenders[offenderCount].width++ - } - } - else{ - // is that a black pixel again? Let's reset the 'first offender' - firstOffender = -1; - } - } - - return offenderCount; - } - - // _gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){ - // let firstOffender = -1; - // 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 - // // columns to sample - // if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){ - // this.aard.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION); - // if(firstOffender < 0){ - // firstOffender = (i - rowStart) >> 2; - // offenderCount++; - // offenders.push({x: firstOffender, width: 1}); - // } - // else{ - // offenders[offenderCount].width++ - // } - // } - // else{ - // this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR); - // // is that a black pixel again? Let's reset the 'first offender' - // firstOffender = -1; - // } - - // } - - // return offenderCount; - // } - - _ti_checkRow(image, rowStart, rowEnd, successThreshold): boolean { - for(let i = rowStart; i < rowEnd; i+=4){ - if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){ - if(successThreshold --<= 0){ - return true; - } - } - } - - return false; - } - - // _ti_debugCheckRow(image, rowStart, rowEnd, successThreshold) { - // for(let i = rowStart; i < rowEnd; i+=4){ - // if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){ - // this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_IMAGE); - // if(successThreshold --<= 0){ - // return true; - // } - // } else { - // this.aard.debugCanvas.trace(i, DebugCanvasClasses.WARN); - // } - // } - - // return false; - // } -} - -export default GuardLine; diff --git a/src/ext/lib/ar-detect/edge-detect/EdgeDetect.ts b/src/ext/lib/ar-detect/edge-detect/EdgeDetect.ts deleted file mode 100644 index 07ea2bc..0000000 --- a/src/ext/lib/ar-detect/edge-detect/EdgeDetect.ts +++ /dev/null @@ -1,1241 +0,0 @@ -import Debug from '../../../conf/Debug'; -import EdgeStatus from './enums/EdgeStatusEnum'; -import EdgeDetectQuality from './enums/EdgeDetectQualityEnum'; -import EdgeDetectPrimaryDirection from './enums/EdgeDetectPrimaryDirectionEnum'; -import AntiGradientMode from '../../../../common/enums/AntiGradientMode.enum'; -import ArDetector from '../ArDetector'; -import Logger from '../../Logger'; -import Settings from '../../Settings'; - -if (Debug.debug) { - console.log("Loading EdgeDetect.js"); -} - -class EdgeDetect{ - conf: ArDetector; - logger: Logger; - settings: Settings; - - - // helper stuff - private sampleWidthBase: number; - private halfSample: number; - private detectionThreshold: number; - private colsThreshold: number; - private blackbarThreshold: number; - private imageThreshold: number; - - constructor(ardConf){ - this.conf = ardConf; - this.logger = ardConf.logger; - this.settings = ardConf.settings; - - this.sampleWidthBase = this.settings.active.arDetect.edgeDetection.sampleWidth << 2; // corrected so we can work on imageData - this.halfSample = this.sampleWidthBase >> 1; - - this.detectionThreshold = this.settings.active.arDetect.edgeDetection.detectionThreshold; - - this.init(); // initiate things that can change - } - - // initiates things that we may have to change later down the line - init() { - - } - - findBars(image, sampleCols, direction = EdgeDetectPrimaryDirection.Vertical, quality = EdgeDetectQuality.Improved, guardLineOut?, blackFrameAnalysis?){ - let fastCandidates, edgeCandidates, bars; - if (direction == EdgeDetectPrimaryDirection.Vertical) { - try { - fastCandidates = this.findCandidates(image, sampleCols, guardLineOut); - - if (! this.isValidSample(fastCandidates)) { - return {status: EdgeStatus.ARUnknown}; - } - // if(quality == EdgeDetectQuality.FAST){ - // edges = fastCandidates; // todo: processing - // } else { - edgeCandidates = this.edgeDetect(image, fastCandidates); - bars = this.edgePostprocess(edgeCandidates); - // } - } catch (e) { - this.logger.log('error', 'arDetect', '%c[EdgeDetect::findBars] find bars failed.', 'background: #f00, color: #000', e); - return {status: EdgeStatus.ARUnknown} - } - } else { - bars = this.pillarTest(image) ? {status: EdgeStatus.ARKnown} : {status: EdgeStatus.ARUnknown}; - } - - return bars; - - } - - findCandidates(image, sampleCols, guardLineOut){ - try { - let upper_top, upper_bottom, lower_top, lower_bottom; - - // const cols_a = sampleCols.slice(0); - const cols_a = new Array(sampleCols.length); - const res_top = []; - - for (let i = 0; i < cols_a.length; i++) { - cols_a[i] = { - id: i, - value: sampleCols[i], - }; - } - - const cols_b = cols_a.slice(0); - const res_bottom = []; - - this.colsThreshold = sampleCols.length * this.settings.active.arDetect.edgeDetection.minColsForSearch; - if (this.colsThreshold == 0) - this.colsThreshold = 1; - - this.blackbarThreshold = this.conf.blackLevel + this.settings.active.arDetect.blackbar.threshold; - this.imageThreshold = this.blackbarThreshold + this.settings.active.arDetect.blackbar.imageThreshold; - - // if guardline didn't fail and imageDetect did, we don't have to check the upper few pixels - // but only if upper and lower edge are defined. If they're not, we need to check full height - if(guardLineOut){ - if(guardLineOut.imageFail && !guardLineOut.blackbarFail && this.conf.guardLine.blackbar.top) { - upper_top = this.conf.guardLine.blackbar.top; - upper_bottom = this.conf.glCanvas.height >> 1; - lower_top = upper_bottom; - lower_bottom = this.conf.guardLine.blackbar.bottom; - } else if (! guardLineOut.imageFail && !guardLineOut.blackbarFail && this.conf.guardLine.blackbar.top) { - // ta primer se lahko zgodi tudi zaradi kakšnega logotipa. Ker nočemo, da nam en jeben - // logotip vsili reset razmerja stranic, se naredimo hrvata in vzamemo nekaj varnostnega - // pasu preko točke, ki jo označuje guardLine.blackbar. Recimo 1/8 višine platna na vsaki strani. - // a logo could falsely trigger this case, so we need to add some extra margins past - // the point marked by guardLine.blackbar. Let's say 1/8 of canvas height on either side. - upper_top = 0; - upper_bottom = this.conf.guardLine.blackbar.top + (this.conf.glCanvas.height >> 3); - lower_top = this.conf.guardLine.blackbar.bottom - (this.conf.glCanvas.height >> 3); - lower_bottom = this.conf.glCanvas.height - 1; - } else { - upper_top = 0; - upper_bottom = (this.conf.glCanvas.height >> 1) /*- parseInt(this.conf.glCanvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/ - lower_top = (this.conf.glCanvas.height >> 1) /*+ parseInt(this.conf.glCanvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/ - lower_bottom = this.conf.glCanvas.height - 1; - } - } else{ - upper_top = 0; - upper_bottom = (this.conf.glCanvas.height >> 1) /*- parseInt(this.conf.glCanvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/ - lower_top = (this.conf.glCanvas.height >> 1) /*+ parseInt(this.conf.glCanvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/ - lower_bottom = this.conf.glCanvas.height - 1; - } - - this.logger.log('info', 'arDetect', '[EdgeDetect::findCandidates] searching for candidates on ranges', upper_top, '<->', upper_bottom, ';', lower_top, '<->', lower_bottom); - - let upper_top_corrected = upper_top * this.conf.canvasImageDataRowLength; - let upper_bottom_corrected = upper_bottom * this.conf.canvasImageDataRowLength; - let lower_top_corrected = lower_top * this.conf.canvasImageDataRowLength; - let lower_bottom_corrected = lower_bottom * this.conf.canvasImageDataRowLength; - - // if(Debug.debugCanvas.enabled){ - // this._columnTest_dbgc(image, upper_top_corrected, upper_bottom_corrected, cols_a, res_top, false); - // this._columnTest_dbgc(image, lower_top_corrected, lower_bottom_corrected, cols_b, res_bottom, true); - // } else { - this._columnTest3_cross(image, upper_top_corrected, upper_bottom_corrected, cols_a, res_top, false); - this._columnTest3_cross(image, lower_top_corrected, lower_bottom_corrected, cols_b, res_bottom, true); - // } - - this.logger.log('info', 'arDetect', '[EdgeDetect::findCandidates] candidates found -->', {res_top: res_top, res_bottom: res_bottom}); - - return {res_top: res_top, res_bottom: res_bottom}; - } catch (e) { - this.logger.log('error', 'debug', '[EdgeDetect::findCandidates] there was an error while finding candidates:', e); - } - } - - - - // dont call the following outside of this class - - isValidSample(samples) { - // NOTE: this is very simple and will need to be reworked in case we ever - // go for quorum-based edge detection. (Probably not gonna happen) - const topPoint = { - row: this.conf.glCanvas.height, - gradient: false, // does current row have a gradient sample - noGradient: false, // does current row have 100% confirmed edge sample - } - const bottomPoint = { - row: 0, - gradient: false, - noGradient: false, - } - - // process the top row - for (let i = 0; i < samples.res_top.length; i++) { - // if we find new highest point, we reset gradient and noGradient - if (samples.res_top[i].black < topPoint.row) { - topPoint.row = samples.res_top[i].black; - topPoint.gradient = false; - topPoint.noGradient = false; - } - - // if we're in the top row, we update gradient/no gradient - // we track gradient and nogradient points separately - if (samples.res_top[i].black === topPoint.row) { - if (samples.res_top[i].hasGradient) { - topPoint.gradient = true; - } else { - topPoint.noGradient = true; - } - } - } - - // process the bottom row - for (let i = 0; i < samples.res_top.length; i++) { - // if we find new highest point, we reset gradient and noGradient - if (samples.res_top[i].black > bottomPoint.row) { - bottomPoint.row = samples.res_top[i].black; - bottomPoint.gradient = false; - bottomPoint.noGradient = false; - } - - // if we're in the top row, we update gradient/no gradient - // we track gradient and nogradient points separately - if (samples.res_top[i].black === bottomPoint.row) { - if (samples.res_top[i].hasGradient) { - bottomPoint.gradient = true; - } else { - bottomPoint.noGradient = true; - } - } - } - - if (topPoint.noGradient && bottomPoint.noGradient) { - return true; - } - - - if (! topPoint.noGradient) { - if (! bottomPoint.noGradient) { - // top sample in both is a gradient with no solid sample present in that row - // this means validation failed: - return false; - } else { - // if top gradient-only sample is closer to the edge than the bottom sample, - // validation also fails. Otherwise, we can assume success. - return (topPoint.row >= this.conf.glCanvas.height - bottomPoint.row); - } - } - - // this is the only combination that we have to handle at this point. Because we're - // here, we know that the top row is reasonably gradient-free. We only need to check - // whether gradient-only result of bottom row is closer to the edge than than the top - // sample. - if (! bottomPoint.noGradient) { - return (topPoint.row < this.conf.glCanvas.height - bottomPoint.row); - } - - return false; - } - - edgeDetect(image, samples){ - let edgeCandidatesTop = {count: 0}; - let edgeCandidatesBottom = {count: 0}; - - let detections; - let canvasWidth = this.conf.glCanvas.width; - let canvasHeight = this.conf.glCanvas.height; - - let sampleStart, sampleEnd, loopEnd; - let sampleRow_black, sampleRow_color; - - let blackEdgeViolation = false; - - let topEdgeCount = 0; - let bottomEdgeCount = 0; - - try { - for (const sample of samples.res_top){ - blackEdgeViolation = false; // reset this - - // determine our bounds. Note that sample.col is _not_ corrected for imageData, but halfSample is - sampleStart = (sample.col << 2) - this.halfSample; - - if(sampleStart < 0) - sampleStart = 0; - - sampleEnd = sampleStart + this.sampleWidthBase; - if (sampleEnd > this.conf.canvasImageDataRowLength) - sampleEnd = this.conf.canvasImageDataRowLength; - - // calculate row offsets for imageData array - sampleRow_black = (sample.black - this.settings.active.arDetect.edgeDetection.edgeTolerancePx - 1) * this.conf.canvasImageDataRowLength; - sampleRow_color = (sample.black + this.settings.active.arDetect.edgeDetection.edgeTolerancePx) * this.conf.canvasImageDataRowLength; - - // že ena kršitev črnega roba pomeni, da kandidat ni primeren - // even a single black edge violation means the candidate is not an edge - loopEnd = sampleRow_black + sampleEnd; - - if (Debug.debugCanvas.enabled){ - // blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd); - } else { - blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd); - } - - // če je bila črna črta skrunjena, preverimo naslednjega kandidata - // if we failed, we continue our search with the next candidate - if (blackEdgeViolation) { - continue; - } - - detections = 0; - loopEnd = sampleRow_color + sampleEnd; - - if (Debug.debugCanvas.enabled) { - // this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop) - } else { - this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop); - } - } - - for (const sample of samples.res_bottom){ - blackEdgeViolation = false; // reset this - - // determine our bounds. Note that sample.col is _not_ corrected for imageData, but this.halfSample is - sampleStart = (sample.col << 2) - this.halfSample; - - if(sampleStart < 0) - sampleStart = 0; - - sampleEnd = sampleStart + this.sampleWidthBase; - if(sampleEnd > this.conf.canvasImageDataRowLength) - sampleEnd = this.conf.canvasImageDataRowLength; - - // calculate row offsets for imageData array - sampleRow_black = (sample.black + this.settings.active.arDetect.edgeDetection.edgeTolerancePx + 1) * this.conf.canvasImageDataRowLength; - sampleRow_color = (sample.black - this.settings.active.arDetect.edgeDetection.edgeTolerancePx) * this.conf.canvasImageDataRowLength; - - // že ena kršitev črnega roba pomeni, da kandidat ni primeren - // even a single black edge violation means the candidate is not an edge - loopEnd = sampleRow_black + sampleEnd; - - if(Debug.debugCanvas.enabled){ - // blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd); - } else { - blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd); - } - - // če je bila črna črta skrunjena, preverimo naslednjega kandidata - // if we failed, we continue our search with the next candidate - if (blackEdgeViolation) { - continue; - } - - detections = 0; - loopEnd = sampleRow_color + sampleEnd; - - if(Debug.debugCanvas.enabled) { - // this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom); - } else { - this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom); - } - } - } catch (e) { - this.logger.log('error', 'debug', '[EdgeDetect::edgeDetect] There was an error:', e); - } - - return { - edgeCandidatesTop: edgeCandidatesTop, - edgeCandidatesTopCount: edgeCandidatesTop.count, - edgeCandidatesBottom: edgeCandidatesBottom, - edgeCandidatesBottomCount: edgeCandidatesBottom.count - }; - } - - edgePostprocess(edges){ - let edgesTop = []; - let edgesBottom = []; - let alignMargin = this.conf.glCanvas.height * this.settings.active.arDetect.allowedMisaligned; - - let missingEdge = edges.edgeCandidatesTopCount == 0 || edges.edgeCandidatesBottomCount == 0; - - // pretvorimo objekt v tabelo - // convert objects to array - - delete(edges.edgeCandidatesTop.count); - delete(edges.edgeCandidatesBottom.count); - - if( edges.edgeCandidatesTopCount > 0){ - for(let e in edges.edgeCandidatesTop){ - let edge = edges.edgeCandidatesTop[e]; - edgesTop.push({ - distance: edge.offset, - absolute: edge.offset, - count: edge.count - }); - } - } - - if( edges.edgeCandidatesBottomCount > 0){ - for(let e in edges.edgeCandidatesBottom){ - let edge = edges.edgeCandidatesBottom[e]; - edgesBottom.push({ - distance: this.conf.glCanvas.height - edge.offset, - absolute: edge.offset, - count: edge.count - }); - } - } - - // sort by distance - edgesTop = edgesTop.sort((a,b) => {return a.distance - b.distance}); - edgesBottom = edgesBottom.sort((a,b) => {return a.distance - b.distance}); - - - // če za vsako stran (zgoraj in spodaj) poznamo vsaj enega kandidata, potem lahko preverimo nekaj - // stvari - - if (!missingEdge){ - // predvidevamo, da je logo zgoraj ali spodaj, nikakor pa ne na obeh straneh hkrati. - // če kanal logotipa/watermarka ni vključil v video, potem si bosta razdaliji (edge.distance) prvih ključev - // zgornjega in spodnjega roba približno enaki - // - // we'll assume that no youtube channel is rude enough to put channel logo/watermark both on top and the bottom - // of the video. If logo's not included in the video, distances (edge.distance) of the first two keys should be - // roughly equal. Let's check for that. - if( edgesTop[0].distance >= edgesBottom[0].distance - alignMargin && - edgesTop[0].distance <= edgesBottom[0].distance + alignMargin ){ - - let blackbarWidth = edgesTop[0].distance > edgesBottom[0].distance ? - edgesTop[0].distance : edgesBottom[0].distance; - - if (edgesTop[0].count + edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold - || ( edgesTop[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold && edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold) ){ - return { - status: EdgeStatus.ARKnown, - blackbarWidth: blackbarWidth, - guardLineTop: edgesTop[0].distance, - guardLineBottom: edgesBottom[0].absolute, - - top: edgesTop[0].distance, - bottom: edgesBottom[0].distance - }; - } - } - - // torej, lahko da je na sliki watermark. Lahko, da je slika samo ornh črna. Najprej preverimo za watermark - // it could be watermark. It could be a dark frame. Let's check for watermark first. - if (edgesTop[0].distance < edgesBottom[0].distance && - edgesTop[0].count < edgesBottom[0].count && - edgesTop[0].count < this.conf.sampleCols.length * this.settings.active.arDetect.edgeDetection.logoThreshold){ - // možno, da je watermark zgoraj. Preverimo, če se kateri od drugih potencialnih robov na zgornjem robu - // ujema s prvim spodnjim (+/- variance). Če je temu tako, potem bo verjetno watermark. Logo mora imeti - // manj vzorcev kot navaden rob. - - if (edgesTop[0].length > 1){ - let lowMargin = edgesBottom[0].distance - alignMargin; - let highMargin = edgesBottom[0].distance + alignMargin; - - for (let i = 1; i < edgesTop.length; i++){ - if(edgesTop[i].distance >= lowMargin && edgesTop[i].distance <= highMargin){ - // dobili smo dejanski rob. vrnimo ga - // we found the actual edge. let's return that. - let blackbarWidth = edgesTop[i].distance > edgesBottom[0].distance ? - edgesTop[i].distance : edgesBottom[0].distance; - - if (edgesTop[i].count + edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold - || (edgesTop[i].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold && edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold) ) { - return { - status: EdgeStatus.ARKnown, - blackbarWidth: blackbarWidth, - guardLineTop: edgesTop[i].distance, - guardLineBottom: edgesBottom[0].absolute, - - top: edgesTop[i].distance, - bottom: edgesBottom[0].distance - }; - } - } - } - } - } - if (edgesBottom[0].distance < edgesTop[0].distance && - edgesBottom[0].count < edgesTop[0].count && - edgesBottom[0].count < this.conf.sampleCols.length * this.settings.active.arDetect.edgeDetection.logoThreshold){ - - if(edgesBottom[0].length > 1){ - let lowMargin = edgesTop[0].distance - alignMargin; - let highMargin = edgesTop[0].distance + alignMargin; - - for(let i = 1; i < edgesBottom.length; i++){ - if (edgesBottom[i].distance >= lowMargin && edgesTop[i].distance <= highMargin) { - // dobili smo dejanski rob. vrnimo ga - // we found the actual edge. let's return that. - let blackbarWidth = edgesBottom[i].distance > edgesTop[0].distance ? - edgesBottom[i].distance : edgesTop[0].distance; - - if (edgesTop[0].count + edgesBottom[i].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold - || (edgesTop[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold && edgesBottom[i].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold)) { - return { - status: EdgeStatus.ARKnown, - blackbarWidth: blackbarWidth, - guardLineTop: edgesTop[0].distance, - guardLineBottom: edgesBottom[i].absolute, - - top: edgesTop[0].distance, - bottom: edgesBottom[i].distance - }; - } - } - } - } - } - } - else { - // zgornjega ali spodnjega roba nismo zaznali. Imamo še en trik, s katerim lahko poskusimo - // določiti razmerje stranic - // either the top or the bottom edge remains undetected, but we have one more trick that we - // can try. It also tries to work around logos. - - const edgeDetectionThreshold = this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold; - - if (edges.edgeCandidatesTopCount == 0 && edges.edgeCandidatesBottomCount != 0){ - for(let edge of edgesBottom){ - if(edge.count >= edgeDetectionThreshold) - return { - status: EdgeStatus.ARKnown, - blackbarWidth: edge.distance, - guardLineTop: null, - guardLineBottom: edge.bottom, - - top: edge.distance, - bottom: edge.distance - } - } - } - if (edges.edgeCandidatesTopCount != 0 && edges.edgeCandidatesBottomCount == 0){ - for(let edge of edgesTop){ - if(edge.count >= edgeDetectionThreshold) - return { - status: EdgeStatus.ARKnown, - blackbarWidth: edge.distance, - guardLineTop: edge.top, - guardLineBottom: null, - - top: edge.distance, - bottom: edge.distance - } - } - } - } - // če pridemo do sem, nam ni uspelo nič. Razmerje stranic ni znano - // if we reach this bit, we have failed in determining aspect ratio. It remains unknown. - return {status: EdgeStatus.ARUnknown} - } - - pillarTest(image){ - // preverimo, če na sliki obstajajo navpične črne obrobe. Vrne 'true' če so zaznane (in če so približno enako debele), 'false' sicer. - // true vrne tudi, če zaznamo preveč črnine. - // <==XX(::::}----{::::)XX==> - // checks the image for presence of vertical pillars. Less accurate than 'find blackbar limits'. If we find a non-black object that's - // roughly centered, we return true. Otherwise we return false. - // we also return true if we detect too much black - - let blackbarThreshold, upper, lower; - blackbarThreshold = this.conf.blackLevel + this.settings.active.arDetect.blackbar.threshold; - - - let middleRowStart = (this.conf.glCanvas.height >> 1) * this.conf.glCanvas.width; - let middleRowEnd = middleRowStart + this.conf.glCanvas.width - 1; - - let rowStart = middleRowStart << 2; - let midpoint = (middleRowStart + (this.conf.glCanvas.width >> 1)) << 2 - let rowEnd = middleRowEnd << 2; - - let edge_left = -1, edge_right = -1; - - // preverimo na levi strani - // let's check for edge on the left side - for(let i = rowStart; i < midpoint; i+=4){ - if(image[i] > blackbarThreshold || image[i+1] > blackbarThreshold || image[i+2] > blackbarThreshold){ - edge_left = (i - rowStart) >> 2; - break; - } - } - - // preverimo na desni strani - // check on the right - for(let i = rowEnd; i > midpoint; i-= 4){ - if(image[i] > blackbarThreshold || image[i+1] > blackbarThreshold || image[i+2] > blackbarThreshold){ - edge_right = this.conf.glCanvas.width - ((i - rowStart) >> 2); - break; - } - } - - // če je katerikoli -1, potem imamo preveč črnine - // we probably have too much black if either of those two is -1 - if(edge_left == -1 || edge_right == -1){ - return true; - } - - // če sta oba robova v mejah merske napake, potem vrnemo 'false' - // if both edges resemble rounding error, we retunr 'false' - if(edge_left < this.settings.active.arDetect.pillarTest.ignoreThinPillarsPx && edge_right < this.settings.active.arDetect.pillarTest.ignoreThinPillarsPx){ - return false; - } - - let edgeError = this.settings.active.arDetect.pillarTest.allowMisaligned; - let error_low = 1 - edgeError; - let error_hi = 1 + edgeError; - - // če sta 'edge_left' in 'edge_right' podobna/v mejah merske napake, potem vrnemo true — lahko da smo našli logo na sredini zaslona - // if 'edge_left' and 'edge_right' are similar enough to each other, we return true. If we found a logo in a black frame, we could - // crop too eagerly - if( (edge_left * error_low) < edge_right && - (edge_left * error_hi) > edge_right ){ - return true; - } - - // če se ne zgodi nič od neštetega, potem nismo našli problemov - // if none of the above, we haven't found a problem - return false; - } - - - // pomožne funkcije - // helper functions - - - // Column tests - // Here's a fun thing. I reckon this bit of code could potentially run often enough that L1/L2 cache misses - // could really start to add up (especially if I figure the RAM usage problem which causes insane RAM usage - // if you run this 30-60 times a second) - // - // so here's two functions. _columnTest3_cross has some optimization that tries to minimize cache misses, - // but the problem is that I don't actually know 100% what I'm doing so it might be pointless. It scans the - // image array line-by-line, rather than column-by-column. This has some advantages (e.g. we can end the - // search for letterbox early), and some disadvantages (the code is a mess) - // - // some time later down the line, I might actually implement _columnTest3_singleCol, which does shit in the - // opposite direction (column-by-column rather than row-by-row) - _columnTest3_cross(image, top, bottom, colsIn, colsOut, reverseSearchDirection) { - // this function is such a /r/badcode bait. - // - // this is the shit we do to avoid function calls and one extra if sentence/code repetition - // pretend I was drunk when I wrote this - let tmpI, edgeDetectCount = 0, edgeDetectColsLeft = colsIn.length; - let tmpVal = 0; - let increment, arrayStart, arrayEnd; - - let loopCond, loopComparator, loopIndex; - - if (reverseSearchDirection) { - increment = -this.conf.canvasImageDataRowLength; - // don't subtract this.conf.canvasImageDataRowLength — it has already been accounted for - // when we calculated bottom and top - arrayStart = bottom; - arrayEnd = top; - - // this is a hack so we get pointer-like things rather than values - loopCond = {compare: {i: arrayEnd}, index: {i: 0}} - loopComparator = loopCond.index; - loopIndex = loopCond.compare; - } else { - increment = this.conf.canvasImageDataRowLength; - arrayStart = top; - arrayEnd = bottom; - - // this is a hack so we get pointer-like things rather than values - loopCond = {compare: {i: arrayEnd}, index: {i: 0}} - loopComparator = loopCond.compare; - loopIndex = loopCond.index; - } - - // keep temporary column data in a separate column array: - const colsTmp = new Array(colsIn.length); - for (let i = 0; i < colsTmp.length; i++) { - colsTmp[i] = { - col: -1, - blackFound: false, - imageFound: false, // misleading name — also true if we ran over gradientSampleSize pixels from image - // whether that actually count as an image depends on how aggressive gradientDetection is - hasGradient: false, - blackRow: -1, - imageRow: -1, - lastValue: -1, - diffIndex: 0, - diffs: new Array(this.settings.active.arDetect.blackbar.gradientSampleSize).fill(0) - } - } - - // Things to keep in mind: loopCond.index.i is always index. - // loopIndex.i could actually be loopCond.compare.i (comparator) and - // loopComparator.i could actually be loopCond.index.i (real index) - for (loopCond.index.i = arrayStart; loopIndex.i < loopComparator.i; loopCond.index.i += increment) { - - // če smo našli toliko mejnih točk, da s preostalimi stolpci ne moremo doseči naše meje, potem prenehamo - // if we found enough edge points so that we couldn't top that limit with remaining columns, then we stop - // searching forward - edgeDetectColsLeft -= edgeDetectCount; - if (edgeDetectColsLeft < this.colsThreshold || edgeDetectCount >= this.colsThreshold) { - break; - } - edgeDetectCount = 0; - - - // če v eni vrstici dobimo dovolj točk, ki grejo čez našo mejo zaznavanja, potem bomo nehali - // the first line that goes over our detection treshold wins - for (let c = 0; c < colsIn.length; c++) { - - // there's really no point in checking this column if we already found image point - if (colsTmp[c].imageFound) { - continue; - } - - tmpI = loopCond.index.i + (colsIn[c].value << 2); - - // najprej preverimo, če je piksel presegel mejo črnega robu - // first we check whether blackbarThreshold was exceeded - if (! colsTmp[c].blackFound) { - if( image[tmpI] > this.blackbarThreshold || - image[tmpI + 1] > this.blackbarThreshold || - image[tmpI + 2] > this.blackbarThreshold ){ - - colsTmp[c].col = colsIn[c].value; - colsTmp[c].blackFound = true; - colsTmp[c].blackRow = ~~(loopCond.index.i / this.conf.canvasImageDataRowLength); - - // prisili, da se zanka izvede še enkrat ter preveri, - // ali trenuten piksel preseže tudi imageThreshold - // - // force the loop to repeat this step and check whether - // current pixel exceeds imageThreshold as well - c--; - continue; - } - } else { - // če smo dobili piksel, ki presega blackbar, preverimo do gradientSampleSize dodatnih pikslov. - // ko dobimo piksel čez imageTreshold oz. gradientSampleSize, nastavimo imageFound. Ali je to veljavno - // bomo preverili v koraku analize, ki sledi kasneje - // - // if we found a pixel that exceeds blackbar, we check up to gradientSampleSize additional pixels. - // when we get a pixel over imageTreshold or gradientSampleSize, we flip the imageFound. We'll bother - // with whether that's legit in analysis step, which will follow soon (tm) - - if (image[tmpI] > this.imageThreshold || - image[tmpI + 1] > this.imageThreshold || - image[tmpI + 2] > this.imageThreshold ){ - - colsTmp[c].imageRow = ~~(loopCond.index.i / this.conf.canvasImageDataRowLength) - - - colsTmp[c].imageFound = true; - edgeDetectCount++; - } - - // v vsakem primeru shranimo razliko med prejšnjim in trenutnim pikslom za kasnejšo analizo - // in any case, save the difference between the current and the previous pixel for later analysis - - colsTmp[c].lastValue = image[tmpI] + image[tmpI+1] + image[tmpI+2]; - if (colsTmp[c].diffIndex !== 0) { - colsTmp[c].diffs[colsTmp[c].diffIndex] = colsTmp[c].lastValue - colsTmp[c].diffs[colsTmp[c].diffIndex - 1]; - } - - colsTmp[c].diffIndex++; - if (colsTmp[c].diffIndex > this.settings.active.arDetect.blackbar.gradientSampleSize) { - colsTmp[c].imageFound = true; - continue; - } - - } - } - } - - // sprocesirajmo rezultate - // let's process our results - for (const c of colsTmp) { - if (c.blackFound) { - if (this.settings.active.arDetect.blackbar.antiGradientMode === AntiGradientMode.Disabled) { - // if gradient detection is disabled, we treat such columns as detections/not gradient - } - if (c.imageFound) { - if (c.imageRow - c.blackRow <= this.settings.active.arDetect.blackbar.gradientThreshold) { - // this is within our margin of error. Colums like this are auto-accepted - colsOut.push({ - col: c.col, - black: c.blackRow - }); - continue; - } else { - tmpVal = 0; - - let i; - // if we detected gradient, we'll analyse whether gradient is legit - for (i = 0; i < c.diffIndex; i++) { - tmpVal += c.diffs[i]; - - // if difference is negative, we aren't looking at a gradient - if (c.diffs[i] < this.settings.active.arDetect.blackbar.gradientNegativeTreshold) { - colsOut.push({ - col: c.col, - black: c.blackRow - }); - break; - } - - // if difference is too big, we assume we aren't looking at a gradient - if (c.diffs[i] > this.settings.active.arDetect.blackbar.maxGradient) { - colsOut.push({ - col: c.col, - black: c.blackRow - }); - break; - } - - // in case neither of the previous options happens, we might have a gradient. - // Since this column is sus af, we don't include it for further examination/ - // determining aspect ratio - } - - // if we didn't find any "not a gradient" diffs, we check for standard deviation - if (i >= c.diffIndex && c.diffIndex > 1) { - tmpVal /= c.diffIndex; // tmpVal is now average - let squareSum = 0, stdev = 0; - for (i = 0; i < c.diffIndex; i++) { - squareSum += Math.pow((c.diffs[i] - tmpVal), 2) - } - stdev = Math.sqrt((squareSum / (c.diffIndex - 1))); - - // if standard deviation is too big, we're not on a gradient (prolly) - if (stdev > this.settings.active.arDetect.blackbar.gradientMaxSD) { - colsOut.push({ - col: c.col, - black: c.blackRow - }); - continue; - } else { - // this means we're on a gradient. We still push an object to colsOut — this is - // important. While "bad" detection doesn't help us with determining the aspect - // ratio, bad detections can prevent us from setting aspect ratio incorrectly. - // for example, if we detect a gradient closer to the frame edge than a proper - // edge, we still know that cropping based on the confirmed edges would crop too - // much of the frame. In situations like this, we must ignore the results — and - // since results are handled outside of this function, we need to pass an - // additional parameter that allows us to distinguish real results from noise. - colsOut.push({ - col: c.col, - black: c.blackRow, - hasGradient: true, - }); - continue; - } - } else { - // in this case, we aren't looking at a gradient. We also aren't looking at a - // valid column - continue; - } - } - } else { - // we have blackbar but we haven't found a point that goes over imageTreshold. - // how these cases are handled is determiend by what antiGradientMode we're using. - // strict mode — treat as gradient. Lax mode — treat as not gradient - if (this.settings.active.arDetect.blackbar.antiGradientMode === AntiGradientMode.Lax) { - colsOut.push({ - col: c.col, - black: c.blackRow - }); - continue; - } else { - colsOut.push({ - col: c.col, - black: c.blackRow, - hasGradient: true, - }); - continue; - } - } - } - } - } - - _columnTest3_singleCol(image, top, bottom, colsIn, colsOut, reverseSearchDirection) { - - } - - _columnTest2(image, top, bottom, colsIn, colsOut, reverseSearchDirection) { - let tmpI; - let lastTmpI = 0; - let edgeDetectCount = 0; - for(const c of colsOut) { - c.diffs = []; - } - if (reverseSearchDirection) { - if (this.settings.active.arDetect.blackbar.antiGradientMode === AntiGradientMode.Disabled) { - // todo: remove gradient detection code from this branch - for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){ - for(let c = 0; c < colsIn.length; c++){ - if (colsIn[c].blackFound && colsIn[c].imageFound) { - // če smo našli obe točki, potem ne pregledujemo več. - // if we found both points, we don't continue anymore - continue; - } - tmpI = i + (colsIn[c].value << 2); - - // najprej preverimo, če je piksel presegel mejo črnega robu - // first we check whether blackbarThreshold was exceeded - if(! colsIn[c].blackFound) { - if( image[tmpI] > this.blackbarThreshold || - image[tmpI + 1] > this.blackbarThreshold || - image[tmpI + 2] > this.blackbarThreshold ){ - - colsOut[c].black = (i / this.conf.canvasImageDataRowLength) - 1; - colsOut[c].col = colsIn[c].value; - colsIn[c].blackFound = 1; - - // prisili, da se zanka izvede še enkrat ter preveri, - // ali trenuten piksel preseže tudi imageThreshold - // - // force the loop to repeat this step and check whether - // current pixel exceeds imageThreshold as well - c--; - continue; - } - } else { - if (colsIn[c].blackFound++ > this.settings.active.arDetect.blackbar.gradientSampleSize) { - colsIn[c].imageFound = true; - continue; - } - // zatem preverimo, če je piksel presegel mejo, po kateri sklepamo, da - // predstavlja sliko. Preverimo samo, če smo v stolpcu že presegli - // blackThreshold - // - // then we check whether pixel exceeded imageThreshold - if (image[tmpI] > this.imageThreshold || - image[tmpI + 1] > this.imageThreshold || - image[tmpI + 2] > this.imageThreshold ){ - - colsOut[c].image = (i / this.conf.canvasImageDataRowLength) - colsIn[c].imageFound = true; - edgeDetectCount++; - } - } - } - if(edgeDetectCount >= this.colsThreshold) { - break; - } - } - } else { - // anti-gradient detection - for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){ - for(let c = 0; c < colsIn.length; c++){ - if (colsIn[c].blackFound && colsIn[c].imageFound) { - // če smo našli obe točki, potem ne pregledujemo več. - // if we found both points, we don't continue anymore. - - if (colsIn[c].analysisDone) { - continue; - } - - if (colsOut[c].diffs.length < 5) { - colsIn[c].analysisDone = true; - } - - // average analysis — if steps between pixels are roughly equal, we're looking at a gradient - let sum_avg = 0; - for (let i = 2; i <= colsOut[c].diffs; i++) { - sum_avg += colsOut[c].diffs[i-1] - colsOut[c].diffs[i]; - } - sum_avg /= colsOut[c].diffs.length - 2; - - for (let i = 2; i <= colsOut[c].diffs; i++) { - sum_avg += colsOut[c].diffs[i-1] - colsOut[c].diffs[i]; - } - - continue; - } - - tmpI = i + (colsIn[c].value << 2); - - // najprej preverimo, če je piksel presegel mejo črnega robu - // first we check whether blackbarThreshold was exceeded - if(! colsIn[c].blackFound) { - if( image[tmpI] > this.blackbarThreshold || - image[tmpI + 1] > this.blackbarThreshold || - image[tmpI + 2] > this.blackbarThreshold ){ - - colsOut[c].black = (i / this.conf.canvasImageDataRowLength) - 1; - colsOut[c].col = colsIn[c].value; - colsIn[c].blackFound = 1; - - // prisili, da se zanka izvede še enkrat ter preveri, - // ali trenuten piksel preseže tudi imageThreshold - // - // force the loop to repeat this step and check whether - // current pixel exceeds imageThreshold as well - c--; - colsOut[c].lastImageValue = image[tmpI] + image[tmpI+1] + image[tmpI+2]; - continue; - } - } else { - // če smo dobili piksel, ki presega blackbar, preverimo do gradientSampleSize dodatnih pikslov. - // ko dobimo piksel čez imageTreshold oz. gradientSampleSize, izračunamo ali gre za gradient. - if (colsIn[c].blackFound++ > this.settings.active.arDetect.blackbar.gradientSampleSize) { - colsIn[c].imageFound = true; - continue; - } - // zatem preverimo, če je piksel presegel mejo, po kateri sklepamo, da - // predstavlja sliko. Preverimo samo, če smo v stolpcu že presegli - // blackThreshold - // - // then we check whether pixel exceeded imageThreshold - if (image[tmpI] > this.imageThreshold || - image[tmpI + 1] > this.imageThreshold || - image[tmpI + 2] > this.imageThreshold ){ - - colsOut[c].image = (i / this.conf.canvasImageDataRowLength) - - - colsIn[c].imageFound = true; - edgeDetectCount++; - } - - - // shranimo razliko med prejšnjim in trenutnim pikslom za kasnejšo analizo - // save difference between current and previous pixel for later analysis - const imageValue = image[tmpI] + image[tmpI+1] + image[tmpI+2]; - colsOut[c].diffs.push(imageValue - colsOut[c].lastImage); - colsOut[c].lastImageValue = imageValue; - } - } - if(edgeDetectCount >= this.colsThreshold) { - break; - } - } - } - } else { - for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){ - for(let c = 0; c < colsIn.length; c++){ - if (colsIn[c].blackFound && colsIn[c].imageFound) { - // če smo našli obe točki, potem ne pregledujemo več. - // if we found both points, we don't continue anymore - continue; - } - tmpI = i + (colsIn[c].value << 2); - - // najprej preverimo, če je piksel presegel mejo črnega robu - // first we check whether blackbarThreshold was exceeded - if(! colsIn[c].blackFound) { - if( image[tmpI] > this.blackbarThreshold || - image[tmpI + 1] > this.blackbarThreshold || - image[tmpI + 2] > this.blackbarThreshold ){ - - colsOut[c].black = (i / this.conf.canvasImageDataRowLength); - colsOut[c].col = colsIn[c].value; - colsIn[c].blackFound = true; - - // prisili, da se zanka izvede še enkrat ter preveri, - // ali trenuten piksel preseže tudi imageThreshold - // - // force the loop to repeat this step and check whether - // current pixel exceeds imageThreshold as well - c--; - continue; - } - } else { - if (colsIn[c].blackFound++ > this.settings.active.arDetect.blackbar.gradientSampleSize) { - colsIn[c].imageFound = true; - continue; - } - // zatem preverimo, če je piksel presegel mejo, po kateri sklepamo, da - // predstavlja sliko. Preverimo samo, če smo v stolpcu že presegli - // blackThreshold - // - // then we check whether pixel exceeded imageThreshold - if (image[tmpI] > this.imageThreshold || - image[tmpI + 1] > this.imageThreshold || - image[tmpI + 2] > this.imageThreshold ){ - - colsOut[c].image = (i / this.conf.canvasImageDataRowLength) - colsIn[c].imageFound = true; - edgeDetectCount++; - } - } - } - if(edgeDetectCount >= this.colsThreshold) { - break; - } - } - } - - } - - _columnTest(image, top: number, bottom: number, colsIn, colsOut, reverseSearchDirection){ - let tmpI; - if(reverseSearchDirection){ - for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){ - for(const col of colsIn){ - tmpI = i + (col << 2); - - if( image[tmpI] > this.blackbarThreshold || - image[tmpI + 1] > this.blackbarThreshold || - image[tmpI + 2] > this.blackbarThreshold ){ - - const bottom = (i / this.conf.canvasImageDataRowLength) + 1; - colsOut.push({ - col: col, - bottom: bottom - }); - colsIn.splice(colsIn.indexOf(col), 1); - } - } - if(colsIn.length < this.colsThreshold) - break; - } - } else { - for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){ - for(const col of colsIn){ - tmpI = i + (col << 2); - - if( image[tmpI] > this.blackbarThreshold || - image[tmpI + 1] > this.blackbarThreshold || - image[tmpI + 2] > this.blackbarThreshold ){ - - colsOut.push({ - col: col, - top: (i / this.conf.canvasImageDataRowLength) - 1 - }); - colsIn.splice(colsIn.indexOf(col), 1); - } - } - if(colsIn.length < this.colsThreshold) - break; - } - } - } - - // _columnTest_dbgc(image, top, bottom, colsIn, colsOut, reverseSearchDirection){ - // let tmpI; - // if(reverseSearchDirection){ - // for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){ - // for(let col of colsIn){ - // tmpI = i + (col << 2); - - // if( image[tmpI] > this.blackbarThreshold || - // image[tmpI + 1] > this.blackbarThreshold || - // image[tmpI + 2] > this.blackbarThreshold ){ - - // let bottom = (i / this.conf.canvasImageDataRowLength) + 1; - // colsOut.push({ - // col: col, - // bottom: bottom - // }); - // colsIn.splice(colsIn.indexOf(col), 1); - // this.conf.debugCanvas.trace(tmpI,DebugCanvasClasses.EDGEDETECT_CANDIDATE); - // } - // else{ - // this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK); - // } - // } - // if(colsIn.length < this.colsThreshold) - // break; - // } - // } else { - // for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){ - // for(let col of colsIn){ - // tmpI = i + (col << 2); - - // if( image[tmpI] > this.blackbarThreshold || - // image[tmpI + 1] > this.blackbarThreshold || - // image[tmpI + 2] > this.blackbarThreshold ){ - - // colsOut.push({ - // col: col, - // top: (i / this.conf.canvasImageDataRowLength) - 1 - // }); - // colsIn.splice(colsIn.indexOf(col), 1); - // this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_CANDIDATE); - // if(tmpI-1 > 0){ - // this.conf.debugCanvas.trace(tmpI - 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY); - // } - // if(tmpI+1 < image.length){ - // this.conf.debugCanvas.trace(tmpI + 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY); - // } - // } else { - // this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK); - // } - // } - // if(colsIn.length < this.colsThreshold) - // break; - // } - // } - // } - - _blackbarTest(image, start, end){ - for(let i = start; i < end; i += 4){ - if( image[i ] > this.blackbarThreshold || - image[i+1] > this.blackbarThreshold || - image[i+2] > this.blackbarThreshold ){ - return true; - } - } - return false; // no violation - } - - // _blackbarTest_dbg(image, start, end){ - // for(let i = start; i < end; i += 4){ - // if( image[i ] > this.blackbarThreshold || - // image[i+1] > this.blackbarThreshold || - // image[i+2] > this.blackbarThreshold ){ - - // this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION) - // return true; - // } else { - // this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_BLACKBAR) - // } - // } - // return false; // no violation - // } - - _imageTest(image, start, end, sampleOffset, edgeCandidates){ - let detections = 0; - - for (let i = start; i < end; i += 4){ - if (image[i ] > this.blackbarThreshold || - image[i+1] > this.blackbarThreshold || - image[i+2] > this.blackbarThreshold ){ - ++detections; - } - } - if(detections >= this.detectionThreshold){ - if(edgeCandidates[sampleOffset] != undefined) - edgeCandidates[sampleOffset].count++; - else{ - edgeCandidates[sampleOffset] = {offset: sampleOffset, count: 1}; - edgeCandidates.count++; - } - } - } - - // _imageTest_dbg(image, start, end, sampleOffset, edgeCandidates){ - // let detections = 0; - - // for(let i = start; i < end; i += 4){ - // if( image[i ] > this.blackbarThreshold || - // image[i+1] > this.blackbarThreshold || - // image[i+2] > this.blackbarThreshold ){ - // ++detections; - // this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_IMAGE); - // } else { - // this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN); - // } - // } - // if(detections >= this.detectionThreshold){ - // if(edgeCandidates[sampleOffset] != undefined) - // edgeCandidates[sampleOffset].count++; - // else{ - // edgeCandidates[sampleOffset] = {offset: sampleOffset, count: 1}; - // edgeCandidates.count++; - // } - // } - // } - -} - -export default EdgeDetect; diff --git a/src/ext/lib/ar-detect/edge-detect/enums/EdgeDetectPrimaryDirectionEnum.ts b/src/ext/lib/ar-detect/edge-detect/enums/EdgeDetectPrimaryDirectionEnum.ts deleted file mode 100644 index 23d2977..0000000 --- a/src/ext/lib/ar-detect/edge-detect/enums/EdgeDetectPrimaryDirectionEnum.ts +++ /dev/null @@ -1,6 +0,0 @@ -enum EdgeDetectPrimaryDirection { - Vertical = 0, - Horizontal = 1 -}; - -export default EdgeDetectPrimaryDirection; diff --git a/src/ext/lib/ar-detect/edge-detect/enums/EdgeDetectQualityEnum.ts b/src/ext/lib/ar-detect/edge-detect/enums/EdgeDetectQualityEnum.ts deleted file mode 100644 index 4d1882b..0000000 --- a/src/ext/lib/ar-detect/edge-detect/enums/EdgeDetectQualityEnum.ts +++ /dev/null @@ -1,6 +0,0 @@ -enum EdgeDetectQuality { - Fast = 0, - Improved = 1 -}; - -export default EdgeDetectQuality; diff --git a/src/ext/lib/ar-detect/edge-detect/enums/EdgeStatusEnum.ts b/src/ext/lib/ar-detect/edge-detect/enums/EdgeStatusEnum.ts deleted file mode 100644 index 5a1b66d..0000000 --- a/src/ext/lib/ar-detect/edge-detect/enums/EdgeStatusEnum.ts +++ /dev/null @@ -1,6 +0,0 @@ -enum EdgeStatus { - ARUnknown = 0, - ARKnown = 1, -}; - -export default EdgeStatus;