diff --git a/modules/matlab/README.md b/modules/matlab/README.md index 687cdff53d..0c8074818f 100644 --- a/modules/matlab/README.md +++ b/modules/matlab/README.md @@ -59,22 +59,23 @@ The first thing you need to learn how to do is write a mex-file with Matlab cons // this automatically includes opencv core.hpp and mex.h) #include using namespace cv; -using namespace std; +using namespace matlab; +using namespace bridge; // define the mex gateway void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // claim the inputs into scoped management - MxArrayVector raw_inputs(prhs, prhs+nrhs); + MxArrayVector raw(prhs, prhs+nrhs); // add an argument parser to automatically handle basic options ArgumentParser parser("my function"); parser.addVariant(1, 1, "opt"); - MxArrayVector parsed_inputs = parser.parse(inputs); + MxArrayVector reordered = parser.parse(raw); - // if we get here, we know the inputs are valid. Unpack... - BridgeVector inputs(parsed_inputs); + // if we get here, we know the inputs are valid and reordered. Unpack... + BridgeVector inputs(reordered.begin(), reordered.end()); Mat required = inputs[0].toMat(); string optional = inputs[1].empty() ? "Default string" : inputs[1].toString(); @@ -339,6 +340,37 @@ The MxArray object uses scoped memory management. If you wish to pass an MxArray plhs[0] = mat.releaseOwnership(); ``` +mxarray.hpp also includes a number of helper utilities that make working in mex-world a little easier. One such utility is the `ArgumentParser`. `ArgumentParser` automatically handles required and optional arguments to a method, and even enables named arguments as used in many core Matlab functions. For example, if you had a function with the following signature: + +```cpp +void f(Mat first, Mat second, Mat mask=Mat(), int dtype=-1); +``` + +then you can create an `ArgumentParser` as follows: + +```cpp +ArgumentParser parser("f"); +parser.addVariant(2, 2, "mask", "dtype"); +MxArrayVector inputs = parser.parse(prhs, prhs+nrhs); +``` + +and that will make available the following calling syntaxes: + +```matlab +f(first, second); +f(first, second, mask); +f(first, second, mask, dtype); +f(first, second, 'dtype', dtype, 'mask', mask); % optional ordering does not matter +f(first, second, 'dtype', dtype); % only second optional argument provided +f(first, second, mask, 'dtype', dtype); % mixture of ordered and named +``` + +Further, the output of the `parser.parse()` method will always contain the total number of required and optional arguments that the method can take, with unspecified arguments given by empty matrices. Thus, to check if an optional argument has been given, you can do: + +```cpp +int dtype = inputs[3].empty() ? -1 : inputs[3].scalar(); +``` + **bridge.hpp** The bridge interface defines a `Bridge` class which provides type conversion between std/OpenCV and Matlab types. A type conversion must provide the following: diff --git a/modules/matlab/generator/templates/functional.cpp b/modules/matlab/generator/templates/functional.cpp index 3beac2e785..d87e209309 100644 --- a/modules/matlab/generator/templates/functional.cpp +++ b/modules/matlab/generator/templates/functional.cpp @@ -57,6 +57,21 @@ {%- endmacro %} +/* + * composeVariant + * compose a variant call for the ArgumentParser + */ +{% macro composeVariant(fun) %} +addVariant("{{ fun.name }}", {{ fun.req|inputs|length }}, {{ fun.opt|inputs|length }} +{%- if fun.opt|inputs|length %}, {% endif -%} +{%- for arg in fun.opt|inputs -%} + "{{arg.name}}" + {%- if not loop.last %}, {% endif -%} +{% endfor -%} +) +{%- endmacro %} + + /* * composeWithExceptionHandler * compose a function call wrapped in exception traps diff --git a/modules/matlab/generator/templates/template_function_base.cpp b/modules/matlab/generator/templates/template_function_base.cpp index e8e3424253..6b86e1e944 100644 --- a/modules/matlab/generator/templates/template_function_base.cpp +++ b/modules/matlab/generator/templates/template_function_base.cpp @@ -30,18 +30,20 @@ using namespace bridge; void mexFunction(int nlhs, mxArray*{% if fun|noutputs %} plhs[]{% else %}*{% endif %}, int nrhs, const mxArray*{% if fun|ninputs %} prhs[]{% else %}*{% endif %}) { - // assertions - conditionalError(nrhs >= {{fun.req|length - fun.req|only|outputs|length}}, "Too few required input arguments specified"); - conditionalError(nrhs <= {{fun.req|length + fun.opt|length - fun.req|only|outputs|length - fun.opt|only|outputs|length}}, "Too many input arguments specified"); - conditionalError(nlhs <= {{ fun.rtp|void|not + fun.req|outputs|length + fun.opt|outputs|length}}, "Too many output arguments specified"); + {% if fun|ninputs %} + // parse the inputs + ArgumentParser parser("{{fun.name}}"); + parser.{{ functional.composeVariant(fun) }}; + MxArrayVector sorted = parser.parse(MxArrayVector(prhs, prhs+nrhs)); + {%endif %} {% if fun|ninputs or fun|noutputs %} // setup {% if fun|ninputs %} - std::vector inputs(prhs, prhs+nrhs); + BridgeVector inputs(sorted.begin(), sorted.end()); {% endif -%} {%- if fun|noutputs %} - std::vector outputs({{fun|noutputs}}); + BridgeVector outputs({{fun|noutputs}}); {% endif %} {% endif %} diff --git a/modules/matlab/include/opencv2/matlab/bridge.hpp b/modules/matlab/include/opencv2/matlab/bridge.hpp index d11e7413b1..a213665858 100644 --- a/modules/matlab/include/opencv2/matlab/bridge.hpp +++ b/modules/matlab/include/opencv2/matlab/bridge.hpp @@ -73,6 +73,7 @@ typedef cv::Ptr Ptr_FeatureDetector; // PREDECLARATIONS // ---------------------------------------------------------------------------- class Bridge; +typedef std::vector BridgeVector; template void deepCopyAndTranspose(const cv::Mat& src, matlab::MxArray& dst); diff --git a/modules/matlab/include/opencv2/matlab/mxarray.hpp b/modules/matlab/include/opencv2/matlab/mxarray.hpp index e9124d2f09..3998459ecb 100644 --- a/modules/matlab/include/opencv2/matlab/mxarray.hpp +++ b/modules/matlab/include/opencv2/matlab/mxarray.hpp @@ -75,6 +75,12 @@ extern "C" { #endif namespace matlab { +// ---------------------------------------------------------------------------- +// PREDECLARATIONS +// ---------------------------------------------------------------------------- +class MxArray; +typedef std::vector MxArrayVector; + /*! * @brief raise error if condition fails * @@ -476,7 +482,6 @@ private: typedef std::string String; typedef std::vector StringVector; typedef std::vector IndexVector; - typedef std::vector MxArrayVector; typedef std::vector VariantVector; /* @class Variant