From 53c9c40ebab8b52dcd6e96eb0b50c1d6b0eb71e2 Mon Sep 17 00:00:00 2001 From: hbristow Date: Fri, 28 Jun 2013 19:48:42 -0700 Subject: [PATCH] started MxArray wrapper around mxArray class --- .../templates/template_function_base.cpp | 2 +- modules/matlab/include/bridge.hpp | 546 ++++++++++++++---- 2 files changed, 434 insertions(+), 114 deletions(-) diff --git a/modules/matlab/generator/templates/template_function_base.cpp b/modules/matlab/generator/templates/template_function_base.cpp index 731ac83e8..3516f171f 100644 --- a/modules/matlab/generator/templates/template_function_base.cpp +++ b/modules/matlab/generator/templates/template_function_base.cpp @@ -46,7 +46,7 @@ void mexFunction(int nlhs, mxArray* plhs[], {%- if noutputs %} // push the outputs back to matlab for (size_t n = 0; n < nlhs; ++n) { - plhs[n] = outputs[n].toMxArray(); + plhs[n] = outputs[n].toMxArray().releaseOwnership(); } {% endif %} } diff --git a/modules/matlab/include/bridge.hpp b/modules/matlab/include/bridge.hpp index f3c1510b3..014d573ec 100644 --- a/modules/matlab/include/bridge.hpp +++ b/modules/matlab/include/bridge.hpp @@ -8,6 +8,27 @@ #include #include +/* + * All recent versions of Matlab ship with the MKL library which contains + * a blas extension called mkl_?omatcopy(). This defines an out-of-place + * copy and transpose operation. + * + * The mkl library is in ${MATLAB_ROOT}/bin/${MATLAB_MEXEXT}/libmkl... + * Matlab does not ship headers for the mkl functions, so we define them + * here. + * + * This operation is used extensively to copy between Matlab's column-major + * format and OpenCV's row-major format. + */ +#ifdef __cplusplus +extern "C" { +#endif + void mkl_somatcopy(char, char, size_t, size_t, const float, const float*, size_t, float*, size_t); + void mkl_domatcopy(char, char, size_t, size_t, const double, const double*, size_t, double*, size_t); +#ifdef __cplusplus +} +#endif + /* * Custom typedefs * Parsed names from the hdr_parser @@ -29,6 +50,261 @@ void conditionalError(bool expr, const std::string& str) { if (!expr) mexErrMsgTxt(std::string("condition failed: ").append(str).c_str()); } +void error(const std::string& str) { + mexErrMsgTxt(str.c_str()); +} + + +namespace Matlab { + class DefaultTraits {}; + class InheritType {}; + static const int Dynamic = -1; + + // -------------------------------------------------------------------------- + // INTERNAL TRAITS CLASS + // -------------------------------------------------------------------------- + template class Traits { + public: + static const mxClassID ScalarType = mxUNKNOWN_CLASS; + static const mxComplexity Complex = mxCOMPLEX; + static const mxComplexity Real = mxCOMPLEX; + }; + // bool + template<> class Traits { + public: + static const mxClassID ScalarType = mxLOGICAL_CLASS; + }; + // uint8_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxUINT8_CLASS; + }; + // int8_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxINT8_CLASS; + }; + // uint16_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxUINT16_CLASS; + }; + // int16_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxINT16_CLASS; + }; + // uint32_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxUINT32_CLASS; + }; + // int32_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxINT32_CLASS; + }; + // uint64_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxUINT64_CLASS; + }; + // int64_t + template<> class Traits { + public: + static const mxClassID ScalarType = mxINT64_CLASS; + }; + // float + template<> class Traits { + public: + static const mxClassID ScalarType = mxSINGLE_CLASS; + }; + // double + template<> class Traits { + public: + static const mxClassID ScalarType = mxDOUBLE_CLASS; + }; + // size_t + template<> class Traits { + public: + static const mxClassID ScalarType = (sizeof(size_t) == 4) ? mxUINT32_CLASS : mxUINT64_CLASS; + }; + // char + template<> class Traits { + public: + static const mxClassID ScalarType = mxCHAR_CLASS; + }; +}; + + + +// ---------------------------------------------------------------------------- +// MXARRAY +// ---------------------------------------------------------------------------- + + +/*! + * @class MxArray + * @brief A thin wrapper around Matlab's mxArray types + * + * MxArray provides a thin object oriented wrapper around Matlab's + * native mxArray type which exposes most of the functionality of the + * Matlab interface, but in a more C++ manner. + */ +class MxArray { +private: + mxArray* ptr_; + bool owns_; +public: + MxArray() : ptr_(NULL), owns_(false) {} + MxArray(const mxArray* ptr) : ptr_(const_cast(ptr)), owns_(false) {} + ~MxArray() { + if (owns_ && ptr_) mxDestroyArray(ptr_); + } + + /* + * @brief release ownership to allow return into Matlab workspace + * + * MxArray is not directly convertible back to mxArray types through assignment + * because the MxArray may have been allocated on the free store, making it impossible + * to know whether the returned pointer will be released by someone else or not. + * + * Since Matlab requires mxArrays be passed back into the workspace, the only way + * to achieve that is through this function, which explicitly releases ownership + * of the object, assuming the Matlab interpreter receving the object will delete + * it at a later time + * + * e.g. + * { + * MxArray A(5, 5); // allocates memory + * MxArray B(5, 5); // ditto + * plhs[0] = A; // not allowed!! + * plhs[0] = A.releaseOwnership(); // makes explicit that ownership is being released + * } // end of scope. B is released, A isn't + * + */ + mxArray* releaseOwnership() { + owns_ = false; + return ptr_; + } + + + // TODO: Make sure NULL pointers are checked in all functions! + template + explicit MxArray(size_t m, size_t n, size_t k=1) : owns_(true) { + mwSize dims[] = {m, n, k}; + ptr_ = mxCreateNumericArray(3, dims, Matlab::Traits::ScalarType, Matlab::Traits<>::Real); + } + + template + explicit MxArray(const cv::Mat& mat) : owns_(true) { + mwSize dims[] = { mat.rows, mat.cols, mat.channels() }; + if (mat.channels() == 2) { + ptr_ = mxCreateNumericArray(2, dims, Matlab::Traits::ScalarType, Matlab::Traits<>::Complex); + } else { + ptr_ = mxCreateNumericArray(2, dims, Matlab::Traits::ScalarType, Matlab::Traits<>::Real); + } + } + + template + cv::Mat toMat() const { + cv::Mat mat(cols(), rows(), CV_MAKETYPE(cv::DataType::type, channels())); + return mat; + } + + template + void fromMat(const cv::Mat& mat) { + + } + + MxArray field(const std::string& name) { return MxArray(mxGetField(ptr_, 0, name.c_str())); } + + template + Scalar* real() { return static_cast(mxGetData(ptr_)); } + + template + Scalar* imag() { return static_cast(mxGetData(ptr_)); } + + template + const Scalar* real() const { return static_cast(mxGetData(ptr_)); } + + template + const Scalar* imag() const { return static_cast(mxGetData(ptr_)); } + + template + Scalar scalar() const { return static_cast(mxGetData(ptr_))[0]; } + + std::string toString() const { + conditionalError(isString(), "Attempted to convert non-string type to string"); + std::string str; + str.reserve(size()+1); + mxGetString(ptr_, const_cast(str.data()), str.size()); + return str; + } + + size_t size() const { return mxGetNumberOfElements(ptr_); } + size_t rows() const { return mxGetM(ptr_); } + size_t cols() const { return mxGetN(ptr_); } + size_t channels() const { return (mxGetNumberOfDimensions(ptr_) > 2) ? mxGetDimensions(ptr_)[2] : 1; } + bool isComplex() const { return mxIsComplex(ptr_); } + bool isNumeric() const { return mxIsNumeric(ptr_); } + bool isLogical() const { return mxIsLogical(ptr_); } + bool isString() const { return mxIsChar(ptr_); } + bool isCell() const { return mxIsCell(ptr_); } + bool isStructure() const { return mxIsStruct(ptr_); } + bool isClass(const std::string& name) const { return mxIsClass(ptr_, name.c_str()); } + std::string className() const { return std::string(mxGetClassName(ptr_)); } + mxClassID ID() const { return mxGetClassID(ptr_); } + +}; + + +template <> +cv::Mat MxArray::toMat() const { + return cv::Mat(); +} + +template <> +void MxArray::fromMat(const cv::Mat& mat) { + +} + + + +// ---------------------------------------------------------------------------- +// MATRIX TRANSPOSE +// ---------------------------------------------------------------------------- + +template +void deepCopyAndTranspose(const cv::Mat& src, MxArray& dst) { +} + +template +void deepCopyAndTranspose(const MxArray& src, cv::Mat& dst) { +} + +template <> +void deepCopyAndTranspose(const cv::Mat& src, MxArray& dst) { +} + +template <> +void deepCopyAndTranspose(const cv::Mat& src, MxArray& dst) { +} + +template <> +void deepCopyAndTranspose(const MxArray& src, cv::Mat& dst) { +} + +template <> +void deepCopyAndTranspose(const MxArray& src, cv::Mat& dst) { +} + + + +// ---------------------------------------------------------------------------- +// BRIDGE +// ---------------------------------------------------------------------------- + /*! * @class Bridge * @brief Type conversion class for converting OpenCV and native C++ types @@ -48,6 +324,10 @@ void conditionalError(bool expr, const std::string& str) { * std and stl types to Matlab's mxArray format. By inheriting Bridge, * you can add your own custom type conversions. * + * Because Matlab uses a homogeneous storage type, all operations are provided + * relative to Matlab's type. That is, Bridge always stores an MxArray object + * and converts to and from other object types on demand. + * * NOTE: for the explicit conversion function, the object name must be * in UpperCamelCase, for example: * int --> toInt @@ -67,7 +347,7 @@ void conditionalError(bool expr, const std::string& str) { */ class Bridge { private: - mxArray* ptr_; + MxArray ptr_; public: // bridges are default constructible Bridge() {} @@ -82,99 +362,58 @@ public: * NOTE! This function assumes that the C++ pointer is stored in inst_ */ template - Object* getObjectByName(const char* name) { + Object* getObjectByName(const std::string& name) { // check that the object is actually of correct type before unpacking // TODO: Traverse class hierarchy? - if (!mxIsClass(ptr_, name)) { - const char* actual = mxGetClassName(ptr_); - mexErrMsgTxt(std::string("Expected class ").append(std::string(name)) - .append(" but was given ").append(std::string(actual)).c_str()); + if (!ptr_.isClass(name)) { + error(std::string("Expected class ").append(std::string(name)) + .append(" but was given ").append(ptr_.className())); } // get the instance field - mxArray* inst = mxGetField(ptr_, 0, "inst_"); + MxArray inst = ptr_.field("inst_"); Object* obj = NULL; // make sure the pointer is the correct size for the system - if (sizeof(void *) == 8 && mxIsClass(inst, "uint64")) { + if (sizeof(void *) == 8 && inst.ID() == mxUINT64_CLASS) { // 64-bit pointers // TODO: Do we REALLY REALLY need to reinterpret_cast? - obj = reinterpret_cast( - reinterpret_cast(mxGetData(inst))[0]); - } else if (sizeof(void *) == 4 && mxIsClass(inst, "uint32")) { + obj = reinterpret_cast(inst.scalar()); + } else if (sizeof(void *) == 4 && inst.ID() == mxUINT32_CLASS) { // 32-bit pointers - obj = reinterpret_cast( - reinterpret_cast(mxGetData(inst))[0]); + obj = reinterpret_cast(inst.scalar()); } else { - mexErrMsgTxt("Incorrect pointer type stored for architecture"); + error("Incorrect pointer type stored for architecture"); } // finally check if the object is NULL - if (!obj) mexErrMsgTxt(std::string("Object ").append(std::string(name)).append(std::string(" is NULL")).c_str()); + conditionalError(obj, std::string("Object ").append(std::string(name)).append(std::string(" is NULL"))); return obj; } + - // --------------------------- mxArray -------------------------------------- + + + + + // -------------------------------------------------------------------------- + // MATLAB TYPES + // -------------------------------------------------------------------------- Bridge& operator=(const mxArray* obj) { return *this; } - Bridge(const mxArray* obj) {} - mxArray* toMxArray() { return NULL; } - - // --------------------------- cv::Mat -------------------------------------- - Bridge& operator=(const cv::Mat& obj) { return *this; } - cv::Mat toMat() { return cv::Mat(); } - operator cv::Mat() { return toMat(); } - - // -------------------- vector_Mat -------------------------------- - Bridge& operator=(const vector_Mat& obj) { return *this; } - vector_Mat toVectorMat() { return vector_Mat(); } - operator vector_Mat() { return toVectorMat(); } - - // --------------------------- int -------------------------------------- - Bridge& operator=(const int& obj) { return *this; } - int toInt() { return 0; } - operator int() { return toInt(); } - - // --------------------------- Ptr_StereoBM -------------------------------------- - Bridge& operator=(const Ptr_StereoBM& obj) { return *this; } - Ptr_StereoBM toPtrStereoBM() { return Ptr_StereoBM(); } - operator Ptr_StereoBM() { return toPtrStereoBM(); } - - // --------------------------- Ptr_StereoSGBM -------------------------------------- - Bridge& operator=(const Ptr_StereoSGBM& obj) { return *this; } - Ptr_StereoSGBM toPtrStereoSGBM() { return Ptr_StereoSGBM(); } - operator Ptr_StereoSGBM() { return toPtrStereoSGBM(); } - - // --------------------------- Ptr_FeatureDetector -------------------------------------- - Bridge& operator=(const Ptr_FeatureDetector& obj) { return *this; } - Ptr_FeatureDetector toPtrFeatureDetector() { return Ptr_FeatureDetector(); } - operator Ptr_FeatureDetector() { return toPtrFeatureDetector(); } - - // --------------------------- vector_int ---------------------------------- - Bridge& operator=(const vector_int& obj) { return *this; } - vector_int toVectorInt() { return vector_int(); } - operator vector_int() { return toVectorInt(); } - - // --------------------------- vector_float ---------------------------------- - Bridge& operator=(const vector_float& obj) { return *this; } - vector_float toVectorFloat() { return vector_float(); } - operator vector_float() { return toVectorFloat(); } - - // --------------------------- vector_Rect ---------------------------------- - Bridge& operator=(const vector_Rect& obj) { return *this; } - vector_Rect toVectorRect() { return vector_Rect(); } - operator vector_Rect() { return toVectorRect(); } - - // --------------------------- vector_KeyPoint ---------------------------------- - Bridge& operator=(const vector_KeyPoint& obj) { return *this; } - vector_KeyPoint toVectorKeyPoint() { return vector_KeyPoint(); } - operator vector_KeyPoint() { return toVectorKeyPoint(); } - - // --------------------------- vector_String ---------------------------------- - Bridge& operator=(const vector_String& obj) { return *this; } - vector_String toVectorString() { return vector_String(); } - operator vector_String() { return toVectorString(); } - + Bridge(const mxArray* obj) : ptr_(obj) {} + MxArray toMxArray() { return ptr_; } + + + + + + // -------------------------------------------------------------------------- + // INTEGRAL TYPES + // -------------------------------------------------------------------------- + // --------------------------- string -------------------------------------- Bridge& operator=(const std::string& obj) { return *this; } - std::string toString() { return ""; } + std::string toString() { + return ptr_.toString(); + } operator std::string() { return toString(); } // --------------------------- bool -------------------------------------- @@ -184,70 +423,78 @@ public: // --------------------------- double -------------------------------------- Bridge& operator=(const double& obj) { return *this; } - double toDouble() { return 0; } + double toDouble() { return ptr_.scalar(); } operator double() { return toDouble(); } - // --------------------------- float -------------------------------------- + // --------------------------- float --------------------------------------- Bridge& operator=(const float& obj) { return *this; } - float toFloat() { return 0; } + float toFloat() { return ptr_.scalar(); } operator float() { return toFloat(); } + // --------------------------- int -------------------------------------- + Bridge& operator=(const int& obj) { return *this; } + int toInt() { return ptr_.scalar(); } + operator int() { return toInt(); } + + + + + + // -------------------------------------------------------------------------- + // CORE OPENCV TYPES + // -------------------------------------------------------------------------- + + // --------------------------- cv::Mat -------------------------------------- + Bridge& operator=(const cv::Mat& obj) { return *this; } + cv::Mat toMat() const { return ptr_.toMat(); } + operator cv::Mat() const { return toMat(); } + // -------------------------- Point -------------------------------------- Bridge& operator=(const cv::Point& obj) { return *this; } - cv::Point toPoint() { return cv::Point(); } - operator cv::Point() { return toPoint(); } - + cv::Point toPoint() const { return cv::Point(); } + operator cv::Point() const { return toPoint(); } + // -------------------------- Point2f ------------------------------------ Bridge& operator=(const cv::Point2f& obj) { return *this; } - cv::Point2f toPoint2f() { return cv::Point2f(); } - operator cv::Point2f() { return toPoint2f(); } - + cv::Point2f toPoint2f() const { return cv::Point2f(); } + operator cv::Point2f() const { return toPoint2f(); } + // -------------------------- Point2d ------------------------------------ Bridge& operator=(const cv::Point2d& obj) { return *this; } - cv::Point2d toPoint2d() { return cv::Point2d(); } - operator cv::Point2d() { return toPoint2d(); } - + cv::Point2d toPoint2d() const { return cv::Point2d(); } + operator cv::Point2d() const { return toPoint2d(); } + // -------------------------- Size --------------------------------------- Bridge& operator=(const cv::Size& obj) { return *this; } - cv::Size toSize() { return cv::Size(); } - operator cv::Size() { return toSize(); } - - // -------------------------- Moments --------------------------------------- + cv::Size toSize() const { return cv::Size(); } + operator cv::Size() const { return toSize(); } + + // -------------------------- Moments -------------------------------------- Bridge& operator=(const cv::Moments& obj) { return *this; } - cv::Moments toMoments() { return cv::Moments(); } - operator cv::Moments() { return toMoments(); } - - // ------------------------ vector_Point ------------------------------------ - Bridge& operator=(const vector_Point& obj) { return *this; } - vector_Point toVectorPoint() { return vector_Point(); } - operator vector_Point() { return toVectorPoint(); } - - // ------------------------ vector_uchar ------------------------------------- - Bridge& operator=(const vector_uchar& obj) { return *this; } - vector_uchar toVectorUchar() { return vector_uchar(); } - operator vector_uchar() { return toVectorUchar(); } - + cv::Moments toMoments() const { return cv::Moments(); } + operator cv::Moments() const { return toMoments(); } + // -------------------------- Scalar -------------------------------------- Bridge& operator=(const cv::Scalar& obj) { return *this; } cv::Scalar toScalar() { return cv::Scalar(); } operator cv::Scalar() { return toScalar(); } - - // -------------------------- Rect -------------------------------------- + + // -------------------------- Rect ----------------------------------------- Bridge& operator=(const cv::Rect& obj) { return *this; } cv::Rect toRect() { return cv::Rect(); } operator cv::Rect() { return toRect(); } - - // ---------------------- RotatedRect ------------------------------------ + + // ---------------------- RotatedRect --------------------------------------- Bridge& operator=(const cv::RotatedRect& obj) { return *this; } cv::RotatedRect toRotatedRect() { return cv::RotatedRect(); } operator cv::RotatedRect() { return toRotatedRect(); } - - // ---------------------- TermCriteria ----------------------------------- + + // ---------------------- TermCriteria -------------------------------------- Bridge& operator=(const cv::TermCriteria& obj) { return *this; } cv::TermCriteria toTermCriteria() { return cv::TermCriteria(); } operator cv::TermCriteria() { return toTermCriteria(); } - - // ---------------------- RNG ----------------------------------- + + // ---------------------- RNG -------------------------------------- Bridge& operator=(const cv::RNG& obj) { return *this; } /*! @brief explicit conversion to cv::RNG() * @@ -255,11 +502,84 @@ public: * the object is an RNG in matlab space before attempting to deference * its pointer */ - cv::RNG toRNG() { + cv::RNG toRNG() { return (*getObjectByName("RNG")); } operator cv::RNG() { return toRNG(); } + + + + + // -------------------------------------------------------------------------- + // OPENCV VECTOR TYPES + // -------------------------------------------------------------------------- + + // -------------------- vector_Mat ------------------------------------------ + Bridge& operator=(const vector_Mat& obj) { return *this; } + vector_Mat toVectorMat() { return vector_Mat(); } + operator vector_Mat() { return toVectorMat(); } + + // --------------------------- vector_int ---------------------------------- + Bridge& operator=(const vector_int& obj) { return *this; } + vector_int toVectorInt() { return vector_int(); } + operator vector_int() { return toVectorInt(); } + + // --------------------------- vector_float -------------------------------- + Bridge& operator=(const vector_float& obj) { return *this; } + vector_float toVectorFloat() { return vector_float(); } + operator vector_float() { return toVectorFloat(); } + + // --------------------------- vector_Rect --------------------------------- + Bridge& operator=(const vector_Rect& obj) { return *this; } + vector_Rect toVectorRect() { return vector_Rect(); } + operator vector_Rect() { return toVectorRect(); } + + // --------------------------- vector_KeyPoint ----------------------------- + Bridge& operator=(const vector_KeyPoint& obj) { return *this; } + vector_KeyPoint toVectorKeyPoint() { return vector_KeyPoint(); } + operator vector_KeyPoint() { return toVectorKeyPoint(); } + + // --------------------------- vector_String ------------------------------- + Bridge& operator=(const vector_String& obj) { return *this; } + vector_String toVectorString() { return vector_String(); } + operator vector_String() { return toVectorString(); } + + // ------------------------ vector_Point ------------------------------------ + Bridge& operator=(const vector_Point& obj) { return *this; } + vector_Point toVectorPoint() { return vector_Point(); } + operator vector_Point() { return toVectorPoint(); } + + // ------------------------ vector_uchar ------------------------------------ + Bridge& operator=(const vector_uchar& obj) { return *this; } + vector_uchar toVectorUchar() { return vector_uchar(); } + operator vector_uchar() { return toVectorUchar(); } + + + + + + // -------------------------------------------------------------------------- + // OPENCV COMPLEX TYPES + // -------------------------------------------------------------------------- + + // --------------------------- Ptr_StereoBM ----------------------------- + Bridge& operator=(const Ptr_StereoBM& obj) { return *this; } + Ptr_StereoBM toPtrStereoBM() { return Ptr_StereoBM(); } + operator Ptr_StereoBM() { return toPtrStereoBM(); } + + // --------------------------- Ptr_StereoSGBM --------------------------- + Bridge& operator=(const Ptr_StereoSGBM& obj) { return *this; } + Ptr_StereoSGBM toPtrStereoSGBM() { return Ptr_StereoSGBM(); } + operator Ptr_StereoSGBM() { return toPtrStereoSGBM(); } + + // --------------------------- Ptr_FeatureDetector ---------------------- + Bridge& operator=(const Ptr_FeatureDetector& obj) { return *this; } + Ptr_FeatureDetector toPtrFeatureDetector() { return Ptr_FeatureDetector(); } + operator Ptr_FeatureDetector() { return toPtrFeatureDetector(); } + + + }; #endif