Finish aspect ratio check

This commit is contained in:
Tamius Han 2024-10-21 01:08:03 +02:00
parent 2736ac418f
commit 7c5e4101b0
3 changed files with 152 additions and 47 deletions

View File

@ -165,6 +165,15 @@ export interface AardSettings {
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
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
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
confirmationThreshold: number, //

View File

@ -381,7 +381,12 @@ class Aard {
// STEP 3:
// 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);
@ -964,7 +969,7 @@ class Aard {
* @param height
*/
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);
@ -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) {
/**
* 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 ]
* |
@ -1422,65 +1433,141 @@ class Aard {
* 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 (
this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1]
&& 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.topCandidateQuality =
this.testResults.aspectRatioCheck.topQuality[0]
+ this.testResults.aspectRatioCheck.topQuality[1]
+ this.testResults.aspectRatioCheck.topQuality[2];
} else if (
this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2]
} else if (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
* from the edge. If top and bottom candidates do not match, or fail to meet
* the edge detection threshold, then we set 'unreliable detection' flag, which
* 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.
* Get final results.
* Let candidateA hold better-quality candidate, and let the candidateB hold the lower-quality candidate.
* candidateA must match or exceed minQualitySingleEdge and candidateB must match or exceed minQualitySecondEdge.
*/
/**
* Calculate how dissimilar each sampling segment is from.
*
* Similarity matrix can tell us a few things:
*
* 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.
* 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];
let candidateA, candidateB;
if (this.testResults.aspectRatioCheck.bottomCandidateQuality > this.testResults.aspectRatioCheck.topCandidateQuality) {
candidateA = this.testResults.aspectRatioCheck.bottomCandidate;
candidateB = this.testResults.aspectRatioCheck.topCandidate;
} else {
candidateA = this.testResults.aspectRatioCheck.topCandidate;
candidateB = this.testResults.aspectRatioCheck.bottomCandidate;
}
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

View File

@ -23,8 +23,13 @@ export interface AardTestResults {
bottomRows: [number, number, number],
bottomQuality: [number, number, number],
topCandidate: number,
topCandidateQuality: number
topCandidateQuality: number,
bottomCandidate: number,
bottomCandidateQuality: number,
},
aspectRatioUncertain: boolean,
letterboxWidth: number,
letterboxOffset: number,
logoDetected: [boolean, boolean, boolean, boolean]
}
@ -52,8 +57,12 @@ export function initAardTestResults(settings: AardSettings): AardTestResults {
bottomRows: [-1, -1, -1],
bottomQuality: [0, 0, 0],
topCandidate: 0,
topCandidateQuality: 0
topCandidateQuality: 0,
bottomCandidate: 0,
bottomCandidateQuality: 0,
},
letterboxWidth: 0,
letterboxOffset: 0,
logoDetected: [false, false, false, false]
}