UrielCh/opencv-sandbox

Conversion note.

Opened this issue · 8 comments

jsopencv_to method are generated by the macro:

CV_JS_TO_CLASS(TYPE)
CV_JS_TO_CLASS_PTR(TYPE)
CV_JS_TO_ENUM(TYPE)

Those Marco are called from:

  • opencv/modules/core/misc/python/pyopencv_async.hpp
  • opencv/modules/core/misc/python/pyopencv_cuda.hpp
  • opencv/modules/core/misc/python/pyopencv_umat.hpp
  • opencv/modules/core/misc/python/shadow_umat.hpp

image

CURRENT ERROR:

#define CV_JS_TO_ENUM(TYPE) template<> bool jsopencv_to(const Napi::CallbackInfo &info, const Napi::Value* dst, TYPE& src, const ArgInfo& argInfo) { if (dst.IsNull() || dst.IsUndefined()) return true; int underlying = 0; if (!jsopencv_to(info, dst, underlying, argInfo)) return false; src = static_cast<TYPE>(underlying); return true; }
CV_PY_TO_ENUM
Expands to:

template<> bool jsopencv_to(const Napi::CallbackInfo &info, const Napi::Value* dst, AKAZE_DescriptorType& src, const ArgInfo& argInfo) { if (dst.IsNull() || dst.IsUndefined()) return true; int underlying = 0; if (!jsopencv_to(info, dst, underlying, argInfo)) return false; src = static_cast<AKAZE_DescriptorType>(underlying); return true; }
no instance of overloaded function "jsopencv_to" matches the specified typeC/C++(493)

I think the missing function is:

template<> bool jsopencv_to(const Napi::CallbackInfo &info, Napi::Value* obj, int& value, const ArgInfo& Arginfo);

but it is available in cv2_convert.cpp/hpp

by removing the line:

if (!jsopencv_to(info, dst, underlying, argInfo)) return false; 

from CV_JS_TO_ENUM I still get the same error. so that the missing jsopencv_to

test by replacing a macro usage:

image

2nd case:
if (!jsopencv_AKAZE_getp(self, self1))

jsopencv_AKAZE_getp is generated by the macro:
#define CVJS_TYPE_DECLARE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, SCOPE) \

CVJS_TYPE_DECLARE is called by the macro:

define CVJS_TYPE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, _1, _2, SCOPE) CVJS_TYPE_DECLARE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, SCOPE)

CVJS_TYPE is called from jsopencv_generated_types.h

The original line should be generated by:

CVJS_TYPE(AKAZE, AKAZE, Ptr<cv::AKAZE>, Ptr, Feature2D, 0, "");

currently extended as:

struct jsopencv_AKAZE_t { PyObject_HEAD Ptr<cv::AKAZE> v; };
static JsTypeObject jsopencv_AKAZE_TypeXXX = { PyVarObject_HEAD_INIT(&PyType_Type, 0) MODULESTR """.""AKAZE", sizeof(jsopencv_AKAZE_t), };
static PyTypeObject * jsopencv_AKAZE_TypePtr = &jsopencv_AKAZE_TypeXXX;
static bool jsopencv_AKAZE_getp(Napi::Value * self, Ptr<cv::AKAZE> * & dst) {
    if (PyObject_TypeCheck(self, jsopencv_AKAZE_TypePtr)) {
        dst = &(((jsopencv_AKAZE_t*)self)->v);
        return true;
    }
    return false;
}
static Napi::Value * jsopencv_AKAZE_Instance(const Ptr<cv::AKAZE> &r) {
    jsopencv_AKAZE_t *m = PyObject_NEW(jsopencv_AKAZE_t, jsopencv_AKAZE_TypePtr);
    new (&(m->v)) Ptr<cv::AKAZE>(r);
    return (Napi::Value*)m;
}
static void jsopencv_AKAZE_dealloc(Napi::Value* self) {
    ((jsopencv_AKAZE_t*)self)->v.Ptr<cv::AKAZE>::~Ptr();
    PyObject_Del(self);
}
static Napi::Value* jsopencv_AKAZE_repr(Napi::Value* self) {
    char str[1000];
    snprintf(str, sizeof(str), "< " MODULESTR """.""AKAZE"" %p>", self);
    return PyString_FromString(str);
}

