Repository: lonekorean/diff-cam-engine
Branch: master
Commit: 6226b1c4fef1
Files: 4
Total size: 13.4 KB
Directory structure:
gitextract_50974trg/
├── .gitignore
├── LICENSE.md
├── README.md
└── diff-cam-engine.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
Thumbs.db
================================================
FILE: LICENSE.md
================================================
Copyright (c) 2016 Will Boyd
This software is released under the MIT license: http://opensource.org/licenses/MIT
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# diff-cam-engine
Core engine for building motion detection web apps.
### Usage
`diff-cam-engine.js` provides a `DiffCamEngine` object that accesses the webcam, captures images from it, and evaluates motion.
You'll want to use the `adapter.js` shim, which is available here: https://github.com/webrtc/adapter. Add it before `diff-cam-engine.js`.
With that in place, call `DiffCamEngine.init()` to initialize. This will set things up and ask the user for permission to access the webcam.
``` javascript
DiffCamEngine.init({
// config options go here
});
```
Then call `start()` to begin the actual motion sensing. Do not call `start()` before `init()` has finished. It's a good idea to call `start()` from `initSuccessCallback()` (more on this later).
Call `stop()` to turn things off.
### Configuration
You can customize how `DiffCamEngine` behaves by passing an object of name/value pairs into `init()`. For example:
``` javascript
DiffCamEngine.init({
video: myVideo,
captureIntervalTime: 50,
captureCallback: myCaptureHandler
// etc.
});
```
##### Variables
The following variables can be passed into `init()`:
| Name | Default | Description |
| --- | --- | --- |
| video | internal <video> (not visible) | The <video> element for showing the live webcam stream |
| motionCanvas | internal <canvas> (not visible) | The <canvas> element for showing the visual motion heatmap |
| captureIntervalTime | 100 | Number of ms between capturing images from the stream |
| captureWidth | 640 | Width of captured images from stream |
| captureHeight | 480 | Height of capture images from stream |
| diffWidth | 64 | Width of (usually downsized) images used for diffing and showing motion |
| diffHeight | 48 | Height of (usually downsized) images used for diffing and showing motion |
| pixelDiffThreshold | 32 | Minimum difference in a pixel to be considered changed |
| scoreThreshold | 16 | Minimum number of changed pixels for an image to be considered as having motion |
| includeMotionBox | false | Flag to calculate and display (on motionCanvas) the bounding box of motion |
| includeMotionPixels | false | Flag to include data indicating all the changed pixels |
##### Callbacks
There are also a couple callback functions you can specify. This is the primary way of interacting with `DiffCamEngine` during execution. Pass these into `init()` just like the variables above.
| Name | Description |
| --- | --- |
| initSuccessCallback | Called when init has successfully completed |
| initErrorCallback | Called when init fails |
| startCompleteCallback | Called once the webcam has begun streaming |
| captureCallback | Called after a captured image from the stream has been evaluated |
`captureCallback` is the only one with a parameter. This parameter is an object with multiple properties on it. These properties are:
| Property | Description |
| --- | --- |
| imageData | The imageData object for the captured image |
| score | The evaluated score for the captured image |
| hasMotion | Whether or not the score meets the score threshold for motion |
| motionBox | An object containg x min/max and y min/max for a box wrapping the region in which motion occurred, only returned if includeMotionBox is `true` |
| motionPixels | An object containg each pixel in the image that changed (indicating motion), only returned if includeMotionPixels is `true` |
| getURL | Convenience function, returns imageData converted to a URL suitable for setting as the src of an image |
| checkMotionPixel | Convenience function, takes in an x and y cooridnate, returns a boolean indicating if the pixel at that location has changed |
### Functions
`DiffCamEngine` exposes the following public functions:
| Function | Description |
| --- | --- |
| init | Initializes everything and requests permission to access the webcam |
| start | Begin streaming from the webcam |
| stop | Stop streaming from the webcam |
| getPixelDiffThreshold | Get pixelDiffThreshold during execution |
| setPixelDiffThreshold | Set pixelDiffThreshold during execution |
| getScoreThreshold | Get scoreThreshold during execution |
| setScoreThreshold | Set scoreThreshold during execution |
### Examples
Check out https://github.com/lonekorean/diff-cam-scratchpad (specifically /diff-cam-example and /turret-security) for examples that use diff-cam-engine. Check out https://github.com/lonekorean/diff-cam-feed for a more complex web app powered by diff-cam-engine.
================================================
FILE: diff-cam-engine.js
================================================
var DiffCamEngine = (function() {
var stream; // stream obtained from webcam
var video; // shows stream
var captureCanvas; // internal canvas for capturing full images from video
var captureContext; // context for capture canvas
var diffCanvas; // internal canvas for diffing downscaled captures
var diffContext; // context for diff canvas
var motionCanvas; // receives processed diff images
var motionContext; // context for motion canvas
var initSuccessCallback; // called when init succeeds
var initErrorCallback; // called when init fails
var startCompleteCallback; // called when start is complete
var captureCallback; // called when an image has been captured and diffed
var captureInterval; // interval for continuous captures
var captureIntervalTime; // time between captures, in ms
var captureWidth; // full captured image width
var captureHeight; // full captured image height
var diffWidth; // downscaled width for diff/motion
var diffHeight; // downscaled height for diff/motion
var isReadyToDiff; // has a previous capture been made to diff against?
var pixelDiffThreshold; // min for a pixel to be considered significant
var scoreThreshold; // min for an image to be considered significant
var includeMotionBox; // flag to calculate and draw motion bounding box
var includeMotionPixels; // flag to create object denoting pixels with motion
function init(options) {
// sanity check
if (!options) {
throw 'No options object provided';
}
// incoming options with defaults
video = options.video || document.createElement('video');
motionCanvas = options.motionCanvas || document.createElement('canvas');
captureIntervalTime = options.captureIntervalTime || 100;
captureWidth = options.captureWidth || 640;
captureHeight = options.captureHeight || 480;
diffWidth = options.diffWidth || 64;
diffHeight = options.diffHeight || 48;
pixelDiffThreshold = options.pixelDiffThreshold || 32;
scoreThreshold = options.scoreThreshold || 16;
includeMotionBox = options.includeMotionBox || false;
includeMotionPixels = options.includeMotionPixels || false;
// callbacks
initSuccessCallback = options.initSuccessCallback || function() {};
initErrorCallback = options.initErrorCallback || function() {};
startCompleteCallback = options.startCompleteCallback || function() {};
captureCallback = options.captureCallback || function() {};
// non-configurable
captureCanvas = document.createElement('canvas');
diffCanvas = document.createElement('canvas');
isReadyToDiff = false;
// prep video
video.autoplay = true;
// prep capture canvas
captureCanvas.width = captureWidth;
captureCanvas.height = captureHeight;
captureContext = captureCanvas.getContext('2d');
// prep diff canvas
diffCanvas.width = diffWidth;
diffCanvas.height = diffHeight;
diffContext = diffCanvas.getContext('2d');
// prep motion canvas
motionCanvas.width = diffWidth;
motionCanvas.height = diffHeight;
motionContext = motionCanvas.getContext('2d');
requestWebcam();
}
function requestWebcam() {
var constraints = {
audio: false,
video: { width: captureWidth, height: captureHeight }
};
navigator.mediaDevices.getUserMedia(constraints)
.then(initSuccess)
.catch(initError);
}
function initSuccess(requestedStream) {
stream = requestedStream;
initSuccessCallback();
}
function initError(error) {
console.log(error);
initErrorCallback();
}
function start() {
if (!stream) {
throw 'Cannot start after init fail';
}
// streaming takes a moment to start
video.addEventListener('canplay', startComplete);
video.srcObject = stream;
}
function startComplete() {
video.removeEventListener('canplay', startComplete);
captureInterval = setInterval(capture, captureIntervalTime);
startCompleteCallback();
}
function stop() {
clearInterval(captureInterval);
video.src = '';
motionContext.clearRect(0, 0, diffWidth, diffHeight);
isReadyToDiff = false;
}
function capture() {
// save a full-sized copy of capture
captureContext.drawImage(video, 0, 0, captureWidth, captureHeight);
var captureImageData = captureContext.getImageData(0, 0, captureWidth, captureHeight);
// diff current capture over previous capture, leftover from last time
diffContext.globalCompositeOperation = 'difference';
diffContext.drawImage(video, 0, 0, diffWidth, diffHeight);
var diffImageData = diffContext.getImageData(0, 0, diffWidth, diffHeight);
if (isReadyToDiff) {
var diff = processDiff(diffImageData);
motionContext.putImageData(diffImageData, 0, 0);
if (diff.motionBox) {
motionContext.strokeStyle = '#fff';
motionContext.strokeRect(
diff.motionBox.x.min + 0.5,
diff.motionBox.y.min + 0.5,
diff.motionBox.x.max - diff.motionBox.x.min,
diff.motionBox.y.max - diff.motionBox.y.min
);
}
captureCallback({
imageData: captureImageData,
score: diff.score,
hasMotion: diff.score >= scoreThreshold,
motionBox: diff.motionBox,
motionPixels: diff.motionPixels,
getURL: function() {
return getCaptureUrl(this.imageData);
},
checkMotionPixel: function(x, y) {
return checkMotionPixel(this.motionPixels, x, y)
}
});
}
// draw current capture normally over diff, ready for next time
diffContext.globalCompositeOperation = 'source-over';
diffContext.drawImage(video, 0, 0, diffWidth, diffHeight);
isReadyToDiff = true;
}
function processDiff(diffImageData) {
var rgba = diffImageData.data;
// pixel adjustments are done by reference directly on diffImageData
var score = 0;
var motionPixels = includeMotionPixels ? [] : undefined;
var motionBox = undefined;
for (var i = 0; i < rgba.length; i += 4) {
var pixelDiff = rgba[i] * 0.3 + rgba[i + 1] * 0.6 + rgba[i + 2] * 0.1;
var normalized = Math.min(255, pixelDiff * (255 / pixelDiffThreshold));
rgba[i] = 0;
rgba[i + 1] = normalized;
rgba[i + 2] = 0;
if (pixelDiff >= pixelDiffThreshold) {
score++;
coords = calculateCoordinates(i / 4);
if (includeMotionBox) {
motionBox = calculateMotionBox(motionBox, coords.x, coords.y);
}
if (includeMotionPixels) {
motionPixels = calculateMotionPixels(motionPixels, coords.x, coords.y, pixelDiff);
}
}
}
return {
score: score,
motionBox: score > scoreThreshold ? motionBox : undefined,
motionPixels: motionPixels
};
}
function calculateCoordinates(pixelIndex) {
return {
x: pixelIndex % diffWidth,
y: Math.floor(pixelIndex / diffWidth)
};
}
function calculateMotionBox(currentMotionBox, x, y) {
// init motion box on demand
var motionBox = currentMotionBox || {
x: { min: coords.x, max: x },
y: { min: coords.y, max: y }
};
motionBox.x.min = Math.min(motionBox.x.min, x);
motionBox.x.max = Math.max(motionBox.x.max, x);
motionBox.y.min = Math.min(motionBox.y.min, y);
motionBox.y.max = Math.max(motionBox.y.max, y);
return motionBox;
}
function calculateMotionPixels(motionPixels, x, y, pixelDiff) {
motionPixels[x] = motionPixels[x] || [];
motionPixels[x][y] = true;
return motionPixels;
}
function getCaptureUrl(captureImageData) {
// may as well borrow captureCanvas
captureContext.putImageData(captureImageData, 0, 0);
return captureCanvas.toDataURL();
}
function checkMotionPixel(motionPixels, x, y) {
return motionPixels && motionPixels[x] && motionPixels[x][y];
}
function getPixelDiffThreshold() {
return pixelDiffThreshold;
}
function setPixelDiffThreshold(val) {
pixelDiffThreshold = val;
}
function getScoreThreshold() {
return scoreThreshold;
}
function setScoreThreshold(val) {
scoreThreshold = val;
}
return {
// public getters/setters
getPixelDiffThreshold: getPixelDiffThreshold,
setPixelDiffThreshold: setPixelDiffThreshold,
getScoreThreshold: getScoreThreshold,
setScoreThreshold: setScoreThreshold,
// public functions
init: init,
start: start,
stop: stop
};
})();
gitextract_50974trg/ ├── .gitignore ├── LICENSE.md ├── README.md └── diff-cam-engine.js
SYMBOL INDEX (18 symbols across 1 files)
FILE: diff-cam-engine.js
function init (line 28) | function init(options) {
function requestWebcam (line 79) | function requestWebcam() {
function initSuccess (line 90) | function initSuccess(requestedStream) {
function initError (line 95) | function initError(error) {
function start (line 100) | function start() {
function startComplete (line 110) | function startComplete() {
function stop (line 116) | function stop() {
function capture (line 123) | function capture() {
function processDiff (line 167) | function processDiff(diffImageData) {
function calculateCoordinates (line 203) | function calculateCoordinates(pixelIndex) {
function calculateMotionBox (line 210) | function calculateMotionBox(currentMotionBox, x, y) {
function calculateMotionPixels (line 225) | function calculateMotionPixels(motionPixels, x, y, pixelDiff) {
function getCaptureUrl (line 232) | function getCaptureUrl(captureImageData) {
function checkMotionPixel (line 238) | function checkMotionPixel(motionPixels, x, y) {
function getPixelDiffThreshold (line 242) | function getPixelDiffThreshold() {
function setPixelDiffThreshold (line 246) | function setPixelDiffThreshold(val) {
function getScoreThreshold (line 250) | function getScoreThreshold() {
function setScoreThreshold (line 254) | function setScoreThreshold(val) {
Condensed preview — 4 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (15K chars).
[
{
"path": ".gitignore",
"chars": 20,
"preview": ".DS_Store\nThumbs.db\n"
},
{
"path": "LICENSE.md",
"chars": 1138,
"preview": "Copyright (c) 2016 Will Boyd\n\nThis software is released under the MIT license: http://opensource.org/licenses/MIT\n\nPermi"
},
{
"path": "README.md",
"chars": 4480,
"preview": "# diff-cam-engine\n\nCore engine for building motion detection web apps.\n\n### Usage\n\n`diff-cam-engine.js` provides a `Diff"
},
{
"path": "diff-cam-engine.js",
"chars": 8046,
"preview": "var DiffCamEngine = (function() {\n\tvar stream;\t\t\t\t\t// stream obtained from webcam\n\tvar video;\t\t\t\t\t// shows stream\n\tvar c"
}
]
About this extraction
This page contains the full source code of the lonekorean/diff-cam-engine GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 4 files (13.4 KB), approximately 3.4k tokens, and a symbol index with 18 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.