65 Commits

Autor SHA1 Mensagem Data
Peter Braden 1322e58bd9 VideoWriter 2013-03-05 17:42:58 -08:00
Peter Braden daca1a14cb PyrDown / PyrUp 2013-03-05 11:28:37 -08:00
Peter Braden 11db468dcf v0.0.12 2013-03-05 11:19:25 -08:00
Peter Braden 55cbf0a37f add matrix clone 2013-03-04 12:03:35 -08:00
Peter Braden 77279a33c0 Add basic highgui window support 2013-03-04 11:12:33 -08:00
Peter Braden 6225a3c0f0 add keywords to package.json 2013-02-28 18:14:37 -08:00
Peter Braden f2ec2cd3ed Allow video capture to be streamed 2013-02-26 19:07:39 -08:00
Peter Braden c3d3f0f4b4 beta 0.0.11 2013-02-26 17:23:19 -08:00
Peter Braden 0840e0b2e2 Merge branch 'provide-cascades' into beta
Conflicts:
	lib/opencv.js
2013-02-26 16:52:36 -08:00
Peter Braden 4ca165635e provide image to event 2013-02-26 16:51:45 -08:00
Peter Braden a8e5ac4b6b Fix path 2013-02-26 16:27:34 -08:00
Peter Braden 24f255a161 unit test for imagestream 2013-02-26 16:23:14 -08:00
Peter Braden 0bb7fe9918 Add shortcut for face cascade file -> cv.FACE_CASCADE 2013-02-26 15:34:27 -08:00
Peter Braden 0cb436e930 Changing ImageStream -> ImageDataStream and creating ImageStream.
To pipe data into an image matrix, you now use ImageDataStream.

ImageStream is now a stream that transforms image buffers into matrices.
2013-02-26 15:26:54 -08:00
Peter Braden 3a7469fe52 catch older versions in build step 2013-02-26 15:00:29 -08:00
Peter Braden e1e1267bc3 Merge pull request #37 from ryansouza/node-08
Have travis test against node 0.8 as well
2013-02-26 10:43:49 -08:00
Peter Braden 32a1b76852 Merge pull request #36 from ryansouza/contour-stuff
Include more info about working with contours
2013-02-26 10:43:30 -08:00
Ryan Souza a320ff9af0 Have travis test against node 0.8 as well 2013-02-25 23:06:21 -08:00
Ryan Souza 521139a76e Include more info about working with contours
Some readme examples for using functions on a Contours object
Examples detecting and playing with simple shapes and contours
2013-02-25 22:58:14 -08:00
Peter Braden 454fd1188a s/cols,rows/rows,cols/g re #30 2013-02-13 13:30:31 -08:00
Peter Braden 0de039956e Clarify width, height params re #30 2013-02-12 05:24:50 -08:00
Peter Braden bb1fa037f3 Merge pull request #34 from mintplant/contours
Contours expansion: minAreaRect and convexHull bindings, expose underlying point data
2013-02-11 12:18:16 -08:00
Michael Smith 84e5f255df Add ConvexHull binding for Contours class 2013-02-10 15:26:58 -08:00
Michael Smith 4f7968e567 Expose MinAreaRect binding for Contours 2013-02-10 15:25:26 -08:00
Michael Smith a197586673 Expose point data from Contours
Add in a Point method to the Contours class to access the underlying
point coordinates from the JavaScript side.
2013-02-10 15:04:37 -08:00
Peter Braden e64b558bbd v0.0.10 2013-02-05 14:39:19 -08:00
Peter Braden c057d411a5 Remove todo - that's what github issues are for 2013-02-05 14:27:46 -08:00
Peter Braden fb55cec501 Merge pull request #29 from jtlebi/master
New bindings
2013-02-05 14:25:55 -08:00
Peter Braden be92fe5e65 Strip trailing spaces in matrix.cc re #29 2013-02-05 14:24:05 -08:00
Peter Braden 642f111f9d Merge branch 'master' of github.com:peterbraden/node-opencv 2013-02-05 14:21:35 -08:00
Peter Braden 8d6180bcaf Merge pull request #26 from prabirshrestha/buffers
fixed require('buffers')
2013-02-04 09:00:34 -08:00
Peter Braden 63891ae4fa Merge pull request #31 from gluxon/master
Add bindings for inRange
2013-02-03 11:01:33 -08:00
Brandon Cheng 22bdf646b1 Add bindings for cv::inRange 2013-01-27 18:16:30 -05:00
Jean-Tiare LE BIGOT 3b3c591517 add bindings for boundingBox in contours --> returns an (x, y, width, height) object 2013-01-20 15:39:51 +01:00
Jean-Tiare LE BIGOT abf5e9948c add bindings for cornerCount (size in OpenCV) in contours + fix bool instead of double in approxPolyDP 2013-01-20 15:07:20 +01:00
Jean-Tiare LE BIGOT d2e8b1969f add bindings for isConvex in contours 2013-01-20 14:20:33 +01:00
Jean-Tiare LE BIGOT e3280ddc1e add bindings for approxPolyDP in contours 2013-01-20 14:16:16 +01:00
Jean-Tiare LE BIGOT a94bc32d5f add bindings for arcLength in contours 2013-01-20 14:02:48 +01:00
Jean-Tiare LE BIGOT 7be98efacf add bindings for gaussianBlur 2013-01-20 01:05:43 +01:00
Jean-Tiare LE BIGOT b73ccb67fa add bindings for 'erode' 2013-01-19 23:28:54 +01:00
prabirshrestha 3efe469ea0 fixed require('buffers') 2012-12-03 13:34:50 -05:00
Peter Braden 8b899d073a don't save images in example 2012-11-29 13:44:31 -08:00
Peter Braden 2d0e747a4d Getting rid of old build script 2012-11-27 17:13:57 -08:00
Peter Braden 644f6797d3 Merge branch 'camshift' 2012-11-27 17:09:18 -08:00
Peter Braden f90cea5ffd std::vector, explicitly 2012-11-27 16:54:29 -08:00
Peter Braden 4f56834420 correct import. Linux is way whinier about this sort of stuff. 2012-11-27 16:52:05 -08:00
Peter Braden ab686c5445 specify std::dtring 2012-11-27 16:48:20 -08:00
Peter Braden dcf7462b13 travis is puking again 2012-11-27 16:42:33 -08:00
Peter Braden 7d43360b72 fix unit test 2012-11-27 16:36:18 -08:00
Peter Braden 20fa7a1c51 ignore out jpgs and examples avi's - too big for repos and test output 2012-11-27 16:15:31 -08:00
Peter Braden 0ab26f913c WIP 2012-11-27 16:13:23 -08:00
Peter Braden c4aac1d201 Allow specifying which image channel to use for the back projection 2012-11-27 16:08:58 -08:00
Peter Braden 6df8bbbb9b WIP on camshift re #22 - Got a working example but still unreliable
This is a combination of 4 commits.
WIP on camshift - need to focus the histogram on the ROI

Weird fringing issue, but otherwise tracking objects successfully

motion track example

WIP on camshift

