mirror of
https://github.com/peterbraden/node-opencv.git
synced 2025-12-08 19:45:55 +00:00
Merge branch 'camshift'
This commit is contained in:
commit
644f6797d3
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ build
|
||||
.DS_Store
|
||||
node_modules
|
||||
npm-debug.log
|
||||
out.jpg
|
||||
out*.jpg
|
||||
examples/*.avi
|
||||
|
||||
@ -3,6 +3,7 @@ node_js:
|
||||
- 0.6
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install libcv-dev
|
||||
- sudo apt-get install libopencv-dev
|
||||
- sudo apt-get install libhighgui-dev
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
, "src/Contours.cc"
|
||||
, "src/Point.cc"
|
||||
, "src/VideoCaptureWrap.cc"
|
||||
, "src/CamShift.cc"
|
||||
]
|
||||
, 'libraries': [
|
||||
'<!@(pkg-config --libs opencv)'
|
||||
|
||||
BIN
examples/coin1.jpg
Normal file
BIN
examples/coin1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
BIN
examples/coin2.jpg
Normal file
BIN
examples/coin2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
1
examples/make-example-files.sh
Executable file
1
examples/make-example-files.sh
Executable file
@ -0,0 +1 @@
|
||||
mencoder examples/motion.mov -ovc raw -vf format=i420 -nosound -o examples/motion.avi
|
||||
24
examples/motion-track.js
Normal file
24
examples/motion-track.js
Normal file
@ -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();
|
||||
})
|
||||
|
||||
BIN
examples/motion.mov
Normal file
BIN
examples/motion.mov
Normal file
Binary file not shown.
@ -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
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
var cv = require('../lib/opencv')
|
||||
|
||||
|
||||
|
||||
/*
|
||||
new cv.VideoCapture(0).read(function(mat){
|
||||
|
||||
@ -15,7 +18,7 @@ new cv.VideoCapture(0).read(function(mat){
|
||||
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
|
||||
|
||||
cv.readImage("./examples/stuff.png", function(err, im){
|
||||
@ -32,3 +35,4 @@ cv.readImage("./examples/stuff.png", function(err, im){
|
||||
console.log(features)
|
||||
im.save('./out.jpg');
|
||||
});
|
||||
*/
|
||||
|
||||
191
src/CamShift.cc
Normal file
191
src/CamShift.cc
Normal file
@ -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
src/CamShift.h
Normal file
23
src/CamShift.h
Normal file
@ -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);
|
||||
|
||||
};
|
||||
@ -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{
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "CascadeClassifierWrap.h"
|
||||
#include "VideoCaptureWrap.h"
|
||||
#include "Contours.h"
|
||||
#include "CamShift.h"
|
||||
|
||||
|
||||
extern "C" void
|
||||
@ -14,7 +15,8 @@ init(Handle<Object> target) {
|
||||
Matrix::Init(target);
|
||||
CascadeClassifierWrap::Init(target);
|
||||
VideoCaptureWrap::Init(target);
|
||||
Contour::Init(target);
|
||||
Contour::Init(target);
|
||||
TrackedObject::Init(target);
|
||||
};
|
||||
|
||||
NODE_MODULE(opencv, init)
|
||||
|
||||
37
test/unit.js
37
test/unit.js
@ -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": {
|
||||
|
||||
@ -264,6 +274,33 @@ vows.describe('Smoke Tests OpenCV').addBatch({
|
||||
|
||||
}
|
||||
|
||||
, "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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}).export(module);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user