Files
ai-box/cxx/cv/node.cc
2025-03-06 14:38:32 +08:00

146 lines
4.9 KiB
C++

#include <iostream>
#include <functional>
#include <opencv2/opencv.hpp>
#include "node.h"
using namespace Napi;
#define MAT_INSTANCE_METHOD(method) InstanceMethod<&CVMat::method>(#method, static_cast<napi_property_attributes>(napi_writable | napi_configurable))
static FunctionReference *constructor = nullptr;
class CVMat : public ObjectWrap<CVMat> {
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports)
{
Function func = DefineClass(env, "Mat", {
MAT_INSTANCE_METHOD(IsEmpty),
MAT_INSTANCE_METHOD(GetCols),
MAT_INSTANCE_METHOD(GetRows),
MAT_INSTANCE_METHOD(GetChannels),
MAT_INSTANCE_METHOD(Resize),
MAT_INSTANCE_METHOD(Crop),
MAT_INSTANCE_METHOD(Rotate),
MAT_INSTANCE_METHOD(Clone),
MAT_INSTANCE_METHOD(DrawCircle),
MAT_INSTANCE_METHOD(Data),
MAT_INSTANCE_METHOD(Encode),
});
constructor = new FunctionReference();
*constructor = Napi::Persistent(func);
exports.Set("Mat", func);
env.SetInstanceData<FunctionReference>(constructor);
return exports;
}
CVMat(const CallbackInfo &info)
: ObjectWrap<CVMat>(info)
{
int mode = cv::IMREAD_COLOR_BGR;
if (info.Length() > 1 && info[1].IsObject()) {
Object options = info[1].As<Object>();
if (options.Has("mode") && options.Get("mode").IsNumber()) mode = options.Get("mode").As<Number>().Int32Value();
}
if (info[0].IsString()) im_ = cv::imread(info[0].As<String>().Utf8Value(), mode);
else if (info[0].IsTypedArray()) {
auto buffer = info[0].As<TypedArray>().ArrayBuffer();
uint8_t *bufferPtr = static_cast<uint8_t *>(buffer.Data());
std::vector<uint8_t> data(bufferPtr, bufferPtr + buffer.ByteLength());
im_ = cv::imdecode(data, mode);
}
}
~CVMat() { im_.release(); }
Napi::Value IsEmpty(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), im_.empty()); }
Napi::Value GetCols(const Napi::CallbackInfo &info) { return Number::New(info.Env(), im_.cols); }
Napi::Value GetRows(const Napi::CallbackInfo &info) { return Number::New(info.Env(), im_.rows); }
Napi::Value GetChannels(const Napi::CallbackInfo &info) { return Number::New(info.Env(), im_.channels()); }
Napi::Value Resize(const Napi::CallbackInfo &info)
{
return CreateMat(info.Env(), [this, &info](auto &mat) { cv::resize(im_, mat.im_, cv::Size(info[0].As<Number>().Int32Value(), info[1].As<Number>().Int32Value())); });
}
Napi::Value Crop(const Napi::CallbackInfo &info)
{
return CreateMat(info.Env(), [this, &info](auto &mat) {
mat.im_ = im_(cv::Rect(
info[0].As<Number>().Int32Value(), info[1].As<Number>().Int32Value(),
info[2].As<Number>().Int32Value(), info[3].As<Number>().Int32Value()));
});
}
Napi::Value Rotate(const Napi::CallbackInfo &info)
{
return CreateMat(info.Env(), [this, &info](auto &mat) {
auto x = info[0].As<Number>().DoubleValue();
auto y = info[1].As<Number>().DoubleValue();
auto angle = info[2].As<Number>().DoubleValue();
cv::Mat rotation_matix = cv::getRotationMatrix2D(cv::Point2f(x, y), angle, 1.0);
cv::warpAffine(im_, mat.im_, rotation_matix, im_.size());
});
}
Napi::Value Clone(const Napi::CallbackInfo &info)
{
return CreateMat(info.Env(), [this, &info](auto &mat) { mat.im_ = im_.clone(); });
}
Napi::Value DrawCircle(const Napi::CallbackInfo &info)
{
int x = info[0].As<Number>().Int32Value();
int y = info[1].As<Number>().Int32Value();
int radius = info[2].As<Number>().Int32Value();
int b = info[3].As<Number>().Int32Value();
int g = info[4].As<Number>().Int32Value();
int r = info[5].As<Number>().Int32Value();
int thickness = info[6].As<Number>().Int32Value();
int lineType = info[7].As<Number>().Int32Value();
int shift = info[8].As<Number>().Int32Value();
cv::circle(im_, cv::Point(x, y), radius, cv::Scalar(b, g, r), thickness, lineType, shift);
return info.Env().Undefined();
}
Napi::Value Data(const Napi::CallbackInfo &info) { return ArrayBuffer::New(info.Env(), im_.ptr(), im_.elemSize() * im_.total()); }
Napi::Value Encode(const Napi::CallbackInfo &info)
{
auto options = info[0].As<Object>();
auto extname = options.Get("extname").As<String>().Utf8Value();
cv::imencode(extname, im_, encoded_);
return ArrayBuffer::New(info.Env(), encoded_.data(), encoded_.size());
}
private:
inline Napi::Object EmptyMat(Napi::Env env) { return constructor->New({}).As<Object>(); }
inline CVMat &GetMat(Napi::Object obj) { return *ObjectWrap<CVMat>::Unwrap(obj); }
inline Napi::Object CreateMat(Napi::Env env, std::function<void(CVMat &mat)> callback)
{
auto obj = EmptyMat(env);
callback(GetMat(obj));
return obj;
}
private:
cv::Mat im_;
std::vector<uint8_t> encoded_;
};
void InstallOpenCVAPI(Env env, Object exports)
{
CVMat::Init(env, exports);
}
#ifdef USE_OPENCV
static Object Init(Env env, Object exports)
{
InstallOpenCVAPI(env, exports);
return exports;
}
NODE_API_MODULE(addon, Init)
#endif