Private
Public Access
36 lines
2.3 KiB
JavaScript
36 lines
2.3 KiB
JavaScript
/* Pure measurement processing; the video uses 32 clockwise edge patches. */
|
|
function calibrationFit(baseline,patches,model){
|
|
if(!model || baseline.length!==16 || patches.length!==32)throw new Error('Incomplete calibration measurements.');
|
|
var candidates=[],edges=[0,0,0,0];
|
|
for(var region=0;region<16;region++){
|
|
var responses=patches.map(function(p){if(!p || p.length!==16)throw new Error('Missing video measurements.');return Math.max(0,p[region]-baseline[region]);});
|
|
var peak=Math.max.apply(Math,responses),best=responses.indexOf(peak);
|
|
if(peak<8)continue;
|
|
// Reject regions responding equally around the screen (global histograms).
|
|
var strong=responses.filter(function(v){return v>=peak*.65;}).length;
|
|
if(strong>10)continue;
|
|
var distant=responses.some(function(v,i){var d=Math.min(Math.abs(i-best),32-Math.abs(i-best));return v>=peak*.65 && d>6;});
|
|
if(distant)continue;
|
|
var sum=0,weighted=0;
|
|
for(var offset=-3;offset<=3;offset++){
|
|
var index=(best+offset+32)%32,weight=Math.max(0,responses[index]-peak*.35);
|
|
weighted+=offset*weight;sum+=weight;
|
|
}
|
|
var position=((best+.5+weighted/sum)/8+4)%4;
|
|
candidates.push({position:position,region:region,score:peak/strong});
|
|
}
|
|
// Nested LG regions can measure the same edge position. Keep the most
|
|
// localized response rather than making duplicate interpolation anchors.
|
|
candidates.sort(function(a,b){return b.score-a.score || a.region-b.region;});
|
|
var points=[];
|
|
candidates.forEach(function(p){if(points.some(function(q){var d=Math.abs(p.position-q.position);return Math.min(d,4-d)<.06;}))return;points.push({position:p.position,region:p.region});edges[Math.floor(p.position)]++;});
|
|
points.sort(function(a,b){return a.position-b.position;});
|
|
if(points.length<4 || edges.some(function(n){return !n;}))throw new Error('No reliable spatial response on every edge. Keep the video full-screen; this TV may not expose local histogram regions. Your saved profile is unchanged.');
|
|
return {model:model,points:points};
|
|
}
|
|
function calibrationLuma(regions){return regions.map(function(c){return (c[0]+c[1]+c[2])/3;});}
|
|
function calibrationAverage(samples){
|
|
if(samples.length<3)throw new Error('Not enough fresh histogram samples. Keep the app visible and repeat calibration.');
|
|
return samples[0].map(function(_,r){return samples.reduce(function(sum,s){return sum+s[r];},0)/samples.length;});
|
|
}
|