Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'camshift'

  • Loading branch information...
commit 644f6797d3110cb7e232ae7e327b9089d7e623d5 2 parents 33146b1 + f90cea5
@peterbraden authored
View
3  .gitignore
@@ -3,4 +3,5 @@ build
.DS_Store
node_modules
npm-debug.log
-out.jpg
+out*.jpg
+examples/*.avi
View
1  .travis.yml
@@ -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
View
1  binding.gyp
@@ -9,6 +9,7 @@
, "src/Contours.cc"
, "src/Point.cc"
, "src/VideoCaptureWrap.cc"
+ , "src/CamShift.cc"
]
, 'libraries': [
'<!@(pkg-config --libs opencv)'
View
BIN  examples/coin1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  examples/coin2.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
1  examples/make-example-files.sh
@@ -0,0 +1 @@
+mencoder examples/motion.mov -ovc raw -vf format=i420 -nosound -o examples/motion.avi
View
24 examples/motion-track.js
@@ -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();
+})
+
View
BIN  examples/motion.mov
Binary file not shown
View
6 smoke/smoke.sh
@@ -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
View
6 smoke/smoketest.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');
});
+*/
View
191 src/CamShift.cc
@@ -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);
+
+}
View
23 src/CamShift.h
@@ -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);
+
+};
View
2  src/OpenCV.h
@@ -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{
View
15 src/VideoCaptureWrap.cc
@@ -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) {
View
4 src/init.cc
@@ -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)
View
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);
Please sign in to comment.
Something went wrong with that request. Please try again.