Skip to content

Commit 1fe10ef

Browse files
committed
Merge pull request #1761 from alalek:build_fix_matlab
2 parents b10554c + 9ef878f commit 1fe10ef

File tree

9 files changed

+107
-44
lines changed

9 files changed

+107
-44
lines changed

modules/matlab/CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
set(BUILD_opencv_matlab_INIT OFF) # Matlab module is broken
2-
31
# ----------------------------------------------------------------------------
42
# CMake file for Matlab/Octave support
53
#
@@ -261,6 +259,13 @@ add_custom_command(
261259
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/test/help.m ${CMAKE_CURRENT_BINARY_DIR}/+cv
262260
COMMAND ${CMAKE_COMMAND} -E touch ${GENERATE_PROXY}
263261
COMMENT "Generating Matlab source files"
262+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/build_info.py"
263+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/cvmex.py"
264+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/filters.py"
265+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/gen_matlab.py"
266+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/parse_tree.py"
267+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/templates/functional.cpp"
268+
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/templates/template_function_base.cpp"
264269
)
265270

266271
# compile

modules/matlab/compile.cmake

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,18 @@ endif()
2828
# 2. attempt compile if required
2929
# 3. if the compile fails, throw an error and cancel compilation
3030
file(GLOB SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/src/*.cpp")
31+
list(LENGTH SOURCE_FILES __size)
32+
message("Matlab: compiling ${__size} files")
33+
set(__index 0)
3134
foreach(SOURCE_FILE ${SOURCE_FILES})
35+
MATH(EXPR __index "${__index}+1")
3236
# strip out the filename
3337
get_filename_component(FILENAME ${SOURCE_FILE} NAME_WE)
38+
message("[${__index}/${__size}] Compiling: ${FILENAME}")
3439
# compile the source file using mex
35-
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/+cv/${FILENAME}.${MATLAB_MEXEXT})
40+
if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/+cv/${FILENAME}.${MATLAB_MEXEXT}" OR
41+
"${SOURCE_FILE}" IS_NEWER_THAN "${CMAKE_CURRENT_BINARY_DIR}/+cv/${FILENAME}.${MATLAB_MEXEXT}"
42+
)
3643
execute_process(
3744
COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} "CXXFLAGS=\$CXXFLAGS ${MEX_CXXFLAGS}" ${MEX_INCLUDE_DIRS_LIST}
3845
${MEX_LIB_DIR} ${MEX_LIBS_LIST} ${SOURCE_FILE}

modules/matlab/generator/filters.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
def inputs(args):
77
'''Keeps only the input arguments in a list of elements.
8-
In OpenCV input arguments are all arguments with names
9-
not beginning with 'dst'
108
'''
119
try:
1210
return [arg for arg in args['only'] if arg.I and not arg.O]
@@ -20,7 +18,7 @@ def ninputs(fun):
2018
def outputs(args):
2119
'''Determines whether any of the given arguments is an output
2220
reference, and returns a list of only those elements.
23-
In OpenCV, output references are preceeded by 'dst'
21+
In OpenCV, output references are preceeded by CV_OUT or has *OutputArray* type
2422
'''
2523
try:
2624
return [arg for arg in args['only'] if arg.O and not arg.I]

modules/matlab/generator/gen_matlab.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@
44
from parse_tree import ParseTree, todict, constants
55
from filters import *
66

7+
updated_files = []
8+
9+
def update_file(fname, content):
10+
if fname in updated_files:
11+
print('ERROR(gen_matlab.py): attemption to write file multiple times: {}'.format(fname))
12+
return
13+
updated_files.append(fname)
14+
if os.path.exists(fname):
15+
with open(fname, 'rb') as f:
16+
old_content = f.read()
17+
if old_content == content:
18+
#print('Up-to-date: {}'.format(fname))
19+
return
20+
print('Updating: {}'.format(fname))
21+
else:
22+
print('Writing: {}'.format(fname))
23+
with open(fname, 'wb') as f:
24+
f.write(content)
25+
26+
727
class MatlabWrapperGenerator(object):
828
"""
929
MatlabWrapperGenerator is a class for generating Matlab mex sources from
@@ -107,24 +127,20 @@ def gen(self, module_roots, modules, extras, output_dir):
107127
# functions
108128
for method in namespace.methods:
109129
populated = tfunction.render(fun=method, time=time, includes=namespace.name)
110-
with open(output_source_dir+'/'+method.name+'.cpp', 'wb') as f:
111-
f.write(populated.encode('utf-8'))
130+
update_file(output_source_dir+'/'+method.name+'.cpp', populated.encode('utf-8'))
112131
# classes
113132
for clss in namespace.classes:
114133
# cpp converter
115134
populated = tclassc.render(clss=clss, time=time)
116-
with open(output_private_dir+'/'+clss.name+'Bridge.cpp', 'wb') as f:
117-
f.write(populated.encode('utf-8'))
135+
update_file(output_private_dir+'/'+clss.name+'Bridge.cpp', populated.encode('utf-8'))
118136
# matlab classdef
119137
populated = tclassm.render(clss=clss, time=time)
120-
with open(output_class_dir+'/'+clss.name+'.m', 'wb') as f:
121-
f.write(populated.encode('utf-8'))
138+
update_file(output_class_dir+'/'+clss.name+'.m', populated.encode('utf-8'))
122139

123140
# create a global constants lookup table
124141
const = dict(constants(todict(parse_tree.namespaces)))
125142
populated = tconst.render(constants=const, time=time)
126-
with open(output_dir+'/cv.m', 'wb') as f:
127-
f.write(populated.encode('utf-8'))
143+
update_file(output_dir+'/cv.m', populated.encode('utf-8'))
128144

129145

130146
if __name__ == "__main__":

modules/matlab/generator/parse_tree.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@
88
# Python 3.3+
99
basestring = str
1010

11+
12+
valid_types = (
13+
'int', 'bool', 'float', 'double', 'size_t', 'char',
14+
'Mat', 'Scalar', 'String',
15+
'TermCriteria', 'Size', 'Point', 'Point2f', 'Point2d', 'Rect', 'RotatedRect',
16+
'RNG', 'DMatch', 'Moments',
17+
'vector_Mat', 'vector_Point', 'vector_int', 'vector_float', 'vector_double', 'vector_String', 'vector_uchar', 'vector_Rect', 'vector_DMatch', 'vector_KeyPoint',
18+
'vector_Point2f', 'vector_vector_char', 'vector_vector_DMatch', 'vector_vector_KeyPoint',
19+
'Ptr_StereoBM', 'Ptr_StereoSGBM', 'Ptr_FeatureDetector', 'Ptr_CLAHE', 'Ptr_LineSegmentDetector', 'Ptr_AlignMTB', 'Ptr_CalibrateDebevec',
20+
'Ptr_CalibrateRobertson', 'Ptr_DenseOpticalFlow', 'Ptr_DualTVL1OpticalFlow', 'Ptr_MergeDebevec', 'Ptr_MergeMertens', 'Ptr_MergeRobertson',
21+
'Ptr_Stitcher', 'Ptr_Tonemap', 'Ptr_TonemapDrago', 'Ptr_TonemapDurand', 'Ptr_TonemapMantiuk', 'Ptr_TonemapReinhard', 'Ptr_float',
22+
# Not supported:
23+
#vector_vector_KeyPoint
24+
)
25+
1126
class ParseTree(object):
1227
"""
1328
The ParseTree class produces a semantic tree of C++ definitions given
@@ -89,7 +104,11 @@ def build(self, namespaces):
89104
methods = []
90105
constants = []
91106
for defn in definitions:
92-
obj = babel.translate(defn)
107+
try:
108+
obj = babel.translate(defn)
109+
except Exception as e:
110+
print(e)
111+
obj = None
93112
if obj is None:
94113
continue
95114
if type(obj) is Class or obj.clss:
@@ -176,15 +195,15 @@ def translateConstant(self, defn):
176195
return Constant(name, clss, tp, const, '', val)
177196

178197
def translateArgument(self, defn):
198+
modifiers = defn[3]
179199
ref = '*' if '*' in defn[0] else ''
180-
ref = '&' if '&' in defn[0] else ref
181-
const = ' const ' in ' '+defn[0]+' '
200+
ref = '&' if '&' in defn[0] or '/Ref' in modifiers else ref
201+
const = '/C' in modifiers
182202
tp = " ".join([word for word in defn[0].replace(ref, '').split() if not ' const ' in ' '+word+' '])
183203
name = defn[1]
184204
default = defn[2] if defn[2] else ''
185-
modifiers = ''.join(defn[3])
186-
I = True if not modifiers or 'I' in modifiers else False
187-
O = True if 'O' in modifiers else False
205+
I = True if '/I' in modifiers or not '/O' in modifiers else False
206+
O = True if '/O' in modifiers else False
188207
return Argument(name, tp, const, I, O, ref, default)
189208

190209
def translateName(self, name):
@@ -292,6 +311,8 @@ def __init__(self, name='', tp='', const=False, I=True, O=False, ref='', default
292311
self.O = O
293312
self.const = const
294313
self.default = default
314+
if not tp in valid_types:
315+
raise Exception("Non-supported argument type: {} (name: {})".format(tp, name))
295316

296317
def __str__(self):
297318
return ('const ' if self.const else '')+self.tp+self.ref+\

modules/matlab/generator/templates/functional.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,23 @@ addVariant("{{ fun.name }}", {{ fun.req|inputs|length }}, {{ fun.opt|inputs|leng
104104
{%- macro handleInputs(fun) %}
105105

106106
{% if fun|ninputs or (fun|noutputs and not fun.constructor) %}
107-
// unpack the arguments
108-
{# ----------- Inputs ------------- #}
107+
// - inputs
109108
{% for arg in fun.req|inputs %}
110109
{{arg.tp}} {{arg.name}} = inputs[{{ loop.index0 }}].to{{arg.tp|toUpperCamelCase}}();
111110
{% endfor %}
111+
// - inputs (opt)
112112
{% for opt in fun.opt|inputs %}
113113
{{opt.tp}} {{opt.name}} = inputs[{{loop.index0 + fun.req|inputs|length}}].empty() ? ({{opt.tp}}) {% if opt.ref == '*' -%} {{opt.tp}}() {%- else -%} {{opt.default}} {%- endif %} : inputs[{{loop.index0 + fun.req|inputs|length}}].to{{opt.tp|toUpperCamelCase}}();
114114
{% endfor %}
115-
{# ----------- Outputs ------------ #}
115+
// - outputs
116116
{% for arg in fun.req|only|outputs %}
117117
{{arg.tp}} {{arg.name}};
118118
{% endfor %}
119+
// - outputs (opt)
119120
{% for opt in fun.opt|only|outputs %}
120121
{{opt.tp}} {{opt.name}};
121122
{% endfor %}
123+
// - return
122124
{% if not fun.rtp|void and not fun.constructor %}
123125
{{fun.rtp}} retval;
124126
{% endif %}

modules/matlab/generator/templates/template_class_base.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
/*
33
* file: {{clss.name}}Bridge.cpp
44
* author: A trusty code generator
5-
* date: {{time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())}}
65
*
76
* This file was autogenerated, do not modify.
87
* See LICENSE for full modification and redistribution details.
9-
* Copyright {{time.strftime("%Y", time.localtime())}} The OpenCV Foundation
8+
* Copyright 2018 The OpenCV Foundation
109
*/
1110
#include <mex.h>
1211
#include <vector>

modules/matlab/generator/templates/template_function_base.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
/*
33
* file: {{fun.name}}.cpp
44
* author: A trusty code generator
5-
* date: {{time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())}}
65
*
76
* This file was autogenerated, do not modify.
87
* See LICENSE for full modification and redistribution details.
9-
* Copyright {{time.strftime("%Y", time.localtime())}} The OpenCV Foundation
8+
* Copyright 2018 The OpenCV Foundation
109
*/
1110
#include <string>
1211
#include <vector>

modules/matlab/include/opencv2/matlab/bridge.hpp

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,13 @@ class Bridge {
249249
case mxUINT16_CLASS: deepCopyAndTranspose<uint16_t, Scalar>(ptr_, mat); break;
250250
case mxINT32_CLASS: deepCopyAndTranspose<int32_t, Scalar>(ptr_, mat); break;
251251
case mxUINT32_CLASS: deepCopyAndTranspose<uint32_t, Scalar>(ptr_, mat); break;
252-
case mxINT64_CLASS: deepCopyAndTranspose<int64_t, Scalar>(ptr_, mat); break;
253-
case mxUINT64_CLASS: deepCopyAndTranspose<uint64_t, Scalar>(ptr_, mat); break;
252+
//case mxINT64_CLASS: deepCopyAndTranspose<int64_t, Scalar>(ptr_, mat); break;
253+
//case mxUINT64_CLASS: deepCopyAndTranspose<uint64_t, Scalar>(ptr_, mat); break;
254254
case mxSINGLE_CLASS: deepCopyAndTranspose<float, Scalar>(ptr_, mat); break;
255255
case mxDOUBLE_CLASS: deepCopyAndTranspose<double, Scalar>(ptr_, mat); break;
256256
case mxCHAR_CLASS: deepCopyAndTranspose<char, Scalar>(ptr_, mat); break;
257257
case mxLOGICAL_CLASS: deepCopyAndTranspose<int8_t, Scalar>(ptr_, mat); break;
258-
default: matlab::error("Attempted to convert from unknown class");
258+
default: matlab::error("Attempted to convert from unknown/unsupported class");
259259
}
260260
return mat;
261261
}
@@ -576,13 +576,13 @@ cv::Mat Bridge::toMat<matlab::InheritType>() const {
576576
case mxUINT16_CLASS: return toMat<uint16_t>();
577577
case mxINT32_CLASS: return toMat<int32_t>();
578578
case mxUINT32_CLASS: return toMat<int32_t>();
579-
case mxINT64_CLASS: return toMat<int64_t>();
580-
case mxUINT64_CLASS: return toMat<int64_t>();
579+
//case mxINT64_CLASS: return toMat<int64_t>();
580+
//case mxUINT64_CLASS: return toMat<int64_t>();
581581
case mxSINGLE_CLASS: return toMat<float>();
582582
case mxDOUBLE_CLASS: return toMat<float>(); //NOTE: OpenCV uses float as native type!
583583
case mxCHAR_CLASS: return toMat<int8_t>();
584584
case mxLOGICAL_CLASS: return toMat<int8_t>();
585-
default: matlab::error("Attempted to convert from unknown class");
585+
default: matlab::error("Attempted to convert from unknown/unsuported class");
586586
}
587587
return cv::Mat();
588588
}
@@ -598,36 +598,52 @@ cv::Mat Bridge::toMat() const { return toMat<matlab::InheritType>(); }
598598

