diff --git a/modules/matlab/CMakeLists.txt b/modules/matlab/CMakeLists.txt index f13fea7ee..d288c8f9a 100644 --- a/modules/matlab/CMakeLists.txt +++ b/modules/matlab/CMakeLists.txt @@ -56,6 +56,10 @@ prepend("-I" MEX_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) prepend("-L" MEX_LIB_DIR ${CMAKE_BINARY_DIR}/lib) set(MEX_OPTS "-largeArrayDims") +if (ENABLE_SOLUTION_FOLDERS) + set_target_properties(${the_module} PROPERTIES FOLDER "Matlab bindings") +endif() + if (BUILD_TESTS) add_subdirectory(test) endif() @@ -63,11 +67,23 @@ endif() # ---------------------------------------------------------------------------- # Configure time components # ---------------------------------------------------------------------------- +string(REPLACE "opencv_" "" OPENCV_MATLAB_MODULES "${OPENCV_MODULE_${the_module}_REQ_DEPS}; + ${OPENCV_MODULE_${the_module}_OPT_DEPS}") +foreach(module ${OPENCV_MATLAB_MODULES}) + if (HAVE_opencv_${module}) + list(APPEND opencv_hdrs "${OPENCV_MODULE_opencv_${module}_LOCATION}/include/opencv2/${module}.hpp") + prepend("-I" MEX_INCLUDE_DIRS "${OPENCV_MODULE_opencv_${module}_LOCATION}/include") + prepend("-l" MEX_LIBS "opencv_${module}") + endif() +endforeach() + +# attempt to generate a gateway for a function message("-- Trying to generate Matlab code") execute_process( + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_SOURCE_DIR}/test/trigger.cpp COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab_caller.py ${HDR_PARSER_PATH} - ${CMAKE_CURRENT_SOURCE_DIR}/test/test_generator.hpp ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_generator.hpp ${CMAKE_BINARY_DIR}/junk ERROR_VARIABLE GEN_ERROR OUTPUT_QUIET ) @@ -80,12 +96,12 @@ else() message("-- Trying to generate Matlab code - OK") endif() -# attempt to compile the file using mex +# attempt to compile a gateway using mex message("-- Trying to compile mex file") execute_process( COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} ${MEX_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_compiler.cpp - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/junk ERROR_VARIABLE MEX_ERROR OUTPUT_QUIET ) @@ -104,36 +120,39 @@ set_property(GLOBAL PROPERTY MEX_WORKS TRUE) # ---------------------------------------------------------------------------- # Build time components # ---------------------------------------------------------------------------- -string(REPLACE "opencv_" "" OPENCV_MATLAB_MODULES "${OPENCV_MODULE_${the_module}_REQ_DEPS}; - ${OPENCV_MODULE_${the_module}_OPT_DEPS}") -foreach(module ${OPENCV_MATLAB_MODULES}) - if (HAVE_opencv_${module}) - list(APPEND opencv_hdrs "${OPENCV_MODULE_opencv_${module}_LOCATION}/include/opencv2/${module}.hpp") - prepend("-I" MEX_INCLUDE_DIRS "${OPENCV_MODULE_opencv_${module}_LOCATION}/include") - prepend("-l" MEX_LIBS "opencv_${module}") - endif() -endforeach() +set(GENERATE_PROXY ${CMAKE_CURRENT_BINARY_DIR}/generate.proxy) +set(COMPILE_PROXY ${CMAKE_CURRENT_BINARY_DIR}/compile.proxy) -# synthesise the matlab sources -add_custom_target(opencv_matlab_sources +add_custom_command( + OUTPUT ${GENERATE_PROXY} COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab_caller.py ${HDR_PARSER_PATH} ${opencv_hdrs} ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${GENERATE_PROXY} + COMMENT "Generating matlab source files" ) -# compile the matlab sources to mex -add_custom_target(opencv_matlab ALL DEPENDS opencv_matlab_sources) -file(GLOB SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/src/*.cpp") -foreach(SOURCE_FILE ${SOURCE_FILES}) - get_filename_component(FILENAME ${SOURCE_FILE} NAME_WE) - # compile the source file using mex - add_custom_command(TARGET opencv_matlab PRE_BUILD - COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} ${MEX_INCLUDE_DIRS} - ${MEX_LIB_DIR} ${MEX_LIBS} ${SOURCE_FILE} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src - ) -endforeach() +add_custom_command( + OUTPUT ${COMPILE_PROXY} + COMMAND ${CMAKE_COMMAND} -DMATLAB_MEX_SCRIPT=${MATLAB_MEX_SCRIPT} + -DMEX_OPTS=${MEX_OPTS} + -DMEX_INCLUDE_DIRS="${MEX_INCLUDE_DIRS}" + -DMEX_LIB_DIR=${MEX_LIB_DIR} + -DMEX_LIBS="${MEX_LIBS}" + -P ${CMAKE_CURRENT_SOURCE_DIR}/compile.cmake + COMMAND ${CMAKE_COMMAND} -E touch ${COMPILE_PROXY} + COMMENT "Compiling Matlab source files. This could take a while..." +) + +add_custom_target(${the_module}_sources ALL DEPENDS ${GENERATE_PROXY}) +add_custom_target(${the_module} ALL DEPENDS ${COMPILE_PROXY}) +add_dependencies(${the_module} ${the_module}_sources) # ---------------------------------------------------------------------------- # Install time components # ---------------------------------------------------------------------------- +file(GLOB MATLAB_FUNCTIONS "${CMAKE_CURRENT_BINARY_DIR}/src/*.mex*") +file(GLOB MATLAB_CLASSES "${CMAKE_CURRENT_BINARY_DIR}/src/private/*.mex*" "${CMAKE_CURRENT_BINARY_DIR}/+cv/*.m") +install(FILES ${MATLAB_FUNCTIONS} ${MATLAB_CLASSES} + DESTINATION ${CMAKE_INSTALL_PREFIX}/matlab/+cv +) diff --git a/modules/matlab/compile.cmake b/modules/matlab/compile.cmake new file mode 100644 index 000000000..8f4b11269 --- /dev/null +++ b/modules/matlab/compile.cmake @@ -0,0 +1,14 @@ +macro(listify OUT_LIST IN_STRING) + string(REPLACE " " ";" ${OUT_LIST} ${IN_STRING}) +endmacro() + +listify(MEX_INCLUDE_DIRS_LIST ${MEX_INCLUDE_DIRS}) +file(GLOB SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/src/*.cpp") +foreach(SOURCE_FILE ${SOURCE_FILES}) + # compile the source file using mex + execute_process( + COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} ${MEX_INCLUDE_DIRS_LIST} + ${MEX_LIB_DIR} ${MEX_LIBS} ${SOURCE_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src + ) +endforeach() diff --git a/modules/matlab/generator/filters.py b/modules/matlab/generator/filters.py index 1d877b36c..c37b9ee1c 100644 --- a/modules/matlab/generator/filters.py +++ b/modules/matlab/generator/filters.py @@ -43,12 +43,15 @@ def noutputs(args): '''Counts the number of output arguments in the input list''' return len(outputs(args)) - -def toUpperCamelCase(text): +def capitalizeFirst(text): return text[0].upper() + text[1:] +def toUpperCamelCase(text): + return ''.join([capitalizeFirst(word) for word in text.split('_')]) + def toLowerCamelCase(text): - return text[0].lower() + text[1:] + upper_camel = toUpperCamelCase(test) + return upper_camel[0].lower() + upper_camel[1:] def toUnderCase(text): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', text) diff --git a/modules/matlab/generator/templates/functional.cpp b/modules/matlab/generator/templates/functional.cpp index be81daf1e..3daf1b8d6 100644 --- a/modules/matlab/generator/templates/functional.cpp +++ b/modules/matlab/generator/templates/functional.cpp @@ -8,7 +8,7 @@ {% macro compose(fun) %} {# ----------- Return type ------------- #} {%- if not fun.rtp|void -%} retval = {% endif -%} - {%- if fun.clss -%}inst.{%- else -%} cv:: {% endif -%} + {%- if fun.clss -%}inst.{%- else -%} cv:: {%- endif -%} {{fun.name}}( {#- ----------- Required ------------- -#} {%- for arg in fun.req -%} @@ -32,10 +32,10 @@ // unpack the arguments {# ----------- Inputs ------------- #} {% for arg in fun.req|inputs %} - {{arg.tp}} {{arg.name}} = inputs[{{ loop.index0 }}]; + {{arg.tp}} {{arg.name}} = inputs[{{ loop.index0 }}].to{{arg.tp|toUpperCamelCase}}(); {% endfor %} {% for opt in fun.opt|inputs %} - {{opt.tp}} {{opt.name}} = (nrhs > {{loop.index0 + fun.req|inputs|length}}) ? ({{opt.tp}})inputs[{{loop.index0 + fun.req|inputs|length}}] : {{opt.default}}; + {{opt.tp}} {{opt.name}} = (nrhs > {{loop.index0 + fun.req|inputs|length}}) ? inputs[{{loop.index0 + fun.req|inputs|length}}].to{{opt.tp|toUpperCamelCase}}() : {% if opt.ref == '*' -%} {{opt.tp}}() {%- else -%} {{opt.default}} {%- endif %}; {% endfor %} {# ----------- Outputs ------------ #} {% for arg in fun.req|only|outputs %} diff --git a/modules/matlab/generator/templates/template_function_base.cpp b/modules/matlab/generator/templates/template_function_base.cpp index 192af46c9..2539ed363 100644 --- a/modules/matlab/generator/templates/template_function_base.cpp +++ b/modules/matlab/generator/templates/template_function_base.cpp @@ -42,8 +42,10 @@ void mexFunction(int nlhs, mxArray* plhs[], {{ functional.generate(fun) }} + {%- if noutputs %} // push the outputs back to matlab for (size_t n = 0; n < nlhs; ++n) { - plhs[n] = outputs[n].mxArray(); + plhs[n] = outputs[n].toMxArray(); } + {% endif %} } diff --git a/modules/matlab/include/bridge.hpp b/modules/matlab/include/bridge.hpp index 7e93e0af6..23a430d8f 100644 --- a/modules/matlab/include/bridge.hpp +++ b/modules/matlab/include/bridge.hpp @@ -2,8 +2,17 @@ #define OPENCV_BRIDGE_HPP_ #include "mex.h" +#include #include +/* + * Custom typedefs + * Parsed names from the hdr_parser + */ +typedef std::vector vector_Mat; +typedef std::vector vector_Point; +typedef std::vector vector_int; + /*! * @class Bridge * @brief Type conversion class for converting OpenCV and native C++ types @@ -17,12 +26,20 @@ * // implicit conversion from Bridge --> ObjectType * operator ObjectType(); * // explicit conversion from Bridge --> ObjectType - * ObjectType toObjectType + * ObjectType toObjectType(); * * The bridging class provides common conversions between OpenCV types, * std and stl types to Matlab's mxArray format. By inheriting Bridge, * you can add your own custom type conversions. * + * NOTE: for the explicit conversion function, the object name must be + * in UpperCamelCase, for example: + * int --> toInt + * my_object --> MyObject + * my_Object --> MyObject + * myObject --> MyObject + * this is because the binding generator standardises the calling syntax. + * * Bridge attempts to make as few assumptions as possible, however in * some cases where 1-to-1 mappings don't exist, some assumptions are necessary. * In particular: @@ -39,19 +56,85 @@ public: virtual ~Bridge() {} // --------------------------- mxArray -------------------------------------- - Bridge& operator=(const mxArray* obj) {} + Bridge& operator=(const mxArray* obj) { return *this; } Bridge(const mxArray* obj) {} - mxArray* mxArray() { return NULL; } + mxArray* toMxArray() { return NULL; } // --------------------------- cv::Mat -------------------------------------- - Bridge& operator=(const cv::Mat& obj) {} + Bridge& operator=(const cv::Mat& obj) { return *this; } operator cv::Mat() { return cv::Mat(); } cv::Mat toMat() { return cv::Mat(); } + // -------------------- vector_Mat -------------------------------- + Bridge& operator=(const vector_Mat& obj) { return *this; } + operator vector_Mat() { return vector_Mat(); } + vector_Mat toVectorMat() { return vector_Mat(); } + // --------------------------- int -------------------------------------- - Bridge& operator=(const int& obj) {} + Bridge& operator=(const int& obj) { return *this; } operator int() { return 0; } int toInt() { return 0; } + + // --------------------------- vector_int ---------------------------------- + Bridge& operator=(const vector_int& obj) { return *this; } + operator vector_int() { return vector_int(); } + vector_int toVectorInt() { return vector_int(); } + + // --------------------------- string -------------------------------------- + Bridge& operator=(const std::string& obj) { return *this; } + operator std::string() { return ""; } + std::string toString() { return ""; } + + // --------------------------- bool -------------------------------------- + Bridge& operator=(const bool& obj) { return *this; } + operator bool() { return 0; } + bool toBool() { return 0; } + + // --------------------------- double -------------------------------------- + Bridge& operator=(const double& obj) { return *this; } + operator double() { return 0; } + double toDouble() { return 0; } + + // -------------------------- Point -------------------------------------- + Bridge& operator=(const cv::Point& obj) { return *this; } + operator cv::Point() { return cv::Point(); } + cv::Point toPoint() { return cv::Point(); } + + // -------------------------- Size --------------------------------------- + Bridge& operator=(const cv::Size& obj) { return *this; } + operator cv::Size() { return cv::Size(); } + cv::Size toSize() { return cv::Size(); } + + // ------------------------ vector_Point ------------------------------------ + Bridge& operator=(const vector_Point& obj) { return *this; } + operator vector_Point() { return vector_Point(); } + vector_Point toVectorPoint() { return vector_Point(); } + + // -------------------------- Scalar -------------------------------------- + Bridge& operator=(const cv::Scalar& obj) { return *this; } + operator cv::Scalar() { return cv::Scalar(); } + cv::Scalar toScalar() { return cv::Scalar(); } + + // -------------------------- Rect -------------------------------------- + Bridge& operator=(const cv::Rect& obj) { return *this; } + operator cv::Rect() { return cv::Rect(); } + cv::Rect toRect() { return cv::Rect(); } + + // ---------------------- RotatedRect ------------------------------------ + Bridge& operator=(const cv::RotatedRect& obj) { return *this; } + operator cv::RotatedRect() { return cv::RotatedRect(); } + cv::RotatedRect toRotatedRect() { return cv::RotatedRect(); } + + // ---------------------- TermCriteria ----------------------------------- + Bridge& operator=(const cv::TermCriteria& obj) { return *this; } + operator cv::TermCriteria() { return cv::TermCriteria(); } + cv::TermCriteria toTermCriteria() { return cv::TermCriteria(); } + + // ---------------------- RNG ----------------------------------- + Bridge& operator=(const cv::RNG& obj) { return *this; } + operator cv::RNG() { return cv::RNG(); } + cv::RNG toRNG() { return cv::RNG(); } + }; #endif