下載Opencv安裝包: http://opencv.org/releases.htmlhtml
解壓進入Opencv目錄node
編譯Opencv(須要cmake)linux
mkdir release cd release cmake -G "Unix Makefiles" .. (爲Unix 系統生成Makefile,Mac OSX是基於Unix的。未安裝cmake 能夠經過Homebrew安裝,未安裝Homebrew需安裝Homebrew) make
安裝Opencvios
make install
安裝完成的目錄c++
/usr/local/lib (Opencv庫文件) /usr/local/include (Opencv頭文件) /usr/local/share/ (Opencv xml配置文件)
使用Opencv(C++ Version)bootstrap
編寫CMakeLists.txt文件bash
project( ORBFeatureAndCompare ) // 項目的名稱 cmake_minimum_required(VERSION 2.8) // cmake的版本要求 find_package( OpenCV REQUIRED ) // 查找對應的Opencv依賴庫 find_package(Boost COMPONENTS log log_setup thread filesystem system) // 查找對應的Boost依賴庫(下文出現Boost的安裝方法) add_executable( ORBFeatureAndCompare ORBFeatureAndCompare ) // 指定可運行的文件 // 引入對應的依賴庫文件的位置 target_link_libraries(ORBFeatureAndCompare ${OpenCV_LIBS} ${Boost_LOG_SETUP_LIBRARY} ${Boost_LOG_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} )
編寫Opencv的cpp文件ui
//必要的頭文件 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/features2d/features2d.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <iostream> #include <fstream> #include <cstring> #include <iterator> #include <vector> using namespace boost::filesystem; namespace newfs = boost::filesystem; using namespace cv; using namespace std; int main () { .... return 0; }
建立一個images(其餘命名均可以)的資源文件夾spa
cd images cmake .. make ./ORBFeatureAndCompare //編譯出的可執行程序
下載Boost安裝包: https://dl.bintray.com/boosto...插件
解壓進入Boost目錄
編譯安裝Boost
./bootstrap.sh ./b2
引入Boost庫進行數據序列化,一樣是在CmakeList中引入文件路徑, 在cpp文件中使用
依賴安裝
全局安裝node-gyp
本地安裝nan
編寫binding.gyp
{ "targets": [ { "target_name": "feature", "sources": [ "./src/feature.cc" ], "include_dirs": [ "<!(node -e \"require('nan')\")", "/usr/local/include/boost", "/usr/local/include/opencv2" ], "conditions": [ [ "OS==\"linux\" or OS==\"freebsd\" or OS==\"openbsd\" or OS==\"solaris\" or OS==\"aix\"", { } ], ["OS==\"mac\"", { "libraries": [ "/usr/local/lib/libboost_log_setup-mt.dylib", "/usr/local/lib/libboost_log-mt.dylib", "/usr/local/lib/libboost_filesystem-mt.dylib", "/usr/local/lib/libboost_thread-mt.dylib", "/usr/local/lib/libboost_system-mt.dylib", "/usr/local/lib/libopencv_core.3.2.0.dylib", "/usr/local/lib/libopencv_highgui.3.2.0.dylib", "/usr/local/lib/libopencv_imgproc.3.2.0.dylib", "/usr/local/lib/libopencv_features2d.3.2.0.dylib" ] } ] ] } ] }
編寫node c++ 插件
#include <node.h> #include <nan.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/features2d/features2d.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <iostream> #include <fstream> #include <string> #include <iterator> #include <vector> using namespace boost::filesystem; namespace newfs = boost::filesystem; using namespace v8; using namespace std; vector<uchar> matToString(cv::Mat descriptorMat) { vector<uchar> buf; imencode(".png", descriptorMat, buf); return buf; } vector<uchar> descriptorMat(cv::Mat image) { vector<cv::KeyPoint> keyPoint; cv::Ptr<cv::ORB> orb = cv::ORB::create(4000, 1.2f, 8, 31, 0, 2, cv::ORB::HARRIS_SCORE, 31, 20); orb->detect(image, keyPoint); cv::Mat descriptorMat; orb->compute(image, keyPoint, descriptorMat); return matToString(descriptorMat); } void imageFeature(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 1) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong number of arguments"))); return; } if (!args[0]->IsString()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments"))); return; } String::Utf8Value pathValue(Local<String>::Cast(args[0])); string path = string(*pathValue); cv::Mat image = cv::imread(path, 1); if (image.empty()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Input image error"))); return; } vector<uchar> descriptorString = descriptorMat(image); Local<Object> buf = Nan::NewBuffer(descriptorString.size()).ToLocalChecked(); uchar* data = (uchar*) node::Buffer::Data(buf); memcpy(data, &descriptorString[0], descriptorString.size()); v8::Local<v8::Object> globalObj = Nan::GetCurrentContext()->Global(); v8::Local<v8::Function> bufferConstructor = v8::Local<v8::Function>::Cast(globalObj->Get(Nan::New<String>("Buffer").ToLocalChecked())); v8::Local<v8::Value> constructorArgs[3] = {buf, Nan::New<v8::Integer>((unsigned)descriptorString.size()), Nan::New<v8::Integer>(0)}; v8::Local<v8::Object> actualBuffer = bufferConstructor->NewInstance(3, constructorArgs); args.GetReturnValue().Set(actualBuffer); } int bfMatcherCompare (cv::Mat &descriptors1, cv::Mat &descriptors2) { cv::BFMatcher matcher(cv::NORM_HAMMING); vector<cv::DMatch> matches; matcher.match(descriptors1, descriptors2, matches); double max_dist = 0; double min_dist = 100; /* for (int i = 0; i < descriptors1.rows; i++) { double dist = matches[i].distance; if (dist < min_dist) min_dist = dist; if (dist > max_dist) max_dist = dist; } vector<cv::DMatch> good_matches; for (int i = 0; i < descriptors1.rows; i++) { if (matches[i].distance < 3 * min_dist) good_matches.push_back(matches[i]); } return good_matches.size(); */ for (int i = 0; i < descriptors1.rows; i++) { double dist = matches[i].distance; if (dist < min_dist) { min_dist = dist; } if (dist > max_dist) { max_dist = dist; } } std::vector<cv::DMatch> good_matches; double good_matches_sum = 0.0; for (int i = 0; i < descriptors1.rows; i++) { double distance = matches[i].distance; if (distance <= std::max(2 * min_dist, 0.02)) { good_matches.push_back(matches[i]); good_matches_sum += distance; } } return (double) good_matches_sum / (double) good_matches.size(); } void similarity(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); Local<Object> arg1 = args[0]->ToObject(); int size1 = args[1]->NumberValue(); Local<Object> arg2 = args[2]->ToObject(); int size2 = args[3]->NumberValue(); if (args.Length() < 4) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong number of arguments"))); return; } uchar*buffer1 = (uchar*) node::Buffer::Data(arg1); std::vector<uchar> vec1(buffer1, buffer1 + (unsigned int) size1); cv::Mat img_decode1; img_decode1 = cv::imdecode(vec1, CV_8U); uchar*buffer2 = (uchar*) node::Buffer::Data(arg2); std::vector<uchar> vec2(buffer2, buffer2 + (unsigned int) size2); cv::Mat img_decode2; img_decode2 = cv::imdecode(vec2, CV_8U); int similarity = bfMatcherCompare(img_decode1, img_decode2); args.GetReturnValue().Set(similarity); } void init(Local<Object> exports, Local<Object> module) { NODE_SET_METHOD(exports, "imageFeature", imageFeature); NODE_SET_METHOD(exports, "similarity", similarity); } NODE_MODULE(addon, init)
編寫Js文件
const feature = require('./build/Release/feature'); exports.getImageFeature = (filePath) => { return feature.imageFeature(filePath).toString('utf8'); }; exports.getImageSimilarity = (descriptor1, descriptor2) => { let matBuffer1 = Buffer.from(descriptor1); let matBuffer2 = Buffer.from(descriptor2); return feature.similarity(matBuffer1, matBuffer1.length, matBuffer2, matBuffer2.length); };
編譯運行
node-gyp configure build node test.js