Split MxArray out of bridge into standalone file. Does not depend on bridge. Working...
authorhbristow <hilton.bristow@gmail.com>
Tue, 2 Jul 2013 23:42:37 +0000 (16:42 -0700)
committerhbristow <hilton.bristow@gmail.com>
Tue, 2 Jul 2013 23:42:37 +0000 (16:42 -0700)
modules/matlab/generator/gen_matlab.py
modules/matlab/generator/parse_tree.py
modules/matlab/generator/templates/functional.cpp
modules/matlab/generator/templates/template_class_base.cpp
modules/matlab/generator/templates/template_function_base.cpp
modules/matlab/include/bridge.hpp
modules/matlab/include/mxarray.hpp [new file with mode: 0644]

index f53c5ae..34b306c 100644 (file)
@@ -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:
index c038994..e70bc30 100644 (file)
@@ -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
index a478a73..8e2e9d0 100644 (file)
@@ -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 -%} 
   );
 {%- 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 %}
   {% 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 %}
   outputs[{{loop.index0 + fun.rtp|void|not + fun.req|outputs|length}}] = {{opt.name}};
   {% endfor %}
   {% endif %}
-  
-{% endmacro %}
+{%- endmacro %}
index 872f563..f353f75 100644 (file)
@@ -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 %}
+{% 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>& args) {
-  {{ functional.generate(function) }}
+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
index 3fb41fe..5089455 100644 (file)
@@ -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();
index f976ef6..8333f0e 100644 (file)
@@ -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(); }
   
diff --git a/modules/matlab/include/mxarray.hpp b/modules/matlab/include/mxarray.hpp
new file mode 100644 (file)
index 0000000..65ec4ec
--- /dev/null
@@ -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