diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2375a98 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.0) +project(GeneralizedDistanceTransform) + + +# Settings -------------------------------------------------------------------- + +option(ENABLE_TESTING "Compile tests") +option(ENABLE_PYTHON "Generate a python wrapper") +set(PYTHON_VERSION "" CACHE STRING "Desired python version for the bindings") + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -Wall") + + +# Dependencies ---------------------------------------------------------------- + +find_package(OpenCV REQUIRED) +include(cmake/CPPyModule.cmake) + + +# Targets --------------------------------------------------------------------- + +# C++ Library +add_library(gendistrans SHARED + gendistrans/src/gendistrans.cpp) +target_include_directories(gendistrans PUBLIC + ${OpenCV_INCLUDE_DIRS} + $ + $) +target_link_libraries(gendistrans + ${OpenCV_LIBS}) +set_property(TARGET gendistrans + PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) + +# Python module +if(${ENABLE_PYTHON}) + as_python_module(gendistrans_py + NAMESPACE cv # C++ code is written as if it was an OpenCV module + MODULE_NAME gendistrans # but we cannot reuse the cv2 module name + # Library source files are alread formated as wrappers + HEADERS gendistrans/include/opencv2/gendistrans.hpp + # Add the whole source code to the module (standalone module) + SOURCES gendistrans/src/gendistrans.cpp) + # No linking because all source files have been put into the module + target_include_directories(gendistrans_py PUBLIC + ${OpenCV_INCLUDE_DIRS} + gendistrans/include) +endif() + + +# Install --------------------------------------------------------------------- + + + +# Testing --------------------------------------------------------------------- + +if(${ENABLE_TESTING}) + enable_testing() + + add_executable(dontcrash + tests/dontcrash.cpp) + target_link_libraries(dontcrash + ${OpenCV_LIBS} + gendistrans) + add_test(test_dontcrash + dontcrash) +endif() diff --git a/cmake/CPPyModule.cmake b/cmake/CPPyModule.cmake new file mode 100644 index 0000000..1368325 --- /dev/null +++ b/cmake/CPPyModule.cmake @@ -0,0 +1,138 @@ +# author: Nicolas Granger + + +set(this_file_directory ${CMAKE_CURRENT_LIST_DIR}) + +include(CMakeParseArguments) +find_package(OpenCV REQUIRED) +find_package(PythonLibs REQUIRED) + + +function(as_python_module TARGET_NAME) +# as_python_module( +# NAMESPACE namespace +# [MODULE_NAME module] +# HEADERS file1.hpp [file2.hpp ...] +# [SOURCES file1.cpp [file2.cpp ...]]) +# +# `as_python_module` creates a binary python module from C++ source files. It +# relies on the header parsing tools from the OpenCV project, and thus uses +# the same conventions (TODO: add link to the conventions). +# The cmake target is specified as the first argument. +# For technical reasons, it is mandatory to pack up the exported items +# in one named namespace which is specified by NAMESPACE. Note that nested +# namespaces are automatically converted to submodules. +# `MODULE_NAME` specifies the name of the python module, and determines the name +# of the resulting file (the extension depends on the plateform), by default, +# the namespace name is used. +# HEADERS is a list of header files to parse in order to find exported items. +# SOURCES specifies source files that should be compiled in the library. +# +# Warning: Because of the way the code generation works, you may have unused +# compilation warning (unused functions for example) and symbol collisions +# (rather unlikely considering the names). +# +# Note 1: This resulting target is generated by an add_library call. Thus +# compilation features and target properties may be used as for any other +# library. +# +# Note 2: if you wish to set the version of the python libraries, use this: +# set(Python_ADDITIONAL_VERSIONS "3.4") +# if(NOT ("${ARG_PYVER}" STREQUAL "") +# AND NOT ("${PYTHONLIBS_VERSION_STRING}" MATCHES "^${ARG_PYVER}*")) +# message(FATAL_ERROR "Python version ${PYVER} was not found (note: \ +# found ${PYTHON_VERSION_STRING}).") +# endif() + + # Parse arguments --------------------------------------------------------- + + set(options) + set(oneValueArgs NAMESPACE MODULE_NAME) + set(multiValueArgs HEADERS SOURCES) + cmake_parse_arguments(ARG + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if("${ARG_NAMESPACE}" STREQUAL "") + message(FATAL_ERROR "NAMESPACE argument not specified") + endif() + if("${ARG_MODULE_NAME}" STREQUAL "") + set(ARG_MODULE_NAME ${ARG_NAMESPACE}) + endif() + + # Source code generation -------------------------------------------------- + + # List header files in headers.txt + set(gen_files_path ${CMAKE_CURRENT_BINARY_DIR}/${ARG_MODULE_NAME}) + file(MAKE_DIRECTORY ${gen_files_path}) + file(WRITE ${gen_files_path}/headers.txt "${ARG_HEADERS}") + + # Parse headers and generate python bindings + add_custom_command( + OUTPUT ${gen_files_path}/pyopencv_generated_include.h + ${gen_files_path}/pyopencv_generated_funcs.h + ${gen_files_path}/pyopencv_generated_ns_reg.h + ${gen_files_path}/pyopencv_generated_type_reg.h + ${gen_files_path}/pyopencv_generated_types.h + DEPENDS ${ARG_HEADERS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${this_file_directory}/bin/gen2.py + ${gen_files_path} + ${gen_files_path}/headers.txt + ${ARG_NAMESPACE} + ${ARG_MODULE_NAME}) + + # Make a target for cmake + add_custom_target(${TARGET_NAME}_generated_headers + DEPENDS ${gen_files_path}/pyopencv_generated_include.h + ${gen_files_path}/pyopencv_generated_funcs.h + ${gen_files_path}/pyopencv_generated_ns_reg.h + ${gen_files_path}/pyopencv_generated_type_reg.h + ${gen_files_path}/pyopencv_generated_types.h) + + # Copy additional sources + file(COPY ${this_file_directory}/src/module.cpp + ${this_file_directory}/src/pycompat.hpp + ${this_file_directory}/src/py_cv_converters.hpp + ${this_file_directory}/src/utils.hpp + DESTINATION ${gen_files_path}) + configure_file(${this_file_directory}/src/init_func.cpp.in + ${gen_files_path}/init_func.cpp) + + # Module compilation ------------------------------------------------------ + + add_library(${TARGET_NAME} SHARED + ${gen_files_path}/module.cpp + ${ARG_SOURCES}) + + add_dependencies(${TARGET_NAME} ${TARGET_NAME}_generated_headers) + + set_property(TARGET ${TARGET_NAME} + PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) + target_compile_features(${TARGET_NAME} PRIVATE cxx_long_long_type) + + # Note: the module needn't provide a header, python opens the library + # and looks for internal symbols to find the entry point (namely the + # PyInit_ function) + target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} # parsed headers may have relative path + ${gen_files_path} + ${PYTHON_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS}) + + target_link_libraries(${TARGET_NAME} + ${PYTHON_LIBRARIES} + ${OpenCV_LIBS}) + + # Output file name -------------------------------------------------------- + + if(WIN32 AND NOT CYGWIN) + set(module_extension ".pyd") + else() + set(module_extension ".so") + endif() + set_target_properties(${TARGET_NAME} PROPERTIES + PREFIX "" + OUTPUT_NAME ${ARG_MODULE_NAME} + EXTENSION ${module_extension}) + +endfunction() + diff --git a/cmake/bin/gen2.py b/cmake/bin/gen2.py new file mode 100755 index 0000000..cea2828 --- /dev/null +++ b/cmake/bin/gen2.py @@ -0,0 +1,1011 @@ +#!/usr/bin/env python + +# The content of this file has been taken from the OpenCV project at +# https://github.com/Itseez/opencv +# It has been modified to make it more reusable for generating binding +# on other projects. +# modifications: Nicolas Granger + +# For your information, the original License is given below: + +# By downloading, copying, installing or using the software you agree to this license. +# If you do not agree to this license, do not download, install, +# copy or use the software. +# +# +# License Agreement +# For Open Source Computer Vision Library +# (3-clause BSD License) +# +# Copyright (C) 2000-2016, Intel Corporation, all rights reserved. +# Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +# Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +# Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +# Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +# Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +# Third party copyrights are property of their respective owners. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# * Neither the names of the copyright holders nor the names of the contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# This software is provided by the copyright holders and contributors "as is" and +# any express or implied warranties, including, but not limited to, the implied +# warranties of merchantability and fitness for a particular purpose are disclaimed. +# In no event shall copyright holders or contributors be liable for any direct, +# indirect, incidental, special, exemplary, or consequential damages +# (including, but not limited to, procurement of substitute goods or services; +# loss of use, data, or profits; or business interruption) however caused +# and on any theory of liability, whether in contract, strict liability, +# or tort (including negligence or otherwise) arising in any way out of +# the use of this software, even if advised of the possibility of such damage. + +from __future__ import print_function +import sys, re, os +from string import Template + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +import hdr_parser + +if sys.version_info[0] >= 3: + from io import StringIO +else: + from cStringIO import StringIO + +ignored_arg_types = ["RNG*"] + +gen_template_check_self = Template(""" if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type)) + return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); + $cname* _self_ = ${amp}((pyopencv_${name}_t*)self)->v${get}; +""") + +gen_template_check_self_algo = Template(""" if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type)) + return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); + $cname* _self_ = dynamic_cast<$cname*>(${amp}((pyopencv_${name}_t*)self)->v.get()); +""") + +gen_template_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); + new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new + if(self) """) + +gen_template_call_constructor = Template("""self->v.reset(new ${cname}${args})""") + +gen_template_simple_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); + if(self) """) + +gen_template_simple_call_constructor = Template("""self->v = ${cname}${args}""") + +gen_template_parse_args = Template("""const char* keywords[] = { $kw_list, NULL }; + if( PyArg_ParseTupleAndKeywords(args, kw, "$fmtspec", (char**)keywords, $parse_arglist)$code_cvt )""") + +gen_template_func_body = Template("""$code_decl + $code_parse + { + ${code_prelude}ERRWRAP2($code_fcall); + $code_ret; + } +""") + +py_major_version = sys.version_info[0] +if py_major_version >= 3: + head_init_str = "PyVarObject_HEAD_INIT(&PyType_Type, 0)" +else: + head_init_str = """PyObject_HEAD_INIT(&PyType_Type) +0,""" + +gen_template_simple_type_decl = Template(""" +struct pyopencv_${name}_t +{ + PyObject_HEAD + ${cname} v; +}; + +static PyTypeObject pyopencv_${name}_Type = +{ + %s + MODULESTR".$wname", + sizeof(pyopencv_${name}_t), +}; + +static void pyopencv_${name}_dealloc(PyObject* self) +{ + PyObject_Del(self); +} + +template<> PyObject* pyopencv_from(const ${cname}& r) +{ + pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); + m->v = r; + return (PyObject*)m; +} + +template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name) +{ + if( src == NULL || src == Py_None ) + return true; + if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type)) + { + failmsg("Expected ${cname} for argument '%%s'", name); + return false; + } + dst = ((pyopencv_${name}_t*)src)->v; + return true; +} +""" % head_init_str) + + +gen_template_type_decl = Template(""" +struct pyopencv_${name}_t +{ + PyObject_HEAD + Ptr<${cname1}> v; +}; + +static PyTypeObject pyopencv_${name}_Type = +{ + %s + MODULESTR".$wname", + sizeof(pyopencv_${name}_t), +}; + +static void pyopencv_${name}_dealloc(PyObject* self) +{ + ((pyopencv_${name}_t*)self)->v.release(); + PyObject_Del(self); +} + +template<> PyObject* pyopencv_from(const Ptr<${cname}>& r) +{ + pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); + new (&(m->v)) Ptr<$cname1>(); // init Ptr with placement new + m->v = r; + return (PyObject*)m; +} + +template<> bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name) +{ + if( src == NULL || src == Py_None ) + return true; + if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type)) + { + failmsg("Expected ${cname} for argument '%%s'", name); + return false; + } + dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>(); + return true; +} + +""" % head_init_str) + +gen_template_map_type_cvt = Template(""" +template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name); +""") + +gen_template_set_prop_from_map = Template(""" + if( PyMapping_HasKeyString(src, (char*)"$propname") ) + { + tmp = PyMapping_GetItemString(src, (char*)"$propname"); + ok = tmp && pyopencv_to(tmp, dst.$propname); + Py_DECREF(tmp); + if(!ok) return false; + }""") + +gen_template_type_impl = Template(""" +static PyObject* pyopencv_${name}_repr(PyObject* self) +{ + char str[1000]; + sprintf(str, "<$wname %p>", self); + return PyString_FromString(str); +} + +${getset_code} + +static PyGetSetDef pyopencv_${name}_getseters[] = +{${getset_inits} + {NULL} /* Sentinel */ +}; + +${methods_code} + +static PyMethodDef pyopencv_${name}_methods[] = +{ +${methods_inits} + {NULL, NULL} +}; + +static void pyopencv_${name}_specials(void) +{ + pyopencv_${name}_Type.tp_base = ${baseptr}; + pyopencv_${name}_Type.tp_dealloc = pyopencv_${name}_dealloc; + pyopencv_${name}_Type.tp_repr = pyopencv_${name}_repr; + pyopencv_${name}_Type.tp_getset = pyopencv_${name}_getseters; + pyopencv_${name}_Type.tp_methods = pyopencv_${name}_methods;${extra_specials} +} +""") + + +gen_template_get_prop = Template(""" +static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure) +{ + return pyopencv_from(p->v${access}${member}); +} +""") + +gen_template_get_prop_algo = Template(""" +static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure) +{ + return pyopencv_from(dynamic_cast<$cname*>(p->v.get())${access}${member}); +} +""") + +gen_template_set_prop = Template(""" +static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure) +{ + if (value == NULL) + { + PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute"); + return -1; + } + return pyopencv_to(value, p->v${access}${member}) ? 0 : -1; +} +""") + +gen_template_set_prop_algo = Template(""" +static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure) +{ + if (value == NULL) + { + PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute"); + return -1; + } + return pyopencv_to(value, dynamic_cast<$cname*>(p->v.get())${access}${member}) ? 0 : -1; +} +""") + + +gen_template_prop_init = Template(""" + {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, NULL, (char*)"${member}", NULL},""") + +gen_template_rw_prop_init = Template(""" + {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, (setter)pyopencv_${name}_set_${member}, (char*)"${member}", NULL},""") + +simple_argtype_mapping = { + "bool": ("bool", "b", "0"), + "int": ("int", "i", "0"), + "float": ("float", "f", "0.f"), + "double": ("double", "d", "0"), + "c_string": ("char*", "s", '(char*)""') +} + +def normalize_class_name(name): + return re.sub(r"^cv\.", "", name).replace(".", "_") + +class ClassProp(object): + def __init__(self, decl): + self.tp = decl[0].replace("*", "_ptr") + self.name = decl[1] + self.readonly = True + if "/RW" in decl[3]: + self.readonly = False + +class ClassInfo(object): + def __init__(self, name, decl=None, root_ns="cv"): + self.cname = name.replace(".", "::") + self.name = self.wname = normalize_class_name(name) + self.ismap = False + self.issimple = False + self.isalgorithm = False + self.methods = {} + self.props = [] + self.consts = {} + self.base = None + customname = False + + if decl: + bases = decl[1].split()[1:] + if len(bases) > 1: + print("Note: Class %s has more than 1 base class (not supported by Python C extensions)" % (self.name,)) + print(" Bases: ", " ".join(bases)) + print(" Only the first base class will be used") + #return sys.exit(-1) + elif len(bases) == 1: + self.base = bases[0].strip(",") + self.base = re.sub(r"^"+self.root_ns+r"::\.", "", self.base) + if self.base == "Algorithm" and self.root_ns == "cv": + self.isalgorithm = True + self.base = self.base.replace("::", "_") + + for m in decl[2]: + if m.startswith("="): + self.wname = m[1:] + customname = True + elif m == "/Map": + self.ismap = True + elif m == "/Simple": + self.issimple = True + self.props = [ClassProp(p) for p in decl[3]] + + if not customname and self.wname.startswith("Cv") and self.root_ns == "cv": + self.wname = self.wname[2:] + + def gen_map_code(self, all_classes): + code = "static bool pyopencv_to(PyObject* src, %s& dst, const char* name)\n{\n PyObject* tmp;\n bool ok;\n" % (self.cname) + code += "".join([gen_template_set_prop_from_map.substitute(propname=p.name,proptype=p.tp) for p in self.props]) + if self.base: + code += "\n return pyopencv_to(src, (%s&)dst, name);\n}\n" % all_classes[self.base].cname + else: + code += "\n return true;\n}\n" + return code + + def gen_code(self, all_classes): + if self.ismap: + return self.gen_map_code(all_classes) + + getset_code = StringIO() + getset_inits = StringIO() + + sorted_props = [(p.name, p) for p in self.props] + sorted_props.sort() + + access_op = "->" + if self.issimple: + access_op = "." + + for pname, p in sorted_props: + if self.isalgorithm: + getset_code.write(gen_template_get_prop_algo.substitute(name=self.name, cname=self.cname, member=pname, membertype=p.tp, access=access_op)) + else: + getset_code.write(gen_template_get_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op)) + if p.readonly: + getset_inits.write(gen_template_prop_init.substitute(name=self.name, member=pname)) + else: + if self.isalgorithm: + getset_code.write(gen_template_set_prop_algo.substitute(name=self.name, cname=self.cname, member=pname, membertype=p.tp, access=access_op)) + else: + getset_code.write(gen_template_set_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op)) + getset_inits.write(gen_template_rw_prop_init.substitute(name=self.name, member=pname)) + + methods_code = StringIO() + methods_inits = StringIO() + + sorted_methods = list(self.methods.items()) + sorted_methods.sort() + + for mname, m in sorted_methods: + methods_code.write(m.gen_code(all_classes)) + methods_inits.write(m.get_tab_entry()) + + baseptr = "NULL" + if self.base and self.base in all_classes: + baseptr = "&pyopencv_" + all_classes[self.base].name + "_Type" + + code = gen_template_type_impl.substitute(name=self.name, wname=self.wname, cname=self.cname, + getset_code=getset_code.getvalue(), getset_inits=getset_inits.getvalue(), + methods_code=methods_code.getvalue(), methods_inits=methods_inits.getvalue(), + baseptr=baseptr, extra_specials="") + + return code + + +def handle_ptr(tp): + if tp.startswith('Ptr_'): + tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>' + return tp + + +class ArgInfo(object): + def __init__(self, arg_tuple): + self.tp = handle_ptr(arg_tuple[0]) + self.name = arg_tuple[1] + self.defval = arg_tuple[2] + self.isarray = False + self.arraylen = 0 + self.arraycvt = None + self.inputarg = True + self.outputarg = False + self.returnarg = False + for m in arg_tuple[3]: + if m == "/O": + self.inputarg = False + self.outputarg = True + self.returnarg = True + elif m == "/IO": + self.inputarg = True + self.outputarg = True + self.returnarg = True + elif m.startswith("/A"): + self.isarray = True + self.arraylen = m[2:].strip() + elif m.startswith("/CA"): + self.isarray = True + self.arraycvt = m[2:].strip() + self.py_inputarg = False + self.py_outputarg = False + + def isbig(self): + return self.tp == "Mat" or self.tp == "vector_Mat"# or self.tp.startswith("vector") + + def crepr(self): + return "ArgInfo(\"%s\", %d)" % (self.name, self.outputarg) + + +class FuncVariant(object): + def __init__(self, classname, name, decl, isconstructor, root_ns="cv"): + self.classname = classname + self.name = self.wname = name + self.isconstructor = isconstructor + self.root_ns = root_ns + + self.rettype = decl[4] if len(decl) >=5 else handle_ptr(decl[1]) + if self.rettype == "void": + self.rettype = "" + self.args = [] + self.array_counters = {} + for a in decl[3]: + ainfo = ArgInfo(a) + if ainfo.isarray and not ainfo.arraycvt: + c = ainfo.arraylen + c_arrlist = self.array_counters.get(c, []) + if c_arrlist: + c_arrlist.append(ainfo.name) + else: + self.array_counters[c] = [ainfo.name] + self.args.append(ainfo) + self.init_pyproto() + + def init_pyproto(self): + # string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g. + # "src1, src2[, dst[, mask]]" for cv.add + argstr = "" + + # list of all input arguments of the Python function, with the argument numbers: + # [("src1", 0), ("src2", 1), ("dst", 2), ("mask", 3)] + # we keep an argument number to find the respective argument quickly, because + # some of the arguments of C function may not present in the Python function (such as array counters) + # or even go in a different order ("heavy" output parameters of the C function + # become the first optional input parameters of the Python function, and thus they are placed right after + # non-optional input parameters) + arglist = [] + + # the list of "heavy" output parameters. Heavy parameters are the parameters + # that can be expensive to allocate each time, such as vectors and matrices (see isbig). + outarr_list = [] + + # the list of output parameters. Also includes input/output parameters. + outlist = [] + + firstoptarg = 1000000 + argno = -1 + for a in self.args: + argno += 1 + if a.name in self.array_counters: + continue + if a.tp in ignored_arg_types: + continue + if a.returnarg: + outlist.append((a.name, argno)) + if (not a.inputarg) and a.isbig(): + outarr_list.append((a.name, argno)) + continue + if not a.inputarg: + continue + if not a.defval: + arglist.append((a.name, argno)) + else: + firstoptarg = min(firstoptarg, len(arglist)) + # if there are some array output parameters before the first default parameter, they + # are added as optional parameters before the first optional parameter + if outarr_list: + arglist += outarr_list + outarr_list = [] + arglist.append((a.name, argno)) + + if outarr_list: + firstoptarg = min(firstoptarg, len(arglist)) + arglist += outarr_list + firstoptarg = min(firstoptarg, len(arglist)) + + noptargs = len(arglist) - firstoptarg + argnamelist = [aname for aname, argno in arglist] + argstr = ", ".join(argnamelist[:firstoptarg]) + argstr = "[, ".join([argstr] + argnamelist[firstoptarg:]) + argstr += "]" * noptargs + if self.rettype: + outlist = [("retval", -1)] + outlist + elif self.isconstructor: + assert outlist == [] + outlist = [("self", -1)] + if self.isconstructor: + classname = self.classname + if classname.startswith("Cv") and self.root_ns == "cv": + classname=classname[2:] + outstr = "<%s object>" % (classname,) + elif outlist: + outstr = ", ".join([o[0] for o in outlist]) + else: + outstr = "None" + + self.py_docstring = "%s(%s) -> %s" % (self.wname, argstr, outstr) + self.py_noptargs = noptargs + self.py_arglist = arglist + for aname, argno in arglist: + self.args[argno].py_inputarg = True + for aname, argno in outlist: + if argno >= 0: + self.args[argno].py_outputarg = True + self.py_outlist = outlist + + +class FuncInfo(object): + def __init__(self, classname, name, cname, isconstructor, namespace, root_ns="cv"): + self.classname = classname + self.name = name + self.cname = cname + self.isconstructor = isconstructor + self.namespace = namespace + self.variants = [] + self.root_ns = root_ns + + def add_variant(self, decl): + self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor, root_ns=self.root_ns)) + + def get_wrapper_name(self): + name = self.name + if self.classname: + classname = self.classname + "_" + if "[" in name: + name = "getelem" + else: + classname = "" + return "pyopencv_" + self.namespace.replace('.','_') + '_' + classname + name + + def get_wrapper_prototype(self): + full_fname = self.get_wrapper_name() + if self.classname and not self.isconstructor: + self_arg = "self" + else: + self_arg = "" + return "static PyObject* %s(PyObject* %s, PyObject* args, PyObject* kw)" % (full_fname, self_arg) + + def get_tab_entry(self): + docstring_list = [] + have_empty_constructor = False + for v in self.variants: + s = v.py_docstring + if (not v.py_arglist) and self.isconstructor: + have_empty_constructor = True + if s not in docstring_list: + docstring_list.append(s) + # if there are just 2 constructors: default one and some other, + # we simplify the notation. + # Instead of ClassName(args ...) -> object or ClassName() -> object + # we write ClassName([args ...]) -> object + if have_empty_constructor and len(self.variants) == 2: + idx = self.variants[1].py_arglist != [] + s = self.variants[idx].py_docstring + p1 = s.find("(") + p2 = s.rfind(")") + docstring_list = [s[:p1+1] + "[" + s[p1+1:p2] + "]" + s[p2:]] + + return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n' + ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(), + py_docstring = " or ".join(docstring_list)) + + def gen_code(self, all_classes): + proto = self.get_wrapper_prototype() + code = "%s\n{\n" % (proto,) + code += " using namespace %s;\n\n" % self.namespace.replace('.', '::') + + selfinfo = ClassInfo("", root_ns=self.root_ns) + ismethod = self.classname != "" and not self.isconstructor + # full name is needed for error diagnostic in PyArg_ParseTupleAndKeywords + fullname = self.name + + if self.classname: + selfinfo = all_classes[self.classname] + if not self.isconstructor: + amp = "&" if selfinfo.issimple else "" + if selfinfo.isalgorithm: + code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) + else: + get = "" if selfinfo.issimple else ".get()" + code += gen_template_check_self.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp, get=get) + fullname = selfinfo.wname + "." + fullname + + all_code_variants = [] + declno = -1 + for v in self.variants: + code_decl = "" + code_ret = "" + code_cvt_list = [] + + code_args = "(" + all_cargs = [] + parse_arglist = [] + + # declare all the C function arguments, + # add necessary conversions from Python objects to code_cvt_list, + # form the function/method call, + # for the list of type mappings + for a in v.args: + if a.tp in ignored_arg_types: + defval = a.defval + if not defval and a.tp.endswith("*"): + defval = 0 + assert defval + if not code_args.endswith("("): + code_args += ", " + code_args += defval + all_cargs.append([[None, ""], ""]) + continue + tp1 = tp = a.tp + amp = "" + defval0 = "" + if tp.endswith("*"): + tp = tp1 = tp[:-1] + amp = "&" + if tp.endswith("*"): + defval0 = "0" + tp1 = tp.replace("*", "_ptr") + if tp1.endswith("*"): + print("Error: type with star: a.tp=%s, tp=%s, tp1=%s" % (a.tp, tp, tp1)) + sys.exit(-1) + + amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0)) + parse_name = a.name + if a.py_inputarg: + if amapping[1] == "O": + code_decl += " PyObject* pyobj_%s = NULL;\n" % (a.name,) + parse_name = "pyobj_" + a.name + if a.tp == 'char': + code_cvt_list.append("convert_to_char(pyobj_%s, &%s, %s)"% (a.name, a.name, a.crepr())) + else: + code_cvt_list.append("pyopencv_to(pyobj_%s, %s, %s)" % (a.name, a.name, a.crepr())) + + all_cargs.append([amapping, parse_name]) + + defval = a.defval + if not defval: + defval = amapping[2] + # "tp arg = tp();" is equivalent to "tp arg;" in the case of complex types + if defval == tp + "()" and amapping[1] == "O": + defval = "" + if a.outputarg and not a.inputarg: + defval = "" + if defval: + code_decl += " %s %s=%s;\n" % (amapping[0], a.name, defval) + else: + code_decl += " %s %s;\n" % (amapping[0], a.name) + + if not code_args.endswith("("): + code_args += ", " + code_args += amp + a.name + + code_args += ")" + + if self.isconstructor: + code_decl += " pyopencv_%s_t* self = 0;\n" % selfinfo.name + if selfinfo.issimple: + templ_prelude = gen_template_simple_call_constructor_prelude + templ = gen_template_simple_call_constructor + else: + templ_prelude = gen_template_call_constructor_prelude + templ = gen_template_call_constructor + + code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname) + code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname, args=code_args) + else: + code_prelude = "" + code_fcall = "" + if v.rettype: + code_decl += " " + v.rettype + " retval;\n" + code_fcall += "retval = " + if ismethod: + code_fcall += "_self_->" + self.cname + else: + code_fcall += self.cname + code_fcall += code_args + + if code_cvt_list: + code_cvt_list = [""] + code_cvt_list + + # add info about return value, if any, to all_cargs. if there non-void return value, + # it is encoded in v.py_outlist as ("retval", -1) pair. + # As [-1] in Python accesses the last element of a list, we automatically handle the return value by + # adding the necessary info to the end of all_cargs list. + if v.rettype: + tp = v.rettype + tp1 = tp.replace("*", "_ptr") + amapping = simple_argtype_mapping.get(tp, (tp, "O", "0")) + all_cargs.append(amapping) + + if v.args and v.py_arglist: + # form the format spec for PyArg_ParseTupleAndKeywords + fmtspec = "".join([all_cargs[argno][0][1] for aname, argno in v.py_arglist]) + if v.py_noptargs > 0: + fmtspec = fmtspec[:-v.py_noptargs] + "|" + fmtspec[-v.py_noptargs:] + fmtspec += ":" + fullname + + # form the argument parse code that: + # - declares the list of keyword parameters + # - calls PyArg_ParseTupleAndKeywords + # - converts complex arguments from PyObject's to native OpenCV types + code_parse = gen_template_parse_args.substitute( + kw_list = ", ".join(['"' + aname + '"' for aname, argno in v.py_arglist]), + fmtspec = fmtspec, + parse_arglist = ", ".join(["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]), + code_cvt = " &&\n ".join(code_cvt_list)) + else: + code_parse = "if(PyObject_Size(args) == 0 && (kw == NULL || PyObject_Size(kw) == 0))" + + if len(v.py_outlist) == 0: + code_ret = "Py_RETURN_NONE" + elif len(v.py_outlist) == 1: + if self.isconstructor: + code_ret = "return (PyObject*)self" + else: + aname, argno = v.py_outlist[0] + code_ret = "return pyopencv_from(%s)" % (aname,) + else: + # ther is more than 1 return parameter; form the tuple out of them + fmtspec = "N"*len(v.py_outlist) + backcvt_arg_list = [] + for aname, argno in v.py_outlist: + amapping = all_cargs[argno][0] + backcvt_arg_list.append("%s(%s)" % (amapping[2], aname)) + code_ret = "return Py_BuildValue(\"(%s)\", %s)" % \ + (fmtspec, ", ".join(["pyopencv_from(" + aname + ")" for aname, argno in v.py_outlist])) + + all_code_variants.append(gen_template_func_body.substitute(code_decl=code_decl, + code_parse=code_parse, code_prelude=code_prelude, code_fcall=code_fcall, code_ret=code_ret)) + + if len(all_code_variants)==1: + # if the function/method has only 1 signature, then just put it + code += all_code_variants[0] + else: + # try to execute each signature + code += " PyErr_Clear();\n\n".join([" {\n" + v + " }\n" for v in all_code_variants]) + code += "\n return NULL;\n}\n\n" + return code + + +class Namespace(object): + def __init__(self): + self.funcs = {} + self.consts = {} + + +class PythonWrapperGenerator(object): + def __init__(self, root_ns, module_name): + self.clear() + self.root_ns = root_ns + self.module_name = module_name + + def clear(self): + self.classes = {} + self.namespaces = {} + self.consts = {} + self.code_include = StringIO() + self.code_types = StringIO() + self.code_funcs = StringIO() + self.code_type_reg = StringIO() + self.code_ns_reg = StringIO() + self.class_idx = 0 + + def add_class(self, stype, name, decl): + classinfo = ClassInfo(name, decl, root_ns=self.root_ns) + classinfo.decl_idx = self.class_idx + self.class_idx += 1 + + if classinfo.name in self.classes: + print("Generator error: class %s (cname=%s) already exists" \ + % (classinfo.name, classinfo.cname)) + sys.exit(-1) + self.classes[classinfo.name] = classinfo + + def split_decl_name(self, name): + chunks = name.split('.') + namespace = chunks[:-1] + classes = [] + while namespace and '.'.join(namespace) not in self.parser.namespaces: + classes.insert(0, namespace.pop()) + return namespace, classes, chunks[-1] + + + def add_const(self, name, decl): + cname = name.replace('.','::') + namespace, classes, name = self.split_decl_name(name) + namespace = '.'.join(namespace) + name = '_'.join(classes+[name]) + ns = self.namespaces.setdefault(namespace, Namespace()) + if name in ns.consts: + print("Generator error: constant %s (cname=%s) already exists" \ + % (name, cname)) + sys.exit(-1) + ns.consts[name] = cname + + def add_func(self, decl): + namespace, classes, barename = self.split_decl_name(decl[0]) + cname = "::".join(namespace+classes+[barename]) + name = barename + classname = '' + bareclassname = '' + if classes: + classname = normalize_class_name('.'.join(namespace+classes)) + bareclassname = classes[-1] + namespace = '.'.join(namespace) + + isconstructor = name == bareclassname + isclassmethod = False + for m in decl[2]: + if m == "/S": + isclassmethod = True + elif m.startswith("="): + name = m[1:] + if isclassmethod: + name = "_".join(classes+[name]) + classname = '' + elif isconstructor: + name = "_".join(classes[:-1]+[name]) + + if classname and not isconstructor: + cname = barename + func_map = self.classes[classname].methods + else: + func_map = self.namespaces.setdefault(namespace, Namespace()).funcs + + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, root_ns=self.root_ns)) + func.add_variant(decl) + + + def gen_namespace(self, ns_name): + ns = self.namespaces[ns_name] + wname = normalize_class_name(ns_name) + + self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname) + for name, func in sorted(ns.funcs.items()): + self.code_ns_reg.write(func.get_tab_entry()) + self.code_ns_reg.write(' {NULL, NULL}\n};\n\n') + + self.code_ns_reg.write('static ConstDef consts_%s[] = {\n'%wname) + for name, cname in sorted(ns.consts.items()): + self.code_ns_reg.write(' {"%s", %s},\n'%(name, cname)) + compat_name = re.sub(r"([a-z])([A-Z])", r"\1_\2", name).upper() + if name != compat_name: + self.code_ns_reg.write(' {"%s", %s},\n'%(compat_name, cname)) + self.code_ns_reg.write(' {NULL, 0}\n};\n\n') + + def gen_namespaces_reg(self): + self.code_ns_reg.write('static void init_submodules(PyObject * root) \n{\n') + for ns_name in sorted(self.namespaces): + if ns_name.split('.')[0] == self.root_ns: + wname = normalize_class_name(ns_name) + mod_name = self.module_name + ns_name[len(self.root_ns):] + self.code_ns_reg.write( + ' init_submodule(root, "%s", methods_%s, consts_%s);\n' + % (mod_name, wname, wname)) + self.code_ns_reg.write('}\n') + + + def save(self, path, name, buf): + f = open(path + "/" + name, "wt") + f.write(buf.getvalue()) + f.close() + + def gen(self, srcfiles, output_path): + self.clear() + self.parser = hdr_parser.CppHeaderParser(self.root_ns) + + # step 1: scan the headers and build more descriptive maps of classes, consts, functions + for hdr in srcfiles: + decls = self.parser.parse(hdr) + if len(decls) == 0: + continue + #self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]) ) + self.code_include.write('#include "{0}"\n'.format(hdr)) + for decl in decls: + name = decl[0] + if name.startswith("struct") or name.startswith("class"): + # class/struct + p = name.find(" ") + stype = name[:p] + name = name[p+1:].strip() + self.add_class(stype, name, decl) + elif name.startswith("const"): + # constant + self.add_const(name.replace("const ", "").strip(), decl) + else: + # function + self.add_func(decl) + + # step 1.5 check if all base classes exist + for name, classinfo in self.classes.items(): + if classinfo.base: + chunks = classinfo.base.split('_') + base = '_'.join(chunks) + while base not in self.classes and len(chunks)>1: + del chunks[-2] + base = '_'.join(chunks) + if base not in self.classes: + print("Generator error: unable to resolve base %s for %s" + % (classinfo.base, classinfo.name)) + sys.exit(-1) + classinfo.base = base + classinfo.isalgorithm |= self.classes[base].isalgorithm + self.classes[name] = classinfo + + # step 2: generate code for the classes and their methods + classlist = list(self.classes.items()) + classlist.sort() + for name, classinfo in classlist: + if classinfo.ismap: + self.code_types.write(gen_template_map_type_cvt.substitute(name=name, cname=classinfo.cname)) + else: + if classinfo.issimple: + templ = gen_template_simple_type_decl + else: + templ = gen_template_type_decl + self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname, + cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname))) + + # register classes in the same order as they have been declared. + # this way, base classes will be registered in Python before their derivatives. + classlist1 = [(classinfo.decl_idx, name, classinfo) for name, classinfo in classlist] + classlist1.sort() + + for decl_idx, name, classinfo in classlist1: + code = classinfo.gen_code(self.classes) + self.code_types.write(code) + if not classinfo.ismap: + self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) + + # step 3: generate the code for all the global functions + for ns_name, ns in sorted(self.namespaces.items()): + if ns_name.split('.')[0] != self.root_ns: + continue + for name, func in sorted(ns.funcs.items()): + code = func.gen_code(self.classes) + self.code_funcs.write(code) + self.gen_namespace(ns_name) + self.gen_namespaces_reg() + + # step 4: generate the code for constants + constlist = list(self.consts.items()) + constlist.sort() + for name, constinfo in constlist: + self.gen_const_reg(constinfo) + + # That's it. Now save all the files + self.save(output_path, "pyopencv_generated_include.h", self.code_include) + self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs) + self.save(output_path, "pyopencv_generated_types.h", self.code_types) + self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg) + self.save(output_path, "pyopencv_generated_ns_reg.h", self.code_ns_reg) + + +def main(): + srcfiles = None + dstdir = None + assert len(sys.argv) == 5 + dstdir = sys.argv[1] + srcfiles = open(sys.argv[2], 'r').read().split(';') + root_ns = sys.argv[3] + module_name = sys.argv[4] + + generator = PythonWrapperGenerator(root_ns=root_ns, + module_name=module_name) + generator.gen(srcfiles, dstdir) + + +if __name__ == "__main__": + main() diff --git a/cmake/bin/hdr_parser.py b/cmake/bin/hdr_parser.py new file mode 100755 index 0000000..b2007fa --- /dev/null +++ b/cmake/bin/hdr_parser.py @@ -0,0 +1,913 @@ +#!/usr/bin/env python + +# The content of this file has been taken from the OpenCV project at +# https://github.com/Itseez/opencv +# It has been modified to make it more reusable for generating binding +# on other projects. +# modifications: Nicolas Granger + +# For your information, the original License is given below: + +# By downloading, copying, installing or using the software you agree to this license. +# If you do not agree to this license, do not download, install, +# copy or use the software. +# +# +# License Agreement +# For Open Source Computer Vision Library +# (3-clause BSD License) +# +# Copyright (C) 2000-2016, Intel Corporation, all rights reserved. +# Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +# Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +# Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +# Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +# Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +# Third party copyrights are property of their respective owners. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# * Neither the names of the copyright holders nor the names of the contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# This software is provided by the copyright holders and contributors "as is" and +# any express or implied warranties, including, but not limited to, the implied +# warranties of merchantability and fitness for a particular purpose are disclaimed. +# In no event shall copyright holders or contributors be liable for any direct, +# indirect, incidental, special, exemplary, or consequential damages +# (including, but not limited to, procurement of substitute goods or services; +# loss of use, data, or profits; or business interruption) however caused +# and on any theory of liability, whether in contract, strict liability, +# or tort (including negligence or otherwise) arising in any way out of +# the use of this software, even if advised of the possibility of such damage. + +from __future__ import print_function +import os, sys, re, string, io + + +""" +Each declaration is [funcname, return_value_type /* in C, not in Python */, , ], +where each element of is 4-element list itself: +[argtype, argname, default_value /* or "" if none */, ] +where the list of modifiers is yet another nested list of strings + (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods + and "/A value" for the plain C arrays with counters) +""" + +class CppHeaderParser(object): + + def __init__(self, root_ns): + self.BLOCK_TYPE = 0 + self.BLOCK_NAME = 1 + self.PROCESS_FLAG = 2 + self.PUBLIC_SECTION = 3 + self.CLASS_DECL = 4 + + self.root_ns = root_ns + self.namespaces = set() + + def batch_replace(self, s, pairs): + for before, after in pairs: + s = s.replace(before, after) + return s + + def get_macro_arg(self, arg_str, npos): + npos2 = npos3 = arg_str.find("(", npos) + if npos2 < 0: + print("Error: no arguments for the macro at %d" % (self.lineno,)) + sys.exit(-1) + balance = 1 + while 1: + t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1) + if npos3 < 0: + print("Error: no matching ')' in the macro call at %d" % (self.lineno,)) + sys.exit(-1) + if t == '(': + balance += 1 + if t == ')': + balance -= 1 + if balance == 0: + break + + return arg_str[npos2+1:npos3].strip(), npos3 + + def parse_arg(self, arg_str, argno): + """ + Parses [arg_name] + Returns arg_type, arg_name, modlist, argno, where + modlist is the list of wrapper-related modifiers (such as "output argument", "has counter", ...) + and argno is the new index of an anonymous argument. + That is, if no arg_str is just an argument type without argument name, the argument name is set to + "arg" + str(argno), and then argno is incremented. + """ + modlist = [] + + # pass 0: extracts the modifiers + if "CV_OUT" in arg_str: + modlist.append("/O") + arg_str = arg_str.replace("CV_OUT", "") + + if "CV_IN_OUT" in arg_str: + modlist.append("/IO") + arg_str = arg_str.replace("CV_IN_OUT", "") + + isarray = False + npos = arg_str.find("CV_CARRAY") + if npos >= 0: + isarray = True + macro_arg, npos3 = self.get_macro_arg(arg_str, npos) + + modlist.append("/A " + macro_arg) + arg_str = arg_str[:npos] + arg_str[npos3+1:] + + npos = arg_str.find("CV_CUSTOM_CARRAY") + if npos >= 0: + isarray = True + macro_arg, npos3 = self.get_macro_arg(arg_str, npos) + + modlist.append("/CA " + macro_arg) + arg_str = arg_str[:npos] + arg_str[npos3+1:] + + arg_str = arg_str.strip() + word_start = 0 + word_list = [] + npos = -1 + + #print self.lineno, ":\t", arg_str + + # pass 1: split argument type into tokens + while 1: + npos += 1 + t, npos = self.find_next_token(arg_str, [" ", "&", "*", "<", ">", ","], npos) + w = arg_str[word_start:npos].strip() + if w == "operator": + word_list.append("operator " + arg_str[npos:].strip()) + break + if w not in ["", "const"]: + word_list.append(w) + if t not in ["", " ", "&"]: + word_list.append(t) + if not t: + break + word_start = npos+1 + npos = word_start - 1 + + arg_type = "" + arg_name = "" + angle_stack = [] + + #print self.lineno, ":\t", word_list + + # pass 2: decrypt the list + wi = -1 + prev_w = "" + for w in word_list: + wi += 1 + if w == "*": + if prev_w == "char" and not isarray: + arg_type = arg_type[:-len("char")] + "c_string" + else: + arg_type += w + continue + elif w == "<": + arg_type += "_" + angle_stack.append(0) + elif w == "," or w == '>': + if not angle_stack: + print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,)) + sys.exit(-1) + if w == ",": + arg_type += "_and_" + elif w == ">": + if angle_stack[0] == 0: + print("Error at %s:%d: template has no arguments" % (self.hname, self.lineno)) + sys.exit(-1) + if angle_stack[0] > 1: + arg_type += "_end_" + angle_stack[-1:] = [] + elif angle_stack: + arg_type += w + angle_stack[-1] += 1 + elif arg_type == "struct": + arg_type += " " + w + elif arg_type and arg_type != "~": + arg_name = " ".join(word_list[wi:]) + break + else: + arg_type += w + prev_w = w + + counter_str = "" + add_star = False + if ("[" in arg_name) and not ("operator" in arg_str): + #print arg_str + p1 = arg_name.find("[") + p2 = arg_name.find("]",p1+1) + if p2 < 0: + print("Error at %d: no closing ]" % (self.lineno,)) + sys.exit(-1) + counter_str = arg_name[p1+1:p2].strip() + if counter_str == "": + counter_str = "?" + if not isarray: + modlist.append("/A " + counter_str.strip()) + arg_name = arg_name[:p1] + add_star = True + + if not arg_name: + if arg_type.startswith("operator"): + arg_type, arg_name = "", arg_type + else: + arg_name = "arg" + str(argno) + argno += 1 + + while arg_type.endswith("_end_"): + arg_type = arg_type[:-len("_end_")] + + if add_star: + arg_type += "*" + + arg_type = self.batch_replace(arg_type, [("std::", ""), ("cv::", ""), ("::", "_")]) + + return arg_type, arg_name, modlist, argno + + def parse_enum(self, decl_str): + l = decl_str + ll = l.split(",") + if ll[-1].strip() == "": + ll = ll[:-1] + prev_val = "" + prev_val_delta = -1 + decl = [] + for pair in ll: + pv = pair.split("=") + if len(pv) == 1: + prev_val_delta += 1 + val = "" + if prev_val: + val = prev_val + "+" + val += str(prev_val_delta) + else: + prev_val_delta = 0 + prev_val = val = pv[1].strip() + decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []]) + return decl + + def parse_class_decl(self, decl_str): + """ + Parses class/struct declaration start in the form: + {class|struct} [CV_EXPORTS] [: public [, ...]] + Returns class_name1, + """ + l = decl_str + modlist = [] + if "CV_EXPORTS_W_MAP" in l: + l = l.replace("CV_EXPORTS_W_MAP", "") + modlist.append("/Map") + if "CV_EXPORTS_W_SIMPLE" in l: + l = l.replace("CV_EXPORTS_W_SIMPLE", "") + modlist.append("/Simple") + npos = l.find("CV_EXPORTS_AS") + if npos >= 0: + macro_arg, npos3 = self.get_macro_arg(l, npos) + modlist.append("=" + macro_arg) + l = l[:npos] + l[npos3+1:] + + l = self.batch_replace(l, [("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("public virtual ", " "), ("public ", " "), ("::", ".")]).strip() + ll = re.split(r'\s*[,:]?\s*', l) + ll = [le for le in ll if le] + classname = ll[1] + bases = ll[2:] + return classname, bases, modlist + + def parse_func_decl_no_wrap(self, decl_str, static_method = False): + decl_str = (decl_str or "").strip() + virtual_method = False + explicit_method = False + if decl_str.startswith("explicit"): + decl_str = decl_str[len("explicit"):].lstrip() + explicit_method = True + if decl_str.startswith("virtual"): + decl_str = decl_str[len("virtual"):].lstrip() + virtual_method = True + if decl_str.startswith("static"): + decl_str = decl_str[len("static"):].lstrip() + static_method = True + + fdecl = decl_str.replace("CV_OUT", "").replace("CV_IN_OUT", "") + fdecl = fdecl.strip().replace("\t", " ") + while " " in fdecl: + fdecl = fdecl.replace(" ", " ") + fname = fdecl[:fdecl.find("(")].strip() + fnpos = fname.rfind(" ") + if fnpos < 0: + fnpos = 0 + fname = fname[fnpos:].strip() + rettype = fdecl[:fnpos].strip() + + if rettype.endswith("operator"): + fname = ("operator " + fname).strip() + rettype = rettype[:rettype.rfind("operator")].strip() + if rettype.endswith("::"): + rpos = rettype.rfind(" ") + if rpos >= 0: + fname = rettype[rpos+1:].strip() + fname + rettype = rettype[:rpos].strip() + else: + fname = rettype + fname + rettype = "" + + apos = fdecl.find("(") + if fname.endswith("operator"): + fname += " ()" + apos = fdecl.find("(", apos+1) + + fname = self.root_ns + "." + fname.replace("::", ".") + decl = [fname, rettype, [], []] + + # inline constructor implementation + implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:]) + if bool(implmatch): + fdecl = fdecl[:apos] + implmatch.group(1) + + args0str = fdecl[apos+1:fdecl.rfind(")")].strip() + + if args0str != "" and args0str != "void": + args0str = re.sub(r"\([^)]*\)", lambda m: m.group(0).replace(',', "@comma@"), args0str) + args0 = args0str.split(",") + + args = [] + narg = "" + for arg in args0: + narg += arg.strip() + balance_paren = narg.count("(") - narg.count(")") + balance_angle = narg.count("<") - narg.count(">") + if balance_paren == 0 and balance_angle == 0: + args.append(narg.strip()) + narg = "" + + for arg in args: + dfpos = arg.find("=") + defval = "" + if dfpos >= 0: + defval = arg[dfpos+1:].strip() + else: + dfpos = arg.find("CV_DEFAULT") + if dfpos >= 0: + defval, pos3 = self.get_macro_arg(arg, dfpos) + else: + dfpos = arg.find("CV_WRAP_DEFAULT") + if dfpos >= 0: + defval, pos3 = self.get_macro_arg(arg, dfpos) + if dfpos >= 0: + defval = defval.replace("@comma@", ",") + arg = arg[:dfpos].strip() + pos = len(arg)-1 + while pos >= 0 and (arg[pos] in "_[]" or arg[pos].isalpha() or arg[pos].isdigit()): + pos -= 1 + if pos >= 0: + aname = arg[pos+1:].strip() + atype = arg[:pos+1].strip() + if aname.endswith("&") or aname.endswith("*") or (aname in ["int", "String", "Mat"]): + atype = (atype + " " + aname).strip() + aname = "" + else: + atype = arg + aname = "" + if aname.endswith("]"): + bidx = aname.find('[') + atype += aname[bidx:] + aname = aname[:bidx] + decl[3].append([atype, aname, defval, []]) + + if static_method: + decl[2].append("/S") + if virtual_method: + decl[2].append("/V") + if explicit_method: + decl[2].append("/E") + if bool(re.match(r".*\)\s*(const)?\s*=\s*0", decl_str)): + decl[2].append("/A") + if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)): + decl[2].append("/C") + if "virtual" in decl_str: + print(decl_str) + return decl + + def parse_func_decl(self, decl_str): + """ + Parses the function or method declaration in the form: + [([CV_EXPORTS] ) | CVAPI(rettype)] + [~] + ( [=] [, [=] ...]) + [const] {; | } + + Returns the function declaration entry: + [, , , ] (see above) + """ + + if self.wrap_mode: + if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \ + ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)): + return [] + + # ignore old API in the documentation check (for now) + if "CVAPI(" in decl_str and self.wrap_mode: + return [] + + top = self.block_stack[-1] + func_modlist = [] + + npos = decl_str.find("CV_EXPORTS_AS") + if npos >= 0: + arg, npos3 = self.get_macro_arg(decl_str, npos) + func_modlist.append("="+arg) + decl_str = decl_str[:npos] + decl_str[npos3+1:] + npos = decl_str.find("CV_WRAP_AS") + if npos >= 0: + arg, npos3 = self.get_macro_arg(decl_str, npos) + func_modlist.append("="+arg) + decl_str = decl_str[:npos] + decl_str[npos3+1:] + + # filter off some common prefixes, which are meaningless for Python wrappers. + # note that we do not strip "static" prefix, which does matter; + # it means class methods, not instance methods + decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\ + ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")]).strip() + + static_method = False + context = top[0] + if decl_str.startswith("static") and (context == "class" or context == "struct"): + decl_str = decl_str[len("static"):].lstrip() + static_method = True + + args_begin = decl_str.find("(") + if decl_str.startswith("CVAPI"): + rtype_end = decl_str.find(")", args_begin+1) + if rtype_end < 0: + print("Error at %d. no terminating ) in CVAPI() macro: %s" % (self.lineno, decl_str)) + sys.exit(-1) + decl_str = decl_str[args_begin+1:rtype_end] + " " + decl_str[rtype_end+1:] + args_begin = decl_str.find("(") + if args_begin < 0: + print("Error at %d: no args in '%s'" % (self.lineno, decl_str)) + sys.exit(-1) + + decl_start = decl_str[:args_begin].strip() + # handle operator () case + if decl_start.endswith("operator"): + args_begin = decl_str.find("(", args_begin+1) + if args_begin < 0: + print("Error at %d: no args in '%s'" % (self.lineno, decl_str)) + sys.exit(-1) + decl_start = decl_str[:args_begin].strip() + # TODO: normalize all type of operators + if decl_start.endswith("()"): + decl_start = decl_start[0:-2].rstrip() + " ()" + + # constructor/destructor case + if bool(re.match(r'^(\w+::)*(?P\w+)::~?(?P=x)$', decl_start)): + decl_start = "void " + decl_start + + rettype, funcname, modlist, argno = self.parse_arg(decl_start, -1) + + # determine original return type, hack for return types with underscore + original_type = None + i = decl_start.rfind(funcname) + if i > 0: + original_type = decl_start[:i].replace("&", "").replace("const", "").strip() + + if argno >= 0: + classname = top[1] + if rettype == classname or rettype == "~" + classname: + rettype, funcname = "", rettype + else: + if bool(re.match('\w+\s+\(\*\w+\)\s*\(.*\)', decl_str)): + return [] # function typedef + elif bool(re.match('\w+\s+\(\w+::\*\w+\)\s*\(.*\)', decl_str)): + return [] # class method typedef + elif bool(re.match('[A-Z_]+', decl_start)): + return [] # it seems to be a macro instantiation + elif "__declspec" == decl_start: + return [] + elif bool(re.match(r'\w+\s+\(\*\w+\)\[\d+\]', decl_str)): + return [] # exotic - dynamic 2d array + else: + #print rettype, funcname, modlist, argno + print("Error at %s:%d the function/method name is missing: '%s'" % (self.hname, self.lineno, decl_start)) + sys.exit(-1) + + if self.wrap_mode and (("::" in funcname) or funcname.startswith("~")): + # if there is :: in function name (and this is in the header file), + # it means, this is inline implementation of a class method. + # Thus the function has been already declared within the class and we skip this repeated + # declaration. + # Also, skip the destructors, as they are always wrapped + return [] + + funcname = self.get_dotted_name(funcname) + + if not self.wrap_mode: + decl = self.parse_func_decl_no_wrap(decl_str, static_method) + decl[0] = funcname + return decl + + arg_start = args_begin+1 + npos = arg_start-1 + balance = 1 + angle_balance = 0 + # scan the argument list; handle nested parentheses + args_decls = [] + args = [] + argno = 1 + + while balance > 0: + npos += 1 + t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos) + if not t: + print("Error: no closing ')' at %d" % (self.lineno,)) + print(decl_str) + print(decl_str[arg_start:]) + sys.exit(-1) + if t == "<": + angle_balance += 1 + if t == ">": + angle_balance -= 1 + if t == "(": + balance += 1 + if t == ")": + balance -= 1 + + if (t == "," and balance == 1 and angle_balance == 0) or balance == 0: + # process next function argument + a = decl_str[arg_start:npos].strip() + #print "arg = ", a + arg_start = npos+1 + if a: + eqpos = a.find("=") + defval = "" + modlist = [] + if eqpos >= 0: + defval = a[eqpos+1:].strip() + else: + eqpos = a.find("CV_DEFAULT") + if eqpos >= 0: + defval, pos3 = self.get_macro_arg(a, eqpos) + else: + eqpos = a.find("CV_WRAP_DEFAULT") + if eqpos >= 0: + defval, pos3 = self.get_macro_arg(a, eqpos) + if defval == "NULL": + defval = "0" + if eqpos >= 0: + a = a[:eqpos].strip() + arg_type, arg_name, modlist, argno = self.parse_arg(a, argno) + if self.wrap_mode: + if arg_type == "InputArray": + arg_type = "Mat" + elif arg_type == "InputOutputArray": + arg_type = "Mat" + modlist.append("/IO") + elif arg_type == "OutputArray": + arg_type = "Mat" + modlist.append("/O") + elif arg_type == "InputArrayOfArrays": + arg_type = "vector_Mat" + elif arg_type == "InputOutputArrayOfArrays": + arg_type = "vector_Mat" + modlist.append("/IO") + elif arg_type == "OutputArrayOfArrays": + arg_type = "vector_Mat" + modlist.append("/O") + defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector"), + ("InputOutputArrayOfArrays", "vector"), + ("OutputArrayOfArrays", "vector"), + ("InputArray", "Mat"), + ("InputOutputArray", "Mat"), + ("OutputArray", "Mat"), + ("noArray", arg_type)]).strip() + args.append([arg_type, arg_name, defval, modlist]) + npos = arg_start-1 + + if static_method: + func_modlist.append("/S") + + if original_type is None: + return [funcname, rettype, func_modlist, args] + else: + return [funcname, rettype, func_modlist, args, original_type] + + def get_dotted_name(self, name): + """ + adds the dot-separated container class/namespace names to the bare function/class name, e.g. when we have + + namespace ~root_ns~ { + class A { + public: + f(int); + }; + } + + the function will convert "A" to "~root_ns~.A" and "f" to "~root_ns~.A.f". + """ + if not self.block_stack: + return name + if name.startswith(self.root_ns + "."): + return name + qualified_name = (("." in name) or ("::" in name)) + n = "" + for b in self.block_stack: + block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME] + if block_type in ["file", "enum"]: + continue + if block_type not in ["struct", "class", "namespace"]: + print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack)) + sys.exit(-1) + if block_name and (block_type == "namespace" or not qualified_name): + n += block_name + "." + n += name.replace("::", ".") + if self.root_ns == "cv" and n.endswith(".Algorithm"): + n = "cv.Algorithm" + return n + + def parse_stmt(self, stmt, end_token): + """ + parses the statement (ending with ';' or '}') or a block head (ending with '{') + + The function calls parse_class_decl or parse_func_decl when necessary. It returns + , , , + where the first 3 values only make sense for blocks (i.e. code blocks, namespaces, classes, enums and such) + """ + stack_top = self.block_stack[-1] + context = stack_top[self.BLOCK_TYPE] + + stmt_type = "" + if end_token == "{": + stmt_type = "block" + + if context == "block": + print("Error at %d: should not call parse_stmt inside blocks" % (self.lineno,)) + sys.exit(-1) + + if context == "class" or context == "struct": + while 1: + colon_pos = stmt.find(":") + if colon_pos < 0: + break + w = stmt[:colon_pos].strip() + if w in ["public", "protected", "private"]: + if w == "public" or (not self.wrap_mode and w == "protected"): + stack_top[self.PUBLIC_SECTION] = True + else: + stack_top[self.PUBLIC_SECTION] = False + stmt = stmt[colon_pos+1:].strip() + break + + # do not process hidden class members and template classes/functions + if not stack_top[self.PUBLIC_SECTION] or stmt.startswith("template"): + return stmt_type, "", False, None + + if end_token == "{": + if not self.wrap_mode and stmt.startswith("typedef struct"): + stmt_type = "struct" + try: + classname, bases, modlist = self.parse_class_decl(stmt[len("typedef "):]) + except: + print("Error at %s:%d" % (self.hname, self.lineno)) + exit(1) + if classname.startswith("_Ipl"): + classname = classname[1:] + decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []] + if bases: + decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases]) + return stmt_type, classname, True, decl + + if stmt.startswith("class") or stmt.startswith("struct"): + stmt_type = stmt.split()[0] + if stmt.strip() != stmt_type: + try: + classname, bases, modlist = self.parse_class_decl(stmt) + except: + print("Error at %s:%d" % (self.hname, self.lineno)) + exit(1) + decl = [] + if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" in stmt)): + decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []] + if bases: + decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases]) + return stmt_type, classname, True, decl + + if stmt.startswith("enum"): + return "enum", "", True, None + + if stmt.startswith("namespace"): + stmt_list = stmt.split() + if len(stmt_list) < 2: + stmt_list.append("") + return stmt_list[0], stmt_list[1], True, None + if stmt.startswith("extern") and "\"C\"" in stmt: + return "namespace", "", True, None + + if end_token == "}" and context == "enum": + decl = self.parse_enum(stmt) + return "enum", "", False, decl + + if end_token == ";" and stmt.startswith("typedef"): + # TODO: handle typedef's more intelligently + return stmt_type, "", False, None + + paren_pos = stmt.find("(") + if paren_pos >= 0: + # assume it's function or method declaration, + # since we filtered off the other places where '(' can normally occur: + # - code blocks + # - function pointer typedef's + decl = self.parse_func_decl(stmt) + # we return parse_flag == False to prevent the parser to look inside function/method bodies + # (except for tracking the nested blocks) + return stmt_type, "", False, decl + + if (context == "struct" or context == "class") and end_token == ";" and stmt: + # looks like it's member declaration; append the members to the class declaration + class_decl = stack_top[self.CLASS_DECL] + if ("CV_PROP" in stmt): # or (class_decl and ("/Map" in class_decl[2])): + var_modlist = [] + if "CV_PROP_RW" in stmt: + var_modlist.append("/RW") + stmt = self.batch_replace(stmt, [("CV_PROP_RW", ""), ("CV_PROP", "")]).strip() + var_list = stmt.split(",") + var_type, var_name1, modlist, argno = self.parse_arg(var_list[0], -1) + var_list = [var_name1] + [i.strip() for i in var_list[1:]] + + for v in var_list: + class_decl[3].append([var_type, v, "", var_modlist]) + return stmt_type, "", False, None + + # something unknown + return stmt_type, "", False, None + + def find_next_token(self, s, tlist, p=0): + """ + Finds the next token from the 'tlist' in the input 's', starting from position 'p'. + Returns the first occured token and its position, or ("", len(s)) when no token is found + """ + token = "" + tpos = len(s) + for t in tlist: + pos = s.find(t, p) + if pos < 0: + continue + if pos < tpos: + tpos = pos + token = t + return token, tpos + + def parse(self, hname, wmode=True): + """ + The main method. Parses the input file. + Returns the list of declarations (that can be print using print_decls) + """ + self.hname = hname + decls = [] + f = io.open(hname, 'rt', encoding='utf-8') + linelist = list(f.readlines()) + f.close() + + # states: + SCAN = 0 # outside of a comment or preprocessor directive + COMMENT = 1 # inside a multi-line comment + DIRECTIVE = 2 # inside a multi-line preprocessor directive + + state = SCAN + + self.block_stack = [["file", hname, True, True, None]] + block_head = "" + self.lineno = 0 + self.wrap_mode = wmode + + for l0 in linelist: + self.lineno += 1 + #print self.lineno + + l = l0.strip() + + if state == SCAN and l.startswith("#"): + state = DIRECTIVE + # fall through to the if state == DIRECTIVE check + + if state == DIRECTIVE: + if not l.endswith("\\"): + state = SCAN + continue + + if state == COMMENT: + pos = l.find("*/") + if pos < 0: + continue + l = l[pos+2:] + state = SCAN + + if state != SCAN: + print("Error at %d: invlid state = %d" % (self.lineno, state)) + sys.exit(-1) + + while 1: + token, pos = self.find_next_token(l, [";", "\"", "{", "}", "//", "/*"]) + + if not token: + block_head += " " + l + break + + if token == "//": + block_head += " " + l[:pos] + break + + if token == "/*": + block_head += " " + l[:pos] + pos = l.find("*/", pos+2) + if pos < 0: + state = COMMENT + break + l = l[pos+2:] + continue + + if token == "\"": + pos2 = pos + 1 + while 1: + t2, pos2 = self.find_next_token(l, ["\\", "\""], pos2) + if t2 == "": + print("Error at %d: no terminating '\"'" % (self.lineno,)) + sys.exit(-1) + if t2 == "\"": + break + pos2 += 2 + + block_head += " " + l[:pos2+1] + l = l[pos2+1:] + continue + + stmt = (block_head + " " + l[:pos]).strip() + stmt = " ".join(stmt.split()) # normalize the statement + stack_top = self.block_stack[-1] + + if stmt.startswith("@"): + # Objective C ? + break + + decl = None + if stack_top[self.PROCESS_FLAG]: + # even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement, + # since it can start with "public:" + stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token) + if decl: + if stmt_type == "enum": + for d in decl: + decls.append(d) + else: + decls.append(decl) + if stmt_type == "namespace": + chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name] + self.namespaces.add('.'.join(chunks)) + else: + stmt_type, name, parse_flag = "block", "", False + + if token == "{": + if stmt_type == "class": + public_section = False + else: + public_section = True + self.block_stack.append([stmt_type, name, parse_flag, public_section, decl]) + + if token == "}": + if not self.block_stack: + print("Error at %d: the block stack is empty" % (self.lineno,)) + self.block_stack[-1:] = [] + if pos+1 < len(l) and l[pos+1] == ';': + pos += 1 + + block_head = "" + l = l[pos+1:] + + return decls + + def print_decls(self, decls): + """ + Prints the list of declarations, retrieived by the parse() method + """ + for d in decls: + print(d[0], d[1], ";".join(d[2])) + for a in d[3]: + print(" ", a[0], a[1], a[2], end="") + if a[3]: + print("; ".join(a[3])) + else: + print() diff --git a/cmake/src/init_func.cpp.in b/cmake/src/init_func.cpp.in new file mode 100644 index 0000000..9e2d03c --- /dev/null +++ b/cmake/src/init_func.cpp.in @@ -0,0 +1,34 @@ +static PyMethodDef module_methods[] = { + {NULL, NULL} +}; + +static struct PyModuleDef moduledef = +{ + PyModuleDef_HEAD_INIT, + "@ARG_NAMESPACE@", + "Python bindings", + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + module_methods +}; + +extern "C" PyMODINIT_FUNC +PyInit_@ARG_MODULE_NAME@() +{ + // imports numpy convertors (or something like that): + // http://stackoverflow.com/questions/12957492/ + // writing-python-bindings-for-c-code-that-use-opencv + import_array(); + + // Create empty module +#if PY_MAJOR_VERSION >= 3 + PyObject* m = PyModule_Create(&moduledef); +#else + PyObject* m = Py_InitModule("@ARG_NAMESPACE@", module_methods); +#endif + + // Fill with items from this module +#include "pyopencv_generated_type_reg.h" + init_submodules(m); // from "pyopencv_generated_ns_reg.h" + return m; +} diff --git a/cmake/src/module.cpp b/cmake/src/module.cpp new file mode 100644 index 0000000..1ba3984 --- /dev/null +++ b/cmake/src/module.cpp @@ -0,0 +1,135 @@ +/* +The content of this file has been taken from the OpenCV project at +https:github.com/Itseez/opencv +It has been modified to make it more reusable for generating binding on other +projects. +modifications: Nicolas Granger + + +For your information, the original License is given below: + +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2016, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +Third party copyrights are property of their respective owners. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. +*/ + +#include +#include "pycompat.hpp" + +#define MODULESTR "cv2" +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + +#include "pyopencv_generated_include.h" + +#include "opencv2/opencv_modules.hpp" + +#include "utils.hpp" +#include "py_cv_converters.hpp" + + +using namespace cv; + +#include "pyopencv_generated_types.h" +#include "pyopencv_generated_funcs.h" + + +/************************************************************************/ +/* Module init */ + +struct ConstDef +{ + const char * name; + long val; +}; + +static void init_submodule(PyObject * root, const char * name, + PyMethodDef * methods, ConstDef * consts) +{ + // traverse and create nested submodules + std::string s = name; + size_t i = s.find('.'); + while (i < s.length() && i != std::string::npos) + { + size_t j = s.find('.', i); + if (j == std::string::npos) + j = s.length(); + std::string short_name = s.substr(i, j-i); + std::string full_name = s.substr(0, j); + i = j+1; + + PyObject * d = PyModule_GetDict(root); + PyObject * submod = PyDict_GetItemString(d, short_name.c_str()); + if (submod == NULL) + { + submod = PyImport_AddModule(full_name.c_str()); + PyDict_SetItemString(d, short_name.c_str(), submod); + } + + if (short_name != "") + root = submod; + } + + // populate module's dict + PyObject * d = PyModule_GetDict(root); + for (PyMethodDef * m = methods; m->ml_name != NULL; ++m) + { + PyObject * method_obj = PyCFunction_NewEx(m, NULL, NULL); + PyDict_SetItemString(d, m->ml_name, method_obj); + Py_DECREF(method_obj); + } + for (ConstDef * c = consts; c->name != NULL; ++c) + { + PyDict_SetItemString(d, c->name, PyInt_FromLong(c->val)); + } + +} + +#include "pyopencv_generated_ns_reg.h" + +static int to_ok(PyTypeObject *to) +{ + to->tp_alloc = PyType_GenericAlloc; + to->tp_new = PyType_GenericNew; + to->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; + return (PyType_Ready(to) == 0); +} + +#include "init_func.cpp" diff --git a/cmake/src/py_cv_converters.hpp b/cmake/src/py_cv_converters.hpp new file mode 100644 index 0000000..0e08d3d --- /dev/null +++ b/cmake/src/py_cv_converters.hpp @@ -0,0 +1,1130 @@ +/* +The content of this file has been taken from the OpenCV project at +https:github.com/Itseez/opencv +It has been modified to make it more reusable for generating binding on other +projects. +modifications: Nicolas Granger + +For your information, the original License is given below: + +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2016, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +Third party copyrights are property of their respective owners. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. +*/ + +#ifndef __PY_CV_CONVERTERS_HPP__ +#define __PY_CV_CONVERTERS_HPP__ + + +#include + +using namespace cv; + +static PyObject* failmsgp(const char *fmt, ...) +{ + char str[1000]; + + va_list ap; + va_start(ap, fmt); + vsnprintf(str, sizeof(str), fmt, ap); + va_end(ap); + + PyErr_SetString(PyExc_TypeError, str); + return 0; +} + +struct ArgInfo +{ + const char * name; + bool outputarg; + // more fields may be added if necessary + + ArgInfo(const char * name_, bool outputarg_) + : name(name_) + , outputarg(outputarg_) {} + + // to match with older pyopencv_to function signature + operator const char *() const { return name; } +}; + + +class NumpyAllocator : public MatAllocator +{ +public: + NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); } + ~NumpyAllocator() {} + + UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const + { + UMatData* u = new UMatData(this); + u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o); + npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o); + for( int i = 0; i < dims - 1; i++ ) + step[i] = (size_t)_strides[i]; + step[dims-1] = CV_ELEM_SIZE(type); + u->size = sizes[0]*step[0]; + u->userdata = o; + return u; + } + + UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const + { + if( data != 0 ) + { + CV_Error(Error::StsAssert, "The data should normally be NULL!"); + // probably this is safe to do in such extreme case + return stdAllocator->allocate(dims0, sizes, type, data, step, flags, usageFlags); + } + PyEnsureGIL gil; + + int depth = CV_MAT_DEPTH(type); + int cn = CV_MAT_CN(type); + const int f = (int)(sizeof(size_t)/8); + int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE : + depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT : + depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT : + depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT; + int i, dims = dims0; + cv::AutoBuffer _sizes(dims + 1); + for( i = 0; i < dims; i++ ) + _sizes[i] = sizes[i]; + if( cn > 1 ) + _sizes[dims++] = cn; + PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum); + if(!o) + CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims)); + return allocate(o, dims0, sizes, type, step); + } + + bool allocate(UMatData* u, int accessFlags, UMatUsageFlags usageFlags) const + { + return stdAllocator->allocate(u, accessFlags, usageFlags); + } + + void deallocate(UMatData* u) const + { + if(!u) + return; + PyEnsureGIL gil; + CV_Assert(u->urefcount >= 0); + CV_Assert(u->refcount >= 0); + if(u->refcount == 0) + { + PyObject* o = (PyObject*)u->userdata; + Py_XDECREF(o); + delete u; + } + } + + const MatAllocator* stdAllocator; +}; + +NumpyAllocator g_numpyAllocator; + + +template static +bool pyopencv_to(PyObject* obj, T& p, const char* name = ""); + +template static +PyObject* pyopencv_from(const T& src); + +enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 }; + +// special case, when the convertor needs full ArgInfo structure +static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info) +{ + bool allowND = true; + if(!o || o == Py_None) + { + if( !m.data ) + m.allocator = &g_numpyAllocator; + return true; + } + + if( PyInt_Check(o) ) + { + double v[] = {static_cast(PyInt_AsLong((PyObject*)o)), 0., 0., 0.}; + m = Mat(4, 1, CV_64F, v).clone(); + return true; + } + if( PyFloat_Check(o) ) + { + double v[] = {PyFloat_AsDouble((PyObject*)o), 0., 0., 0.}; + m = Mat(4, 1, CV_64F, v).clone(); + return true; + } + if( PyTuple_Check(o) ) + { + int i, sz = (int)PyTuple_Size((PyObject*)o); + m = Mat(sz, 1, CV_64F); + for( i = 0; i < sz; i++ ) + { + PyObject* oi = PyTuple_GET_ITEM(o, i); + if( PyInt_Check(oi) ) + m.at(i) = (double)PyInt_AsLong(oi); + else if( PyFloat_Check(oi) ) + m.at(i) = (double)PyFloat_AsDouble(oi); + else + { + failmsg("%s is not a numerical tuple", info.name); + m.release(); + return false; + } + } + return true; + } + + if( !PyArray_Check(o) ) + { + failmsg("%s is not a numpy array, neither a scalar", info.name); + return false; + } + + PyArrayObject* oarr = (PyArrayObject*) o; + + bool needcopy = false, needcast = false; + int typenum = PyArray_TYPE(oarr), new_typenum = typenum; + int type = typenum == NPY_UBYTE ? CV_8U : + typenum == NPY_BYTE ? CV_8S : + typenum == NPY_USHORT ? CV_16U : + typenum == NPY_SHORT ? CV_16S : + typenum == NPY_INT ? CV_32S : + typenum == NPY_INT32 ? CV_32S : + typenum == NPY_FLOAT ? CV_32F : + typenum == NPY_DOUBLE ? CV_64F : -1; + + if( type < 0 ) + { + if( typenum == NPY_INT64 || typenum == NPY_UINT64 || typenum == NPY_LONG ) + { + needcopy = needcast = true; + new_typenum = NPY_INT; + type = CV_32S; + } + else + { + failmsg("%s data type = %d is not supported", info.name, typenum); + return false; + } + } + +#ifndef CV_MAX_DIM + const int CV_MAX_DIM = 32; +#endif + + int ndims = PyArray_NDIM(oarr); + if(ndims >= CV_MAX_DIM) + { + failmsg("%s dimensionality (=%d) is too high", info.name, ndims); + return false; + } + + int size[CV_MAX_DIM+1]; + size_t step[CV_MAX_DIM+1]; + size_t elemsize = CV_ELEM_SIZE1(type); + const npy_intp* _sizes = PyArray_DIMS(oarr); + const npy_intp* _strides = PyArray_STRIDES(oarr); + bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX; + + for( int i = ndims-1; i >= 0 && !needcopy; i-- ) + { + // these checks handle cases of + // a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases + // b) transposed arrays, where _strides[] elements go in non-descending order + // c) flipped arrays, where some of _strides[] elements are negative + // the _sizes[i] > 1 is needed to avoid spurious copies when NPY_RELAXED_STRIDES is set + if( (i == ndims-1 && _sizes[i] > 1 && (size_t)_strides[i] != elemsize) || + (i < ndims-1 && _sizes[i] > 1 && _strides[i] < _strides[i+1]) ) + needcopy = true; + } + + if( ismultichannel && _strides[1] != (npy_intp)elemsize*_sizes[2] ) + needcopy = true; + + if (needcopy) + { + if (info.outputarg) + { + failmsg("Layout of the output array %s is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)", info.name); + return false; + } + + if( needcast ) { + o = PyArray_Cast(oarr, new_typenum); + oarr = (PyArrayObject*) o; + } + else { + oarr = PyArray_GETCONTIGUOUS(oarr); + o = (PyObject*) oarr; + } + + _strides = PyArray_STRIDES(oarr); + } + + // Normalize strides in case NPY_RELAXED_STRIDES is set + size_t default_step = elemsize; + for ( int i = ndims - 1; i >= 0; --i ) + { + size[i] = (int)_sizes[i]; + if ( size[i] > 1 ) + { + step[i] = (size_t)_strides[i]; + default_step = step[i] * size[i]; + } + else + { + step[i] = default_step; + default_step *= size[i]; + } + } + + // handle degenerate case + if( ndims == 0) { + size[ndims] = 1; + step[ndims] = elemsize; + ndims++; + } + + if( ismultichannel ) + { + ndims--; + type |= CV_MAKETYPE(0, size[2]); + } + + if( ndims > 2 && !allowND ) + { + failmsg("%s has more than 2 dimensions", info.name); + return false; + } + + m = Mat(ndims, size, type, PyArray_DATA(oarr), step); + m.u = g_numpyAllocator.allocate(o, ndims, size, type, step); + m.addref(); + + if( !needcopy ) + { + Py_INCREF(o); + } + m.allocator = &g_numpyAllocator; + + return true; +} + +template<> +bool pyopencv_to(PyObject* o, Mat& m, const char* name) +{ + return pyopencv_to(o, m, ArgInfo(name, 0)); +} + +template<> +PyObject* pyopencv_from(const Mat& m) +{ + if( !m.data ) + Py_RETURN_NONE; + Mat temp, *p = (Mat*)&m; + if(!p->u || p->allocator != &g_numpyAllocator) + { + temp.allocator = &g_numpyAllocator; + ERRWRAP2(m.copyTo(temp)); + p = &temp; + } + PyObject* o = (PyObject*)p->u->userdata; + Py_INCREF(o); + return o; +} + +template<> +bool pyopencv_to(PyObject *o, Scalar& s, const char *name) +{ + if(!o || o == Py_None) + return true; + if (PySequence_Check(o)) { + PyObject *fi = PySequence_Fast(o, name); + if (fi == NULL) + return false; + if (4 < PySequence_Fast_GET_SIZE(fi)) + { + failmsg("Scalar value for argument '%s' is longer than 4", name); + return false; + } + for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(fi); i++) { + PyObject *item = PySequence_Fast_GET_ITEM(fi, i); + if (PyFloat_Check(item) || PyInt_Check(item)) { + s[(int)i] = PyFloat_AsDouble(item); + } else { + failmsg("Scalar value for argument '%s' is not numeric", name); + return false; + } + } + Py_DECREF(fi); + } else { + if (PyFloat_Check(o) || PyInt_Check(o)) { + s[0] = PyFloat_AsDouble(o); + } else { + failmsg("Scalar value for argument '%s' is not numeric", name); + return false; + } + } + return true; +} + +template<> +PyObject* pyopencv_from(const Scalar& src) +{ + return Py_BuildValue("(dddd)", src[0], src[1], src[2], src[3]); +} + +template<> +PyObject* pyopencv_from(const bool& value) +{ + return PyBool_FromLong(value); +} + +#ifdef HAVE_OPENCV_STITCHING +template<> +PyObject* pyopencv_from(const Status& value) +{ + return PyInt_FromLong(value); +} +#endif + +template<> +bool pyopencv_to(PyObject* obj, bool& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + int _val = PyObject_IsTrue(obj); + if(_val < 0) + return false; + value = _val > 0; + return true; +} + +template<> +PyObject* pyopencv_from(const size_t& value) +{ + return PyLong_FromSize_t(value); +} + +template<> +bool pyopencv_to(PyObject* obj, size_t& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + value = (int)PyLong_AsUnsignedLong(obj); + return value != (size_t)-1 || !PyErr_Occurred(); +} + +template<> +PyObject* pyopencv_from(const int& value) +{ + return PyInt_FromLong(value); +} + +#ifdef HAVE_OPENCV_FLANN +template<> +PyObject* pyopencv_from(const cvflann_flann_algorithm_t& value) +{ + return PyInt_FromLong(int(value)); +} + +template<> +PyObject* pyopencv_from(const cvflann_flann_distance_t& value) +{ + return PyInt_FromLong(int(value)); +} +#endif + +template<> +bool pyopencv_to(PyObject* obj, int& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(PyInt_Check(obj)) + value = (int)PyInt_AsLong(obj); + else if(PyLong_Check(obj)) + value = (int)PyLong_AsLong(obj); + else + return false; + return value != -1 || !PyErr_Occurred(); +} + +template<> +PyObject* pyopencv_from(const uchar& value) +{ + return PyInt_FromLong(value); +} + +template<> +bool pyopencv_to(PyObject* obj, uchar& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + int ivalue = (int)PyInt_AsLong(obj); + value = cv::saturate_cast(ivalue); + return ivalue != -1 || !PyErr_Occurred(); +} + +template<> +PyObject* pyopencv_from(const double& value) +{ + return PyFloat_FromDouble(value); +} + +template<> +bool pyopencv_to(PyObject* obj, double& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(!!PyInt_CheckExact(obj)) + value = (double)PyInt_AS_LONG(obj); + else + value = PyFloat_AsDouble(obj); + return !PyErr_Occurred(); +} + +template<> +PyObject* pyopencv_from(const float& value) +{ + return PyFloat_FromDouble(value); +} + +template<> +bool pyopencv_to(PyObject* obj, float& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(!!PyInt_CheckExact(obj)) + value = (float)PyInt_AS_LONG(obj); + else + value = (float)PyFloat_AsDouble(obj); + return !PyErr_Occurred(); +} + +template<> +PyObject* pyopencv_from(const int64& value) +{ + return PyLong_FromLongLong(value); +} + +template<> +PyObject* pyopencv_from(const String& value) +{ + return PyString_FromString(value.empty() ? "" : value.c_str()); +} + +template<> +bool pyopencv_to(PyObject* obj, String& value, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + char* str = PyString_AsString(obj); + if(!str) + return false; + value = String(str); + return true; +} + +template<> +bool pyopencv_to(PyObject* obj, Size& sz, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + return PyArg_ParseTuple(obj, "ii", &sz.width, &sz.height) > 0; +} + +template<> +PyObject* pyopencv_from(const Size& sz) +{ + return Py_BuildValue("(ii)", sz.width, sz.height); +} + +template<> +bool pyopencv_to(PyObject* obj, Rect& r, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + return PyArg_ParseTuple(obj, "iiii", &r.x, &r.y, &r.width, &r.height) > 0; +} + +template<> +PyObject* pyopencv_from(const Rect& r) +{ + return Py_BuildValue("(iiii)", r.x, r.y, r.width, r.height); +} + +template<> +bool pyopencv_to(PyObject* obj, Rect2d& r, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + return PyArg_ParseTuple(obj, "dddd", &r.x, &r.y, &r.width, &r.height) > 0; +} + +template<> +PyObject* pyopencv_from(const Rect2d& r) +{ + return Py_BuildValue("(dddd)", r.x, r.y, r.width, r.height); +} + +template<> +bool pyopencv_to(PyObject* obj, Range& r, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(PyObject_Size(obj) == 0) + { + r = Range::all(); + return true; + } + return PyArg_ParseTuple(obj, "ii", &r.start, &r.end) > 0; +} + +template<> +PyObject* pyopencv_from(const Range& r) +{ + return Py_BuildValue("(ii)", r.start, r.end); +} + +template<> +bool pyopencv_to(PyObject* obj, Point& p, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(!!PyComplex_CheckExact(obj)) + { + Py_complex c = PyComplex_AsCComplex(obj); + p.x = saturate_cast(c.real); + p.y = saturate_cast(c.imag); + return true; + } + return PyArg_ParseTuple(obj, "ii", &p.x, &p.y) > 0; +} + +template<> +bool pyopencv_to(PyObject* obj, Point2f& p, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(!!PyComplex_CheckExact(obj)) + { + Py_complex c = PyComplex_AsCComplex(obj); + p.x = saturate_cast(c.real); + p.y = saturate_cast(c.imag); + return true; + } + return PyArg_ParseTuple(obj, "ff", &p.x, &p.y) > 0; +} + +template<> +bool pyopencv_to(PyObject* obj, Point2d& p, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(!!PyComplex_CheckExact(obj)) + { + Py_complex c = PyComplex_AsCComplex(obj); + p.x = saturate_cast(c.real); + p.y = saturate_cast(c.imag); + return true; + } + return PyArg_ParseTuple(obj, "dd", &p.x, &p.y) > 0; +} + +template<> +bool pyopencv_to(PyObject* obj, Point3f& p, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + return PyArg_ParseTuple(obj, "fff", &p.x, &p.y, &p.z) > 0; +} + +template<> +bool pyopencv_to(PyObject* obj, Point3d& p, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + return PyArg_ParseTuple(obj, "ddd", &p.x, &p.y, &p.z) > 0; +} + +template<> +PyObject* pyopencv_from(const Point& p) +{ + return Py_BuildValue("(ii)", p.x, p.y); +} + +template<> +PyObject* pyopencv_from(const Point2f& p) +{ + return Py_BuildValue("(dd)", p.x, p.y); +} + +template<> +PyObject* pyopencv_from(const Point3f& p) +{ + return Py_BuildValue("(ddd)", p.x, p.y, p.z); +} + +template<> +bool pyopencv_to(PyObject* obj, Vec3d& v, const char* name) +{ + (void)name; + if(!obj) + return true; + return PyArg_ParseTuple(obj, "ddd", &v[0], &v[1], &v[2]) > 0; +} + +template<> +PyObject* pyopencv_from(const Vec3d& v) +{ + return Py_BuildValue("(ddd)", v[0], v[1], v[2]); +} + +template<> +PyObject* pyopencv_from(const Vec2d& v) +{ + return Py_BuildValue("(dd)", v[0], v[1]); +} + +template<> +PyObject* pyopencv_from(const Point2d& p) +{ + return Py_BuildValue("(dd)", p.x, p.y); +} + +template<> +PyObject* pyopencv_from(const Point3d& p) +{ + return Py_BuildValue("(ddd)", p.x, p.y, p.y); +} + +template struct pyopencvVecConverter +{ + static bool to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo info) + { + typedef typename DataType<_Tp>::channel_type _Cp; + if(!obj || obj == Py_None) + return true; + if (PyArray_Check(obj)) + { + Mat m; + pyopencv_to(obj, m, info); + m.copyTo(value); + } + if (!PySequence_Check(obj)) + return false; + PyObject *seq = PySequence_Fast(obj, info.name); + if (seq == NULL) + return false; + int i, j, n = (int)PySequence_Fast_GET_SIZE(seq); + value.resize(n); + + int type = DataType<_Tp>::type; + int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type); + PyObject** items = PySequence_Fast_ITEMS(seq); + + for( i = 0; i < n; i++ ) + { + PyObject* item = items[i]; + PyObject* seq_i = 0; + PyObject** items_i = &item; + _Cp* data = (_Cp*)&value[i]; + + if( channels == 2 && PyComplex_CheckExact(item) ) + { + Py_complex c = PyComplex_AsCComplex(obj); + data[0] = saturate_cast<_Cp>(c.real); + data[1] = saturate_cast<_Cp>(c.imag); + continue; + } + if( channels > 1 ) + { + if( PyArray_Check(item)) + { + Mat src; + pyopencv_to(item, src, info); + if( src.dims != 2 || src.channels() != 1 || + ((src.cols != 1 || src.rows != channels) && + (src.cols != channels || src.rows != 1))) + break; + Mat dst(src.rows, src.cols, depth, data); + src.convertTo(dst, type); + if( dst.data != (uchar*)data ) + break; + continue; + } + + seq_i = PySequence_Fast(item, info.name); + if( !seq_i || (int)PySequence_Fast_GET_SIZE(seq_i) != channels ) + { + Py_XDECREF(seq_i); + break; + } + items_i = PySequence_Fast_ITEMS(seq_i); + } + + for( j = 0; j < channels; j++ ) + { + PyObject* item_ij = items_i[j]; + if( PyInt_Check(item_ij)) + { + int v = (int)PyInt_AsLong(item_ij); + if( v == -1 && PyErr_Occurred() ) + break; + data[j] = saturate_cast<_Cp>(v); + } + else if( PyLong_Check(item_ij)) + { + int v = (int)PyLong_AsLong(item_ij); + if( v == -1 && PyErr_Occurred() ) + break; + data[j] = saturate_cast<_Cp>(v); + } + else if( PyFloat_Check(item_ij)) + { + double v = PyFloat_AsDouble(item_ij); + if( PyErr_Occurred() ) + break; + data[j] = saturate_cast<_Cp>(v); + } + else + break; + } + Py_XDECREF(seq_i); + if( j < channels ) + break; + } + Py_DECREF(seq); + return i == n; + } + + static PyObject* from(const std::vector<_Tp>& value) + { + if(value.empty()) + return PyTuple_New(0); + Mat src((int)value.size(), DataType<_Tp>::channels, DataType<_Tp>::depth, (uchar*)&value[0]); + return pyopencv_from(src); + } +}; + +template +bool pyopencv_to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo info) +{ + return pyopencvVecConverter<_Tp>::to(obj, value, info); +} + +template +PyObject* pyopencv_from(const std::vector<_Tp>& value) +{ + return pyopencvVecConverter<_Tp>::from(value); +} + +template static inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<_Tp>& value, const ArgInfo info) +{ + if(!obj || obj == Py_None) + return true; + if (!PySequence_Check(obj)) + return false; + PyObject *seq = PySequence_Fast(obj, info.name); + if (seq == NULL) + return false; + int i, n = (int)PySequence_Fast_GET_SIZE(seq); + value.resize(n); + + PyObject** items = PySequence_Fast_ITEMS(seq); + + for( i = 0; i < n; i++ ) + { + PyObject* item = items[i]; + if(!pyopencv_to(item, value[i], info)) + break; + } + Py_DECREF(seq); + return i == n; +} + +template static inline PyObject* pyopencv_from_generic_vec(const std::vector<_Tp>& value) +{ + int i, n = (int)value.size(); + PyObject* seq = PyList_New(n); + for( i = 0; i < n; i++ ) + { + PyObject* item = pyopencv_from(value[i]); + if(!item) + break; + PyList_SET_ITEM(seq, i, item); + } + if( i < n ) + { + Py_DECREF(seq); + return 0; + } + return seq; +} + + +template struct pyopencvVecConverter > +{ + static bool to(PyObject* obj, std::vector >& value, const ArgInfo info) + { + return pyopencv_to_generic_vec(obj, value, info); + } + + static PyObject* from(const std::vector >& value) + { + return pyopencv_from_generic_vec(value); + } +}; + +template<> struct pyopencvVecConverter +{ + static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + { + return pyopencv_to_generic_vec(obj, value, info); + } + + static PyObject* from(const std::vector& value) + { + return pyopencv_from_generic_vec(value); + } +}; + +template<> struct pyopencvVecConverter +{ + static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + { + return pyopencv_to_generic_vec(obj, value, info); + } + + static PyObject* from(const std::vector& value) + { + return pyopencv_from_generic_vec(value); + } +}; + +template<> struct pyopencvVecConverter +{ + static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + { + return pyopencv_to_generic_vec(obj, value, info); + } + + static PyObject* from(const std::vector& value) + { + return pyopencv_from_generic_vec(value); + } +}; + +template<> struct pyopencvVecConverter +{ + static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + { + return pyopencv_to_generic_vec(obj, value, info); + } + + static PyObject* from(const std::vector& value) + { + return pyopencv_from_generic_vec(value); + } +}; + +template<> +bool pyopencv_to(PyObject *obj, TermCriteria& dst, const char *name) +{ + (void)name; + if(!obj) + return true; + return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.maxCount, &dst.epsilon) > 0; +} + +template<> +PyObject* pyopencv_from(const TermCriteria& src) +{ + return Py_BuildValue("(iid)", src.type, src.maxCount, src.epsilon); +} + +template<> +bool pyopencv_to(PyObject *obj, RotatedRect& dst, const char *name) +{ + (void)name; + if(!obj) + return true; + return PyArg_ParseTuple(obj, "(ff)(ff)f", &dst.center.x, &dst.center.y, &dst.size.width, &dst.size.height, &dst.angle) > 0; +} + +template<> +PyObject* pyopencv_from(const RotatedRect& src) +{ + return Py_BuildValue("((ff)(ff)f)", src.center.x, src.center.y, src.size.width, src.size.height, src.angle); +} + +template<> +PyObject* pyopencv_from(const Moments& m) +{ + return Py_BuildValue("{s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d}", + "m00", m.m00, "m10", m.m10, "m01", m.m01, + "m20", m.m20, "m11", m.m11, "m02", m.m02, + "m30", m.m30, "m21", m.m21, "m12", m.m12, "m03", m.m03, + "mu20", m.mu20, "mu11", m.mu11, "mu02", m.mu02, + "mu30", m.mu30, "mu21", m.mu21, "mu12", m.mu12, "mu03", m.mu03, + "nu20", m.nu20, "nu11", m.nu11, "nu02", m.nu02, + "nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03); +} + +#ifdef HAVE_OPENCV_FLANN +template<> +bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) +{ + (void)name; + bool ok = true; + PyObject* key = NULL; + PyObject* item = NULL; + Py_ssize_t pos = 0; + + if(PyDict_Check(o)) { + while(PyDict_Next(o, &pos, &key, &item)) { + if( !PyString_Check(key) ) { + ok = false; + break; + } + + String k = PyString_AsString(key); + if( PyString_Check(item) ) + { + const char* value = PyString_AsString(item); + p.setString(k, value); + } + else if( !!PyBool_Check(item) ) + p.setBool(k, item == Py_True); + else if( PyInt_Check(item) ) + { + int value = (int)PyInt_AsLong(item); + if( strcmp(k.c_str(), "algorithm") == 0 ) + p.setAlgorithm(value); + else + p.setInt(k, value); + } + else if( PyFloat_Check(item) ) + { + double value = PyFloat_AsDouble(item); + p.setDouble(k, value); + } + else + { + ok = false; + break; + } + } + } + + return ok && !PyErr_Occurred(); +} + +template<> +bool pyopencv_to(PyObject* obj, cv::flann::SearchParams & value, const char * name) +{ + return pyopencv_to(obj, value, name); +} +#endif + +template +bool pyopencv_to(PyObject *o, Ptr& p, const char *name) +{ + p = makePtr(); + return pyopencv_to(o, *p, name); +} + +#ifdef HAVE_OPENCV_FLANN +template<> +bool pyopencv_to(PyObject *o, cvflann::flann_distance_t& dist, const char *name) +{ + int d = (int)dist; + bool ok = pyopencv_to(o, d, name); + dist = (cvflann::flann_distance_t)d; + return ok; +} +#endif + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// TODO: REMOVE used only by ml wrapper + +template<> +bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const char *name) +{ + (void)name; + if(!obj) + return true; + return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.max_iter, &dst.epsilon) > 0; +} + +template<> +bool pyopencv_to(PyObject* obj, CvSlice& r, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(PyObject_Size(obj) == 0) + { + r = CV_WHOLE_SEQ; + return true; + } + return PyArg_ParseTuple(obj, "ii", &r.start_index, &r.end_index) > 0; +} + + +#endif // __PY_CV_CONVERTERS_HPP__ diff --git a/cmake/src/pycompat.hpp b/cmake/src/pycompat.hpp new file mode 100644 index 0000000..1838908 --- /dev/null +++ b/cmake/src/pycompat.hpp @@ -0,0 +1,75 @@ +/* +This file has been taken from the OpenCV project at https:github.com/Itseez/opencv +For your information, the original License is given below: +*/ + +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +// Defines for Python 2/3 compatibility. +#ifndef __PYCOMPAT_HPP__ +#define __PYCOMPAT_HPP__ + +#if PY_MAJOR_VERSION >= 3 +// Python3 treats all ints as longs, PyInt_X functions have been removed. +#define PyInt_Check PyLong_Check +#define PyInt_CheckExact PyLong_CheckExact +#define PyInt_AsLong PyLong_AsLong +#define PyInt_AS_LONG PyLong_AS_LONG +#define PyInt_FromLong PyLong_FromLong +#define PyNumber_Int PyNumber_Long + +// Python3 strings are unicode, these defines mimic the Python2 functionality. +#define PyString_Check PyUnicode_Check +#define PyString_FromString PyUnicode_FromString +#define PyString_FromStringAndSize PyUnicode_FromStringAndSize +#define PyString_Size PyUnicode_GET_SIZE + +// PyUnicode_AsUTF8 isn't available until Python 3.3 +#if (PY_VERSION_HEX < 0x03030000) +#define PyString_AsString _PyUnicode_AsString +#else +#define PyString_AsString PyUnicode_AsUTF8 +#endif +#endif + +#endif // END HEADER GUARD diff --git a/cmake/src/utils.hpp b/cmake/src/utils.hpp new file mode 100644 index 0000000..15e1606 --- /dev/null +++ b/cmake/src/utils.hpp @@ -0,0 +1,164 @@ +/* +The content of this file has been taken from the OpenCV project at +https:github.com/Itseez/opencv +It has been modified to make it more reusable for generating binding +on other projects. +modifications: Nicolas Granger + +For your information, the original License is given below: + +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2016, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +Third party copyrights are property of their respective owners. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. +*/ + + +#ifndef __UTILS_HPP__ +#define __UTILS_HPP__ + + +#include "opencv2/core/types_c.h" + + +using namespace cv; + + +typedef std::vector vector_uchar; +typedef std::vector vector_char; +typedef std::vector vector_int; +typedef std::vector vector_float; +typedef std::vector vector_double; +typedef std::vector vector_Point; +typedef std::vector vector_Point2f; +typedef std::vector vector_Point3f; +typedef std::vector vector_Vec2f; +typedef std::vector vector_Vec3f; +typedef std::vector vector_Vec4f; +typedef std::vector vector_Vec6f; +typedef std::vector vector_Vec4i; +typedef std::vector vector_Rect; +typedef std::vector vector_Rect2d; +typedef std::vector vector_KeyPoint; +typedef std::vector vector_Mat; +typedef std::vector vector_DMatch; +typedef std::vector vector_String; +typedef std::vector vector_Scalar; + +typedef std::vector > vector_vector_char; +typedef std::vector > vector_vector_Point; +typedef std::vector > vector_vector_Point2f; +typedef std::vector > vector_vector_Point3f; +typedef std::vector > vector_vector_DMatch; + + +#ifdef HAVE_OPENCV_FEATURES2D +#include "opencv2/features2d.hpp" +typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; +#endif + +#ifdef HAVE_OPENCV_FLANN +#include "opencv2/flann.hpp" +typedef cvflann::flann_distance_t cvflann_flann_distance_t; +typedef cvflann::flann_algorithm_t cvflann_flann_algorithm_t; +#endif + +#ifdef HAVE_OPENCV_STITCHING +#include "opencv2/stitching.hpp" +typedef Stitcher::Status Status; +#endif + + +static PyObject* opencv_error = 0; + + +static int failmsg(const char *fmt, ...) +{ + char str[1000]; + + va_list ap; + va_start(ap, fmt); + vsnprintf(str, sizeof(str), fmt, ap); + va_end(ap); + + PyErr_SetString(PyExc_TypeError, str); + return 0; +} + + +class PyAllowThreads +{ +public: + PyAllowThreads() : _state(PyEval_SaveThread()) {} + ~PyAllowThreads() + { + PyEval_RestoreThread(_state); + } +private: + PyThreadState* _state; +}; + +class PyEnsureGIL +{ +public: + PyEnsureGIL() : _state(PyGILState_Ensure()) {} + ~PyEnsureGIL() + { + PyGILState_Release(_state); + } +private: + PyGILState_STATE _state; +}; + + +#define ERRWRAP2(expr) \ +try \ +{ \ + PyAllowThreads allowThreads; \ + expr; \ +} \ +catch (const cv::Exception &e) \ +{ \ + PyErr_SetString(opencv_error, e.what()); \ + return 0; \ +} + + +#endif // __UTILS_HPP__ diff --git a/dt.hpp b/gendistrans/include/opencv2/gendistrans.hpp similarity index 90% rename from dt.hpp rename to gendistrans/include/opencv2/gendistrans.hpp index 491f050..9d6b809 100644 --- a/dt.hpp +++ b/gendistrans/include/opencv2/gendistrans.hpp @@ -32,9 +32,14 @@ #include "opencv2/opencv.hpp" -// Calculates the distance trasnform as described in Distance Transforms of Sampled Functions -void distanceTransform(const cv::Mat &inputMatrix, cv::Mat &outputMatrix, - cv::Mat &locations, std::vector weights = std::vector()); +namespace cv +{ +// Calculates the distance trasnform as described in Distance Transforms of Sampled Functions +CV_EXPORTS_W void distanceTransform(cv::InputArray sampled, + cv::OutputArray dist, cv::OutputArray locations, + cv::InputArray weights=cv::noArray()); + +} #endif diff --git a/dt.cpp b/gendistrans/src/gendistrans.cpp similarity index 81% rename from dt.cpp rename to gendistrans/src/gendistrans.cpp index 36cebb5..0255a33 100644 --- a/dt.cpp +++ b/gendistrans/src/gendistrans.cpp @@ -29,16 +29,20 @@ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/opencv_modules.hpp" -#include "dt.hpp" +#include "opencv2/gendistrans.hpp" #include using namespace std; using namespace cv; +namespace cv +{ + template T square(const T &x) { return x * x; } +namespace { // avoid exporting symbols /* * Calculates the distance transform on a one dimensional array. @@ -145,13 +149,13 @@ void distanceTransform1d(float *f, float *d, int *l, int n) { // Parallel invoker class DistanceTransformInvoker: public ParallelLoopBody { public: - DistanceTransformInvoker(Mat& inputMatrix, Mat *outputMatrix, - Mat *locations, int **steps, int dim) { - *outputMatrix = inputMatrix.clone(); - this->outputMatrix = outputMatrix; - this->locationsMatrix = locations; - this->steps = steps; - this->dim = dim; + DistanceTransformInvoker(Mat& inputMatrix_, Mat *outputMatrix_, + Mat *locations_, int **steps_, int dim_) { + *outputMatrix_ = inputMatrix_.clone(); + this->outputMatrix = outputMatrix_; + this->locationsMatrix = locations_; + this->steps = steps_; + this->dim = dim_; } @@ -177,19 +181,19 @@ class DistanceTransformInvoker: public ParallelLoopBody { * This array will hold the section of the global matrix were the * distance transform would be performed. */ - for (int i = 0; i < outputMatrix->size[dim]; ++i) { - f[i] = castedOutputMatrix[dataStart - + i * outputMatrix->step[dim] / 4]; + for (int j = 0; j < outputMatrix->size[dim]; ++j) { + f[j] = castedOutputMatrix[dataStart + + j * outputMatrix->step[dim] / 4]; } distanceTransform1d(f, d, l, outputMatrix->size[dim]); // Strided write - for (int i = 0; i < outputMatrix->size[dim]; ++i) { - castedOutputMatrix[dataStart + i * outputMatrix->step[dim] / 4] = - d[i]; + for (int j = 0; j < outputMatrix->size[dim]; ++j) { + castedOutputMatrix[dataStart + j * outputMatrix->step[dim] / 4] = + d[j]; castedLocationsMatrix[dataStart - + i * outputMatrix->step[dim] / 4 - + dim * locationsMatrix->step[0] / 4] = l[i]; + + j * outputMatrix->step[dim] / 4 + + dim * locationsMatrix->step[0] / 4] = l[j]; } delete[] f; @@ -208,30 +212,35 @@ class DistanceTransformInvoker: public ParallelLoopBody { }; +} // local namespace + + /* * Calculates the distance transform. */ -void distanceTransform(const Mat &inputMatrix, Mat &outputMatrix, - Mat &locations, std::vector weights) { +void distanceTransform(InputArray _sampled, OutputArray _dist, + OutputArray _locations, InputArray _weights) { + + const cv::Mat inputMatrix = _sampled.getMat(); + _sampled.copyTo(_dist); + cv::Mat outputMatrix = _dist.getMat(); + const cv::Mat weights = _weights.getMat(); // Input matrix has proper type CV_Assert(inputMatrix.type() == CV_32FC1); + // Dimension scaling is unspecified or has proper size + CV_Assert(weights.empty() + || (weights.total() == (size_t)inputMatrix.dims)); - CV_Assert( - (weights.size() == 0) - || ((int ) weights.size() == inputMatrix.dims)); - - // This way we don't mess with users input, they may want to use it later - outputMatrix = inputMatrix.clone(); - + // Create location matrix, for each input pixel the location matrix will + // have "inputMatrix.dims" parameters. vector sizes; - // For each input pixel the location matrix will have "inputMatrix.dims" parameters sizes.push_back(inputMatrix.dims); for (int d = 0; d < inputMatrix.dims; ++d) { sizes.push_back(inputMatrix.size[d]); } - - locations = Mat(sizes.size(), &sizes[0], CV_32SC1); + _locations.create(sizes.size(), &sizes[0], CV_32SC1); + Mat locations = _locations.getMat(); for (int dim = outputMatrix.dims - 1; dim >= 0; --dim) { @@ -290,9 +299,9 @@ void distanceTransform(const Mat &inputMatrix, Mat &outputMatrix, // When the weight is too small use 0.00001 since 0 would screw the results double zero = 0.00001; - if (weights.size() != 0) { - if (weights[dim] >= 0.1) { - outputMatrix *= weights[dim]; + if (!weights.empty()) { + if (weights.at(dim) >= 0.1) { + outputMatrix *= weights.at(dim); } else { outputMatrix *= zero; } @@ -306,20 +315,21 @@ void distanceTransform(const Mat &inputMatrix, Mat &outputMatrix, cv::parallel_for_(range, invoker); - if (weights.size() != 0) { - if (weights[dim] >= 0.1) { - outputMatrix /= weights[dim]; + if (!weights.empty()) { + if (weights.at(dim) >= 0.1) { + outputMatrix /= weights.at(dim); } else { outputMatrix /= zero; } } for (int i = 0; i < iterations; ++i) { - delete (currentStep[i]); + delete[](currentStep[i]); } - delete (currentStep); + delete[](currentStep); } } +} // namepsace cv diff --git a/main.cpp b/tests/dontcrash.cpp similarity index 85% rename from main.cpp rename to tests/dontcrash.cpp index cf45a53..a4d4dec 100644 --- a/main.cpp +++ b/tests/dontcrash.cpp @@ -1,9 +1,10 @@ - #include "opencv2/opencv.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/opencv_modules.hpp" -#include "dt.hpp" + +// If this module reaches upstream OpenCV modules tree, remove this line: +#include "opencv2/gendistrans.hpp" using namespace cv; @@ -15,9 +16,8 @@ int main() { Mat l; // Just to test that weighting is working - vector weights; - weights.push_back(2); - weights.push_back(2); + double w_[] = {2, 2}; + Mat weights(2, 1, CV_32FC2, w_); t.at(2, 2) = 1;