diff --git a/examples/readimage.js b/examples/readimage.js new file mode 100644 index 0000000..d0d49b9 --- /dev/null +++ b/examples/readimage.js @@ -0,0 +1,111 @@ +var cv = require('../lib/opencv'); + + +// read as a buffer for decode operations later +var fs = require('fs'); +var imgdata = fs.readFileSync("./files/mona.png"); + + + +var img = cv.readImage("./files/mona.png"); +console.log('Synchronous readImage("./files/mona.png")'+img.width()+'x'+img.height()); + +cv.readImage("./files/mona.png", function(err, im){ + console.log('callback readImage("./files/mona.png", fn(){})'+im.width()+'x'+im.height()); +}); + +img = cv.readImage( 100, 100 ); +console.log('Synchronous readImage(100, 100) (create mat)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImage(100, 100, function(err, im){ + console.log('callback readImage(100, 100, fn(){}) (create mat)'+im.width()+'x'+im.height()); +}); + +img = cv.readImage(imgdata); +console.log('Synchronous readImage(imgdata:Buffer)'+img.width()+'x'+img.height()); + +cv.readImage(imgdata, function(err, im){ + console.log('callback readImage(imgdata:Buffer, fn(){})'+im.width()+'x'+im.height()); +}); + + +// try with flags now +console.log('Now with flags'); + +img = cv.readImage("./files/mona.png", 0); +console.log('Synchronous readImage("./files/mona.png", 0) (monochrome)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImage("./files/mona.png", 1, function(err, im){ + console.log('callback readImage("./files/mona.png", 1, fn(){}) (colour)'+im.width()+'x'+im.height()+' type '+im.type()); +}); + +img = cv.readImage( 100, 100, cv.Constants.CV_8UC3 ); +console.log('Synchronous readImage(100, 100, cv.Constants.CV_8UC3) (create 8 bit 3 channel mat)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImage(100, 100, cv.Constants.CV_8UC1, function(err, im){ + console.log('callback readImage(100, 100, cv.Constants.CV_8UC1, fn(){}) (create mat)'+im.width()+'x'+im.height()+' type '+im.type()); +}); + +img = cv.readImage(imgdata, 0); +console.log('Synchronous readImage(imgdata:Buffer, 0) (monochrome)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImage(imgdata, 1, function(err, im){ + console.log('callback readImage(imgdata:Buffer, 1, fn(){}) (colour)'+im.width()+'x'+im.height()+' type '+im.type()); +}); + + + + + +// try with readImageAsync +console.log('Now with readImageAsync'); + +img = cv.readImageAsync("./files/mona.png"); +console.log('Synchronous readImageAsync("./files/mona.png")'+img.width()+'x'+img.height()); + +cv.readImageAsync("./files/mona.png", function(err, im){ + console.log('Asynchronous callback readImageAsync("./files/mona.png", fn(){})'+im.width()+'x'+im.height()); +}); + +img = cv.readImageAsync( 100, 100 ); +console.log('Synchronous readImageAsync(100, 100) (create mat)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImageAsync(100, 100, function(err, im){ + console.log('!!Synchronous!! callback readImageAsync(100, 100, fn(){}) (create mat)'+im.width()+'x'+im.height()); +}); + +img = cv.readImageAsync(imgdata); +console.log('Synchronous readImageAsync(imgdata:Buffer)'+img.width()+'x'+img.height()); + +cv.readImageAsync(imgdata, function(err, im){ + console.log('Asynchronous callback readImageAsync(imgdata:Buffer, fn(){})'+im.width()+'x'+im.height()); +}); + + + +// try with flags now +console.log('Now Async with flags'); + +img = cv.readImageAsync("./files/mona.png", 0); +console.log('Synchronous readImageAsync("./files/mona.png", 0) (monochrome)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImageAsync("./files/mona.png", 1, function(err, im){ + console.log('Asynchronous callback readImageAsync("./files/mona.png", 1, fn(){}) (colour)'+im.width()+'x'+im.height()+' type '+im.type()); +}); + +img = cv.readImageAsync( 100, 100, cv.Constants.CV_8UC3 ); +console.log('Synchronous readImageAsync(100, 100, cv.Constants.CV_8UC3) (create 8 bit 3 channel mat)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImageAsync(100, 100, cv.Constants.CV_8UC1, function(err, im){ + console.log('!!Synchronous!! callback readImageAsync(100, 100, cv.Constants.CV_8UC1, fn(){}) (create mat)'+im.width()+'x'+im.height()+' type '+im.type()); +}); + +img = cv.readImageAsync(imgdata, 0); +console.log('Synchronous readImageAsync(imgdata:Buffer, 0) (monochrome)'+img.width()+'x'+img.height()+' type '+img.type()); + +cv.readImageAsync(imgdata, 1, function(err, im){ + console.log('Asynchronous callback readImageAsync(imgdata:Buffer, 1, fn(){}) (colour)'+im.width()+'x'+im.height()+' type '+im.type()); +}); + +console.log('\nTruely Async callbacks should be after this line\n'); + diff --git a/src/OpenCV.cc b/src/OpenCV.cc index d77090e..ab8c8a5 100755 --- a/src/OpenCV.cc +++ b/src/OpenCV.cc @@ -11,13 +11,238 @@ void OpenCV::Init(Local target) { target->Set(Nan::New("version").ToLocalChecked(), Nan::New(out, n).ToLocalChecked()); Nan::SetMethod(target, "readImage", ReadImage); + Nan::SetMethod(target, "readImageAsync", ReadImageAsync); Nan::SetMethod(target, "readImageMulti", ReadImageMulti); } + +// worker which decodes an image from data. +class AsyncImDecodeWorker: public Nan::AsyncWorker { +public: + + AsyncImDecodeWorker(Nan::Callback *callback, uint8_t *buf, unsigned len, int flags = -1): + Nan::AsyncWorker(callback), + buf(buf), + len(len), + flags(flags){ + } + + ~AsyncImDecodeWorker() { + } + + void Execute() { +// Local img_to_return = + // Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked(); + // img = Nan::ObjectWrap::Unwrap(img_to_return); + cv::Mat *mbuf = new cv::Mat(len, 1, CV_64FC1, buf); + outputmat = cv::imdecode(*mbuf, flags); + } + + void HandleOKCallback() { + Nan::HandleScope scope; + + Local im_to_return= Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked(); + Matrix *img = Nan::ObjectWrap::Unwrap(im_to_return); + img->mat = outputmat; + + Local argv[] = { + Nan::Null(), + im_to_return + }; + + Nan::TryCatch try_catch; + callback->Call(2, argv); + if (try_catch.HasCaught()) { + Nan::FatalException(try_catch); + } + } + +private: + uint8_t *buf; + unsigned len; + int flags; + cv::Mat outputmat; + //Matrix *img; +}; + + +// worker which reads an image from a file. +class AsyncImReadWorker: public Nan::AsyncWorker { +public: + + AsyncImReadWorker(Nan::Callback *callback, std::string filename, int flags = CV_LOAD_IMAGE_UNCHANGED): + Nan::AsyncWorker(callback), + filename(filename), + flags(flags) { + } + + ~AsyncImReadWorker() { + } + + void Execute() { + outputmat = cv::imread(filename, CV_LOAD_IMAGE_UNCHANGED); + } + + void HandleOKCallback() { + Nan::HandleScope scope; + + Local im_to_return= Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked(); + Matrix *img = Nan::ObjectWrap::Unwrap(im_to_return); + img->mat = outputmat; + + Local argv[] = { + Nan::Null(), + im_to_return + }; + + Nan::TryCatch try_catch; + callback->Call(2, argv); + if (try_catch.HasCaught()) { + Nan::FatalException(try_catch); + } + } + +private: + std::string filename; + int flags; + cv::Mat outputmat; +}; + + + +NAN_METHOD(OpenCV::ReadImageAsync) { + Nan::EscapableHandleScope scope; + + Local argv[2]; + argv[0] = Nan::Null(); + argv[1] = Nan::Null(); + + int callback_arg = -1; + int numargs = info.Length(); + + Local cb; + + // deal with situation where we have int, int, cb + if (info[numargs-1]->IsFunction()){ + callback_arg = numargs-1; + cb = Local::Cast(info[callback_arg]); + } + + try { + + if (info[0]->IsNumber() && info[1]->IsNumber()) { + ////////////////////////////// + // create image from a filename + // always do sync as this take no time? + int width, height; + int type = CV_64FC1; + + // if we have a type arg + if ((numargs > 2) && info[2]->IsNumber()){ + type = info[2]->Uint32Value(); + } + + width = info[0]->Uint32Value(); + height = info[1]->Uint32Value(); + Local img_to_return = + Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked(); + Matrix *img = Nan::ObjectWrap::Unwrap(img_to_return); + img->mat = *(new cv::Mat(width, height, type)); + if (callback_arg < 0){ + info.GetReturnValue().Set(img_to_return); + return; + } else { + argv[0] = Nan::Null(); + argv[1] = img_to_return; + cb->Call(Nan::GetCurrentContext()->Global(), 2, argv); + return; + } + // WILL have returned by here unless exception + ////////////////////////////// + + } else if (info[0]->IsString()) { + + ////////////////////////////// + // read image from a filename + std::string filename = std::string(*Nan::Utf8String(info[0]->ToString())); + int flags = CV_LOAD_IMAGE_UNCHANGED; + if (numargs > 1){ + if (info[1]->IsNumber()){ + flags = info[1]->Uint32Value(); + } + } + if (callback_arg < 0){ + Local img_to_return = + Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked(); + Matrix *img = Nan::ObjectWrap::Unwrap(img_to_return); + img->mat = cv::imread(filename, flags); + info.GetReturnValue().Set(img_to_return); + return; + } else { + Nan::Callback *callback = new Nan::Callback(cb.As()); + Nan::AsyncQueueWorker(new AsyncImReadWorker(callback, filename, flags)); + return; + } + // WILL have returned by here unless exception + ////////////////////////////// + + } else if (Buffer::HasInstance(info[0])) { + ////////////////////////////// + // read image from a buffer + // if sync + int flags = CV_LOAD_IMAGE_UNCHANGED; + if (numargs > 1){ + if (info[1]->IsNumber()){ + flags = info[1]->Uint32Value(); + } + } + if (callback_arg < 0){ + Local img_to_return = + Nan::NewInstance(Nan::GetFunction(Nan::New(Matrix::constructor)).ToLocalChecked()).ToLocalChecked(); + Matrix *img = Nan::ObjectWrap::Unwrap(img_to_return); + 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); + img->mat = cv::imdecode(*mbuf, flags); + info.GetReturnValue().Set(img_to_return); + return; + } else { + // async + uint8_t *buf = (uint8_t *) Buffer::Data(info[0]->ToObject()); + unsigned len = Buffer::Length(info[0]->ToObject()); + Nan::Callback *callback = new Nan::Callback(cb.As()); + Nan::AsyncQueueWorker(new AsyncImDecodeWorker(callback, buf, len, flags)); + return; + } + // WILL have returned by here unless exception + ////////////////////////////// + } + + } catch (cv::Exception& e) { + argv[0] = Nan::Error(e.what()); + argv[1] = Nan::Null(); + } + + Nan::TryCatch try_catch; + // if we got a callback + if (callback_arg >= 0){ + // if using callback + cb->Call(Nan::GetCurrentContext()->Global(), 2, argv); + } else { + // can only get here by exception + info.GetReturnValue().Set(Nan::New(false)); + } + if (try_catch.HasCaught()) { + Nan::FatalException(try_catch); + } + return; +} + + + NAN_METHOD(OpenCV::ReadImage) { Nan::EscapableHandleScope scope; - REQ_FUN_ARG(1, cb); Local argv[2]; argv[0] = Nan::Null(); @@ -26,45 +251,82 @@ NAN_METHOD(OpenCV::ReadImage) { Matrix *img = Nan::ObjectWrap::Unwrap(im_h); argv[1] = im_h; + int callback_arg = -1; + int numargs = info.Length(); + int success = 1; + + Local cb; + + // deal with situation where we have int, int, cb + if (info[numargs-1]->IsFunction()){ + callback_arg = numargs-1; + cb = Local::Cast(info[callback_arg]); + } + try { cv::Mat mat; if (info[0]->IsNumber() && info[1]->IsNumber()) { int width, height; - + int type = CV_64FC1; + // if we have a type arg + if ((numargs > 2) && info[2]->IsNumber()){ + type = info[2]->Uint32Value(); + } width = info[0]->Uint32Value(); height = info[1]->Uint32Value(); - mat = *(new cv::Mat(width, height, CV_64FC1)); + mat = *(new cv::Mat(width, height, type)); } else if (info[0]->IsString()) { std::string filename = std::string(*Nan::Utf8String(info[0]->ToString())); - mat = cv::imread(filename, CV_LOAD_IMAGE_UNCHANGED); + int flags = CV_LOAD_IMAGE_UNCHANGED; + if (numargs > 1){ + if (info[1]->IsNumber()){ + flags = info[1]->Uint32Value(); + } + } + mat = cv::imread(filename, flags); } else if (Buffer::HasInstance(info[0])) { uint8_t *buf = (uint8_t *) Buffer::Data(info[0]->ToObject()); unsigned len = Buffer::Length(info[0]->ToObject()); - + int flags = CV_LOAD_IMAGE_UNCHANGED; + if (numargs > 1){ + if (info[1]->IsNumber()){ + flags = info[1]->Uint32Value(); + } + } cv::Mat *mbuf = new cv::Mat(len, 1, CV_64FC1, buf); - mat = cv::imdecode(*mbuf, CV_LOAD_IMAGE_UNCHANGED); - + mat = cv::imdecode(*mbuf, flags); if (mat.empty()) { + success = 0; argv[0] = Nan::Error("Error loading file"); } + } img->mat = mat; } catch (cv::Exception& e) { argv[0] = Nan::Error(e.what()); argv[1] = Nan::Null(); + success = 0; } Nan::TryCatch try_catch; - cb->Call(Nan::GetCurrentContext()->Global(), 2, argv); - + // if we got a callback + if (callback_arg >= 0){ + // if using callback + cb->Call(Nan::GetCurrentContext()->Global(), 2, argv); + } else { + // if to return the mat + if (success) + info.GetReturnValue().Set(im_h); + else + info.GetReturnValue().Set(Nan::New(false)); + } if (try_catch.HasCaught()) { Nan::FatalException(try_catch); } - return; } diff --git a/src/OpenCV.h b/src/OpenCV.h index 60739de..6ef42db 100755 --- a/src/OpenCV.h +++ b/src/OpenCV.h @@ -86,6 +86,7 @@ public: static void Init(Local target); static NAN_METHOD(ReadImage); + static NAN_METHOD(ReadImageAsync); static NAN_METHOD(ReadImageMulti); };