JeVois  1.22
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
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
50namespace 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 ==============================================================
67 class PyAllowThreads {
68 public:
69 PyAllowThreads() : _state(PyEval_SaveThread())
70 { }
71 ~PyAllowThreads()
72 { PyEval_RestoreThread(_state); }
73 private:
74 PyThreadState* _state;
75 };
76
77 class PyEnsureGIL {
78 public:
79 PyEnsureGIL() : _state(PyGILState_Ensure())
80 { }
81 ~PyEnsureGIL()
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:
92 NumpyAllocator()
93 { stdAllocator = Mat::getStdAllocator(); }
94 ~NumpyAllocator()
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 ==================================================
160 NumpyAllocator g_numpyAllocator;
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
300 matFromNDArrayBoostConverter::matFromNDArrayBoostConverter()
301 {
302 boost::python::converter::registry::push_back(convertible, construct, boost::python::type_id<Mat>());
303 }
304
305 // Check if PyObject is an array and can be converted to OpenCV matrix.
306 void* matFromNDArrayBoostConverter::convertible(PyObject* object)
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.
333 void matFromNDArrayBoostConverter::construct(PyObject* 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
#define o
Definition Font10x20.C:6
#define ERRWRAP2(expr)
Definition Engine.H:120