├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── boost-python.cmake ├── np_opencv_converter.cpp ├── np_opencv_converter.hpp ├── tests └── np_opencv_module.cpp └── utils ├── container.h ├── conversion.cpp ├── conversion.h └── template.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Author: Sudeep Pillai (Sep 14, 2014) 2 | project (numpy-opencv-converter) 3 | cmake_minimum_required (VERSION 2.6.0) 4 | 5 | # Include package config, boost-python 6 | find_package(PkgConfig REQUIRED) 7 | include(cmake/boost-python.cmake) 8 | 9 | # configure opencv 10 | pkg_check_modules(OpenCV opencv) 11 | include_directories(${OpenCV_INCLUDE_DIRS}) 12 | 13 | # Include python (use -D flags instead) 14 | if (NOT PYTHON_INCLUDE_DIRS OR NOT PYTHON_LIBRARY) 15 | SET(PYTHON_INCLUDE_DIRS "/usr/include/python2.7") 16 | SET(PYTHON_LIBRARY "/usr/lib/python2.7/config/libpython2.7.so") 17 | endif() 18 | 19 | # Build np<=>opencv converter library 20 | boost_python_module(np_opencv_converter np_opencv_converter.cpp utils/conversion.cpp) 21 | target_link_libraries(np_opencv_converter boost_system boost_python ${OpenCV_LDFLAGS}) 22 | 23 | # Build test library 24 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 25 | boost_python_module(np_opencv_module tests/np_opencv_module.cpp) 26 | target_link_libraries(np_opencv_module boost_system boost_python np_opencv_converter ${OpenCV_LDFLAGS}) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Sudeep Pillai 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NUMPY <=> OpenCV's cv::Mat converter 2 | [Sudeep Pillai](http://people.csail.mit.edu/spillai) (spillai@csail.mit.edu) Sep 14, 2014 3 | 4 | A convenience library that registers a boost::python converter to implicitly 5 | convert between OpenCV's popular cv::Mat datatype and NumPy's popular 6 | np.array() datatype. This allows a developer to go back and forth between their 7 | OpenCV C++ API and Python API written using NumPy with relative ease, avoiding the need to 8 | write additional wrappers that handle PyObjects being passed around or returned. 9 | 10 | This work was mostly inspired by 11 | https://github.com/yati-sagade/opencv-ndarray-conversion. 12 | 13 | **BSD License** 14 | 15 | ## A simple example 16 | 17 | Imagine writing a C++ API using OpenCV as so: 18 | ```c++ 19 | cv::Mat process_mat(const cv::Mat& in) { 20 | // process matrix, or just plain-simple cloning! 21 | cv::Mat out = in.clone(); 22 | return out; 23 | } 24 | ``` 25 | 26 | Wrap it using Boost::Python in say, cv_module.cpp: 27 | ```python 28 | boost::python::def("process_mat", &process_mat); 29 | ``` 30 | 31 | Call it from Python: 32 | ```python 33 | import numpy as np 34 | from cv_module import process_mat 35 | A = np.random.random(shape=(4,3)) 36 | B = process_mat(A) 37 | ``` 38 | 39 | As simple as that! Hope you find it useful! 40 | 41 | ## Build and Install 42 | See dependencies below before building. 43 | ``` 44 | git clone git@github.com:spillai/numpy-opencv-converter 45 | mkdir build 46 | cd build && cmake .. 47 | make 48 | ``` 49 | 50 | ## Usage 51 | 52 | Make sure the built np_opencv_converter.so and np_opencv_module.so 53 | (currently in numpy-opencv-converter/build/) are in the $LD_LIBRARY_PATH 54 | environment variable, before running the following in python/ipython. 55 | 56 | ```python 57 | In [1]: import np_opencv_module as npcv 58 | PYTHON TYPE CONVERTERS exported 59 | ``` 60 | On succesful import, the cv::Mat <=> np.ndarray converters are exported. 61 | 62 | We then test a simple function that takes in a cv::Mat (appropriately converted 63 | from np.ndarray A) as an argument and returns 64 | another cv::Mat (again appropriately converted back to np.ndarray B). 65 | 66 | ```python 67 | In [2]: import numpy as np 68 | In [3]: A = np.random.random((4,3)) 69 | In [4]: B = npcv.test_np_mat(A) 70 | in: [0.2793205402416998, 0.466896711918419, 0.3834843006535923; 71 | 0.5374426625812107, 0.3737008026047054, 0.3685794034255524; 72 | 0.993469313797578, 0.2619403678989528, 0.5700175530375297; 73 | 0.5711496315041438, 0.3286727439294438, 0.1250325059375548] 74 | sz: [3 x 4] 75 | 76 | In [5]: print A.dtype, B.dtype 77 | float64 float64 78 | ``` 79 | 80 | With default args 81 | ```python 82 | In [6]: npcv.test_with_args(A, var1=0, var2=20.0, name='test_name2') 83 | in: [0.27932054, 0.46689671, 0.3834843; 84 | 0.53744268, 0.3737008, 0.36857942; 85 | 0.9934693, 0.26194036, 0.57001758; 86 | 0.57114965, 0.32867274, 0.1250325] 87 | sz: [3 x 4] 88 | Returning transpose 89 | 90 | Out[6]: 91 | array([[ 0.27932054, 0.53744268, 0.9934693 , 0.57114965], 92 | [ 0.46689671, 0.3737008 , 0.26194036, 0.32867274], 93 | [ 0.3834843 , 0.36857942, 0.57001758, 0.1250325 ]], 94 | dtype=float32) 95 | ``` 96 | 97 | Finally, testing a wrapper class 98 | ```python 99 | In [7]: gw = npcv.GenericWrapper(var_int=1, var_float=2.0, var_double=3.0, var_string='string') 100 | In [8]: gw.process(A) 101 | 102 | in: [0.2793205402416998, 0.466896711918419, 0.3834843006535923; 103 | 0.5374426625812107, 0.3737008026047054, 0.3685794034255524; 104 | 0.993469313797578, 0.2619403678989528, 0.5700175530375297; 105 | 0.5711496315041438, 0.3286727439294438, 0.1250325059375548] 106 | sz: [3 x 4] 107 | Returning transpose 108 | 109 | Out[8]: 110 | array([[ 0.27932054, 0.53744266, 0.99346931, 0.57114963], 111 | [ 0.46689671, 0.3737008 , 0.26194037, 0.32867274], 112 | [ 0.3834843 , 0.3685794 , 0.57001755, 0.12503251]]) 113 | ``` 114 | 115 | ## Dependencies 116 | Currently only Linux is supported, although the code is pretty barebones that 117 | should allow you to port it to Windows, or Mac OSX with relative ease. 118 | I personally wouldn't recommend installing opencv as indicated below, but I'll 119 | assume you know how to deal with pkg-config and opencv to make any 120 | modifications to the CMakeLists.txt file. 121 | 122 | ``` 123 | $ sudo apt-get install libboost-python-dev libopencv-dev 124 | ``` 125 | -------------------------------------------------------------------------------- /cmake/boost-python.cmake: -------------------------------------------------------------------------------- 1 | function(boost_python_module NAME) 2 | find_package(Boost COMPONENTS python system REQUIRED) 3 | 4 | # message("Boost_INCLUDE_DIRS: " ${Boost_INCLUDE_DIRS} ) 5 | # message("Boost_LIBRARIES: " ${Boost_LIBRARIES} ) 6 | 7 | # For some odd reason, Boost_DIR is set to not found 8 | # http://stackoverflow.com/questions/18927970/boost-dir-notfound-on-cmake 9 | set (Boost_DIR "" CACHE INTERNAL "") 10 | find_package(PythonLibs REQUIRED) 11 | 12 | set(DEP_LIBS 13 | ${Boost_PYTHON_LIBRARY} 14 | ${PYTHON_LIBRARIES} 15 | ) 16 | #these are required includes for every ecto module 17 | include_directories( 18 | ${PYTHON_INCLUDE_PATH} 19 | ${Boost_INCLUDE_DIRS} 20 | ) 21 | add_library(${NAME} SHARED 22 | ${ARGN} 23 | ) 24 | set_target_properties(${NAME} 25 | PROPERTIES 26 | OUTPUT_NAME ${NAME} 27 | COMPILE_FLAGS "${FASTIDIOUS_FLAGS}" 28 | LINK_FLAGS -dynamic 29 | PREFIX "" 30 | ) 31 | if( WIN32 ) 32 | set_target_properties(${NAME} PROPERTIES SUFFIX ".pyd") 33 | elseif( APPLE OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 34 | # on mac osx, python cannot import libraries with .dylib extension 35 | set_target_properties(${NAME} PROPERTIES SUFFIX ".so") 36 | endif() 37 | target_link_libraries(${NAME} 38 | ${DEP_LIBS} 39 | ) 40 | endfunction() -------------------------------------------------------------------------------- /np_opencv_converter.cpp: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // License: BSD 3 | // Last modified: Sep 14, 2014 4 | 5 | #include "np_opencv_converter.hpp" 6 | 7 | namespace fs { namespace python { 8 | 9 | // Static PyInit 10 | static void py_init() { 11 | Py_Initialize(); 12 | import_array(); 13 | } 14 | 15 | // Singleton init and export converters 16 | static bool export_type_conversions_once = false; 17 | bool init_and_export_converters() { 18 | 19 | if (export_type_conversions_once) 20 | return false; 21 | 22 | std::cerr << "PYTHON TYPE CONVERTERS exported" << std::endl; 23 | export_type_conversions_once = true; 24 | 25 | // Py_Init and array import 26 | py_init(); 27 | 28 | // => py::list 29 | expose_template_type(); 30 | expose_template_type(); 31 | expose_template_type(); 32 | 33 | // std::vector => py::list 34 | expose_template_type< std::vector >(); 35 | expose_template_type< std::vector >(); 36 | expose_template_type< std::vector >(); 37 | 38 | expose_template_type< std::vector >(); 39 | expose_template_type< std::vector >(); 40 | expose_template_type< std::vector >(); 41 | 42 | expose_template_type< std::vector >(); 43 | expose_template_type< std::vector >(); 44 | expose_template_type< std::vector >(); 45 | 46 | // std::map => py::dict 47 | expose_template_type > >(); 48 | expose_template_type > >(); 49 | expose_template_type >(); 50 | 51 | // various converters to cv::Mat 52 | py::to_python_converter(); 53 | py::to_python_converter(); 54 | py::to_python_converter(); 55 | py::to_python_converter(); 56 | 57 | // register the to-from-python converter for each of the types 58 | Mat_PyObject_converter< cv::Mat >(); 59 | 60 | // 1-channel 61 | Mat_PyObject_converter< cv::Mat1b >(); 62 | Mat_PyObject_converter< cv::Mat1s >(); 63 | Mat_PyObject_converter< cv::Mat1w >(); 64 | Mat_PyObject_converter< cv::Mat1i >(); 65 | Mat_PyObject_converter< cv::Mat1f >(); 66 | Mat_PyObject_converter< cv::Mat1d >(); 67 | 68 | // 2-channel 69 | Mat_PyObject_converter< cv::Mat2b >(); 70 | Mat_PyObject_converter< cv::Mat2s >(); 71 | Mat_PyObject_converter< cv::Mat2w >(); 72 | Mat_PyObject_converter< cv::Mat2i >(); 73 | Mat_PyObject_converter< cv::Mat2f >(); 74 | Mat_PyObject_converter< cv::Mat2d >(); 75 | 76 | // 3-channel 77 | Mat_PyObject_converter< cv::Mat3b >(); 78 | Mat_PyObject_converter< cv::Mat3s >(); 79 | Mat_PyObject_converter< cv::Mat3w >(); 80 | Mat_PyObject_converter< cv::Mat3i >(); 81 | Mat_PyObject_converter< cv::Mat3f >(); 82 | Mat_PyObject_converter< cv::Mat3d >(); 83 | 84 | // add more if needed 85 | 86 | return true; 87 | } 88 | 89 | } // namespace python 90 | } // namespace fs 91 | 92 | -------------------------------------------------------------------------------- /np_opencv_converter.hpp: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // License: BSD 3 | // Last modified: Sep 14, 2014 4 | 5 | #ifndef NP_OPENCV_CONVERTER_HPP_ 6 | #define NP_OPENCV_CONVERTER_HPP_ 7 | 8 | // Boost python includes 9 | #include 10 | #include 11 | 12 | // Include templated conversion utils 13 | #include "utils/template.h" 14 | #include "utils/container.h" 15 | #include "utils/conversion.h" 16 | 17 | // opencv includes 18 | #include 19 | 20 | namespace fs { namespace python { 21 | 22 | // TODO: Template these 23 | // Vec3f => cv::Mat 24 | struct Vec3f_to_mat { 25 | static PyObject* convert(const cv::Vec3f& v){ 26 | NDArrayConverter cvt; 27 | PyObject* ret = cvt.toNDArray(cv::Mat(v)); 28 | return ret; 29 | } 30 | }; 31 | 32 | // cv::Point => cv::Mat 33 | struct Point_to_mat { 34 | static PyObject* convert(const cv::Point& v){ 35 | NDArrayConverter cvt; 36 | PyObject* ret = cvt.toNDArray(cv::Mat(v)); 37 | return ret; 38 | } 39 | }; 40 | 41 | // cv::Point2f => cv::Mat 42 | struct Point2f_to_mat { 43 | static PyObject* convert(const cv::Point2f& v){ 44 | NDArrayConverter cvt; 45 | PyObject* ret = cvt.toNDArray(cv::Mat(v)); 46 | return ret; 47 | } 48 | }; 49 | 50 | // cv::Point3f => cv::Mat 51 | struct Point3f_to_mat { 52 | static PyObject* convert(const cv::Point3f& v){ 53 | NDArrayConverter cvt; 54 | PyObject* ret = cvt.toNDArray(cv::Mat(v)); 55 | return ret; 56 | } 57 | }; 58 | 59 | // cv::Mat_ => Numpy PyObject 60 | template 61 | struct Mat_to_PyObject { 62 | static PyObject* convert(const T& mat){ 63 | NDArrayConverter cvt; 64 | PyObject* ret = cvt.toNDArray(mat); 65 | return ret; 66 | } 67 | }; 68 | 69 | // Generic templated cv::Mat <=> Numpy PyObject converter 70 | template 71 | struct Mat_PyObject_converter 72 | { 73 | // Register from converter 74 | Mat_PyObject_converter() { 75 | boost::python::converter::registry::push_back( 76 | &convertible, 77 | &construct, 78 | boost::python::type_id()); 79 | 80 | // Register to converter 81 | py::to_python_converter >(); 82 | } 83 | 84 | // Convert from type T to PyObject (numpy array) 85 | // Assume obj_ptr can be converted in a cv::Mat 86 | static void* convertible(PyObject* obj_ptr) 87 | { 88 | // Check validity? 89 | assert(obj_ptr != 0); 90 | return obj_ptr; 91 | } 92 | 93 | // Convert obj_ptr into a cv::Mat 94 | static void construct(PyObject* obj_ptr, 95 | boost::python::converter::rvalue_from_python_stage1_data* data) 96 | { 97 | using namespace boost::python; 98 | typedef converter::rvalue_from_python_storage< T > storage_t; 99 | 100 | storage_t* the_storage = reinterpret_cast( data ); 101 | void* memory_chunk = the_storage->storage.bytes; 102 | 103 | NDArrayConverter cvt; 104 | T* newvec = new (memory_chunk) T(cvt.toMat(obj_ptr)); 105 | data->convertible = memory_chunk; 106 | 107 | return; 108 | } 109 | }; 110 | 111 | bool init_and_export_converters(); 112 | 113 | } // namespace python 114 | } // namespace fs 115 | 116 | #endif // NP_OPENCV_CONVERTER_HPP_ 117 | -------------------------------------------------------------------------------- /tests/np_opencv_module.cpp: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // License: BSD 3 | // Last modified: Sep 14, 2014 4 | 5 | // Wrapper for most external modules 6 | #include 7 | #include 8 | #include 9 | 10 | // Opencv includes 11 | #include 12 | 13 | // np_opencv_converter 14 | #include "np_opencv_converter.hpp" 15 | 16 | namespace py = boost::python; 17 | 18 | cv::Mat test_np_mat(const cv::Mat& in) { 19 | std::cerr << "in: " << in << std::endl; 20 | std::cerr << "sz: " << in.size() << std::endl; 21 | return in.clone(); 22 | } 23 | 24 | cv::Mat test_with_args(const cv::Mat_& in, const int& var1 = 1, 25 | const double& var2 = 10.0, const std::string& name=std::string("test_name")) { 26 | std::cerr << "in: " << in << std::endl; 27 | std::cerr << "sz: " << in.size() << std::endl; 28 | std::cerr << "Returning transpose" << std::endl; 29 | return in.t(); 30 | } 31 | 32 | class GenericWrapper { 33 | public: 34 | GenericWrapper(const int& _var_int = 1, const float& _var_float = 1.f, 35 | const double& _var_double = 1.d, const std::string& _var_string = std::string("test_string")) 36 | : var_int(_var_int), var_float(_var_float), var_double(_var_double), var_string(_var_string) 37 | { 38 | 39 | } 40 | 41 | cv::Mat process(const cv::Mat& in) { 42 | std::cerr << "in: " << in << std::endl; 43 | std::cerr << "sz: " << in.size() << std::endl; 44 | std::cerr << "Returning transpose" << std::endl; 45 | return in.t(); 46 | } 47 | 48 | private: 49 | int var_int; 50 | float var_float; 51 | double var_double; 52 | std::string var_string; 53 | }; 54 | 55 | // Wrap a few functions and classes for testing purposes 56 | namespace fs { namespace python { 57 | 58 | BOOST_PYTHON_MODULE(np_opencv_module) 59 | { 60 | // Main types export 61 | fs::python::init_and_export_converters(); 62 | py::scope scope = py::scope(); 63 | 64 | // Basic test 65 | py::def("test_np_mat", &test_np_mat); 66 | 67 | // With arguments 68 | py::def("test_with_args", &test_with_args, 69 | (py::arg("src"), py::arg("var1")=1, py::arg("var2")=10.0, py::arg("name")="test_name")); 70 | 71 | // Class 72 | py::class_("GenericWrapper") 73 | .def(py::init >( 74 | (py::arg("var_int")=1, py::arg("var_float")=1.f, py::arg("var_double")=1.d, 75 | py::arg("var_string")=std::string("test")))) 76 | .def("process", &GenericWrapper::process) 77 | ; 78 | } 79 | 80 | } // namespace fs 81 | } // namespace python 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /utils/container.h: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // Note: Stripped from pyxx project 3 | 4 | #ifndef UTILS_CONTAINER_H_ 5 | #define UTILS_CONTAINER_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace py = boost::python; 20 | 21 | template 22 | struct expose_template_type< std::vector > : 23 | public expose_template_type_base< std::vector > 24 | { 25 | typedef expose_template_type_base< std::vector > base_type; 26 | typedef expose_template_type< std::vector > this_type; 27 | typedef std::vector wrapped_type; 28 | 29 | expose_template_type() 30 | { 31 | ::expose_template_type(); 32 | if( !base_type::wrapped() ) 33 | { 34 | py::to_python_converter< wrapped_type, this_type >(); 35 | py::converter::registry::push_back( 36 | this_type::convertible, 37 | this_type::construct, 38 | py::type_id< wrapped_type >() ); 39 | 40 | } 41 | } 42 | 43 | static PyObject * convert( const wrapped_type & container) 44 | { 45 | py::list l; 46 | for(typename wrapped_type::const_iterator iter = container.begin(); iter != container.end(); iter++) 47 | { 48 | l.append( py::object( *iter ) ); 49 | } 50 | Py_INCREF( l.ptr() ); 51 | return l.ptr(); 52 | } 53 | 54 | static void * convertible( PyObject * py_obj) 55 | { 56 | // we are supposed to indicate whether or not we can convert this 57 | // we don't really know, but we'll try any sequence 58 | if( PySequence_Check(py_obj) ) 59 | return py_obj; 60 | return 0; 61 | } 62 | 63 | static void construct( PyObject * py_obj, py::converter::rvalue_from_python_stage1_data* data) 64 | { 65 | using namespace boost::python; 66 | typedef converter::rvalue_from_python_storage< wrapped_type > storage_t; 67 | 68 | storage_t* the_storage = reinterpret_cast( data ); 69 | void* memory_chunk = the_storage->storage.bytes; 70 | wrapped_type * newvec = new (memory_chunk) wrapped_type; 71 | data->convertible = memory_chunk; 72 | 73 | object sequence(handle<>( borrowed( py_obj ) ) ); 74 | 75 | for(int idx = 0; idx < len(sequence);idx++) 76 | { 77 | newvec->push_back( extract( sequence[idx] )() ); 78 | } 79 | 80 | } 81 | 82 | }; 83 | 84 | template 85 | struct expose_template_type< std::map > : 86 | public expose_template_type_base< std::map > 87 | { 88 | typedef std::map wrapped_type; 89 | typedef expose_template_type_base< wrapped_type > base_type; 90 | typedef expose_template_type< wrapped_type > this_type; 91 | 92 | expose_template_type() 93 | { 94 | if( !base_type::wrapped() ) 95 | { 96 | py::to_python_converter< wrapped_type, this_type >(); 97 | py::converter::registry::push_back( 98 | this_type::convertible, 99 | this_type::construct, 100 | py::type_id< wrapped_type >() ); 101 | 102 | } 103 | } 104 | 105 | static PyObject * convert( const wrapped_type & container) 106 | { 107 | py::dict d; 108 | for(typename wrapped_type::const_iterator iter = container.begin(); iter != container.end(); iter++) 109 | { 110 | d[iter->first] = py::object(iter->second); 111 | } 112 | Py_INCREF( d.ptr() ); 113 | return d.ptr(); 114 | } 115 | 116 | static void * convertible( PyObject * py_obj) 117 | { 118 | // we are supposed to indicate whether or not we can convert this 119 | // we don't really know, but we'll try any sequence 120 | if( PyMapping_Check(py_obj) ) 121 | return py_obj; 122 | return 0; 123 | } 124 | 125 | static void construct( PyObject * py_obj, py::converter::rvalue_from_python_stage1_data* data) 126 | { 127 | using namespace boost::python; 128 | typedef converter::rvalue_from_python_storage< wrapped_type > storage_t; 129 | 130 | storage_t* the_storage = reinterpret_cast( data ); 131 | void* memory_chunk = the_storage->storage.bytes; 132 | wrapped_type * newvec = new (memory_chunk) wrapped_type; 133 | data->convertible = memory_chunk; 134 | 135 | object sequence(handle<>( borrowed( py_obj ) ) ); 136 | sequence = sequence.attr("items")(); 137 | 138 | for(int idx = 0; idx < len(sequence);idx++) 139 | { 140 | Key key = py::extract(sequence[idx][0])(); 141 | Value value = py::extract(sequence[idx][1])(); 142 | (*newvec)[key] = value; 143 | } 144 | 145 | } 146 | 147 | }; 148 | 149 | template 150 | struct expose_template_type< typename std::pair > : 151 | public expose_template_type_base< std::pair > 152 | { 153 | typedef std::pair wrapped_type; 154 | typedef expose_template_type_base< wrapped_type > base_type; 155 | typedef expose_template_type< wrapped_type > this_type; 156 | 157 | expose_template_type() 158 | { 159 | if( !base_type::wrapped() ) 160 | { 161 | py::converter::registry::push_back( 162 | this_type::convertible, 163 | this_type::construct, 164 | py::type_id< wrapped_type >() ); 165 | 166 | } 167 | } 168 | 169 | static void * convertible( PyObject * py_obj) 170 | { 171 | // we are supposed to indicate whether or not we can convert this 172 | // we don't really know, but we'll try any sequence 173 | if( PyTuple_Check(py_obj) && PyTuple_Size(py_obj) == 2) 174 | return py_obj; 175 | return 0; 176 | } 177 | 178 | static void construct( PyObject * py_obj, py::converter::rvalue_from_python_stage1_data* data) 179 | { 180 | using namespace boost::python; 181 | typedef converter::rvalue_from_python_storage< wrapped_type > storage_t; 182 | 183 | storage_t* the_storage = reinterpret_cast( data ); 184 | void* memory_chunk = the_storage->storage.bytes; 185 | wrapped_type * newvec = new (memory_chunk) wrapped_type; 186 | data->convertible = memory_chunk; 187 | 188 | object sequence(handle<>( borrowed( py_obj ) ) ); 189 | newvec->first = extract(sequence[0])(); 190 | newvec->second = extract(sequence[1])(); 191 | } 192 | 193 | }; 194 | 195 | #endif // UTILS_CONTAINER_H_ 196 | -------------------------------------------------------------------------------- /utils/conversion.cpp: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // Note: Stripped from Opencv (opencv/modules/python/src2/cv2.cpp) 3 | 4 | # include "conversion.h" 5 | /* 6 | * The following conversion functions are taken/adapted from OpenCV's cv2.cpp file 7 | * inside modules/python/src2 folder. 8 | */ 9 | 10 | static void init() 11 | { 12 | import_array(); 13 | } 14 | 15 | static int failmsg(const char *fmt, ...) 16 | { 17 | char str[1000]; 18 | 19 | va_list ap; 20 | va_start(ap, fmt); 21 | vsnprintf(str, sizeof(str), fmt, ap); 22 | va_end(ap); 23 | 24 | PyErr_SetString(PyExc_TypeError, str); 25 | return 0; 26 | } 27 | 28 | class PyAllowThreads 29 | { 30 | public: 31 | PyAllowThreads() : _state(PyEval_SaveThread()) {} 32 | ~PyAllowThreads() 33 | { 34 | PyEval_RestoreThread(_state); 35 | } 36 | private: 37 | PyThreadState* _state; 38 | }; 39 | 40 | class PyEnsureGIL 41 | { 42 | public: 43 | PyEnsureGIL() : _state(PyGILState_Ensure()) {} 44 | ~PyEnsureGIL() 45 | { 46 | PyGILState_Release(_state); 47 | } 48 | private: 49 | PyGILState_STATE _state; 50 | }; 51 | 52 | using namespace cv; 53 | static PyObject* failmsgp(const char *fmt, ...) 54 | { 55 | char str[1000]; 56 | 57 | va_list ap; 58 | va_start(ap, fmt); 59 | vsnprintf(str, sizeof(str), fmt, ap); 60 | va_end(ap); 61 | 62 | PyErr_SetString(PyExc_TypeError, str); 63 | return 0; 64 | } 65 | 66 | #define OPENCV_3 0 67 | #if OPENCV_3 68 | class NumpyAllocator : public MatAllocator 69 | { 70 | public: 71 | NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); } 72 | ~NumpyAllocator() {} 73 | 74 | UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const 75 | { 76 | UMatData* u = new UMatData(this); 77 | u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o); 78 | npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o); 79 | for( int i = 0; i < dims - 1; i++ ) 80 | step[i] = (size_t)_strides[i]; 81 | step[dims-1] = CV_ELEM_SIZE(type); 82 | u->size = sizes[0]*step[0]; 83 | u->userdata = o; 84 | return u; 85 | } 86 | 87 | UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags) const 88 | { 89 | if( data != 0 ) 90 | { 91 | CV_Error(Error::StsAssert, "The data should normally be NULL!"); 92 | // probably this is safe to do in such extreme case 93 | return stdAllocator->allocate(dims0, sizes, type, data, step, flags); 94 | } 95 | PyEnsureGIL gil; 96 | 97 | int depth = CV_MAT_DEPTH(type); 98 | int cn = CV_MAT_CN(type); 99 | const int f = (int)(sizeof(size_t)/8); 100 | int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE : 101 | depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT : 102 | depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT : 103 | depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT; 104 | int i, dims = dims0; 105 | cv::AutoBuffer _sizes(dims + 1); 106 | for( i = 0; i < dims; i++ ) 107 | _sizes[i] = sizes[i]; 108 | if( cn > 1 ) 109 | _sizes[dims++] = cn; 110 | PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum); 111 | if(!o) 112 | CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims)); 113 | return allocate(o, dims0, sizes, type, step); 114 | } 115 | 116 | bool allocate(UMatData* u, int accessFlags) const 117 | { 118 | return stdAllocator->allocate(u, accessFlags); 119 | } 120 | 121 | void deallocate(UMatData* u) const 122 | { 123 | if(u) 124 | { 125 | PyEnsureGIL gil; 126 | PyObject* o = (PyObject*)u->userdata; 127 | Py_XDECREF(o); 128 | delete u; 129 | } 130 | } 131 | 132 | const MatAllocator* stdAllocator; 133 | }; 134 | #else 135 | class NumpyAllocator : public MatAllocator 136 | { 137 | public: 138 | NumpyAllocator() {} 139 | ~NumpyAllocator() {} 140 | 141 | void allocate(int dims, const int* sizes, int type, int*& refcount, 142 | uchar*& datastart, uchar*& data, size_t* step) 143 | { 144 | PyEnsureGIL gil; 145 | 146 | int depth = CV_MAT_DEPTH(type); 147 | int cn = CV_MAT_CN(type); 148 | const int f = (int)(sizeof(size_t)/8); 149 | int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE : 150 | depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT : 151 | depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT : 152 | depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT; 153 | int i; 154 | npy_intp _sizes[CV_MAX_DIM+1]; 155 | for( i = 0; i < dims; i++ ) 156 | _sizes[i] = sizes[i]; 157 | if( cn > 1 ) 158 | { 159 | /*if( _sizes[dims-1] == 1 ) 160 | _sizes[dims-1] = cn; 161 | else*/ 162 | _sizes[dims++] = cn; 163 | } 164 | PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum); 165 | if(!o) 166 | CV_Error_(CV_StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims)); 167 | refcount = refcountFromPyObject(o); 168 | npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o); 169 | for( i = 0; i < dims - (cn > 1); i++ ) 170 | step[i] = (size_t)_strides[i]; 171 | datastart = data = (uchar*)PyArray_DATA((PyArrayObject*) o); 172 | } 173 | 174 | void deallocate(int* refcount, uchar*, uchar*) 175 | { 176 | PyEnsureGIL gil; 177 | if( !refcount ) 178 | return; 179 | PyObject* o = pyObjectFromRefcount(refcount); 180 | Py_INCREF(o); 181 | Py_DECREF(o); 182 | } 183 | }; 184 | #endif 185 | 186 | 187 | 188 | NumpyAllocator g_numpyAllocator; 189 | 190 | NDArrayConverter::NDArrayConverter() { init(); } 191 | 192 | void NDArrayConverter::init() 193 | { 194 | import_array(); 195 | } 196 | 197 | cv::Mat NDArrayConverter::toMat(const PyObject *o) 198 | { 199 | cv::Mat m; 200 | 201 | if(!o || o == Py_None) 202 | { 203 | if( !m.data ) 204 | m.allocator = &g_numpyAllocator; 205 | } 206 | 207 | if( !PyArray_Check(o) ) 208 | { 209 | failmsg("toMat: Object is not a numpy array"); 210 | } 211 | 212 | int typenum = PyArray_TYPE(o); 213 | int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S : 214 | typenum == NPY_USHORT ? CV_16U : typenum == NPY_SHORT ? CV_16S : 215 | typenum == NPY_INT || typenum == NPY_LONG ? CV_32S : 216 | typenum == NPY_FLOAT ? CV_32F : 217 | typenum == NPY_DOUBLE ? CV_64F : -1; 218 | 219 | if( type < 0 ) 220 | { 221 | failmsg("toMat: Data type = %d is not supported", typenum); 222 | } 223 | 224 | int ndims = PyArray_NDIM(o); 225 | 226 | if(ndims >= CV_MAX_DIM) 227 | { 228 | failmsg("toMat: Dimensionality (=%d) is too high", ndims); 229 | } 230 | 231 | int size[CV_MAX_DIM+1]; 232 | size_t step[CV_MAX_DIM+1], elemsize = CV_ELEM_SIZE1(type); 233 | const npy_intp* _sizes = PyArray_DIMS(o); 234 | const npy_intp* _strides = PyArray_STRIDES(o); 235 | bool transposed = false; 236 | 237 | for(int i = 0; i < ndims; i++) 238 | { 239 | size[i] = (int)_sizes[i]; 240 | step[i] = (size_t)_strides[i]; 241 | } 242 | 243 | if( ndims == 0 || step[ndims-1] > elemsize ) { 244 | size[ndims] = 1; 245 | step[ndims] = elemsize; 246 | ndims++; 247 | } 248 | 249 | if( ndims >= 2 && step[0] < step[1] ) 250 | { 251 | std::swap(size[0], size[1]); 252 | std::swap(step[0], step[1]); 253 | transposed = true; 254 | } 255 | 256 | // std::cerr << " ndims: " << ndims 257 | // << " size: " << size 258 | // << " type: " << type 259 | // << " step: " << step 260 | // << " size: " << size[2] << std::endl; 261 | 262 | // TODO: Possible bug in multi-dimensional matrices 263 | #if 1 264 | if( ndims == 3 && size[2] <= CV_CN_MAX && step[1] == elemsize*size[2] ) 265 | { 266 | ndims--; 267 | type |= CV_MAKETYPE(0, size[2]); 268 | } 269 | #endif 270 | 271 | if( ndims > 2) 272 | { 273 | failmsg("toMat: Object has more than 2 dimensions"); 274 | } 275 | 276 | m = Mat(ndims, size, type, PyArray_DATA(o), step); 277 | // m.u = g_numpyAllocator.allocate(o, ndims, size, type, step); 278 | 279 | if( m.data ) 280 | { 281 | #if OPENCV_3 282 | m.addref(); 283 | Py_INCREF(o); 284 | #else 285 | m.refcount = refcountFromPyObject(o); 286 | m.addref(); // protect the original numpy array from deallocation 287 | // (since Mat destructor will decrement the reference counter) 288 | #endif 289 | }; 290 | m.allocator = &g_numpyAllocator; 291 | 292 | if( transposed ) 293 | { 294 | Mat tmp; 295 | tmp.allocator = &g_numpyAllocator; 296 | transpose(m, tmp); 297 | m = tmp; 298 | } 299 | return m; 300 | } 301 | 302 | PyObject* NDArrayConverter::toNDArray(const cv::Mat& m) 303 | { 304 | #if OPENCV_3 305 | if( !m.data ) 306 | Py_RETURN_NONE; 307 | Mat temp, *p = (Mat*)&m; 308 | if(!p->u || p->allocator != &g_numpyAllocator) 309 | { 310 | temp.allocator = &g_numpyAllocator; 311 | m.copyTo(temp); 312 | p = &temp; 313 | } 314 | PyObject* o = (PyObject*)p->u->userdata; 315 | Py_INCREF(o); 316 | // p->addref(); 317 | // pyObjectFromRefcount(p->refcount); 318 | return o; 319 | #else 320 | if( !m.data ) 321 | Py_RETURN_NONE; 322 | Mat temp, *p = (Mat*)&m; 323 | if(!p->refcount || p->allocator != &g_numpyAllocator) 324 | { 325 | temp.allocator = &g_numpyAllocator; 326 | ERRWRAP2(m.copyTo(temp)); 327 | p = &temp; 328 | } 329 | p->addref(); 330 | return pyObjectFromRefcount(p->refcount); 331 | #endif 332 | 333 | } 334 | -------------------------------------------------------------------------------- /utils/conversion.h: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // Note: Stripped from Opencv (opencv/modules/python/src2/cv2.cpp) 3 | 4 | # ifndef __COVERSION_OPENCV_H__ 5 | # define __COVERSION_OPENCV_H__ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static PyObject* opencv_error = 0; 13 | 14 | static int failmsg(const char *fmt, ...); 15 | 16 | class PyAllowThreads; 17 | 18 | class PyEnsureGIL; 19 | 20 | #define ERRWRAP2(expr) \ 21 | try \ 22 | { \ 23 | PyAllowThreads allowThreads; \ 24 | expr; \ 25 | } \ 26 | catch (const cv::Exception &e) \ 27 | { \ 28 | PyErr_SetString(opencv_error, e.what()); \ 29 | return 0; \ 30 | } 31 | 32 | static PyObject* failmsgp(const char *fmt, ...); 33 | 34 | static size_t REFCOUNT_OFFSET = (size_t)&(((PyObject*)0)->ob_refcnt) + 35 | (0x12345678 != *(const size_t*)"\x78\x56\x34\x12\0\0\0\0\0")*sizeof(int); 36 | 37 | static inline PyObject* pyObjectFromRefcount(const int* refcount) 38 | { 39 | return (PyObject*)((size_t)refcount - REFCOUNT_OFFSET); 40 | } 41 | 42 | static inline int* refcountFromPyObject(const PyObject* obj) 43 | { 44 | return (int*)((size_t)obj + REFCOUNT_OFFSET); 45 | } 46 | 47 | 48 | class NumpyAllocator; 49 | 50 | enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 }; 51 | 52 | class NDArrayConverter 53 | { 54 | private: 55 | void init(); 56 | public: 57 | NDArrayConverter(); 58 | cv::Mat toMat(const PyObject* o); 59 | PyObject* toNDArray(const cv::Mat& mat); 60 | }; 61 | 62 | # endif 63 | -------------------------------------------------------------------------------- /utils/template.h: -------------------------------------------------------------------------------- 1 | // Author: Sudeep Pillai (spillai@csail.mit.edu) 2 | // Note: Stripped from pyxx project 3 | 4 | #ifndef PYXX_TEMPLATE_H 5 | #define PYXX_TEMPLATE_H 6 | 7 | #include 8 | /* 9 | * Provides template support 10 | */ 11 | 12 | template 13 | struct expose_template_type 14 | { 15 | // do nothing! 16 | }; 17 | 18 | template 19 | struct expose_template_type_base 20 | { 21 | bool wrapped() 22 | { 23 | using namespace boost::python::converter; 24 | using namespace boost::python; 25 | registration const * p = registry::query( type_id() ); 26 | return p && (p->m_class_object || p->m_to_python); 27 | } 28 | 29 | }; 30 | 31 | #endif 32 | --------------------------------------------------------------------------------