JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
PythonOpenCV.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 // This file is derived from https://github.com/Algomorph/pyboostcvconverter
19 
20 /*
21  * CV3BoostConverter.cpp
22  *
23  * Created on: May 21, 2015
24  * Author: Gregory Kramida
25  * Copyright: 2015 Gregory Kramida
26  The MIT License (MIT)
27 
28  Copyright (c) 2014 Gregory Kramida
29 
30  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
31  documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
32  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
33  persons to whom the Software is furnished to do so, subject to the following conditions:
34 
35  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
36  Software.
37 
38  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
39  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
40  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
41  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 
43 */
44 
45 #define NO_IMPORT_ARRAY
46 #define PY_ARRAY_UNIQUE_SYMBOL pbcvt_ARRAY_API
47 
49 
50 namespace pbcvt
51 {
52  PyObject * opencv_error = nullptr;
53 
54  using namespace cv;
55  //=================== ERROR HANDLING =========================================================
56  int failmsg(const char *fmt, ...) {
57  char str[1000];
58  va_list ap;
59  va_start(ap, fmt);
60  vsnprintf(str, sizeof(str), fmt, ap);
61  va_end(ap);
62  PyErr_SetString(PyExc_TypeError, str);
63  return 0;
64  }
65 
66  //=================== THREADING ==============================================================
68  public:
69  PyAllowThreads() : _state(PyEval_SaveThread())
70  { }
72  { PyEval_RestoreThread(_state); }
73  private:
74  PyThreadState* _state;
75  };
76 
77  class PyEnsureGIL {
78  public:
79  PyEnsureGIL() : _state(PyGILState_Ensure())
80  { }
82  { PyGILState_Release(_state); }
83  private:
84  PyGILState_STATE _state;
85  };
86 
87  enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 };
88 
89  class NumpyAllocator : public MatAllocator
90  {
91  public:
93  { stdAllocator = Mat::getStdAllocator(); }
95  { }
96 
97  UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const
98  {
99  UMatData* u = new UMatData(this);
100  u->data = u->origdata = (uchar*) PyArray_DATA((PyArrayObject*) o);
101  npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
102  for (int i = 0; i < dims - 1; i++) step[i] = (size_t) _strides[i];
103  step[dims - 1] = CV_ELEM_SIZE(type);
104  u->size = sizes[0] * step[0];
105  u->userdata = o;
106  return u;
107  }
108 
109  UMatData* allocate(int dims0, const int* sizes, int type, void* data,
110  size_t* step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const
111  {
112  if (data != nullptr)
113  {
114  CV_Error(Error::StsAssert, "The data should normally be NULL!");
115  // probably this is safe to do in such extreme case
116  return stdAllocator->allocate(dims0, sizes, type, data, step, flags, usageFlags);
117  }
118  PyEnsureGIL gil;
119 
120  int depth = CV_MAT_DEPTH(type);
121  int cn = CV_MAT_CN(type);
122  const int f = (int) (sizeof(size_t) / 8);
123  int typenum =
124  depth == CV_8U ? NPY_UBYTE :
125  depth == CV_8S ? NPY_BYTE :
126  depth == CV_16U ? NPY_USHORT :
127  depth == CV_16S ? NPY_SHORT :
128  depth == CV_32S ? NPY_INT :
129  depth == CV_32F ? NPY_FLOAT :
130  depth == CV_64F ? NPY_DOUBLE :
131  f * NPY_ULONGLONG + (f ^ 1) * NPY_UINT;
132  int i, dims = dims0;
133  cv::AutoBuffer<npy_intp> _sizes(dims + 1);
134  for (i = 0; i < dims; i++) _sizes[i] = sizes[i];
135  if (cn > 1) _sizes[dims++] = cn;
136  PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
137  if (!o) CV_Error_(Error::StsError,
138  ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
139  return allocate(o, dims0, sizes, type, step);
140  }
141 
142  bool allocate(UMatData* u, cv::AccessFlag accessFlags, cv::UMatUsageFlags usageFlags) const
143  { return stdAllocator->allocate(u, accessFlags, usageFlags); }
144 
145  void deallocate(UMatData* u) const
146  {
147  if (u)
148  {
149  PyEnsureGIL gil;
150  PyObject* o = (PyObject*) u->userdata;
151  Py_XDECREF(o);
152  delete u;
153  }
154  }
155 
156  const MatAllocator* stdAllocator;
157  };
158 
159  //=================== ALLOCATOR INITIALIZTION ==================================================
161 
162  //=================== STANDALONE CONVERTER FUNCTIONS =========================================
163 
164  PyObject* fromMatToNDArray(const Mat& m)
165  {
166  if (!m.data) Py_RETURN_NONE;
167  Mat temp, *p = (Mat*) &m;
168  if (!p->u || p->allocator != &g_numpyAllocator)
169  {
170  temp.allocator = &g_numpyAllocator;
171  ERRWRAP2(m.copyTo(temp));
172  p = &temp;
173  }
174  PyObject* o = (PyObject*) p->u->userdata;
175  Py_INCREF(o);
176  return o;
177  }
178 
179  Mat fromNDArrayToMat(PyObject* o)
180  {
181  cv::Mat m;
182  bool allowND = true;
183  if (!PyArray_Check(o))
184  {
185  failmsg("argument is not a numpy array");
186  if (!m.data) m.allocator = &g_numpyAllocator;
187  }
188  else
189  {
190  PyArrayObject* oarr = (PyArrayObject*) o;
191 
192  bool needcopy = false, needcast = false;
193  int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
194  int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S :
195  typenum == NPY_USHORT ? CV_16U :
196  typenum == NPY_SHORT ? CV_16S :
197  typenum == NPY_INT ? CV_32S :
198  typenum == NPY_INT32 ? CV_32S :
199  typenum == NPY_FLOAT ? CV_32F :
200  typenum == NPY_DOUBLE ? CV_64F : -1;
201 
202  if (type < 0) {
203  if (typenum == NPY_INT64 || typenum == NPY_UINT64 || type == NPY_LONG) {
204  needcopy = needcast = true;
205  new_typenum = NPY_INT;
206  type = CV_32S;
207  } else {
208  failmsg("Argument data type is not supported");
209  m.allocator = &g_numpyAllocator;
210  return m;
211  }
212  }
213 
214 #ifndef CV_MAX_DIM
215  const int CV_MAX_DIM = 32;
216 #endif
217 
218  int ndims = PyArray_NDIM(oarr);
219  if (ndims >= CV_MAX_DIM) {
220  failmsg("Dimensionality of argument is too high");
221  if (!m.data) m.allocator = &g_numpyAllocator;
222  return m;
223  }
224 
225  int size[CV_MAX_DIM + 1];
226  size_t step[CV_MAX_DIM + 1];
227  size_t elemsize = CV_ELEM_SIZE1(type);
228  const npy_intp* _sizes = PyArray_DIMS(oarr);
229  const npy_intp* _strides = PyArray_STRIDES(oarr);
230  bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX;
231 
232  for (int i = ndims - 1; i >= 0 && !needcopy; i--)
233  {
234  // these checks handle cases of
235  // a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases
236  // b) transposed arrays, where _strides[] elements go in non-descending order
237  // c) flipped arrays, where some of _strides[] elements are negative
238  if ((i == ndims - 1 && (size_t) _strides[i] != elemsize) || (i < ndims - 1 && _strides[i] < _strides[i + 1]))
239  needcopy = true;
240  }
241 
242  if (ismultichannel && _strides[1] != (npy_intp) elemsize * _sizes[2]) needcopy = true;
243 
244  if (needcopy)
245  {
246  if (needcast)
247  {
248  o = PyArray_Cast(oarr, new_typenum);
249  oarr = (PyArrayObject*) o;
250  }
251  else
252  {
253  oarr = PyArray_GETCONTIGUOUS(oarr);
254  o = (PyObject*) oarr;
255  }
256 
257  _strides = PyArray_STRIDES(oarr);
258  }
259 
260  for (int i = 0; i < ndims; i++) { size[i] = (int) _sizes[i]; step[i] = (size_t) _strides[i]; }
261 
262  // handle degenerate case
263  if (ndims == 0) { size[ndims] = 1; step[ndims] = elemsize; ndims++; }
264 
265  if (ismultichannel) { ndims--; type |= CV_MAKETYPE(0, size[2]); }
266 
267  if (ndims > 2 && !allowND) {
268  failmsg("%s has more than 2 dimensions");
269  } else {
270  m = Mat(ndims, size, type, PyArray_DATA(oarr), step);
271  m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);
272  m.addref();
273 
274  if (!needcopy) {
275  Py_INCREF(o);
276  }
277  }
278  m.allocator = &g_numpyAllocator;
279  }
280  return m;
281  }
282 
283  //=================== BOOST CONVERTERS =======================================================
284  PyObject* matToNDArrayBoostConverter::convert(Mat const& m) {
285  if (!m.data)
286  Py_RETURN_NONE;
287  Mat temp,
288  *p = (Mat*) &m;
289  if (!p->u || p->allocator != &g_numpyAllocator)
290  {
291  temp.allocator = &g_numpyAllocator;
292  ERRWRAP2(m.copyTo(temp));
293  p = &temp;
294  }
295  PyObject* o = (PyObject*) p->u->userdata;
296  Py_INCREF(o);
297  return o;
298  }
299 
301  {
302  boost::python::converter::registry::push_back(convertible, construct, boost::python::type_id<Mat>());
303  }
304 
305  /// @brief Check if PyObject is an array and can be converted to OpenCV matrix.
307  {
308  if (!PyArray_Check(object)) {
309  return NULL;
310  }
311 #ifndef CV_MAX_DIM
312  const int CV_MAX_DIM = 32;
313 #endif
314  PyArrayObject* oarr = (PyArrayObject*) object;
315 
316  int typenum = PyArray_TYPE(oarr);
317  if (typenum != NPY_INT64 && typenum != NPY_UINT64 && typenum != NPY_LONG
318  && typenum != NPY_UBYTE && typenum != NPY_BYTE
319  && typenum != NPY_USHORT && typenum != NPY_SHORT
320  && typenum != NPY_INT && typenum != NPY_INT32
321  && typenum != NPY_FLOAT && typenum != NPY_DOUBLE) {
322  return NULL;
323  }
324  int ndims = PyArray_NDIM(oarr); //data type not supported
325 
326  if (ndims >= CV_MAX_DIM) {
327  return NULL; //too many dimensions
328  }
329  return object;
330  }
331 
332  /// Construct a Mat from an NDArray object.
334  boost::python::converter::rvalue_from_python_stage1_data* data) {
335  namespace python = boost::python;
336  // Object is a borrowed reference, so create a handle indicting it is borrowed for proper reference counting.
337  python::handle<> handle(python::borrowed(object));
338 
339  // Obtain a handle to the memory block that the converter has allocated for the C++ type.
340  typedef python::converter::rvalue_from_python_storage<Mat> storage_type;
341  void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
342 
343  // Allocate the C++ type into the converter's memory block, and assign its handle to the converter's convertible
344  // variable. The C++ container is populated by passing the begin and end iterators of the python object to the
345  // container's constructor.
346  PyArrayObject* oarr = (PyArrayObject*) object;
347 
348  bool needcopy = false, needcast = false;
349  int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
350  int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S :
351  typenum == NPY_USHORT ? CV_16U :
352  typenum == NPY_SHORT ? CV_16S :
353  typenum == NPY_INT ? CV_32S :
354  typenum == NPY_INT32 ? CV_32S :
355  typenum == NPY_FLOAT ? CV_32F :
356  typenum == NPY_DOUBLE ? CV_64F : -1;
357 
358  if (type < 0) {
359  needcopy = needcast = true;
360  new_typenum = NPY_INT;
361  type = CV_32S;
362  }
363 
364 #ifndef CV_MAX_DIM
365  const int CV_MAX_DIM = 32;
366 #endif
367  int ndims = PyArray_NDIM(oarr);
368 
369  int size[CV_MAX_DIM + 1];
370  size_t step[CV_MAX_DIM + 1];
371  size_t elemsize = CV_ELEM_SIZE1(type);
372  const npy_intp* _sizes = PyArray_DIMS(oarr);
373  const npy_intp* _strides = PyArray_STRIDES(oarr);
374  bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX;
375 
376  for (int i = ndims - 1; i >= 0 && !needcopy; i--) {
377  // these checks handle cases of
378  // a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases
379  // b) transposed arrays, where _strides[] elements go in non-descending order
380  // c) flipped arrays, where some of _strides[] elements are negative
381  if ((i == ndims - 1 && (size_t) _strides[i] != elemsize)
382  || (i < ndims - 1 && _strides[i] < _strides[i + 1]))
383  needcopy = true;
384  }
385 
386  if (ismultichannel && _strides[1] != (npy_intp) elemsize * _sizes[2])
387  needcopy = true;
388 
389  if (needcopy) {
390 
391  if (needcast) {
392  object = PyArray_Cast(oarr, new_typenum);
393  oarr = (PyArrayObject*) object;
394  } else {
395  oarr = PyArray_GETCONTIGUOUS(oarr);
396  object = (PyObject*) oarr;
397  }
398 
399  _strides = PyArray_STRIDES(oarr);
400  }
401 
402  for (int i = 0; i < ndims; i++) {
403  size[i] = (int) _sizes[i];
404  step[i] = (size_t) _strides[i];
405  }
406 
407  // handle degenerate case
408  if (ndims == 0) {
409  size[ndims] = 1;
410  step[ndims] = elemsize;
411  ndims++;
412  }
413 
414  if (ismultichannel) {
415  ndims--;
416  type |= CV_MAKETYPE(0, size[2]);
417  }
418  if (!needcopy) {
419  Py_INCREF(object);
420  }
421 
422  cv::Mat* m = new (storage) cv::Mat(ndims, size, type, PyArray_DATA(oarr), step);
423  m->u = g_numpyAllocator.allocate(object, ndims, size, type, step);
424  m->allocator = &g_numpyAllocator;
425  m->addref();
426  data->convertible = storage;
427  }
428 
429 } //end namespace pbcvt
ERRWRAP2
#define ERRWRAP2(expr)
Definition: PythonOpenCV.H:64
pbcvt::matFromNDArrayBoostConverter::convertible
static void * convertible(PyObject *object)
Check if PyObject is an array and can be converted to OpenCV matrix.
Definition: PythonOpenCV.C:306
pbcvt::NumpyAllocator::deallocate
void deallocate(UMatData *u) const
Definition: PythonOpenCV.C:145
pbcvt::fromMatToNDArray
PyObject * fromMatToNDArray(const Mat &m)
Definition: PythonOpenCV.C:164
pbcvt::PyEnsureGIL
Definition: PythonOpenCV.C:77
pbcvt::PyAllowThreads
Definition: PythonOpenCV.C:67
pbcvt::PyAllowThreads::~PyAllowThreads
~PyAllowThreads()
Definition: PythonOpenCV.C:71
pbcvt::ARG_SCALAR
@ ARG_SCALAR
Definition: PythonOpenCV.C:87
pbcvt::PyAllowThreads::PyAllowThreads
PyAllowThreads()
Definition: PythonOpenCV.C:69
pbcvt::matFromNDArrayBoostConverter::matFromNDArrayBoostConverter
matFromNDArrayBoostConverter()
Definition: PythonOpenCV.C:300
o
#define o
Definition: Font10x20.C:6
pbcvt::NumpyAllocator::NumpyAllocator
NumpyAllocator()
Definition: PythonOpenCV.C:92
pbcvt::g_numpyAllocator
NumpyAllocator g_numpyAllocator
Definition: PythonOpenCV.C:160
pbcvt::NumpyAllocator::allocate
bool allocate(UMatData *u, cv::AccessFlag accessFlags, cv::UMatUsageFlags usageFlags) const
Definition: PythonOpenCV.C:142
pbcvt::matFromNDArrayBoostConverter::construct
static void construct(PyObject *object, boost::python::converter::rvalue_from_python_stage1_data *data)
Construct a Mat from an NDArray object.
Definition: PythonOpenCV.C:333
pbcvt::NumpyAllocator::~NumpyAllocator
~NumpyAllocator()
Definition: PythonOpenCV.C:94
pbcvt::PyEnsureGIL::PyEnsureGIL
PyEnsureGIL()
Definition: PythonOpenCV.C:79
pbcvt::matToNDArrayBoostConverter::convert
static PyObject * convert(Mat const &m)
Definition: PythonOpenCV.C:284
pbcvt::fromNDArrayToMat
Mat fromNDArrayToMat(PyObject *o)
Definition: PythonOpenCV.C:179
pbcvt::opencv_error
PyObject * opencv_error
Definition: PythonOpenCV.C:52
pbcvt::NumpyAllocator
Definition: PythonOpenCV.C:89
pbcvt
Definition: PythonOpenCV.H:56
pbcvt::ARG_NONE
@ ARG_NONE
Definition: PythonOpenCV.C:87
pbcvt::NumpyAllocator::allocate
UMatData * allocate(int dims0, const int *sizes, int type, void *data, size_t *step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const
Definition: PythonOpenCV.C:109
pbcvt::ARG_MAT
@ ARG_MAT
Definition: PythonOpenCV.C:87
pbcvt::NumpyAllocator::stdAllocator
const MatAllocator * stdAllocator
Definition: PythonOpenCV.C:156
PythonOpenCV.H
pbcvt::PyEnsureGIL::~PyEnsureGIL
~PyEnsureGIL()
Definition: PythonOpenCV.C:81
pbcvt::failmsg
int failmsg(const char *fmt,...)
Definition: PythonOpenCV.C:56
pbcvt::NumpyAllocator::allocate
UMatData * allocate(PyObject *o, int dims, const int *sizes, int type, size_t *step) const
Definition: PythonOpenCV.C:97