├── .gitignore ├── server ├── lib │ ├── config │ │ └── server.js │ └── routes │ │ ├── index.js │ │ └── socket.js ├── package.json └── server.js ├── client ├── styles.css ├── index.html └── app.js ├── README.md └── LICENSE.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /server/lib/config/server.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | module.exports = { 4 | httpPort: 8080, 5 | staticFolder: path.join(__dirname + '/../../../client') 6 | }; -------------------------------------------------------------------------------- /client/styles.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding-top: 50px; 3 | } 4 | 5 | .center { 6 | text-align: center; 7 | } 8 | 9 | #canvas-video { 10 | width: 640px; 11 | height: 480px; 12 | border: 1px solid #ccc; 13 | } -------------------------------------------------------------------------------- /server/lib/routes/index.js: -------------------------------------------------------------------------------- 1 | // Load the page 2 | // NB: This needs to be the last route added 3 | exports.serveIndex = function (app, staticFolder) { 4 | app.get('*', function (req, res) { 5 | res.sendFile('index.html', { root: staticFolder }); 6 | }); 7 | }; -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edi-cam", 3 | "private": true, 4 | "scripts": { 5 | "start": "node server.js" 6 | }, 7 | "dependencies": { 8 | "express": "4.10.1", 9 | "morgan": "1.4.1", 10 | "opencv": "git+https://github.com/peterbraden/node-opencv.git", 11 | "socket.io": "1.2.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | face-detection-node-opencv 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # face-detection-node-opencv 2 | 3 | Real-time face detection using OpenCV, Node.js, and WebSockets. 4 | 5 | Click [here](http://youtu.be/v2SY0naPBFw) to see it in action. 6 | 7 | ## Requirements 8 | 9 | * [Node.js](http://nodejs.org/) 10 | * [OpenCV 3.4.0](http://opencv.org/) 11 | * May also work with older versions of OpenCV, but most recently tested with OpenCV 3.4.0 12 | * A webcam, e.g. laptop-integrated webcam, USB webcam 13 | 14 | ## Installing Node.js packages 15 | 16 | * Navigate to the `server` directory 17 | * To install the packages: `npm install` 18 | 19 | ## Running the demo 20 | 21 | * Make sure you are still in the `server` directory 22 | * To run the server: `node server.js` 23 | * To run the demo locally, open a browser and go to `localhost:8080` 24 | 25 | The app should be up and running! 26 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // modules 2 | var express = require('express') 3 | , http = require('http') 4 | , morgan = require('morgan'); 5 | 6 | // configuration files 7 | var configServer = require('./lib/config/server'); 8 | 9 | // app parameters 10 | var app = express(); 11 | app.set('port', configServer.httpPort); 12 | app.use(express.static(configServer.staticFolder)); 13 | app.use(morgan('dev')); 14 | 15 | // serve index 16 | require('./lib/routes').serveIndex(app, configServer.staticFolder); 17 | 18 | // HTTP server 19 | var server = http.createServer(app); 20 | server.listen(app.get('port'), function () { 21 | console.log('HTTP server listening on port ' + app.get('port')); 22 | }); 23 | 24 | // WebSocket server 25 | var io = require('socket.io')(server); 26 | io.on('connection', require('./lib/routes/socket')); 27 | 28 | module.exports.app = app; -------------------------------------------------------------------------------- /client/app.js: -------------------------------------------------------------------------------- 1 | // MODIFY THIS TO THE APPROPRIATE URL IF IT IS NOT BEING RUN LOCALLY 2 | var socket = io.connect('http://localhost'); 3 | 4 | var canvas = document.getElementById('canvas-video'); 5 | var context = canvas.getContext('2d'); 6 | var img = new Image(); 7 | 8 | // show loading notice 9 | context.fillStyle = '#333'; 10 | context.fillText('Loading...', canvas.width/2-30, canvas.height/3); 11 | 12 | socket.on('frame', function (data) { 13 | // Reference: http://stackoverflow.com/questions/24107378/socket-io-began-to-support-binary-stream-from-1-0-is-there-a-complete-example-e/24124966#24124966 14 | var uint8Arr = new Uint8Array(data.buffer); 15 | var str = String.fromCharCode.apply(null, uint8Arr); 16 | var base64String = btoa(str); 17 | 18 | img.onload = function () { 19 | context.drawImage(this, 0, 0, canvas.width, canvas.height); 20 | }; 21 | img.src = 'data:image/png;base64,' + base64String; 22 | }); -------------------------------------------------------------------------------- /server/lib/routes/socket.js: -------------------------------------------------------------------------------- 1 | var cv = require('opencv'); 2 | 3 | // camera properties 4 | var camWidth = 320; 5 | var camHeight = 240; 6 | var camFps = 10; 7 | var camInterval = 1000 / camFps; 8 | 9 | // face detection properties 10 | var rectColor = [0, 255, 0]; 11 | var rectThickness = 2; 12 | 13 | // initialize camera 14 | var camera = new cv.VideoCapture(0); 15 | camera.setWidth(camWidth); 16 | camera.setHeight(camHeight); 17 | 18 | module.exports = function (socket) { 19 | setInterval(function() { 20 | camera.read(function(err, im) { 21 | if (err) throw err; 22 | 23 | im.detectObject('./node_modules/opencv/data/haarcascade_frontalface_alt2.xml', {}, function(err, faces) { 24 | if (err) throw err; 25 | 26 | for (var i = 0; i < faces.length; i++) { 27 | face = faces[i]; 28 | im.rectangle([face.x, face.y], [face.width, face.height], rectColor, rectThickness); 29 | } 30 | 31 | socket.emit('frame', { buffer: im.toBuffer() }); 32 | }); 33 | }); 34 | }, camInterval); 35 | }; 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Esther Jun Kim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------