Split MxArray out of bridge into standalone file. Does not depend on bridge. Working to make it API complete with respect to mxArray, but with better calling syntax
This commit is contained in:
parent
504558c0be
commit
baa946c390
@ -58,9 +58,9 @@ class MatlabWrapperGenerator(object):
|
||||
# populate templates
|
||||
for namespace in parse_tree.namespaces:
|
||||
# functions
|
||||
for function in namespace.functions:
|
||||
populated = tfunction.render(fun=function, time=time, includes=namespace.name)
|
||||
with open(output_source_dir+'/'+function.name+'.cpp', 'wb') as f:
|
||||
for method in namespace.methods:
|
||||
populated = tfunction.render(fun=method, time=time, includes=namespace.name)
|
||||
with open(output_source_dir+'/'+method.name+'.cpp', 'wb') as f:
|
||||
f.write(populated)
|
||||
# classes
|
||||
for clss in namespace.classes:
|
||||
|
@ -13,7 +13,7 @@ class ParseTree(object):
|
||||
babel = Translator()
|
||||
for name, definitions in namespaces.items():
|
||||
class_tree = {}
|
||||
functions = []
|
||||
methods = []
|
||||
constants = []
|
||||
for defn in definitions:
|
||||
obj = babel.translate(defn)
|
||||
@ -21,13 +21,13 @@ class ParseTree(object):
|
||||
continue
|
||||
if type(obj) is Class or obj.clss:
|
||||
self.insertIntoClassTree(obj, class_tree)
|
||||
elif type(obj) is Function:
|
||||
functions.append(obj)
|
||||
elif type(obj) is Method:
|
||||
methods.append(obj)
|
||||
elif type(obj) is Constant:
|
||||
constants.append(obj)
|
||||
else:
|
||||
raise TypeError('Unexpected object type: '+str(type(obj)))
|
||||
self.namespaces.append(Namespace(name, constants, class_tree.values(), functions))
|
||||
self.namespaces.append(Namespace(name, constants, class_tree.values(), methods))
|
||||
|
||||
def insertIntoClassTree(self, obj, class_tree):
|
||||
cname = obj.name if type(obj) is Class else obj.clss
|
||||
@ -38,8 +38,8 @@ class ParseTree(object):
|
||||
class_tree[cname] = Class(cname)
|
||||
# insert the definition into the class
|
||||
val = class_tree[cname]
|
||||
if type(obj) is Function:
|
||||
val.functions.append(obj)
|
||||
if type(obj) is Method:
|
||||
val.methods.append(obj)
|
||||
elif type(obj) is Constant:
|
||||
val.constants.append(obj)
|
||||
else:
|
||||
@ -63,7 +63,7 @@ class Translator(object):
|
||||
# --- function ---
|
||||
# functions either need to have input arguments, or not uppercase names
|
||||
elif defn[3] or not self.translateName(defn[0]).split('_')[0].isupper():
|
||||
return self.translateFunction(defn)
|
||||
return self.translateMethod(defn)
|
||||
# --- constant ---
|
||||
else:
|
||||
return self.translateConstant(defn)
|
||||
@ -71,7 +71,7 @@ class Translator(object):
|
||||
def translateClass(self, defn):
|
||||
return Class()
|
||||
|
||||
def translateFunction(self, defn, class_tree=None):
|
||||
def translateMethod(self, defn, class_tree=None):
|
||||
name = self.translateName(defn[0])
|
||||
clss = self.translateClassName(defn[0])
|
||||
rtp = defn[1]
|
||||
@ -83,7 +83,7 @@ class Translator(object):
|
||||
if arg:
|
||||
a = self.translateArgument(arg)
|
||||
opt.append(a) if a.default else req.append(a)
|
||||
return Function(name, clss, static, '', rtp, False, req, opt)
|
||||
return Method(name, clss, static, '', rtp, False, req, opt)
|
||||
|
||||
def translateConstant(self, defn):
|
||||
const = True if 'const' in defn[0] else False
|
||||
@ -116,34 +116,35 @@ class Translator(object):
|
||||
|
||||
|
||||
class Namespace(object):
|
||||
def __init__(self, name='', constants=None, classes=None, functions=None):
|
||||
def __init__(self, name='', constants=None, classes=None, methods=None):
|
||||
self.name = name
|
||||
self.constants = constants if constants else []
|
||||
self.classes = classes if classes else []
|
||||
self.functions = functions if functions else []
|
||||
self.methods = methods if methods else []
|
||||
|
||||
def __str__(self):
|
||||
return 'namespace '+self.name+' {\n\n'+\
|
||||
(join((c.__str__() for c in self.constants), '\n')+'\n\n' if self.constants else '')+\
|
||||
(join((f.__str__() for f in self.functions), '\n')+'\n\n' if self.functions else '')+\
|
||||
(join((f.__str__() for f in self.methods), '\n')+'\n\n' if self.methods else '')+\
|
||||
(join((o.__str__() for o in self.classes), '\n\n') if self.classes else '')+'\n};'
|
||||
|
||||
class Class(object):
|
||||
def __init__(self, name='', namespace='', constants=None, functions=None):
|
||||
def __init__(self, name='', namespace='', constants=None, methods=None):
|
||||
self.name = name
|
||||
self.namespace = namespace
|
||||
self.constants = constants if constants else []
|
||||
self.functions = functions if functions else []
|
||||
self.methods = methods if methods else []
|
||||
|
||||
def __str__(self):
|
||||
return 'class '+self.name+' {\n\t'+\
|
||||
(join((c.__str__() for c in self.constants), '\n\t')+'\n\n\t' if self.constants else '')+\
|
||||
(join((f.__str__() for f in self.functions), '\n\t') if self.functions else '')+'\n};'
|
||||
(join((f.__str__() for f in self.methods), '\n\t') if self.methods else '')+'\n};'
|
||||
|
||||
class Function(object):
|
||||
class Method(object):
|
||||
def __init__(self, name='', clss='', static=False, namespace='', rtp='', const=False, req=None, opt=None):
|
||||
self.name = name
|
||||
self.clss = clss
|
||||
self.constructor = True if name == clss else False
|
||||
self.static = static
|
||||
self.const = const
|
||||
self.namespace = namespace
|
||||
|
@ -1,14 +1,14 @@
|
||||
/*
|
||||
* compose
|
||||
* compose a function call
|
||||
* This macro takes as input a Function object and composes
|
||||
* This macro takes as input a Method object and composes
|
||||
* a function call by inspecting the types and argument names
|
||||
*/
|
||||
/
|
||||
{% macro compose(fun) %}
|
||||
{# ----------- Return type ------------- #}
|
||||
{%- if not fun.rtp|void -%} retval = {% endif -%}
|
||||
{%- if fun.clss -%}inst.{%- else -%} cv:: {%- endif -%}
|
||||
{%- if not fun.rtp|void and not fun.constructor -%} retval = {% endif -%}
|
||||
{%- if fun.constructor -%}{{fun.clss}} obj = {% endif -%}
|
||||
{%- if fun.clss and not fun.constructor -%}inst.{%- else -%} cv:: {%- endif -%}
|
||||
{{fun.name}}(
|
||||
{#- ----------- Required ------------- -#}
|
||||
{%- for arg in fun.req -%}
|
||||
@ -26,10 +26,39 @@
|
||||
);
|
||||
{%- endmacro %}
|
||||
|
||||
// create a full function invocation
|
||||
{%- macro generate(fun) -%}
|
||||
|
||||
{% if fun|ninputs or fun|noutputs %}
|
||||
/*
|
||||
* composeWithExceptionHandler
|
||||
* compose a function call wrapped in exception traps
|
||||
* This macro takes an input a Method object and composes a function
|
||||
* call through the compose() macro, then wraps the return in traps
|
||||
* for cv::Exceptions, std::exceptions, and all generic exceptions
|
||||
* and returns a useful error message to the Matlab interpreter
|
||||
*/
|
||||
{%- macro composeWithExceptionHandler(fun) -%}
|
||||
// call the opencv function
|
||||
// [out =] namespace.fun(src1, ..., srcn, dst1, ..., dstn, opt1, ..., optn);
|
||||
try {
|
||||
{{ compose(fun) }}
|
||||
} catch(cv::Exception& e) {
|
||||
error(std::string("cv::exception caught: ").append(e.what()).c_str());
|
||||
} catch(std::exception& e) {
|
||||
error(std::string("std::exception caught: ").append(e.what()).c_str());
|
||||
} catch(...) {
|
||||
error("Uncaught exception occurred in {{fun.name}}");
|
||||
}
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
/*
|
||||
* handleInputs
|
||||
* unpack input arguments from the Bridge
|
||||
* Given an input Bridge object, this unpacks the object from the Bridge and
|
||||
* casts them into the correct type
|
||||
*/
|
||||
{%- macro handleInputs(fun) %}
|
||||
|
||||
{% if fun|ninputs or (fun|noutputs and not fun.constructor) %}
|
||||
// unpack the arguments
|
||||
{# ----------- Inputs ------------- #}
|
||||
{% for arg in fun.req|inputs %}
|
||||
@ -45,26 +74,24 @@
|
||||
{% for opt in fun.opt|only|outputs %}
|
||||
{{opt.tp}} {{opt.name}};
|
||||
{% endfor %}
|
||||
{% if not fun.rtp|void %}
|
||||
{% if not fun.rtp|void and not fun.constructor %}
|
||||
{{fun.rtp}} retval;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
// call the opencv function
|
||||
// [out =] namespace.fun(src1, ..., srcn, dst1, ..., dstn, opt1, ..., optn);
|
||||
try {
|
||||
{{ compose(fun) }}
|
||||
} catch(cv::Exception& e) {
|
||||
mexErrMsgTxt(std::string("cv::exception caught: ").append(e.what()).c_str());
|
||||
} catch(std::exception& e) {
|
||||
mexErrMsgTxt(std::string("std::exception caught: ").append(e.what()).c_str());
|
||||
} catch(...) {
|
||||
mexErrMsgTxt("Uncaught exception occurred in {{fun.name}}");
|
||||
}
|
||||
{%- endmacro %}
|
||||
|
||||
/*
|
||||
* handleOutputs
|
||||
* pack outputs into the bridge
|
||||
* Given a set of outputs, this methods assigns them into the bridge for
|
||||
* return to the calling method
|
||||
*/
|
||||
{%- macro handleOutputs(fun) %}
|
||||
|
||||
{% if fun|noutputs %}
|
||||
// assign the outputs into the bridge
|
||||
{% if not fun.rtp|void %}
|
||||
{% if not fun.rtp|void and not fun.constructor %}
|
||||
outputs[0] = retval;
|
||||
{% endif %}
|
||||
{% for arg in fun.req|outputs %}
|
||||
@ -74,5 +101,4 @@
|
||||
outputs[{{loop.index0 + fun.rtp|void|not + fun.req|outputs|length}}] = {{opt.name}};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endmacro %}
|
||||
{%- endmacro %}
|
||||
|
@ -9,30 +9,41 @@
|
||||
* Copyright {{time.strftime("%Y", time.localtime())}} The OpenCV Foundation
|
||||
*/
|
||||
#include "mex.h"
|
||||
#include "map.hpp"
|
||||
#include "bridge.hpp"
|
||||
#include <vector>
|
||||
//TODO: Standard C++ does not have an unordered_map (only C++11 and Boost)
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <opencv2/core.hpp>
|
||||
using namespace cv;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef std::unordered_map Map;
|
||||
typedef std::vector<Bridge> (*)({{clss.name}}&, const std::vector<Bridge>&) MethodSignature;
|
||||
|
||||
{% for function in clss.functions %}
|
||||
// wrapper for {{function.name}}() method
|
||||
std::vector<Bridge> {{function.name}}({{clss.name}}& inst, const std::vector<Bridge>& args) {
|
||||
{{ functional.generate(function) }}
|
||||
}
|
||||
{% for function in clss.methods %}
|
||||
|
||||
{% if function.constructor %}
|
||||
// wrapper for {{function.name}}() constructor
|
||||
{{ function.clss }} {{function.name}}(const std::vector<Bridge>& inputs) {
|
||||
{{ functional.handleInputs(function) }}
|
||||
{{ functional.compose(function) }}
|
||||
return obj;
|
||||
}
|
||||
{% else %}
|
||||
// wrapper for {{function.name}}() method
|
||||
std::vector<Bridge> {{function.name}}({{clss.name}}& inst, const std::vector<Bridge>& inputs) {
|
||||
std::vector<Bridge> outputs{% if function|noutputs %}({{function|noutputs}}){% endif %};
|
||||
{{ functional.handleInputs(function) }}
|
||||
{{ functional.composeWithExceptionHandler(function) }}
|
||||
{{ functional.handleOutputs(function) }}
|
||||
return outputs;
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
map<std::string, MethodSignature> createMethodMap() {
|
||||
Map<std::string, MethodSignature> createMethodMap() {
|
||||
Map<std::string, MethodSignature> m;
|
||||
{% for function in clss.functions -%}
|
||||
{% for function in clss.methods %}
|
||||
m["{{function.name}}"] = &{{function.name}};
|
||||
{% endfor %}
|
||||
|
||||
@ -82,4 +93,4 @@ void mexFunction(int nlhs, mxArray* plhs[],
|
||||
|
||||
}
|
||||
|
||||
}; // end namespace
|
||||
} // end namespace
|
||||
|
@ -44,9 +44,11 @@ void mexFunction(int nlhs, mxArray*{% if fun|noutputs %} plhs[]{% else %}*{% end
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{{ functional.generate(fun) }}
|
||||
{{ functional.handleInputs(fun) }}
|
||||
{{ functional.composeWithExceptionHandler(fun) }}
|
||||
{{ functional.handleOutputs(fun) }}
|
||||
|
||||
{%- if fun|noutputs %}
|
||||
{% if fun|noutputs %}
|
||||
// push the outputs back to matlab
|
||||
for (size_t n = 0; n < static_cast<size_t>(nlhs); ++n) {
|
||||
plhs[n] = outputs[n].toMxArray().releaseOwnership();
|
||||
|
@ -1,34 +1,12 @@
|
||||
#ifndef OPENCV_BRIDGE_HPP_
|
||||
#define OPENCV_BRIDGE_HPP_
|
||||
|
||||
#include "mex.h"
|
||||
#include "map.hpp"
|
||||
#include "mxarray.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/calib3d.hpp>
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -46,427 +24,6 @@ typedef cv::Ptr<cv::StereoSGBM> Ptr_StereoSGBM;
|
||||
typedef cv::Ptr<cv::FeatureDetector> Ptr_FeatureDetector;
|
||||
|
||||
|
||||
/*!
|
||||
* @brief raise error if condition fails
|
||||
*
|
||||
* This is a conditional wrapper for mexErrMsgTxt. If the conditional
|
||||
* expression fails, an error is raised and the mex function returns
|
||||
* to Matlab, otherwise this function does nothing
|
||||
*/
|
||||
void conditionalError(bool expr, const std::string& str) {
|
||||
if (!expr) mexErrMsgTxt(std::string("condition failed: ").append(str).c_str());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief raise an error
|
||||
*
|
||||
* This function is a wrapper around mexErrMsgTxt
|
||||
*/
|
||||
void error(const std::string& str) {
|
||||
mexErrMsgTxt(str.c_str());
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PREDECLARATIONS
|
||||
// ----------------------------------------------------------------------------
|
||||
class MxArray;
|
||||
class Bridge;
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const cv::Mat& src, MxArray& dst);
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const MxArray& src, cv::Mat& dst);
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// MATLAB TRAITS
|
||||
// ----------------------------------------------------------------------------
|
||||
namespace Matlab {
|
||||
class DefaultTraits {};
|
||||
class InheritType {};
|
||||
static const int Dynamic = -1;
|
||||
|
||||
template<typename _Tp = DefaultTraits> class Traits {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUNKNOWN_CLASS;
|
||||
static const mxComplexity Complex = mxCOMPLEX;
|
||||
static const mxComplexity Real = mxCOMPLEX;
|
||||
static std::string ToString() { return "Unknown/Unsupported"; }
|
||||
};
|
||||
// bool
|
||||
template<> class Traits<bool> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxLOGICAL_CLASS;
|
||||
static std::string ToString() { return "boolean"; }
|
||||
};
|
||||
// uint8_t
|
||||
template<> class Traits<uint8_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT8_CLASS;
|
||||
static std::string ToString() { return "uint8_t"; }
|
||||
};
|
||||
// int8_t
|
||||
template<> class Traits<int8_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT8_CLASS;
|
||||
static std::string ToString() { return "int8_t"; }
|
||||
};
|
||||
// uint16_t
|
||||
template<> class Traits<uint16_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT16_CLASS;
|
||||
static std::string ToString() { return "uint16_t"; }
|
||||
};
|
||||
// int16_t
|
||||
template<> class Traits<int16_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT16_CLASS;
|
||||
static std::string ToString() { return "int16_t"; }
|
||||
};
|
||||
// uint32_t
|
||||
template<> class Traits<uint32_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT32_CLASS;
|
||||
static std::string ToString() { return "uint32_t"; }
|
||||
};
|
||||
// int32_t
|
||||
template<> class Traits<int32_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT32_CLASS;
|
||||
static std::string ToString() { return "int32_t"; }
|
||||
};
|
||||
// uint64_t
|
||||
template<> class Traits<uint64_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT64_CLASS;
|
||||
static std::string ToString() { return "uint64_t"; }
|
||||
};
|
||||
// int64_t
|
||||
template<> class Traits<int64_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT64_CLASS;
|
||||
static std::string ToString() { return "int64_t"; }
|
||||
};
|
||||
// float
|
||||
template<> class Traits<float> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxSINGLE_CLASS;
|
||||
static std::string ToString() { return "float"; }
|
||||
};
|
||||
// double
|
||||
template<> class Traits<double> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxDOUBLE_CLASS;
|
||||
static std::string ToString() { return "double"; }
|
||||
};
|
||||
// size_t
|
||||
template<> class Traits<size_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = (sizeof(size_t) == 4) ? mxUINT32_CLASS : mxUINT64_CLASS;
|
||||
static std::string ToString() { return "size_t"; }
|
||||
};
|
||||
// char
|
||||
template<> class Traits<char> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxCHAR_CLASS;
|
||||
static std::string ToString() { return "char"; }
|
||||
};
|
||||
// char
|
||||
template<> class Traits<Matlab::InheritType> {
|
||||
public:
|
||||
static std::string ToString() { return "Inherited type"; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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. MxArray objects are scoped,
|
||||
* so you can freely create and destroy them without worrying about memory
|
||||
* management. If you wish to pass the underlying mxArray* representation
|
||||
* back to Matlab as an lvalue, see the releaseOwnership() method
|
||||
*/
|
||||
class MxArray {
|
||||
private:
|
||||
mxArray* ptr_;
|
||||
bool owns_;
|
||||
|
||||
void dealloc() {
|
||||
if (owns_ && ptr_) { mxDestroyArray(ptr_); ptr_ = NULL; owns_ = false; }
|
||||
}
|
||||
public:
|
||||
// constructors and destructor
|
||||
MxArray() : ptr_(mxCreateDoubleMatrix(1, 1, Matlab::Traits<>::Real)), owns_(true) {}
|
||||
MxArray(const mxArray* ptr) : ptr_(const_cast<mxArray *>(ptr)), owns_(false) {}
|
||||
virtual ~MxArray() {
|
||||
dealloc();
|
||||
}
|
||||
// copy constructor
|
||||
// all copies are deep copies
|
||||
MxArray(const MxArray& other) : ptr_(mxDuplicateArray(other.ptr_)), owns_(true) {}
|
||||
// swap
|
||||
friend void swap(MxArray& first, MxArray& second) {
|
||||
using std::swap;
|
||||
swap(first.ptr_, second.ptr_);
|
||||
swap(first.owns_, second.owns_);
|
||||
}
|
||||
// assignment operator
|
||||
// copy-and-swap idiom
|
||||
// all copies are deep copies
|
||||
MxArray& operator=(MxArray other) {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
#if __cplusplus >= 201103L
|
||||
// move constructor, if C++11
|
||||
// default construct and swap
|
||||
MxArray(MxArray&& other) : MxArray() {
|
||||
swap(*this, other);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @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<double>(5, 5); // allocates memory
|
||||
* MxArray B<double>(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_;
|
||||
}
|
||||
|
||||
|
||||
template <typename Scalar>
|
||||
explicit MxArray(size_t m, size_t n, size_t k=1) : owns_(true) {
|
||||
mwSize dims[] = { static_cast<mwSize>(m), static_cast<mwSize>(n), static_cast<mwSize>(k) };
|
||||
ptr_ = mxCreateNumericArray(3, dims, Matlab::Traits<Scalar>::ScalarType, Matlab::Traits<>::Real);
|
||||
}
|
||||
|
||||
// this function is called exclusively from constructors!!
|
||||
template <typename Scalar>
|
||||
MxArray& fromMat(const cv::Mat& mat) {
|
||||
// dealloc any existing storage before reallocating
|
||||
dealloc();
|
||||
mwSize dims[] = { static_cast<mwSize>(mat.rows), static_cast<mwSize>(mat.cols), static_cast<mwSize>(mat.channels()) };
|
||||
ptr_ = mxCreateNumericArray(3, dims, Matlab::Traits<Scalar>::ScalarType, Matlab::Traits<>::Real);
|
||||
owns_ = true;
|
||||
switch (mat.depth()) {
|
||||
case CV_8U: deepCopyAndTranspose<uint8_t, Scalar>(mat, *this); break;
|
||||
case CV_8S: deepCopyAndTranspose<int8_t, Scalar>(mat, *this); break;
|
||||
case CV_16U: deepCopyAndTranspose<uint16_t, Scalar>(mat, *this); break;
|
||||
case CV_16S: deepCopyAndTranspose<int16_t, Scalar>(mat, *this); break;
|
||||
case CV_32S: deepCopyAndTranspose<int32_t, Scalar>(mat, *this); break;
|
||||
case CV_32F: deepCopyAndTranspose<float, Scalar>(mat, *this); break;
|
||||
case CV_64F: deepCopyAndTranspose<double, Scalar>(mat, *this); break;
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
cv::Mat toMat() const {
|
||||
cv::Mat mat(cols(), rows(), CV_MAKETYPE(cv::DataType<Scalar>::type, channels()));
|
||||
switch (ID()) {
|
||||
case mxINT8_CLASS: deepCopyAndTranspose<int8_t, Scalar>(*this, mat); break;
|
||||
case mxUINT8_CLASS: deepCopyAndTranspose<uint8_t, Scalar>(*this, mat); break;
|
||||
case mxINT16_CLASS: deepCopyAndTranspose<int16_t, Scalar>(*this, mat); break;
|
||||
case mxUINT16_CLASS: deepCopyAndTranspose<uint16_t, Scalar>(*this, mat); break;
|
||||
case mxINT32_CLASS: deepCopyAndTranspose<int32_t, Scalar>(*this, mat); break;
|
||||
case mxUINT32_CLASS: deepCopyAndTranspose<uint32_t, Scalar>(*this, mat); break;
|
||||
case mxINT64_CLASS: deepCopyAndTranspose<int64_t, Scalar>(*this, mat); break;
|
||||
case mxUINT64_CLASS: deepCopyAndTranspose<uint64_t, Scalar>(*this, mat); break;
|
||||
case mxSINGLE_CLASS: deepCopyAndTranspose<float, Scalar>(*this, mat); break;
|
||||
case mxDOUBLE_CLASS: deepCopyAndTranspose<double, Scalar>(*this, mat); break;
|
||||
case mxCHAR_CLASS: deepCopyAndTranspose<char, Scalar>(*this, mat); break;
|
||||
case mxLOGICAL_CLASS: deepCopyAndTranspose<int8_t, Scalar>(*this, mat); break;
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
MxArray field(const std::string& name) { return MxArray(mxGetField(ptr_, 0, name.c_str())); }
|
||||
|
||||
template <typename Scalar>
|
||||
Scalar* real() { return static_cast<Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
Scalar* imag() { return static_cast<Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
const Scalar* real() const { return static_cast<const Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
const Scalar* imag() const { return static_cast<const Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
Scalar scalar() const { return static_cast<Scalar *>(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<char *>(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_); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* @brief template specialization for inheriting types
|
||||
*
|
||||
* This template specialization attempts to preserve the best mapping
|
||||
* between OpenCV and Matlab types. Matlab uses double types almost universally, so
|
||||
* all floating float types are converted to doubles.
|
||||
* Unfortunately OpenCV does not have a native logical type, so
|
||||
* that gets mapped to an unsigned 8-bit value
|
||||
*/
|
||||
template <>
|
||||
MxArray& MxArray::fromMat<Matlab::InheritType>(const cv::Mat& mat) {
|
||||
switch (mat.depth()) {
|
||||
case CV_8U: return fromMat<uint8_t>(mat); break;
|
||||
case CV_8S: return fromMat<int8_t>(mat); break;
|
||||
case CV_16U: return fromMat<uint16_t>(mat); break;
|
||||
case CV_16S: return fromMat<int16_t>(mat); break;
|
||||
case CV_32S: return fromMat<int32_t>(mat); break;
|
||||
case CV_32F: return fromMat<double>(mat); break; //NOTE: Matlab uses double as native type!
|
||||
case CV_64F: return fromMat<double>(mat); break;
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief template specialization for inheriting types
|
||||
*
|
||||
* This template specialization attempts to preserve the best mapping
|
||||
* between Matlab and OpenCV types. OpenCV has poor support for double precision
|
||||
* types, so all floating point types are cast to float. Logicals get cast
|
||||
* to unsignd 8-bit value.
|
||||
*/
|
||||
template <>
|
||||
cv::Mat MxArray::toMat<Matlab::InheritType>() const {
|
||||
switch (ID()) {
|
||||
case mxINT8_CLASS: return toMat<int8_t>();
|
||||
case mxUINT8_CLASS: return toMat<uint8_t>();;
|
||||
case mxINT16_CLASS: return toMat<int16_t>();
|
||||
case mxUINT16_CLASS: return toMat<uint16_t>();
|
||||
case mxINT32_CLASS: return toMat<int32_t>();
|
||||
case mxUINT32_CLASS: return toMat<int32_t>();
|
||||
case mxINT64_CLASS: return toMat<int64_t>();
|
||||
case mxUINT64_CLASS: return toMat<int64_t>();
|
||||
case mxSINGLE_CLASS: return toMat<float>();
|
||||
case mxDOUBLE_CLASS: return toMat<float>(); //NOTE: OpenCV uses float as native type!
|
||||
case mxCHAR_CLASS: return toMat<int8_t>();
|
||||
case mxLOGICAL_CLASS: return toMat<int8_t>();
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// MATRIX TRANSPOSE
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const cv::Mat& in, MxArray& out) {
|
||||
conditionalError(static_cast<size_t>(in.rows) == out.rows(), "Matrices must have the same number of rows");
|
||||
conditionalError(static_cast<size_t>(in.cols) == out.cols(), "Matrices must have the same number of cols");
|
||||
conditionalError(static_cast<size_t>(in.channels()) == out.channels(), "Matrices must have the same number of channels");
|
||||
OutputScalar* outp = out.real<OutputScalar>();
|
||||
const size_t M = out.rows();
|
||||
const size_t N = out.cols();
|
||||
for (size_t m = 0; m < M; ++m) {
|
||||
const InputScalar* inp = in.ptr<InputScalar>(m);
|
||||
for (size_t n = 0; n < N; ++n) {
|
||||
// copy and transpose
|
||||
outp[m + n*M] = inp[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const MxArray& in, cv::Mat& out) {
|
||||
conditionalError(in.rows() == static_cast<size_t>(out.rows), "Matrices must have the same number of rows");
|
||||
conditionalError(in.cols() == static_cast<size_t>(out.cols), "Matrices must have the same number of cols");
|
||||
conditionalError(in.channels() == static_cast<size_t>(out.channels()), "Matrices must have the same number of channels");
|
||||
const InputScalar* inp = in.real<InputScalar>();
|
||||
const size_t M = in.rows();
|
||||
const size_t N = in.cols();
|
||||
for (size_t m = 0; m < M; ++m) {
|
||||
OutputScalar* outp = out.ptr<OutputScalar>(m);
|
||||
for (size_t n = 0; n < N; ++n) {
|
||||
// copy and transpose
|
||||
outp[n] = inp[m + n*M];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<float, float>(const cv::Mat&, MxArray&) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<double, double>(const cv::Mat&, MxArray&) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<float, float>(const MxArray&, cv::Mat&) {
|
||||
// use mkl
|
||||
}
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<double, double>(const MxArray&, cv::Mat& ) {
|
||||
// use mkl
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -613,7 +170,7 @@ public:
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// --------------------------- cv::Mat --------------------------------------
|
||||
Bridge& operator=(const cv::Mat& mat) { ptr_ = MxArray().fromMat<Matlab::InheritType>(mat); return *this; }
|
||||
Bridge& operator=(const cv::Mat& mat) { ptr_ = MxArray::FromMat<Matlab::InheritType>(mat); return *this; }
|
||||
cv::Mat toMat() const { return ptr_.toMat<Matlab::InheritType>(); }
|
||||
operator cv::Mat() const { return toMat(); }
|
||||
|
||||
|
547
modules/matlab/include/mxarray.hpp
Normal file
547
modules/matlab/include/mxarray.hpp
Normal file
@ -0,0 +1,547 @@
|
||||
#ifndef OPENCV_MXARRAY_HPP_
|
||||
#define OPENCV_MXARRAY_HPP_
|
||||
|
||||
#include "mex.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
/*!
|
||||
* @brief raise error if condition fails
|
||||
*
|
||||
* This is a conditional wrapper for mexErrMsgTxt. If the conditional
|
||||
* expression fails, an error is raised and the mex function returns
|
||||
* to Matlab, otherwise this function does nothing
|
||||
*/
|
||||
void conditionalError(bool expr, const std::string& str) {
|
||||
if (!expr) mexErrMsgTxt(std::string("condition failed: ").append(str).c_str());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief raise an error
|
||||
*
|
||||
* This function is a wrapper around mexErrMsgTxt
|
||||
*/
|
||||
void error(const std::string& str) {
|
||||
mexErrMsgTxt(str.c_str());
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PREDECLARATIONS
|
||||
// ----------------------------------------------------------------------------
|
||||
class MxArray;
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const cv::Mat& src, MxArray& dst);
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const MxArray& src, cv::Mat& dst);
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// MATLAB TRAITS
|
||||
// ----------------------------------------------------------------------------
|
||||
namespace Matlab {
|
||||
class DefaultTraits {};
|
||||
class InheritType {};
|
||||
static const int Dynamic = -1;
|
||||
|
||||
template<typename _Tp = DefaultTraits> class Traits {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUNKNOWN_CLASS;
|
||||
static const mxComplexity Complex = mxCOMPLEX;
|
||||
static const mxComplexity Real = mxCOMPLEX;
|
||||
static std::string ToString() { return "Unknown/Unsupported"; }
|
||||
};
|
||||
// bool
|
||||
template<> class Traits<bool> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxLOGICAL_CLASS;
|
||||
static std::string ToString() { return "boolean"; }
|
||||
};
|
||||
// uint8_t
|
||||
template<> class Traits<uint8_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT8_CLASS;
|
||||
static std::string ToString() { return "uint8_t"; }
|
||||
};
|
||||
// int8_t
|
||||
template<> class Traits<int8_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT8_CLASS;
|
||||
static std::string ToString() { return "int8_t"; }
|
||||
};
|
||||
// uint16_t
|
||||
template<> class Traits<uint16_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT16_CLASS;
|
||||
static std::string ToString() { return "uint16_t"; }
|
||||
};
|
||||
// int16_t
|
||||
template<> class Traits<int16_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT16_CLASS;
|
||||
static std::string ToString() { return "int16_t"; }
|
||||
};
|
||||
// uint32_t
|
||||
template<> class Traits<uint32_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT32_CLASS;
|
||||
static std::string ToString() { return "uint32_t"; }
|
||||
};
|
||||
// int32_t
|
||||
template<> class Traits<int32_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT32_CLASS;
|
||||
static std::string ToString() { return "int32_t"; }
|
||||
};
|
||||
// uint64_t
|
||||
template<> class Traits<uint64_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxUINT64_CLASS;
|
||||
static std::string ToString() { return "uint64_t"; }
|
||||
};
|
||||
// int64_t
|
||||
template<> class Traits<int64_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxINT64_CLASS;
|
||||
static std::string ToString() { return "int64_t"; }
|
||||
};
|
||||
// float
|
||||
template<> class Traits<float> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxSINGLE_CLASS;
|
||||
static std::string ToString() { return "float"; }
|
||||
};
|
||||
// double
|
||||
template<> class Traits<double> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxDOUBLE_CLASS;
|
||||
static std::string ToString() { return "double"; }
|
||||
};
|
||||
// size_t
|
||||
template<> class Traits<size_t> {
|
||||
public:
|
||||
static const mxClassID ScalarType = (sizeof(size_t) == 4) ? mxUINT32_CLASS : mxUINT64_CLASS;
|
||||
static std::string ToString() { return "size_t"; }
|
||||
};
|
||||
// char
|
||||
template<> class Traits<char> {
|
||||
public:
|
||||
static const mxClassID ScalarType = mxCHAR_CLASS;
|
||||
static std::string ToString() { return "char"; }
|
||||
};
|
||||
// char
|
||||
template<> class Traits<Matlab::InheritType> {
|
||||
public:
|
||||
static std::string ToString() { return "Inherited type"; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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. MxArray objects are scoped,
|
||||
* so you can freely create and destroy them without worrying about memory
|
||||
* management. If you wish to pass the underlying mxArray* representation
|
||||
* back to Matlab as an lvalue, see the releaseOwnership() method
|
||||
*
|
||||
* MxArrays can be directly converted into OpenCV mat objects and std::string
|
||||
* objects, since there is a natural mapping between these types. More
|
||||
* complex types are mapped through the Bridge which does custom conversions
|
||||
* such as MxArray --> cv::Keypoints, etc
|
||||
*/
|
||||
class MxArray {
|
||||
private:
|
||||
mxArray* ptr_;
|
||||
bool owns_;
|
||||
|
||||
/*!
|
||||
* @brief swap all members of this and other
|
||||
*
|
||||
* the swap method is used by the assignment and move constructors
|
||||
* to swap the members of two MxArrays, leaving both in destructible states
|
||||
*/
|
||||
friend void swap(MxArray& first, MxArray& second) {
|
||||
using std::swap;
|
||||
swap(first.ptr_, second.ptr_);
|
||||
swap(first.owns_, second.owns_);
|
||||
}
|
||||
|
||||
void dealloc() {
|
||||
if (owns_ && ptr_) { mxDestroyArray(ptr_); ptr_ = NULL; owns_ = false; }
|
||||
}
|
||||
public:
|
||||
// --------------------------------------------------------------------------
|
||||
// CONSTRUCTORS
|
||||
// --------------------------------------------------------------------------
|
||||
/*!
|
||||
* @brief default constructor
|
||||
*
|
||||
* Construct a valid 0x0 matrix (so all other methods do not need validity checks
|
||||
*/
|
||||
MxArray() : ptr_(mxCreateDoubleMatrix(1, 1, Matlab::Traits<>::Real)), owns_(true) {}
|
||||
|
||||
/*!
|
||||
* @brief inheriting constructor
|
||||
*
|
||||
* Inherit an mxArray from Matlab. Don't claim ownership of the array,
|
||||
* just encapsulate it
|
||||
*/
|
||||
MxArray(const mxArray* ptr) : ptr_(const_cast<mxArray *>(ptr)), owns_(false) {}
|
||||
|
||||
/*!
|
||||
* @brief explicit typed constructor
|
||||
*
|
||||
* This constructor explicitly creates an MxArray of the given size and type.
|
||||
*/
|
||||
MxArray(size_t m, size_t n, size_t k, mxClassID id, mxComplexity com = Matlab::Traits<>::Real) : owns_(true) {
|
||||
mwSize dims[] = { static_cast<mwSize>(m), static_cast<mwSize>(n), static_cast<mwSize>(k) };
|
||||
ptr_ = mxCreateNumericArray(3, dims, id, com);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief explicit tensor constructor
|
||||
*
|
||||
* Explicitly construct a tensor of given size and type. Since constructors cannot
|
||||
* be explicitly templated, this is a static factory method
|
||||
*/
|
||||
template <typename Scalar>
|
||||
static MxArray Tensor(size_t m, size_t n, size_t k=1) {
|
||||
return MxArray(m, n, k, Matlab::Traits<Scalar>::ScalarType);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief explicit matrix constructor
|
||||
*
|
||||
* Explicitly construct a matrix of given size and type. Since constructors cannot
|
||||
* be explicitly templated, this is a static factory method
|
||||
*/
|
||||
template <typename Scalar>
|
||||
static MxArray Matrix(size_t m, size_t n) {
|
||||
return MxArray(m, n, 1, Matlab::Traits<Scalar>::ScalarType);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief explicit vector constructor
|
||||
*
|
||||
* Explicitly construct a vector of given size and type. Since constructors cannot
|
||||
* be explicitly templated, this is a static factory method
|
||||
*/
|
||||
template <typename Scalar>
|
||||
static MxArray Vector(size_t m) {
|
||||
return MxArray(m, 1, 1, Matlab::Traits<Scalar>::ScalarType);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief explicit scalar constructor
|
||||
*
|
||||
* Explicitly construct a scalar of given type. Since constructors cannot
|
||||
* be explicitly templated, this is a static factory method
|
||||
*/
|
||||
template <typename Scalar>
|
||||
static MxArray Scalar(Scalar value = 0) {
|
||||
MxArray s(1, 1, 1, Matlab::Traits<Scalar>::ScalarType);
|
||||
s.real<Scalar>()[0] = value;
|
||||
return s;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief destructor
|
||||
*
|
||||
* The destructor deallocates any data allocated by mxCreate* methods only
|
||||
* if the object is owned
|
||||
*/
|
||||
virtual ~MxArray() {
|
||||
dealloc();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief copy constructor
|
||||
*
|
||||
* All copies are deep copies. If you have a C++11 compatible compiler, prefer
|
||||
* move construction to copy construction
|
||||
*/
|
||||
MxArray(const MxArray& other) : ptr_(mxDuplicateArray(other.ptr_)), owns_(true) {}
|
||||
|
||||
/*!
|
||||
* @brief copy-and-swap assignment
|
||||
*
|
||||
* This assignment operator uses the copy and swap idiom to provide a strong
|
||||
* exception guarantee when swapping two objects.
|
||||
*
|
||||
* Note in particular that the other MxArray is passed by value, thus invoking
|
||||
* the copy constructor which performs a deep copy of the input. The members of
|
||||
* this and other are then swapped
|
||||
*/
|
||||
MxArray& operator=(MxArray other) {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
#if __cplusplus >= 201103L
|
||||
/*
|
||||
* @brief C++11 move constructor
|
||||
*
|
||||
* When C++11 support is available, move construction is used to move returns
|
||||
* out of functions, etc. This is much fast than copy construction, since the
|
||||
* move constructed object replaced itself with a default constructed MxArray,
|
||||
* which is of size 0 x 0.
|
||||
*/
|
||||
MxArray(MxArray&& other) : MxArray() {
|
||||
swap(*this, other);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @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<double>(5, 5); // allocates memory
|
||||
* MxArray B<double>(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_;
|
||||
}
|
||||
|
||||
|
||||
template <typename Scalar>
|
||||
static MxArray FromMat(const cv::Mat& mat) {
|
||||
MxArray arr(mat.rows, mat.cols, mat.channels(), Matlab::Traits<Scalar>::ScalarType);
|
||||
switch (mat.depth()) {
|
||||
case CV_8U: deepCopyAndTranspose<uint8_t, Scalar>(mat, arr); break;
|
||||
case CV_8S: deepCopyAndTranspose<int8_t, Scalar>(mat, arr); break;
|
||||
case CV_16U: deepCopyAndTranspose<uint16_t, Scalar>(mat, arr); break;
|
||||
case CV_16S: deepCopyAndTranspose<int16_t, Scalar>(mat, arr); break;
|
||||
case CV_32S: deepCopyAndTranspose<int32_t, Scalar>(mat, arr); break;
|
||||
case CV_32F: deepCopyAndTranspose<float, Scalar>(mat, arr); break;
|
||||
case CV_64F: deepCopyAndTranspose<double, Scalar>(mat, arr); break;
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
cv::Mat toMat() const {
|
||||
cv::Mat mat(cols(), rows(), CV_MAKETYPE(cv::DataType<Scalar>::type, channels()));
|
||||
switch (ID()) {
|
||||
case mxINT8_CLASS: deepCopyAndTranspose<int8_t, Scalar>(*this, mat); break;
|
||||
case mxUINT8_CLASS: deepCopyAndTranspose<uint8_t, Scalar>(*this, mat); break;
|
||||
case mxINT16_CLASS: deepCopyAndTranspose<int16_t, Scalar>(*this, mat); break;
|
||||
case mxUINT16_CLASS: deepCopyAndTranspose<uint16_t, Scalar>(*this, mat); break;
|
||||
case mxINT32_CLASS: deepCopyAndTranspose<int32_t, Scalar>(*this, mat); break;
|
||||
case mxUINT32_CLASS: deepCopyAndTranspose<uint32_t, Scalar>(*this, mat); break;
|
||||
case mxINT64_CLASS: deepCopyAndTranspose<int64_t, Scalar>(*this, mat); break;
|
||||
case mxUINT64_CLASS: deepCopyAndTranspose<uint64_t, Scalar>(*this, mat); break;
|
||||
case mxSINGLE_CLASS: deepCopyAndTranspose<float, Scalar>(*this, mat); break;
|
||||
case mxDOUBLE_CLASS: deepCopyAndTranspose<double, Scalar>(*this, mat); break;
|
||||
case mxCHAR_CLASS: deepCopyAndTranspose<char, Scalar>(*this, mat); break;
|
||||
case mxLOGICAL_CLASS: deepCopyAndTranspose<int8_t, Scalar>(*this, mat); break;
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
MxArray field(const std::string& name) { return MxArray(mxGetField(ptr_, 0, name.c_str())); }
|
||||
|
||||
template <typename Scalar>
|
||||
Scalar* real() { return static_cast<Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
Scalar* imag() { return static_cast<Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
const Scalar* real() const { return static_cast<const Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
const Scalar* imag() const { return static_cast<const Scalar *>(mxGetData(ptr_)); }
|
||||
|
||||
template <typename Scalar>
|
||||
Scalar scalar() const { return static_cast<Scalar *>(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<char *>(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_); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* @brief template specialization for inheriting types
|
||||
*
|
||||
* This template specialization attempts to preserve the best mapping
|
||||
* between OpenCV and Matlab types. Matlab uses double types almost universally, so
|
||||
* all floating float types are converted to doubles.
|
||||
* Unfortunately OpenCV does not have a native logical type, so
|
||||
* that gets mapped to an unsigned 8-bit value
|
||||
*/
|
||||
template <>
|
||||
MxArray MxArray::FromMat<Matlab::InheritType>(const cv::Mat& mat) {
|
||||
switch (mat.depth()) {
|
||||
case CV_8U: return FromMat<uint8_t>(mat);
|
||||
case CV_8S: return FromMat<int8_t>(mat);
|
||||
case CV_16U: return FromMat<uint16_t>(mat);
|
||||
case CV_16S: return FromMat<int16_t>(mat);
|
||||
case CV_32S: return FromMat<int32_t>(mat);
|
||||
case CV_32F: return FromMat<double>(mat); //NOTE: Matlab uses double as native type!
|
||||
case CV_64F: return FromMat<double>(mat);
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return MxArray();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief template specialization for inheriting types
|
||||
*
|
||||
* This template specialization attempts to preserve the best mapping
|
||||
* between Matlab and OpenCV types. OpenCV has poor support for double precision
|
||||
* types, so all floating point types are cast to float. Logicals get cast
|
||||
* to unsignd 8-bit value.
|
||||
*/
|
||||
template <>
|
||||
cv::Mat MxArray::toMat<Matlab::InheritType>() const {
|
||||
switch (ID()) {
|
||||
case mxINT8_CLASS: return toMat<int8_t>();
|
||||
case mxUINT8_CLASS: return toMat<uint8_t>();;
|
||||
case mxINT16_CLASS: return toMat<int16_t>();
|
||||
case mxUINT16_CLASS: return toMat<uint16_t>();
|
||||
case mxINT32_CLASS: return toMat<int32_t>();
|
||||
case mxUINT32_CLASS: return toMat<int32_t>();
|
||||
case mxINT64_CLASS: return toMat<int64_t>();
|
||||
case mxUINT64_CLASS: return toMat<int64_t>();
|
||||
case mxSINGLE_CLASS: return toMat<float>();
|
||||
case mxDOUBLE_CLASS: return toMat<float>(); //NOTE: OpenCV uses float as native type!
|
||||
case mxCHAR_CLASS: return toMat<int8_t>();
|
||||
case mxLOGICAL_CLASS: return toMat<int8_t>();
|
||||
default: error("Attempted to convert from unknown class");
|
||||
}
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// MATRIX TRANSPOSE
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const cv::Mat& in, MxArray& out) {
|
||||
conditionalError(static_cast<size_t>(in.rows) == out.rows(), "Matrices must have the same number of rows");
|
||||
conditionalError(static_cast<size_t>(in.cols) == out.cols(), "Matrices must have the same number of cols");
|
||||
conditionalError(static_cast<size_t>(in.channels()) == out.channels(), "Matrices must have the same number of channels");
|
||||
OutputScalar* outp = out.real<OutputScalar>();
|
||||
const size_t M = out.rows();
|
||||
const size_t N = out.cols();
|
||||
for (size_t m = 0; m < M; ++m) {
|
||||
const InputScalar* inp = in.ptr<InputScalar>(m);
|
||||
for (size_t n = 0; n < N; ++n) {
|
||||
// copy and transpose
|
||||
outp[m + n*M] = inp[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputScalar, typename OutputScalar>
|
||||
void deepCopyAndTranspose(const MxArray& in, cv::Mat& out) {
|
||||
conditionalError(in.rows() == static_cast<size_t>(out.rows), "Matrices must have the same number of rows");
|
||||
conditionalError(in.cols() == static_cast<size_t>(out.cols), "Matrices must have the same number of cols");
|
||||
conditionalError(in.channels() == static_cast<size_t>(out.channels()), "Matrices must have the same number of channels");
|
||||
const InputScalar* inp = in.real<InputScalar>();
|
||||
const size_t M = in.rows();
|
||||
const size_t N = in.cols();
|
||||
for (size_t m = 0; m < M; ++m) {
|
||||
OutputScalar* outp = out.ptr<OutputScalar>(m);
|
||||
for (size_t n = 0; n < N; ++n) {
|
||||
// copy and transpose
|
||||
outp[n] = inp[m + n*M];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<float, float>(const cv::Mat&, MxArray&) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<double, double>(const cv::Mat&, MxArray&) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<float, float>(const MxArray&, cv::Mat&) {
|
||||
// use mkl
|
||||
}
|
||||
|
||||
template <>
|
||||
void deepCopyAndTranspose<double, double>(const MxArray&, cv::Mat& ) {
|
||||
// use mkl
|
||||
}
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user