2018-12-31 01:03:07 +01:00
import Debug from '../../../conf/Debug' ;
import EdgeStatus from './enums/EdgeStatusEnum' ;
import EdgeDetectQuality from './enums/EdgeDetectQualityEnum' ;
import EdgeDetectPrimaryDirection from './enums/EdgeDetectPrimaryDirectionEnum' ;
2021-02-08 23:04:54 +01:00
import AntiGradientMode from '../../../../common/enums/AntiGradientMode.enum' ;
2021-02-18 22:29:23 +01:00
import ArDetector from '../ArDetector' ;
import Logger from '../../Logger' ;
import Settings from '../../Settings' ;
2018-05-13 13:49:25 +02:00
2019-02-21 21:21:25 +01:00
if ( Debug . debug ) {
console . log ( "Loading EdgeDetect.js" ) ;
}
2018-05-11 00:49:50 +02:00
class EdgeDetect {
2021-02-18 22:29:23 +01:00
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 ;
2018-05-11 00:49:50 +02:00
constructor ( ardConf ) {
this . conf = ardConf ;
2019-09-03 00:48:18 +02:00
this . logger = ardConf . logger ;
2018-08-05 23:48:56 +02:00
this . settings = ardConf . settings ;
this . sampleWidthBase = this . settings . active . arDetect . edgeDetection . sampleWidth << 2 ; // corrected so we can work on imageData
2018-05-11 00:49:50 +02:00
this . halfSample = this . sampleWidthBase >> 1 ;
2019-02-22 23:02:48 +01:00
this . detectionThreshold = this . settings . active . arDetect . edgeDetection . detectionThreshold ;
2018-05-11 00:49:50 +02:00
this . init ( ) ; // initiate things that can change
}
// initiates things that we may have to change later down the line
init() {
}
2021-03-06 04:01:47 +01:00
findBars ( image , sampleCols , direction = EdgeDetectPrimaryDirection . Vertical , quality = EdgeDetectQuality . Improved , guardLineOut ? , blackFrameAnalysis ? ) {
2019-05-02 22:43:40 +02:00
let fastCandidates , edgeCandidates , bars ;
2021-03-06 04:01:47 +01:00
if ( direction == EdgeDetectPrimaryDirection . Vertical ) {
2019-05-05 00:09:49 +02:00
try {
fastCandidates = this . findCandidates ( image , sampleCols , guardLineOut ) ;
if ( ! this . isValidSample ( fastCandidates ) ) {
2021-03-06 04:01:47 +01:00
return { status : EdgeStatus.ARUnknown } ;
2019-05-05 00:09:49 +02:00
}
// if(quality == EdgeDetectQuality.FAST){
// edges = fastCandidates; // todo: processing
// } else {
edgeCandidates = this . edgeDetect ( image , fastCandidates ) ;
2021-02-18 22:29:23 +01:00
bars = this . edgePostprocess ( edgeCandidates ) ;
2019-05-05 00:09:49 +02:00
// }
} catch ( e ) {
2019-09-03 00:48:18 +02:00
this . logger . log ( 'error' , 'arDetect' , '%c[EdgeDetect::findBars] find bars failed.' , 'background: #f00, color: #000' , e ) ;
2021-03-06 04:01:47 +01:00
return { status : EdgeStatus.ARUnknown }
2019-05-05 00:09:49 +02:00
}
2018-05-12 01:51:43 +02:00
} else {
2021-03-06 04:01:47 +01:00
bars = this . pillarTest ( image ) ? { status : EdgeStatus.ARKnown } : { status : EdgeStatus.ARUnknown } ;
2018-05-11 00:49:50 +02:00
}
2018-05-12 01:51:43 +02:00
return bars ;
2019-05-05 00:09:49 +02:00
2018-05-11 00:49:50 +02:00
}
findCandidates ( image , sampleCols , guardLineOut ) {
2019-02-21 21:21:25 +01:00
try {
2019-09-03 00:48:18 +02:00
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 ] ,
} ;
}
2019-02-21 21:21:25 +01:00
2019-09-03 00:48:18 +02:00
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 . canvas . 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 . canvas . height >> 3 ) ;
lower_top = this . conf . guardLine . blackbar . bottom - ( this . conf . canvas . height >> 3 ) ;
lower_bottom = this . conf . canvas . height - 1 ;
} else {
upper_top = 0 ;
upper_bottom = ( this . conf . canvas . height >> 1 ) /*- parseInt(this.conf.canvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/
lower_top = ( this . conf . canvas . height >> 1 ) /*+ parseInt(this.conf.canvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/
lower_bottom = this . conf . canvas . height - 1 ;
}
} else {
2018-05-11 00:49:50 +02:00
upper_top = 0 ;
2018-08-05 23:48:56 +02:00
upper_bottom = ( this . conf . canvas . height >> 1 ) /*- parseInt(this.conf.canvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/
lower_top = ( this . conf . canvas . height >> 1 ) /*+ parseInt(this.conf.canvas.height * this.settings.active.arDetect.edgeDetection.middleIgnoredArea);*/
2018-05-12 01:51:43 +02:00
lower_bottom = this . conf . canvas . height - 1 ;
2018-05-11 00:49:50 +02:00
}
2019-09-03 00:48:18 +02:00
this . logger . log ( 'info' , 'arDetect' , '[EdgeDetect::findCandidates] searching for candidates on ranges' , upper_top , '<->' , upper_bottom , ';' , lower_top , '<->' , lower_bottom ) ;
2021-02-18 22:38:32 +01:00
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 ;
2019-09-03 00:48:18 +02:00
// 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 } ) ;
2018-05-11 00:49:50 +02:00
2019-09-03 00:48:18 +02:00
return { res_top : res_top , res_bottom : res_bottom } ;
2019-02-21 21:21:25 +01:00
} catch ( e ) {
2019-09-03 00:48:18 +02:00
this . logger . log ( 'error' , 'debug' , '[EdgeDetect::findCandidates] there was an error while finding candidates:' , e ) ;
2019-02-21 21:21:25 +01:00
}
2018-05-11 00:49:50 +02:00
}
// dont call the following outside of this class
2019-05-04 21:33:48 +02:00
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.canvas.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 . canvas . 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 . canvas . height - bottomPoint . row ) ;
}
return false ;
}
2018-05-11 00:49:50 +02:00
edgeDetect ( image , samples ) {
2021-02-18 22:38:32 +01:00
let edgeCandidatesTop = { count : 0 } ;
let edgeCandidatesBottom = { count : 0 } ;
2018-05-11 00:49:50 +02:00
2021-02-18 22:38:32 +01:00
let detections ;
let canvasWidth = this . conf . canvas . width ;
let canvasHeight = this . conf . canvas . height ;
2018-05-11 00:49:50 +02:00
2021-02-18 22:38:32 +01:00
let sampleStart , sampleEnd , loopEnd ;
let sampleRow_black , sampleRow_color ;
2018-05-11 00:49:50 +02:00
2021-02-18 22:38:32 +01:00
let blackEdgeViolation = false ;
2018-05-11 00:49:50 +02:00
2021-02-18 22:38:32 +01:00
let topEdgeCount = 0 ;
let bottomEdgeCount = 0 ;
2019-05-02 22:43:40 +02:00
try {
2019-08-24 22:35:56 +02:00
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 ;
2018-05-11 00:49:50 +02:00
2019-08-24 22:35:56 +02:00
if ( Debug . debugCanvas . enabled ) {
2021-02-18 22:29:23 +01:00
// blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
2019-08-24 22:35:56 +02:00
} else {
blackEdgeViolation = this . _blackbarTest ( image , sampleRow_black + sampleStart , loopEnd ) ;
}
2018-05-11 00:49:50 +02:00
2019-08-24 22:35:56 +02:00
// č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 ;
2018-05-11 00:49:50 +02:00
2019-08-24 22:35:56 +02:00
if ( Debug . debugCanvas . enabled ) {
2021-02-18 22:29:23 +01:00
// this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop)
2019-08-24 22:35:56 +02:00
} else {
this . _imageTest ( image , sampleRow_color + sampleStart , loopEnd , sample . black , edgeCandidatesTop ) ;
}
2019-05-02 22:43:40 +02:00
}
2018-05-11 00:49:50 +02:00
2019-08-24 22:35:56 +02:00
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 ) {
2021-02-18 22:29:23 +01:00
// blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
2019-08-24 22:35:56 +02:00
} 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 ;
2018-05-11 00:49:50 +02:00
2019-08-24 22:35:56 +02:00
if ( Debug . debugCanvas . enabled ) {
2021-02-18 22:29:23 +01:00
// this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom);
2019-08-24 22:35:56 +02:00
} else {
this . _imageTest ( image , sampleRow_color + sampleStart , loopEnd , sample . black , edgeCandidatesBottom ) ;
}
2018-05-11 00:49:50 +02:00
}
2019-05-02 22:43:40 +02:00
} catch ( e ) {
2019-09-03 00:48:18 +02:00
this . logger . log ( 'error' , 'debug' , '[EdgeDetect::edgeDetect] There was an error:' , e ) ;
2019-05-02 22:43:40 +02:00
}
2018-05-11 00:49:50 +02:00
return {
edgeCandidatesTop : edgeCandidatesTop ,
edgeCandidatesTopCount : edgeCandidatesTop.count ,
edgeCandidatesBottom : edgeCandidatesBottom ,
edgeCandidatesBottomCount : edgeCandidatesBottom.count
} ;
}
2018-05-13 21:05:11 +02:00
edgePostprocess ( edges ) {
2021-02-18 22:38:32 +01:00
let edgesTop = [ ] ;
let edgesBottom = [ ] ;
let alignMargin = this . conf . canvas . height * this . settings . active . arDetect . allowedMisaligned ;
2018-05-12 01:51:43 +02:00
2021-02-18 22:38:32 +01:00
let missingEdge = edges . edgeCandidatesTopCount == 0 || edges . edgeCandidatesBottomCount == 0 ;
2018-05-12 01:51:43 +02:00
// pretvorimo objekt v tabelo
// convert objects to array
2018-05-15 21:40:53 +02:00
delete ( edges . edgeCandidatesTop . count ) ;
delete ( edges . edgeCandidatesBottom . count ) ;
2018-05-12 01:51:43 +02:00
if ( edges . edgeCandidatesTopCount > 0 ) {
2021-02-18 22:38:32 +01:00
for ( let e in edges . edgeCandidatesTop ) {
let edge = edges . edgeCandidatesTop [ e ] ;
2018-05-15 21:40:53 +02:00
edgesTop . push ( {
distance : edge.offset ,
absolute : edge.offset ,
count : edge.count
} ) ;
2018-05-12 01:51:43 +02:00
}
}
if ( edges . edgeCandidatesBottomCount > 0 ) {
2021-02-18 22:38:32 +01:00
for ( let e in edges . edgeCandidatesBottom ) {
let edge = edges . edgeCandidatesBottom [ e ] ;
2018-05-15 21:40:53 +02:00
edgesBottom . push ( {
distance : this.conf.canvas.height - edge . offset ,
absolute : edge.offset ,
count : edge.count
} ) ;
2018-05-12 01:51:43 +02:00
}
}
2018-05-15 21:40:53 +02:00
// sort by distance
edgesTop = edgesTop . sort ( ( a , b ) = > { return a . distance - b . distance } ) ;
edgesBottom = edgesBottom . sort ( ( a , b ) = > { return a . distance - b . distance } ) ;
2019-06-01 23:33:21 +02:00
2018-05-12 01:51:43 +02:00
// če za vsako stran (zgoraj in spodaj) poznamo vsaj enega kandidata, potem lahko preverimo nekaj
// stvari
2019-06-01 23:33:21 +02:00
if ( ! missingEdge ) {
2018-05-12 01:51:43 +02:00
// 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 ) {
2021-02-18 22:38:32 +01:00
let blackbarWidth = edgesTop [ 0 ] . distance > edgesBottom [ 0 ] . distance ?
2018-05-12 01:51:43 +02:00
edgesTop [ 0 ] . distance : edgesBottom [ 0 ] . distance ;
2019-06-02 00:34:37 +02:00
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 ) ) {
2019-06-01 23:33:21 +02:00
return {
2021-03-06 04:01:47 +01:00
status : EdgeStatus.ARKnown ,
2019-06-01 23:33:21 +02:00
blackbarWidth : blackbarWidth ,
guardLineTop : edgesTop [ 0 ] . distance ,
guardLineBottom : edgesBottom [ 0 ] . absolute ,
top : edgesTop [ 0 ] . distance ,
bottom : edgesBottom [ 0 ] . distance
} ;
}
2018-05-12 01:51:43 +02:00
}
// 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.
2019-06-01 23:33:21 +02:00
if ( edgesTop [ 0 ] . distance < edgesBottom [ 0 ] . distance &&
2018-05-12 01:51:43 +02:00
edgesTop [ 0 ] . count < edgesBottom [ 0 ] . count &&
2019-02-22 23:02:48 +01:00
edgesTop [ 0 ] . count < this . conf . sampleCols . length * this . settings . active . arDetect . edgeDetection . logoThreshold ) {
2018-05-12 01:51:43 +02:00
// 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.
2019-06-01 23:33:21 +02:00
if ( edgesTop [ 0 ] . length > 1 ) {
2021-02-18 22:38:32 +01:00
let lowMargin = edgesBottom [ 0 ] . distance - alignMargin ;
let highMargin = edgesBottom [ 0 ] . distance + alignMargin ;
2018-05-12 01:51:43 +02:00
2021-02-18 22:38:32 +01:00
for ( let i = 1 ; i < edgesTop . length ; i ++ ) {
2018-05-12 01:51:43 +02:00
if ( edgesTop [ i ] . distance >= lowMargin && edgesTop [ i ] . distance <= highMargin ) {
// dobili smo dejanski rob. vrnimo ga
// we found the actual edge. let's return that.
2021-02-18 22:38:32 +01:00
let blackbarWidth = edgesTop [ i ] . distance > edgesBottom [ 0 ] . distance ?
2018-05-12 01:51:43 +02:00
edgesTop [ i ] . distance : edgesBottom [ 0 ] . distance ;
2019-06-01 23:33:21 +02:00
2019-06-02 00:34:37 +02:00
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 ) ) {
2019-06-01 23:33:21 +02:00
return {
2021-03-06 04:01:47 +01:00
status : EdgeStatus.ARKnown ,
2019-06-01 23:33:21 +02:00
blackbarWidth : blackbarWidth ,
guardLineTop : edgesTop [ i ] . distance ,
guardLineBottom : edgesBottom [ 0 ] . absolute ,
top : edgesTop [ i ] . distance ,
bottom : edgesBottom [ 0 ] . distance
} ;
}
2018-05-12 01:51:43 +02:00
}
}
}
}
2019-06-01 23:33:21 +02:00
if ( edgesBottom [ 0 ] . distance < edgesTop [ 0 ] . distance &&
2018-05-12 01:51:43 +02:00
edgesBottom [ 0 ] . count < edgesTop [ 0 ] . count &&
2019-06-01 23:33:21 +02:00
edgesBottom [ 0 ] . count < this . conf . sampleCols . length * this . settings . active . arDetect . edgeDetection . logoThreshold ) {
2018-05-12 01:51:43 +02:00
if ( edgesBottom [ 0 ] . length > 1 ) {
2021-02-18 22:38:32 +01:00
let lowMargin = edgesTop [ 0 ] . distance - alignMargin ;
let highMargin = edgesTop [ 0 ] . distance + alignMargin ;
2018-05-12 01:51:43 +02:00
2021-02-18 22:38:32 +01:00
for ( let i = 1 ; i < edgesBottom . length ; i ++ ) {
2019-06-01 23:33:21 +02:00
if ( edgesBottom [ i ] . distance >= lowMargin && edgesTop [ i ] . distance <= highMargin ) {
2018-05-12 01:51:43 +02:00
// dobili smo dejanski rob. vrnimo ga
// we found the actual edge. let's return that.
2021-02-18 22:38:32 +01:00
let blackbarWidth = edgesBottom [ i ] . distance > edgesTop [ 0 ] . distance ?
2018-05-12 01:51:43 +02:00
edgesBottom [ i ] . distance : edgesTop [ 0 ] . distance ;
2019-06-02 00:34:37 +02:00
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 ) ) {
2019-06-01 23:33:21 +02:00
return {
2021-03-06 04:01:47 +01:00
status : EdgeStatus.ARKnown ,
2019-06-01 23:33:21 +02:00
blackbarWidth : blackbarWidth ,
guardLineTop : edgesTop [ 0 ] . distance ,
guardLineBottom : edgesBottom [ i ] . absolute ,
top : edgesTop [ 0 ] . distance ,
bottom : edgesBottom [ i ] . distance
} ;
}
2018-05-12 01:51:43 +02:00
}
}
}
}
}
2019-06-01 23:33:21 +02:00
else {
2018-05-12 01:51:43 +02:00
// 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.
2019-06-02 00:34:37 +02:00
const edgeDetectionThreshold = this . settings . active . arDetect . edgeDetection . singleSideConfirmationThreshold ;
2018-05-12 01:51:43 +02:00
2019-06-01 23:33:21 +02:00
if ( edges . edgeCandidatesTopCount == 0 && edges . edgeCandidatesBottomCount != 0 ) {
2021-02-18 22:38:32 +01:00
for ( let edge of edgesBottom ) {
2019-02-22 23:02:48 +01:00
if ( edge . count >= edgeDetectionThreshold )
2018-05-15 21:40:53 +02:00
return {
2021-03-06 04:01:47 +01:00
status : EdgeStatus.ARKnown ,
2018-05-15 21:40:53 +02:00
blackbarWidth : edge.distance ,
guardLineTop : null ,
guardLineBottom : edge.bottom ,
top : edge.distance ,
bottom : edge.distance
}
2018-05-12 01:51:43 +02:00
}
}
2019-06-01 23:33:21 +02:00
if ( edges . edgeCandidatesTopCount != 0 && edges . edgeCandidatesBottomCount == 0 ) {
2021-02-18 22:38:32 +01:00
for ( let edge of edgesTop ) {
2019-02-22 23:02:48 +01:00
if ( edge . count >= edgeDetectionThreshold )
2018-05-15 21:40:53 +02:00
return {
2021-03-06 04:01:47 +01:00
status : EdgeStatus.ARKnown ,
2018-05-15 21:40:53 +02:00
blackbarWidth : edge.distance ,
guardLineTop : edge.top ,
guardLineBottom : null ,
top : edge.distance ,
bottom : edge.distance
}
2018-05-12 01:51:43 +02:00
}
}
}
// č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.
2021-03-06 04:01:47 +01:00
return { status : EdgeStatus.ARUnknown }
2018-05-12 01:51:43 +02:00
}
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
2021-02-18 22:38:32 +01:00
let blackbarThreshold , upper , lower ;
2019-02-22 23:02:48 +01:00
blackbarThreshold = this . conf . blackLevel + this . settings . active . arDetect . blackbar . threshold ;
2018-05-12 01:51:43 +02:00
2021-02-18 22:38:32 +01:00
let middleRowStart = ( this . conf . canvas . height >> 1 ) * this . conf . canvas . width ;
let middleRowEnd = middleRowStart + this . conf . canvas . width - 1 ;
2018-05-12 01:51:43 +02:00
2021-02-18 22:38:32 +01:00
let rowStart = middleRowStart << 2 ;
let midpoint = ( middleRowStart + ( this . conf . canvas . width >> 1 ) ) << 2
let rowEnd = middleRowEnd << 2 ;
2018-05-12 01:51:43 +02:00
2021-02-18 22:38:32 +01:00
let edge_left = - 1 , edge_right = - 1 ;
2018-05-12 01:51:43 +02:00
// preverimo na levi strani
// let's check for edge on the left side
2021-02-18 22:38:32 +01:00
for ( let i = rowStart ; i < midpoint ; i += 4 ) {
2019-02-22 23:02:48 +01:00
if ( image [ i ] > blackbarThreshold || image [ i + 1 ] > blackbarThreshold || image [ i + 2 ] > blackbarThreshold ) {
2018-05-12 01:51:43 +02:00
edge_left = ( i - rowStart ) >> 2 ;
break ;
}
}
// preverimo na desni strani
// check on the right
2021-02-18 22:38:32 +01:00
for ( let i = rowEnd ; i > midpoint ; i -= 4 ) {
2019-02-22 23:02:48 +01:00
if ( image [ i ] > blackbarThreshold || image [ i + 1 ] > blackbarThreshold || image [ i + 2 ] > blackbarThreshold ) {
2018-05-12 01:51:43 +02:00
edge_right = this . conf . canvas . 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'
2018-08-05 23:48:56 +02:00
if ( edge_left < this . settings . active . arDetect . pillarTest . ignoreThinPillarsPx && edge_right < this . settings . active . arDetect . pillarTest . ignoreThinPillarsPx ) {
2018-05-12 01:51:43 +02:00
return false ;
}
2021-02-18 22:38:32 +01:00
let edgeError = this . settings . active . arDetect . pillarTest . allowMisaligned ;
let error_low = 1 - edgeError ;
let error_hi = 1 + edgeError ;
2018-05-12 01:51:43 +02:00
// č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 ;
}
2018-05-11 00:49:50 +02:00
// pomožne funkcije
// helper functions
2019-02-21 21:21:25 +01:00
2019-05-02 00:57:37 +02:00
// 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
2019-05-02 22:43:40 +02:00
let tmpI , edgeDetectCount = 0 , edgeDetectColsLeft = colsIn . length ;
2019-05-02 00:57:37 +02:00
let tmpVal = 0 ;
let increment , arrayStart , arrayEnd ;
let loopCond , loopComparator , loopIndex ;
if ( reverseSearchDirection ) {
increment = - this . conf . canvasImageDataRowLength ;
2019-08-24 22:35:56 +02:00
// don't subtract this.conf.canvasImageDataRowLength — it has already been accounted for
// when we calculated bottom and top
arrayStart = bottom ;
2019-05-02 00:57:37 +02:00
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 ] = {
2019-05-02 22:43:40 +02:00
col : - 1 ,
2019-05-02 00:57:37 +02:00
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
2019-05-04 21:33:48 +02:00
hasGradient : false ,
2019-05-02 22:43:40 +02:00
blackRow : - 1 ,
imageRow : - 1 ,
2019-05-02 00:57:37 +02:00
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 ) {
2019-05-02 22:43:40 +02:00
colsTmp [ c ] . col = colsIn [ c ] . value ;
2019-05-02 00:57:37 +02:00
colsTmp [ c ] . blackFound = true ;
2019-05-02 22:43:40 +02:00
colsTmp [ c ] . blackRow = ~ ~ ( loopCond . index . i / this . conf . canvasImageDataRowLength ) ;
2019-05-02 00:57:37 +02:00
// 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 -- ;
2019-02-21 21:21:25 +01:00
continue ;
}
2019-05-02 00:57:37 +02:00
} 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 ) {
2019-05-02 22:43:40 +02:00
colsTmp [ c ] . imageRow = ~ ~ ( loopCond . index . i / this . conf . canvasImageDataRowLength )
2019-02-21 21:21:25 +01:00
2019-05-02 00:57:37 +02:00
colsTmp [ c ] . imageFound = true ;
edgeDetectCount ++ ;
}
2019-02-21 21:21:25 +01:00
2019-05-02 00:57:37 +02:00
// 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 ) {
2019-05-02 22:43:40 +02:00
colsTmp [ c ] . diffs [ colsTmp [ c ] . diffIndex ] = colsTmp [ c ] . lastValue - colsTmp [ c ] . diffs [ colsTmp [ c ] . diffIndex - 1 ] ;
2019-05-02 00:57:37 +02:00
}
2019-05-02 22:43:40 +02:00
colsTmp [ c ] . diffIndex ++ ;
2019-05-02 00:57:37 +02:00
if ( colsTmp [ c ] . diffIndex > this . settings . active . arDetect . blackbar . gradientSampleSize ) {
colsTmp [ c ] . imageFound = true ;
continue ;
}
}
}
}
2019-05-02 22:43:40 +02:00
// 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 ;
2019-05-04 21:33:48 +02:00
} 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 ;
2019-05-02 22:43:40 +02:00
}
} else {
2019-05-04 21:33:48 +02:00
// in this case, we aren't looking at a gradient. We also aren't looking at a
// valid column
2019-05-02 22:43:40 +02:00
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 ;
2019-05-04 21:33:48 +02:00
} else {
colsOut . push ( {
col : c.col ,
black : c.blackRow ,
hasGradient : true ,
} ) ;
continue ;
2019-05-02 22:43:40 +02:00
}
}
}
}
2019-05-02 00:57:37 +02:00
}
_columnTest3_singleCol ( image , top , bottom , colsIn , colsOut , reverseSearchDirection ) {
}
_columnTest2 ( image , top , bottom , colsIn , colsOut , reverseSearchDirection ) {
let tmpI ;
let lastTmpI = 0 ;
let edgeDetectCount = 0 ;
2021-02-18 22:35:58 +01:00
for ( const c of colsOut ) {
2019-05-02 00:57:37 +02:00
c . diffs = [ ] ;
}
if ( reverseSearchDirection ) {
if ( this . settings . active . arDetect . blackbar . antiGradientMode === AntiGradientMode . Disabled ) {
// todo: remove gradient detection code from this branch
2021-02-18 22:38:32 +01:00
for ( let i = bottom - this . conf . canvasImageDataRowLength ; i >= top ; i -= this . conf . canvasImageDataRowLength ) {
2019-05-02 00:57:37 +02:00
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
2019-02-21 21:21:25 +01:00
continue ;
}
2019-05-02 00:57:37 +02:00
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
2021-02-18 22:38:32 +01:00
for ( let i = bottom - this . conf . canvasImageDataRowLength ; i >= top ; i -= this . conf . canvasImageDataRowLength ) {
2019-05-02 00:57:37 +02:00
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 ] ;
}
2019-02-21 21:21:25 +01:00
continue ;
}
2019-05-02 00:57:37 +02:00
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 ;
2019-02-21 21:21:25 +01:00
}
}
2019-05-02 00:57:37 +02:00
if ( edgeDetectCount >= this . colsThreshold ) {
break ;
}
2019-02-21 21:21:25 +01:00
}
}
} else {
2021-02-18 22:35:58 +01:00
for ( let i = top ; i < bottom ; i += this . conf . canvasImageDataRowLength ) {
2019-02-21 21:21:25 +01:00
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
2019-02-22 23:02:48 +01:00
// first we check whether blackbarThreshold was exceeded
2019-02-21 21:21:25 +01:00
if ( ! colsIn [ c ] . blackFound ) {
2019-02-22 23:02:48 +01:00
if ( image [ tmpI ] > this . blackbarThreshold ||
image [ tmpI + 1 ] > this . blackbarThreshold ||
image [ tmpI + 2 ] > this . blackbarThreshold ) {
2019-02-21 21:21:25 +01:00
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,
2019-02-22 23:02:48 +01:00
// ali trenuten piksel preseže tudi imageThreshold
2019-02-21 21:21:25 +01:00
//
// force the loop to repeat this step and check whether
2019-02-22 23:02:48 +01:00
// current pixel exceeds imageThreshold as well
2019-02-21 21:21:25 +01:00
c -- ;
continue ;
}
} else {
2019-02-21 21:51:35 +01:00
if ( colsIn [ c ] . blackFound ++ > this . settings . active . arDetect . blackbar . gradientSampleSize ) {
2019-02-21 21:21:25 +01:00
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
2019-02-22 23:02:48 +01:00
// blackThreshold
2019-02-21 21:21:25 +01:00
//
2019-02-22 23:02:48 +01:00
// then we check whether pixel exceeded imageThreshold
if ( image [ tmpI ] > this . imageThreshold ||
image [ tmpI + 1 ] > this . imageThreshold ||
image [ tmpI + 2 ] > this . imageThreshold ) {
2019-02-21 21:21:25 +01:00
colsOut [ c ] . image = ( i / this . conf . canvasImageDataRowLength )
colsIn [ c ] . imageFound = true ;
edgeDetectCount ++ ;
}
}
}
2019-02-22 23:02:48 +01:00
if ( edgeDetectCount >= this . colsThreshold ) {
2019-02-21 21:21:25 +01:00
break ;
}
}
}
}
2021-02-18 22:35:58 +01:00
_columnTest ( image , top : number , bottom : number , colsIn , colsOut , reverseSearchDirection ) {
let tmpI ;
2018-05-11 00:49:50 +02:00
if ( reverseSearchDirection ) {
2021-02-18 22:35:58 +01:00
for ( let i = bottom - this . conf . canvasImageDataRowLength ; i >= top ; i -= this . conf . canvasImageDataRowLength ) {
for ( const col of colsIn ) {
2018-05-11 00:49:50 +02:00
tmpI = i + ( col << 2 ) ;
2019-02-22 23:02:48 +01:00
if ( image [ tmpI ] > this . blackbarThreshold ||
image [ tmpI + 1 ] > this . blackbarThreshold ||
image [ tmpI + 2 ] > this . blackbarThreshold ) {
2018-05-11 00:49:50 +02:00
2021-02-18 22:35:58 +01:00
const bottom = ( i / this . conf . canvasImageDataRowLength ) + 1 ;
2018-05-11 00:49:50 +02:00
colsOut . push ( {
col : col ,
2018-05-15 21:40:53 +02:00
bottom : bottom
2018-05-11 00:49:50 +02:00
} ) ;
colsIn . splice ( colsIn . indexOf ( col ) , 1 ) ;
}
}
2019-02-22 23:02:48 +01:00
if ( colsIn . length < this . colsThreshold )
2018-05-11 00:49:50 +02:00
break ;
}
} else {
2021-02-18 22:35:58 +01:00
for ( let i = top ; i < bottom ; i += this . conf . canvasImageDataRowLength ) {
for ( const col of colsIn ) {
2018-05-11 00:49:50 +02:00
tmpI = i + ( col << 2 ) ;
2019-02-22 23:02:48 +01:00
if ( image [ tmpI ] > this . blackbarThreshold ||
image [ tmpI + 1 ] > this . blackbarThreshold ||
image [ tmpI + 2 ] > this . blackbarThreshold ) {
2018-05-11 00:49:50 +02:00
colsOut . push ( {
col : col ,
top : ( i / this . conf . canvasImageDataRowLength ) - 1
} ) ;
2018-06-27 23:55:37 +02:00
colsIn . splice ( colsIn . indexOf ( col ) , 1 ) ;
2018-05-11 00:49:50 +02:00
}
}
2019-02-22 23:02:48 +01:00
if ( colsIn . length < this . colsThreshold )
2018-05-11 00:49:50 +02:00
break ;
}
}
}
2021-02-18 22:29:23 +01:00
// _columnTest_dbgc(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
2021-02-18 22:38:32 +01:00
// let tmpI;
2021-02-18 22:29:23 +01:00
// if(reverseSearchDirection){
2021-02-18 22:38:32 +01:00
// for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
// for(let col of colsIn){
2021-02-18 22:29:23 +01:00
// tmpI = i + (col << 2);
2018-05-11 00:49:50 +02:00
2021-02-18 22:29:23 +01:00
// if( image[tmpI] > this.blackbarThreshold ||
// image[tmpI + 1] > this.blackbarThreshold ||
// image[tmpI + 2] > this.blackbarThreshold ){
2018-05-11 00:49:50 +02:00
2021-02-18 22:38:32 +01:00
// let bottom = (i / this.conf.canvasImageDataRowLength) + 1;
2021-02-18 22:29:23 +01:00
// 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 {
2021-02-18 22:38:32 +01:00
// for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
// for(let col of colsIn){
2021-02-18 22:29:23 +01:00
// tmpI = i + (col << 2);
2018-05-11 00:49:50 +02:00
2021-02-18 22:29:23 +01:00
// if( image[tmpI] > this.blackbarThreshold ||
// image[tmpI + 1] > this.blackbarThreshold ||
// image[tmpI + 2] > this.blackbarThreshold ){
2018-05-11 00:49:50 +02:00
2021-02-18 22:29:23 +01:00
// 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;
// }
// }
// }
2018-05-11 00:49:50 +02:00
_blackbarTest ( image , start , end ) {
2021-02-18 22:38:32 +01:00
for ( let i = start ; i < end ; i += 4 ) {
2019-02-22 23:02:48 +01:00
if ( image [ i ] > this . blackbarThreshold ||
image [ i + 1 ] > this . blackbarThreshold ||
image [ i + 2 ] > this . blackbarThreshold ) {
2018-05-11 00:49:50 +02:00
return true ;
}
}
return false ; // no violation
}
2021-02-18 22:29:23 +01:00
// _blackbarTest_dbg(image, start, end){
2021-02-18 22:38:32 +01:00
// for(let i = start; i < end; i += 4){
2021-02-18 22:29:23 +01:00
// if( image[i ] > this.blackbarThreshold ||
// image[i+1] > this.blackbarThreshold ||
// image[i+2] > this.blackbarThreshold ){
2018-05-11 00:49:50 +02:00
2021-02-18 22:29:23 +01:00
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION)
// return true;
// } else {
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_BLACKBAR)
// }
// }
// return false; // no violation
// }
2018-05-11 00:49:50 +02:00
2018-05-15 21:40:53 +02:00
_imageTest ( image , start , end , sampleOffset , edgeCandidates ) {
2021-02-18 22:35:58 +01:00
let detections = 0 ;
2018-05-11 00:49:50 +02:00
2021-02-18 22:35:58 +01:00
for ( let i = start ; i < end ; i += 4 ) {
2019-05-02 22:43:40 +02:00
if ( image [ i ] > this . blackbarThreshold ||
2019-02-22 23:02:48 +01:00
image [ i + 1 ] > this . blackbarThreshold ||
image [ i + 2 ] > this . blackbarThreshold ) {
2018-05-11 00:49:50 +02:00
++ detections ;
}
}
2019-02-22 23:02:48 +01:00
if ( detections >= this . detectionThreshold ) {
2018-05-15 21:40:53 +02:00
if ( edgeCandidates [ sampleOffset ] != undefined )
edgeCandidates [ sampleOffset ] . count ++ ;
2018-05-11 00:49:50 +02:00
else {
2018-05-15 21:40:53 +02:00
edgeCandidates [ sampleOffset ] = { offset : sampleOffset , count : 1 } ;
2018-05-11 00:49:50 +02:00
edgeCandidates . count ++ ;
}
}
}
2021-02-18 22:29:23 +01:00
// _imageTest_dbg(image, start, end, sampleOffset, edgeCandidates){
2021-02-18 22:38:32 +01:00
// let detections = 0;
2021-02-18 22:29:23 +01:00
2021-02-18 22:38:32 +01:00
// for(let i = start; i < end; i += 4){
2021-02-18 22:29:23 +01:00
// 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++;
// }
// }
// }
2018-05-11 00:49:50 +02:00
2018-12-31 01:03:07 +01:00
}
export default EdgeDetect ;