599599
template <typename InputScalar, typename OutputScalar>
600600
void deepCopyAndTranspose(const cv::Mat& in, matlab::MxArray& out) {
601+
const int cols = out.cols();
602+
const int rows = out.rows();
601603
matlab::conditionalError(static_cast<size_t>(in.rows) == out.rows(), "Matrices must have the same number of rows");
602604
matlab::conditionalError(static_cast<size_t>(in.cols) == out.cols(), "Matrices must have the same number of cols");
603605
matlab::conditionalError(static_cast<size_t>(in.channels()) == out.channels(), "Matrices must have the same number of channels");
604606
std::vector<cv::Mat> channels;
605607
cv::split(in, channels);
606608
for (size_t c = 0; c < out.channels(); ++c) {
607-
cv::transpose(channels[c], channels[c]);
608-
cv::Mat outmat(out.cols(), out.rows(), cv::DataType<OutputScalar>::type,
609-
static_cast<void *>(out.real<OutputScalar>() + out.cols()*out.rows()*c));
610-
channels[c].convertTo(outmat, cv::DataType<OutputScalar>::type);
609+
cv::Mat_<InputScalar> m;
610+
cv::transpose(channels[c], m);
611+
OutputScalar* dst_plane = (OutputScalar*)out.real<InputScalar>() + cols*rows*c;
612+
for (int x = 0; x < cols; x++)
613+
{
614+
OutputScalar* dst_col = dst_plane + x * rows;
615+
for (int y = 0; y < rows; y++)
616+
{
617+
dst_col[y] = cv::saturate_cast<OutputScalar>(m(y, x));
618+
}
619+
}
611620
}
612-
613621
//const InputScalar* inp = in.ptr<InputScalar>(0);
614622
//OutputScalar* outp = out.real<OutputScalar>();
615623
//gemt('R', out.rows(), out.cols(), inp, in.step1(), outp, out.rows());
616624
}
617625

618626
template <typename InputScalar, typename OutputScalar>
619627
void deepCopyAndTranspose(const matlab::MxArray& in, cv::Mat& out) {
628+
const int cols = in.cols();
629+
const int rows = in.rows();
620630
matlab::conditionalError(in.rows() == static_cast<size_t>(out.rows), "Matrices must have the same number of rows");
621631
matlab::conditionalError(in.cols() == static_cast<size_t>(out.cols), "Matrices must have the same number of cols");
622632
matlab::conditionalError(in.channels() == static_cast<size_t>(out.channels()), "Matrices must have the same number of channels");
623633
std::vector<cv::Mat> channels;
624634
for (size_t c = 0; c < in.channels(); ++c) {
625-
cv::Mat outmat;
626-
cv::Mat inmat(in.cols(), in.rows(), cv::DataType<InputScalar>::type,
627-
static_cast<void *>(const_cast<InputScalar *>(in.real<InputScalar>() + in.cols()*in.rows()*c)));
628-
inmat.convertTo(outmat, cv::DataType<OutputScalar>::type);
629-
cv::transpose(outmat, outmat);
630-
channels.push_back(outmat);
635+
cv::Mat_<OutputScalar> m(cols, rows);
636+
const InputScalar* src_plane = in.real<InputScalar>() + cols*rows*c;
637+
for (int x = 0; x < cols; x++)
638+
{
639+
const InputScalar* src_col = src_plane + x * rows;
640+
for (int y = 0; y < rows; y++)
641+
{
642+
m(y, x) = cv::saturate_cast<InputScalar>(src_col[y]);
643+
}
644+
}
645+
cv::transpose(m, m);
646+
channels.push_back(m);
631647
}
632648
cv::merge(channels, out);
633649

0 commit comments

Comments
 (0)