diff --git a/CMakeLists.txt b/CMakeLists.txt index 12d96da..b0e9ad2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ endif() add_library(cv SHARED ${CMAKE_JS_SRC} cxx/node.cc cxx/mat.cc + cxx/videocap.cc cxx/util.cc ) target_link_libraries(cv ${CMAKE_JS_LIB} ${OpenCV_LIBS}) diff --git a/cxx/mat.cc b/cxx/mat.cc index d1f51f5..dcfab7c 100644 --- a/cxx/mat.cc +++ b/cxx/mat.cc @@ -1,459 +1,488 @@ -#include "node.h" +#include "mat.h" + using namespace Napi; #define MAT_INSTANCE_METHOD(method) InstanceMethod<&CVMat::method>(#method, static_cast(napi_writable | napi_configurable)) #define MAT_STATIC_METHOD(method) StaticMethod<&CVMat::method>(#method, static_cast(napi_writable | napi_configurable)) +#define NODE_FUNCTION_IMPL(name) Napi::Value CVMat::name(const Napi::CallbackInfo &info) -static FunctionReference *constructor = nullptr; +FunctionReference *CVMat::constructor = nullptr; -class CVMat : public ObjectWrap { - public: - static Napi::Object Init(Napi::Env env, Napi::Object exports) - { - Function func = DefineClass(env, "Mat", { - MAT_STATIC_METHOD(ImRead), - MAT_STATIC_METHOD(ImDecode), - MAT_STATIC_METHOD(ImWrite), - MAT_STATIC_METHOD(ImEncode), +Napi::Object CVMat::Init(Napi::Env env, Napi::Object exports) +{ + Function func = DefineClass(env, "Mat", { + MAT_STATIC_METHOD(ImRead), + MAT_STATIC_METHOD(ImDecode), + MAT_STATIC_METHOD(ImWrite), + MAT_STATIC_METHOD(ImEncode), - MAT_STATIC_METHOD(Crop), - MAT_STATIC_METHOD(Resize), - MAT_STATIC_METHOD(WarpAffine), - MAT_STATIC_METHOD(Blur), - MAT_STATIC_METHOD(CopyMakeBorder), - MAT_STATIC_METHOD(Flip), - MAT_STATIC_METHOD(GetRotationMatrix2D), + MAT_STATIC_METHOD(Crop), + MAT_STATIC_METHOD(Resize), + MAT_STATIC_METHOD(WarpAffine), + MAT_STATIC_METHOD(Blur), + MAT_STATIC_METHOD(CopyMakeBorder), + MAT_STATIC_METHOD(Flip), + MAT_STATIC_METHOD(GetRotationMatrix2D), - MAT_STATIC_METHOD(Rectangle), - MAT_STATIC_METHOD(Circle), - MAT_STATIC_METHOD(Line), - MAT_STATIC_METHOD(Ellipse), - MAT_STATIC_METHOD(Polylines), - MAT_STATIC_METHOD(FillPoly), - MAT_STATIC_METHOD(FillConvexPoly), - MAT_STATIC_METHOD(DrawMarker), - MAT_STATIC_METHOD(PutText), + MAT_STATIC_METHOD(Rectangle), + MAT_STATIC_METHOD(Circle), + MAT_STATIC_METHOD(Line), + MAT_STATIC_METHOD(Ellipse), + MAT_STATIC_METHOD(Polylines), + MAT_STATIC_METHOD(FillPoly), + MAT_STATIC_METHOD(FillConvexPoly), + MAT_STATIC_METHOD(DrawMarker), + MAT_STATIC_METHOD(PutText), - MAT_INSTANCE_METHOD(IsEmpty), - MAT_INSTANCE_METHOD(GetCols), - MAT_INSTANCE_METHOD(GetRows), - MAT_INSTANCE_METHOD(GetType), - MAT_INSTANCE_METHOD(GetDepth), - MAT_INSTANCE_METHOD(GetChannels), - MAT_INSTANCE_METHOD(GetFlags), - MAT_INSTANCE_METHOD(GetDims), - MAT_INSTANCE_METHOD(GetIsContinuous), - MAT_INSTANCE_METHOD(GetIsSubmatrix), - MAT_INSTANCE_METHOD(GetElemSize), - MAT_INSTANCE_METHOD(GetElemSize1), - MAT_INSTANCE_METHOD(GetTotal), - MAT_INSTANCE_METHOD(GetTotalWithDim), - MAT_INSTANCE_METHOD(GetSize), - MAT_INSTANCE_METHOD(GetData), + MAT_INSTANCE_METHOD(IsEmpty), + MAT_INSTANCE_METHOD(GetCols), + MAT_INSTANCE_METHOD(GetRows), + MAT_INSTANCE_METHOD(GetType), + MAT_INSTANCE_METHOD(GetDepth), + MAT_INSTANCE_METHOD(GetChannels), + MAT_INSTANCE_METHOD(GetFlags), + MAT_INSTANCE_METHOD(GetDims), + MAT_INSTANCE_METHOD(GetIsContinuous), + MAT_INSTANCE_METHOD(GetIsSubmatrix), + MAT_INSTANCE_METHOD(GetElemSize), + MAT_INSTANCE_METHOD(GetElemSize1), + MAT_INSTANCE_METHOD(GetTotal), + MAT_INSTANCE_METHOD(GetTotalWithDim), + MAT_INSTANCE_METHOD(GetSize), + MAT_INSTANCE_METHOD(GetData), - MAT_INSTANCE_METHOD(Col), - MAT_INSTANCE_METHOD(ColRange), - MAT_INSTANCE_METHOD(Row), - MAT_INSTANCE_METHOD(RowRange), - MAT_INSTANCE_METHOD(Diag), + MAT_INSTANCE_METHOD(Col), + MAT_INSTANCE_METHOD(ColRange), + MAT_INSTANCE_METHOD(Row), + MAT_INSTANCE_METHOD(RowRange), + MAT_INSTANCE_METHOD(Diag), - MAT_INSTANCE_METHOD(Clone), - MAT_INSTANCE_METHOD(CopyTo), + MAT_INSTANCE_METHOD(Clone), + MAT_INSTANCE_METHOD(CopyTo), - }); - constructor = new FunctionReference(); - *constructor = Napi::Persistent(func); - exports.Set("Mat", func); - env.SetInstanceData(constructor); - return exports; - } + }); + constructor = new FunctionReference(); + *constructor = Napi::Persistent(func); + exports.Set("Mat", func); + env.SetInstanceData(constructor); + return exports; +} - CVMat(const CallbackInfo &info) - : ObjectWrap(info) - { - if (info.Length() >= 2) { - auto sizesArray = info[0].As(); - std::vector sizes(sizesArray.Length()); - for (auto i = 0; i < sizes.size(); ++i) sizes[i] = sizesArray.Get(i).As().Int32Value(); - auto type = info[1].As().Int32Value(); - // 使用sizes和type初始化 - if (info.Length() < 3) mat_ = cv::Mat(sizes, type); - // 使用数据初始化 - else if (info[2].IsTypedArray()) { - auto dataArray = info[2].As(); - auto data = static_cast(dataArray.ArrayBuffer().Data()) + dataArray.ByteOffset(); - mat_ = cv::Mat(sizes, type, data); - } - // 其他 TODO - else mat_ = cv::Mat(sizes, type); +CVMat::CVMat(const CallbackInfo &info) + : ObjectWrap(info) +{ + if (info.Length() >= 2) { + auto sizesArray = info[0].As(); + std::vector sizes(sizesArray.Length()); + for (auto i = 0; i < sizes.size(); ++i) sizes[i] = sizesArray.Get(i).As().Int32Value(); + auto type = info[1].As().Int32Value(); + // 使用sizes和type初始化 + if (info.Length() < 3) mat_ = cv::Mat(sizes, type); + // 使用数据初始化 + else if (info[2].IsTypedArray()) { + auto dataArray = info[2].As(); + auto data = static_cast(dataArray.ArrayBuffer().Data()) + dataArray.ByteOffset(); + mat_ = cv::Mat(sizes, type, data); } + // 其他 TODO + else mat_ = cv::Mat(sizes, type); } +} - static Napi::Value ImRead(const Napi::CallbackInfo &info) - { - auto filename = info[0].As().Utf8Value(); - int flag = (info.Length() >= 2) ? info[1].As().Int32Value() : cv::IMREAD_COLOR_BGR; - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::imread(filename, flag); }); +NODE_FUNCTION_IMPL(ImRead) +{ + auto filename = info[0].As().Utf8Value(); + int flag = (info.Length() >= 2) ? info[1].As().Int32Value() : cv::IMREAD_COLOR_BGR; + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::imread(filename, flag); }); +} +NODE_FUNCTION_IMPL(ImDecode) +{ + auto arr = info[0].As(); + auto data = static_cast(arr.ArrayBuffer().Data()) + arr.ByteOffset(); + std::vector buffer(data, data + arr.ByteLength()); + int flag = (info.Length() >= 2) ? info[1].As().Int32Value() : cv::IMREAD_COLOR_BGR; + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::imdecode(buffer, flag); }); +} +NODE_FUNCTION_IMPL(ImWrite) +{ + auto &mat = GetMat(info[0].As()); + auto filename = info[1].As().Utf8Value(); + std::vector params; + if (info[2].IsArray()) { + auto paramArray = info[2].As(); + params.resize(paramArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As().Int32Value(); } - static Napi::Value ImDecode(const Napi::CallbackInfo &info) - { - auto arr = info[0].As(); - auto data = static_cast(arr.ArrayBuffer().Data()) + arr.ByteOffset(); - std::vector buffer(data, data + arr.ByteLength()); - int flag = (info.Length() >= 2) ? info[1].As().Int32Value() : cv::IMREAD_COLOR_BGR; - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::imdecode(buffer, flag); }); + auto res = cv::imwrite(filename, mat.mat_, params); + return Boolean::New(info.Env(), res); +} +NODE_FUNCTION_IMPL(ImEncode) +{ + auto &mat = GetMat(info[0].As()); + auto extname = info[1].As().Utf8Value(); + std::vector params; + if (info[2].IsArray()) { + auto paramArray = info[2].As(); + params.resize(paramArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As().Int32Value(); } - static Napi::Value ImWrite(const Napi::CallbackInfo &info) - { - auto &mat = GetMat(info[0].As()); - auto filename = info[1].As().Utf8Value(); - std::vector params; - if (info[2].IsArray()) { - auto paramArray = info[2].As(); - params.resize(paramArray.Length()); - for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As().Int32Value(); - } - auto res = cv::imwrite(filename, mat.mat_, params); - return Boolean::New(info.Env(), res); - } - static Napi::Value ImEncode(const Napi::CallbackInfo &info) - { - auto &mat = GetMat(info[0].As()); - auto extname = info[1].As().Utf8Value(); - std::vector params; - if (info[2].IsArray()) { - auto paramArray = info[2].As(); - params.resize(paramArray.Length()); - for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As().Int32Value(); - } - std::vector buf; - if (!cv::imencode(extname, mat.mat_, buf, params)) return info.Env().Undefined(); - auto arrayBuffer = ArrayBuffer::New(info.Env(), buf.size()); - auto bufferPtr = static_cast(arrayBuffer.Data()); - for (auto ch : buf) { - *bufferPtr = ch; - bufferPtr++; - } - return arrayBuffer; + std::vector buf; + if (!cv::imencode(extname, mat.mat_, buf, params)) return info.Env().Undefined(); + auto arrayBuffer = ArrayBuffer::New(info.Env(), buf.size()); + auto bufferPtr = static_cast(arrayBuffer.Data()); + for (auto ch : buf) { + *bufferPtr = ch; + bufferPtr++; } + return arrayBuffer; +} - Napi::Value IsEmpty(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), mat_.empty()); } - Napi::Value GetCols(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.cols); } - Napi::Value GetRows(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.rows); } - Napi::Value GetType(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.type()); } - Napi::Value GetDepth(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.depth()); } - Napi::Value GetChannels(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.channels()); } - Napi::Value GetFlags(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.flags); } - Napi::Value GetDims(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.dims); } - Napi::Value GetIsContinuous(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), mat_.isContinuous()); } - Napi::Value GetIsSubmatrix(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), mat_.isSubmatrix()); } - Napi::Value GetElemSize(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.elemSize())); } - Napi::Value GetElemSize1(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.elemSize1())); } - Napi::Value GetTotal(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.total())); } - Napi::Value GetTotalWithDim(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.total(info[0].As().Int32Value(), info[1].As().Int32Value()))); } - Napi::Value GetSize(const Napi::CallbackInfo &info) - { - auto ret = Array::New(info.Env(), mat_.dims); - auto &size = mat_.size; - for (int i = 0; i < mat_.dims; ++i) ret.Set(i, size[i]); - return ret; - } - Napi::Value GetData(const Napi::CallbackInfo &info) - { - auto ptr = mat_.ptr(); - auto bytes = mat_.elemSize() * mat_.total(); - return ArrayBuffer::New(info.Env(), ptr, bytes); - } +NODE_FUNCTION_IMPL(IsEmpty) +{ + return Boolean::New(info.Env(), mat_.empty()); +} +NODE_FUNCTION_IMPL(GetCols) +{ + return Number::New(info.Env(), mat_.cols); +} +NODE_FUNCTION_IMPL(GetRows) +{ + return Number::New(info.Env(), mat_.rows); +} +NODE_FUNCTION_IMPL(GetType) +{ + return Number::New(info.Env(), mat_.type()); +} +NODE_FUNCTION_IMPL(GetDepth) +{ + return Number::New(info.Env(), mat_.depth()); +} +NODE_FUNCTION_IMPL(GetChannels) +{ + return Number::New(info.Env(), mat_.channels()); +} +NODE_FUNCTION_IMPL(GetFlags) +{ + return Number::New(info.Env(), mat_.flags); +} +NODE_FUNCTION_IMPL(GetDims) +{ + return Number::New(info.Env(), mat_.dims); +} +NODE_FUNCTION_IMPL(GetIsContinuous) +{ + return Boolean::New(info.Env(), mat_.isContinuous()); +} +NODE_FUNCTION_IMPL(GetIsSubmatrix) +{ + return Boolean::New(info.Env(), mat_.isSubmatrix()); +} +NODE_FUNCTION_IMPL(GetElemSize) +{ + return Number::New(info.Env(), static_cast(mat_.elemSize())); +} +NODE_FUNCTION_IMPL(GetElemSize1) +{ + return Number::New(info.Env(), static_cast(mat_.elemSize1())); +} +NODE_FUNCTION_IMPL(GetTotal) +{ + return Number::New(info.Env(), static_cast(mat_.total())); +} +NODE_FUNCTION_IMPL(GetTotalWithDim) +{ + return Number::New(info.Env(), static_cast(mat_.total(info[0].As().Int32Value(), info[1].As().Int32Value()))); +} +NODE_FUNCTION_IMPL(GetSize) +{ + auto ret = Array::New(info.Env(), mat_.dims); + auto &size = mat_.size; + for (int i = 0; i < mat_.dims; ++i) ret.Set(i, size[i]); + return ret; +} +NODE_FUNCTION_IMPL(GetData) +{ + auto ptr = mat_.ptr(); + auto bytes = mat_.elemSize() * mat_.total(); + return ArrayBuffer::New(info.Env(), ptr, bytes); +} - Napi::Value Col(const Napi::CallbackInfo &info) - { - auto col = info[0].As().Int32Value(); - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.col(col); }); - } - Napi::Value ColRange(const Napi::CallbackInfo &info) - { - auto start = info[0].As().Int32Value(); - auto end = info[1].As().Int32Value(); - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.colRange(start, end); }); - } - Napi::Value Row(const Napi::CallbackInfo &info) - { - auto row = info[0].As().Int32Value(); - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.row(row); }); - } - Napi::Value RowRange(const Napi::CallbackInfo &info) - { - auto start = info[0].As().Int32Value(); - auto end = info[1].As().Int32Value(); - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.rowRange(start, end); }); - } - Napi::Value Diag(const Napi::CallbackInfo &info) - { - auto d = info[0].As().Int32Value(); - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.diag(d); }); - } +NODE_FUNCTION_IMPL(Col) +{ + auto col = info[0].As().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.col(col); }); +} +NODE_FUNCTION_IMPL(ColRange) +{ + auto start = info[0].As().Int32Value(); + auto end = info[1].As().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.colRange(start, end); }); +} +NODE_FUNCTION_IMPL(Row) +{ + auto row = info[0].As().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.row(row); }); +} +NODE_FUNCTION_IMPL(RowRange) +{ + auto start = info[0].As().Int32Value(); + auto end = info[1].As().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.rowRange(start, end); }); +} +NODE_FUNCTION_IMPL(Diag) +{ + auto d = info[0].As().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.diag(d); }); +} - Napi::Value Clone(const Napi::CallbackInfo &info) - { - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.clone(); }); - } - Napi::Value CopyTo(const Napi::CallbackInfo &info) { - auto &target = GetMat(info[0].As()); - mat_.copyTo(target.mat_); - return info.Env().Undefined(); - } +NODE_FUNCTION_IMPL(Clone) +{ + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.clone(); }); +} +NODE_FUNCTION_IMPL(CopyTo) +{ + auto &target = GetMat(info[0].As()); + mat_.copyTo(target.mat_); + return info.Env().Undefined(); +} - static Napi::Value Crop(const Napi::CallbackInfo &info) - { - auto &src = GetMat(info[0].As()); - auto &dst = GetMat(info[1].As()); - auto rangeArray = info[2].As(); - std::vector ranges(rangeArray.Length()); - for (int i = 0; i < ranges.size(); ++i) { - auto item = rangeArray.Get(i).As(); - auto start = item.Get("start").As().Int32Value(); - auto end = item.Get("end").As().Int32Value(); - ranges[i] = cv::Range(start, end); - } - dst.mat_ = src.mat_(ranges); - return info[1]; - } - static Napi::Value Resize(const Napi::CallbackInfo &info) - { - auto &src = GetMat(info[0].As()); - auto &dst = GetMat(info[1].As()); - auto width = info[2].As().Int32Value(); - auto height = info[3].As().Int32Value(); - auto fx = info[4].As().DoubleValue(); - auto fy = info[5].As().DoubleValue(); - auto interpolation = info[6].As().Int32Value(); - cv::resize(src.mat_, dst.mat_, cv::Size(width, height), fx, fy, interpolation); - return info[1]; - } - static Napi::Value WarpAffine(const Napi::CallbackInfo &info) - { - auto &src = GetMat(info[0].As()); - auto &dst = GetMat(info[1].As()); - auto &transformMat = GetMat(info[2].As()); - auto dwidth = info[3].As().Int32Value(); - auto dheight = info[4].As().Int32Value(); - auto flags = info[5].As().Int32Value(); - auto borderMode = info[6].As().Int32Value(); - cv::warpAffine(src.mat_, dst.mat_, transformMat.mat_, cv::Size(dwidth, dheight), flags, borderMode); - return info[1]; - } - static Napi::Value Blur(const Napi::CallbackInfo &info) - { - auto &src = GetMat(info[0].As()); - auto &dst = GetMat(info[1].As()); - auto kwidth = info[2].As().Int32Value(); - auto kheight = info[3].As().Int32Value(); - auto anchorX = info[4].As().Int32Value(); - auto anchorY = info[5].As().Int32Value(); - auto borderType = info[6].As().Int32Value(); - cv::blur(src.mat_, dst.mat_, cv::Size(kwidth, kheight), cv::Point(anchorX, anchorY), borderType); - return info[1]; - } - static Napi::Value CopyMakeBorder(const Napi::CallbackInfo &info) - { - auto &src = GetMat(info[0].As()); - auto &dst = GetMat(info[1].As()); - auto top = info[2].As().Int32Value(); - auto bottom = info[3].As().Int32Value(); - auto left = info[4].As().Int32Value(); - auto right = info[5].As().Int32Value(); - auto borderType = info[6].As().Int32Value(); - cv::copyMakeBorder(src.mat_, dst.mat_, top, bottom, left, right, borderType); - return info[1]; - } - static Napi::Value Flip(const Napi::CallbackInfo &info) - { - auto &src = GetMat(info[0].As()); - auto &dst = GetMat(info[1].As()); - auto flipCode = info[2].As().Int32Value(); - cv::flip(src.mat_, dst.mat_, flipCode); - return info[1]; - } - static Napi::Value GetRotationMatrix2D(const Napi::CallbackInfo &info) - { - auto centerX = info[0].As().FloatValue(); - auto centerY = info[1].As().FloatValue(); - auto angle = info[2].As().DoubleValue(); - auto scale = info[3].As().DoubleValue(); - return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::getRotationMatrix2D(cv::Point2f(centerX, centerY), angle, scale); }); +NODE_FUNCTION_IMPL(Crop) +{ + auto &src = GetMat(info[0].As()); + auto &dst = GetMat(info[1].As()); + auto rangeArray = info[2].As(); + std::vector ranges(rangeArray.Length()); + for (int i = 0; i < ranges.size(); ++i) { + auto item = rangeArray.Get(i).As(); + auto start = item.Get("start").As().Int32Value(); + auto end = item.Get("end").As().Int32Value(); + ranges[i] = cv::Range(start, end); } + dst.mat_ = src.mat_(ranges); + return info[1]; +} +NODE_FUNCTION_IMPL(Resize) +{ + auto &src = GetMat(info[0].As()); + auto &dst = GetMat(info[1].As()); + auto width = info[2].As().Int32Value(); + auto height = info[3].As().Int32Value(); + auto fx = info[4].As().DoubleValue(); + auto fy = info[5].As().DoubleValue(); + auto interpolation = info[6].As().Int32Value(); + cv::resize(src.mat_, dst.mat_, cv::Size(width, height), fx, fy, interpolation); + return info[1]; +} +NODE_FUNCTION_IMPL(WarpAffine) +{ + auto &src = GetMat(info[0].As()); + auto &dst = GetMat(info[1].As()); + auto &transformMat = GetMat(info[2].As()); + auto dwidth = info[3].As().Int32Value(); + auto dheight = info[4].As().Int32Value(); + auto flags = info[5].As().Int32Value(); + auto borderMode = info[6].As().Int32Value(); + cv::warpAffine(src.mat_, dst.mat_, transformMat.mat_, cv::Size(dwidth, dheight), flags, borderMode); + return info[1]; +} +NODE_FUNCTION_IMPL(Blur) +{ + auto &src = GetMat(info[0].As()); + auto &dst = GetMat(info[1].As()); + auto kwidth = info[2].As().Int32Value(); + auto kheight = info[3].As().Int32Value(); + auto anchorX = info[4].As().Int32Value(); + auto anchorY = info[5].As().Int32Value(); + auto borderType = info[6].As().Int32Value(); + cv::blur(src.mat_, dst.mat_, cv::Size(kwidth, kheight), cv::Point(anchorX, anchorY), borderType); + return info[1]; +} +NODE_FUNCTION_IMPL(CopyMakeBorder) +{ + auto &src = GetMat(info[0].As()); + auto &dst = GetMat(info[1].As()); + auto top = info[2].As().Int32Value(); + auto bottom = info[3].As().Int32Value(); + auto left = info[4].As().Int32Value(); + auto right = info[5].As().Int32Value(); + auto borderType = info[6].As().Int32Value(); + cv::copyMakeBorder(src.mat_, dst.mat_, top, bottom, left, right, borderType); + return info[1]; +} +NODE_FUNCTION_IMPL(Flip) +{ + auto &src = GetMat(info[0].As()); + auto &dst = GetMat(info[1].As()); + auto flipCode = info[2].As().Int32Value(); + cv::flip(src.mat_, dst.mat_, flipCode); + return info[1]; +} +NODE_FUNCTION_IMPL(GetRotationMatrix2D) +{ + auto centerX = info[0].As().FloatValue(); + auto centerY = info[1].As().FloatValue(); + auto angle = info[2].As().DoubleValue(); + auto scale = info[3].As().DoubleValue(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::getRotationMatrix2D(cv::Point2f(centerX, centerY), angle, scale); }); +} - static Napi::Value Rectangle(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto x1 = info[1].As().Int32Value(); - auto y1 = info[2].As().Int32Value(); - auto x2 = info[3].As().Int32Value(); - auto y2 = info[4].As().Int32Value(); - auto b = info[5].As().DoubleValue(); - auto g = info[6].As().DoubleValue(); - auto r = info[7].As().DoubleValue(); - auto thickness = info[8].As().Int32Value(); - auto lineType = info[9].As().Int32Value(); - auto shift = info[10].As().Int32Value(); - cv::rectangle(img.mat_, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), thickness, lineType, shift); - return info.Env().Undefined(); +NODE_FUNCTION_IMPL(Rectangle) +{ + auto &img = GetMat(info[0].As()); + auto x1 = info[1].As().Int32Value(); + auto y1 = info[2].As().Int32Value(); + auto x2 = info[3].As().Int32Value(); + auto y2 = info[4].As().Int32Value(); + auto b = info[5].As().DoubleValue(); + auto g = info[6].As().DoubleValue(); + auto r = info[7].As().DoubleValue(); + auto thickness = info[8].As().Int32Value(); + auto lineType = info[9].As().Int32Value(); + auto shift = info[10].As().Int32Value(); + cv::rectangle(img.mat_, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), thickness, lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(Circle) +{ + auto &img = GetMat(info[0].As()); + auto x = info[1].As().Int32Value(); + auto y = info[2].As().Int32Value(); + auto radius = info[3].As().Int32Value(); + auto b = info[4].As().DoubleValue(); + auto g = info[5].As().DoubleValue(); + auto r = info[6].As().DoubleValue(); + auto thickness = info[7].As().Int32Value(); + auto lineType = info[8].As().Int32Value(); + auto shift = info[9].As().Int32Value(); + cv::circle(img.mat_, cv::Point(x, y), radius, cv::Scalar(b, g, r), thickness, lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(Line) +{ + auto &img = GetMat(info[0].As()); + auto x1 = info[1].As().Int32Value(); + auto y1 = info[2].As().Int32Value(); + auto x2 = info[3].As().Int32Value(); + auto y2 = info[4].As().Int32Value(); + auto b = info[5].As().DoubleValue(); + auto g = info[6].As().DoubleValue(); + auto r = info[7].As().DoubleValue(); + auto thickness = info[8].As().Int32Value(); + auto lineType = info[9].As().Int32Value(); + auto shift = info[10].As().Int32Value(); + cv::line(img.mat_, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), thickness, lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(Ellipse) +{ + auto &img = GetMat(info[0].As()); + auto x = info[1].As().Int32Value(); + auto y = info[2].As().Int32Value(); + auto width = info[3].As().Int32Value(); + auto height = info[4].As().Int32Value(); + auto angle = info[5].As().DoubleValue(); + auto startAngle = info[6].As().DoubleValue(); + auto endAngle = info[7].As().DoubleValue(); + auto b = info[8].As().DoubleValue(); + auto g = info[9].As().DoubleValue(); + auto r = info[10].As().DoubleValue(); + auto thickness = info[11].As().Int32Value(); + auto lineType = info[12].As().Int32Value(); + auto shift = info[13].As().Int32Value(); + cv::ellipse(img.mat_, cv::Point(x, y), cv::Size(width, height), angle, startAngle, endAngle, cv::Scalar(b, g, r), thickness, lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(Polylines) +{ + auto &img = GetMat(info[0].As()); + auto points_ = info[1].As(); + std::vector points(points_.Length()); + for (uint32_t i = 0; i < points_.Length(); i++) { + auto pt = points_.Get(i).As(); + points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); } - static Napi::Value Circle(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto x = info[1].As().Int32Value(); - auto y = info[2].As().Int32Value(); - auto radius = info[3].As().Int32Value(); - auto b = info[4].As().DoubleValue(); - auto g = info[5].As().DoubleValue(); - auto r = info[6].As().DoubleValue(); - auto thickness = info[7].As().Int32Value(); - auto lineType = info[8].As().Int32Value(); - auto shift = info[9].As().Int32Value(); - cv::circle(img.mat_, cv::Point(x, y), radius, cv::Scalar(b, g, r), thickness, lineType, shift); - return info.Env().Undefined(); + auto isClosed = info[2].As().Value(); + auto b = info[3].As().DoubleValue(); + auto g = info[4].As().DoubleValue(); + auto r = info[5].As().DoubleValue(); + auto thickness = info[6].As().Int32Value(); + auto lineType = info[7].As().Int32Value(); + auto shift = info[8].As().Int32Value(); + cv::polylines(img.mat_, points, isClosed, cv::Scalar(b, g, r), thickness, lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(FillPoly) +{ + auto &img = GetMat(info[0].As()); + auto points_ = info[1].As(); + std::vector points(points_.Length()); + for (uint32_t i = 0; i < points_.Length(); i++) { + auto pt = points_.Get(i).As(); + points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); } - static Napi::Value Line(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto x1 = info[1].As().Int32Value(); - auto y1 = info[2].As().Int32Value(); - auto x2 = info[3].As().Int32Value(); - auto y2 = info[4].As().Int32Value(); - auto b = info[5].As().DoubleValue(); - auto g = info[6].As().DoubleValue(); - auto r = info[7].As().DoubleValue(); - auto thickness = info[8].As().Int32Value(); - auto lineType = info[9].As().Int32Value(); - auto shift = info[10].As().Int32Value(); - cv::line(img.mat_, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), thickness, lineType, shift); - return info.Env().Undefined(); + auto b = info[2].As().DoubleValue(); + auto g = info[3].As().DoubleValue(); + auto r = info[4].As().DoubleValue(); + auto lineType = info[5].As().Int32Value(); + auto shift = info[6].As().Int32Value(); + cv::fillPoly(img.mat_, points, cv::Scalar(b, g, r), lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(FillConvexPoly) +{ + auto &img = GetMat(info[0].As()); + auto points_ = info[1].As(); + std::vector points(points_.Length()); + for (uint32_t i = 0; i < points_.Length(); i++) { + auto pt = points_.Get(i).As(); + points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); } - static Napi::Value Ellipse(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto x = info[1].As().Int32Value(); - auto y = info[2].As().Int32Value(); - auto width = info[3].As().Int32Value(); - auto height = info[4].As().Int32Value(); - auto angle = info[5].As().DoubleValue(); - auto startAngle = info[6].As().DoubleValue(); - auto endAngle = info[7].As().DoubleValue(); - auto b = info[8].As().DoubleValue(); - auto g = info[9].As().DoubleValue(); - auto r = info[10].As().DoubleValue(); - auto thickness = info[11].As().Int32Value(); - auto lineType = info[12].As().Int32Value(); - auto shift = info[13].As().Int32Value(); - cv::ellipse(img.mat_, cv::Point(x, y), cv::Size(width, height), angle, startAngle, endAngle, cv::Scalar(b, g, r), thickness, lineType, shift); - return info.Env().Undefined(); - } - static Napi::Value Polylines(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto points_ = info[1].As(); - std::vector points(points_.Length()); - for (uint32_t i = 0; i < points_.Length(); i++) { - auto pt = points_.Get(i).As(); - points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); - } - auto isClosed = info[2].As().Value(); - auto b = info[3].As().DoubleValue(); - auto g = info[4].As().DoubleValue(); - auto r = info[5].As().DoubleValue(); - auto thickness = info[6].As().Int32Value(); - auto lineType = info[7].As().Int32Value(); - auto shift = info[8].As().Int32Value(); - cv::polylines(img.mat_, points, isClosed, cv::Scalar(b, g, r), thickness, lineType, shift); - return info.Env().Undefined(); - } - static Napi::Value FillPoly(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto points_ = info[1].As(); - std::vector points(points_.Length()); - for (uint32_t i = 0; i < points_.Length(); i++) { - auto pt = points_.Get(i).As(); - points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); - } - auto b = info[2].As().DoubleValue(); - auto g = info[3].As().DoubleValue(); - auto r = info[4].As().DoubleValue(); - auto lineType = info[5].As().Int32Value(); - auto shift = info[6].As().Int32Value(); - cv::fillPoly(img.mat_, points, cv::Scalar(b, g, r), lineType, shift); - return info.Env().Undefined(); - } - static Napi::Value FillConvexPoly(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto points_ = info[1].As(); - std::vector points(points_.Length()); - for (uint32_t i = 0; i < points_.Length(); i++) { - auto pt = points_.Get(i).As(); - points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); - } - auto b = info[2].As().DoubleValue(); - auto g = info[3].As().DoubleValue(); - auto r = info[4].As().DoubleValue(); - auto lineType = info[5].As().Int32Value(); - auto shift = info[6].As().Int32Value(); - cv::fillConvexPoly(img.mat_, points, cv::Scalar(b, g, r), lineType, shift); - return info.Env().Undefined(); - } - static Napi::Value DrawMarker(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto x = info[1].As().Int32Value(); - auto y = info[2].As().Int32Value(); - auto b = info[3].As().DoubleValue(); - auto g = info[4].As().DoubleValue(); - auto r = info[5].As().DoubleValue(); - auto markerType = info[6].As().Int32Value(); - auto markerSize = info[7].As().Int32Value(); - auto lineType = info[8].As().Int32Value(); - auto shift = info[9].As().Int32Value(); - cv::drawMarker(img.mat_, cv::Point(x, y), cv::Scalar(b, g, r), markerType, markerSize, lineType, shift); - return info.Env().Undefined(); - } - static Napi::Value PutText(const Napi::CallbackInfo &info) - { - auto &img = GetMat(info[0].As()); - auto text = info[1].As().Utf8Value(); - auto x = info[2].As().Int32Value(); - auto y = info[3].As().Int32Value(); - auto fontFace = info[4].As().Int32Value(); - auto fontScale = info[5].As().DoubleValue(); - auto b = info[6].As().DoubleValue(); - auto g = info[7].As().DoubleValue(); - auto r = info[8].As().DoubleValue(); - auto thickness = info[9].As().Int32Value(); - auto lineType = info[10].As().Int32Value(); - auto shift = info[11].As().Int32Value(); - auto bottomLeftOrigin = info[12].As().Value(); - cv::putText(img.mat_, text, cv::Point(x, y), fontFace, fontScale, cv::Scalar(b, g, r), thickness, lineType, bottomLeftOrigin); - return info.Env().Undefined(); - } - - private: - inline static Napi::Object EmptyMat(Napi::Env env) { return constructor->New({}).As(); } - inline static CVMat &GetMat(Napi::Object obj) { return *ObjectWrap::Unwrap(obj); } - inline static Napi::Object CreateMat(Napi::Env env, std::function callback) - { - auto obj = EmptyMat(env); - callback(GetMat(obj)); - return obj; - } - - private: - cv::Mat mat_; -}; + auto b = info[2].As().DoubleValue(); + auto g = info[3].As().DoubleValue(); + auto r = info[4].As().DoubleValue(); + auto lineType = info[5].As().Int32Value(); + auto shift = info[6].As().Int32Value(); + cv::fillConvexPoly(img.mat_, points, cv::Scalar(b, g, r), lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(DrawMarker) +{ + auto &img = GetMat(info[0].As()); + auto x = info[1].As().Int32Value(); + auto y = info[2].As().Int32Value(); + auto b = info[3].As().DoubleValue(); + auto g = info[4].As().DoubleValue(); + auto r = info[5].As().DoubleValue(); + auto markerType = info[6].As().Int32Value(); + auto markerSize = info[7].As().Int32Value(); + auto lineType = info[8].As().Int32Value(); + auto shift = info[9].As().Int32Value(); + cv::drawMarker(img.mat_, cv::Point(x, y), cv::Scalar(b, g, r), markerType, markerSize, lineType, shift); + return info.Env().Undefined(); +} +NODE_FUNCTION_IMPL(PutText) +{ + auto &img = GetMat(info[0].As()); + auto text = info[1].As().Utf8Value(); + auto x = info[2].As().Int32Value(); + auto y = info[3].As().Int32Value(); + auto fontFace = info[4].As().Int32Value(); + auto fontScale = info[5].As().DoubleValue(); + auto b = info[6].As().DoubleValue(); + auto g = info[7].As().DoubleValue(); + auto r = info[8].As().DoubleValue(); + auto thickness = info[9].As().Int32Value(); + auto lineType = info[10].As().Int32Value(); + auto shift = info[11].As().Int32Value(); + auto bottomLeftOrigin = info[12].As().Value(); + cv::putText(img.mat_, text, cv::Point(x, y), fontFace, fontScale, cv::Scalar(b, g, r), thickness, lineType, bottomLeftOrigin); + return info.Env().Undefined(); +} void InitMatAPI(Napi::Env env, Napi::Object exports) { diff --git a/cxx/mat.h b/cxx/mat.h new file mode 100644 index 0000000..c72fa72 --- /dev/null +++ b/cxx/mat.h @@ -0,0 +1,79 @@ +#ifndef __CV_MAT_H__ +#define __CV_MAT_H__ + +#include "node.h" + +class CVMat : public Napi::ObjectWrap { + public: + friend class CVVideoCapture; + + public: + static Napi::Object Init(Napi::Env env, Napi::Object exports); + CVMat(const Napi::CallbackInfo &info); + static Napi::Value ImRead(const Napi::CallbackInfo &info); + static Napi::Value ImDecode(const Napi::CallbackInfo &info); + static Napi::Value ImWrite(const Napi::CallbackInfo &info); + static Napi::Value ImEncode(const Napi::CallbackInfo &info); + + Napi::Value IsEmpty(const Napi::CallbackInfo &info); + Napi::Value GetCols(const Napi::CallbackInfo &info); + Napi::Value GetRows(const Napi::CallbackInfo &info); + Napi::Value GetType(const Napi::CallbackInfo &info); + Napi::Value GetDepth(const Napi::CallbackInfo &info); + Napi::Value GetChannels(const Napi::CallbackInfo &info); + Napi::Value GetFlags(const Napi::CallbackInfo &info); + Napi::Value GetDims(const Napi::CallbackInfo &info); + Napi::Value GetIsContinuous(const Napi::CallbackInfo &info); + Napi::Value GetIsSubmatrix(const Napi::CallbackInfo &info); + Napi::Value GetElemSize(const Napi::CallbackInfo &info); + Napi::Value GetElemSize1(const Napi::CallbackInfo &info); + Napi::Value GetTotal(const Napi::CallbackInfo &info); + Napi::Value GetTotalWithDim(const Napi::CallbackInfo &info); + Napi::Value GetSize(const Napi::CallbackInfo &info); + Napi::Value GetData(const Napi::CallbackInfo &info); + + Napi::Value Col(const Napi::CallbackInfo &info); + Napi::Value ColRange(const Napi::CallbackInfo &info); + Napi::Value Row(const Napi::CallbackInfo &info); + Napi::Value RowRange(const Napi::CallbackInfo &info); + Napi::Value Diag(const Napi::CallbackInfo &info); + + + Napi::Value Clone(const Napi::CallbackInfo &info); + Napi::Value CopyTo(const Napi::CallbackInfo &info); + + static Napi::Value Crop(const Napi::CallbackInfo &info); + static Napi::Value Resize(const Napi::CallbackInfo &info); + static Napi::Value WarpAffine(const Napi::CallbackInfo &info); + static Napi::Value Blur(const Napi::CallbackInfo &info); + static Napi::Value CopyMakeBorder(const Napi::CallbackInfo &info); + static Napi::Value Flip(const Napi::CallbackInfo &info); + static Napi::Value GetRotationMatrix2D(const Napi::CallbackInfo &info); + + + static Napi::Value Rectangle(const Napi::CallbackInfo &info); + static Napi::Value Circle(const Napi::CallbackInfo &info); + static Napi::Value Line(const Napi::CallbackInfo &info); + static Napi::Value Ellipse(const Napi::CallbackInfo &info); + static Napi::Value Polylines(const Napi::CallbackInfo &info); + static Napi::Value FillPoly(const Napi::CallbackInfo &info); + static Napi::Value FillConvexPoly(const Napi::CallbackInfo &info); + static Napi::Value DrawMarker(const Napi::CallbackInfo &info); + static Napi::Value PutText(const Napi::CallbackInfo &info); + + private: + inline static Napi::Object EmptyMat(Napi::Env env) { return constructor->New({}).As(); } + inline static CVMat &GetMat(Napi::Object obj) { return *ObjectWrap::Unwrap(obj); } + inline static Napi::Object CreateMat(Napi::Env env, std::function callback) + { + auto obj = EmptyMat(env); + callback(GetMat(obj)); + return obj; + } + + private: + static Napi::FunctionReference *constructor; + cv::Mat mat_; +}; + +#endif \ No newline at end of file diff --git a/cxx/node.cc b/cxx/node.cc index c3087eb..3631847 100644 --- a/cxx/node.cc +++ b/cxx/node.cc @@ -5,7 +5,8 @@ static Napi::Object Init(Napi::Env env, Napi::Object exports) { InitMatAPI(env, exports); - InitProcAPI(env, exports); + InitVideoCaptureAPI(env, exports); + InitUtilAPI(env, exports); return exports; } NODE_API_MODULE(addon, Init); \ No newline at end of file diff --git a/cxx/node.h b/cxx/node.h index 1913d0f..0602b38 100644 --- a/cxx/node.h +++ b/cxx/node.h @@ -4,7 +4,11 @@ #include #include +#define NODE_FUNCTION(name) Napi::Value name(const CallbackInfo &info) +#define NODE_FUNCTION_STATIC(name) static Napi::Value name(const CallbackInfo &info) + void InitMatAPI(Napi::Env env, Napi::Object exports); -void InitProcAPI(Napi::Env env, Napi::Object exports); +void InitVideoCaptureAPI(Napi::Env env, Napi::Object exports); +void InitUtilAPI(Napi::Env env, Napi::Object exports); #endif diff --git a/cxx/util.cc b/cxx/util.cc index 913333f..5b1c51b 100644 --- a/cxx/util.cc +++ b/cxx/util.cc @@ -27,7 +27,7 @@ DEFINE_FUNCTION(GetFontScaleFromHeight) return Number::New(info.Env(), cv::getFontScaleFromHeight(fontFace, pixelHeight, thickness)); } -void InitProcAPI(Napi::Env env, Napi::Object exports) +void InitUtilAPI(Napi::Env env, Napi::Object exports) { auto obj = Object::New(env); ADD_PROC_FUNCTION(GetTextSize); diff --git a/cxx/videocap.cc b/cxx/videocap.cc new file mode 100644 index 0000000..05ab369 --- /dev/null +++ b/cxx/videocap.cc @@ -0,0 +1,113 @@ +#include +#include +#include "node.h" +#include "mat.h" +#include "videocap.h" + +using namespace Napi; + +#define MAT_INSTANCE_METHOD(method) InstanceMethod<&CVVideoCapture::method>(#method, static_cast(napi_writable | napi_configurable)) +#define MAT_STATIC_METHOD(method) StaticMethod<&CVVideoCapture::method>(#method, static_cast(napi_writable | napi_configurable)) + + +FunctionReference *CVVideoCapture::constructor = nullptr; + +Napi::Object CVVideoCapture::Init(Napi::Env env, Napi::Object exports) +{ + Function func = DefineClass(env, "VideoCapture", { + MAT_INSTANCE_METHOD(Open), + MAT_INSTANCE_METHOD(IsOpened), + MAT_INSTANCE_METHOD(Grab), + MAT_INSTANCE_METHOD(Retrieve), + MAT_INSTANCE_METHOD(Read), + MAT_INSTANCE_METHOD(Set), + MAT_INSTANCE_METHOD(Get), + MAT_INSTANCE_METHOD(GetBackendName), + }); + constructor = new FunctionReference(); + *constructor = Napi::Persistent(func); + exports.Set("VideoCapture", func); + env.SetInstanceData(constructor); + return exports; +} + +CVVideoCapture::CVVideoCapture(const Napi::CallbackInfo &info) + : ObjectWrap(info) +{ + if (info[0].IsString()) { + auto apiPreference = (info.Length() > 1) ? info[1].As().Int32Value() : cv::CAP_ANY; + std::vector params; + if (info.Length() > 2) { + auto paramsArray = info[2].As(); + params.resize(paramsArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramsArray.Get(i).As().Int32Value(); + } + capture_ = cv::VideoCapture(info[0].As().Utf8Value(), apiPreference, params); + } + else if (info[0].IsNumber()) { + auto index = info[0].As().Int32Value(); + auto apiPreference = (info.Length() > 1) ? info[1].As().Int32Value() : cv::CAP_ANY; + std::vector params; + if (info.Length() > 2) { + auto paramsArray = info[2].As(); + params.resize(paramsArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramsArray.Get(i).As().Int32Value(); + } + capture_ = cv::VideoCapture(index, apiPreference, params); + } + else capture_ = cv::VideoCapture(); +} + +Napi::Value CVVideoCapture::Open(const Napi::CallbackInfo &info) +{ + bool res = false; + auto apiPreference = (info.Length() > 1) ? info[1].As().Int32Value() : cv::CAP_ANY; + std::vector params; + if (info.Length() > 2) { + auto paramsArray = info[2].As(); + params.resize(paramsArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramsArray.Get(i).As().Int32Value(); + } + if (info[0].IsString()) res = capture_.open(info[0].As().Utf8Value(), apiPreference, params); + else if (info[0].IsNumber()) res = capture_.open(info[0].As().Int32Value(), apiPreference, params); + return Boolean::New(info.Env(), res); +} +Napi::Value CVVideoCapture::IsOpened(const Napi::CallbackInfo &info) +{ + return Boolean::New(info.Env(), capture_.isOpened()); +} +Napi::Value CVVideoCapture::Grab(const Napi::CallbackInfo &info) +{ + return Boolean::New(info.Env(), capture_.grab()); +} +Napi::Value CVVideoCapture::Retrieve(const Napi::CallbackInfo &info) +{ + auto flag = info[0].As().Int32Value(); + return CVMat::CreateMat(info.Env(), [&](CVMat &mat) { + capture_.retrieve(mat.mat_, flag); + }); +} +Napi::Value CVVideoCapture::Read(const Napi::CallbackInfo &info) +{ + return CVMat::CreateMat(info.Env(), [&](CVMat &mat) { + capture_.read(mat.mat_); + }); +} +Napi::Value CVVideoCapture::Set(const Napi::CallbackInfo &info) +{ + capture_.set(info[0].As().Int32Value(), info[1].As().DoubleValue()); +} +Napi::Value CVVideoCapture::Get(const Napi::CallbackInfo &info) +{ + return Number::New(info.Env(), capture_.get(info[0].As().Int32Value())); +} +Napi::Value CVVideoCapture::GetBackendName(const Napi::CallbackInfo &info) +{ + return String::New(info.Env(), capture_.getBackendName()); +} + + +void InitVideoCaptureAPI(Napi::Env env, Napi::Object exports) +{ + CVVideoCapture::Init(env, exports); +} \ No newline at end of file diff --git a/cxx/videocap.h b/cxx/videocap.h new file mode 100644 index 0000000..fc6ea7a --- /dev/null +++ b/cxx/videocap.h @@ -0,0 +1,26 @@ +#ifndef __CV_VIDEO_CAP_H__ +#define __CV_VIDEO_CAP_H__ + +#include "node.h" + +class CVVideoCapture : public Napi::ObjectWrap { + public: + static Napi::Object Init(Napi::Env env, Napi::Object exports); + + CVVideoCapture(const Napi::CallbackInfo &info); + + Napi::Value Open(const Napi::CallbackInfo &info); + Napi::Value IsOpened(const Napi::CallbackInfo &info); + Napi::Value Grab(const Napi::CallbackInfo &info); + Napi::Value Retrieve(const Napi::CallbackInfo &info); + Napi::Value Read(const Napi::CallbackInfo &info); + Napi::Value Set(const Napi::CallbackInfo &info); + Napi::Value Get(const Napi::CallbackInfo &info); + Napi::Value GetBackendName(const Napi::CallbackInfo &info); + + private: + static Napi::FunctionReference *constructor; + cv::VideoCapture capture_; +}; + +#endif \ No newline at end of file diff --git a/package.json b/package.json index 674d816..d2ec11b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@yizhi/cv", - "version": "1.0.2", - "releaseVersion": "1.0.2", + "version": "1.0.3", + "releaseVersion": "1.0.3", "main": "dist/index.js", "types": "typing/index.d.ts", "scripts": { diff --git a/src/cv/addon.ts b/src/cv/addon.ts index 3a2fe4e..2d1388a 100644 --- a/src/cv/addon.ts +++ b/src/cv/addon.ts @@ -14,9 +14,11 @@ export function getAddon() { return require(config.ADDON_PATH); } +console.log(getAddon()) + export function getConfig(name: N): AddonConfig[N] { return config[name]; } export function setConfig(name: N, value: AddonConfig[N]) { config[name] = value; } export function CVMat() { return getAddon().Mat; } - +export function CVVideoCapture() { return getAddon().VideoCapture; } export function CVUtil() { return getAddon().util; } diff --git a/src/cv/common.ts b/src/cv/common.ts index 848180e..5259400 100644 --- a/src/cv/common.ts +++ b/src/cv/common.ts @@ -1,10 +1,14 @@ export { getConfig as C } from "./addon"; import { Mat } from "./mat"; +import { VideoCapture } from "./videocapture"; export type TypedArray = Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array | BigUint64Array | BigInt64Array | Float32Array | Float64Array -export function FromCV(mat: any): Mat { return new (Mat as any)(mat); } +export function __Mat(mat: any): Mat { return new (Mat as any)(mat); } +export function __VideoCapture(cap: any): VideoCapture { return new (VideoCapture as any)(cap); } + export function M(mat: Mat) { return (mat as any).__mat__; } +export function VC(cap: VideoCapture) { return (cap as any).__cap__; } export function resolveArgs>(args: any[], checker: boolean | (() => boolean)): R { if (typeof checker === "function") checker = checker(); diff --git a/src/cv/consts.ts b/src/cv/consts.ts index 3b98369..0218486 100644 --- a/src/cv/consts.ts +++ b/src/cv/consts.ts @@ -164,3 +164,114 @@ export const MARKER_DIAMOND = 3; export const MARKER_SQUARE = 4; export const MARKER_TRIANGLE_UP = 5; export const MARKER_TRIANGLE_DOWN = 6; + + +export const CAP_ANY = 0; +export const CAP_VFW = 200; +export const CAP_V4L = 200; +export const CAP_V4L2 = CAP_V4L; +export const CAP_FIREWIRE = 300; +export const CAP_FIREWARE = CAP_FIREWIRE; +export const CAP_IEEE1394 = CAP_FIREWIRE; +export const CAP_DC1394 = CAP_FIREWIRE; +export const CAP_CMU1394 = CAP_FIREWIRE; +export const CAP_QT = 500; +export const CAP_UNICAP = 600; +export const CAP_DSHOW = 700; +export const CAP_PVAPI = 800; +export const CAP_OPENNI = 900; +export const CAP_OPENNI_ASUS = 910; +export const CAP_ANDROID = 1000; +export const CAP_XIAPI = 1100; +export const CAP_AVFOUNDATION = 1200; +export const CAP_GIGANETIX = 1300; +export const CAP_MSMF = 1400; +export const CAP_WINRT = 1410; +export const CAP_INTELPERC = 1500; +export const CAP_REALSENSE = 1500; +export const CAP_OPENNI2 = 1600; +export const CAP_OPENNI2_ASUS = 1610; +export const CAP_OPENNI2_ASTRA = 1620; +export const CAP_GPHOTO2 = 1700; +export const CAP_GSTREAMER = 1800; +export const CAP_FFMPEG = 1900; +export const CAP_IMAGES = 2000; +export const CAP_ARAVIS = 2100; +export const CAP_OPENCV_MJPEG = 2200; +export const CAP_INTEL_MFX = 2300; +export const CAP_XINE = 2400; +export const CAP_UEYE = 2500; +export const CAP_OBSENSOR = 2600; + +export const CAP_PROP_POS_MSEC = 0; +export const CAP_PROP_POS_FRAMES = 1; +export const CAP_PROP_POS_AVI_RATIO = 2; +export const CAP_PROP_FRAME_WIDTH = 3; +export const CAP_PROP_FRAME_HEIGHT = 4; +export const CAP_PROP_FPS = 5; +export const CAP_PROP_FOURCC = 6; +export const CAP_PROP_FRAME_COUNT = 7; +export const CAP_PROP_FORMAT = 8; +export const CAP_PROP_MODE = 9; +export const CAP_PROP_BRIGHTNESS = 10; +export const CAP_PROP_CONTRAST = 11; +export const CAP_PROP_SATURATION = 12; +export const CAP_PROP_HUE = 13; +export const CAP_PROP_GAIN = 14; +export const CAP_PROP_EXPOSURE = 15; +export const CAP_PROP_CONVERT_RGB = 16; +export const CAP_PROP_WHITE_BALANCE_BLUE_U = 17; +export const CAP_PROP_RECTIFICATION = 18; +export const CAP_PROP_MONOCHROME = 1; +export const CAP_PROP_SHARPNESS = 2; +export const CAP_PROP_AUTO_EXPOSURE = 21; +export const CAP_PROP_GAMMA = 2; +export const CAP_PROP_TEMPERATURE = 2; +export const CAP_PROP_TRIGGER = 2; +export const CAP_PROP_TRIGGER_DELAY = 2; +export const CAP_PROP_WHITE_BALANCE_RED_V = 2; +export const CAP_PROP_ZOOM = 2; +export const CAP_PROP_FOCUS = 2; +export const CAP_PROP_GUID = 2; +export const CAP_PROP_ISO_SPEED = 3; +export const CAP_PROP_BACKLIGHT = 3; +export const CAP_PROP_PAN = 3; +export const CAP_PROP_TILT = 3; +export const CAP_PROP_ROLL = 3; +export const CAP_PROP_IRIS = 3; +export const CAP_PROP_SETTINGS = 37; +export const CAP_PROP_BUFFERSIZE = 3; +export const CAP_PROP_AUTOFOCUS = 3; +export const CAP_PROP_SAR_NUM = 40; +export const CAP_PROP_SAR_DEN = 41; +export const CAP_PROP_BACKEND = 42; +export const CAP_PROP_CHANNEL = 43; +export const CAP_PROP_AUTO_WB = 44; +export const CAP_PROP_WB_TEMPERATURE = 45; +export const CAP_PROP_CODEC_PIXEL_FORMAT = 46; +export const CAP_PROP_BITRATE = 47; +export const CAP_PROP_ORIENTATION_META = 48; +export const CAP_PROP_ORIENTATION_AUTO = 49; +export const CAP_PROP_HW_ACCELERATION = 50; +export const CAP_PROP_HW_DEVICE = 51; +export const CAP_PROP_HW_ACCELERATION_USE_OPENCL = 52; +export const CAP_PROP_OPEN_TIMEOUT_MSEC = 53; +export const CAP_PROP_READ_TIMEOUT_MSEC = 54; +export const CAP_PROP_STREAM_OPEN_TIME_USEC = 55; +export const CAP_PROP_VIDEO_TOTAL_CHANNELS = 5; +export const CAP_PROP_VIDEO_STREAM = 5; +export const CAP_PROP_AUDIO_STREAM = 5; +export const CAP_PROP_AUDIO_POS = 5; +export const CAP_PROP_AUDIO_SHIFT_NSEC = 6; +export const CAP_PROP_AUDIO_DATA_DEPTH = 6; +export const CAP_PROP_AUDIO_SAMPLES_PER_SECOND = 6; +export const CAP_PROP_AUDIO_BASE_INDEX = 6; +export const CAP_PROP_AUDIO_TOTAL_CHANNELS = 6; +export const CAP_PROP_AUDIO_TOTAL_STREAMS = 6; +export const CAP_PROP_AUDIO_SYNCHRONIZE = 6; +export const CAP_PROP_LRF_HAS_KEY_FRAME = 6; +export const CAP_PROP_CODEC_EXTRADATA_INDEX = 6; +export const CAP_PROP_FRAME_TYPE = 6; +export const CAP_PROP_N_THREADS = 7; +export const CAP_PROP_PTS = 7; +export const CAP_PROP_DTS_DELAY = 7; diff --git a/src/cv/index.ts b/src/cv/index.ts index 65ce3d9..7f7dd66 100644 --- a/src/cv/index.ts +++ b/src/cv/index.ts @@ -1,6 +1,7 @@ export { setConfig as config } from "./addon"; export { downloadAddon } from "./download"; export * from "./mat"; +export * from "./videocapture"; export * from "./consts"; export * from "./proc"; export * from "./draw"; diff --git a/src/cv/mat.ts b/src/cv/mat.ts index 304f266..b99c279 100644 --- a/src/cv/mat.ts +++ b/src/cv/mat.ts @@ -1,5 +1,5 @@ import { CVMat } from "./addon"; -import { FromCV, M, TypedArray } from "./common"; +import { __Mat, M, TypedArray } from "./common"; import { IMREAD_COLOR_BGR } from "./consts"; @@ -40,18 +40,18 @@ export class Mat { else return this.copyTo(this.clone()).data; } - public clone() { return FromCV(this.#mat.Clone()); } + public clone() { return __Mat(this.#mat.Clone()); } public copyTo(mat: Mat) { return this.#mat.CopyTo(M(mat)), mat; } - public row(y: number) { return FromCV(this.#mat.Row(y)); } - public col(x: number) { return FromCV(this.#mat.Col(x)); } - public rowRange(start: number, end: number) { return FromCV(this.#mat.RowRange(start, end)); } - public colRange(start: number, end: number) { return FromCV(this.#mat.ColRange(start, end)); } - public diag(d: number) { return FromCV(this.#mat.Diag(d)); } + public row(y: number) { return __Mat(this.#mat.Row(y)); } + public col(x: number) { return __Mat(this.#mat.Col(x)); } + public rowRange(start: number, end: number) { return __Mat(this.#mat.RowRange(start, end)); } + public colRange(start: number, end: number) { return __Mat(this.#mat.ColRange(start, end)); } + public diag(d: number) { return __Mat(this.#mat.Diag(d)); } }; -export function imread(filename: string, flag?: number) { return FromCV(CVMat().ImRead(filename, flag ?? IMREAD_COLOR_BGR)); } -export function imdecode(data: Uint8Array, flag?: number) { return FromCV(CVMat().ImDecode(data, flag ?? IMREAD_COLOR_BGR)); } +export function imread(filename: string, flag?: number) { return __Mat(CVMat().ImRead(filename, flag ?? IMREAD_COLOR_BGR)); } +export function imdecode(data: Uint8Array, flag?: number) { return __Mat(CVMat().ImDecode(data, flag ?? IMREAD_COLOR_BGR)); } export function imwrite(filename: string, mat: Mat, params?: number[]): boolean { return CVMat().ImWrite(M(mat), filename, params); } export function imencode(ext: string, mat: Mat, params?: number[]): Uint8Array | null { const res = CVMat().ImEncode(M(mat), ext, params); diff --git a/src/cv/proc.ts b/src/cv/proc.ts index 5265700..728296f 100644 --- a/src/cv/proc.ts +++ b/src/cv/proc.ts @@ -1,5 +1,5 @@ import { Mat } from "./mat"; -import { FromCV, M, resolveArgs } from "./common"; +import { __Mat, M, resolveArgs } from "./common"; import { BORDER_CONSTANT, BORDER_DEFAULT, INTER_LINEAR } from "./consts"; import { CVMat } from "./addon"; @@ -9,14 +9,14 @@ export function crop(src: Mat, dst: Mat, range: CropRange): Mat; export function crop(...args: any[]) { let [src, dst, range] = resolveArgs<[src: Mat, dst: Mat, range: CropRange]>(args, args[1] instanceof Mat); if (!(range instanceof Array)) range = [{ start: range.y, end: range.y + range.height }, { start: range.x, end: range.x + range.width }]; - return FromCV(CVMat().Crop(M(src), M(dst), range)); + return __Mat(CVMat().Crop(M(src), M(dst), range)); } export function resize(src: Mat, width: number, height: number, fx?: number, fy?: number, interpolation?: number): Mat; export function resize(src: Mat, dst: Mat, width: number, height: number, fx?: number, fy?: number, interpolation?: number): Mat; export function resize(...args: any[]) { const [src, dst, width, height, fx, fy, interpolation] = resolveArgs<[src: Mat, dst: Mat, width: number, height: number, fx?: number, fy?: number, interpolation?: number]>(args, args[1] instanceof Mat); - return FromCV(CVMat().Resize(M(src), M(dst), width, height, fx ?? 0, fy ?? 0, interpolation ?? INTER_LINEAR)); + return __Mat(CVMat().Resize(M(src), M(dst), width, height, fx ?? 0, fy ?? 0, interpolation ?? INTER_LINEAR)); } export function blur(src: Mat, kernelWidth: number, kernelHeight: number, anchorX?: number, anchorY?: number, borderType?: number): Mat @@ -24,14 +24,14 @@ export function blur(src: Mat, dst: Mat, kernelWidth: number, kernelHeight: numb export function blur(...args: any[]) { let [src, dst, kernelWidth, kernelHeight, anchorX = -1, anchorY = -1, borderType = BORDER_DEFAULT] = resolveArgs<[src: Mat, dst: Mat, kernelWidth: number, kernelHeight: number, anchorX?: number, anchorY?: number, borderType?: number]>(args, args[1] instanceof Mat); if (anchorX == -1 || anchorY == -1) anchorX = anchorY = -1; - return FromCV(CVMat().Blur(M(src), M(dst), kernelWidth, kernelHeight, anchorX, anchorY, borderType)); + return __Mat(CVMat().Blur(M(src), M(dst), kernelWidth, kernelHeight, anchorX, anchorY, borderType)); } export function copyMakeBorder(src: Mat, top: number, bottom: number, left: number, right: number, borderType: number): Mat export function copyMakeBorder(src: Mat, dst: Mat, top: number, bottom: number, left: number, right: number, borderType: number): Mat export function copyMakeBorder(...args: any[]) { const [src, dst, top, bottom, left, right, borderType] = resolveArgs<[src: Mat, dst: Mat, top: number, bottom: number, left: number, right: number, borderType: number]>(args, args[1] instanceof Mat); - return FromCV(CVMat().CopyMakeBorder(M(src), M(dst), top, bottom, left, right, borderType)); + return __Mat(CVMat().CopyMakeBorder(M(src), M(dst), top, bottom, left, right, borderType)); } type FlipCode = 0 | 1 | -1 | "x" | "y" | "both"; @@ -40,14 +40,14 @@ export function flip(src: Mat, dst: Mat, flipCode: FlipCode): Mat export function flip(...args: any[]) { let [src, dst, flipCode] = resolveArgs<[src: Mat, dst: Mat, flipCode: FlipCode]>(args, args[1] instanceof Mat); if (typeof flipCode == "string") flipCode = ({ x: 0, y: 1, both: -1 } as Record<"x" | "y" | "both", 0 | 1 | -1>)[flipCode]; - return FromCV(CVMat().Flip(M(src), M(dst), flipCode)); + return __Mat(CVMat().Flip(M(src), M(dst), flipCode)); } export function warpAffine(src: Mat, transfromMat: Mat, dwidth: number, dheight: number, flags?: number, borderMode?: number): Mat; export function warpAffine(src: Mat, dst: Mat, transfromMat: Mat, dwidth: number, dheight: number, flags?: number, borderMode?: number): Mat; export function warpAffine(...args: any[]) { const [src, dst, transfromMat, dwidth, dheight, flags, borderMode] = resolveArgs<[src: Mat, dst: Mat, transfromMat: Mat, dwidth: number, dheight: number, flags?: number, borderMode?: number]>(args, () => args[2] instanceof Mat); - return FromCV(CVMat().WarpAffine(M(src), M(dst), M(transfromMat), dwidth, dheight, flags ?? INTER_LINEAR, borderMode ?? BORDER_CONSTANT)); + return __Mat(CVMat().WarpAffine(M(src), M(dst), M(transfromMat), dwidth, dheight, flags ?? INTER_LINEAR, borderMode ?? BORDER_CONSTANT)); } -export function getRotationMatrix2D(centerX: number, centerY: number, angle: number, scale: number) { return FromCV(CVMat().GetRotationMatrix2D(centerX, centerY, angle, scale)); } +export function getRotationMatrix2D(centerX: number, centerY: number, angle: number, scale: number) { return __Mat(CVMat().GetRotationMatrix2D(centerX, centerY, angle, scale)); } diff --git a/src/cv/videocapture.ts b/src/cv/videocapture.ts new file mode 100644 index 0000000..a4999c7 --- /dev/null +++ b/src/cv/videocapture.ts @@ -0,0 +1,33 @@ +import { CVVideoCapture } from "./addon"; +import { __Mat } from "./common"; +import { CAP_ANY } from "./consts"; + +export class VideoCapture { + #cap: any + + constructor(source: string | number, apiPreference?: number, params?: number[]) { + const CVCap = CVVideoCapture(); + if ((source as any) instanceof CVCap) this.#cap = source; + else this.#cap = new CVCap(source, apiPreference ?? CAP_ANY, params ?? []); + } + + private get __cap__() { return this.#cap; } + + public open(source: string | number, apiPreference?: number, params?: number[]): boolean { return this.#cap.Open(source, apiPreference ?? CAP_ANY, params ?? []); } + public get isOpened(): boolean { return this.#cap.IsOpened(); } + + public grab(): boolean { return this.#cap.Grab(); } + + public retrieve(flag?: number) { return __Mat(this.#cap.Retrieve(flag ?? 0)); } + public read() { return __Mat(this.#cap.Read()); } + + public get backendName(): string { return this.#cap.GetBackendName(); } + + public prop(propId: number, value: number): void + public prop(propId: number): number + public prop(propId: number, value?: number) { + if (typeof value === "undefined") return this.#cap.Get(propId); + else this.#cap.Set(propId, value); + } + +} diff --git a/src/test.ts b/src/test.ts index 161365e..ae76e11 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,7 +1,7 @@ import cv from "."; -async function test() { +async function testImage() { await cv.downloadAddon(); const fs = await import("fs"); // const buffer = fs.readFileSync("data/im1.jpeg") @@ -32,5 +32,14 @@ async function test() { cv.imwrite("test_data/draw.jpg", res); } +async function testMovie() { + const cap = new cv.VideoCapture("test_data/movie.mp4"); + if(cap.grab()){ + const im = cap.retrieve(); + debugger; + } + const im = cap.read(); + debugger +} -test(); \ No newline at end of file +testMovie(); \ No newline at end of file