WIP on camshift - have a smoke example, but bad results are coming back.
2012-11-26 20:11:59 -08:00
Peter Braden 33146b15e0 stubbing test for multiple imports bug 2012-11-19 12:39:49 -08:00
Contra c50cdf35a4 allow thickness and color args for ellipse 2012-11-19 12:15:59 -08:00
Peter Braden fa6e6b4da1 poitential fix for null pointer error. Test still screwed though. 2012-11-19 12:14:18 -08:00
Peter Braden b6468cff4e 0.0.9 2012-11-12 13:33:53 -08:00
Peter Braden e33afd97ef WIP on TrackedObject re #22 2012-11-12 13:30:26 -08:00
Peter Braden a9717c7dc0 WIP on camshift re #22 2012-11-12 10:54:37 -08:00
Peter Braden b5fe027703 setting up trackedobject 2012-11-09 17:29:54 -08:00
Peter Braden 3a1477c527 WIP on CamShift with TrackedObject re #22 2012-11-09 16:48:31 -08:00
Peter Braden e4c0ddd4e4 changelog 2012-11-09 10:29:46 -08:00
Peter Braden d71b0ae7b3 if toBuffer has a cb, run it async. re #21 2012-11-09 10:21:41 -08:00
Peter Braden ac6c685b56 Fix async toBufffer test 2012-10-29 23:48:12 -07:00
Peter Braden 56fd1824d3 WIP on an async toBuffer re #21 2012-10-29 23:10:34 -07:00
35 arquivos alterados com 1286 adições e 167 exclusões
+2 -1
Ver Arquivo
@@ -3,4 +3,5 @@ build
.DS_Store
node_modules
npm-debug.log
out.jpg
out*.jpg
examples/*.avi
+2
Ver Arquivo
@@ -1,8 +1,10 @@
language: node_js
node_js:
- 0.6
- 0.8
before_install:
- sudo apt-get update
- sudo apt-get install libcv-dev
- sudo apt-get install libopencv-dev
- sudo apt-get install libhighgui-dev
+91 -7
Ver Arquivo
@@ -29,7 +29,7 @@ Or to build the repo:
cv.readImage("./examples/test.jpg", function(err, im){
im.detectObject("./data/haarcascade_frontalface_alt.xml", {}, function(err, faces){
im.detectObject(cv.FACE_CASCADE, {}, function(err, faces){
for (var i=0;i<faces.length; i++){
var x = faces[i]
im.ellipse(x.x + x.width/2, x.y + x.height/2, x.width/2, x.height/2);
@@ -49,7 +49,11 @@ base datastructure in OpenCV. Things like images are just matrices of pixels.
#### Creation
new Matrix(width, height)
new Matrix(rows, cols)
Or if you're thinking of a Matrix as an image:
new Matrix(height, width)
Or you can use opencv to read in image files. Supported formats are in the OpenCV docs, but jpgs etc are supported.
@@ -61,15 +65,30 @@ Or you can use opencv to read in image files. Supported formats are in the OpenC
...
})
If you need to pipe data into an image, you can use an imagestream:
If you need to pipe data into an image, you can use an ImageDataStream:
var s = new cv.ImageDataStream()
s.on('load', function(matrix){
...
})
fs.createReadStream('./examples/test.jpg').pipe(s);
If however, you have a series of images, and you wish to stream them into a
stream of Matrices, you can use an ImageStream. Thus:
var s = new cv.ImageStream()
s.on('load', function(matrix){
...
})
s.on('data', function(matrix){
...
})
ardrone.createPngStream().pipe(s);
Note: Each 'data' event into the ImageStream should be a complete image buffer.
fs.createReadStream('./examples/test.jpg').pipe(s);
#### Accessing Data
@@ -113,6 +132,7 @@ detection. This can be used for face detection etc.
mat.detectObject(haar_cascade_xml, opts, function(err, matches){})
For convenience in face recognition, cv.FACE_CASCADE is a cascade that can be used for frontal face recognition.
Also:
@@ -125,7 +145,71 @@ Also:
mat.drawContour
mat.drawAllContours
### Using Contours
`findContours` returns a `Contours` collection object, not a native array. This object provides
functions for accessing, computing with, and altering the contours contained in it.
See [relevant source code](src/Contours.cc) and [examples](examples/)
var contours = im.findContours;
# Count of contours in the Contours object
contours.size();
# Count of corners(verticies) of contour `index`
contours.cornerCount(index);
# Access vertex data of contours
for(var c = 0; c < contours.size(); ++c) {
console.log("Contour " + c);
for(var i = 0; i < contours.cornerCount(c); ++i) {
var point = contours.point(c, i);
console.log("(" + point.x + "," + point.y + ")");"
}
}
# Computations of contour `index`
contours.area(index);
contours.arcLength(index, isClosed);
contours.boundingRect(index);
contours.minAreaRect(index);
contours.isConvex(index);
# Destructively alter contour `index`
contours.approxPolyDP(index, epsilon, isClosed);
contours.convexHull(index, clockwise);
## MIT License
The library is distributed under the MIT License - if for some reason that
doesn't work for you please get in touch.
## Changelog
#### 0.0.12
- Matrix clone()
- NamedWindow Support
#### 0.0.11
- Bug Fixes
- ImageStream becomes ImageDataStream, and new ImageStream allows multiple images to be
streamed as matrices, for example, with an object detection stream.
- @ryansouza improved documentation
- Correcting matrix constructor (thanks @gluxon)
- @Michael Smith expanded Contours functionality.
Thanks all!
#### 0.0.10
- Bug Fixes
- @Contra added code that allows thickness and color args for ellipse
- Camshift Support
- @jtlebi added bindings for erode, gaussianBlur, arcLength, approxPolyDP, isConvex, cornerCount
- @gluxon added bindings for inRange
Thanks everyone!
#### 0.0.9
- toBuffer can now take a callback and be run async (re #21)
-45
Ver Arquivo
@@ -1,45 +0,0 @@
im.calcHistograms(function(err, hist){})
im.calcHistograms(mask, function(err, hist){})
## Face recognition TODO
// Load Database
// TODO<
cv.loadImage('test.jpg', function(err, im){
im.detectObject("front-face.xml", {}, function(err, faces){
_.each(faces, function(v){
// TODO {
var section = im.slice(v.x, v.y, v.x + v.width, v.y + v.height);
section.convertGrayscale()
section.resize(WID, HEIGHT);
section.equaliseHistogram();
// } TODO
})
})
})
-----
http://www.athile.net/library/wiki/index.php?title=Library/V8/Tutorial#Wrapping_a_Javascript_function_as_a_std::function.3C.3E
https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/
+4 -1
Ver Arquivo
@@ -9,12 +9,15 @@
, "src/Contours.cc"
, "src/Point.cc"
, "src/VideoCaptureWrap.cc"
, "src/VideoWriter.cc"
, "src/CamShift.cc"
, "src/HighGUI.cc"
]
, 'libraries': [
'<!@(pkg-config --libs opencv)'
]
, 'cflags': [
'<!@(pkg-config --cflags --libs opencv)'
'<!@(pkg-config --cflags --libs "opencv >= 2.3.1" )'
, '-Wall'
]
, 'cflags!' : [ '-fno-exceptions']
-6
Ver Arquivo
@@ -1,6 +0,0 @@
#!/bin/bash
node-gyp rebuild &&
cd examples &&
#node face_detection.js
node $1
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 9.4 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 13 KiB

+12
Ver Arquivo
@@ -0,0 +1,12 @@
var cv = require('../lib/opencv');
// (B)lue, (G)reen, (R)ed
var lower_threshold = [46, 57, 83];
var upper_threshold = [80, 96, 115];
cv.readImage('./coin1.jpg', function(err, im) {
im.inRange(lower_threshold, upper_threshold);
im.save('./coin_detected.jpg');
});
Arquivo executável
+52
Ver Arquivo
@@ -0,0 +1,52 @@
#!/usr/bin/env node
//
// Detects triangles and quadrilaterals
//
var cv = require('../lib/opencv');
var lowThresh = 0;
var highThresh = 100;
var nIters = 2;
var minArea = 2000;
var BLUE = [0, 255, 0]; //B, G, R
var RED = [0, 0, 255]; //B, G, R
var GREEN = [0, 255, 0]; //B, G, R
var WHITE = [255, 255, 255]; //B, G, R
cv.readImage('./shapes.jpg', function(err, im) {
var out = new cv.Matrix(im.height(), im.width());
im.convertGrayscale();
im_canny = im.copy();
im_canny.canny(lowThresh, highThresh);
im_canny.dilate(nIters);
contours = im_canny.findContours();
for(i = 0; i < contours.size(); i++) {
if(contours.area(i) < minArea) continue;
var arcLength = contours.arcLength(i, true);
contours.approxPolyDP(i, 0.01 * arcLength, true);
switch(contours.cornerCount(i)) {
case 3:
out.drawContour(contours, i, GREEN);
break;
case 4:
out.drawContour(contours, i, RED);
break;
default:
out.drawContour(contours, i, WHITE);
}
}
out.save('./out.png');
});
+1
Ver Arquivo
@@ -0,0 +1 @@
mencoder examples/motion.mov -ovc raw -vf format=i420 -nosound -o examples/motion.avi
+24
Ver Arquivo
@@ -0,0 +1,24 @@
var cv = require('../lib/opencv')
var vid = new cv.VideoCapture("/Users/peterbraden/Desktop/repos/node-opencv/examples/motion.avi")
vid.read(function(mat){
var track = new cv.TrackedObject(mat, [420, 110, 490, 170], {channel: "value"});
var x = 0;
var iter = function(){
vid.read(function(m2){
x++;
var rec = track.track(m2)
console.log(">>", x, ":" , rec)
if (x % 10 == 0){
m2.rectangle([rec[0], rec[1]], [rec[2], rec[3]])
// m2.save('./out-motiontrack-' + x + '.jpg')
}
if (x<100)
iter();
})
}
iter();
})
Arquivo binário não exibido.
Arquivo executável
+55
Ver Arquivo
@@ -0,0 +1,55 @@
#!/usr/bin/env node
//
// Finds quadrilaterals and fills them with an X
//
var cv = require('../lib/opencv');
var lowThresh = 0;
var highThresh = 100;
var nIters = 2;
var minArea = 2000;
var maxArea = 100000;
var BLUE = [0, 255, 0]; //B, G, R
var RED = [0, 0, 255]; //B, G, R
var GREEN = [0, 255, 0]; //B, G, R
var WHITE = [255, 255, 255]; //B, G, R
cv.readImage('./quads.jpg', function(err, im) {
var out = im.copy();
im.convertGrayscale();
im_canny = im.copy();
im_canny.canny(lowThresh, highThresh);
im_canny.dilate(nIters);
contours = im_canny.findContours();
for(i = 0; i < contours.size(); i++) {
var area = contours.area(i);
if(area < minArea || area > maxArea) continue;
var arcLength = contours.arcLength(i, true);
contours.approxPolyDP(i, 0.01 * arcLength, true);
if(contours.cornerCount(i) != 4) continue;
var points = [
contours.point(i, 0),
contours.point(i, 1),
contours.point(i, 2),
contours.point(i, 3)
]
out.line([points[0].x,points[0].y], [points[2].x, points[2].y], RED);
out.line([points[1].x,points[1].y], [points[3].x, points[3].y], RED);
}
out.save('./out.png');
});
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 111 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 116 KiB

+108 -8
Ver Arquivo
@@ -1,6 +1,7 @@
var Stream = require('stream').Stream
, Buffers = require('buffer')
, util = require('util');
, Buffers = require('buffers')
, util = require('util')
, path = require('path')
var bindings = require('./bindings')
@@ -20,7 +21,15 @@ var matrix = cv.Matrix.prototype;
matrix.detectObject = function(classifier, opts, cb){
opts = opts || {}
var face_cascade = new cv.CascadeClassifier(classifier);
cv._detectObjectClassifiers = cv._detectObjectClassifiers || {}
if (cv._detectObjectClassifiers[classifier]){
var face_cascade = cv._detectObjectClassifiers[classifier];
} else{
var 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]);
}
@@ -32,21 +41,21 @@ matrix.inspect = function(){
}
cv.ImageStream = function(){
cv.ImageDataStream = function(){
this.data = Buffers([])
this.writable = true
}
util.inherits(cv.ImageStream, Stream);
var imagestream = cv.ImageStream.prototype;
util.inherits(cv.ImageDataStream, Stream);
var imagedatastream = cv.ImageDataStream.prototype;
imagestream.write = function(buf){
imagedatastream.write = function(buf){
this.data.push(buf)
return true;
}
imagestream.end = function(b){
imagedatastream.end = function(b){
var self = this;
if (b)
@@ -59,3 +68,94 @@ imagestream.end = function(b){
});
}
cv.ImageStream = function(){
this.writable = true
}
util.inherits(cv.ImageStream, Stream);
var imagestream = cv.ImageStream.prototype;
imagestream.write = function(buf){
var self = this;
cv.readImage(buf, function(err, matrix){
self.emit('data', matrix);
});
}
// Object detect stream
cv.ObjectDetectionStream = function(cascade, opts){
this.classifier = new cv.CascadeClassifier(cascade);
this.opts = opts || {}
this.readable = true;
this.writable = true;
}
util.inherits(cv.ObjectDetectionStream, Stream);
var ods = cv.ObjectDetectionStream.prototype;
ods.write = function(m){
var self = this;
this.classifier.detectMultiScale(m,
function(e, objs){
if (e) { throw e }
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]);
}
// == Video Stream ==
cv.VideoStream = function(src){
if (src instanceof cv.VideoCapture){
this.video = src
} else {
this.video = new cv.VideoCapture(src);
}
this.readable = true;
this.paused = false;
}
util.inherits(cv.VideoStream, Stream);
var videostream = cv.VideoStream.prototype;
cv.VideoCapture.prototype.toStream = function(){
return new cv.VideoStream(this);
}
videostream.read = function(){
var self = this;
var frame = function(){
self.video.read(function(mat){
if (mat.width() && mat.height()){
self.emit('data', mat)
if (!this.paused){
process.nextTick(frame)
}
} else {
self.emit('end')
}
})
}
frame();
}
videostream.pause = function(){
this.paused = true
}
videostream.resume = function(){
this.paused = false
this.read()
}
// Provide cascade data for faces etc.
cv.FACE_CASCADE = path.resolve(__dirname, '../data/haarcascade_frontalface_alt.xml')
+2 -1
Ver Arquivo
@@ -5,7 +5,7 @@
"dependencies": {
"buffers": "0.1.1"
},
"version": "0.0.8",
"version": "0.0.12",
"devDependencies": {
"vows": "*"
},
@@ -14,5 +14,6 @@
"preinstall": "node-gyp clean rebuild",
"test": "vows test/unit.js"
},
"keywords" : ["opencv", "computer", "vision", "quadrocopter"],
"main": "./lib/opencv"
}
+4 -2
Ver Arquivo
@@ -1,6 +1,8 @@
#!/bin/bash
node-gyp rebuild && echo '-- Compiled OK --
node-gyp build && echo '-- Compiled OK --
' && node smoke/smoketest.js && echo '-- Smoke Done, running tests --
' && npm test
' && npm test # && echo '-- Tests Run, runnning examples --
#(building example data)
#' && ./examples/make-example-files.sh && node examples/motion-track.js
+14 -1
Ver Arquivo
@@ -1,4 +1,16 @@
var cv = require('../lib/opencv')
var win = new cv.NamedWindow("foo");
cv.readImage('./examples/stuff.png', function(e, im){
im.pyrDown();
win.show(im);
setTimeout(function(){
win.destroy();
}, 1000)
})
/*
new cv.VideoCapture(0).read(function(mat){
@@ -15,7 +27,7 @@ new cv.VideoCapture(0).read(function(mat){
})
})
*/
cv.readImage("./examples/stuff.png", function(err, im){
@@ -32,3 +44,4 @@ cv.readImage("./examples/stuff.png", function(err, im){
console.log(features)
im.save('./out.jpg');
});
*/
+191
Ver Arquivo
@@ -0,0 +1,191 @@
#include "CamShift.h"
#include "OpenCV.h"
#include "Matrix.h"
#define CHANNEL_HUE 0
#define CHANNEL_SATURATION 1
#define CHANNEL_VALUE 2
Persistent<FunctionTemplate> TrackedObject::constructor;
void
TrackedObject::Init(Handle<Object> target) {
HandleScope scope;
// Constructor
constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(TrackedObject::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("TrackedObject"));
// Prototype
//Local<ObjectTemplate> proto = constructor->PrototypeTemplate();
NODE_SET_PROTOTYPE_METHOD(constructor, "track", Track);
target->Set(String::NewSymbol("TrackedObject"), constructor->GetFunction());
};
Handle<Value>
TrackedObject::New(const Arguments &args) {
HandleScope scope;
if (args.This()->InternalFieldCount() == 0){
JSTHROW_TYPE("Cannot Instantiate without new")
}
Matrix* m = ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
cv::Rect r;
int channel = CHANNEL_HUE;
if (args[1]->IsArray()){
Local<Object> v8rec = args[1]->ToObject();
r = cv::Rect(
v8rec->Get(0)->IntegerValue(),
v8rec->Get(1)->IntegerValue(),
v8rec->Get(2)->IntegerValue() - v8rec->Get(0)->IntegerValue(),
v8rec->Get(3)->IntegerValue() - v8rec->Get(1)->IntegerValue());
} else {
JSTHROW_TYPE("Must pass rectangle to track")
}
if (args[2]->IsObject()){
Local<Object> opts = args[2]->ToObject();
if (opts->Get(String::New("channel"))->IsString()){
v8::String::Utf8Value c(opts->Get(String::New("channel"))->ToString());
std::string cc = std::string(*c);
if (cc == "hue" || cc == "h"){
channel = CHANNEL_HUE;
}
if (cc == "saturation" || cc == "s"){
channel = CHANNEL_SATURATION;
}
if (cc == "value" || cc == "v"){
channel = CHANNEL_VALUE;
}
}
}
TrackedObject *to = new TrackedObject(m->mat, r, channel);
to->Wrap(args.This());
return args.This();
}
void update_chann_image(TrackedObject* t, cv::Mat image){
// Store HSV Hue Image
cv::cvtColor(image, t->hsv, CV_BGR2HSV); // convert to HSV space
//mask out-of-range values
int vmin = 65, vmax = 256, smin = 55;
cv::inRange(t->hsv, //source
cv::Scalar(0, smin, MIN(vmin, vmax), 0), //lower bound
cv::Scalar(180, 256, MAX(vmin, vmax) ,0), //upper bound
t->mask); //destination
//extract the hue channel, split: src, dest channels
std::vector<cv::Mat> hsvplanes;
cv::split(t->hsv, hsvplanes);
t->hue = hsvplanes[t->channel];
}
TrackedObject::TrackedObject(cv::Mat image, cv::Rect rect, int chan){
channel = chan;
update_chann_image(this, image);
prev_rect = rect;
// Calculate Histogram
int hbins = 30, sbins = 32;
int histSizes[] = {hbins, sbins};
//float hranges[] = { 0, 180 };
// saturation varies from 0 (black-gray-white) to
// 255 (pure spectrum color)
float sranges[] = { 0, 256 };
const float* ranges[] = { sranges };
cv::Mat hue_roi = hue(rect);
cv::Mat mask_roi = mask(rect);
cv::calcHist(&hue_roi, 1, 0, mask_roi, hist, 1, histSizes, ranges, true, false);
}
Handle<Value>
TrackedObject::Track(const v8::Arguments& args){
SETUP_FUNCTION(TrackedObject)
if (args.Length() != 1){
v8::ThrowException(v8::Exception::TypeError(v8::String::New("track takes an image param")));
return Undefined();
}
Matrix *im = ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
cv::RotatedRect r;
if (self->prev_rect.x <0 ||
self->prev_rect.y <0 ||
self->prev_rect.width <= 1 ||
self->prev_rect.height <= 1){
return v8::ThrowException(v8::Exception::TypeError(v8::String::New("OPENCV ERROR: prev rectangle is illogical")));
}
update_chann_image(self, im->mat);
cv::Rect backup_prev_rect = cv::Rect(
self->prev_rect.x,
self->prev_rect.y,
self->prev_rect.width,
self->prev_rect.height);
float sranges[] = { 0, 256 };
const float* ranges[] = { sranges };
int channel = 0;
cv::calcBackProject(&self->hue, 1, &channel, self->hist, self->prob, ranges);
r = cv::CamShift(self->prob, self->prev_rect,
cv::TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
cv::Rect bounds = r.boundingRect();
if (bounds.x >=0 && bounds.y >=0 && bounds.width > 1 && bounds.height > 1){
self->prev_rect = bounds;
} else {
//printf("CRAP> %i %i %i %i ;", self->prev_rect.x, self->prev_rect.y, self->prev_rect.width, self->prev_rect.height);
// We have encountered a bug in opencv. Somehow the prev_rect has got mangled, so we
// must reset it to a good value.
self->prev_rect = backup_prev_rect;
}
v8::Local<v8::Array> arr = v8::Array::New(4);
arr->Set(0, Number::New(bounds.x));
arr->Set(1, Number::New(bounds.y));
arr->Set(2, Number::New(bounds.x + bounds.width));
arr->Set(3, Number::New(bounds.y + bounds.height));
/*
cv::Point2f pts[4];
r.points(pts);
for (int i=0; i<8; i+=2){
arr->Set(i, Number::New(pts[i].x));
arr->Set(i+1, Number::New(pts[i].y));
}
*/
return scope.Close(arr);
}
+23
Ver Arquivo
@@ -0,0 +1,23 @@
#include "OpenCV.h"
class TrackedObject: public node::ObjectWrap {
public:
int channel;
cv::Mat hsv;
cv::Mat hue;
cv::Mat mask;
cv::Mat prob;
cv::Mat hist;
cv::Rect prev_rect;
static Persistent<FunctionTemplate> constructor;
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
TrackedObject(cv::Mat image, cv::Rect rect, int channel);
JSFUNC(Track);
};
+152 -3
Ver Arquivo
@@ -23,8 +23,16 @@ Contour::Init(Handle<Object> target) {
//Local<ObjectTemplate> proto = constructor->PrototypeTemplate();
NODE_SET_PROTOTYPE_METHOD(constructor, "point", Point);
NODE_SET_PROTOTYPE_METHOD(constructor, "size", Size);
NODE_SET_PROTOTYPE_METHOD(constructor, "cornerCount", CornerCount);
NODE_SET_PROTOTYPE_METHOD(constructor, "area", Area);
NODE_SET_PROTOTYPE_METHOD(constructor, "arcLength", ArcLength);
NODE_SET_PROTOTYPE_METHOD(constructor, "approxPolyDP", ApproxPolyDP);
NODE_SET_PROTOTYPE_METHOD(constructor, "convexHull", ConvexHull);
NODE_SET_PROTOTYPE_METHOD(constructor, "boundingRect", BoundingRect);
NODE_SET_PROTOTYPE_METHOD(constructor, "minAreaRect", BoundingRect);
NODE_SET_PROTOTYPE_METHOD(constructor, "isConvex", IsConvex);
target->Set(String::NewSymbol("Contours"), m->GetFunction());
};
@@ -48,6 +56,26 @@ Contour::Contour(): ObjectWrap() {
}
Handle<Value>
Contour::Point(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
int index = args[1]->NumberValue();
cv::Point point = self->contours[pos][index];
Local<Object> data = Object::New();
data->Set(String::NewSymbol("x"), Number::New(point.x));
data->Set(String::NewSymbol("y"), Number::New(point.y));
return scope.Close(data);
}
// FIXME: this sould better be called "Length" as ``Contours`` is an Array like structure
// also, this would allow to use ``Size`` for the function returning the number of corners
// in the contour for better consistency with OpenCV.
Handle<Value>
Contour::Size(const Arguments &args) {
HandleScope scope;
@@ -55,9 +83,17 @@ Contour::Size(const Arguments &args) {
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
return scope.Close(Number::New(self->contours.size()));
}
Handle<Value>
Contour::CornerCount(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
return scope.Close(Number::New(self->contours[pos].size()));
}
Handle<Value>
Contour::Area(const Arguments &args) {
@@ -68,6 +104,119 @@ Contour::Area(const Arguments &args) {
//return scope.Close(Number::New(contourArea(self->contours)));
return scope.Close(Number::New(contourArea(cv::Mat(self->contours[pos]))));
}
Handle<Value>
Contour::ArcLength(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
bool isClosed = args[1]->BooleanValue();
return scope.Close(Number::New(arcLength(cv::Mat(self->contours[pos]), isClosed)));
}
Handle<Value>
Contour::ApproxPolyDP(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
double epsilon = args[1]->NumberValue();
bool isClosed = args[2]->BooleanValue();
cv::Mat approxed;
approxPolyDP(cv::Mat(self->contours[pos]), approxed, epsilon, isClosed);
approxed.copyTo(self->contours[pos]);
return scope.Close(v8::Null());
}
Handle<Value>
Contour::ConvexHull(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
bool clockwise = args[1]->BooleanValue();
cv::Mat hull;
cv::convexHull(cv::Mat(self->contours[pos]), hull, clockwise);
hull.copyTo(self->contours[pos]);
return scope.Close(v8::Null());
}
Handle<Value>
Contour::BoundingRect(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
cv::Rect bounding = cv::boundingRect(cv::Mat(self->contours[pos]));
Local<Object> rect = Object::New();
rect->Set(String::NewSymbol("x"), Number::New(bounding.x));
rect->Set(String::NewSymbol("y"), Number::New(bounding.y));
rect->Set(String::NewSymbol("width"), Number::New(bounding.width));
rect->Set(String::NewSymbol("height"), Number::New(bounding.height));
return scope.Close(rect);
}
Handle<Value>
Contour::MinAreaRect(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
cv::RotatedRect minimum = cv::minAreaRect(cv::Mat(self->contours[pos]));
Local<Object> rect = Object::New();
rect->Set(String::NewSymbol("angle"), Number::New(minimum.angle));
Local<Object> size = Object::New();
size->Set(String::NewSymbol("height"), Number::New(minimum.size.height));
size->Set(String::NewSymbol("width"), Number::New(minimum.size.width));
rect->Set(String::NewSymbol("size"), size);
Local<Object> center = Object::New();
center->Set(String::NewSymbol("x"), Number::New(minimum.center.x));
center->Set(String::NewSymbol("y"), Number::New(minimum.center.y));
v8::Local<v8::Array> points = v8::Array::New(4);
cv::Point2f rect_points[4];
minimum.points(rect_points);
for (unsigned int i=0; i<4; i++){
Local<Object> point = Object::New();
point->Set(String::NewSymbol("x"), Number::New(rect_points[i].x));
point->Set(String::NewSymbol("y"), Number::New(rect_points[i].y));
points->Set(i, point);
}
rect->Set(String::NewSymbol("points"), points);
return scope.Close(rect);
}
Handle<Value>
Contour::IsConvex(const Arguments &args) {
HandleScope scope;
Contour *self = ObjectWrap::Unwrap<Contour>(args.This());
int pos = args[0]->NumberValue();
return scope.Close(Boolean::New(isContourConvex(cv::Mat(self->contours[pos]))));
}
+9 -1
Ver Arquivo
@@ -11,10 +11,18 @@ class Contour: public node::ObjectWrap {
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
Contour();
Contour();
//JSFUNC(Size)
static Handle<Value> Point(const v8::Arguments&);
static Handle<Value> Size(const v8::Arguments&);
static Handle<Value> CornerCount(const v8::Arguments&);
static Handle<Value> Area(const v8::Arguments&);
static Handle<Value> ArcLength(const v8::Arguments&);
static Handle<Value> ApproxPolyDP(const v8::Arguments&);
static Handle<Value> ConvexHull(const v8::Arguments&);
static Handle<Value> BoundingRect(const v8::Arguments&);
static Handle<Value> MinAreaRect(const v8::Arguments&);
static Handle<Value> IsConvex(const v8::Arguments&);
};
+67
Ver Arquivo
@@ -0,0 +1,67 @@
#include "HighGUI.h"
#include "OpenCV.h"
#include "Matrix.h"
Persistent<FunctionTemplate> NamedWindow::constructor;
void
NamedWindow::Init(Handle<Object> target) {
HandleScope scope;
// Constructor
constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(NamedWindow::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("NamedWindow"));
// Prototype
//Local<ObjectTemplate> proto = constructor->PrototypeTemplate();
NODE_SET_PROTOTYPE_METHOD(constructor, "show", Show);
NODE_SET_PROTOTYPE_METHOD(constructor, "destroy", Destroy);
target->Set(String::NewSymbol("NamedWindow"), constructor->GetFunction());
};
Handle<Value>
NamedWindow::New(const Arguments &args) {
HandleScope scope;
if (args.This()->InternalFieldCount() == 0){
JSTHROW_TYPE("Cannot Instantiate without new")
}
NamedWindow* win;
if (args.Length() == 1){
win = new NamedWindow(std::string(*v8::String::AsciiValue(args[0]->ToString())), 0);
} else if (args.Length() == 2){
win = new NamedWindow(std::string(*v8::String::AsciiValue(args[0]->ToString())), 0);
}
win->Wrap(args.Holder());
return scope.Close(args.Holder());
}
NamedWindow::NamedWindow(const std::string& name, int f){
winname = std::string(name);
flags = f;
cv::namedWindow(winname, flags);
}
Handle<Value>
NamedWindow::Show(const v8::Arguments& args){
SETUP_FUNCTION(NamedWindow)
Matrix *im = ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
cv::imshow(self->winname, im->mat);
return scope.Close(args.Holder());
}
Handle<Value>
NamedWindow::Destroy(const v8::Arguments& args){
SETUP_FUNCTION(NamedWindow)
cv::destroyWindow(self->winname);
return scope.Close(args.Holder());
}
+19
Ver Arquivo
@@ -0,0 +1,19 @@
#include "OpenCV.h"
class NamedWindow: public node::ObjectWrap {
public:
std::string winname;
int flags;
static Persistent<FunctionTemplate> constructor;
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
NamedWindow(const std::string& winname, int flags);
JSFUNC(Show);
JSFUNC(Destroy);
};
+238 -37
Ver Arquivo
@@ -37,30 +37,38 @@ Matrix::Init(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(constructor, "width", Width);
NODE_SET_PROTOTYPE_METHOD(constructor, "height", Height);
NODE_SET_PROTOTYPE_METHOD(constructor, "size", Size);
NODE_SET_PROTOTYPE_METHOD(constructor, "clone", Clone);
NODE_SET_PROTOTYPE_METHOD(constructor, "toBuffer", ToBuffer);
NODE_SET_PROTOTYPE_METHOD(constructor, "toBufferAsync", ToBufferAsync);
NODE_SET_PROTOTYPE_METHOD(constructor, "ellipse", Ellipse);
NODE_SET_PROTOTYPE_METHOD(constructor, "rectangle", Rectangle);
NODE_SET_PROTOTYPE_METHOD(constructor, "line", Line);
NODE_SET_PROTOTYPE_METHOD(constructor, "save", Save);
NODE_SET_PROTOTYPE_METHOD(constructor, "resize", Resize);
NODE_SET_PROTOTYPE_METHOD(constructor, "pyrDown", PyrDown);
NODE_SET_PROTOTYPE_METHOD(constructor, "pyrUp", PyrUp);
NODE_SET_PROTOTYPE_METHOD(constructor, "channels", Channels);
NODE_SET_PROTOTYPE_METHOD(constructor, "convertGrayscale", ConvertGrayscale);
NODE_SET_PROTOTYPE_METHOD(constructor, "convertHSVscale", ConvertHSVscale);
NODE_SET_PROTOTYPE_METHOD(constructor, "convertHSVscale", ConvertHSVscale);
NODE_SET_PROTOTYPE_METHOD(constructor, "gaussianBlur", GaussianBlur);
NODE_SET_PROTOTYPE_METHOD(constructor, "copy", Copy);
NODE_SET_PROTOTYPE_METHOD(constructor, "ptr", Ptr);
NODE_SET_PROTOTYPE_METHOD(constructor, "addWeighted", AddWeighted);
NODE_SET_PROTOTYPE_METHOD(constructor, "split", Split);
NODE_SET_PROTOTYPE_METHOD(constructor, "canny", Canny);
NODE_SET_PROTOTYPE_METHOD(constructor, "dilate", Dilate);
NODE_SET_PROTOTYPE_METHOD(constructor, "dilate", Dilate);
NODE_SET_PROTOTYPE_METHOD(constructor, "erode", Erode);
NODE_SET_PROTOTYPE_METHOD(constructor, "findContours", FindContours);
NODE_SET_PROTOTYPE_METHOD(constructor, "drawContour", DrawContour);
NODE_SET_PROTOTYPE_METHOD(constructor, "drawAllContours", DrawAllContours);
NODE_SET_PROTOTYPE_METHOD(constructor, "goodFeaturesToTrack", GoodFeaturesToTrack);
NODE_SET_PROTOTYPE_METHOD(constructor, "houghLinesP", HoughLinesP);
NODE_SET_PROTOTYPE_METHOD(constructor, "inRange", inRange);
NODE_SET_METHOD(constructor, "Eye", Eye);
@@ -100,10 +108,8 @@ Matrix::Matrix(): ObjectWrap() {
}
Matrix::Matrix(int w, int h): ObjectWrap() {
mat = cv::Mat(w, h, CV_32FC3);
//TODO:Parametrizar esto
//mat = cv::Mat(h, w, CV_8UC3);
Matrix::Matrix(int rows, int cols): ObjectWrap() {
mat = cv::Mat(rows, cols, CV_32FC3);
}
Matrix::Matrix(cv::Mat m, cv::Rect roi): ObjectWrap() {
@@ -159,7 +165,7 @@ Matrix::Get(const Arguments& args){
}
Handle<Value>
Handle<Value>
Matrix::Set(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -196,7 +202,7 @@ Matrix::Set(const Arguments& args){
}
Handle<Value>
Handle<Value>
Matrix::Size(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -207,7 +213,19 @@ Matrix::Size(const Arguments& args){
return scope.Close(arr);
}
Handle<Value>
Handle<Value>
Matrix::Clone(const Arguments& args){
SETUP_FUNCTION(Matrix)
Local<Object> im_h = Matrix::constructor->GetFunction()->NewInstance();
Matrix *m = ObjectWrap::Unwrap<Matrix>(im_h);
m->mat = self->mat.clone();
return scope.Close(im_h);
}
Handle<Value>
Matrix::Row(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -224,7 +242,7 @@ Matrix::Row(const Arguments& args){
}
Handle<Value>
Handle<Value>
Matrix::PixelRow(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -243,7 +261,7 @@ Handle<Value>
return scope.Close(arr);
}
Handle<Value>
Handle<Value>
Matrix::Col(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -259,7 +277,7 @@ Matrix::Col(const Arguments& args){
}
Handle<Value>
Handle<Value>
Matrix::PixelCol(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -277,21 +295,21 @@ Matrix::PixelCol(const Arguments& args){
return scope.Close(arr);
}
Handle<Value>
Handle<Value>
Matrix::Width(const Arguments& args){
SETUP_FUNCTION(Matrix)
return scope.Close(Number::New(self->mat.size().width));
}
Handle<Value>
Handle<Value>
Matrix::Height(const Arguments& args){
SETUP_FUNCTION(Matrix)
return scope.Close(Number::New(self->mat.size().height));
}
Handle<Value>
Handle<Value>
Matrix::Channels(const Arguments& args){
SETUP_FUNCTION(Matrix)
@@ -303,6 +321,10 @@ Handle<Value>
Matrix::ToBuffer(const v8::Arguments& args){
SETUP_FUNCTION(Matrix)
if (args.Length() > 0){
return Matrix::ToBufferAsync(args);
}
std::vector<uchar> vec(0);
std::vector<int> params(0);//CV_IMWRITE_JPEG_QUALITY 90
@@ -321,20 +343,110 @@ Matrix::ToBuffer(const v8::Arguments& args){
}
Handle<Value>
Matrix::Ellipse(const v8::Arguments& args){
struct matrixToBuffer_baton_t {
Matrix *mm;
Persistent<Function> cb;
std::vector<uchar> res;
int resSize;
int sleep_for;
uv_work_t request;
};
void AsyncToBufferAsync(uv_work_t *req);
void AfterAsyncToBufferAsync(uv_work_t *req);
Handle<Value>
Matrix::ToBufferAsync(const v8::Arguments& args){
SETUP_FUNCTION(Matrix)
REQ_FUN_ARG(0, cb);
matrixToBuffer_baton_t *baton = new matrixToBuffer_baton_t();
baton->mm = self;
baton->cb = Persistent<Function>::New(cb);
baton->request.data = baton;
baton->sleep_for = 1;
uv_queue_work(uv_default_loop(), &baton->request, AsyncToBufferAsync, AfterAsyncToBufferAsync);
return Undefined();
}
void AsyncToBufferAsync(uv_work_t *req) {
matrixToBuffer_baton_t *baton = static_cast<matrixToBuffer_baton_t *>(req->data);
std::vector<uchar> vec(0);
std::vector<int> params(0);//CV_IMWRITE_JPEG_QUALITY 90
cv::imencode(".jpg", baton->mm->mat, vec, params);
baton->res = vec;
}
void AfterAsyncToBufferAsync(uv_work_t *req) {
HandleScope scope;
matrixToBuffer_baton_t *baton = static_cast<matrixToBuffer_baton_t *>(req->data);
// ev_unref(EV_DEFAULT_UC);
// baton->cc->Unref();
Local<Value> argv[2];
argv[0] = Local<Value>::New(Null());
node::Buffer *buf = node::Buffer::New(baton->res.size());
uchar* data = (uchar*) Buffer::Data(buf);
memcpy(data, &baton->res[0], baton->res.size());
v8::Local<v8::Object> globalObj = v8::Context::GetCurrent()->Global();
v8::Local<v8::Function> bufferConstructor = v8::Local<v8::Function>::Cast(globalObj->Get(v8::String::New("Buffer")));
v8::Handle<v8::Value> constructorArgs[3] = {buf->handle_, v8::Integer::New(baton->res.size()), v8::Integer::New(0)};
v8::Local<v8::Object> actualBuffer = bufferConstructor->NewInstance(3, constructorArgs);
argv[1] = actualBuffer;
TryCatch try_catch;
baton->cb->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
baton->cb.Dispose();
delete baton;
// return 0;
}
Handle<Value>
Matrix::Ellipse(const v8::Arguments& args){
SETUP_FUNCTION(Matrix)
int x = args[0]->Uint32Value();
int y = args[1]->Uint32Value();
int width = args[2]->Uint32Value();
int height = args[3]->Uint32Value();
uint color = args[4]->Uint32Value();
int height = args[3]->Uint32Value();
cv::Scalar color(0, 0, 255);
cv::ellipse(self->mat, cv::Point(x, y), cv::Size(width, height), 0, 0, 360,
cv::Scalar( (color >> 16) & 0xff , (color >> 8) & 0xff, color & 0xff ), 4, 8, 0);
if(args[4]->IsArray()) {
Local<Object> objColor = args[4]->ToObject();
color = setColor(objColor);
}
int thickness = 1;
if(args[5]->IntegerValue())
thickness = args[5]->IntegerValue();
cv::ellipse(self->mat, cv::Point(x, y), cv::Size(width, height), 0, 0, 360, color, thickness, 8, 0);
return scope.Close(v8::Null());
}
}
Handle<Value>
@@ -352,7 +464,7 @@ Matrix::Rectangle(const Arguments& args) {
Local<Object> objColor = args[2]->ToObject();
color = setColor(objColor);
}
int x = xy->Get(0)->IntegerValue();
int y = xy->Get(1)->IntegerValue();
@@ -385,7 +497,7 @@ Matrix::Line(const Arguments& args) {
Local<Object> objColor = args[2]->ToObject();
color = setColor(objColor);
}
int x1 = xy1->Get(0)->IntegerValue();
int y1 = xy1->Get(1)->IntegerValue();
@@ -417,7 +529,7 @@ Matrix::Save(const v8::Arguments& args){
}
Handle<Value>
Handle<Value>
Matrix::Eye(const v8::Arguments& args){
HandleScope scope;
@@ -453,16 +565,50 @@ Matrix::ConvertGrayscale(const v8::Arguments& args) {
Handle<Value>
Matrix::ConvertHSVscale(const v8::Arguments& args) {
HandleScope scope;
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
if(self->mat.channels() != 3)
return v8::ThrowException(String::New("Image is no 3-channel"));
cv::Mat hsv;
cv::cvtColor(self->mat, hsv, CV_BGR2HSV);
hsv.copyTo(self->mat);
return scope.Close(v8::Null());
}
Handle<Value>
Matrix::GaussianBlur(const v8::Arguments& args) {
HandleScope scope;
cv::Size ksize;
cv::Mat blurred;
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
if(self->mat.channels() != 3)
return v8::ThrowException(String::New("Image is no 3-channel"));
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
cv::Mat hsv;
if (args.Length() < 1) {
ksize = cv::Size(5, 5);
}
else {
if(!args[0]->IsArray()) {
return ThrowException(Exception::TypeError(String::New(
"'ksize' argument must be a 2 double array")));
}
Local<Object> array = args[0]->ToObject();
// TODO: Length check
Local<Value> x = array->Get(0);
Local<Value> y = array->Get(1);
if(!x->IsNumber() || !y->IsNumber()) {
return ThrowException(Exception::TypeError(String::New(
"'ksize' argument must be a 2 double array")));
}
ksize = cv::Size(x->NumberValue(), y->NumberValue());
}
cv::cvtColor(self->mat, hsv, CV_BGR2HSV);
hsv.copyTo(self->mat);
cv::GaussianBlur(self->mat, blurred, ksize, 0);
blurred.copyTo(self->mat);
return scope.Close(v8::Null());
}
@@ -515,7 +661,7 @@ Matrix::AddWeighted(const v8::Arguments& args) {
int gamma = 0;
cv::addWeighted(src1->mat, alpha, src2->mat, beta, gamma, self->mat);
return scope.Close(v8::Null());
}
@@ -557,6 +703,18 @@ Matrix::Dilate(const v8::Arguments& args) {
return scope.Close(v8::Null());
}
Handle<Value>
Matrix::Erode(const v8::Arguments& args) {
HandleScope scope;
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
int niters = args[0]->NumberValue();
cv::erode(self->mat, self->mat, cv::Mat(), cv::Point(-1, -1), niters);
return scope.Close(v8::Null());
}
Handle<Value>
Matrix::FindContours(const v8::Arguments& args) {
@@ -620,14 +778,14 @@ Matrix::GoodFeaturesToTrack(const v8::Arguments& args) {
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
std::vector<cv::Point2f> corners;
cv::Mat gray;
cvtColor(self->mat, gray, CV_BGR2GRAY);
equalizeHist(gray, gray);
cv::goodFeaturesToTrack(gray, corners, 500, 0.01, 10);
v8::Local<v8::Array> arr = v8::Array::New(corners.size());
@@ -648,14 +806,14 @@ Matrix::HoughLinesP(const v8::Arguments& args) {
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
std::vector<cv::Vec4i> lines;
cv::Mat gray;
equalizeHist(self->mat, gray);
// cv::Canny(gray, gray, 50, 200, 3);
cv::HoughLinesP(gray, lines, 1, CV_PI/180, 80, 30, 10);
v8::Local<v8::Array> arr = v8::Array::New(lines.size());
@@ -700,3 +858,46 @@ Matrix::Resize(const v8::Arguments& args){
return scope.Close(Undefined());
}
Handle<Value>
Matrix::PyrDown(const v8::Arguments& args){
SETUP_FUNCTION(Matrix)
cv::pyrDown(self->mat, self->mat);
return scope.Close(v8::Undefined());
}
Handle<Value>
Matrix::PyrUp(const v8::Arguments& args){
SETUP_FUNCTION(Matrix)
cv::pyrUp(self->mat, self->mat);
return scope.Close(v8::Undefined());
}
Handle<Value>
Matrix::inRange(const v8::Arguments& args) {
HandleScope scope;
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
/*if(self->mat.channels() != 3)
return v8::ThrowException(String::New("Image is no 3-channel"));*/
if(args[0]->IsArray() && args[1]->IsArray()) {
Local<Object> args_lowerb = args[0]->ToObject();
Local<Object> args_upperb = args[1]->ToObject();
cv::Scalar lowerb(0, 0, 0);
cv::Scalar upperb(0, 0, 0);
lowerb = setColor(args_lowerb);
upperb = setColor(args_upperb);
cv::Mat mask;
cv::inRange(self->mat, lowerb, upperb, mask);
mask.copyTo(self->mat);
}
return scope.Close(v8::Null());
}
+13 -4
Ver Arquivo
@@ -2,12 +2,12 @@
class Matrix: public node::ObjectWrap {
public:
cv::Mat mat;
static Persistent<FunctionTemplate> constructor;
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
Matrix();
Matrix();
Matrix(cv::Mat other, cv::Rect roi);
Matrix(int rows, int cols);
Matrix(int rows, int cols, int typ);
@@ -28,6 +28,7 @@ class Matrix: public node::ObjectWrap {
JSFUNC(Width)
JSFUNC(Height)
JSFUNC(Channels)
JSFUNC(Clone)
JSFUNC(Ellipse)
JSFUNC(Rectangle)
@@ -37,16 +38,22 @@ class Matrix: public node::ObjectWrap {
JSFUNC(Save)
JSFUNC(ToBuffer)
JSFUNC(ToBufferAsync)
JSFUNC(Resize)
JSFUNC(PyrDown)
JSFUNC(PyrUp)
JSFUNC(ConvertGrayscale)
JSFUNC(ConvertHSVscale)
JSFUNC(GaussianBlur)
JSFUNC(Copy)
JSFUNC(Ptr)
JSFUNC(AddWeighted)
JSFUNC(Split)
JSFUNC(Canny)
JSFUNC(Dilate)
JSFUNC(Erode)
JSFUNC(FindContours)
JSFUNC(DrawContour)
@@ -56,6 +63,8 @@ class Matrix: public node::ObjectWrap {
JSFUNC(GoodFeaturesToTrack)
JSFUNC(HoughLinesP)
JSFUNC(inRange)
/*
static Handle<Value> Val(const Arguments& args);
static Handle<Value> RowRange(const Arguments& args);
@@ -85,8 +94,8 @@ class Matrix: public node::ObjectWrap {
static Handle<Value> Depth(const Arguments& args);
static Handle<Value> Channels(const Arguments& args);
static Handle<Value> StepOne(const Arguments& args);
*/
};
+2
Ver Arquivo
@@ -27,6 +27,8 @@ using namespace node;
#define JSFUNC(NAME) \
static Handle<Value> NAME(const Arguments& args);
#define JSTHROW_TYPE(ERR) \
return v8::ThrowException(v8::Exception::TypeError(v8::String::New(ERR)));
class OpenCV: public node::ObjectWrap{
+14 -1
Ver Arquivo
@@ -46,7 +46,10 @@ VideoCaptureWrap::New(const Arguments &args) {
if (args[0]->IsNumber()){
v = new VideoCaptureWrap(args[0]->NumberValue());
} else {}
} else {
//TODO - assumes that we have string, verify
v = new VideoCaptureWrap(std::string(*v8::String::AsciiValue(args[0]->ToString())));
}
v->Wrap(args.This());
@@ -64,6 +67,16 @@ VideoCaptureWrap::VideoCaptureWrap(int device){
}
}
VideoCaptureWrap::VideoCaptureWrap(const std::string& filename){
HandleScope scope;
cap.open(filename);
// TODO! At the moment this only takes a full path - do relative too.
if(!cap.isOpened()){
v8::ThrowException(v8::Exception::Error(String::New("Video file could not be opened (opencv reqs. non relative paths)")));
}
}
Handle<Value>
VideoCaptureWrap::Read(const Arguments &args) {
+77
Ver Arquivo
@@ -0,0 +1,77 @@
#include "VideoWriter.h"
#include "Matrix.h"
#include "OpenCV.h"
v8::Persistent<FunctionTemplate> VideoWriterWrap::constructor;
void
VideoWriterWrap::Init(Handle<Object> target) {
HandleScope scope;
// Constructor
constructor = Persistent<FunctionTemplate>::New(
FunctionTemplate::New(VideoWriterWrap::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("VideoWriter"));
// Prototype
//Local<ObjectTemplate> proto = constructor->PrototypeTemplate();
NODE_SET_PROTOTYPE_METHOD(constructor, "write", Write);
target->Set(String::NewSymbol("VideoWriter"), constructor->GetFunction());
};
Handle<Value>
VideoWriterWrap::New(const Arguments &args) {
HandleScope scope;
if (args.This()->InternalFieldCount() == 0)
return v8::ThrowException(
v8::Exception::TypeError(v8::String::New("Cannot Instantiate without new")));
VideoWriterWrap *v;
std::string filename;
int codec = CV_FOURCC('M','J','P','G'); // Default mjpg
double fps = 24.0;
cv::Size framesize = cv::Size(640, 320);
//bool isColor; // TODO
filename = std::string(*v8::String::AsciiValue(args[0]->ToString()));
v = new VideoWriterWrap(filename, codec, fps, framesize);
v->Wrap(args.This());
return args.This();
}
VideoWriterWrap::VideoWriterWrap(const std::string& filename, int codec,
double fps, cv::Size framesize){
writer = cv::VideoWriter(filename, codec, fps, framesize);
if (!writer.isOpened()){
std::cout << "VideoWriter: Could not be opened!"
}
}
Handle<Value>
VideoWriterWrap::Write(const Arguments &args) {
SETUP_FUNCTION(VideoWriterWrap)
Matrix *im = ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
self->writer << im->mat;
return scope.Close(v8::Undefined());
}
+15
Ver Arquivo
@@ -0,0 +1,15 @@
#include "OpenCV.h"
class VideoWriterWrap: public node::ObjectWrap {
public:
cv::VideoWriter writer;
static Persistent<FunctionTemplate> constructor;
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
VideoWriterWrap(const std::string& filename, int codec,
double fps, cv::Size framesize);
static Handle<Value> Write(const v8::Arguments&);
};
+8 -1
Ver Arquivo
@@ -3,7 +3,10 @@
#include "Matrix.h"
#include "CascadeClassifierWrap.h"
#include "VideoCaptureWrap.h"
#include "VideoWriter.h"
#include "Contours.h"
#include "CamShift.h"
#include "HighGUI.h"
extern "C" void
@@ -14,7 +17,11 @@ init(Handle<Object> target) {
Matrix::Init(target);
CascadeClassifierWrap::Init(target);
VideoCaptureWrap::Init(target);
Contour::Init(target);
VideoWriterWrap::Init(target);
Contour::Init(target);
TrackedObject::Init(target);
NamedWindow::Init(target);
};
NODE_MODULE(opencv, init)
+87 -3
Ver Arquivo
@@ -9,6 +9,16 @@ assertDeepSimilar = function(res, exp){
assert.deepEqual(res, exp)
}
assertWithinRange = function(res, exp, range){
assert.ok((res - exp) < range || (res - exp) > -range, "Not within range:" + res + " (" + exp + "+- " + range + ")")
}
assertWithinRanges = function(res, exp, range){
for (var i =0; i<res.length; i++){
assertWithinRange(res[i], exp[i], range);
}
}
vows.describe('Smoke Tests OpenCV').addBatch({
"Importing": {
@@ -29,6 +39,13 @@ vows.describe('Smoke Tests OpenCV').addBatch({
, '.Matrix imports': function(topic){
assert.ok(!!topic.Matrix)
}
, 'importing library multiple times is ok' : function(){
var cv1 = require('../lib/opencv')
, cv2 = require('../lib/opencv')
cv1.readImage('./examples/mona.png', function(){});
cv2.readImage('./examples/mona.png', function(){});
}
}
, "Point" : {
@@ -137,6 +154,23 @@ vows.describe('Smoke Tests OpenCV').addBatch({
})
}
, "toBuffer Async": {
topic: function(cv){
var buf = fs.readFileSync('./examples/mona.png')
, cb = this.callback
cv.readImage(buf.slice(0), function(err, mat){
var buff = mat.toBuffer(function(){
cb.apply(this, arguments)
})
})
}
, 'gives a buffer' : function(e, res){
assert.ok(!e)
assert.ok(res);
assert.ok(res.length > 100);
}
}
, "detectObject": {
@@ -145,7 +179,7 @@ vows.describe('Smoke Tests OpenCV').addBatch({
, cb = this.callback
cv.readImage("./examples/mona.png", function(err, im){
im.detectObject("./data/haarcascade_frontalface_alt.xml", {}, cb)
im.detectObject(cv.FACE_CASCADE, {}, cb)
})
}
@@ -216,12 +250,12 @@ vows.describe('Smoke Tests OpenCV').addBatch({
}
, "ImageStream" : {
, "ImageDataStream" : {
topic : require('../lib/opencv')
, "pipe" : {
topic : function(cv){
var s = new cv.ImageStream()
var s = new cv.ImageDataStream()
, self = this
s.on('load', function(im){
assert.ok(im)
@@ -239,6 +273,56 @@ vows.describe('Smoke Tests OpenCV').addBatch({
}
, "ImageStream" :{
topic : require('../lib/opencv')
, "write" : {
topic: function(cv){
var s = new cv.ImageStream()
, im = fs.readFileSync('./examples/mona.png')
, self = this;
s.on('data', function(m){
self.callback(null, m)
})
s.write(im);
}
, "receives data" : function(mat){
assert.deepEqual(mat.size(), [756,500])
}
}
}
, "ObjectDetectionStream" :{
topic : require('../lib/opencv')
}
, "CamShift" : {
"Can Create and Track" : {
topic : function(){
var cv = require('../lib/opencv')
, self = this
cv.readImage('./examples/coin1.jpg', function(e, im){
cv.readImage('./examples/coin2.jpg', function(e, im2){
self.callback(im, im2, cv)
})
})
}
, "create TrackedObject" : function(im, im2, cv){
var tracked = new cv.TrackedObject(im, [420, 110, 490, 170]);
assert.ok(tracked);
}
, "use TrackedObject.track" : function(im, im2, cv){
var tracked = new cv.TrackedObject(im, [420, 110, 490, 170], {channel: 'v'});
assertWithinRanges(tracked.track(im2), [386, 112, 459, 166], 10);
}
}
}
-45
Ver Arquivo
@@ -1,45 +0,0 @@
im.calcHistograms(function(err, hist){})
im.calcHistograms(mask, function(err, hist){})
## Face recognition TODO
// Load Database
// TODO<
cv.loadImage('test.jpg', function(err, im){
im.detectObject("front-face.xml", {}, function(err, faces){
_.each(faces, function(v){
// TODO {
var section = im.slice(v.x, v.y, v.x + v.width, v.y + v.height);
section.convertGrayscale()
section.resize(WID, HEIGHT);
section.equaliseHistogram();
// } TODO
})
})
})
-----
http://www.athile.net/library/wiki/index.php?title=Library/V8/Tutorial#Wrapping_a_Javascript_function_as_a_std::function.3C.3E
https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/