diff --git a/src/Matrix.cc b/src/Matrix.cc index 8cbc363..a708453 100755 --- a/src/Matrix.cc +++ b/src/Matrix.cc @@ -47,6 +47,7 @@ Matrix::Init(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor, "saveAsync", SaveAsync); NODE_SET_PROTOTYPE_METHOD(constructor, "resize", Resize); NODE_SET_PROTOTYPE_METHOD(constructor, "rotate", Rotate); + NODE_SET_PROTOTYPE_METHOD(constructor, "copyTo", CopyTo); NODE_SET_PROTOTYPE_METHOD(constructor, "pyrDown", PyrDown); NODE_SET_PROTOTYPE_METHOD(constructor, "pyrUp", PyrUp); NODE_SET_PROTOTYPE_METHOD(constructor, "channels", Channels); @@ -62,7 +63,7 @@ Matrix::Init(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor, "addWeighted", AddWeighted); NODE_SET_PROTOTYPE_METHOD(constructor, "bitwiseXor", BitwiseXor); NODE_SET_PROTOTYPE_METHOD(constructor, "countNonZero", CountNonZero); - NODE_SET_PROTOTYPE_METHOD(constructor, "split", Split); + //NODE_SET_PROTOTYPE_METHOD(constructor, "split", Split); NODE_SET_PROTOTYPE_METHOD(constructor, "canny", Canny); NODE_SET_PROTOTYPE_METHOD(constructor, "dilate", Dilate); NODE_SET_PROTOTYPE_METHOD(constructor, "erode", Erode); @@ -80,6 +81,11 @@ Matrix::Init(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor, "threshold", Threshold); NODE_SET_PROTOTYPE_METHOD(constructor, "meanStdDev", MeanStdDev); + + NODE_SET_PROTOTYPE_METHOD(constructor, "cvtColor", CvtColor); + NODE_SET_PROTOTYPE_METHOD(constructor, "split", Split); + NODE_SET_PROTOTYPE_METHOD(constructor, "merge", Merge); + NODE_SET_PROTOTYPE_METHOD(constructor, "equalizeHist", EqualizeHist); NODE_SET_METHOD(constructor, "Eye", Eye); @@ -128,6 +134,7 @@ Matrix::Matrix(cv::Mat m, cv::Rect roi): ObjectWrap() { mat = cv::Mat(m, roi); } + Handle Matrix::Empty(const Arguments& args){ SETUP_FUNCTION(Matrix) @@ -333,14 +340,47 @@ Handle Matrix::ToBuffer(const v8::Arguments& args){ SETUP_FUNCTION(Matrix) - if (args.Length() > 0){ - return Matrix::ToBufferAsync(args); - } + if ((args.Length() > 0) && (args[args.Length() - 1]->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::vector params; + // See if the options argument is passed + if ((args.Length() > 0) && (args[0]->IsObject())) { + // Get this options argument + v8::Handle options = v8::Handle::Cast(args[0]); + // If the extension (image format) is provided + if (options->Has(v8::String::New("ext"))) { + v8::String::Utf8Value str ( options->Get(v8::String::New("ext"))->ToString() ); + std::string str2 = std::string(*str); + ext = (const char *) str2.c_str(); + } + if (options->Has(v8::String::New("jpegQuality"))) { + int compression = options->Get(v8::String::New("jpegQuality"))->IntegerValue(); + params.push_back(CV_IMWRITE_JPEG_QUALITY); + params.push_back(compression); + } + if (options->Has(v8::String::New("pngCompression"))) { + int compression = options->Get(v8::String::New("pngCompression"))->IntegerValue(); + params.push_back(CV_IMWRITE_PNG_COMPRESSION); + params.push_back(compression); + } + } + //--------------------------- std::vector vec(0); - std::vector params(0);//CV_IMWRITE_JPEG_QUALITY 90 - cv::imencode(".jpg", self->mat, vec, params); + // We use operator * before the "ext" variable, because it converts v8::String::AsciiValue to char * + cv::imencode(ext, self->mat, vec, params); node::Buffer *buf = node::Buffer::New(vec.size()); uchar* data = (uchar*) Buffer::Data(buf); @@ -827,14 +867,14 @@ Matrix::CountNonZero(const v8::Arguments& args) { return scope.Close(v8::Number::New(count)); } -Handle +/*Handle Matrix::Split(const v8::Arguments& args) { HandleScope scope; //Matrix *self = ObjectWrap::Unwrap(args.This()); return scope.Close(v8::Null()); -} +}*/ Handle @@ -1008,10 +1048,18 @@ Matrix::Resize(const v8::Arguments& args){ 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, cv::INTER_LINEAR); + cv::resize(self->mat, res, cv::Size(x, y), 0, 0, interpolation); ~self->mat; self->mat = res; @@ -1029,11 +1077,39 @@ Matrix::Rotate(const v8::Arguments& args){ 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) { return scope.Close(Undefined()); } + 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); + + return scope.Close(Undefined()); + } + //------------- + 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()); @@ -1175,3 +1251,150 @@ Matrix::MeanStdDev(const v8::Arguments& args) { data->Set(String::NewSymbol("stddev"), stddev); return scope.Close(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 +Handle +Matrix::CopyTo(const v8::Arguments& args) { + HandleScope scope; + + 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); + + return scope.Close(Undefined()); +} + + + +// @author SergeMv +// Does in-place color transformation +// img.cvtColor('CV_BGR2YCrCb'); +Handle +Matrix::CvtColor(const v8::Arguments& args) { + HandleScope scope; + + 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 + return v8::ThrowException(Exception::TypeError(String::New( + "Conversion code is unsupported"))); + } + + cv::cvtColor(self->mat, self->mat, iTransform); + + return scope.Close(Undefined()); +} + + +// @author SergeMv +// arrChannels = img.split(); +Handle +Matrix::Split(const v8::Arguments& args) { + HandleScope scope; + + Matrix * self = ObjectWrap::Unwrap(args.This()); + + vector channels; + cv::split(self->mat, channels); + unsigned int size = channels.size(); + v8::Local arrChannels = v8::Array::New(size); + for (unsigned int i = 0; i < size; i++) { + Local matObject = Matrix::constructor->GetFunction()->NewInstance(); + Matrix * m = ObjectWrap::Unwrap(matObject); + m->mat = channels[i]; + arrChannels->Set(i, matObject); + } + + return scope.Close(arrChannels); +} + + +// @author SergeMv +// img.merge(arrChannels); +Handle +Matrix::Merge(const v8::Arguments& args) { + HandleScope scope; + + Matrix * self = ObjectWrap::Unwrap(args.This()); + + if (!args[0]->IsArray()) { + return v8::ThrowException(Exception::TypeError(String::New( + "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); + + return scope.Close(Undefined()); +} + + +// @author SergeMv +// Equalizes histogram +// img.equalizeHist() +Handle +Matrix::EqualizeHist(const v8::Arguments& args) { + HandleScope scope; + + Matrix * self = ObjectWrap::Unwrap(args.This()); + + cv::equalizeHist(self->mat, self->mat); + + return scope.Close(Undefined()); +} diff --git a/src/Matrix.h b/src/Matrix.h index 3dcb691..a21ed0e 100755 --- a/src/Matrix.h +++ b/src/Matrix.h @@ -29,7 +29,6 @@ class Matrix: public node::ObjectWrap { JSFUNC(Height) JSFUNC(Channels) JSFUNC(Clone) - JSFUNC(Ellipse) JSFUNC(Rectangle) JSFUNC(Line) @@ -57,7 +56,7 @@ class Matrix: public node::ObjectWrap { JSFUNC(AddWeighted) JSFUNC(BitwiseXor) JSFUNC(CountNonZero) - JSFUNC(Split) + //JSFUNC(Split) JSFUNC(Canny) JSFUNC(Dilate) JSFUNC(Erode) @@ -77,6 +76,13 @@ class Matrix: public node::ObjectWrap { JSFUNC(Threshold) JSFUNC(MeanStdDev) + + JSFUNC(CopyTo) + JSFUNC(CvtColor) + JSFUNC(Split) + JSFUNC(Merge) + JSFUNC(EqualizeHist) + /* static Handle Val(const Arguments& args); static Handle RowRange(const Arguments& args);