Skip to content

Instantly share code, notes, and snippets.

@sjoerdvisscher
Created April 14, 2015 09:55
Show Gist options
  • Save sjoerdvisscher/ffa9fa33d327606087e0 to your computer and use it in GitHub Desktop.
Save sjoerdvisscher/ffa9fa33d327606087e0 to your computer and use it in GitHub Desktop.
image recognition of elevator level indicator
var MIN_EDGE_LEVEL = 40;
function detect(W, H, data, newData, clipX, clipY, clipW, clipH) {
newData = newData || [];
clipX = clipX || 0;
clipY = clipY || 0;
clipW = clipW || W;
clipH = clipH || H;
var bytesPerPixel = Math.round(data.length / W / H);
for (var i = 0; i < data.length; i++)
data[i] = Math.max(100, data[i]);
var lcds = [
[.3, 0,.7,.2], // top
[ 0,.2,.4,.4], // top left
[.6,.2,1 ,.4], // top right
[.3,.4,.7,.6], // middle
[ 0,.6,.4,.8], // bottom left
[.6,.6,1 ,.8], // bottom right
[.3,.8,.7,1 ], // bottom
[.4,.2,.6,.4], // top hole for base-line darkness
];
var nums = [
[1,1,1,0,1,1,1,0], // 0
[0,0,1,0,0,1,0,0], // 1
[1,0,1,1,1,0,1,0], // 2
[1,0,1,1,0,1,1,0], // 3
[0,1,1,1,0,1,0,0], // 4
[1,1,0,1,0,1,1,0], // 5
[1,1,0,1,1,1,1,0], // 6
[1,0,1,0,0,1,0,0], // 7
[1,1,1,1,1,1,1,0], // 8
[1,1,1,1,0,1,1,0] // 9
]
// Edge detection with Sobel operators
var xkernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
var ykernel = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
function calcKernel(kernel, i) {
var total = 0;
for (var y = 0; y < kernel.length; y++)
for (var x = 0; x < kernel[y].length; x++)
total += kernel[y][x] * data[i + (y * W + x) * bytesPerPixel];
return total / 8;
}
function calcGrad(i) {
var grad = Math.sqrt(Math.pow(calcKernel(xkernel, i), 2) + Math.pow(calcKernel(ykernel, i), 2));
if (grad > 255) grad = 255;
return grad;
}
// Detect red edges
var minX = W, minY = H, maxX = 0, maxY = 0;
for (var y = clipY; y < clipY + clipH; y++) {
for (var x = clipX; x < clipX + clipW; x++) {
var i = (y * W + x) * bytesPerPixel;
var rEdge = calcGrad(i);
var gEdge = calcGrad(i+1);
var rValue = data[i];
var gValue = data[i + 1];
var c = Math.max(0, rEdge - gEdge) + gEdge * Math.max(0, rValue - gValue) / 64;
if (c > MIN_EDGE_LEVEL) {
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
i = (y * W + x) * 4;
newData[i] = c;
newData[i+1] = c;
newData[i+2] = c;
newData[i+3] = 255;
}
}
var width = maxX - minX;
var height = maxY - minY;
// If we only detect edges in a tal narrow box, we've got a 1
if (width < height / 3)
return 1;
// measure light level for each lcd part
var minTotal = 1e9, maxTotal = 0;
var totals = [];
for (var l = 0; l < lcds.length; l++) {
var total = 0;
var lcd = lcds[l];
var x0 = Math.round(minX + lcd[0] * width);
var x1 = Math.round(minX + lcd[2] * width);
var y0 = Math.round(minY + lcd[1] * height);
var y1 = Math.round(minY + lcd[3] * height);
for (var y = y0; y < y1; y++) {
for (var x = x0; x < x1; x++) {
var i = (y * W + x) * 4;
total += newData[i];
}
}
total /= (lcd[2] - lcd[0]) * (lcd[3] - lcd[1]);
minTotal = Math.min(minTotal, total);
maxTotal = Math.max(maxTotal, total);
totals[l] = total;
}
// normalise light levels
for (var l = 0; l < lcds.length; l++) {
var total = totals[l];
var c = totals[l] = (total - minTotal) / (maxTotal - minTotal) * 255;
var lcd = lcds[l];
var x0 = Math.round(minX + lcd[0] * width);
var x1 = Math.round(minX + lcd[2] * width);
var y0 = Math.round(minY + lcd[1] * height);
var y1 = Math.round(minY + lcd[3] * height);
for (var y = y0; y < y1; y++) {
for (var x = x0; x < x1; x++) {
var i = (y * W + x) * 4;
newData[i + 3] = 200 - c / 5;
}
}
}
// Find best number match
var best = 0, bestAt = -1;
for (var n = 0; n < 10; n++) {
var total = 0;
var num = nums[n];
for (var l = 0; l < num.length; l++) {
total += (num[l] * 2 - 1) * (totals[l] - 128);
}
if (total > best) {
best = total;
bestAt = n;
}
}
return bestAt;
}
exports.detect = detect;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment