Improvements to gradient detection
This commit is contained in:
parent
9a3ba39076
commit
5fbdb3822c
@ -3,7 +3,7 @@
|
|||||||
const _prod = false;
|
const _prod = false;
|
||||||
|
|
||||||
var Debug = {
|
var Debug = {
|
||||||
performanceMetrics: true, // should not be affected by debug.debug in order to allow benchmarking of the impact logging in console has
|
// performanceMetrics: true, // should not be affected by debug.debug in order to allow benchmarking of the impact logging in console has
|
||||||
init: true,
|
init: true,
|
||||||
debug: true,
|
debug: true,
|
||||||
// debug: false,
|
// debug: false,
|
||||||
@ -16,7 +16,7 @@ var Debug = {
|
|||||||
// comms: true,
|
// comms: true,
|
||||||
// showArDetectCanvas: true,
|
// showArDetectCanvas: true,
|
||||||
// flushStoredSettings: true,
|
// flushStoredSettings: true,
|
||||||
flushStoredSettings: false,
|
// flushStoredSettings: false,
|
||||||
// playerDetectDebug: true,
|
// playerDetectDebug: true,
|
||||||
// periodic: true,
|
// periodic: true,
|
||||||
// videoRescan: true,
|
// videoRescan: true,
|
||||||
|
@ -45,12 +45,12 @@ var ExtensionConf = {
|
|||||||
|
|
||||||
// samplingInterval: 10, // we sample at columns at (width/this) * [ 1 .. this - 1]
|
// samplingInterval: 10, // we sample at columns at (width/this) * [ 1 .. this - 1]
|
||||||
blackframe: {
|
blackframe: {
|
||||||
sufficientColorVariance: 0.05, // calculate difference between average intensity and pixel, for every pixel for every color
|
sufficientColorVariance: 0.09, // calculate difference between average intensity and pixel, for every pixel for every color
|
||||||
// component. Average intensity is normalized to where 0 is black and 1 is biggest value for
|
// component. Average intensity is normalized to where 0 is black and 1 is biggest value for
|
||||||
// that component. If sum of differences between normalized average intensity and normalized
|
// that component. If sum of differences between normalized average intensity and normalized
|
||||||
// component varies more than this % between color components, we can afford to use less strict
|
// component varies more than this % between color components, we can afford to use less strict
|
||||||
// cummulative treshold.
|
// cummulative treshold.
|
||||||
cumulativeTresholdLax: 1600,
|
cumulativeThresholdLax: 1600,
|
||||||
cumulativeThresholdStrict: 2560,// if we add values of all pixels together and get more than this, the frame is bright enough.
|
cumulativeThresholdStrict: 2560,// if we add values of all pixels together and get more than this, the frame is bright enough.
|
||||||
// (note: blackframe is 16x9 px -> 144px total. cumulative threshold can be reached fast)
|
// (note: blackframe is 16x9 px -> 144px total. cumulative threshold can be reached fast)
|
||||||
blackPixelsCondition: 0.6, // How much pixels must be black (1 all, 0 none) before we consider frame as black. Takes
|
blackPixelsCondition: 0.6, // How much pixels must be black (1 all, 0 none) before we consider frame as black. Takes
|
||||||
@ -63,6 +63,7 @@ var ExtensionConf = {
|
|||||||
threshold: 16, // if pixel is darker than the sum of black level and this value, we count it as black
|
threshold: 16, // if pixel is darker than the sum of black level and this value, we count it as black
|
||||||
// on 0-255. Needs to be fairly high (8 might not cut it) due to compression
|
// on 0-255. Needs to be fairly high (8 might not cut it) due to compression
|
||||||
// artifacts in the video itself
|
// artifacts in the video itself
|
||||||
|
frameThreshold: 4, // treshold, but when doing blackframe test
|
||||||
imageThreshold: 16, // in order to detect pixel as "not black", the pixel must be brighter than
|
imageThreshold: 16, // in order to detect pixel as "not black", the pixel must be brighter than
|
||||||
// the sum of black level, threshold and this value.
|
// the sum of black level, threshold and this value.
|
||||||
gradientThreshold: 2, // When trying to determine thickness of the black bars, we take 2 values: position of
|
gradientThreshold: 2, // When trying to determine thickness of the black bars, we take 2 values: position of
|
||||||
|
@ -587,6 +587,8 @@ class ArDetector {
|
|||||||
console.log("%c[ArDetect::frameCheck] Black frame analysis suggests this frame is black or too dark. Doing nothing,", "color: #fa3", bfanalysis);
|
console.log("%c[ArDetect::frameCheck] Black frame analysis suggests this frame is black or too dark. Doing nothing,", "color: #fa3", bfanalysis);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
console.log("%c[ArDetect::frameCheck] Black frame analysis suggests this frame is not completely black. Doing further analysis,", "color: #3fa", bfanalysis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -766,7 +768,7 @@ class ArDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blackframeTest() {
|
blackframeTest() {
|
||||||
if (! this.blackLevel) {
|
if (this.blackLevel === undefined) {
|
||||||
if (Debug.debug && Debug.debugArDetect) {
|
if (Debug.debug && Debug.debugArDetect) {
|
||||||
console.log("[ArDetect::frameCheck] black level undefined, resetting");
|
console.log("[ArDetect::frameCheck] black level undefined, resetting");
|
||||||
}
|
}
|
||||||
@ -786,6 +788,7 @@ class ArDetector {
|
|||||||
let cumulativeValue = 0;
|
let cumulativeValue = 0;
|
||||||
let blackPixelCount = 0;
|
let blackPixelCount = 0;
|
||||||
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data;
|
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data;
|
||||||
|
const blackTreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameTreshold;
|
||||||
|
|
||||||
|
|
||||||
// we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists
|
// we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists
|
||||||
@ -800,8 +803,10 @@ class ArDetector {
|
|||||||
pixelMax = Math.max(bfImageData[i], bfImageData[i+1], bfImageData[i+2]);
|
pixelMax = Math.max(bfImageData[i], bfImageData[i+1], bfImageData[i+2]);
|
||||||
bfImageData[i+3] = pixelMax;
|
bfImageData[i+3] = pixelMax;
|
||||||
|
|
||||||
|
if (pixelMax < blackTreshold) {
|
||||||
if (pixelMax < this.blackLevel) {
|
if (pixelMax < this.blackLevel) {
|
||||||
this.blackLevel = pixelMax;
|
this.blackLevel = pixelMax;
|
||||||
|
}
|
||||||
blackPixelCount++;
|
blackPixelCount++;
|
||||||
} else {
|
} else {
|
||||||
cumulativeValue += pixelMax;
|
cumulativeValue += pixelMax;
|
||||||
@ -865,6 +870,7 @@ class ArDetector {
|
|||||||
blackPixelRatio: (blackPixelCount/(cols * rows)),
|
blackPixelRatio: (blackPixelCount/(cols * rows)),
|
||||||
cumulativeValue: cumulativeValue,
|
cumulativeValue: cumulativeValue,
|
||||||
hasSufficientVariance: hasSufficientVariance,
|
hasSufficientVariance: hasSufficientVariance,
|
||||||
|
blackLevel: this.blackLevel,
|
||||||
variances: {
|
variances: {
|
||||||
raw: {
|
raw: {
|
||||||
r: var_r, g: var_g, b: var_b
|
r: var_r, g: var_g, b: var_b
|
||||||
|
@ -32,6 +32,9 @@ class EdgeDetect{
|
|||||||
if (direction == EdgeDetectPrimaryDirection.VERTICAL) {
|
if (direction == EdgeDetectPrimaryDirection.VERTICAL) {
|
||||||
fastCandidates = this.findCandidates(image, sampleCols, guardLineOut);
|
fastCandidates = this.findCandidates(image, sampleCols, guardLineOut);
|
||||||
|
|
||||||
|
if (! this.isValidSample(fastCandidates)) {
|
||||||
|
return {status: EdgeStatus.AR_UNKNOWN};
|
||||||
|
}
|
||||||
// if(quality == EdgeDetectQuality.FAST){
|
// if(quality == EdgeDetectQuality.FAST){
|
||||||
// edges = fastCandidates; // todo: processing
|
// edges = fastCandidates; // todo: processing
|
||||||
// } else {
|
// } else {
|
||||||
@ -140,6 +143,88 @@ class EdgeDetect{
|
|||||||
|
|
||||||
// dont call the following outside of this class
|
// 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.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;
|
||||||
|
}
|
||||||
|
|
||||||
edgeDetect(image, samples){
|
edgeDetect(image, samples){
|
||||||
var edgeCandidatesTop = {count: 0};
|
var edgeCandidatesTop = {count: 0};
|
||||||
var edgeCandidatesBottom = {count: 0};
|
var edgeCandidatesBottom = {count: 0};
|
||||||
@ -246,13 +331,6 @@ class EdgeDetect{
|
|||||||
console.log("\n\nuwu fucky wucky:", e, "\n\n")
|
console.log("\n\nuwu fucky wucky:", e, "\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("----------- returning: ", {
|
|
||||||
edgeCandidatesTop: edgeCandidatesTop,
|
|
||||||
edgeCandidatesTopCount: edgeCandidatesTop.count,
|
|
||||||
edgeCandidatesBottom: edgeCandidatesBottom,
|
|
||||||
edgeCandidatesBottomCount: edgeCandidatesBottom.count
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
edgeCandidatesTop: edgeCandidatesTop,
|
edgeCandidatesTop: edgeCandidatesTop,
|
||||||
edgeCandidatesTopCount: edgeCandidatesTop.count,
|
edgeCandidatesTopCount: edgeCandidatesTop.count,
|
||||||
@ -556,6 +634,7 @@ class EdgeDetect{
|
|||||||
blackFound: false,
|
blackFound: false,
|
||||||
imageFound: false, // misleading name — also true if we ran over gradientSampleSize pixels from image
|
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
|
// whether that actually count as an image depends on how aggressive gradientDetection is
|
||||||
|
hasGradient: false,
|
||||||
blackRow: -1,
|
blackRow: -1,
|
||||||
imageRow: -1,
|
imageRow: -1,
|
||||||
lastValue: -1,
|
lastValue: -1,
|
||||||
@ -709,8 +788,25 @@ class EdgeDetect{
|
|||||||
black: c.blackRow
|
black: c.blackRow
|
||||||
});
|
});
|
||||||
continue;
|
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 {
|
} else {
|
||||||
|
// in this case, we aren't looking at a gradient. We also aren't looking at a
|
||||||
|
// valid column
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -724,6 +820,13 @@ class EdgeDetect{
|
|||||||
black: c.blackRow
|
black: c.blackRow
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
colsOut.push({
|
||||||
|
col: c.col,
|
||||||
|
black: c.blackRow,
|
||||||
|
hasGradient: true,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user