PyObject => Napi::Value
PyErr_Format => failmsg

JsOpenCV_Converter implementations

  • 74+ generated in jsopencv_generated_types_content.h
  • 2 generic implementation in cv2_convert.h
    • template struct JsOpenCV_Converter< cv::Ptr >
    • template struct PyOpenCV_Converter < T, typename std::enable_if< std::is_same<unsigned int, T>::value && !std::is_same<unsigned int, size_t>::value >::type >
  • 2 hard coded in gapi/misc/python/pyobjectcv_gapi.hpp

How classes are defined:

in jsopencv_generated_types_content.h each class is defined with example for AKAZE:

// used to link getter

static JsGetSetDef jsopencv_AKAZE_getseters[] = {
    {NULL}  /* Sentinel */
}

// used to link methods:

static JsMethodDef jsopencv_AKAZE_methods[] =
{
    {"create", CV_JS_FN_WITH_KW_(jsopencv_cv_AKAZE_create_static, METH_STATIC), "doc ..."},
    {"setThreshold", CV_JS_FN_WITH_KW_(jsopencv_cv_AKAZE_setThreshold, 0), "doc..."},
...
    {NULL,          NULL}
};

All referenced methods are defined before in the same file.

the macro: CVJS_TYPE_INIT_DYNAMIC is used to build those objects.


in N-Api each class should be defined with a code like:

