Finish aspect ratio check
This commit is contained in:
parent
2736ac418f
commit
7c5e4101b0
@ -165,6 +165,15 @@ export interface AardSettings {
|
|||||||
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
|
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
|
||||||
gradientTestMinDelta: number, // if last pixels of the test sample is less than this brighter than the first -> not gradient
|
gradientTestMinDelta: number, // if last pixels of the test sample is less than this brighter than the first -> not gradient
|
||||||
|
|
||||||
|
thresholds: {
|
||||||
|
edgeDetectionLimit: 8, // during scanning of the edge, quit after edge gets detected at this many points
|
||||||
|
minQualitySingleEdge: 6, // At least one of the detected must reach this quality
|
||||||
|
minQualitySecondEdge: 3, // The other edge must reach this quality (must be smaller or equal to single edge quality)
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLetterboxOffset: 0.1, // Upper and lower letterbox can be different by this many (% of height)
|
||||||
|
|
||||||
|
// Previous iteration variables VVVV
|
||||||
sampleWidth: number, // we take a sample this wide for edge detection
|
sampleWidth: number, // we take a sample this wide for edge detection
|
||||||
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
|
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
|
||||||
confirmationThreshold: number, //
|
confirmationThreshold: number, //
|
||||||
|
@ -381,7 +381,12 @@ class Aard {
|
|||||||
|
|
||||||
// STEP 3:
|
// STEP 3:
|
||||||
// If we are here, we must do full aspect ratio detection.
|
// If we are here, we must do full aspect ratio detection.
|
||||||
|
// After aspectRatioCheck is finished, we know how wide the letterbox is.
|
||||||
|
this.aspectRatioCheck(
|
||||||
|
imageData,
|
||||||
|
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width,
|
||||||
|
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
|
||||||
|
);
|
||||||
|
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
@ -964,7 +969,7 @@ class Aard {
|
|||||||
* @param height
|
* @param height
|
||||||
*/
|
*/
|
||||||
private edgeScan(imageData: Uint8Array, width: number, height: number) {
|
private edgeScan(imageData: Uint8Array, width: number, height: number) {
|
||||||
const detectionLimit = 8; // TODO: unhardcode
|
const detectionLimit = this.settings.active.arDetect.edgeDetection.thresholds.edgeDetectionLimit;
|
||||||
|
|
||||||
let mid = ~~(height / 2);
|
let mid = ~~(height / 2);
|
||||||
|
|
||||||
@ -1262,9 +1267,14 @@ class Aard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private similarityMatrix = new Uint16Array(21
|
/**
|
||||||
|
* Processes data gathered by edgeScan, validateEdgeScan, and sampleForGradient.
|
||||||
);
|
* It takes samples and determines how wide the letterbox actually is.
|
||||||
|
* @param imageData
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
private processScanResults(imageData: Uint8Array, width: number, height: number) {
|
private processScanResults(imageData: Uint8Array, width: number, height: number) {
|
||||||
/**
|
/**
|
||||||
* Few things to note —
|
* Few things to note —
|
||||||
@ -1393,7 +1403,8 @@ class Aard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determining our best edge candidate goes something like this:
|
* Determining our best edge candidate should, in theory, go
|
||||||
|
* something like this:
|
||||||
*
|
*
|
||||||
* [ start ]
|
* [ start ]
|
||||||
* |
|
* |
|
||||||
@ -1422,65 +1433,141 @@ class Aard {
|
|||||||
* Center authority.
|
* Center authority.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
* ... however ...
|
||||||
|
* In practice: if there's too much mismatch, we just label detection
|
||||||
|
* as inconclusive and do nothing. Not paid enough to figure out the
|
||||||
|
* worst 5% of cases.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TOP:
|
||||||
if (
|
if (
|
||||||
this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1]
|
this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1]
|
||||||
&& this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2]
|
&& this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2]
|
||||||
) {
|
) {
|
||||||
|
// All three detections are the same
|
||||||
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0];
|
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0];
|
||||||
this.testResults.aspectRatioCheck.topCandidateQuality =
|
this.testResults.aspectRatioCheck.topCandidateQuality =
|
||||||
this.testResults.aspectRatioCheck.topQuality[0]
|
this.testResults.aspectRatioCheck.topQuality[0]
|
||||||
+ this.testResults.aspectRatioCheck.topQuality[1]
|
+ this.testResults.aspectRatioCheck.topQuality[1]
|
||||||
+ this.testResults.aspectRatioCheck.topQuality[2];
|
+ this.testResults.aspectRatioCheck.topQuality[2];
|
||||||
} else if (
|
} else if (this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2]) {
|
||||||
this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2]
|
// Corners are the same, but different from center
|
||||||
|
if (this.testResults.aspectRatioCheck.topRows[0] > this.testResults.aspectRatioCheck.topRows[1]) {
|
||||||
|
// Corners are above center.
|
||||||
|
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0];
|
||||||
|
this.testResults.aspectRatioCheck.topCandidateQuality =
|
||||||
|
this.testResults.aspectRatioCheck.topQuality[0]
|
||||||
|
+ this.testResults.aspectRatioCheck.topQuality[2]
|
||||||
|
} else {
|
||||||
|
// Corners are below center
|
||||||
|
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1];
|
||||||
|
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Corners are different.
|
||||||
|
if (
|
||||||
|
this.testResults.aspectRatioCheck.topRows[0] !== this.testResults.aspectRatioCheck.topRows[1]
|
||||||
|
&& this.testResults.aspectRatioCheck.topRows[2] !== this.testResults.aspectRatioCheck.topRows[1]
|
||||||
|
) {
|
||||||
|
// Center and matches neither of the corners.
|
||||||
|
// TODO: maybe we can figure out to guess aspect ratio in scenarios like this.
|
||||||
|
// But for the time being, just slap it with "inconclusive".
|
||||||
|
this.testResults.aspectRatioUncertain = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// center matches one of the corners
|
||||||
|
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1];
|
||||||
|
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1];
|
||||||
|
|
||||||
|
if (this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1]) {
|
||||||
|
this.testResults.aspectRatioCheck.topCandidateQuality += this.testResults.aspectRatioCheck.topRows[0];
|
||||||
|
} else {
|
||||||
|
this.testResults.aspectRatioCheck.topCandidateQuality += this.testResults.aspectRatioCheck.topRows[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM:
|
||||||
|
if (
|
||||||
|
this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[1]
|
||||||
|
&& this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[2]
|
||||||
) {
|
) {
|
||||||
|
// All three detections are the same
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0];
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidateQuality =
|
||||||
|
this.testResults.aspectRatioCheck.bottomQuality[0]
|
||||||
|
+ this.testResults.aspectRatioCheck.bottomQuality[1]
|
||||||
|
+ this.testResults.aspectRatioCheck.bottomQuality[2];
|
||||||
|
} else if (this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[2]) {
|
||||||
|
// Corners are the same, but different from center
|
||||||
|
if (this.testResults.aspectRatioCheck.bottomRows[0] > this.testResults.aspectRatioCheck.bottomRows[1]) {
|
||||||
|
// Corners are above center.
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0];
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidateQuality =
|
||||||
|
this.testResults.aspectRatioCheck.bottomQuality[0]
|
||||||
|
+ this.testResults.aspectRatioCheck.bottomQuality[2]
|
||||||
|
} else {
|
||||||
|
// Corners are below center
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1];
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Corners are different.
|
||||||
|
if (
|
||||||
|
this.testResults.aspectRatioCheck.bottomRows[0] !== this.testResults.aspectRatioCheck.bottomRows[1]
|
||||||
|
&& this.testResults.aspectRatioCheck.bottomRows[2] !== this.testResults.aspectRatioCheck.bottomRows[1]
|
||||||
|
) {
|
||||||
|
// Center and matches neither of the corners.
|
||||||
|
// TODO: maybe we can figure out to guess aspect ratio in scenarios like this.
|
||||||
|
// But for the time being, just slap it with "inconclusive".
|
||||||
|
this.testResults.aspectRatioUncertain = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// center matches one of the corners
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1];
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1];
|
||||||
|
|
||||||
|
if (this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[1]) {
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidateQuality += this.testResults.aspectRatioCheck.bottomRows[0];
|
||||||
|
} else {
|
||||||
|
this.testResults.aspectRatioCheck.bottomCandidateQuality += this.testResults.aspectRatioCheck.bottomRows[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that both top and bottom candidates are approximately equally distant
|
* Get final results.
|
||||||
* from the edge. If top and bottom candidates do not match, or fail to meet
|
* Let candidateA hold better-quality candidate, and let the candidateB hold the lower-quality candidate.
|
||||||
* the edge detection threshold, then we set 'unreliable detection' flag, which
|
* candidateA must match or exceed minQualitySingleEdge and candidateB must match or exceed minQualitySecondEdge.
|
||||||
* will cause aspect ratio detection to be postponed.
|
|
||||||
*
|
|
||||||
* Otherwise, we set letterbox width (px from edge on detection canvas) and how
|
|
||||||
* far the frame is shifted off-center.
|
|
||||||
*
|
|
||||||
* If frame shifts too much off-center, we also set the 'unreliable detection' flag.
|
|
||||||
*/
|
*/
|
||||||
|
let candidateA, candidateB;
|
||||||
/**
|
if (this.testResults.aspectRatioCheck.bottomCandidateQuality > this.testResults.aspectRatioCheck.topCandidateQuality) {
|
||||||
* Calculate how dissimilar each sampling segment is from.
|
candidateA = this.testResults.aspectRatioCheck.bottomCandidate;
|
||||||
*
|
candidateB = this.testResults.aspectRatioCheck.topCandidate;
|
||||||
* Similarity matrix can tell us a few things:
|
} else {
|
||||||
*
|
candidateA = this.testResults.aspectRatioCheck.topCandidate;
|
||||||
* 1. If a corner is not dissimilar from center and the other corner on its respective side, then we probably don't have a logo.
|
candidateB = this.testResults.aspectRatioCheck.bottomCandidate;
|
||||||
* our edge is also probably accurate.
|
|
||||||
* * that is, unless other
|
|
||||||
* 2. If corner varies a lot from center and other corner, but center and other corner are similar, then we're looking at a logo
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let r: number;
|
|
||||||
for (let i = 0; i < 3; i += 3) {
|
|
||||||
r = i * 3;
|
|
||||||
// similarity top - top
|
|
||||||
this.similarityMatrix[r] = this.testResults.aspectRatioCheck.topRows[i] - this.testResults.aspectRatioCheck.topRows[(i + 1) % 3];
|
|
||||||
this.similarityMatrix[r + 1] = this.testResults.aspectRatioCheck.topRows[i] - this.testResults.aspectRatioCheck.topRows[(i + 2) % 3];
|
|
||||||
|
|
||||||
// similarity top - bottom
|
|
||||||
this.similarityMatrix[r + 2] = this.testResults.aspectRatioCheck.topRows[i] - this.testResults.aspectRatioCheck.bottomRows[0];
|
|
||||||
this.similarityMatrix[r + 3] = this.testResults.aspectRatioCheck.topRows[i] - this.testResults.aspectRatioCheck.bottomRows[1];
|
|
||||||
this.similarityMatrix[r + 4] = this.testResults.aspectRatioCheck.topRows[i] - this.testResults.aspectRatioCheck.bottomRows[2];
|
|
||||||
|
|
||||||
// similarity bottom - bottom
|
|
||||||
this.similarityMatrix[r + 5] = this.testResults.aspectRatioCheck.bottomRows[i] - this.testResults.aspectRatioCheck.bottomRows[(i + 1) % 3];
|
|
||||||
this.similarityMatrix[r + 6] = this.testResults.aspectRatioCheck.bottomRows[i] - this.testResults.aspectRatioCheck.bottomRows[(i + 2) % 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
candidateA < this.settings.active.arDetect.edgeDetection.thresholds.minQualitySingleEdge
|
||||||
|
|| candidateB < this.settings.active.arDetect.edgeDetection.thresholds.minQualitySecondEdge
|
||||||
|
) {
|
||||||
|
this.testResults.aspectRatioUncertain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxOffset = ~~(height * this.settings.active.arDetect.edgeDetection.maxLetterboxOffset)
|
||||||
|
const diff = this.testResults.aspectRatioCheck.topCandidate - this.testResults.aspectRatioCheck.bottomCandidate;
|
||||||
|
const candidateAvg = ~~((this.testResults.aspectRatioCheck.topCandidate + this.testResults.aspectRatioCheck.bottomCandidate) / 2);
|
||||||
|
|
||||||
|
if (diff > maxOffset) {
|
||||||
|
this.testResults.aspectRatioUncertain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.testResults.aspectRatioUncertain = false;
|
||||||
|
this.testResults.letterboxWidth = candidateAvg;
|
||||||
|
this.testResults.letterboxOffset = diff;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -23,8 +23,13 @@ export interface AardTestResults {
|
|||||||
bottomRows: [number, number, number],
|
bottomRows: [number, number, number],
|
||||||
bottomQuality: [number, number, number],
|
bottomQuality: [number, number, number],
|
||||||
topCandidate: number,
|
topCandidate: number,
|
||||||
topCandidateQuality: number
|
topCandidateQuality: number,
|
||||||
|
bottomCandidate: number,
|
||||||
|
bottomCandidateQuality: number,
|
||||||
},
|
},
|
||||||
|
aspectRatioUncertain: boolean,
|
||||||
|
letterboxWidth: number,
|
||||||
|
letterboxOffset: number,
|
||||||
logoDetected: [boolean, boolean, boolean, boolean]
|
logoDetected: [boolean, boolean, boolean, boolean]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +57,12 @@ export function initAardTestResults(settings: AardSettings): AardTestResults {
|
|||||||
bottomRows: [-1, -1, -1],
|
bottomRows: [-1, -1, -1],
|
||||||
bottomQuality: [0, 0, 0],
|
bottomQuality: [0, 0, 0],
|
||||||
topCandidate: 0,
|
topCandidate: 0,
|
||||||
topCandidateQuality: 0
|
topCandidateQuality: 0,
|
||||||
|
bottomCandidate: 0,
|
||||||
|
bottomCandidateQuality: 0,
|
||||||
},
|
},
|
||||||
|
letterboxWidth: 0,
|
||||||
|
letterboxOffset: 0,
|
||||||
logoDetected: [false, false, false, false]
|
logoDetected: [false, false, false, false]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user