opencv
Version:
Node Bindings to OpenCV
272 lines (218 loc) • 7.08 kB
JavaScript
var Stream = require('stream').Stream
, Buffers = require('buffers')
, util = require('util')
, path = require('path')
, os = require('os');
var cv = module.exports = require('./bindings');
var Matrix = cv.Matrix
, ImageStream
, ImageDataStream
, ObjectDetectionStream
, VideoStream;
if (cv.CascadeClassifier) {
Matrix.prototype.detectObject = function(classifier, opts, cb){
var face_cascade;
opts = opts || {};
cv._detectObjectClassifiers = cv._detectObjectClassifiers || {};
if (!(face_cascade = cv._detectObjectClassifiers[classifier])){
face_cascade = new cv.CascadeClassifier(classifier);
cv._detectObjectClassifiers[classifier] = face_cascade;
}
face_cascade.detectMultiScale(this, cb, opts.scale, opts.neighbors
, opts.min && opts.min[0], opts.min && opts.min[1]);
}
} else {
cv.Matrix.prototype.detectObject = function() {
throw new Error("You need to install OpenCV with OBJDETECT module");
}
}
cv.Matrix.prototype.inspect = function(){
var size = (this.size()||[]).join('x');
return "[ Matrix " + size + " ]";
}
// we use the Opencv constants naming convention to extract the number of bytes (8, 16, 32, 64), and the number of channels from constants names
var getNumberOfBytesAndChannelsPerType = function(type){
var regExp = /CV_([0-9]+)([A-Z]+)([0-9]+)/;
for(var k in cv.Constants) if(cv.Constants.hasOwnProperty(k) && k.match(regExp) && cv.Constants[k] === type){
var bytes, channels, dataType;
k.replace(regExp, function(all, b, l, c){
bytes = b;
channels = c;
dataType = l[0]
});
return {
bytes : parseInt(bytes),
channels : !isNaN(parseInt(channels)) && parseInt(channels),
dataType : dataType,
label : k
};
}
};
var getBufferMethodName = function(bytes, dataType, endianness, read){
var fnName;
if(read){
fnName = "read";
} else {
fnName = "write";
}
if (bytes === 32 && (dataType === "F" || dataType === "S")){
if(dataType === "F"){
fnName += "Float"+endianness;
} else {//dataType === "S"
fnName += "Int32"+endianness;
}
} else if(bytes === 8){
fnName += (dataType === "U" ? "U" : "")+"Int8";
} else if(bytes === 16){
fnName += (dataType === "U" ? "U" : "")+"Int16"+endianness;
} else {
throw("This matrix type (CV_"+bytes+dataType+") is not compatible with fromArray/toArray")
}
return fnName;
};
Matrix.fromArray = function(arr, type){
var n_bytes;
var bytesAndChannels = getNumberOfBytesAndChannelsPerType(type);
var bytes = bytesAndChannels.bytes;
var channels = bytesAndChannels.channels;
var dataType = bytesAndChannels.dataType;
var label = bytesAndChannels.label;
if(!Array.isArray(arr) ||!Array.isArray(arr[0]) || !Array.isArray(arr[0][0]) || (channels && arr[0][0].length !== channels)) {
throw(new Error("Input array must be a 3-level array/matrix with size rows x cols x channels corresponding to dataType ("+label+")"));
}
if(!channels){
channels = 1;
}
var rows = arr.length;
var cols = arr[0].length;
var mat = new cv.Matrix(rows, cols, type);
var n_bytes = bytes/8;
var buf = new Buffer(rows * cols * channels * n_bytes);
buf.fill(0);
var fnName = getBufferMethodName(bytes, dataType, os.endianness(), false)
for(var i=0;i<rows * cols * channels;i++){
var c = i%channels;
var r = Math.floor(i/channels);
var y = r%cols;
var x = Math.floor(r/cols);
buf[fnName](arr[x][y][c], i*n_bytes);
}
mat.put(buf);
return mat;
}
Matrix.prototype.toArray = function(){
var size = this.size();
var buf = this.getData();
var type = this.type();
var bytesAndChannels = getNumberOfBytesAndChannelsPerType(type);
var bytes = bytesAndChannels.bytes;
var channels = bytesAndChannels.channels || this.channels();
var dataType = bytesAndChannels.dataType;
var n_bytes = bytes/8;
var fnName = getBufferMethodName(bytes, dataType, os.endianness(), true)
var res = []
for(var i = 0; i < size[0]; i++){
var row = [];
for(var j = 0; j < size[1]; j++){
var channelsValues = [];
for(var k = 0; k < channels; k++){
var index = (i*size[1]+j)*channels+k;
channelsValues.push(buf[fnName](index*n_bytes));
}
row.push(channelsValues);
}
res.push(row);
}
return res;
}
ImageStream = cv.ImageStream = function(){
this.writable = true;
}
util.inherits(ImageStream, Stream);
ImageStream.prototype.write = function(buf){
var self = this;
cv.readImage(buf, function(err, matrix){
if (err) return self.emit('error', err);
self.emit('data', matrix);
});
}
ImageDataStream = cv.ImageDataStream = function(){
this.data = Buffers([]);
this.writable = true;
}
util.inherits(ImageDataStream, Stream);
ImageDataStream.prototype.write = function(buf){
this.data.push(buf);
return true;
}
ImageDataStream.prototype.end = function(b){
var self = this;
if (b) ImageStream.prototype.write.call(this, b);
var buf = this.data.toBuffer();
cv.readImage(buf, function(err, im){
if (err) return self.emit('error', err);
self.emit('load', im);
});
}
if (cv.CascadeClassifier) {
ObjectDetectionStream = cv.ObjectDetectionStream = function(cascade, opts){
this.classifier = new cv.CascadeClassifier(cascade);
this.opts = opts || {};
this.readable = true;
this.writable = true;
}
util.inherits(ObjectDetectionStream, Stream);
ObjectDetectionStream.prototype.write = function(m){
var self = this;
this.classifier.detectMultiScale(m, function(err, objs){
if (err) return self.emit('error', err);
self.emit('data', objs, m);
}
, this.opts.scale
, this.opts.neighbors
, this.opts.min && this.opts.min[0]
, this.opts.min && this.opts.min[1]);
}
}
if (cv.VideoCapture) {
var VideoCapture = cv.VideoCapture
VideoStream = cv.VideoStream = function(src){
if (!(src instanceof VideoCapture)) src = new VideoCapture(src);
this.video = src;
this.readable = true;
this.paused = false;
}
util.inherits(VideoStream, Stream);
VideoStream.prototype.read = function(){
var self = this;
var frame = function(){
self.video.read(function(err, mat){
if (err) return self.emit('error', err);
self.emit('data', mat);
if (!self.paused) process.nextTick(frame);
})
}
frame();
}
VideoStream.prototype.pause = function(){
this.paused = true;
}
VideoStream.prototype.resume = function(){
this.paused = false;
this.read();
}
VideoCapture.prototype.toStream = function(){
return new VideoStream(this);
}
}
// Provide cascade data for faces etc.
var CASCADES = {
FACE_CASCADE: 'haarcascade_frontalface_alt.xml'
, EYE_CASCADE: 'haarcascade_eye.xml'
, EYEGLASSES_CASCADE: 'haarcascade_eye_tree_eyeglasses.xml'
, FULLBODY_CASCADE: 'haarcascade_fullbody.xml'
, CAR_SIDE_CASCADE: 'hogcascade_cars_sideview.xml'
}
Object.keys(CASCADES).forEach(function(k){
cv[k] = path.resolve(__dirname, '../data', CASCADES[k])
})