Init(Napi::Env env, Napi::Object exports) {
  Napi::Function func = DefineClass(env, "AKAZE", {
    InstanceMethod("getDefaultName", &AKAZE::GetDefaultName),
    InstanceMethod("setThreshold", &AKAZE::SetThreshold),
    InstanceAccessor("x", &AKAZE::GetX, nullptr)
  });

  constructor = Napi::Persistent(func);
  constructor.SuppressDestruct();

  exports.Set("AKAZE", func);
  return exports;

This implementation use Napi::ObjectWrap tools

Next implementation sample:

#include <napi.h>

class AKAZE {
public:
    AKAZE() = default;
    ~AKAZE() = default;

    void setThreshold(double threshold) {
        this->threshold = threshold;
    }

    static AKAZE getDefault() {
        AKAZE akaze;
        akaze.setThreshold(0.001);
        return akaze;
    }

private:
    double threshold;
};

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    Napi::HandleScope scope(env);

    Napi::Function akazeConstructor = Napi::Function::New(env, [](const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        AKAZE* akaze = new AKAZE();
        info.This().As<Napi::Object>().SetInternalField(0, Napi::External<AKAZE>::New(env, akaze));
        return info.This();
    });

    Napi::Function setThreshold = Napi::Function::New(env, [](const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        double threshold = info[0].As<Napi::Number>().DoubleValue();
        AKAZE* akaze = info.This().As<Napi::Object>().GetInternalField(0).As<Napi::External<AKAZE>>().Data();
        akaze->setThreshold(threshold);
        return Napi::Value();
    });

    Napi::Function getDefault = Napi::Function::New(env, [](const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        AKAZE akaze = AKAZE::getDefault();
        Napi::Object obj = Napi::Object::New(env);
        obj.SetInternalField(0, Napi::External<AKAZE>::New(env, new AKAZE(akaze)));
        return obj;
    });

    akazeConstructor["getDefault"] = getDefault;
    akazeConstructor.As<Napi::Object>().DefineOwnProperty(env, Napi::String::New(env, "setThreshold"), setThreshold);

    exports.Set("AKAZE", akazeConstructor);

    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

methods from jsopencv_generated_types_content.h relay on function like jsopencv_AKAZE_getp
defined in a macro jscompat.hpp: CVJS_TYPE_DECLARE called by CVJS_TYPE called in jsopencv_generated_types.h

jscompat.hpp:CVJS_TYPE_DECLARE need to be rewritten.

The cv2_macro.hpp:CVJS_TYPE template generates:

  • a struct jsopencv_AKAZE_t { to encapsulate the real object
  • a type jsopencv_AKAZE_TypeXXX containing a string signature and the structure size.
  • jsopencv_AKAZE_TypePtr pointer to jsopencv_AKAZE_TypeXXX
  • jsopencv_AKAZE_getp get a pointer to the expected type in the struct.
  • jsopencv_AKAZE_Instance allocate and init the struct
  • jsopencv_AKAZE_dealloc free the struct
  • jsopencv_AKAZE_repr return s string representation of the object.

alternate implementation:

#include <napi.h>

class AKAZE {
public:
    AKAZE() = default;
    ~AKAZE() = default;

    void setThreshold(double threshold) {}
    void setDescriptorSize(int descriptorSize) {}
    void setDescriptorChannels(int descriptorChannels) {}
    void setDescriptorType(int descriptorType) {}

    static AKAZE getDefault() {
        AKAZE akaze;
        return akaze;
    }
};

void jsopencv_cv_AKAZE_setThreshold(const Napi::CallbackInfo &info) {
    Napi::Env env = info.Env();
    double threshold = info[0].As<Napi::Number>().DoubleValue();
    AKAZE *akaze = info.This().As<Napi::Object>().GetInternalField(0).As<Napi::External<AKAZE>>().Data();
    akaze->setThreshold(threshold);
}

void jsopencv_cv_AKAZE_setDescriptorSize(const Napi::CallbackInfo &info) {
    Napi::Env env = info.Env();
    int descriptorSize = info[0].As<Napi::Number>().Int32Value();
    AKAZE *akaze = info.This().As<Napi::Object>().GetInternalField(0).As<Napi::External<AKAZE>>().Data();
    akaze->setDescriptorSize(descriptorSize);
}

void jsopencv_cv_AKAZE_setDescriptorChannels(const Napi::CallbackInfo &info) {
    Napi::Env env = info.Env();
    int descriptorChannels = info[0].As<Napi::Number>().Int32Value();
    AKAZE *akaze = info.This().As<Napi::Object>().GetInternalField(0).As<Napi::External<AKAZE>>().Data();
    akaze->setDescriptorChannels(descriptorChannels);
}

void jsopencv_cv_AKAZE_setDescriptorType(const Napi::CallbackInfo &info) {
    Napi::Env env = info.Env();
    int descriptorType = info[0].As<Napi::Number>().Int32Value();
    AKAZE *akaze = info.This().As<Napi::Object>().GetInternalField(0).As<Napi::External<AKAZE>>().Data();
    akaze->setDescriptorType(descriptorType);
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    Napi::HandleScope scope(env);

    Napi::Function akazeConstructor = Napi::Function::New(env, [](const Napi::CallbackInfo &info) {
        Napi::Env env = info.Env();
        AKAZE *akaze = new AKAZE();
        info.This().As<Napi::Object>().SetInternalField(0, Napi::External<AKAZE>::New(env, akaze));
        return info.This();
    });

    Napi::Function setThreshold = Napi::Function::New(env, jsopencv_cv_AKAZE_setThreshold);
    Napi::Function setDescriptorSize = Napi::Function::New(env, jsopencv_cv_AKAZE_setDescriptorSize);
    Napi::Function setDescriptorChannels = Napi::Function::New(env, jsopencv_cv_AKAZE_setDescriptorChannels);
    Napi::Function setDescriptorType = Napi::Function::New(env, jsopencv_cv_AKAZE_setDescriptorType);

    Napi::Function getDefault = Napi::Function::New(env, [](const Napi::CallbackInfo &info) {
        Napi::Env env = info.Env();
        AKAZE akaze = AKAZE::getDefault();
        Napi::Object obj = Napi::Object::New(env);
        obj.SetInternalField(0, Napi::External<AKAZE>::New(env, new AKAZE(akaze)));
        return obj;
    });
akazeConstructor["getDefault"] = getDefault;
akazeConstructor.As<Napi::Object>().DefineOwnProperty(env, Napi::String::New(env, "setThreshold"), setThreshold);
akazeConstructor.As<Napi::Object>().DefineOwnProperty(env, Napi::String::New(env, "setDescriptorSize"), setDescriptorSize);
akazeConstructor.As<Napi::Object>().DefineOwnProperty(env, Napi::String::New(env, "setDescriptorChannels"), setDescriptorChannels);
akazeConstructor.As<Napi::Object>().DefineOwnProperty(env, Napi::String::New(env, "setDescriptorType"), setDescriptorType);

exports.Set("AKAZE", akazeConstructor);

return exports;
}