diff --git a/.gitignore b/.gitignore index ef08044..382b1ae 100755 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ out*.png examples/*.avi examples/tmp/* vagrant/.vagrant +coverage/ diff --git a/.travis.yml b/.travis.yml index db65e84..dbd779e 100755 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,8 @@ before_install: - sudo add-apt-repository -y ppa:kubuntu-ppa/backports - sudo apt-get update - sudo apt-get install --force-yes --yes libcv-dev libcvaux-dev libhighgui-dev libopencv-dev + # for code coverage + - sudo apt-get install lcov # get commit message - COMMIT_MESSAGE=$(git show -s --format=%B $TRAVIS_COMMIT | tr -d '\n') # put local node-pre-gyp on PATH @@ -49,11 +51,11 @@ before_install: - platform=$(uname -s | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/") install: - # ensure source install works - - npm install --build-from-source - # test our module - - npm test - - node lib/opencv.js + # install dependencies first + - npm install + # build from source, run test and generate code coverage + - make cover + - NODE_OPENCV_DEBUG=true node lib/opencv.js - docker build -t peterbraden/node-opencv-ubuntu-12-04 -f test/Dockerfile-ubuntu-12-04 . - docker build -t peterbraden/node-opencv-ubuntu-14-04 -f test/Dockerfile-ubuntu-14-04 . @@ -81,3 +83,5 @@ script: after_success: # if success then query and display all published binaries - node-pre-gyp info + # Upload coverage to codecov + - bash <(curl -s https://codecov.io/bash) -s coverage -f *.info diff --git a/CHANGELOG.md b/CHANGELOG.md index 699a4e6..0707b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,45 @@ # Changelog + +## 6.0.0 + +# Enhancements +- @wenq added `contour.moments` method. +- @andreasgal added `matrix.substract` method. +- @jainanshul added `matrix.mean` method. +- @idubinskiy restored `contour.points` method. +- @danschultzer updated node-pre-gyp to fix load of node-opencv in electron runtime. +- @andreasgal made `matrix.getData` work with RGB images. +- @Evilcat325 added `matrix.MatchTemplateByMatrix` method. +- @danschultzer added code coverage. + +# Bug fixes +- @dominikdolancic fixed image load issue in `matrix.matchTemplate()`. +- @AwooOOoo fixed `type_info` errors in Visual Studio with std namespace pollution. +- @mvines fixed issue that prevented `AsyncSaveWorker` from using de-allocated memory. +- @mcwhittemore fixed dissimilarity example image load. +- @saoron fixed dead index.html documentup source. +- @andreasgal fixed an issue with `matrix.crop` (and potentially others), where `matrix.getData` ends up returning less than full matrix. +- @danschultzer fixed `examples/test.js` channel issue, and problematic Vec3b casting (instead of Vec3f) in `matrix.set`. + +## Backwards incompatible changes +- @dxprog changed readImage to load image with `CV_LOAD_IMAGE_UNCHANGED` instead of `CV_LOAD_IMAGE_COLOR`. The latter returned the image as 3-channel. +- @danschultzer changed `VideoCapture.close` to `VideoCapture.release`. + +Thanks to all, also a massive thanks to @danschultzer for helping get the open +tickets and PR's under control. + +## 5.0.0 (Feb 9 2016) + +- @mvines and @svogl started working on OpenCV 3.x support. +- @sirotenko added a getFrameCount method +- @vaceta implemented getFrameAt +- @jainanshul improved some methods +- @cascade256 improved the windows build + +Plus fixes from @banterability, @punnerud, @vargad etc. Thanks all. + ## 4.0.0 I've been super slow releasing this one, and there's a ton of new stuff. diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 4370a91..f9a995d 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -68,3 +68,4 @@ Ordered by date of first contribution. [Auto-generated](https://github.com/xingr - [vyacheslav](https://github.com/vyacheslav-lonschakov) - vyacheslav - [Harold Ozouf](https://github.com/jspdown) +- [Dan Schultzer](https://github.com/danschultzer) diff --git a/Dockerfile b/Dockerfile index 7e4334e..f1ef509 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,16 +4,80 @@ # 2) Build: wget https://raw.github.com/dotcloud/docker/v0.1.6/contrib/docker-build/docker-build && python docker-build $USER/node-opencv < Dockerfile # 3) Test: docker run $USER/node-opencv node -e "console.log(require('opencv').version)" # -# VERSION 0.2 +# VERSION 0.3 # DOCKER-VERSION 8.1.2 + # update to 14.04 -from ubuntu:14.04 -run apt-get update -qq -run apt-get install -y software-properties-common python-software-properties -run add-apt-repository -y ppa:kubuntu-ppa/backports -run apt-get update -run apt-get install -y libcv-dev libcvaux-dev libhighgui-dev libopencv-dev -run curl -sL https://deb.nodesource.com/setup | bash - -run apt-get install -y nodejs -run npm install opencv || cat npm-debug.log +FROM ubuntu:14.04 + +# listing myself as maintainer of _this_ Dockerfile, though I am not the author of the install script (credit to http://rodrigoberriel.com/) +MAINTAINER borromeotlhs@gmail.com + +# run Rodrigo Berriel’s script for installing opencv3 on Ubuntu 14.04 +# I’ll convert this into a full Dockerfile later, once I see if it works +#COPY ./install-opencv3.sh /tmp/ + +# this is needed as libfaac-dev comes from multiverse, according to: +# http://superuser.com/questions/467774/how-to-install-libfaac-dev +RUN echo "deb http://us.archive.ubuntu.com/ubuntu/ precise multiverse\n\ +deb-src http://us.archive.ubuntu.com/ubuntu/ precise multiverse\n\ +deb http://us.archive.ubuntu.com/ubuntu/ precise-updates multiverse\n\ +deb-src http://us.archive.ubuntu.com/ubuntu/ precise-updates multiverse\n"\ +>> /etc/apt/sources.list + + +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \ + software-properties-common +RUN add-apt-repository ppa:george-edison55/cmake-3.x +RUN apt-get update -qq +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \ +curl \ +cmake \ +wget \ +unzip \ +libopencv-dev \ +build-essential \ +git \ +libgtk2.0-dev \ +pkg-config \ +python-dev \ +python-numpy \ +libdc1394-22 \ +libdc1394-22-dev \ +libjpeg-dev \ +libpng12-dev \ +libtiff4-dev \ +libjasper-dev \ +libavcodec-dev \ +libavformat-dev \ +libswscale-dev \ +libxine-dev \ +libgstreamer0.10-dev \ +libgstreamer-plugins-base0.10-dev \ +libv4l-dev \ +libtbb-dev \ +libqt4-dev \ +libfaac-dev \ +libmp3lame-dev \ +libopencore-amrnb-dev \ +libopencore-amrwb-dev \ +libtheora-dev \ +libvorbis-dev \ +libxvidcore-dev \ +x264 \ +v4l-utils + +RUN mkdir opencv +WORKDIR opencv + +RUN wget https://github.com/Itseez/opencv/archive/3.0.0.zip -O opencv-3.0.0.zip +RUN unzip opencv-3.0.0.zip +RUN mkdir opencv-3.0.0/build +WORKDIR opencv-3.0.0/build + +RUN cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D WITH_OPENGL=ON .. + +RUN make -j $(nproc) && make install +RUN echo "/usr/local/lib" > /etc/ld.so.conf.d/opencv.conf +RUN ldconfig diff --git a/Makefile b/Makefile index 9878379..9a6db0a 100644 --- a/Makefile +++ b/Makefile @@ -39,3 +39,63 @@ travis-build: docker build -t peterbraden/node-opencv-ubuntu-12-04 -f test/Dockerfile-ubuntu-12-04 . docker build -t peterbraden/node-opencv-ubuntu-14-04 -f test/Dockerfile-ubuntu-14-04 . .PHONY: travis-build + + +# Below build, coverage and clean tasks were partly lifted from https://github.com/geo-data/node-mapserv/blob/e99b23a44d910d444f5a45d144859758f820e1d1/Makefile +# @author danschultzer + +# The location of the `istanbul` JS code coverage framework. Try and get a +# globally installed version, falling back to a local install. +ISTANBUL := $(shell which istanbul) +ifeq ($(ISTANBUL),) + ISTANBUL = ./node_modules/.bin/istanbul/lib/cli.js +endif + +# The location of the `node-pre-gyp` module builder. Try and get a globally +# installed version, falling back to a local install. +NODE_PRE_GYP = $(shell which node-pre-gyp) +ifeq ($(NODE_GYP),) + NODE_PRE_GYP = ./node_modules/.bin/node-pre-gyp +endif + +NODE := $(shell which node) +test_deps = build \ +./test/*.js \ +./lib/*.js \ +$(NODE) + +build: build/Debug/opencv.node +build/Debug/opencv.node: + $(NODE_PRE_GYP) --verbose --debug rebuild + +# Perform the code coverage +cover: coverage/index.html +coverage/index.html: coverage/node-opencv.info + genhtml --output-directory coverage coverage/node-opencv.info + @echo "\033[0;32mPoint your browser at \`coverage/index.html\`\033[m\017" +coverage/node-opencv.info: coverage/bindings.info + lcov --test-name node-opencv \ + --add-tracefile coverage/lcov.info \ + --add-tracefile coverage/bindings.info \ + --output-file coverage/node-opencv.info +coverage/bindings.info: coverage/addon.info + lcov --extract coverage/addon.info '*opencv/src/*' --output-file coverage/bindings.info +coverage/addon.info: coverage/lcov.info + lcov --capture --base-directory build/ --directory . --output-file coverage/addon.info +# This generates the JS lcov info as well as gcov `*.gcda` files: +coverage/lcov.info: $(test_deps) $(ISTANBUL) + NODE_OPENCV_DEBUG=true $(NODE) --nouse_idle_notification --expose-gc \ + $(ISTANBUL) cover --report lcovonly -- test/unit.js + +$(NODE_PRE_GYP): + npm install node-pre-gyp + +$(ISTANBUL): package.json + npm install istanbul + @touch $(ISTANBUL) + +# Clean up any generated files +clean: $(NODE_PRE_GYP) + $(NODE_PRE_GYP) clean + rm -rf coverage + rm -rf build diff --git a/README.md b/README.md index a967eae..22b49aa 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# node-opencv +# node-opencv [![Build Status](https://secure.travis-ci.org/peterbraden/node-opencv.png)](http://travis-ci.org/peterbraden/node-opencv) - +[![Coverage](http://codecov.io/github/peterbraden/node-opencv/coverage.svg?branch=master)](https://codecov.io/gh/peterbraden/node-opencv) [OpenCV](http://opencv.org) bindings for Node.js. OpenCV is the defacto computer vision library - by interfacing with it natively in node, @@ -13,44 +13,31 @@ cool, I'd love to hear about it! ## Install -You'll need OpenCV 2.3.1 or newer installed before installing node-opencv. +You'll need OpenCV 2.3.1 or newer installed before installing node-opencv. Note +that OpenCV 3.x is not yet fully supported. ## Specific for Windows -1. Download Install opencv @ - (I used version 2.4.4) +1. Download and install OpenCV (Be sure to use a 2.4 version) @ http://opencv.org/downloads.html -Put it in c:\opencv +For these instructions we will assume OpenCV is put at C:\OpenCV, but you can +adjust accordingly. -2. Install python version 2.7 @ -http://www.python.org/download/releases/2.7/ -put it in c:\python27 +2. If you haven't already, create a system variable called OPENCV_DIR and set it + to C:\OpenCV\build\x64\vc12 -3. install pkg-config by downloading the all in one bundle @ - (I used Gtk+ 3.6.4) -http://www.gtk.org/download/win64.php -put it in c:\pkg-config + Make sure the "x64" part matches the version of NodeJS you are using. -4. Add the following to your path variables -C:\pkg-config\bin;C:\OpenCV\build\x64\vc11\bin; + Also add the following to your system PATH + ;%OPENCV_DIR%\bin -5. Install visual-studio in 4 steps +3. Install Visual Studio 2013. Make sure to get the C++ components. + You can use a different edition, just make sure OpenCV supports it, and you + set the "vcxx" part of the variables above to match. - - install Visual C++ 2010 Express - - - install Windows SDK for windows 7 and .net framework 4 - - - install Visual Studio 2010 Service Pack 1 - - - install Visual C++ 2010 Service Pack 1 Compiler - - -6. Download npeterbraden/node-opencv fork +4. Download peterbraden/node-opencv fork git clone https://github.com/peterbraden/node-opencv -7. edit file src/Matrix.cpp -put "inline double round( double d ) { return floor( d + 0.5);}" below "cv::Rect* setRect(Local objRect, cv::Rect &result);" - -8. run npm install - -Then: +5. run npm install ```bash $ npm install opencv @@ -79,7 +66,7 @@ cv.readImage("./examples/files/mona.png", function(err, im){ ### Matrix The [matrix](http://opencv.jp/opencv-2svn_org/cpp/core_basic_structures.html#mat) is the most useful -base datastructure in OpenCV. Things like images are just matrices of pixels. +base data structure in OpenCV. Things like images are just matrices of pixels. #### Creation @@ -175,8 +162,8 @@ im.line([x1,y1], [x2, y2]) #### Object Detection -There is a shortcut method for -[Viola-Jones Haar Cascade](http://www.cognotics.com/opencv/servo_2007_series/part_2/sidebar.html) object +There is a shortcut method for +[Viola-Jones Haar Cascade](http://docs.opencv.org/trunk/d7/d8b/tutorial_py_face_detection.html) object detection. This can be used for face detection etc. ```javascript @@ -206,7 +193,7 @@ functions for accessing, computing with, and altering the contours contained in See [relevant source code](src/Contours.cc) and [examples](examples/) ```javascript -var contours = im.findContours; +var contours = im.findContours(); // Count of contours in the Contours object contours.size(); @@ -236,7 +223,20 @@ contours.approxPolyDP(index, epsilon, isClosed); contours.convexHull(index, clockwise); ``` +## Test + +Using [tape](https://github.com/substack/tape). Run with command: + +`npm test`. + +## Code coverage + +Using [istanbul](http://gotwarlost.github.io/istanbul/) and [lcov](http://ltp.sourceforge.net/coverage/lcov.php). Run with command: + +`make cover` + +Build version of `opencv.node` will be generated, and coverage files will be put in `coverage/` directory. These files can be remvoved automatically by running `make clean`. + ## MIT License The library is distributed under the MIT License - if for some reason that doesn't work for you please get in touch. - diff --git a/binding.gyp b/binding.gyp index 8cd66bf..69b4d12 100755 --- a/binding.gyp +++ b/binding.gyp @@ -24,30 +24,113 @@ ], "libraries": [ - "= 2.3.1\" )", + "= 2.4.9\" )", "-Wall" ], + "defines": [ + "WIN" + ], "msvs_settings": { "VCCLCompilerTool": { "ExceptionHandling": "2", @@ -60,10 +143,10 @@ "xcode_settings": { "OTHER_CFLAGS": [ "-mmacosx-version-min=10.7", - "-std=c++11", - "-stdlib=libc++", - " maxArea) { var moments = contours.moments(i); var cgx = Math.round(moments.m10 / moments.m00); var cgy = Math.round(moments.m01 / moments.m00); - big.drawContour(contours, i, GREEN); + big.drawContour(contours, i, GREEN, thickness, lineType, maxLevel, [0, 0]); big.line([cgx - 5, cgy], [cgx + 5, cgy], RED); big.line([cgx, cgy - 5], [cgx, cgy + 5], RED); } diff --git a/examples/dissimilarity.js b/examples/dissimilarity.js index 15e5e52..7c94989 100644 --- a/examples/dissimilarity.js +++ b/examples/dissimilarity.js @@ -1,9 +1,14 @@ var cv = require('../lib/opencv'); -cv.readImage("./examples/files/car1.jpg", function(err, car1) { +if (cv.ImageSimilarity === undefined) { + console.log('TODO: Please port Features2d.cc to OpenCV 3') + process.exit(0); +} + +cv.readImage("./files/car1.jpg", function(err, car1) { if (err) throw err; - cv.readImage("./examples/files/car2.jpg", function(err, car2) { + cv.readImage("./files/car2.jpg", function(err, car2) { if (err) throw err; cv.ImageSimilarity(car1, car2, function (err, dissimilarity) { diff --git a/examples/files/alpha-test.png b/examples/files/alpha-test.png new file mode 100644 index 0000000..f5a64a7 Binary files /dev/null and b/examples/files/alpha-test.png differ diff --git a/examples/files/car1_template.jpg b/examples/files/car1_template.jpg new file mode 100644 index 0000000..c4fdf1c Binary files /dev/null and b/examples/files/car1_template.jpg differ diff --git a/examples/files/distanceTransform.png b/examples/files/distanceTransform.png new file mode 100644 index 0000000..c1072d5 Binary files /dev/null and b/examples/files/distanceTransform.png differ diff --git a/examples/files/multipage.tif b/examples/files/multipage.tif new file mode 100644 index 0000000..17d286c Binary files /dev/null and b/examples/files/multipage.tif differ diff --git a/examples/files/opencv.png b/examples/files/opencv.png new file mode 100755 index 0000000..521a0dc Binary files /dev/null and b/examples/files/opencv.png differ diff --git a/examples/files/over_text.png b/examples/files/over_text.png index 1e21d42..06a3819 100755 Binary files a/examples/files/over_text.png and b/examples/files/over_text.png differ diff --git a/examples/mat-dct.js b/examples/mat-dct.js new file mode 100644 index 0000000..b3fa89f --- /dev/null +++ b/examples/mat-dct.js @@ -0,0 +1,22 @@ +var cv = require('../lib/opencv'); + +cv.readImage("./files/mona.png", function(err, orig) { + if (err) throw err; + + var chan1 = orig.split()[0]; + + var floatInputMatrix = new cv.Matrix(); + chan1.convertTo(floatInputMatrix, cv.Constants.CV_32F) + + var foatAfterDct = floatInputMatrix.dct(); + + // for inverse dct, set argument to "true" + var afterDoubleDct = foatAfterDct.dct(true); + + var intOutImage = new cv.Matrix(); + + afterDoubleDct.convertTo(intOutImage, cv.Constants.CV_8U); + intOutImage.save("./tmp/dct.png"); + console.log('Image saved to ./tmp/dct.png'); + +}); diff --git a/examples/test.js b/examples/test.js index 5513781..83afa48 100644 --- a/examples/test.js +++ b/examples/test.js @@ -1,6 +1,5 @@ var cv = require('../lib/opencv'); -var mat = new cv.Matrix(1, 2, cv.Constants.CV_8U, [1]); +var mat = new cv.Matrix(1, 2, cv.Constants.CV_8UC3, [1]); var row = mat.pixelRow(0); console.log("mat: " + row[0] + row[1]); - diff --git a/inc/Matrix.h b/inc/Matrix.h new file mode 100755 index 0000000..beed998 --- /dev/null +++ b/inc/Matrix.h @@ -0,0 +1,28 @@ +/* + This file defines the public native interface into an node-opencv Matrix + object. This is used to retrieve the wrapped OpenCV cv:Mat object from other + native code: + + NAN_METHOD(UnwrapMatrix) { + cv::Mat mat = Nan::ObjectWrap::Unwrap(info[0]->ToObject())->mat; + // ... + } + + */ +#ifndef NODE_OPENCV_MATRIX_H +#define NODE_OPENCV_MATRIX_H +#include +#include + +namespace node_opencv { + +class Matrix: public Nan::ObjectWrap { +public: + cv::Mat mat; +protected: + Matrix(): Nan::ObjectWrap() {}; +}; + +} + +#endif // NODE_OPENCV_MATRIX_H diff --git a/include_dirs.js b/include_dirs.js new file mode 100644 index 0000000..8d72392 --- /dev/null +++ b/include_dirs.js @@ -0,0 +1,14 @@ +// Outputs the path to public node-opencv header files. This is used when +// building other native node modules that require access to the +// node-opencv Matrix base class. +// +// To use this file, add something like the following to your binding.gyp: +// +// "include_dirs": [ +// " - + diff --git a/lib/bindings.js b/lib/bindings.js index 4acd6c2..62e551b 100755 --- a/lib/bindings.js +++ b/lib/bindings.js @@ -1,6 +1,6 @@ var binary = require('node-pre-gyp'); var path = require('path'); -var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); +var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')), { debug: !!process.env.NODE_OPENCV_DEBUG }); var binding = require(binding_path); //module.exports = require('../build/Release/opencv.node'); diff --git a/package.json b/package.json index cd735a3..d0d159a 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "opencv", - "version": "4.0.0", + "version": "6.0.0", "description": "Node Bindings to OpenCV", "author": "Peter Braden ", "dependencies": { "buffers": "^0.1.1", + "istanbul": "0.4.5", "nan": "^2.0.9", - "node-pre-gyp": "^0.6.11" + "node-pre-gyp": "^0.6.30" }, "devDependencies": { "tape": "^3.0.0", diff --git a/src/BackgroundSubtractor.cc b/src/BackgroundSubtractor.cc index fdbbaed..fb33fe2 100644 --- a/src/BackgroundSubtractor.cc +++ b/src/BackgroundSubtractor.cc @@ -3,7 +3,11 @@ #include #include -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4)) +#if CV_MAJOR_VERSION >= 3 +#warning TODO: port me to OpenCV 3 +#endif + +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4)) Nan::Persistent BackgroundSubtractorWrap::constructor; diff --git a/src/BackgroundSubtractor.h b/src/BackgroundSubtractor.h index 551d212..d661e33 100644 --- a/src/BackgroundSubtractor.h +++ b/src/BackgroundSubtractor.h @@ -1,6 +1,6 @@ #include "OpenCV.h" -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4)) +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4)) #include diff --git a/src/Calib3D.h b/src/Calib3D.h index 7d94943..a1846c8 100644 --- a/src/Calib3D.h +++ b/src/Calib3D.h @@ -3,6 +3,10 @@ #include "OpenCV.h" +#if CV_MAJOR_VERSION >= 3 +#include +#endif + /** * Implementation of calib3d.hpp functions */ diff --git a/src/CamShift.cc b/src/CamShift.cc index fed246a..bcf00ce 100644 --- a/src/CamShift.cc +++ b/src/CamShift.cc @@ -2,6 +2,9 @@ #include "OpenCV.h" #include "Matrix.h" +#if CV_MAJOR_VERSION >= 3 +#include +#endif #define CHANNEL_HUE 0 #define CHANNEL_SATURATION 1 diff --git a/src/CascadeClassifierWrap.h b/src/CascadeClassifierWrap.h index 0f80ba1..d21c370 100755 --- a/src/CascadeClassifierWrap.h +++ b/src/CascadeClassifierWrap.h @@ -1,4 +1,7 @@ #include "OpenCV.h" +#if CV_MAJOR_VERSION >= 3 +#include +#endif class CascadeClassifierWrap: public Nan::ObjectWrap { public: diff --git a/src/Constants.cc b/src/Constants.cc index 4b7f578..d6f0ff1 100644 --- a/src/Constants.cc +++ b/src/Constants.cc @@ -4,6 +4,9 @@ #define CONST(C) \ obj->Set(Nan::New(#C).ToLocalChecked(), Nan::New(C)); +#define CONST_INT(C) \ + obj->Set(Nan::New(#C).ToLocalChecked(), Nan::New((int)C)); + #define CONST_DOUBLE(C) \ obj->Set(Nan::New(#C).ToLocalChecked(), Nan::New(C)); @@ -62,6 +65,13 @@ void Constants::Init(Local target) { CONST_DOUBLE(CV_PI); CONST(CV_FILLED); + CONST_ENUM(BORDER_DEFAULT); + CONST_ENUM(BORDER_REPLICATE); + CONST_ENUM(BORDER_REFLECT); + CONST_ENUM(BORDER_REFLECT_101); + CONST_ENUM(BORDER_WRAP); + CONST_ENUM(BORDER_CONSTANT); + CONST_ENUM(INTER_NEAREST); CONST_ENUM(INTER_LINEAR); CONST_ENUM(INTER_AREA); @@ -78,6 +88,19 @@ void Constants::Init(Local target) { CONST_ENUM(NORM_RELATIVE); CONST_ENUM(NORM_TYPE_MASK); + CONST_ENUM(RETR_EXTERNAL); + CONST_ENUM(RETR_LIST); + CONST_ENUM(RETR_CCOMP); + CONST_ENUM(RETR_TREE); + + CONST_INT(CV_DIST_C); + CONST_INT(CV_DIST_L1); + CONST_INT(CV_DIST_L2); + + CONST_INT(CV_DIST_MASK_3); + CONST_INT(CV_DIST_MASK_5); + CONST_INT(CV_DIST_MASK_PRECISE); + target->Set(Nan::New("Constants").ToLocalChecked(), obj); } diff --git a/src/Contours.cc b/src/Contours.cc index 406140a..5ef5bf8 100755 --- a/src/Contours.cc +++ b/src/Contours.cc @@ -18,6 +18,7 @@ void Contour::Init(Local target) { // Prototype // Local proto = constructor->PrototypeTemplate(); Nan::SetPrototypeMethod(ctor, "point", Point); + Nan::SetPrototypeMethod(ctor, "points", Points); Nan::SetPrototypeMethod(ctor, "size", Size); Nan::SetPrototypeMethod(ctor, "cornerCount", CornerCount); Nan::SetPrototypeMethod(ctor, "area", Area); @@ -75,7 +76,7 @@ NAN_METHOD(Contour::Points) { Contour *self = Nan::ObjectWrap::Unwrap(info.This()); int pos = info[0]->NumberValue(); - vector points = self->contours[pos]; + std::vector points = self->contours[pos]; Local data = Nan::New(points.size()); for (std::vector::size_type i = 0; i != points.size(); i++) { @@ -114,9 +115,10 @@ NAN_METHOD(Contour::Area) { Contour *self = Nan::ObjectWrap::Unwrap(info.This()); int pos = info[0]->NumberValue(); + bool orientation = (info.Length() > 1 && info[1]->BooleanValue()); // info.GetReturnValue().Set(Nan::New(contourArea(self->contours))); - info.GetReturnValue().Set(Nan::New(contourArea(cv::Mat(self->contours[pos])))); + info.GetReturnValue().Set(Nan::New(contourArea(cv::Mat(self->contours[pos]), orientation))); } NAN_METHOD(Contour::ArcLength) { @@ -295,7 +297,7 @@ NAN_METHOD(Contour::Serialize) { Local contours_data = Nan::New(self->contours.size()); for (std::vector::size_type i = 0; i != self->contours.size(); i++) { - vector points = self->contours[i]; + std::vector points = self->contours[i]; Local contour_data = Nan::New(points.size()); for (std::vector::size_type j = 0; j != points.size(); j++) { @@ -336,12 +338,12 @@ NAN_METHOD(Contour::Deserialize) { Local contours_data = Local::Cast(data->Get(Nan::New("contours").ToLocalChecked())); Local hierarchy_data = Local::Cast(data->Get(Nan::New("hierarchy").ToLocalChecked())); - vector > contours_res; + std::vector > contours_res; int contours_length = contours_data->Length(); for (int i = 0; i < contours_length; i++) { Local contour_data = Local::Cast(contours_data->Get(i)); - vector points; + std::vector points; int contour_length = contour_data->Length(); for (int j = 0; j < contour_length; j++) { @@ -354,7 +356,7 @@ NAN_METHOD(Contour::Deserialize) { contours_res.push_back(points); } - vector hierarchy_res; + std::vector hierarchy_res; int hierarchy_length = hierarchy_data->Length(); for (int i = 0; i < hierarchy_length; i++) { diff --git a/src/Contours.h b/src/Contours.h index cbbff01..8e8352e 100755 --- a/src/Contours.h +++ b/src/Contours.h @@ -1,12 +1,11 @@ #include "OpenCV.h" -using namespace std; class Contour: public Nan::ObjectWrap { public: cv::Mat mat; - vector > contours; - vector hierarchy; + std::vector > contours; + std::vector hierarchy; static Nan::Persistent constructor; static void Init(Local target); diff --git a/src/FaceRecognizer.cc b/src/FaceRecognizer.cc index 9a2dd69..eb41e1a 100644 --- a/src/FaceRecognizer.cc +++ b/src/FaceRecognizer.cc @@ -1,11 +1,19 @@ -#include "FaceRecognizer.h" #include "OpenCV.h" -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4) && (CV_SUBMINOR_VERSION>=4)) - +#ifdef HAVE_OPENCV_FACE +#include "FaceRecognizer.h" #include "Matrix.h" #include +#if CV_MAJOR_VERSION >= 3 +namespace cv { + using std::vector; + using cv::face::createEigenFaceRecognizer; + using cv::face::createFisherFaceRecognizer; + using cv::face::createLBPHFaceRecognizer; +} +#endif + #define EIGEN 0 #define LBPH 1 #define FISHER 2 @@ -274,6 +282,17 @@ NAN_METHOD(FaceRecognizerWrap::PredictSync) { double confidence = 0.0; self->rec->predict(im, predictedLabel, confidence); +#if CV_MAJOR_VERSION >= 3 + // Older versions of OpenCV3 incorrectly returned label=0 at + // confidence=DBL_MAX instead of label=-1 on failure. This can be removed + // once the fix* becomes more widespread. + // + // * https://github.com/Itseez/opencv_contrib/commit/0aa58ae9b30a017b356a86d29453c0b56ed9e625#diff-d9c561bf45c255c5951ff1ab55e80473 + if (predictedLabel == 0 && confidence == DBL_MAX) { + predictedLabel = -1; + } +#endif + v8::Local res = Nan::New(); res->Set(Nan::New("id").ToLocalChecked(), Nan::New(predictedLabel)); res->Set(Nan::New("confidence").ToLocalChecked(), Nan::New(confidence)); @@ -296,6 +315,16 @@ public: void Execute() { this->rec->predict(this->im, this->predictedLabel, this->confidence); +#if CV_MAJOR_VERSION >= 3 + // Older versions of OpenCV3 incorrectly returned label=0 at + // confidence=DBL_MAX instead of label=-1 on failure. This can be removed + // once the fix* becomes more widespread. + // + // * https://github.com/Itseez/opencv_contrib/commit/0aa58ae9b30a017b356a86d29453c0b56ed9e625#diff-d9c561bf45c255c5951ff1ab55e80473 + if (this->predictedLabel == 0 && this->confidence == DBL_MAX) { + this->predictedLabel = -1; + } +#endif } void HandleOKCallback() { @@ -369,7 +398,27 @@ NAN_METHOD(FaceRecognizerWrap::GetMat) { JSTHROW("getMat takes a key") } std::string key = std::string(*Nan::Utf8String(info[0]->ToString())); - cv::Mat m = self->rec->getMat(key); + cv::Mat m; +#if CV_MAJOR_VERSION >= 3 + cv::face::BasicFaceRecognizer *bfr = + dynamic_cast(self->rec.get()); + if (bfr == NULL) { + Nan::ThrowTypeError("getMat not supported"); + return; + } + if (key.compare("mean") == 0) { + m = bfr->getMean(); + } else if (key.compare("eigenvectors") == 0) { + m = bfr->getEigenVectors(); + } else if (key.compare("eigenvalues") == 0) { + m = bfr->getEigenValues(); + } else { + Nan::ThrowTypeError("Unknown getMat keyname"); + return; + } +#else + m = self->rec->getMat(key); +#endif Local im = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *img = Nan::ObjectWrap::Unwrap(im); diff --git a/src/FaceRecognizer.h b/src/FaceRecognizer.h index 3afbf76..a147229 100644 --- a/src/FaceRecognizer.h +++ b/src/FaceRecognizer.h @@ -1,8 +1,15 @@ #include "OpenCV.h" -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4)) +#ifdef HAVE_OPENCV_FACE +#if CV_MAJOR_VERSION >= 3 +#include +namespace cv { + using cv::face::FaceRecognizer; +} +#else #include "opencv2/contrib/contrib.hpp" +#endif class FaceRecognizerWrap: public Nan::ObjectWrap { public: diff --git a/src/Features2d.cc b/src/Features2d.cc index 596ce98..fa8f1cb 100644 --- a/src/Features2d.cc +++ b/src/Features2d.cc @@ -1,10 +1,11 @@ +#include "OpenCV.h" + +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4)) #include "Features2d.h" #include "Matrix.h" #include #include -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4)) - void Features::Init(Local target) { Nan::HandleScope scope; diff --git a/src/Features2d.h b/src/Features2d.h index 6ae2b21..e793f86 100644 --- a/src/Features2d.h +++ b/src/Features2d.h @@ -1,6 +1,6 @@ #include "OpenCV.h" -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4)) +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4)) #include #include diff --git a/src/ImgProc.cc b/src/ImgProc.cc index 8fa3ffb..cc56658 100644 --- a/src/ImgProc.cc +++ b/src/ImgProc.cc @@ -9,11 +9,44 @@ void ImgProc::Init(Local target) { Nan::SetMethod(obj, "undistort", Undistort); Nan::SetMethod(obj, "initUndistortRectifyMap", InitUndistortRectifyMap); Nan::SetMethod(obj, "remap", Remap); + Nan::SetMethod(obj, "distanceTransform", DistanceTransform); Nan::SetMethod(obj, "getStructuringElement", GetStructuringElement); target->Set(Nan::New("imgproc").ToLocalChecked(), obj); } +// cv::distanceTransform +NAN_METHOD(ImgProc::DistanceTransform) { + Nan::EscapableHandleScope scope; + + try { + // Arg 0 is the image + Matrix* m0 = Nan::ObjectWrap::Unwrap(info[0]->ToObject()); + cv::Mat inputImage = m0->mat; + + // Arg 1 is the distance type (CV_DIST_L1, CV_DIST_L2, etc.) + int distType = info[1]->IntegerValue();; + + // Make a mat to hold the result image + cv::Mat outputImage; + + // Perform distance transform + cv::distanceTransform(inputImage, outputImage, distType, 0); + + // Wrap the output image + Local outMatrixWrap = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *outMatrix = Nan::ObjectWrap::Unwrap(outMatrixWrap); + outMatrix->mat = outputImage; + + // Return the output image + info.GetReturnValue().Set(outMatrixWrap); + } catch (cv::Exception &e) { + const char *err_msg = e.what(); + Nan::ThrowError(err_msg); + return; + } +} + // cv::undistort NAN_METHOD(ImgProc::Undistort) { Nan::EscapableHandleScope scope; diff --git a/src/ImgProc.h b/src/ImgProc.h index 48f031f..8754fdd 100644 --- a/src/ImgProc.h +++ b/src/ImgProc.h @@ -9,6 +9,7 @@ class ImgProc: public Nan::ObjectWrap { public: static void Init(Local target); + static NAN_METHOD(DistanceTransform); static NAN_METHOD(Undistort); static NAN_METHOD(InitUndistortRectifyMap); static NAN_METHOD(Remap); diff --git a/src/LDAWrap.cc b/src/LDAWrap.cc index 34d4e17..b787dbf 100644 --- a/src/LDAWrap.cc +++ b/src/LDAWrap.cc @@ -1,8 +1,11 @@ -#include "LDAWrap.h" #include "OpenCV.h" -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4) && (CV_SUBMINOR_VERSION>=4)) +#if CV_MAJOR_VERSION >= 3 +#warning TODO: port me to OpenCV 3 +#endif +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4) && (CV_SUBMINOR_VERSION>=4)) +#include "LDAWrap.h" #include "Matrix.h" #include @@ -17,8 +20,8 @@ void LDAWrap::Init(Local target) { ctor->InstanceTemplate()->SetInternalFieldCount(1); ctor->SetClassName(Nan::New("LDA").ToLocalChecked()); - Nan::SetPrototypeMethod(ctor, "subspaceProject", SubspaceProject); - Nan::SetPrototypeMethod(ctor, "subspaceReconstruct", SubspaceReconstruct); + Nan::SetMethod(ctor, "subspaceProject", SubspaceProject); + Nan::SetMethod(ctor, "subspaceReconstruct", SubspaceReconstruct); target->Set(Nan::New("LDA").ToLocalChecked(), ctor->GetFunction()); }; diff --git a/src/LDAWrap.h b/src/LDAWrap.h index 2c47cba..44671d3 100644 --- a/src/LDAWrap.h +++ b/src/LDAWrap.h @@ -1,6 +1,6 @@ #include "OpenCV.h" -#if ((CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >=4)) +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4)) #include "opencv2/contrib/contrib.hpp" diff --git a/src/Matrix.cc b/src/Matrix.cc index 7bdf195..f0134aa 100755 --- a/src/Matrix.cc +++ b/src/Matrix.cc @@ -35,6 +35,7 @@ void Matrix::Init(Local target) { Nan::SetPrototypeMethod(ctor, "pixel", Pixel); Nan::SetPrototypeMethod(ctor, "width", Width); Nan::SetPrototypeMethod(ctor, "height", Height); + Nan::SetPrototypeMethod(ctor, "type", Type); Nan::SetPrototypeMethod(ctor, "size", Size); Nan::SetPrototypeMethod(ctor, "clone", Clone); Nan::SetPrototypeMethod(ctor, "crop", Crop); @@ -48,7 +49,6 @@ void Matrix::Init(Local target) { Nan::SetPrototypeMethod(ctor, "saveAsync", SaveAsync); Nan::SetPrototypeMethod(ctor, "resize", Resize); Nan::SetPrototypeMethod(ctor, "rotate", Rotate); - Nan::SetPrototypeMethod(ctor, "getRotationMatrix2D", GetRotationMatrix2D); Nan::SetPrototypeMethod(ctor, "warpAffine", WarpAffine); Nan::SetPrototypeMethod(ctor, "copyTo", CopyTo); Nan::SetPrototypeMethod(ctor, "convertTo", ConvertTo); @@ -60,16 +60,21 @@ void Matrix::Init(Local target) { Nan::SetPrototypeMethod(ctor, "gaussianBlur", GaussianBlur); Nan::SetPrototypeMethod(ctor, "medianBlur", MedianBlur); Nan::SetPrototypeMethod(ctor, "bilateralFilter", BilateralFilter); + Nan::SetPrototypeMethod(ctor, "sobel", Sobel); Nan::SetPrototypeMethod(ctor, "copy", Copy); Nan::SetPrototypeMethod(ctor, "flip", Flip); Nan::SetPrototypeMethod(ctor, "roi", ROI); Nan::SetPrototypeMethod(ctor, "ptr", Ptr); Nan::SetPrototypeMethod(ctor, "absDiff", AbsDiff); + Nan::SetPrototypeMethod(ctor, "dct", Dct); + Nan::SetPrototypeMethod(ctor, "idct", Idct); Nan::SetPrototypeMethod(ctor, "addWeighted", AddWeighted); + Nan::SetPrototypeMethod(ctor, "add", Add); Nan::SetPrototypeMethod(ctor, "bitwiseXor", BitwiseXor); Nan::SetPrototypeMethod(ctor, "bitwiseNot", BitwiseNot); Nan::SetPrototypeMethod(ctor, "bitwiseAnd", BitwiseAnd); Nan::SetPrototypeMethod(ctor, "countNonZero", CountNonZero); + Nan::SetPrototypeMethod(ctor, "moments", Moments); Nan::SetPrototypeMethod(ctor, "canny", Canny); Nan::SetPrototypeMethod(ctor, "dilate", Dilate); Nan::SetPrototypeMethod(ctor, "erode", Erode); @@ -78,7 +83,6 @@ void Matrix::Init(Local target) { Nan::SetPrototypeMethod(ctor, "drawAllContours", DrawAllContours); Nan::SetPrototypeMethod(ctor, "goodFeaturesToTrack", GoodFeaturesToTrack); Nan::SetPrototypeMethod(ctor, "houghLinesP", HoughLinesP); - Nan::SetPrototypeMethod(ctor, "crop", Crop); Nan::SetPrototypeMethod(ctor, "houghCircles", HoughCircles); Nan::SetPrototypeMethod(ctor, "inRange", inRange); Nan::SetPrototypeMethod(ctor, "adjustROI", AdjustROI); @@ -92,6 +96,7 @@ void Matrix::Init(Local target) { Nan::SetPrototypeMethod(ctor, "equalizeHist", EqualizeHist); Nan::SetPrototypeMethod(ctor, "floodFill", FloodFill); Nan::SetPrototypeMethod(ctor, "matchTemplate", MatchTemplate); + Nan::SetPrototypeMethod(ctor, "matchTemplateByMatrix", MatchTemplateByMatrix); Nan::SetPrototypeMethod(ctor, "templateMatches", TemplateMatches); Nan::SetPrototypeMethod(ctor, "minMaxLoc", MinMaxLoc); Nan::SetPrototypeMethod(ctor, "pushBack", PushBack); @@ -101,12 +106,15 @@ void Matrix::Init(Local target) { Nan::SetMethod(ctor, "Zeros", Zeros); Nan::SetMethod(ctor, "Ones", Ones); Nan::SetMethod(ctor, "Eye", Eye); + Nan::SetMethod(ctor, "getRotationMatrix2D", GetRotationMatrix2D); Nan::SetPrototypeMethod(ctor, "copyWithMask", CopyWithMask); Nan::SetPrototypeMethod(ctor, "setWithMask", SetWithMask); Nan::SetPrototypeMethod(ctor, "meanWithMask", MeanWithMask); + Nan::SetPrototypeMethod(ctor, "mean", Mean); Nan::SetPrototypeMethod(ctor, "shift", Shift); Nan::SetPrototypeMethod(ctor, "reshape", Reshape); Nan::SetPrototypeMethod(ctor, "release", Release); + Nan::SetPrototypeMethod(ctor, "subtract", Subtract); target->Set(Nan::New("Matrix").ToLocalChecked(), ctor->GetFunction()); }; @@ -145,22 +153,22 @@ NAN_METHOD(Matrix::New) { } Matrix::Matrix() : - Nan::ObjectWrap() { + node_opencv::Matrix() { mat = cv::Mat(); } Matrix::Matrix(int rows, int cols) : - Nan::ObjectWrap() { + node_opencv::Matrix() { mat = cv::Mat(rows, cols, CV_32FC3); } Matrix::Matrix(int rows, int cols, int type) : - Nan::ObjectWrap() { + node_opencv::Matrix() { mat = cv::Mat(rows, cols, type); } Matrix::Matrix(cv::Mat m, cv::Rect roi) : - Nan::ObjectWrap() { + node_opencv::Matrix() { mat = cv::Mat(m, roi); } @@ -193,7 +201,7 @@ double Matrix::DblGet(cv::Mat mat, int i, int j) { switch (mat.type()) { case CV_32FC3: - pix = mat.at(i, j); + pix = mat.at(i, j); pint |= (uchar) pix.val[2]; pint |= ((uchar) pix.val[1]) << 8; pint |= ((uchar) pix.val[0]) << 16; @@ -202,6 +210,9 @@ double Matrix::DblGet(cv::Mat mat, int i, int j) { case CV_64FC1: val = mat.at(i, j); break; + case CV_32FC1: + val = mat.at(i, j); + break; default: val = mat.at(i, j); break; @@ -276,11 +287,14 @@ NAN_METHOD(Matrix::Set) { 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; + 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; + case CV_32FC1: + self->mat.at(i, j) = val; + break; default: self->mat.at(i, j) = val; } @@ -313,10 +327,16 @@ NAN_METHOD(Matrix::GetData) { Nan::HandleScope scope; Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); - int size = self->mat.rows * self->mat.cols * self->mat.elemSize1(); + int size = self->mat.rows * self->mat.cols * self->mat.elemSize(); Local buf = Nan::NewBuffer(size).ToLocalChecked(); uchar* data = (uchar*) Buffer::Data(buf); - memcpy(data, self->mat.data, size); + // if there is padding after each row, clone first to get rid of it + if (self->mat.dims == 2 && self->mat.step[0] != size_t(self->mat.size[1])) { + cv::Mat copy = self->mat.clone(); + memcpy(data, copy.data, size); + } else { + memcpy(data, self->mat.data, size); + } v8::Local globalObj = Nan::GetCurrentContext()->Global(); v8::Local bufferConstructor = v8::Local::Cast(globalObj->Get(Nan::New("Buffer").ToLocalChecked())); @@ -482,6 +502,12 @@ NAN_METHOD(Matrix::Size) { info.GetReturnValue().Set(arr); } +NAN_METHOD(Matrix::Type) { + SETUP_FUNCTION(Matrix) + + info.GetReturnValue().Set(Nan::New(self->mat.type())); +} + NAN_METHOD(Matrix::Clone) { SETUP_FUNCTION(Matrix) @@ -666,8 +692,8 @@ NAN_METHOD(Matrix::ToBuffer) { class AsyncToBufferWorker: public Nan::AsyncWorker { public: - AsyncToBufferWorker(Nan::Callback *callback, Matrix* matrix, string ext, - vector params) : + AsyncToBufferWorker(Nan::Callback *callback, Matrix* matrix, std::string ext, + std::vector params) : Nan::AsyncWorker(callback), matrix(matrix), ext(ext), @@ -980,7 +1006,7 @@ public: private: Matrix* matrix; - char* filename; + std::string filename; int res; }; @@ -1081,6 +1107,7 @@ NAN_METHOD(Matrix::GaussianBlur) { cv::Mat blurred; Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + double sigma = 0; if (info.Length() < 1) { ksize = cv::Size(5, 5); @@ -1097,9 +1124,12 @@ NAN_METHOD(Matrix::GaussianBlur) { Nan::ThrowTypeError("'ksize' argument must be a 2 double array"); } ksize = cv::Size(x->NumberValue(), y->NumberValue()); + if (info[1]->IsNumber()) { + sigma = info[1]->ToNumber()->Value(); + } } - cv::GaussianBlur(self->mat, blurred, ksize, 0); + cv::GaussianBlur(self->mat, blurred, ksize, sigma); blurred.copyTo(self->mat); info.GetReturnValue().Set(Nan::Null()); @@ -1155,6 +1185,36 @@ NAN_METHOD(Matrix::BilateralFilter) { info.GetReturnValue().Set(Nan::Null()); } +NAN_METHOD(Matrix::Sobel) { + Nan::HandleScope scope; + + if (info.Length() < 3) + Nan::ThrowError("Need more arguments: sobel(ddepth, xorder, yorder, ksize=3, scale=1.0, delta=0.0, borderType=CV_BORDER_DEFAULT)"); + + int ddepth = info[0]->IntegerValue(); + int xorder = info[1]->IntegerValue(); + int yorder = info[2]->IntegerValue(); + + int ksize = 3; + if (info.Length() > 3) ksize = info[3]->IntegerValue(); + double scale = 1; + if (info.Length() > 4) scale = info[4]->NumberValue(); + double delta = 0; + if (info.Length() > 5) delta = info[5]->NumberValue(); + int borderType = cv::BORDER_DEFAULT; + if (info.Length() > 6) borderType = info[6]->IntegerValue(); + + Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + + Local result_to_return = + Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *result = Nan::ObjectWrap::Unwrap(result_to_return); + + cv::Sobel(self->mat, result->mat, ddepth, xorder, yorder, ksize, scale, delta, borderType); + + info.GetReturnValue().Set(result_to_return); +} + NAN_METHOD(Matrix::Copy) { Nan::HandleScope scope; @@ -1236,6 +1296,38 @@ NAN_METHOD(Matrix::AbsDiff) { info.GetReturnValue().Set(Nan::Null()); } +NAN_METHOD(Matrix::Dct) { + Nan::HandleScope scope; + + Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + int cols = self->mat.cols; + int rows = self->mat.rows; + + Local out = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *m_out = Nan::ObjectWrap::Unwrap(out); + m_out->mat.create(cols, rows, CV_32F); + + cv::dct(self->mat, m_out->mat); + + info.GetReturnValue().Set(out); +} + +NAN_METHOD(Matrix::Idct) { + Nan::HandleScope scope; + + Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + int cols = self->mat.cols; + int rows = self->mat.rows; + + Local out = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *m_out = Nan::ObjectWrap::Unwrap(out); + m_out->mat.create(cols, rows, CV_32F); + + cv::idct(self->mat, m_out->mat); + + info.GetReturnValue().Set(out); +} + NAN_METHOD(Matrix::AddWeighted) { Nan::HandleScope scope; @@ -1257,6 +1349,29 @@ NAN_METHOD(Matrix::AddWeighted) { info.GetReturnValue().Set(Nan::Null()); } +NAN_METHOD(Matrix::Add) { + Nan::HandleScope scope; + + Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + int cols = self->mat.cols; + int rows = self->mat.rows; + + Matrix *src1 = Nan::ObjectWrap::Unwrap(info[0]->ToObject()); + + Local out = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *m_out = Nan::ObjectWrap::Unwrap(out); + m_out->mat.create(cols, rows, self->mat.type()); + + try { + cv::add(self->mat, src1->mat, m_out->mat); + } catch(cv::Exception& e ) { + const char* err_msg = e.what(); + Nan::ThrowError(err_msg); + } + + info.GetReturnValue().Set(out); +} + NAN_METHOD(Matrix::BitwiseXor) { Nan::HandleScope scope; @@ -1323,6 +1438,45 @@ NAN_METHOD(Matrix::Split) { info.GetReturnValue().Set(Nan::Null()); } */ +NAN_METHOD(Matrix::Moments) { + Nan::HandleScope scope; + + Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + + cv::Moments mo = moments( self->mat, false ); + + Local res = Nan::New(); + + res->Set(Nan::New("m00").ToLocalChecked(), Nan::New(mo.m00)); + res->Set(Nan::New("m10").ToLocalChecked(), Nan::New(mo.m10)); + res->Set(Nan::New("m01").ToLocalChecked(), Nan::New(mo.m01)); + res->Set(Nan::New("m20").ToLocalChecked(), Nan::New(mo.m20)); + res->Set(Nan::New("m11").ToLocalChecked(), Nan::New(mo.m11)); + res->Set(Nan::New("m02").ToLocalChecked(), Nan::New(mo.m02)); + res->Set(Nan::New("m30").ToLocalChecked(), Nan::New(mo.m30)); + res->Set(Nan::New("m21").ToLocalChecked(), Nan::New(mo.m21)); + res->Set(Nan::New("m12").ToLocalChecked(), Nan::New(mo.m12)); + res->Set(Nan::New("m03").ToLocalChecked(), Nan::New(mo.m03)); + + res->Set(Nan::New("mu20").ToLocalChecked(), Nan::New(mo.mu20)); + res->Set(Nan::New("mu11").ToLocalChecked(), Nan::New(mo.mu11)); + res->Set(Nan::New("mu02").ToLocalChecked(), Nan::New(mo.mu02)); + res->Set(Nan::New("mu30").ToLocalChecked(), Nan::New(mo.mu30)); + res->Set(Nan::New("mu21").ToLocalChecked(), Nan::New(mo.mu21)); + res->Set(Nan::New("mu12").ToLocalChecked(), Nan::New(mo.mu12)); + res->Set(Nan::New("mu03").ToLocalChecked(), Nan::New(mo.mu03)); + + res->Set(Nan::New("nu20").ToLocalChecked(), Nan::New(mo.nu20)); + res->Set(Nan::New("nu11").ToLocalChecked(), Nan::New(mo.nu11)); + res->Set(Nan::New("nu02").ToLocalChecked(), Nan::New(mo.nu02)); + res->Set(Nan::New("nu30").ToLocalChecked(), Nan::New(mo.nu30)); + res->Set(Nan::New("nu21").ToLocalChecked(), Nan::New(mo.nu21)); + res->Set(Nan::New("nu12").ToLocalChecked(), Nan::New(mo.nu12)); + res->Set(Nan::New("nu03").ToLocalChecked(), Nan::New(mo.nu03)); + + info.GetReturnValue().Set(res); +} + NAN_METHOD(Matrix::Canny) { Nan::HandleScope scope; @@ -1406,7 +1560,16 @@ NAN_METHOD(Matrix::DrawContour) { } int thickness = info.Length() < 4 ? 1 : info[3]->NumberValue(); - cv::drawContours(self->mat, cont->contours, pos, color, thickness); + int lineType = info.Length() < 5 ? 8 : info[4]->NumberValue(); + int maxLevel = info.Length() < 6 ? 0 : info[5]->NumberValue(); + + cv::Point offset; + if (info.Length() == 6) { + Local _offset = Local::Cast(info[5]); + offset = cv::Point(_offset->Get(0)->ToNumber()->Value(), _offset->Get(1)->ToNumber()->Value()); + } + + cv::drawContours(self->mat, cont->contours, pos, color, thickness, lineType, cont->hierarchy, maxLevel, offset); return; } @@ -1517,13 +1680,27 @@ NAN_METHOD(Matrix::HoughCircles) { } cv::Scalar setColor(Local objColor) { - Local valB = objColor->Get(0); - Local valG = objColor->Get(1); - Local valR = objColor->Get(2); + int64_t channels[4] = { 0, 0, 0, 0 }; - cv::Scalar color = cv::Scalar(valB->IntegerValue(), valG->IntegerValue(), - valR->IntegerValue()); - return color; + // We'll accomodate a channel count up to 4 and fall back to the old + // "assume it's always 3" in the default case + if (!objColor->HasRealIndexedProperty(1)) { + channels[0] = objColor->Get(0)->IntegerValue(); + } else if (!objColor->HasRealIndexedProperty(2)) { + channels[0] = objColor->Get(0)->IntegerValue(); + channels[1] = objColor->Get(1)->IntegerValue(); + } else if (!objColor->HasRealIndexedProperty(4)) { + channels[0] = objColor->Get(0)->IntegerValue(); + channels[1] = objColor->Get(1)->IntegerValue(); + channels[2] = objColor->Get(2)->IntegerValue(); + channels[3] = objColor->Get(3)->IntegerValue(); + } else { + channels[0] = objColor->Get(0)->IntegerValue(); + channels[1] = objColor->Get(1)->IntegerValue(); + channels[2] = objColor->Get(2)->IntegerValue(); + } + + return cv::Scalar(channels[0], channels[1], channels[2], channels[3]); } cv::Point setPoint(Local objPoint) { @@ -1625,24 +1802,23 @@ NAN_METHOD(Matrix::Rotate) { NAN_METHOD(Matrix::GetRotationMatrix2D) { Nan::HandleScope scope; - - Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); - cv::Mat res; + if (info.Length() < 3) { + JSTHROW("Invalid number of arguments"); + } float angle = info[0]->ToNumber()->Value(); - int x = info[1]->IsUndefined() ? round(self->mat.size().width / 2) : - info[1]->Uint32Value(); - int y = info[2]->IsUndefined() ? round(self->mat.size().height / 2) : - info[2]->Uint32Value(); + int x = info[1]->Uint32Value(); + int y = info[2]->Uint32Value(); double scale = info[3]->IsUndefined() ? 1.0 : info[3]->NumberValue(); + Local img_to_return = + Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *img = Nan::ObjectWrap::Unwrap(img_to_return); + cv::Point center = cv::Point(x,y); - res = getRotationMatrix2D(center, angle, scale); + img->mat = getRotationMatrix2D(center, angle, scale); - ~self->mat; - self->mat = res; - - return; + info.GetReturnValue().Set(img_to_return); } NAN_METHOD(Matrix::WarpAffine) { @@ -1985,7 +2161,7 @@ NAN_METHOD(Matrix::Split) { Matrix * self = Nan::ObjectWrap::Unwrap(info.This()); unsigned int size = self->mat.channels(); - vector channels; + std::vector channels; // Split doesn't seem to work on empty vectors for (unsigned int i = 0; i < size; i++) { @@ -2018,7 +2194,7 @@ NAN_METHOD(Matrix::Merge) { v8::Local jsChannels = v8::Local::Cast(info[0]); unsigned int L = jsChannels->Length(); - vector vChannels(L); + std::vector vChannels(L); for (unsigned int i = 0; i < L; i++) { Matrix * matObject = Nan::ObjectWrap::Unwrap(jsChannels->Get(i)->ToObject()); vChannels[i] = matObject->mat; @@ -2132,14 +2308,14 @@ NAN_METHOD(Matrix::TemplateMatches) { 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)); + cv::Point top_left = cv::Point(std::max(0, pt.x - min_x_distance), + std::max(0, pt.y - min_y_distance)); + cv::Point top_right = cv::Point(std::min(max_x, pt.x + min_x_distance), + std::max(0, pt.y - min_y_distance)); + cv::Point bottom_left = cv::Point(std::max(0, pt.x - min_x_distance), + std::min(max_y, pt.y + min_y_distance)); + cv::Point bottom_right = cv::Point(std::min(max_x, pt.x + min_x_distance), + std::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) @@ -2168,6 +2344,36 @@ NAN_METHOD(Matrix::TemplateMatches) { info.GetReturnValue().Set(probabilites_array); } +// @author Evilcat325 +// MatchTemplate accept a Matrix +// Usage: output = input.matchTemplateByMatrix(matrix. method); +NAN_METHOD(Matrix::MatchTemplateByMatrix) { + Nan::HandleScope scope; + + Matrix *self = Nan::ObjectWrap::Unwrap(info.This()); + Matrix *templ = Nan::ObjectWrap::Unwrap(info[0]->ToObject()); + + Local out = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *m_out = Nan::ObjectWrap::Unwrap(out); + int cols = self->mat.cols - templ->mat.cols + 1; + int rows = self->mat.rows - templ->mat.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 = (info.Length() < 2) ? (int)cv::TM_CCORR_NORMED : info[1]->Uint32Value(); + if (!(method >= 0 && method <= 5)) method = (int)cv::TM_CCORR_NORMED; + cv::matchTemplate(self->mat, templ->mat, m_out->mat, method); + info.GetReturnValue().Set(out); +} + // @author ytham // Match Template filter // Usage: output = input.matchTemplate("templateFileString", method); @@ -2179,7 +2385,7 @@ NAN_METHOD(Matrix::MatchTemplate) { v8::String::Utf8Value args0(info[0]->ToString()); std::string filename = std::string(*args0); cv::Mat templ; - templ = cv::imread(filename, CV_8S); + templ = cv::imread(filename, -1); Local out = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); Matrix *m_out = Nan::ObjectWrap::Unwrap(out); @@ -2198,8 +2404,44 @@ NAN_METHOD(Matrix::MatchTemplate) { int method = (info.Length() < 2) ? (int)cv::TM_CCORR_NORMED : info[1]->Uint32Value(); cv::matchTemplate(self->mat, templ, m_out->mat, method); + cv::normalize(m_out->mat, m_out->mat, 0, 1, cv::NORM_MINMAX, -1, cv::Mat()); + double minVal; + double maxVal; + cv::Point minLoc; + cv::Point maxLoc; + cv::Point matchLoc; - info.GetReturnValue().Set(out); + minMaxLoc(m_out->mat, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat()); + + if(method == CV_TM_SQDIFF || method == CV_TM_SQDIFF_NORMED) { + matchLoc = minLoc; + } + else { + matchLoc = maxLoc; + } + + //detected ROI + unsigned int roi_x = matchLoc.x; + unsigned int roi_y = matchLoc.y; + unsigned int roi_width = templ.cols; + unsigned int roi_height = templ.rows; + + //draw rectangle + if(info.Length() >= 3) { + cv::Rect roi(roi_x,roi_y,roi_width,roi_height); + cv::rectangle(self->mat, roi, cv::Scalar(0,0,255)); + } + + m_out->mat.convertTo(m_out->mat, CV_8UC1, 255, 0); + + v8::Local arr = Nan::New(5); + arr->Set(0, out); + arr->Set(1, Nan::New(roi_x)); + arr->Set(2, Nan::New(roi_y)); + arr->Set(3, Nan::New(roi_width)); + arr->Set(4, Nan::New(roi_height)); + + info.GetReturnValue().Set(arr); } // @author ytham @@ -2377,10 +2619,24 @@ NAN_METHOD(Matrix::MeanWithMask) { Matrix *mask = Nan::ObjectWrap::Unwrap(info[0]->ToObject()); cv::Scalar means = cv::mean(self->mat, mask->mat); - v8::Local < v8::Array > arr = Nan::New(3); + v8::Local < v8::Array > arr = Nan::New(4); arr->Set(0, Nan::New(means[0])); arr->Set(1, Nan::New(means[1])); arr->Set(2, Nan::New(means[2])); + arr->Set(3, Nan::New(means[3])); + + info.GetReturnValue().Set(arr); +} + +NAN_METHOD(Matrix::Mean) { + SETUP_FUNCTION(Matrix) + + cv::Scalar means = cv::mean(self->mat); + v8::Local arr = Nan::New(4); + arr->Set(0, Nan::New(means[0])); + arr->Set(1, Nan::New(means[1])); + arr->Set(2, Nan::New(means[2])); + arr->Set(3, Nan::New(means[3])); info.GetReturnValue().Set(arr); } @@ -2463,3 +2719,17 @@ NAN_METHOD(Matrix::Release) { return; } + +NAN_METHOD(Matrix::Subtract) { + SETUP_FUNCTION(Matrix) + + if (info.Length() < 1) { + Nan::ThrowTypeError("Invalid number of arguments"); + } + + Matrix *other = Nan::ObjectWrap::Unwrap(info[0]->ToObject()); + + self->mat -= other->mat; + + return; +} diff --git a/src/Matrix.h b/src/Matrix.h index b26046f..b672879 100755 --- a/src/Matrix.h +++ b/src/Matrix.h @@ -1,9 +1,9 @@ + #include "OpenCV.h" +#include "../inc/Matrix.h" -class Matrix: public Nan::ObjectWrap { +class Matrix: public node_opencv::Matrix{ public: - - cv::Mat mat; static Nan::Persistent constructor; static void Init(Local target); static NAN_METHOD(New); @@ -36,6 +36,7 @@ public: JSFUNC(Size) JSFUNC(Width) JSFUNC(Height) + JSFUNC(Type) JSFUNC(Channels) JSFUNC(Clone) JSFUNC(Ellipse) @@ -62,17 +63,22 @@ public: JSFUNC(GaussianBlur) JSFUNC(MedianBlur) JSFUNC(BilateralFilter) + JSFUNC(Sobel) JSFUNC(Copy) JSFUNC(Flip) JSFUNC(ROI) JSFUNC(Ptr) JSFUNC(AbsDiff) + JSFUNC(Dct) + JSFUNC(Idct) JSFUNC(AddWeighted) + JSFUNC(Add) JSFUNC(BitwiseXor) JSFUNC(BitwiseNot) JSFUNC(BitwiseAnd) JSFUNC(CountNonZero) //JSFUNC(Split) + JSFUNC(Moments) JSFUNC(Canny) JSFUNC(Dilate) JSFUNC(Erode) @@ -107,6 +113,7 @@ public: JSFUNC(FloodFill) JSFUNC(MatchTemplate) + JSFUNC(MatchTemplateByMatrix) JSFUNC(TemplateMatches) JSFUNC(MinMaxLoc) @@ -119,10 +126,13 @@ public: JSFUNC(CopyWithMask) JSFUNC(SetWithMask) JSFUNC(MeanWithMask) + JSFUNC(Mean) JSFUNC(Shift) JSFUNC(Reshape) JSFUNC(Release) + + JSFUNC(Subtract) /* static Handle Val(const Arguments& info); static Handle RowRange(const Arguments& info); diff --git a/src/OpenCV.cc b/src/OpenCV.cc index 62007a4..c0e666b 100755 --- a/src/OpenCV.cc +++ b/src/OpenCV.cc @@ -11,6 +11,7 @@ void OpenCV::Init(Local target) { target->Set(Nan::New("version").ToLocalChecked(), Nan::New(out, n).ToLocalChecked()); Nan::SetMethod(target, "readImage", ReadImage); + Nan::SetMethod(target, "readImageMulti", ReadImageMulti); } NAN_METHOD(OpenCV::ReadImage) { @@ -37,14 +38,14 @@ NAN_METHOD(OpenCV::ReadImage) { } else if (info[0]->IsString()) { std::string filename = std::string(*Nan::Utf8String(info[0]->ToString())); - mat = cv::imread(filename); + mat = cv::imread(filename, CV_LOAD_IMAGE_UNCHANGED); } else 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); + mat = cv::imdecode(*mbuf, CV_LOAD_IMAGE_UNCHANGED); if (mat.empty()) { argv[0] = Nan::Error("Error loading file"); @@ -66,3 +67,54 @@ NAN_METHOD(OpenCV::ReadImage) { return; } + +#if CV_MAJOR_VERSION >= 3 +NAN_METHOD(OpenCV::ReadImageMulti) { + Nan::EscapableHandleScope scope; + + REQ_FUN_ARG(1, cb); + + Local argv[2]; + argv[0] = Nan::Null(); + + std::vector mats; + try { + if (info[0]->IsString()) { + std::string filename = std::string(*Nan::Utf8String(info[0]->ToString())); + cv::imreadmulti(filename, mats); + + if (mats.empty()) { + argv[0] = Nan::Error("Error loading file"); + } + } + } catch (cv::Exception& e) { + argv[0] = Nan::Error(e.what()); + argv[1] = Nan::Null(); + } + + Local output = Nan::New(mats.size()); + argv[1] = output; + + for (std::vector::size_type i = 0; i < mats.size(); i ++) { + Local im_h = Nan::New(Matrix::constructor)->GetFunction()->NewInstance(); + Matrix *img = Nan::ObjectWrap::Unwrap(im_h); + img->mat = mats[i]; + + output->Set(i, im_h); + } + + Nan::TryCatch try_catch; + cb->Call(Nan::GetCurrentContext()->Global(), 2, argv); + + if (try_catch.HasCaught()) { + Nan::FatalException(try_catch); + } + + return; +} +#else +NAN_METHOD(OpenCV::ReadImageMulti) { + info.GetReturnValue().Set(Nan::New(false)); + return; +} +#endif diff --git a/src/OpenCV.h b/src/OpenCV.h index 0d8eb90..cc9a2b9 100755 --- a/src/OpenCV.h +++ b/src/OpenCV.h @@ -1,6 +1,16 @@ #ifndef __NODE_OPENCV_H__ #define __NODE_OPENCV_H__ +#ifdef WIN + /* + This is needed on Windows for Visual Studio to not throw an error in the + build/include/opencv2/flann/any.h file in OpenCV. + */ + namespace std{ typedef type_info type_info; } +#endif + + + #include #include #include @@ -8,6 +18,16 @@ #include #include #include +#if CV_MAJOR_VERSION >= 3 +#include +#include +#include +#include +#endif +#if ((CV_MAJOR_VERSION == 2) && (CV_MINOR_VERSION >=4) && (CV_SUBMINOR_VERSION>=4)) +#define HAVE_OPENCV_FACE +#endif + #include #include @@ -47,6 +67,7 @@ public: static void Init(Local target); static NAN_METHOD(ReadImage); + static NAN_METHOD(ReadImageMulti); }; #endif diff --git a/src/Stereo.cc b/src/Stereo.cc index f8d1c11..7831b66 100644 --- a/src/Stereo.cc +++ b/src/Stereo.cc @@ -1,4 +1,11 @@ + #include "Stereo.h" + +#if CV_MAJOR_VERSION >= 3 +#warning TODO: port me to OpenCV 3 +#endif + +#if CV_MAJOR_VERSION < 3 #include "Matrix.h" #include @@ -312,3 +319,5 @@ NAN_METHOD(StereoGC::Compute) { return; } } + +#endif diff --git a/src/Stereo.h b/src/Stereo.h index fbbdb58..08fdaf0 100644 --- a/src/Stereo.h +++ b/src/Stereo.h @@ -3,6 +3,8 @@ #include "OpenCV.h" +#if CV_MAJOR_VERSION < 3 + class StereoBM: public Nan::ObjectWrap { public: cv::StereoBM stereo; @@ -51,3 +53,4 @@ public: }; #endif +#endif // __NODE_STEREO_H diff --git a/src/VideoCaptureWrap.cc b/src/VideoCaptureWrap.cc index 8bfc453..5afbf65 100755 --- a/src/VideoCaptureWrap.cc +++ b/src/VideoCaptureWrap.cc @@ -3,7 +3,6 @@ #include "OpenCV.h" #include -using namespace std; Nan::Persistent VideoCaptureWrap::constructor; @@ -32,7 +31,9 @@ void VideoCaptureWrap::Init(Local target) { Nan::SetPrototypeMethod(ctor, "setWidth", SetWidth); Nan::SetPrototypeMethod(ctor, "setHeight", SetHeight); Nan::SetPrototypeMethod(ctor, "setPosition", SetPosition); - Nan::SetPrototypeMethod(ctor, "close", Close); + Nan::SetPrototypeMethod(ctor, "getFrameAt", GetFrameAt); + Nan::SetPrototypeMethod(ctor, "getFrameCount", GetFrameCount); + Nan::SetPrototypeMethod(ctor, "release", Release); Nan::SetPrototypeMethod(ctor, "ReadSync", ReadSync); Nan::SetPrototypeMethod(ctor, "grab", Grab); Nan::SetPrototypeMethod(ctor, "retrieve", Retrieve); @@ -93,6 +94,15 @@ NAN_METHOD(VideoCaptureWrap::SetWidth) { return; } +NAN_METHOD(VideoCaptureWrap::GetFrameCount) { + Nan::HandleScope scope; + VideoCaptureWrap *v = Nan::ObjectWrap::Unwrap(info.This()); + + int cnt = int(v->cap.get(CV_CAP_PROP_FRAME_COUNT)); + + info.GetReturnValue().Set(Nan::New(cnt)); +} + NAN_METHOD(VideoCaptureWrap::SetHeight) { Nan::HandleScope scope; VideoCaptureWrap *v = Nan::ObjectWrap::Unwrap(info.This()); @@ -121,7 +131,21 @@ NAN_METHOD(VideoCaptureWrap::SetPosition) { return; } -NAN_METHOD(VideoCaptureWrap::Close) { +NAN_METHOD(VideoCaptureWrap::GetFrameAt) { + Nan::HandleScope scope; + VideoCaptureWrap *v = Nan::ObjectWrap::Unwrap(info.This()); + + if(info.Length() != 1) + return; + + int pos = info[0]->IntegerValue(); + + v->cap.set(CV_CAP_PROP_POS_MSEC, pos); + + return; +} + +NAN_METHOD(VideoCaptureWrap::Release) { Nan::HandleScope scope; VideoCaptureWrap *v = Nan::ObjectWrap::Unwrap(info.This()); diff --git a/src/VideoCaptureWrap.h b/src/VideoCaptureWrap.h index 3cb5a04..cb01a72 100755 --- a/src/VideoCaptureWrap.h +++ b/src/VideoCaptureWrap.h @@ -23,10 +23,10 @@ public: // to set frame position static NAN_METHOD(SetPosition); + static NAN_METHOD(GetFrameCount); static NAN_METHOD(GetFrameAt); - //close the stream - static NAN_METHOD(Close); + // release the stream + static NAN_METHOD(Release); }; - diff --git a/src/init.cc b/src/init.cc index a3af3be..7d91bbe 100755 --- a/src/init.cc +++ b/src/init.cc @@ -30,16 +30,19 @@ extern "C" void init(Local target) { Constants::Init(target); Calib3D::Init(target); ImgProc::Init(target); +#if CV_MAJOR_VERSION < 3 StereoBM::Init(target); StereoSGBM::Init(target); StereoGC::Init(target); - -#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >=4 +#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >=4 BackgroundSubtractorWrap::Init(target); Features::Init(target); - FaceRecognizerWrap::Init(target); LDAWrap::Init(target); #endif +#endif +#ifdef HAVE_OPENCV_FACE + FaceRecognizerWrap::Init(target); +#endif }; NODE_MODULE(opencv, init) diff --git a/test/.unit.js.swp b/test/.unit.js.swp deleted file mode 100644 index b3cd16c..0000000 Binary files a/test/.unit.js.swp and /dev/null differ diff --git a/test/nativemat.cc b/test/nativemat.cc new file mode 100644 index 0000000..494b163 --- /dev/null +++ b/test/nativemat.cc @@ -0,0 +1,20 @@ +#include +#include + +void Size(const Nan::FunctionCallbackInfo& info) { + // Unwrap the node-opencv Matrix object into a normal cv::Mat + cv::Mat mat = Nan::ObjectWrap::Unwrap(info[0]->ToObject())->mat; + + v8::Local < v8::Array > arr = Nan::New(2); + arr->Set(0, Nan::New(mat.size().height)); + arr->Set(1, Nan::New(mat.size().width)); + + info.GetReturnValue().Set(arr); +} + +void Init(v8::Local exports) { + exports->Set(Nan::New("size").ToLocalChecked(), + Nan::New(Size)->GetFunction()); +} + +NODE_MODULE(test_nativemat, Init) diff --git a/test/unit.js b/test/unit.js index 50d92a8..77adf18 100755 --- a/test/unit.js +++ b/test/unit.js @@ -61,8 +61,8 @@ test('Matrix constructor', function(assert){ test('Matrix accessors', function(assert){ var mat = new cv.Matrix(1, 2); - mat.set(0,0,3) - mat.set(0,1,5000) + mat.set(0,0,3); + mat.set(0,1,5000); assert.deepEqual(mat.row(0), [3,5000]); mat = new cv.Matrix(1,2); @@ -103,8 +103,10 @@ test('Matrix functions', function(assert) { // convertTo var mat = new cv.Matrix(75, 75, cv.Constants.CV_32F, [2.0]); var matNew = new cv.Matrix(75, 75, cv.Constants.CV_8U); - mat.convertTo(matNew, cv.Constants.CV_8U, 2, 1); - assert.equal(matNew.pixel(0, 0), 5); + var alpha = 2; + var beta = 1; + mat.convertTo(matNew, cv.Constants.CV_8U, alpha, beta); + assert.equal(matNew.pixel(0, 0), mat.get(0, 0)*alpha + beta); // reshape mat = new cv.Matrix(75, 75, cv.Constants.CV_8UC1); @@ -112,6 +114,10 @@ test('Matrix functions', function(assert) { assert.equal(matNew.height(), 1); assert.equal(matNew.width(), 5625); + // GetRotationMatrix2D + mat = cv.Matrix.getRotationMatrix2D(0, 0, 90, 1.0); + assert.deepEqual(mat.size(), [2,3], 'GetRotationMatrix2D'); + assert.end(); }) @@ -123,7 +129,6 @@ test(".norm", function(assert){ var errorL2 = im.norm(im2, cv.Constants.NORM_L2); assert.equal(errorL2, 7295.591339980605); - errorL2 = im.norm(im, cv.Constants.NORM_L2); assert.equal(errorL2, 0); assert.end(); @@ -201,23 +206,55 @@ test(".bitwiseXor", function(assert){ test("Image read from file", function(assert){ - cv.readImage("./examples/files/mona.png", function(err, im){ + cv.readImage("./examples/files/opencv.png", function(err, im){ assert.ok(im); - assert.equal(im.width(), 500); - assert.equal(im.height(), 756) - assert.equal(im.empty(), false) - assert.end() + assert.equal(im.width(), 82); + assert.equal(im.height(), 99); + assert.equal(im.channels(), 4); + assert.equal(im.empty(), false); + assert.end(); }) }) +test("Multi-page image read from file", function(assert){ + if (parseInt(cv.version) >= 3) { + cv.readImageMulti("./examples/files/multipage.tif", function(err, imgs){ + assert.ok(imgs); + assert.equal(imgs.length, 10); + for (var i = 0; i < imgs.length; i++) { + assert.ok(imgs[i]); + assert.equal(imgs[i].width(), 800); + assert.equal(imgs[i].height(), 600); + assert.equal(imgs[i].channels(), 3); + assert.equal(imgs[i].empty(), false); + } + assert.end(); + }) + } else { + assert.equal(cv.readImageMulti("./examples/files/multipage.tif"), false); + assert.end(); + } +}) + +test("Distance transform", function(assert){ + cv.readImage("./examples/files/distanceTransform.png", function(err, img){ + assert.ok(img); + + // 50px image with single black pixel on right side + var result = cv.imgproc.distanceTransform(img, cv.Constants.CV_DIST_L2, cv.Constants.CV_DIST_MASK_PRECISE); + assert.equal(result.get(0,0), 49); + assert.end(); + }) +}) test("read Image from buffer", function(assert){ - cv.readImage(fs.readFileSync('./examples/files/mona.png'), function(err, im){ + cv.readImage(fs.readFileSync('./examples/files/opencv.png'), function(err, im){ assert.ok(im); - assert.equal(im.width(), 500); - assert.equal(im.height(), 756) - assert.equal(im.empty(), false) - assert.end() + assert.equal(im.width(), 82); + assert.equal(im.height(), 99); + assert.equal(im.channels(), 4); + assert.equal(im.empty(), false); + assert.end(); }) }) @@ -238,7 +275,7 @@ test("Cascade Classifier", function(assert){ test("ImageDataStream", function(assert){ var s = new cv.ImageDataStream() - s.on('load', function(im){ + s.on('load', function(im){ assert.ok(im) assert.equal(im.empty(), false); assert.end() @@ -310,7 +347,107 @@ test("fonts", function(t) { }); }) +test('LDA Wrap', function(assert) { + if (cv.LDA === undefined) { + console.log('TODO: Please port LDAWrap.cc to OpenCV 3') + assert.end(); + return; + } + + // subspaceProject + var mat = cv.LDA.subspaceProject(new cv.Matrix(1, 2, cv.Constants.CV_64F), new cv.Matrix(), new cv.Matrix(2, 1, cv.Constants.CV_8UC1)); + assert.deepEqual(mat.size(), [2,2], 'subspaceProject'); + + // subspaceReconstruct + mat = cv.LDA.subspaceReconstruct(new cv.Matrix(1, 2, cv.Constants.CV_64F), new cv.Matrix(), new cv.Matrix(1, 2, cv.Constants.CV_8UC1)); + assert.deepEqual(mat.size(), [1,1], 'subspaceReconstruct'); + + assert.end(); +}) + + +test('Native Matrix', function(assert) { + var nativemat = require('../build/' + (!!process.env.NODE_OPENCV_DEBUG ? 'Debug' : 'Release') + '/test_nativemat.node'); + var mat = new cv.Matrix(42, 8); + + assert.deepEqual(mat.size(), nativemat.size(mat), 'nativemat'); + assert.end(); +}) + +test('Subtract', function(assert) { + var a = new cv.Matrix.Zeros(1,1); + a.set(0, 0, 3); + var b = new cv.Matrix.Zeros(1,1); + b.set(0, 0, 1); + a.subtract(b); + assert.deepEqual(a.get(0, 0), 2); + assert.end(); +}); + +test('Mean', function(assert) { + var a = new cv.Matrix.Zeros(2, 2, cv.Constants.CV_8UC3); + + // Set [0, 0] element to 1 for all three channels + a.set(0, 0, 1, 0); + a.set(0, 0, 1, 1); + a.set(0, 0, 1, 2); + + var means = a.mean(); + assert.deepEqual(means, [0.25, 0.25, 0.25, 0]); + assert.end(); +}); + +test('MatchTemplateByMatrix', function(assert) { + var cv = require('../lib/opencv'); + var targetFilename = "./examples/files/car1.jpg"; + var templateFilename = "./examples/files/car1_template.jpg"; + cv.readImage(targetFilename, function(err, target){ + cv.readImage(templateFilename, function(err, template){ + var TM_CCORR_NORMED = 3; + var res = target.matchTemplateByMatrix(template, TM_CCORR_NORMED); + var minMax = res.minMaxLoc(); + var topLeft = minMax.maxLoc; + assert.ok(topLeft, "RGB Found Match"); + assert.equal(topLeft.x, 42, "match location x === 42"); + assert.equal(topLeft.y, 263, "match location y === 263"); + target.canny(5,300); + template.canny(5,300); + res = target.matchTemplateByMatrix(template, TM_CCORR_NORMED); + minMax = res.minMaxLoc(); + topLeft = minMax.maxLoc; + assert.ok(topLeft, "Canny edge Found Match"); + assert.equal(topLeft.x, 42, "match location x === 42"); + assert.equal(topLeft.y, 263, "match location y === 263"); + assert.end(); + }); + }) +}); + +test('setColor works will alpha channels', function(assert) { + var cv = require('../lib/opencv'); + var mat = new cv.Matrix(100, 100, cv.Constants.CV_8UC4); + + var SQUARE = [ 50, 50 ]; + mat.rectangle([ 0, 0 ], SQUARE, [ 0, 187, 255, 255 ], -1); + mat.rectangle([ 0, 50 ], SQUARE, [ 0, 187, 124, 200 ], -1); + mat.rectangle([ 50, 0 ], SQUARE, [ 241, 161, 0, 128 ], -1); + mat.rectangle([ 50, 50 ], SQUARE, [ 20, 83, 246, 70 ], -1); + + cv.readImage('./examples/files/alpha-test.png', function(err, imgMat) { + if (!err) { + var diff = new cv.Matrix(); + diff.absDiff(mat, imgMat); + // We'll verify that each channel is 0 + var channels = diff.split(); + for (var i = 0; i < 4; i++) { + assert.equal(channels[i].countNonZero(), 0); + } + } else { + throw err; + } + assert.end(); + }); +}); + // Test the examples folder. require('./examples')() - - diff --git a/utils/find-opencv.js b/utils/find-opencv.js new file mode 100644 index 0000000..c3731ad --- /dev/null +++ b/utils/find-opencv.js @@ -0,0 +1,81 @@ +"use strict"; + +var exec = require("child_process").exec; +var fs = require("fs"); +var flag = process.argv[2] || "--exists"; + +// Normally |pkg-config opencv ...| could report either OpenCV 2.x or OpenCV 3.y +// depending on what is installed. To enable both 2.x and 3.y to co-exist on +// the same machine, the opencv.pc for 3.y can be installed as opencv3.pc and +// then selected by |export PKG_CONFIG_OPENCV3=1| before building node-opencv. +var opencv = process.env.PKG_CONFIG_OPENCV3 === "1" ? "opencv3" : '"opencv >= 2.3.1"'; + +function main(){ + //Try using pkg-config, but if it fails and it is on Windows, try the fallback + exec("pkg-config " + opencv + " " + flag, function(error, stdout, stderr){ + if(error){ + if(process.platform === "win32"){ + fallback(); + } + else{ + throw new Error("ERROR: failed to run: pkg-config", opencv, flag); + } + } + else{ + console.log(stdout); + } + }); +} + +//======================Windows Specific======================================= + +function fallback(){ + exec("echo %OPENCV_DIR%", function(error, stdout, stderr){ + stdout = cleanupEchoOutput(stdout); + if(error){ + throw new Error("ERROR: There was an error reading OPENCV_DIR"); + } + else if(stdout === "%OPENCV_DIR%") { + throw new Error("ERROR: OPENCV_DIR doesn't seem to be defined"); + } + else { + printPaths(stdout); + } + }); +} + +function printPaths(opencvPath){ + if(flag === "--cflags") { + console.log("\"" + opencvPath + "\\..\\..\\include\""); + console.log("\"" + opencvPath + "\\..\\..\\include\\opencv\""); + } + else if(flag === "--libs") { + var libPath = opencvPath + "\\lib\\"; + + fs.readdir(libPath, function(err, files){ + if(err){ + throw new Error("ERROR: couldn't read the lib directory " + err); + } + + var libs = ""; + for(var i = 0; i < files.length; i++){ + if(getExtension(files[i]) === "lib"){ + libs = libs + " \"" + libPath + files[i] + "\" \r\n "; + } + } + console.log(libs); + }); + } + else { + throw new Error("Error: unknown argument '" + flag + "'"); + } +} + +function cleanupEchoOutput(s){ + return s.slice(0, s.length - 2); +} + +function getExtension(s){ + return s.substr(s.lastIndexOf(".") + 1); +} +main();