#include "Contours.h" #include "Matrix.h" #include "OpenCV.h" #include v8::Persistent Matrix::constructor; cv::Scalar setColor(Local objColor); cv::Point setPoint(Local objPoint); cv::Rect* setRect(Local objRect, cv::Rect &result); void Matrix::Init(Handle target) { NanScope(); //Class Local ctor = NanNew(Matrix::New); NanAssignPersistent(constructor, ctor); ctor->InstanceTemplate()->SetInternalFieldCount(1); ctor->SetClassName(NanNew("Matrix")); // Prototype NODE_SET_PROTOTYPE_METHOD(ctor, "row", Row); NODE_SET_PROTOTYPE_METHOD(ctor, "col", Col); NODE_SET_PROTOTYPE_METHOD(ctor, "pixelRow", PixelRow); NODE_SET_PROTOTYPE_METHOD(ctor, "pixelCol", PixelCol); NODE_SET_PROTOTYPE_METHOD(ctor, "empty", Empty); NODE_SET_PROTOTYPE_METHOD(ctor, "get", Get); NODE_SET_PROTOTYPE_METHOD(ctor, "set", Set); NODE_SET_PROTOTYPE_METHOD(ctor, "put", Put); NODE_SET_PROTOTYPE_METHOD(ctor, "brightness", Brightness); NODE_SET_PROTOTYPE_METHOD(ctor, "normalize", Normalize); NODE_SET_PROTOTYPE_METHOD(ctor, "getData", GetData); NODE_SET_PROTOTYPE_METHOD(ctor, "pixel", Pixel); NODE_SET_PROTOTYPE_METHOD(ctor, "width", Width); NODE_SET_PROTOTYPE_METHOD(ctor, "height", Height); NODE_SET_PROTOTYPE_METHOD(ctor, "size", Size); NODE_SET_PROTOTYPE_METHOD(ctor, "clone", Clone); NODE_SET_PROTOTYPE_METHOD(ctor, "crop", Crop); NODE_SET_PROTOTYPE_METHOD(ctor, "toBuffer", ToBuffer); NODE_SET_PROTOTYPE_METHOD(ctor, "toBufferAsync", ToBufferAsync); NODE_SET_PROTOTYPE_METHOD(ctor, "ellipse", Ellipse); NODE_SET_PROTOTYPE_METHOD(ctor, "rectangle", Rectangle); NODE_SET_PROTOTYPE_METHOD(ctor, "line", Line); NODE_SET_PROTOTYPE_METHOD(ctor, "fillPoly", FillPoly); NODE_SET_PROTOTYPE_METHOD(ctor, "save", Save); NODE_SET_PROTOTYPE_METHOD(ctor, "saveAsync", SaveAsync); NODE_SET_PROTOTYPE_METHOD(ctor, "resize", Resize); NODE_SET_PROTOTYPE_METHOD(ctor, "rotate", Rotate); NODE_SET_PROTOTYPE_METHOD(ctor, "copyTo", CopyTo); NODE_SET_PROTOTYPE_METHOD(ctor, "pyrDown", PyrDown); NODE_SET_PROTOTYPE_METHOD(ctor, "pyrUp", PyrUp); NODE_SET_PROTOTYPE_METHOD(ctor, "channels", Channels); NODE_SET_PROTOTYPE_METHOD(ctor, "convertGrayscale", ConvertGrayscale); NODE_SET_PROTOTYPE_METHOD(ctor, "convertHSVscale", ConvertHSVscale); NODE_SET_PROTOTYPE_METHOD(ctor, "gaussianBlur", GaussianBlur); NODE_SET_PROTOTYPE_METHOD(ctor, "medianBlur", MedianBlur); NODE_SET_PROTOTYPE_METHOD(ctor, "bilateralFilter", BilateralFilter); NODE_SET_PROTOTYPE_METHOD(ctor, "copy", Copy); NODE_SET_PROTOTYPE_METHOD(ctor, "flip", Flip); NODE_SET_PROTOTYPE_METHOD(ctor, "roi", ROI); NODE_SET_PROTOTYPE_METHOD(ctor, "ptr", Ptr); NODE_SET_PROTOTYPE_METHOD(ctor, "absDiff", AbsDiff); NODE_SET_PROTOTYPE_METHOD(ctor, "addWeighted", AddWeighted); NODE_SET_PROTOTYPE_METHOD(ctor, "bitwiseXor", BitwiseXor); NODE_SET_PROTOTYPE_METHOD(ctor, "bitwiseNot", BitwiseNot); NODE_SET_PROTOTYPE_METHOD(ctor, "bitwiseAnd", BitwiseAnd); NODE_SET_PROTOTYPE_METHOD(ctor, "countNonZero", CountNonZero); NODE_SET_PROTOTYPE_METHOD(ctor, "canny", Canny); NODE_SET_PROTOTYPE_METHOD(ctor, "dilate", Dilate); NODE_SET_PROTOTYPE_METHOD(ctor, "erode", Erode); NODE_SET_PROTOTYPE_METHOD(ctor, "findContours", FindContours); NODE_SET_PROTOTYPE_METHOD(ctor, "drawContour", DrawContour); NODE_SET_PROTOTYPE_METHOD(ctor, "drawAllContours", DrawAllContours); NODE_SET_PROTOTYPE_METHOD(ctor, "goodFeaturesToTrack", GoodFeaturesToTrack); NODE_SET_PROTOTYPE_METHOD(ctor, "houghLinesP", HoughLinesP); NODE_SET_PROTOTYPE_METHOD(ctor, "crop", Crop); NODE_SET_PROTOTYPE_METHOD(ctor, "houghCircles", HoughCircles); NODE_SET_PROTOTYPE_METHOD(ctor, "inRange", inRange); NODE_SET_PROTOTYPE_METHOD(ctor, "adjustROI", AdjustROI); NODE_SET_PROTOTYPE_METHOD(ctor, "locateROI", LocateROI); NODE_SET_PROTOTYPE_METHOD(ctor, "threshold", Threshold); NODE_SET_PROTOTYPE_METHOD(ctor, "adaptiveThreshold", AdaptiveThreshold); NODE_SET_PROTOTYPE_METHOD(ctor, "meanStdDev", MeanStdDev); NODE_SET_PROTOTYPE_METHOD(ctor, "cvtColor", CvtColor); NODE_SET_PROTOTYPE_METHOD(ctor, "split", Split); NODE_SET_PROTOTYPE_METHOD(ctor, "merge", Merge); NODE_SET_PROTOTYPE_METHOD(ctor, "equalizeHist", EqualizeHist); NODE_SET_PROTOTYPE_METHOD(ctor, "floodFill", FloodFill); NODE_SET_PROTOTYPE_METHOD(ctor, "matchTemplate", MatchTemplate); NODE_SET_PROTOTYPE_METHOD(ctor, "templateMatches", TemplateMatches); NODE_SET_PROTOTYPE_METHOD(ctor, "minMaxLoc", MinMaxLoc); NODE_SET_PROTOTYPE_METHOD(ctor, "pushBack", PushBack); NODE_SET_PROTOTYPE_METHOD(ctor, "putText", PutText); NODE_SET_PROTOTYPE_METHOD(ctor, "getPerspectiveTransform", GetPerspectiveTransform); NODE_SET_PROTOTYPE_METHOD(ctor, "warpPerspective", WarpPerspective); NODE_SET_METHOD(ctor, "Zeros", Zeros); NODE_SET_METHOD(ctor, "Ones", Ones); NODE_SET_METHOD(ctor, "Eye", Eye); NODE_SET_PROTOTYPE_METHOD(ctor, "copyWithMask", CopyWithMask); NODE_SET_PROTOTYPE_METHOD(ctor, "setWithMask", SetWithMask); NODE_SET_PROTOTYPE_METHOD(ctor, "meanWithMask", MeanWithMask); NODE_SET_PROTOTYPE_METHOD(ctor, "shift", Shift); NODE_SET_PROTOTYPE_METHOD(ctor, "release", Release); target->Set(NanNew("Matrix"), ctor->GetFunction()); }; NAN_METHOD(Matrix::New) { NanScope(); if (args.This()->InternalFieldCount() == 0) { NanThrowTypeError("Cannot instantiate without new"); } Matrix *mat; if (args.Length() == 0) { mat = new Matrix; } else if (args.Length() == 2 && args[0]->IsInt32() && args[1]->IsInt32()) { mat = new Matrix(args[0]->IntegerValue(), args[1]->IntegerValue()); } else if (args.Length() == 3 && args[0]->IsInt32() && args[1]->IsInt32() && args[2]->IsInt32()) { mat = new Matrix(args[0]->IntegerValue(), args[1]->IntegerValue(), args[2]->IntegerValue()); } else if (args.Length() == 4 && args[0]->IsInt32() && args[1]->IsInt32() && args[2]->IsInt32() && args[3]->IsArray()) { } else { // if (args.Length() == 5) { Matrix *other = ObjectWrap::Unwrap(args[0]->ToObject()); int x = args[1]->IntegerValue(); int y = args[2]->IntegerValue(); int w = args[3]->IntegerValue(); int h = args[4]->IntegerValue(); mat = new Matrix(other->mat, cv::Rect(x, y, w, h)); } mat->Wrap(args.Holder()); NanReturnValue(args.Holder()); } Matrix::Matrix() : ObjectWrap() { mat = cv::Mat(); } Matrix::Matrix(int rows, int cols) : ObjectWrap() { mat = cv::Mat(rows, cols, CV_32FC3); } Matrix::Matrix(int rows, int cols, int type) : ObjectWrap() { mat = cv::Mat(rows, cols, type); } Matrix::Matrix(cv::Mat m, cv::Rect roi) : ObjectWrap() { mat = cv::Mat(m, roi); } Matrix::Matrix(int rows, int cols, int type, Local scalarObj) { mat = cv::Mat(rows, cols, type); if (mat.channels() == 3) { mat.setTo(cv::Scalar(scalarObj->Get(0)->IntegerValue(), scalarObj->Get(1)->IntegerValue(), scalarObj->Get(2)->IntegerValue())); } else if (mat.channels() == 2) { mat.setTo(cv::Scalar(scalarObj->Get(0)->IntegerValue(), scalarObj->Get(1)->IntegerValue())); } else if (mat.channels() == 1) { mat.setTo(cv::Scalar(scalarObj->Get(0)->IntegerValue())); } else { NanThrowError("Only 1-3 channels are supported"); } } NAN_METHOD(Matrix::Empty) { SETUP_FUNCTION(Matrix) NanReturnValue(NanNew(self->mat.empty())); } double Matrix::DblGet(cv::Mat mat, int i, int j) { double val = 0; cv::Vec3b pix; unsigned int pint = 0; switch (mat.type()) { case CV_32FC3: pix = mat.at(i, j); pint |= (uchar) pix.val[2]; pint |= ((uchar) pix.val[1]) << 8; pint |= ((uchar) pix.val[0]) << 16; val = (double) pint; break; case CV_64FC1: val = mat.at(i, j); break; default: val = mat.at(i, j); break; } return val; } NAN_METHOD(Matrix::Pixel) { SETUP_FUNCTION(Matrix) int y = args[0]->IntegerValue(); int x = args[1]->IntegerValue(); // cv::Scalar scal = self->mat.at(y, x); if (args.Length() == 3) { Local < Object > objColor = args[2]->ToObject(); if (self->mat.channels() == 3) { self->mat.at(y, x)[0] = (uchar) objColor->Get(0)->IntegerValue(); self->mat.at(y, x)[1] = (uchar) objColor->Get(1)->IntegerValue(); self->mat.at(y, x)[2] = (uchar) objColor->Get(2)->IntegerValue(); } else if (self->mat.channels() == 1) self->mat.at(y, x) = (uchar) objColor->Get(0)->IntegerValue(); NanReturnValue(args[2]->ToObject()); } else { if (self->mat.channels() == 3) { cv::Vec3b intensity = self->mat.at(y, x); v8::Local < v8::Array > arr = NanNew(3); arr->Set(0, NanNew(intensity[0])); arr->Set(1, NanNew(intensity[1])); arr->Set(2, NanNew(intensity[2])); NanReturnValue(arr); } else if (self->mat.channels() == 1) { uchar intensity = self->mat.at(y, x); NanReturnValue(NanNew(intensity)); } } NanReturnUndefined(); // double val = Matrix::DblGet(t, i, j); // NanReturnValue(NanNew(val)); } NAN_METHOD(Matrix::Get) { SETUP_FUNCTION(Matrix) int i = args[0]->IntegerValue(); int j = args[1]->IntegerValue(); double val = Matrix::DblGet(self->mat, i, j); NanReturnValue(NanNew(val)); } NAN_METHOD(Matrix::Set) { SETUP_FUNCTION(Matrix) int i = args[0]->IntegerValue(); int j = args[1]->IntegerValue(); double val = args[2]->NumberValue(); int vint = 0; if (args.Length() == 4) { self->mat.at(i, j)[args[3]->NumberValue()] = val; } else if (args.Length() == 3) { switch (self->mat.type()) { case CV_32FC3: vint = static_cast(val + 0.5); self->mat.at(i, j)[0] = (uchar) (vint >> 16) & 0xff; self->mat.at(i, j)[1] = (uchar) (vint >> 8) & 0xff; self->mat.at(i, j)[2] = (uchar) (vint) & 0xff; // printf("!!!i %x, %x, %x", (vint >> 16) & 0xff, (vint >> 8) & 0xff, (vint) & 0xff); break; default: self->mat.at(i, j) = val; } } else { NanThrowTypeError("Invalid number of arguments"); } NanReturnUndefined(); } // @author tualo // put node buffer directly into the image data // img.put(new Buffer([0,100,0,100,100...])); NAN_METHOD(Matrix::Put) { SETUP_FUNCTION(Matrix) if (!Buffer::HasInstance(args[0])) { NanThrowTypeError("Not a buffer"); } const char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); memcpy(self->mat.data, buffer_data, buffer_length); NanReturnUndefined(); } // @author tualo // getData getting node buffer of image data NAN_METHOD(Matrix::GetData) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); int size = self->mat.rows * self->mat.cols * self->mat.elemSize1(); Local buf = NanNewBufferHandle(size); uchar* data = (uchar*) Buffer::Data(buf); memcpy(data, self->mat.data, size); v8::Local globalObj = NanGetCurrentContext()->Global(); v8::Local bufferConstructor = v8::Local::Cast(globalObj->Get(NanNew("Buffer"))); v8::Handle constructorArgs[3] = {buf, NanNew((unsigned) size), NanNew(0)}; v8::Local actualBuffer = bufferConstructor->NewInstance(3, constructorArgs); NanReturnValue(actualBuffer); } NAN_METHOD(Matrix::Brightness) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); if (args.Length() == 2) { cv::Mat image; if (self->mat.channels() == 3) { image = self->mat; } else if (self->mat.channels() == 1) { cv::Mat myimg = self->mat; cv::cvtColor(myimg, image, CV_GRAY2RGB); } else { NanThrowError("those channels are not supported"); } cv::Mat new_image = cv::Mat::zeros( image.size(), image.type() ); double alpha = args[0]->NumberValue(); int beta = args[1]->IntegerValue(); // Do the operation new_image(i,j) = alpha*image(i,j) + beta for (int y = 0; y < image.rows; y++ ) { for (int x = 0; x < image.cols; x++ ) { for (int c = 0; c < 3; c++ ) { new_image.at(y,x)[c] = cv::saturate_cast(alpha*( image.at(y,x)[c] ) + beta); } } } if (self->mat.channels() == 3) { new_image.copyTo(self->mat); } else if (self->mat.channels() == 1) { cv::Mat gray; cv::cvtColor(new_image, gray, CV_BGR2GRAY); gray.copyTo(self->mat); } } else { if (args.Length() == 1) { int diff = args[0]->IntegerValue(); cv::Mat img = self->mat + diff; img.copyTo(self->mat); } else { NanReturnValue(NanNew("Insufficient or wrong arguments")); } } NanReturnNull(); } // @author tualo // normalize wrapper NAN_METHOD(Matrix::Normalize) { if (!args[0]->IsNumber()) { NanThrowTypeError("min is required (argument 1)"); } if (!args[1]->IsNumber()) { NanThrowTypeError("max is required (argument 2)"); } int type = cv::NORM_MINMAX; if (args[2]->IsNumber()) { type = args[2]->Uint32Value(); if ((type != cv::NORM_MINMAX) || (type != cv::NORM_INF) || (type != cv::NORM_L1) || (type != cv::NORM_L2) || (type != cv::NORM_L2SQR) || (type != cv::NORM_HAMMING) || (type != cv::NORM_HAMMING2) || (type != cv::NORM_RELATIVE) || (type != cv::NORM_TYPE_MASK)) { NanThrowTypeError("type value must be NORM_INF=1, NORM_L1=2, NORM_L2=4," " NORM_L2SQR=5, NORM_HAMMING=6, NORM_HAMMING2=7, NORM_TYPE_MASK=7, " "NORM_RELATIVE=8, NORM_MINMAX=32 "); } } int dtype = -1; if (args[3]->IsNumber()) { dtype = args[3]->IntegerValue(); } double min = args[0]->NumberValue(); double max = args[1]->NumberValue(); Matrix *self = ObjectWrap::Unwrap(args.This()); cv::Mat norm; cv::Mat mask; if (args[4]->IsObject()) { Matrix *mmask = ObjectWrap::Unwrap(args[4]->ToObject()); mask = mmask->mat; } cv::normalize(self->mat, norm, min, max, type, dtype, mask); norm.copyTo(self->mat); NanReturnNull(); } NAN_METHOD(Matrix::Size) { SETUP_FUNCTION(Matrix) v8::Local < v8::Array > arr = NanNew(2); arr->Set(0, NanNew(self->mat.size().height)); arr->Set(1, NanNew(self->mat.size().width)); NanReturnValue(arr); } NAN_METHOD(Matrix::Clone) { SETUP_FUNCTION(Matrix) Local < Object > im_h = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *m = ObjectWrap::Unwrap(im_h); m->mat = self->mat.clone(); NanReturnValue(im_h); } NAN_METHOD(Matrix::Crop) { SETUP_FUNCTION(Matrix) if ((args.Length() == 4) && (args[0]->IsNumber()) && (args[1]->IsNumber()) && (args[2]->IsNumber()) && (args[3]->IsNumber())) { int x = args[0]->IntegerValue(); int y = args[1]->IntegerValue(); int width = args[2]->IntegerValue(); int height = args[3]->IntegerValue(); cv::Rect roi(x, y, width, height); Local < Object > im_h = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *m = ObjectWrap::Unwrap(im_h); m->mat = self->mat(roi); NanReturnValue(im_h); } else { NanReturnValue(NanNew("Insufficient or wrong arguments")); } } NAN_METHOD(Matrix::Row) { SETUP_FUNCTION(Matrix) int width = self->mat.size().width; int y = args[0]->IntegerValue(); v8::Local < v8::Array > arr = NanNew(width); for (int x = 0; x < width; x++) { double v = Matrix::DblGet(self->mat, y, x); arr->Set(x, NanNew(v)); } NanReturnValue(arr); } NAN_METHOD(Matrix::PixelRow) { SETUP_FUNCTION(Matrix) int width = self->mat.size().width; int y = args[0]->IntegerValue(); v8::Local < v8::Array > arr = NanNew(width * 3); for (int x = 0; x < width; x++) { cv::Vec3b pixel = self->mat.at(y, x); int offset = x * 3; arr->Set(offset, NanNew((double) pixel.val[0])); arr->Set(offset + 1, NanNew((double) pixel.val[1])); arr->Set(offset + 2, NanNew((double) pixel.val[2])); } NanReturnValue(arr); } NAN_METHOD(Matrix::Col) { SETUP_FUNCTION(Matrix) int height = self->mat.size().height; int x = args[0]->IntegerValue(); v8::Local < v8::Array > arr = NanNew(height); for (int y = 0; y < height; y++) { double v = Matrix::DblGet(self->mat, y, x); arr->Set(y, NanNew(v)); } NanReturnValue(arr); } NAN_METHOD(Matrix::PixelCol) { SETUP_FUNCTION(Matrix) int height = self->mat.size().height; int x = args[0]->IntegerValue(); v8::Local < v8::Array > arr = NanNew(height * 3); for (int y = 0; y < height; y++) { cv::Vec3b pixel = self->mat.at(y, x); int offset = y * 3; arr->Set(offset, NanNew((double) pixel.val[0])); arr->Set(offset + 1, NanNew((double) pixel.val[1])); arr->Set(offset + 2, NanNew((double) pixel.val[2])); } NanReturnValue(arr); } NAN_METHOD(Matrix::Width) { SETUP_FUNCTION(Matrix) NanReturnValue(NanNew(self->mat.size().width)); } NAN_METHOD(Matrix::Height) { SETUP_FUNCTION(Matrix) NanReturnValue(NanNew(self->mat.size().height)); } NAN_METHOD(Matrix::Channels) { SETUP_FUNCTION(Matrix) NanReturnValue(NanNew(self->mat.channels())); } NAN_METHOD(Matrix::ToBuffer) { SETUP_FUNCTION(Matrix) if ((args.Length() > 0) && (args[0]->IsFunction())) { return Matrix::ToBufferAsync(args); } // SergeMv changes // img.toBuffer({ext: ".png", pngCompression: 9}); // default png compression is 3 // img.toBuffer({ext: ".jpg", jpegQuality: 80}); // img.toBuffer(); // creates Jpeg with quality of 95 (Opencv default quality) // via the ext you can do other image formats too (like tiff), see // http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#imencode //--------------------------- // Provide default value const char *ext = ".jpg"; std::string optExt; std::vector params; // See if the options argument is passed if ((args.Length() > 0) && (args[0]->IsObject())) { // Get this options argument v8::Handle < v8::Object > options = v8::Handle::Cast(args[0]); // If the extension (image format) is provided if (options->Has(NanNew("ext"))) { v8::String::Utf8Value str( options->Get(NanNew("ext"))->ToString()); optExt = *str; ext = (const char *) optExt.c_str(); } if (options->Has(NanNew("jpegQuality"))) { int compression = options->Get(NanNew("jpegQuality"))->IntegerValue(); params.push_back(CV_IMWRITE_JPEG_QUALITY); params.push_back(compression); } if (options->Has(NanNew("pngCompression"))) { int compression = options->Get(NanNew("pngCompression"))->IntegerValue(); params.push_back(CV_IMWRITE_PNG_COMPRESSION); params.push_back(compression); } } //--------------------------- std::vector vec(0); cv::imencode(ext, self->mat, vec, params); Local < Object > buf = NanNewBufferHandle(vec.size()); uchar* data = (uchar*) Buffer::Data(buf); memcpy(data, &vec[0], vec.size()); v8::Local < v8::Object > globalObj = NanGetCurrentContext()->Global(); v8::Local < v8::Function > bufferConstructor = v8::Local < v8::Function > ::Cast(globalObj->Get(NanNew("Buffer"))); v8::Handle constructorArgs[3] = {buf, NanNew((unsigned)vec.size()), NanNew(0)}; v8::Local < v8::Object > actualBuffer = bufferConstructor->NewInstance(3, constructorArgs); NanReturnValue(actualBuffer); } class AsyncToBufferWorker: public NanAsyncWorker { public: AsyncToBufferWorker(NanCallback *callback, Matrix* matrix, string ext, vector params) : NanAsyncWorker(callback), matrix(matrix), ext(ext), params(params) { } ~AsyncToBufferWorker() { } void Execute() { std::vector vec(0); // std::vector params(0);//CV_IMWRITE_JPEG_QUALITY 90 cv::imencode(ext, this->matrix->mat, vec, this->params); res = vec; } void HandleOKCallback() { NanScope(); Local buf = NanNewBufferHandle(res.size()); uchar* data = (uchar*) Buffer::Data(buf); memcpy(data, &res[0], res.size()); v8::Local globalObj = NanGetCurrentContext()->Global(); v8::Local bufferConstructor = v8::Local::Cast(globalObj->Get(NanNew("Buffer"))); v8::Handle constructorArgs[3] = {buf, NanNew((unsigned)res.size()), NanNew(0)}; v8::Local actualBuffer = bufferConstructor->NewInstance(3, constructorArgs); Local argv[] = { NanNull(), actualBuffer }; TryCatch try_catch; callback->Call(2, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } } private: Matrix* matrix; std::string ext; std::vector params; std::vector res; }; NAN_METHOD(Matrix::ToBufferAsync) { SETUP_FUNCTION(Matrix) REQ_FUN_ARG(0, cb); std::string ext = std::string(".jpg"); std::vector params; // See if the options argument is passed if ((args.Length() > 1) && (args[1]->IsObject())) { // Get this options argument v8::Handle < v8::Object > options = v8::Handle::Cast(args[1]); // If the extension (image format) is provided if (options->Has(NanNew("ext"))) { v8::String::Utf8Value str( options->Get(NanNew("ext"))->ToString()); std::string str2 = std::string(*str); ext = str2; } if (options->Has(NanNew("jpegQuality"))) { int compression = options->Get(NanNew("jpegQuality"))->IntegerValue(); params.push_back(CV_IMWRITE_JPEG_QUALITY); params.push_back(compression); } if (options->Has(NanNew("pngCompression"))) { int compression = options->Get(NanNew("pngCompression"))->IntegerValue(); params.push_back(CV_IMWRITE_PNG_COMPRESSION); params.push_back(compression); } } NanCallback *callback = new NanCallback(cb.As()); NanAsyncQueueWorker(new AsyncToBufferWorker(callback, self, ext, params)); NanReturnUndefined(); } NAN_METHOD(Matrix::Ellipse) { SETUP_FUNCTION(Matrix) int x = 0; int y = 0; int width = 0; int height = 0; cv::Scalar color(0, 0, 255); int thickness = 1; double angle = 0; double startAngle = 0; double endAngle = 360; int lineType = 8; int shift = 0; if (args[0]->IsObject()) { v8::Handle < v8::Object > options = v8::Handle::Cast(args[0]); if (options->Has(NanNew("center"))) { Local < Object > center = options->Get(NanNew("center"))->ToObject(); x = center->Get(NanNew("x"))->Uint32Value(); y = center->Get(NanNew("y"))->Uint32Value(); } if (options->Has(NanNew("axes"))) { Local < Object > axes = options->Get(NanNew("axes"))->ToObject(); width = axes->Get(NanNew("width"))->Uint32Value(); height = axes->Get(NanNew("height"))->Uint32Value(); } if (options->Has(NanNew("thickness"))) { thickness = options->Get(NanNew("thickness"))->Uint32Value(); } if (options->Has(NanNew("angle"))) { angle = options->Get(NanNew("angle"))->NumberValue(); } if (options->Has(NanNew("startAngle"))) { startAngle = options->Get(NanNew("startAngle"))->NumberValue(); } if (options->Has(NanNew("endAngle"))) { endAngle = options->Get(NanNew("endAngle"))->NumberValue(); } if (options->Has(NanNew("lineType"))) { lineType = options->Get(NanNew("lineType"))->Uint32Value(); } if (options->Has(NanNew("shift"))) { shift = options->Get(NanNew("shift"))->Uint32Value(); } if (options->Has(NanNew("color"))) { Local < Object > objColor = options->Get(NanNew("color"))->ToObject(); color = setColor(objColor); } } else { x = args[0]->Uint32Value(); y = args[1]->Uint32Value(); width = args[2]->Uint32Value(); height = args[3]->Uint32Value(); if (args[4]->IsArray()) { Local < Object > objColor = args[4]->ToObject(); color = setColor(objColor); } if (args[5]->IntegerValue()) thickness = args[5]->IntegerValue(); } cv::ellipse(self->mat, cv::Point(x, y), cv::Size(width, height), angle, startAngle, endAngle, color, thickness, lineType, shift); NanReturnNull(); } NAN_METHOD(Matrix::Rectangle) { SETUP_FUNCTION(Matrix) if (args[0]->IsArray() && args[1]->IsArray()) { Local < Object > xy = args[0]->ToObject(); Local < Object > width_height = args[1]->ToObject(); cv::Scalar color(0, 0, 255); if (args[2]->IsArray()) { Local < Object > objColor = args[2]->ToObject(); color = setColor(objColor); } int x = xy->Get(0)->IntegerValue(); int y = xy->Get(1)->IntegerValue(); int width = width_height->Get(0)->IntegerValue(); int height = width_height->Get(1)->IntegerValue(); int thickness = 1; if (args[3]->IntegerValue()) thickness = args[3]->IntegerValue(); cv::rectangle(self->mat, cv::Point(x, y), cv::Point(x + width, y + height), color, thickness); } NanReturnNull(); } NAN_METHOD(Matrix::Line) { SETUP_FUNCTION(Matrix) if (args[0]->IsArray() && args[1]->IsArray()) { Local < Object > xy1 = args[0]->ToObject(); Local < Object > xy2 = args[1]->ToObject(); cv::Scalar color(0, 0, 255); if (args[2]->IsArray()) { Local < Object > objColor = args[2]->ToObject(); color = setColor(objColor); } int x1 = xy1->Get(0)->IntegerValue(); int y1 = xy1->Get(1)->IntegerValue(); int x2 = xy2->Get(0)->IntegerValue(); int y2 = xy2->Get(1)->IntegerValue(); int thickness = 1; if (args[3]->IntegerValue()) thickness = args[3]->IntegerValue(); cv::line(self->mat, cv::Point(x1, y1), cv::Point(x2, y2), color, thickness); } NanReturnNull(); } NAN_METHOD(Matrix::FillPoly) { SETUP_FUNCTION(Matrix) if (args[0]->IsArray()) { Local < Array > polyArray = Local < Array > ::Cast(args[0]->ToObject()); cv::Point **polygons = new cv::Point*[polyArray->Length()]; int *polySizes = new int[polyArray->Length()]; for (unsigned int i = 0; i < polyArray->Length(); i++) { Local singlePoly = Local ::Cast(polyArray->Get(i)->ToObject()); polygons[i] = new cv::Point[singlePoly->Length()]; polySizes[i] = singlePoly->Length(); for (unsigned int j = 0; j < singlePoly->Length(); j++) { Local point = Local ::Cast(singlePoly->Get(j)->ToObject()); polygons[i][j].x = point->Get(0)->IntegerValue(); polygons[i][j].y = point->Get(1)->IntegerValue(); } } cv::Scalar color(0, 0, 255); if (args[1]->IsArray()) { Local objColor = args[1]->ToObject(); color = setColor(objColor); } cv::fillPoly(self->mat, (const cv::Point **) polygons, polySizes, polyArray->Length(), color); } NanReturnNull(); } NAN_METHOD(Matrix::Save) { SETUP_FUNCTION(Matrix) if (args.Length() > 1) { return SaveAsync(args); } if (!args[0]->IsString()) { NanThrowTypeError("filename required"); } NanAsciiString filename(args[0]); int res = cv::imwrite(*filename, self->mat); NanReturnValue(NanNew(res)); } // All this is for async save, see here for nan example: // https://github.com/rvagg/nan/blob/c579ae858ae3208d7e702e8400042ba9d48fa64b/examples/async_pi_estimate/async.cc class AsyncSaveWorker: public NanAsyncWorker { public: AsyncSaveWorker(NanCallback *callback, Matrix* matrix, char* filename) : NanAsyncWorker(callback), matrix(matrix), filename(filename) { } ~AsyncSaveWorker() { } // 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() { res = cv::imwrite(this->filename, this->matrix->mat); } // 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() { NanScope(); Local argv[] = { NanNull(), NanNew(res) }; TryCatch try_catch; callback->Call(2, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } } private: Matrix* matrix; char* filename; int res; }; NAN_METHOD(Matrix::SaveAsync) { SETUP_FUNCTION(Matrix) if (!args[0]->IsString()) { NanThrowTypeError("filename required"); } NanAsciiString filename(args[0]); REQ_FUN_ARG(1, cb); NanCallback *callback = new NanCallback(cb.As()); NanAsyncQueueWorker(new AsyncSaveWorker(callback, self, *filename)); NanReturnUndefined(); } NAN_METHOD(Matrix::Zeros) { NanScope(); int w = args[0]->Uint32Value(); int h = args[1]->Uint32Value(); int type = (args.Length() > 2) ? args[2]->IntegerValue() : CV_64FC1; Local im_h = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(im_h); cv::Mat mat = cv::Mat::zeros(w, h, type); img->mat = mat; NanReturnValue(im_h); } NAN_METHOD(Matrix::Ones) { NanScope(); int w = args[0]->Uint32Value(); int h = args[1]->Uint32Value(); int type = (args.Length() > 2) ? args[2]->IntegerValue() : CV_64FC1; Local im_h = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(im_h); cv::Mat mat = cv::Mat::ones(w, h, type); img->mat = mat; NanReturnValue(im_h); } NAN_METHOD(Matrix::Eye) { NanScope(); int w = args[0]->Uint32Value(); int h = args[1]->Uint32Value(); int type = (args.Length() > 2) ? args[2]->IntegerValue() : CV_64FC1; Local im_h = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(im_h); cv::Mat mat = cv::Mat::eye(w, h, type); img->mat = mat; NanReturnValue(im_h); } NAN_METHOD(Matrix::ConvertGrayscale) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); if (self->mat.channels() != 3) { NanThrowError("Image is no 3-channel"); } cv::Mat gray; cv::cvtColor(self->mat, gray, CV_BGR2GRAY); gray.copyTo(self->mat); NanReturnNull(); } NAN_METHOD(Matrix::ConvertHSVscale) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); if (self->mat.channels() != 3) { NanThrowError("Image is no 3-channel"); } cv::Mat hsv; cv::cvtColor(self->mat, hsv, CV_BGR2HSV); hsv.copyTo(self->mat); NanReturnNull(); } NAN_METHOD(Matrix::GaussianBlur) { NanScope(); cv::Size ksize; cv::Mat blurred; Matrix *self = ObjectWrap::Unwrap(args.This()); if (args.Length() < 1) { ksize = cv::Size(5, 5); } else { if (!args[0]->IsArray()) { NanThrowTypeError("'ksize' argument must be a 2 double array"); } Local array = args[0]->ToObject(); // TODO: Length check Local x = array->Get(0); Local y = array->Get(1); if (!x->IsNumber() || !y->IsNumber()) { NanThrowTypeError("'ksize' argument must be a 2 double array"); } ksize = cv::Size(x->NumberValue(), y->NumberValue()); } cv::GaussianBlur(self->mat, blurred, ksize, 0); blurred.copyTo(self->mat); NanReturnNull(); } NAN_METHOD(Matrix::MedianBlur) { NanScope(); cv::Mat blurred; int ksize = 3; Matrix *self = ObjectWrap::Unwrap(args.This()); if (args[0]->IsNumber()) { ksize = args[0]->IntegerValue(); if ((ksize % 2) == 0) { NanThrowTypeError("'ksize' argument must be a positive odd integer"); } } else { NanThrowTypeError("'ksize' argument must be a positive odd integer"); } cv::medianBlur(self->mat, blurred, ksize); blurred.copyTo(self->mat); NanReturnNull(); } NAN_METHOD(Matrix::BilateralFilter) { NanScope(); cv::Mat filtered; int d = 15; double sigmaColor = 80; double sigmaSpace = 80; int borderType = cv::BORDER_DEFAULT; Matrix *self = ObjectWrap::Unwrap(args.This()); if (args.Length() != 0) { if (args.Length() < 3 || args.Length() > 4) { NanThrowTypeError("BilateralFilter takes 0, 3, or 4 arguments"); } else { d = args[0]->IntegerValue(); sigmaColor = args[1]->NumberValue(); sigmaSpace = args[2]->NumberValue(); if (args.Length() == 4) { borderType = args[3]->IntegerValue(); } } } cv::bilateralFilter(self->mat, filtered, d, sigmaColor, sigmaSpace, borderType); filtered.copyTo(self->mat); NanReturnNull(); } NAN_METHOD(Matrix::Copy) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Local img_to_return = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(img_to_return); self->mat.copyTo(img->mat); NanReturnValue(img_to_return); } NAN_METHOD(Matrix::Flip) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); if ( args.Length() < 1 || !args[0]->IsInt32() ) { NanThrowTypeError("Flip requires an integer flipCode argument " "(0 = X axis, positive = Y axis, negative = both axis)"); } int flipCode = args[0]->ToInt32()->Value(); Local img_to_return = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(img_to_return); cv::flip(self->mat, img->mat, flipCode); NanReturnValue(img_to_return); } NAN_METHOD(Matrix::ROI) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); if ( args.Length() != 4 ) { NanThrowTypeError("ROI requires x,y,w,h arguments"); } // Although it's an image to return, it is in fact a pointer to ROI of parent matrix Local img_to_return = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(img_to_return); int x = args[0]->IntegerValue(); int y = args[1]->IntegerValue(); int w = args[2]->IntegerValue(); int h = args[3]->IntegerValue(); cv::Mat roi(self->mat, cv::Rect(x,y,w,h)); img->mat = roi; NanReturnValue(img_to_return); } NAN_METHOD(Matrix::Ptr) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); int line = args[0]->Uint32Value(); char* data = self->mat.ptr(line); // uchar* data = self->mat.data; // char *mydata = "Random raw data\0"; Local return_buffer = NanNewBufferHandle((char*)data, self->mat.step); NanReturnValue( return_buffer ); // NanReturnUndefined(); } NAN_METHOD(Matrix::AbsDiff) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Matrix *src1 = ObjectWrap::Unwrap(args[0]->ToObject()); Matrix *src2 = ObjectWrap::Unwrap(args[1]->ToObject()); cv::absdiff(src1->mat, src2->mat, self->mat); NanReturnNull(); } NAN_METHOD(Matrix::AddWeighted) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Matrix *src1 = ObjectWrap::Unwrap(args[0]->ToObject()); Matrix *src2 = ObjectWrap::Unwrap(args[2]->ToObject()); float alpha = args[1]->NumberValue(); float beta = args[3]->NumberValue(); int gamma = 0; try { cv::addWeighted(src1->mat, alpha, src2->mat, beta, gamma, self->mat); } catch(cv::Exception& e ) { const char* err_msg = e.what(); NanThrowError(err_msg); } NanReturnNull(); } NAN_METHOD(Matrix::BitwiseXor) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Matrix *src1 = ObjectWrap::Unwrap(args[0]->ToObject()); Matrix *src2 = ObjectWrap::Unwrap(args[1]->ToObject()); if (args.Length() == 3) { Matrix *mask = ObjectWrap::Unwrap(args[2]->ToObject()); cv::bitwise_xor(src1->mat, src2->mat, self->mat, mask->mat); } else { cv::bitwise_xor(src1->mat, src2->mat, self->mat); } NanReturnNull(); } NAN_METHOD(Matrix::BitwiseNot) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Matrix *dst = ObjectWrap::Unwrap(args[0]->ToObject()); if (args.Length() == 2) { Matrix *mask = ObjectWrap::Unwrap(args[1]->ToObject()); cv::bitwise_not(self->mat, dst->mat, mask->mat); } else { cv::bitwise_not(self->mat, dst->mat); } NanReturnNull(); } NAN_METHOD(Matrix::BitwiseAnd) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Matrix *src1 = ObjectWrap::Unwrap(args[0]->ToObject()); Matrix *src2 = ObjectWrap::Unwrap(args[1]->ToObject()); if (args.Length() == 3) { Matrix *mask = ObjectWrap::Unwrap(args[2]->ToObject()); cv::bitwise_and(src1->mat, src2->mat, self->mat, mask->mat); } else { cv::bitwise_and(src1->mat, src2->mat, self->mat); } NanReturnNull(); } NAN_METHOD(Matrix::CountNonZero) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); double count = (double)cv::countNonZero(self->mat); NanReturnValue(NanNew(count)); } /* NAN_METHOD(Matrix::Split) { NanScope(); //Matrix *self = ObjectWrap::Unwrap(args.This()); NanReturnNull(); } */ NAN_METHOD(Matrix::Canny) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); int lowThresh = args[0]->NumberValue(); int highThresh = args[1]->NumberValue(); cv::Canny(self->mat, self->mat, lowThresh, highThresh); NanReturnNull(); } NAN_METHOD(Matrix::Dilate) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); int niters = args[0]->NumberValue(); cv::dilate(self->mat, self->mat, cv::Mat(), cv::Point(-1, -1), niters); NanReturnNull(); } NAN_METHOD(Matrix::Erode) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); int niters = args[0]->NumberValue(); cv::erode(self->mat, self->mat, cv::Mat(), cv::Point(-1, -1), niters); NanReturnNull(); } NAN_METHOD(Matrix::FindContours) { NanScope(); int mode = CV_RETR_LIST; int chain = CV_CHAIN_APPROX_SIMPLE; if (args.Length() > 0) { if (args[0]->IsNumber()) mode = args[0]->IntegerValue(); } if (args.Length() > 1) { if (args[1]->IsNumber()) chain = args[1]->IntegerValue(); } Matrix *self = ObjectWrap::Unwrap(args.This()); Local conts_to_return= NanNew(Contour::constructor)->GetFunction()->NewInstance(); Contour *contours = ObjectWrap::Unwrap(conts_to_return); cv::findContours(self->mat, contours->contours, contours->hierarchy, mode, chain); NanReturnValue(conts_to_return); } NAN_METHOD(Matrix::DrawContour) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Contour *cont = ObjectWrap::Unwrap(args[0]->ToObject()); int pos = args[1]->NumberValue(); cv::Scalar color(0, 0, 255); if (args[2]->IsArray()) { Local objColor = args[2]->ToObject(); color = setColor(objColor); } int thickness = args.Length() < 4 ? 1 : args[3]->NumberValue(); cv::drawContours(self->mat, cont->contours, pos, color, thickness); NanReturnUndefined(); } NAN_METHOD(Matrix::DrawAllContours) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Contour *cont = ObjectWrap::Unwrap(args[0]->ToObject()); cv::Scalar color(0, 0, 255); if (args[1]->IsArray()) { Local objColor = args[1]->ToObject(); color = setColor(objColor); } int thickness = args.Length() < 3 ? 1 : args[2]->NumberValue(); cv::drawContours(self->mat, cont->contours, -1, color, thickness); NanReturnUndefined(); } NAN_METHOD(Matrix::GoodFeaturesToTrack) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); std::vector corners; cv::Mat gray; cvtColor(self->mat, gray, CV_BGR2GRAY); equalizeHist(gray, gray); cv::goodFeaturesToTrack(gray, corners, 500, 0.01, 10); v8::Local arr = NanNew(corners.size()); for (unsigned int i=0; i pt = NanNew(2); pt->Set(0, NanNew((double) corners[i].x)); pt->Set(1, NanNew((double) corners[i].y)); arr->Set(i, pt); } NanReturnValue(arr); } NAN_METHOD(Matrix::HoughLinesP) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); double rho = args.Length() < 1 ? 1 : args[0]->NumberValue(); double theta = args.Length() < 2 ? CV_PI/180 : args[1]->NumberValue(); int threshold = args.Length() < 3 ? 80 : args[2]->Uint32Value(); double minLineLength = args.Length() < 4 ? 30 : args[3]->NumberValue(); double maxLineGap = args.Length() < 5 ? 10 : args[4]->NumberValue(); std::vector lines; cv::Mat gray; equalizeHist(self->mat, gray); // cv::Canny(gray, gray, 50, 200, 3); cv::HoughLinesP(gray, lines, rho, theta, threshold, minLineLength, maxLineGap); v8::Local arr = NanNew(lines.size()); for (unsigned int i=0; i pt = NanNew(4); pt->Set(0, NanNew((double) lines[i][0])); pt->Set(1, NanNew((double) lines[i][1])); pt->Set(2, NanNew((double) lines[i][2])); pt->Set(3, NanNew((double) lines[i][3])); arr->Set(i, pt); } NanReturnValue(arr); } NAN_METHOD(Matrix::HoughCircles) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); double dp = args.Length() < 1 ? 1 : args[0]->NumberValue(); double minDist = args.Length() < 2 ? 1 : args[1]->NumberValue(); double higherThreshold = args.Length() < 3 ? 100 : args[2]->NumberValue(); double accumulatorThreshold = args.Length() < 4 ? 100 : args[3]->NumberValue(); int minRadius = args.Length() < 5 ? 0 : args[4]->Uint32Value(); int maxRadius = args.Length() < 6 ? 0 : args[5]->Uint32Value(); std::vector circles; cv::Mat gray; equalizeHist(self->mat, gray); cv::HoughCircles(gray, circles, CV_HOUGH_GRADIENT, dp, minDist, higherThreshold, accumulatorThreshold, minRadius, maxRadius); v8::Local arr = NanNew(circles.size()); for (unsigned int i=0; i < circles.size(); i++) { v8::Local pt = NanNew(3); pt->Set(0, NanNew((double) circles[i][0])); // center x pt->Set(1, NanNew((double) circles[i][1]));// center y pt->Set(2, NanNew((double) circles[i][2]));// radius arr->Set(i, pt); } NanReturnValue(arr); } cv::Scalar setColor(Local objColor) { Local valB = objColor->Get(0); Local valG = objColor->Get(1); Local valR = objColor->Get(2); cv::Scalar color = cv::Scalar(valB->IntegerValue(), valG->IntegerValue(), valR->IntegerValue()); return color; } cv::Point setPoint(Local objPoint) { return cv::Point(objPoint->Get(0)->IntegerValue(), objPoint->Get(1)->IntegerValue()); } cv::Rect* setRect(Local objRect, cv::Rect &result) { if (!objRect->IsArray() || !objRect->Get(0)->IsArray() || !objRect->Get(0)->IsArray()) { printf("error"); return 0; }; Local < Object > point = objRect->Get(0)->ToObject(); Local < Object > size = objRect->Get(1)->ToObject(); result.x = point->Get(0)->IntegerValue(); result.y = point->Get(1)->IntegerValue(); result.width = size->Get(0)->IntegerValue(); result.height = size->Get(1)->IntegerValue(); return &result; } NAN_METHOD(Matrix::Resize) { NanScope(); int x = args[0]->Uint32Value(); int y = args[1]->Uint32Value(); /* CV_INTER_NN =0, CV_INTER_LINEAR =1, CV_INTER_CUBIC =2, CV_INTER_AREA =3, CV_INTER_LANCZOS4 =4 */ int interpolation = (args.Length() < 3) ? (int)cv::INTER_LINEAR : args[2]->Uint32Value(); Matrix *self = ObjectWrap::Unwrap(args.This()); cv::Mat res = cv::Mat(x, y, CV_32FC3); cv::resize(self->mat, res, cv::Size(x, y), 0, 0, interpolation); ~self->mat; self->mat = res; NanReturnUndefined(); } NAN_METHOD(Matrix::Rotate) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); cv::Mat rotMatrix(2, 3, CV_32FC1); cv::Mat res; float angle = args[0]->ToNumber()->Value(); // Modification by SergeMv //------------- // If you provide only the angle argument and the angle is multiple of 90, then // we do a fast thing bool rightOrStraight = (ceil(angle) == angle) && (!((int)angle % 90)) && (args.Length() == 1); if (rightOrStraight) { int angle2 = ((int)angle) % 360; if (!angle2) {NanReturnUndefined();} if (angle2 < 0) {angle2 += 360;} // See if we do right angle rotation, we transpose the matrix: if (angle2 % 180) { cv::transpose(self->mat, res); ~self->mat; self->mat = res; } // Now flip the image int mode = -1;// flip around both axes // If counterclockwise, flip around the x-axis if (angle2 == 90) {mode = 0;} // If clockwise, flip around the y-axis if (angle2 == 270) {mode = 1;} cv::flip(self->mat, self->mat, mode); NanReturnUndefined(); } //------------- int x = args[1]->IsUndefined() ? round(self->mat.size().width / 2) : args[1]->Uint32Value(); int y = args[1]->IsUndefined() ? round(self->mat.size().height / 2) : args[2]->Uint32Value(); cv::Point center = cv::Point(x,y); rotMatrix = getRotationMatrix2D(center, angle, 1.0); cv::warpAffine(self->mat, res, rotMatrix, self->mat.size()); ~self->mat; self->mat = res; NanReturnUndefined(); } NAN_METHOD(Matrix::PyrDown) { SETUP_FUNCTION(Matrix) cv::pyrDown(self->mat, self->mat); NanReturnUndefined(); } NAN_METHOD(Matrix::PyrUp) { SETUP_FUNCTION(Matrix) cv::pyrUp(self->mat, self->mat); NanReturnUndefined(); } NAN_METHOD(Matrix::inRange) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); /*if (self->mat.channels() != 3) NanThrowError(String::New("Image is no 3-channel"));*/ if (args[0]->IsArray() && args[1]->IsArray()) { Local args_lowerb = args[0]->ToObject(); Local 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); } NanReturnNull(); } NAN_METHOD(Matrix::AdjustROI) { SETUP_FUNCTION(Matrix) int dtop = args[0]->Uint32Value(); int dbottom = args[1]->Uint32Value(); int dleft = args[2]->Uint32Value(); int dright = args[3]->Uint32Value(); self->mat.adjustROI(dtop, dbottom, dleft, dright); NanReturnNull(); } NAN_METHOD(Matrix::LocateROI) { SETUP_FUNCTION(Matrix) cv::Size wholeSize; cv::Point ofs; self->mat.locateROI(wholeSize, ofs); v8::Local < v8::Array > arr = NanNew(4); arr->Set(0, NanNew(wholeSize.width)); arr->Set(1, NanNew(wholeSize.height)); arr->Set(2, NanNew(ofs.x)); arr->Set(3, NanNew(ofs.y)); NanReturnValue(arr); } NAN_METHOD(Matrix::Threshold) { SETUP_FUNCTION(Matrix) double threshold = args[0]->NumberValue(); double maxVal = args[1]->NumberValue(); int typ = cv::THRESH_BINARY; if (args.Length() == 3) { // typ = args[2]->IntegerValue(); NanAsciiString typstr(args[2]); if (strcmp(*typstr, "Binary") == 0) { typ = 0; } if (strcmp(*typstr, "Binary Inverted") == 0) { typ = 1; } if (strcmp(*typstr, "Threshold Truncated") == 0) { typ = 2; } if (strcmp(*typstr, "Threshold to Zero") == 0) { typ = 3; } if (strcmp(*typstr, "Threshold to Zero Inverted") == 0) { typ = 4; } } Local < Object > img_to_return = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(img_to_return); self->mat.copyTo(img->mat); cv::threshold(self->mat, img->mat, threshold, maxVal, typ); NanReturnValue(img_to_return); } NAN_METHOD(Matrix::AdaptiveThreshold) { SETUP_FUNCTION(Matrix) double maxVal = args[0]->NumberValue(); double adaptiveMethod = args[1]->NumberValue(); double thresholdType = args[2]->NumberValue(); double blockSize = args[3]->NumberValue(); double C = args[4]->NumberValue(); Local < Object > img_to_return = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = ObjectWrap::Unwrap(img_to_return); self->mat.copyTo(img->mat); cv::adaptiveThreshold(self->mat, img->mat, maxVal, adaptiveMethod, thresholdType, blockSize, C); NanReturnValue(img_to_return); } NAN_METHOD(Matrix::MeanStdDev) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Local mean = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *m_mean = ObjectWrap::Unwrap(mean); Local stddev = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *m_stddev = ObjectWrap::Unwrap(stddev); cv::meanStdDev(self->mat, m_mean->mat, m_stddev->mat); Local data = NanNew(); data->Set(NanNew("mean"), mean); data->Set(NanNew("stddev"), stddev); NanReturnValue(data); } // @author SergeMv // Copies our (small) image into a ROI of another (big) image // @param Object another image (destination) // @param Number Destination x (where our image is to be copied) // @param Number Destination y (where our image is to be copied) // Example: smallImg.copyTo(bigImg, 50, 50); // Note, x,y and width and height of our image must be so that // our.width + x <= destination.width (and the same for y and height) // both x and y must be >= 0 NAN_METHOD(Matrix::CopyTo) { NanScope(); Matrix * self = ObjectWrap::Unwrap(args.This()); int width = self->mat.size().width; int height = self->mat.size().height; // param 0 - destination image: Matrix *dest = ObjectWrap::Unwrap(args[0]->ToObject()); // param 1 - x coord of the destination int x = args[1]->IntegerValue(); // param 2 - y coord of the destination int y = args[2]->IntegerValue(); cv::Mat dstROI = cv::Mat(dest->mat, cv::Rect(x, y, width, height)); self->mat.copyTo(dstROI); NanReturnUndefined(); } // @author SergeMv // Does in-place color transformation // img.cvtColor('CV_BGR2YCrCb'); NAN_METHOD(Matrix::CvtColor) { NanScope(); Matrix * self = ObjectWrap::Unwrap(args.This()); v8::String::Utf8Value str (args[0]->ToString()); std::string str2 = std::string(*str); const char * sTransform = (const char *) str2.c_str(); int iTransform; // if (!strcmp(sTransform, "CV_BGR2GRAY")) {iTransform = CV_BGR2GRAY;} else if (!strcmp(sTransform, "CV_GRAY2BGR")) {iTransform = CV_GRAY2BGR;} // else if (!strcmp(sTransform, "CV_BGR2XYZ")) {iTransform = CV_BGR2XYZ;} else if (!strcmp(sTransform, "CV_XYZ2BGR")) {iTransform = CV_XYZ2BGR;} // else if (!strcmp(sTransform, "CV_BGR2YCrCb")) {iTransform = CV_BGR2YCrCb;} else if (!strcmp(sTransform, "CV_YCrCb2BGR")) {iTransform = CV_YCrCb2BGR;} // else if (!strcmp(sTransform, "CV_BGR2HSV")) {iTransform = CV_BGR2HSV;} else if (!strcmp(sTransform, "CV_HSV2BGR")) {iTransform = CV_HSV2BGR;} // else if (!strcmp(sTransform, "CV_BGR2HLS")) {iTransform = CV_BGR2HLS;} else if (!strcmp(sTransform, "CV_HLS2BGR")) {iTransform = CV_HLS2BGR;} // else if (!strcmp(sTransform, "CV_BGR2Lab")) {iTransform = CV_BGR2Lab;} else if (!strcmp(sTransform, "CV_Lab2BGR")) {iTransform = CV_Lab2BGR;} // else if (!strcmp(sTransform, "CV_BGR2Luv")) {iTransform = CV_BGR2Luv;} else if (!strcmp(sTransform, "CV_Luv2BGR")) {iTransform = CV_Luv2BGR;} // else if (!strcmp(sTransform, "CV_BayerBG2BGR")) {iTransform = CV_BayerBG2BGR;} else if (!strcmp(sTransform, "CV_BayerGB2BGR")) {iTransform = CV_BayerGB2BGR;} else if (!strcmp(sTransform, "CV_BayerRG2BGR")) {iTransform = CV_BayerRG2BGR;} else if (!strcmp(sTransform, "CV_BayerGR2BGR")) {iTransform = CV_BayerGR2BGR;} else { iTransform = 0; // to avoid compiler warning NanThrowTypeError("Conversion code is unsupported"); } cv::cvtColor(self->mat, self->mat, iTransform); NanReturnUndefined(); } // @author SergeMv // arrChannels = img.split(); NAN_METHOD(Matrix::Split) { NanScope(); Matrix * self = ObjectWrap::Unwrap(args.This()); unsigned int size = self->mat.channels(); vector channels; // Split doesn't seem to work on empty vectors for (unsigned int i = 0; i < size; i++) { channels.push_back(cv::Mat()); } cv::split(self->mat, channels); size = channels.size(); v8::Local arrChannels = NanNew(size); for (unsigned int i = 0; i < size; i++) { Local matObject = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix * m = ObjectWrap::Unwrap(matObject); m->mat = channels[i]; arrChannels->Set(i, matObject); } NanReturnValue(arrChannels); } // @author SergeMv // img.merge(arrChannels); NAN_METHOD(Matrix::Merge) { NanScope(); Matrix * self = ObjectWrap::Unwrap(args.This()); if (!args[0]->IsArray()) { NanThrowTypeError("The argument must be an array"); } v8::Handle jsChannels = v8::Handle::Cast(args[0]); unsigned int L = jsChannels->Length(); vector vChannels(L); for (unsigned int i = 0; i < L; i++) { Matrix * matObject = ObjectWrap::Unwrap(jsChannels->Get(i)->ToObject()); vChannels[i] = matObject->mat; } cv::merge(vChannels, self->mat); NanReturnUndefined(); } // @author SergeMv // Equalizes histogram // img.equalizeHist() NAN_METHOD(Matrix::EqualizeHist) { NanScope(); Matrix * self = ObjectWrap::Unwrap(args.This()); cv::equalizeHist(self->mat, self->mat); NanReturnUndefined(); } NAN_METHOD(Matrix::FloodFill) { SETUP_FUNCTION(Matrix) // obj->Get(NanNew("x")) // int cv::floodFill(cv::InputOutputArray, cv::Point, cv::Scalar, cv::Rect*, cv::Scalar, cv::Scalar, int) /* mat.floodFill( {seedPoint: [1,1] , newColor: [255,0,0] , rect:[[0,2],[30,40]] , loDiff : [8,90,60], upDiff:[10,100,70] }); */ if (args.Length() < 1 || !args[0]->IsObject()) { // error } Local < Object > obj = args[0]->ToObject(); cv::Rect rect; int ret = cv::floodFill(self->mat, setPoint(obj->Get(NanNew("seedPoint"))->ToObject()), setColor(obj->Get(NanNew("newColor"))->ToObject()), obj->Get(NanNew("rect"))->IsUndefined() ? 0 : setRect(obj->Get(NanNew("rect"))->ToObject(), rect), setColor(obj->Get(NanNew("loDiff"))->ToObject()), setColor(obj->Get(NanNew("upDiff"))->ToObject()), 4); NanReturnValue(NanNew(ret)); } // @author olfox // Returns an array of the most probable positions // Usage: output = input.templateMatches(min_probability, max_probability, limit, ascending, min_x_distance, min_y_distance); NAN_METHOD(Matrix::TemplateMatches) { SETUP_FUNCTION(Matrix) bool filter_min_probability = (args.Length() >= 1) ? args[0]->IsNumber() : false; bool filter_max_probability = (args.Length() >= 2) ? args[1]->IsNumber() : false; double min_probability = filter_min_probability ? args[0]->NumberValue() : 0; double max_probability = filter_max_probability ? args[1]->NumberValue() : 0; int limit = (args.Length() >= 3) ? args[2]->IntegerValue() : 0; bool ascending = (args.Length() >= 4) ? args[3]->BooleanValue() : false; int min_x_distance = (args.Length() >= 5) ? args[4]->IntegerValue() : 0; int min_y_distance = (args.Length() >= 6) ? args[5]->IntegerValue() : 0; cv::Mat_ indices; if (ascending) { cv::sortIdx(self->mat.reshape(0, 1), indices, CV_SORT_ASCENDING + CV_SORT_EVERY_ROW); } else { cv::sortIdx(self->mat.reshape(0, 1), indices, CV_SORT_DESCENDING + CV_SORT_EVERY_ROW); } cv::Mat hit_mask = cv::Mat::zeros(self->mat.size(), CV_64F); v8::Local < v8::Array > probabilites_array = NanNew(limit); cv::Mat_::const_iterator begin = self->mat.begin(); cv::Mat_::const_iterator it = indices.begin(); cv::Mat_::const_iterator end = indices.end(); int index = 0; for (; (limit == 0 || index < limit) && it != end; ++it) { cv::Point pt = (begin + *it).pos(); float probability = self->mat.at(pt.y, pt.x); if (filter_min_probability && probability < min_probability) { if (ascending) { continue; } else { break; } } if (filter_max_probability && probability > max_probability) { if (ascending) break; else continue; } if (min_x_distance != 0 || min_y_distance != 0) { // Check hit mask color for for every corner cv::Size maxSize = hit_mask.size(); int max_x = maxSize.width - 1; int max_y = maxSize.height - 1; cv::Point top_left = cv::Point(max(0, pt.x - min_x_distance), max(0, pt.y - min_y_distance)); cv::Point top_right = cv::Point(min(max_x, pt.x + min_x_distance), max(0, pt.y - min_y_distance)); cv::Point bottom_left = cv::Point(max(0, pt.x - min_x_distance), min(max_y, pt.y + min_y_distance)); cv::Point bottom_right = cv::Point(min(max_x, pt.x + min_x_distance), min(max_y, pt.y + min_y_distance)); if (hit_mask.at(top_left.y, top_left.x) > 0) continue; if (hit_mask.at(top_right.y, top_right.x) > 0) continue; if (hit_mask.at(bottom_left.y, bottom_left.x) > 0) continue; if (hit_mask.at(bottom_right.y, bottom_right.x) > 0) continue; cv::Scalar color(255.0); cv::rectangle(hit_mask, top_left, bottom_right, color, CV_FILLED); } Local x_value = NanNew(pt.x); Local y_value = NanNew(pt.y); Local probability_value = NanNew(probability); Local < Object > probability_object = NanNew(); probability_object->Set(NanNew("x"), x_value); probability_object->Set(NanNew("y"), y_value); probability_object->Set(NanNew("probability"), probability_value); probabilites_array->Set(index, probability_object); index++; } NanReturnValue(probabilites_array); } // @author ytham // Match Template filter // Usage: output = input.matchTemplate("templateFileString", method); NAN_METHOD(Matrix::MatchTemplate) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); v8::String::Utf8Value args0(args[0]->ToString()); std::string filename = std::string(*args0); cv::Mat templ; templ = cv::imread(filename, CV_8S); Local out = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *m_out = ObjectWrap::Unwrap(out); int cols = self->mat.cols - templ.cols + 1; int rows = self->mat.rows - templ.rows + 1; m_out->mat.create(cols, rows, CV_32FC1); /* TM_SQDIFF =0 TM_SQDIFF_NORMED =1 TM_CCORR =2 TM_CCORR_NORMED =3 TM_CCOEFF =4 TM_CCOEFF_NORMED =5 */ int method = (args.Length() < 2) ? (int)cv::TM_CCORR_NORMED : args[1]->Uint32Value(); cv::matchTemplate(self->mat, templ, m_out->mat, method); NanReturnValue(out); } // @author ytham // Min/Max location NAN_METHOD(Matrix::MinMaxLoc) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); double minVal; double maxVal; cv::Point minLoc; cv::Point maxLoc; cv::minMaxLoc(self->mat, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat() ); Local v_minVal = NanNew(minVal); Local v_maxVal = NanNew(maxVal); Local v_minLoc_x = NanNew(minLoc.x); Local v_minLoc_y = NanNew(minLoc.y); Local v_maxLoc_x = NanNew(maxLoc.x); Local v_maxLoc_y = NanNew(maxLoc.y); Local o_minLoc = NanNew(); o_minLoc->Set(NanNew("x"), v_minLoc_x); o_minLoc->Set(NanNew("y"), v_minLoc_y); Local o_maxLoc = NanNew(); o_maxLoc->Set(NanNew("x"), v_maxLoc_x); o_maxLoc->Set(NanNew("y"), v_maxLoc_y); // Output result object Local result = NanNew(); result->Set(NanNew("minVal"), v_minVal); result->Set(NanNew("maxVal"), v_maxVal); result->Set(NanNew("minLoc"), o_minLoc); result->Set(NanNew("maxLoc"), o_maxLoc); NanReturnValue(result); } // @author ytham // Pushes some matrix (argument) the back of a matrix (self) NAN_METHOD(Matrix::PushBack) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); Matrix *m_input = ObjectWrap::Unwrap(args[0]->ToObject()); self->mat.push_back(m_input->mat); NanReturnValue(args.This()); } NAN_METHOD(Matrix::PutText) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); NanAsciiString textString(args[0]); //FIXME: might cause issues, see here https://github.com/rvagg/nan/pull/152 char *text = *textString;//(char *) malloc(textString.length() + 1); //strcpy(text, *textString); int x = args[1]->IntegerValue(); int y = args[2]->IntegerValue(); NanAsciiString fontString(args[3]); char *font = *fontString;//(char *) malloc(fontString.length() + 1); //strcpy(font, *fontString); int constFont = cv::FONT_HERSHEY_SIMPLEX; if (!strcmp(font, "HERSEY_SIMPLEX")) {constFont = cv::FONT_HERSHEY_SIMPLEX;} else if (!strcmp(font, "HERSEY_PLAIN")) {constFont = cv::FONT_HERSHEY_PLAIN;} else if (!strcmp(font, "HERSEY_DUPLEX")) {constFont = cv::FONT_HERSHEY_DUPLEX;} else if (!strcmp(font, "HERSEY_COMPLEX")) {constFont = cv::FONT_HERSHEY_COMPLEX;} else if (!strcmp(font, "HERSEY_TRIPLEX")) {constFont = cv::FONT_HERSHEY_TRIPLEX;} else if (!strcmp(font, "HERSEY_COMPLEX_SMALL")) {constFont = cv::FONT_HERSHEY_COMPLEX_SMALL;} else if (!strcmp(font, "HERSEY_SCRIPT_SIMPLEX")) {constFont = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;} else if (!strcmp(font, "HERSEY_SCRIPT_COMPLEX")) {constFont = cv::FONT_HERSHEY_SCRIPT_COMPLEX;} else if (!strcmp(font, "HERSEY_SCRIPT_SIMPLEX")) {constFont = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;} cv::Scalar color(0, 0, 255); if (args[4]->IsArray()) { Local objColor = args[4]->ToObject(); color = setColor(objColor); } double scale = args.Length() < 6 ? 1 : args[5]->NumberValue(); double thickness = args.Length() < 7 ? 1 : args[6]->NumberValue(); cv::putText(self->mat, text, cv::Point(x, y), constFont, scale, color, thickness); NanReturnUndefined(); } NAN_METHOD(Matrix::GetPerspectiveTransform) { NanScope(); // extract quad args Local srcArray = args[0]->ToObject(); Local tgtArray = args[1]->ToObject(); std::vector src_corners(4); std::vector tgt_corners(4); for (unsigned int i = 0; i < 4; i++) { src_corners[i] = cvPoint(srcArray->Get(i*2)->IntegerValue(),srcArray->Get(i*2+1)->IntegerValue()); tgt_corners[i] = cvPoint(tgtArray->Get(i*2)->IntegerValue(),tgtArray->Get(i*2+1)->IntegerValue()); } Local xfrm = NanNew(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *xfrmmat = ObjectWrap::Unwrap(xfrm); xfrmmat->mat = cv::getPerspectiveTransform(src_corners, tgt_corners); NanReturnValue(xfrm); } NAN_METHOD(Matrix::WarpPerspective) { SETUP_FUNCTION(Matrix) Matrix *xfrm = ObjectWrap::Unwrap(args[0]->ToObject()); int width = args[1]->IntegerValue(); int height = args[2]->IntegerValue(); int flags = cv::INTER_LINEAR; int borderMode = cv::BORDER_REPLICATE; cv::Scalar borderColor(0, 0, 255); if (args[3]->IsArray()) { Local < Object > objColor = args[3]->ToObject(); borderColor = setColor(objColor); } cv::Mat res = cv::Mat(width, height, CV_32FC3); cv::warpPerspective(self->mat, res, xfrm->mat, cv::Size(width, height), flags, borderMode, borderColor); ~self->mat; self->mat = res; NanReturnNull(); } NAN_METHOD(Matrix::CopyWithMask) { SETUP_FUNCTION(Matrix) // param 0 - destination image: Matrix *dest = ObjectWrap::Unwrap(args[0]->ToObject()); // param 1 - mask. same size as src and dest Matrix *mask = ObjectWrap::Unwrap(args[1]->ToObject()); self->mat.copyTo(dest->mat, mask->mat); NanReturnUndefined(); } NAN_METHOD(Matrix::SetWithMask) { SETUP_FUNCTION(Matrix) // param 0 - target value: Local < Object > valArray = args[0]->ToObject(); cv::Scalar newvals; newvals.val[0] = valArray->Get(0)->NumberValue(); newvals.val[1] = valArray->Get(1)->NumberValue(); newvals.val[2] = valArray->Get(2)->NumberValue(); // param 1 - mask. same size as src and dest Matrix *mask = ObjectWrap::Unwrap(args[1]->ToObject()); self->mat.setTo(newvals, mask->mat); NanReturnUndefined(); } NAN_METHOD(Matrix::MeanWithMask) { SETUP_FUNCTION(Matrix) // param 0 - mask. same size as src and dest Matrix *mask = ObjectWrap::Unwrap(args[0]->ToObject()); cv::Scalar means = cv::mean(self->mat, mask->mat); v8::Local < v8::Array > arr = NanNew(3); arr->Set(0, NanNew(means[0])); arr->Set(1, NanNew(means[1])); arr->Set(2, NanNew(means[2])); NanReturnValue(arr); } NAN_METHOD(Matrix::Shift) { SETUP_FUNCTION(Matrix) cv::Mat res; double tx = args[0]->NumberValue(); double ty = args[1]->NumberValue(); // get the integer values of args cv::Point2i deltai(ceil(tx), ceil(ty)); int fill = cv::BORDER_REPLICATE; cv::Scalar value = cv::Scalar(0, 0, 0, 0); // INTEGER SHIFT // first create a border around the parts of the Mat that will be exposed int t = 0, b = 0, l = 0, r = 0; if (deltai.x > 0) { l = deltai.x; } if (deltai.x < 0) { r = -deltai.x; } if (deltai.y > 0) { t = deltai.y; } if (deltai.y < 0) { b = -deltai.y; } cv::Mat padded; cv::copyMakeBorder(self->mat, padded, t, b, l, r, fill, value); // construct the region of interest around the new matrix cv::Rect roi = cv::Rect(std::max(-deltai.x, 0), std::max(-deltai.y, 0), 0, 0) + self->mat.size(); res = padded(roi); ~self->mat; self->mat = res; NanReturnUndefined(); } NAN_METHOD(Matrix::Release) { NanScope(); Matrix *self = ObjectWrap::Unwrap(args.This()); self->mat.release(); NanReturnUndefined(); }