From 755ce9d654483c21547e8bf38a817aa4929d9c71 Mon Sep 17 00:00:00 2001 From: hbristow Date: Sat, 22 Jun 2013 23:26:27 -0700 Subject: [PATCH] Matlab bindings now only building once rather than every call to make, via the use of some proxies. Matlab build currently only happens in one thread, so it can be pretty slow --- modules/matlab/CMakeLists.txt | 71 ++++++++------ modules/matlab/compile.cmake | 14 +++ modules/matlab/generator/filters.py | 9 +- .../matlab/generator/templates/functional.cpp | 6 +- .../templates/template_function_base.cpp | 4 +- modules/matlab/include/bridge.hpp | 93 ++++++++++++++++++- 6 files changed, 159 insertions(+), 38 deletions(-) create mode 100644 modules/matlab/compile.cmake 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