Skip to content

Instantly share code, notes, and snippets.

@Keyes
Created May 1, 2017 13:20
Show Gist options
  • Save Keyes/3f75607f124d94d44b3ae4c991fb662b to your computer and use it in GitHub Desktop.
Save Keyes/3f75607f124d94d44b3ae4c991fb662b to your computer and use it in GitHub Desktop.
/*
* Motion detection with nodeJS
*
* You'll need node-canvas: https://github.com/Automattic/node-canvas
*/
var Canvas = require('canvas'),
Image = Canvas.Image,
canvas = new Canvas(640, 480),
diffCanvas = new Canvas(640, 480),
context = canvas.getContext('2d'),
diffContext = diffCanvas.getContext('2d'),
fs = require('fs');
const canvasWidth = 640;
const canvasHeight = 480;
const pixelDiffThreshold = 50;
const diffWidth = 64;
const diffHeight = 48;
const scoreThreshold = 16;
const movementPadding = 10;
const motionThreshold = 10;
var currentImage = fs.readFileSync('last.jpg');
var lastImage = fs.readFileSync('prelast.jpg');
var img = new Image;
img.src = currentImage;
diffContext.drawImage(img, 0, 0, canvasWidth, canvasHeight);
img = new Image;
img.src = currentImage;
context.drawImage(img, 0, 0, canvasWidth, canvasHeight);
diffContext.globalCompositeOperation = 'difference';
img = new Image;
img.src = lastImage;
diffContext.drawImage(img, 0, 0, canvasWidth, canvasHeight);
diffContext.globalCompositeOperation = 'source-on';
// predefined no-motion-areas: in my case my TV and some reflections
diffContext.fillStyle = "black";
diffContext.fillRect(canvasWidth * 0.68, canvasHeight * 0.52, canvasWidth * 0.29, canvasHeight * 0.32); // TV
diffContext.fillRect(canvasWidth * 0.05, canvasHeight * 0.25, canvasWidth * 0.05, canvasHeight * 0.15); // TV reflection
diffContext.fillRect(canvasWidth * 0.15, canvasHeight * 0.65, canvasWidth * 0.05, canvasHeight * 0.11); // TV reflection
var diff = processDiff();
if (diff.motionBox) {
context.strokeStyle = 'green';
context.lineWidth = 2;
context.strokeRect(
diff.motionBox.x[0] - movementPadding,
diff.motionBox.y[0] - movementPadding,
diff.motionBox.x[1] - diff.motionBox.x[0] + (movementPadding * 2),
diff.motionBox.y[1] - diff.motionBox.y[0] + (movementPadding * 2)
);
}
fs.writeFileSync('difference.png', diffCanvas.toBuffer());
fs.writeFileSync('movement.png', canvas.toBuffer());
console.log('processDiff', diff);
function processDiff() {
var imageData = diffContext.getImageData(0, 0, canvasWidth, canvasHeight);
var rgba = imageData.data;
// pixel adjustments are done by reference directly on diffImageData
var changedPixels = 0;
var motionPixels = [];
var motionBox = { x: [canvasWidth, 0], y: [canvasHeight, 0] };
for (var i = 0; i < rgba.length; i += 4) {
var brightness = 0.34 * rgba[i] + 0.5 * rgba[i + 1] + 0.16 * rgba[i + 2];
coords = {
x: (i / 4) % canvasWidth,
y: Math.floor((i / 4) / canvasWidth)
};
if (brightness >= pixelDiffThreshold) {
changedPixels++;
rgba[i] = brightness;
rgba[i + 1] = brightness;
rgba[i + 2] = brightness;
if (coords.x < motionBox.x[0]) motionBox.x[0] = coords.x;
if (coords.y < motionBox.y[0]) motionBox.y[0] = coords.y;
if (coords.x > motionBox.x[1]) motionBox.x[1] = coords.x;
if (coords.y > motionBox.y[1]) motionBox.y[1] = coords.y;
} else {
rgba[i] = 0;
rgba[i + 1] = 0;
rgba[i + 2] = 0;
}
}
var motionBoxWidth = motionBox.x[1] - motionBox.x[0];
var motionBoxHeight = motionBox.y[1] - motionBox.y[0];
var score = Math.round((changedPixels / (motionBoxWidth * motionBoxHeight)) * 100000) / 1000;
var hasMovement = score > motionThreshold;
diffContext.putImageData(imageData, 0, 0);
return {
score: score,
hasMovement: hasMovement,
motionBox: hasMovement ? motionBox : false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment