Merge pull request #585 from btsimonh/bgsubtractor

Background subtractor Async
This commit is contained in:
btsimonh 2017-11-11 13:20:16 +00:00 committed by GitHub
commit 1085b662e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 525 additions and 10 deletions

110
examples/bgsubtractor.js Normal file
View File

@ -0,0 +1,110 @@
var path = require('path'),
cv = require('../lib/opencv');
var bg = null;
require("v8").setFlagsFromString('--expose_gc');
var gc = require("vm").runInNewContext('gc');
var do_sync = function(done){
// When opening a file, the full path must be passed to opencv
var vid = new cv.VideoCapture(path.join(__dirname, 'files', 'motion.mov'));
var x = 0;
// synchronous
var iter = function(){
vid.read(function(err, m2){
if (err) throw err;
var mat = bg.apply(m2);
delete m2;
// mat is a monochrome img where moving objects are white
if (x++<100){
//console.log("iter "+x);
setTimeout(iter, 1);
} else {
delete vid;
console.log("bg sync done");
gc();
if (undefined !== done)
done();
}
})
};
iter();
};
var do_async = function(done){
// restart video read
var vid = new cv.VideoCapture(path.join(__dirname, 'files', 'motion.mov'));
var x = 0;
// asynchronous
var iter = function(){
vid.read(function(err, m2){
if (err) throw err;
bg.apply(m2, function(err, mat){
delete mat;
if (err) throw err;
// mat is a monochrome img where moving objects are white
if (x++<100){
//console.log("iter "+x);
iter();
} else {
console.log("bg async done");
delete vid;
gc();
if (undefined !== done)
done();
}
});
})
};
iter();
};
var do_default = function( done ){
console.log("doing Default");
bg = new cv.BackgroundSubtractor();
do_sync( function(){
do_async(done);
});
}
var do_gmg = function( done ){
console.log("doing GMG");
bg = cv.BackgroundSubtractor.createGMG();
do_sync( function(){
//console.log("not doing GMG Async - crashes my pi");
do_async(done);
});
}
var do_mog = function( done ){
console.log("doing MOG");
bg = cv.BackgroundSubtractor.createMOG();
do_sync( function(){
do_async(done);
});
}
var do_mog2 = function( done ){
console.log("doing MOG2");
bg = cv.BackgroundSubtractor.createMOG2();
do_sync( function(){
do_async(done);
});
}
do_default(function(){
do_mog(function(){
do_mog2(function(){
do_gmg(function(){
});
});
});
});

View File

@ -3,18 +3,47 @@
#include <iostream>
#include <nan.h>
#ifdef HAVE_OPENCV_VIDEO
#ifdef HAVE_BACKGROUNDSUBTRACTOR
#ifdef HAVE_OPENCV_BGSEGM
#if CV_MAJOR_VERSION >= 3
#ifdef __GNUC__
#warning TODO: port me to OpenCV 3
#warning Building with HAVE_OPENCV_BGSEGM
#else
// vs style message pragma
#pragma message ( "TODO: port me to OpenCV 3" )
#pragma message ( "Building with HAVE_OPENCV_BGSEGM" )
#endif
cv::bgsegm::BackgroundSubtractorMOG* getMOG(BackgroundSubtractorWrap *wrap) {
return dynamic_cast<cv::bgsegm::BackgroundSubtractorMOG *>(wrap->subtractor.get());
}
#else
#ifdef __GNUC__
#warning Building without HAVE_OPENCV_BGSEGM
#else
// vs style message pragma
#pragma message ( "Building without HAVE_OPENCV_BGSEGM - only MOG2 will be available" )
#endif
// without bgsem, it can't be MOG anyway
void* getMOG(BackgroundSubtractorWrap *wrap) {
return NULL;
}
#endif
#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4))
class BGAutoMutex {
public:
BGAutoMutex(cv::Mutex &m){
mutex = &m;
mutex->lock();
};
~BGAutoMutex(){
mutex->unlock();
mutex = NULL;
};
private:
cv::Mutex *mutex;
};
Nan::Persistent<FunctionTemplate> BackgroundSubtractorWrap::constructor;
@ -28,7 +57,14 @@ void BackgroundSubtractorWrap::Init(Local<Object> target) {
ctor->SetClassName(Nan::New("BackgroundSubtractor").ToLocalChecked());
Nan::SetMethod(ctor, "createMOG", CreateMOG);
Nan::SetMethod(ctor, "createMOG2", CreateMOG2);
Nan::SetMethod(ctor, "createGMG", CreateGMG);
Nan::SetPrototypeMethod(ctor, "applyMOG", ApplyMOG);
Nan::SetPrototypeMethod(ctor, "apply", Apply);
Nan::SetPrototypeMethod(ctor, "history", History);
Nan::SetPrototypeMethod(ctor, "nmixtures", Mixtures);
Nan::SetPrototypeMethod(ctor, "noiseSigma", NoiseSigma);
Nan::SetPrototypeMethod(ctor, "backgroundRatio", BackgroundRatio);
target->Set(Nan::New("BackgroundSubtractor").ToLocalChecked(), ctor->GetFunction());
}
@ -41,11 +77,41 @@ NAN_METHOD(BackgroundSubtractorWrap::New) {
}
// Create MOG by default
cv::Ptr<cv::BackgroundSubtractor> bg;
#if CV_MAJOR_VERSION >= 3
#ifdef HAVE_OPENCV_BGSEGM
cv::Ptr<cv::BackgroundSubtractor> bg = cv::bgsegm::createBackgroundSubtractorMOG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from cv::bgsegm::createBackgroundSubtractorMOG()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(info.This());
info.GetReturnValue().Set(info.This());
#else
// if no bgsem, then default to MOG2
cv::Ptr<cv::BackgroundSubtractor> bg = cv::createBackgroundSubtractorMOG2();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from cv::createBackgroundSubtractorMOG2()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(info.This());
info.GetReturnValue().Set(info.This());
#endif
#else
cv::Ptr<cv::BackgroundSubtractor> bg = new cv::BackgroundSubtractorMOG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from new cv::BackgroundSubtractorMOG()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(info.This());
info.GetReturnValue().Set(info.This());
#endif
}
NAN_METHOD(BackgroundSubtractorWrap::CreateMOG) {
@ -65,13 +131,108 @@ NAN_METHOD(BackgroundSubtractorWrap::CreateMOG) {
Local<Object> n = Nan::NewInstance(Nan::GetFunction(Nan::New(BackgroundSubtractorWrap::constructor)).ToLocalChecked()).ToLocalChecked();
cv::Ptr<cv::BackgroundSubtractor> bg;
#if CV_MAJOR_VERSION >= 3
#ifdef HAVE_OPENCV_BGSEGM
cv::Ptr<cv::BackgroundSubtractor> bg = cv::bgsegm::createBackgroundSubtractorMOG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from cv::bgsegm::createBackgroundSubtractorMOG()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(n);
info.GetReturnValue().Set( n );
#else
JSTHROW_TYPE("OpenCV built without bgsem (opencv_contrib)")
#endif
#else
cv::Ptr<cv::BackgroundSubtractor> bg = new cv::BackgroundSubtractorMOG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from new cv::BackgroundSubtractorMOG()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(n);
info.GetReturnValue().Set( n );
#endif
}
NAN_METHOD(BackgroundSubtractorWrap::CreateMOG2) {
Nan::HandleScope scope;
// int history = 200;
// int nmixtures = 5;
// double backgroundRatio = 0.7;
// double noiseSigma = 0;
//
// if(info.Length() > 1){
// INT_FROM_ARGS(history, 0)
// INT_FROM_ARGS(nmixtures, 1)
// DOUBLE_FROM_ARGS(backgroundRatio, 2)
// DOUBLE_FROM_ARGS(noiseSigma, 3)
// }
Local<Object> n = Nan::NewInstance(Nan::GetFunction(Nan::New(BackgroundSubtractorWrap::constructor)).ToLocalChecked()).ToLocalChecked();
#if CV_MAJOR_VERSION >= 3
cv::Ptr<cv::BackgroundSubtractor> bg = cv::createBackgroundSubtractorMOG2();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from cv::createBackgroundSubtractorMOG2()");
}
#else
cv::Ptr<cv::BackgroundSubtractor> bg = new cv::BackgroundSubtractorMOG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from new cv::BackgroundSubtractorMOG()");
}
#endif
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(n);
info.GetReturnValue().Set( n );
}
NAN_METHOD(BackgroundSubtractorWrap::CreateGMG) {
Nan::HandleScope scope;
// int history = 200;
// int nmixtures = 5;
// double backgroundRatio = 0.7;
// double noiseSigma = 0;
//
// if(info.Length() > 1){
// INT_FROM_ARGS(history, 0)
// INT_FROM_ARGS(nmixtures, 1)
// DOUBLE_FROM_ARGS(backgroundRatio, 2)
// DOUBLE_FROM_ARGS(noiseSigma, 3)
// }
Local<Object> n = Nan::NewInstance(Nan::GetFunction(Nan::New(BackgroundSubtractorWrap::constructor)).ToLocalChecked()).ToLocalChecked();
#if CV_MAJOR_VERSION >= 3
#ifdef HAVE_OPENCV_BGSEGM
cv::Ptr<cv::BackgroundSubtractor> bg = cv::bgsegm::createBackgroundSubtractorGMG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from cv::bgsegm::createBackgroundSubtractorGMG()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(n);
info.GetReturnValue().Set( n );
#else
JSTHROW_TYPE("OpenCV built without bgsem (opencv_contrib) - use MOG2")
#endif
#else
cv::Ptr<cv::BackgroundSubtractor> bg = new cv::BackgroundSubtractorMOG();
if (NULL == bg){
JSTHROW_TYPE("OpenCV NULL from new cv::BackgroundSubtractorMOG()");
}
BackgroundSubtractorWrap *pt = new BackgroundSubtractorWrap(bg);
pt->Wrap(n);
info.GetReturnValue().Set( n );
#endif
}
// Fetch foreground mask
NAN_METHOD(BackgroundSubtractorWrap::ApplyMOG) {
SETUP_FUNCTION(BackgroundSubtractorWrap);
@ -85,6 +246,14 @@ NAN_METHOD(BackgroundSubtractorWrap::ApplyMOG) {
return;
}
if (NULL == self->subtractor){
argv[0] = Nan::New("BackgroundSubtractor not created").ToLocalChecked();
argv[1] = Nan::Null();
cb->Call(Nan::GetCurrentContext()->Global(), 2, argv);
return;
}
try {
Local<Object> fgMask =
Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked();
@ -106,9 +275,18 @@ NAN_METHOD(BackgroundSubtractorWrap::ApplyMOG) {
return Nan::ThrowTypeError("Error loading file");
}
cv::Mat _fgMask;
{
// wait here if already in apply - auto-release on scope exit
BGAutoMutex(self->applymutex);
#if CV_MAJOR_VERSION >= 3
self->subtractor->apply(mat, _fgMask);
#else
self->subtractor->operator()(mat, _fgMask);
#endif
}
img->mat = _fgMask;
mat.release();
@ -129,6 +307,219 @@ NAN_METHOD(BackgroundSubtractorWrap::ApplyMOG) {
}
}
class AsyncBackgroundSubtractorWorker: public Nan::AsyncWorker {
public:
AsyncBackgroundSubtractorWorker(
Nan::Callback *callback,
BackgroundSubtractorWrap *bg,
Matrix *img) :
Nan::AsyncWorker(callback),
bg(bg),
img(img) {
}
~AsyncBackgroundSubtractorWorker() {
}
// Executed inside the worker-thread.
// It is not safe to access V8, or V8 data structures
// here, so everything we need for input and output
// should go on `this`.
void Execute() {
// wait here if already in apply - auto-release on scope exit
BGAutoMutex(bg->applymutex);
#if CV_MAJOR_VERSION >= 3
bg->subtractor->apply(this->img->mat, _fgMask);
#else
bg->subtractor->operator()(this->img->mat, _fgMask);
#endif
}
// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use V8 again
void HandleOKCallback() {
Nan::HandleScope scope;
Local<Object> im_to_return= Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked();
Matrix *imgout = Nan::ObjectWrap::Unwrap<Matrix>(im_to_return);
imgout->mat = _fgMask;
Local<Value> argv[] = {
Nan::Null()
, im_to_return
};
Nan::TryCatch try_catch;
callback->Call(2, argv);
if (try_catch.HasCaught()) {
Nan::FatalException(try_catch);
}
}
private:
BackgroundSubtractorWrap *bg;
Matrix *img;
cv::Mat _fgMask;
};
// Fetch foreground mask
NAN_METHOD(BackgroundSubtractorWrap::Apply) {
SETUP_FUNCTION(BackgroundSubtractorWrap);
int callback_arg = -1;
int numargs = info.Length();
int success = 1;
Local<Function> cb;
if (info[numargs-1]->IsFunction()){
callback_arg = numargs-1;
cb = Local<Function>::Cast(info[callback_arg]);
}
// if async
if (callback_arg > 0){
Local<Value> argv[2];
if (info.Length() == 0) {
argv[0] = Nan::New("Input image missing").ToLocalChecked();
argv[1] = Nan::Null();
cb->Call(Nan::GetCurrentContext()->Global(), 2, argv);
return;
}
if (NULL == self->subtractor){
argv[0] = Nan::New("BackgroundSubtractor not created").ToLocalChecked();
argv[1] = Nan::Null();
cb->Call(Nan::GetCurrentContext()->Global(), 2, argv);
return;
}
Nan::Callback *callback = new Nan::Callback(cb.As<Function>());
Matrix *_img = Nan::ObjectWrap::Unwrap<Matrix>(info[0]->ToObject());
Nan::AsyncQueueWorker(new AsyncBackgroundSubtractorWorker( callback, self, _img));
return;
} else { //synchronous - return the image
try {
Local<Object> fgMask =
Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked();
Matrix *img = Nan::ObjectWrap::Unwrap<Matrix>(fgMask);
cv::Mat mat;
if (Buffer::HasInstance(info[0])) {
uint8_t *buf = (uint8_t *) Buffer::Data(info[0]->ToObject());
unsigned len = Buffer::Length(info[0]->ToObject());
cv::Mat *mbuf = new cv::Mat(len, 1, CV_64FC1, buf);
mat = cv::imdecode(*mbuf, -1);
//mbuf->release();
} else {
Matrix *_img = Nan::ObjectWrap::Unwrap<Matrix>(info[0]->ToObject());
mat = (_img->mat).clone();
}
if (mat.empty()) {
return Nan::ThrowTypeError("Empty matrix?");
}
{
// wait here if already in apply - auto-release on scope exit
BGAutoMutex(self->applymutex);
cv::Mat _fgMask;
#if CV_MAJOR_VERSION >= 3
self->subtractor->apply(mat, _fgMask);
#else
self->subtractor->operator()(mat, _fgMask);
#endif
img->mat = _fgMask;
}
mat.release();
info.GetReturnValue().Set(fgMask);
return;
} catch (cv::Exception& e) {
const char* err_msg = e.what();
Nan::ThrowError(err_msg);
return;
}
}
}
NAN_METHOD(BackgroundSubtractorWrap::History) {
SETUP_FUNCTION(BackgroundSubtractorWrap);
auto mog = getMOG(self);
if (!mog) {
Nan::ThrowError("Not using a BackgroundSubtractorMOG");
}
// only support for V3+ with opencv-contrib
#ifdef HAVE_OPENCV_BGSEGM
if (info.Length() > 0) {
mog->setHistory(info[0]->NumberValue());
}
info.GetReturnValue().Set(mog->getHistory());
#else
info.GetReturnValue().Set(Nan::Null());
#endif
}
NAN_METHOD(BackgroundSubtractorWrap::BackgroundRatio) {
SETUP_FUNCTION(BackgroundSubtractorWrap);
auto mog = getMOG(self);
if (!mog) {
Nan::ThrowError("Not using a BackgroundSubtractorMOG");
}
// only support for V3+ with opencv-contrib
#ifdef HAVE_OPENCV_BGSEGM
if (info.Length() > 0) {
mog->setBackgroundRatio(info[0]->NumberValue());
}
info.GetReturnValue().Set(mog->getBackgroundRatio());
#else
info.GetReturnValue().Set(Nan::Null());
#endif
}
NAN_METHOD(BackgroundSubtractorWrap::NoiseSigma) {
SETUP_FUNCTION(BackgroundSubtractorWrap);
auto mog = getMOG(self);
if (!mog) {
Nan::ThrowError("Not using a BackgroundSubtractorMOG");
}
// only support for V3+ with opencv-contrib
#ifdef HAVE_OPENCV_BGSEGM
if (info.Length() > 0) {
mog->setNoiseSigma(info[0]->NumberValue());
}
info.GetReturnValue().Set(mog->getNoiseSigma());
#else
info.GetReturnValue().Set(Nan::Null());
#endif
}
NAN_METHOD(BackgroundSubtractorWrap::Mixtures) {
SETUP_FUNCTION(BackgroundSubtractorWrap);
auto mog = getMOG(self);
if (!mog) {
Nan::ThrowError("Not using a BackgroundSubtractorMOG");
}
// only support for V3+ with opencv-contrib
#ifdef HAVE_OPENCV_BGSEGM
if (info.Length() > 0) {
mog->setNMixtures(info[0]->NumberValue());
}
info.GetReturnValue().Set(mog->getNMixtures());
#else
info.GetReturnValue().Set(Nan::Null());
#endif
}
BackgroundSubtractorWrap::BackgroundSubtractorWrap(
cv::Ptr<cv::BackgroundSubtractor> _subtractor) {
subtractor = _subtractor;
@ -136,4 +527,3 @@ BackgroundSubtractorWrap::BackgroundSubtractorWrap(
#endif
#endif

View File

@ -2,13 +2,19 @@
#ifdef HAVE_OPENCV_VIDEO
#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4))
#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4)) || (CV_MAJOR_VERSION >= 3)
#define HAVE_BACKGROUNDSUBTRACTOR
#include <opencv2/video/background_segm.hpp>
#ifdef HAVE_OPENCV_BGSEGM
#include <opencv2/bgsegm.hpp>
#endif
class BackgroundSubtractorWrap: public Nan::ObjectWrap {
public:
cv::Ptr<cv::BackgroundSubtractor> subtractor;
cv::Mutex applymutex;
static Nan::Persistent<FunctionTemplate> constructor;
static void Init(Local<Object> target);
@ -17,7 +23,16 @@ public:
BackgroundSubtractorWrap(cv::Ptr<cv::BackgroundSubtractor> bg);
static NAN_METHOD(CreateMOG);
static NAN_METHOD(CreateMOG2);
static NAN_METHOD(CreateGMG);
static NAN_METHOD(ApplyMOG);
static NAN_METHOD(Apply);
static NAN_METHOD(History);
static NAN_METHOD(Mixtures);
static NAN_METHOD(NoiseSigma);
static NAN_METHOD(BackgroundRatio);
};
#endif