MIRA
ImageWrapper.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Redistribution and modification of this code is strictly prohibited.
9  *
10  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
11  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
12  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
13  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
16  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
18  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
19  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
20  */
21 
30 #ifndef _MIRA_TOOLBOXES_PYTHON_IMAGEWRAPPER_H_
31 #define _MIRA_TOOLBOXES_PYTHON_IMAGEWRAPPER_H_
32 
33 #define BOOST_HAS_NUMPY_API (BOOST_VERSION >= 106300)
34 
36 
37 #if BOOST_HAS_NUMPY_API
38 #include <boost/python/numpy.hpp>
39 #else
40 #include <boost/python/numeric.hpp>
41 #endif
42 
43 #include <image/Img.h>
44 
45 #if BOOST_HAS_NUMPY_API
46 #else
47 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
48 #endif
49 
50 #include <numpy/ndarrayobject.h>
51 
52 
53 namespace mira {
54 namespace python {
55 
57 
58 #if BOOST_HAS_NUMPY_API
59 
67  static boost::python::numpy::dtype getNumpyDtype(int cvDtype) {
68  namespace np = boost::python::numpy;
69  switch (cvDtype) {
70  case CV_8U:
71  return np::dtype::get_builtin<uint8>();
72  case CV_8S:
73  return np::dtype::get_builtin<int8>();
74  case CV_16U:
75  return np::dtype::get_builtin<uint16>();
76  case CV_16S:
77  return np::dtype::get_builtin<int16>();
78  case CV_32S:
79  return np::dtype::get_builtin<int32>();
80  case CV_32F:
81  return np::dtype::get_builtin<float>();
82  case CV_64F:
83  return np::dtype::get_builtin<double>();
84  default:
85  return np::dtype::get_builtin<char>();
86  }
87  }
88 
89  static int getCvDtype(boost::python::numpy::dtype numpyDtype) {
90  namespace np = boost::python::numpy;
91  int cvDtype = -1;
92  if (numpyDtype == np::dtype::get_builtin<uint8>()) {
93  cvDtype = CV_8U;
94  } else if (numpyDtype == np::dtype::get_builtin<int8>()) {
95  cvDtype = CV_8S;
96  } else if (numpyDtype == np::dtype::get_builtin<uint16>()) {
97  cvDtype = CV_16U;
98  } else if (numpyDtype == np::dtype::get_builtin<int16>()) {
99  cvDtype = CV_16S;
100  } else if (numpyDtype == np::dtype::get_builtin<int32>()) {
101  cvDtype = CV_32S;
102  } else if (numpyDtype == np::dtype::get_builtin<float>()) {
103  cvDtype = CV_32F;
104  } else if (numpyDtype == np::dtype::get_builtin<double>()) {
105  cvDtype = CV_64F;
106  } else {
107  MIRA_THROW(XLogical, "Cannot map given dtype from NumPy to OpenCV");
108  }
109  return cvDtype;
110  }
111 
112 #else
113 
120  static int getNumpyDtype(int cvDtype)
121  {
122  int numpyDtype = -1;
123  switch(cvDtype)
124  {
125  case CV_8U:
126  numpyDtype = NPY_UBYTE;
127  break;
128  case CV_8S:
129  numpyDtype = NPY_BYTE;
130  break;
131  case CV_16U:
132  numpyDtype = NPY_USHORT;
133  break;
134  case CV_16S:
135  numpyDtype = NPY_SHORT;
136  break;
137  case CV_32S:
138  numpyDtype = NPY_INT;
139  break;
140  case CV_32F:
141  numpyDtype = NPY_FLOAT;
142  break;
143  case CV_64F:
144  numpyDtype = NPY_DOUBLE;
145  break;
146  default:
147  MIRA_THROW(XLogical, "Cannot convert CV dtype to NumPy dtype!");
148  }
149  return numpyDtype;
150  }
151 
152  static int getCvDtype(int numpyDtype)
153  {
154  int cvDtype = -1;
155  switch(numpyDtype) {
156  case NPY_UBYTE:
157  cvDtype = CV_8U;
158  break;
159  case NPY_BYTE:
160  cvDtype = CV_8S;
161  break;
162  case NPY_USHORT:
163  cvDtype = CV_16U;
164  break;
165  case NPY_SHORT:
166  cvDtype = CV_16S;
167  break;
168  case NPY_INT:
169  cvDtype = CV_32S;
170  break;
171  case NPY_FLOAT:
172  cvDtype = CV_32F;
173  break;
174  case NPY_DOUBLE:
175  cvDtype = CV_64F;
176  break;
177  default:
178  cvDtype = -1;
179  }
180  return cvDtype;
181  }
182 #endif
183 
184 #if BOOST_HAS_NUMPY_API
185 
186  static int getCVByteDepth(int cvDtype) {
187  switch (cvDtype) {
188  case CV_8U:
189  case CV_8S:
190  return 1;
191  case CV_16U:
192  case CV_16S:
193  return 2;
194  case CV_32S:
195  case CV_32F:
196  return 4;
197  case CV_64F:
198  return 8;
199  default: MIRA_THROW(XLogical, "Cannot convert CV dtype to byte depth!");
200  }
201  return 0;
202  }
203 
204 #endif
205 
207  template<typename T>
209  };
210 
211  template<typename TPixel, int TChannels>
212  struct ImageAccessorBase<mira::Img<TPixel, TChannels> > {
213 
218  static boost::python::object getMat(const mira::Img<TPixel, TChannels> &image) {
219  using namespace boost::python;
220 
221  if (image.isEmpty()) {
222  return boost::python::object();
223  }
224 
225  cv::Mat mat = image.getMat();
226 #if BOOST_HAS_NUMPY_API
227  // generate array description
228  int nBytes = getCVByteDepth(image.depth());
229  numpy::dtype dtype = getNumpyDtype(image.depth());
230  tuple shape = make_tuple(image.height(), image.width(), image.channels());
231  tuple stride = make_tuple(image.width() * image.channels() * nBytes,
232  image.channels() * nBytes, nBytes);
233  if (mat.isContinuous()) {
234  return numpy::from_data((TPixel *) mat.data, dtype,
235  shape, stride, object());//.copy();
236  } else {
237  cv::Mat clone = mat.clone();
238  return numpy::from_data((TPixel *) clone.data, dtype,
239  shape, stride, object());//.copy();
240  }
241 #else
242  npy_intp size[3] = {image.height(), image.width(), image.channels()};
243  // create PyObject
244  PyObject *pyObj;
245  if(mat.isContinuous()) {
246  pyObj = PyArray_SimpleNewFromData(
247  3, &size[0], getNumpyDtype(image.depth()), (TPixel*)mat.data
248  );
249  }
250  else {
251  cv::Mat clone = mat.clone();
252  pyObj = PyArray_SimpleNewFromData(
253  3, &size[0], getNumpyDtype(image.depth()), (TPixel*)clone.data
254  );
255  }
256  handle<> handle(pyObj);
257  return numeric::array(handle);
258 #endif
259  }
260 
261 #if BOOST_HAS_NUMPY_API
262 
263  static cv::Mat arrayToMat(const boost::python::numpy::ndarray &arr)
264 #else
265  static cv::Mat arrayToMat(const boost::python::numeric::array& arr)
266 #endif
267  {
268  // This can probably also be moved to the old API branch
269  PyObject * pyObj = arr.ptr();
270  if (!PyArray_Check(pyObj)) MIRA_THROW(XRuntime, "Not an array object");
271 
272 #if BOOST_HAS_NUMPY_API
273  int ndims = arr.get_nd();
274  assert(ndims >= 2);
275 
276  int size[2] = {(int) arr.get_shape()[0], (int) arr.get_shape()[1]};
277  size_t step[2] = {(size_t) arr.get_strides()[0], (size_t) arr.get_strides()[1]};
278 
279  int type = getCvDtype(arr.get_dtype());
280  // for multidim arrays create multichannel img with just 2 dims but 'special' type
281  if (ndims > 2) {
282  ndims = 2;
283  type = CV_MAKETYPE(type, arr.get_shape()[2]);
284  }
285 
286  void *data = (void *) arr.get_data();
287 #else
288  // there is probably a better way but I did not find it
289  PyArrayObject* arrayObj = reinterpret_cast<PyArrayObject*>(pyObj);
290 
291  int ndims = PyArray_NDIM(arrayObj);
292  assert(ndims >= 2);
293 
294  int size[2];
295  size[0] = PyArray_DIMS(arrayObj)[0];
296  size[1] = PyArray_DIMS(arrayObj)[1];
297 
298  size_t step[2];
299  step[0] = PyArray_STRIDES(arrayObj)[0];
300  step[1] = PyArray_STRIDES(arrayObj)[1];
301 
302  int type = getCvDtype(PyArray_TYPE(arrayObj));
303  // for multidim arrays create multichannel img with just 2 dims but 'special' type
304  if (ndims > 2) {
305  ndims = 2;
306  type = CV_MAKETYPE(type, PyArray_DIMS(arrayObj)[2]);
307  }
308 
309  void* data = PyArray_DATA(arrayObj);
310 #endif
311 
312  // need to clone(), otherwise we just wrap arr's memory
313  return cv::Mat(ndims, size, type, data, step).clone();
314  }
315  };
316 
318  template<typename T>
319  struct ImageAccessor {
320  };
321 
322  // need to specialize differently for typed and untyped image
323  // as the way to convert from cv::Mat is different
324  template<typename TPixel, int TChannels>
325  struct ImageAccessor<mira::Img<TPixel, TChannels> >
326  : public ImageAccessorBase<mira::Img<TPixel, TChannels> > {
328 
333 #if BOOST_HAS_NUMPY_API
334  static void setMat(ImgType &image, const boost::python::numpy::ndarray &arr)
335 #else
336  static void setMat(ImgType& image, const boost::python::numeric::array& arr)
337 #endif
338  {
339  image = ImgType::convertFrom(ImageAccessorBase<ImgType>::arrayToMat(arr));
340  }
341  };
342 
343  template<>
344  struct ImageAccessor<mira::Img<void, 1> >
345  : public ImageAccessorBase<mira::Img<void, 1> > {
346 
348 
353 #if BOOST_HAS_NUMPY_API
354  static void setMat(ImgType &image, const boost::python::numpy::ndarray &arr)
355 #else
356  static void setMat(ImgType& image, const boost::python::numeric::array& arr)
357 #endif
358  {
360  }
361  };
362 
363 }} // namespaces
364 
365 #endif //_MIRA_TOOLBOXES_PYTHON_IMAGEWRAPPER_H_
bool isEmpty() const
static void setMat(ImgType &image, const boost::python::numeric::array &arr)
Set image matrix from numpy.ndarray.
Definition: ImageWrapper.h:336
mira::Img< void, 1 > ImgType
Definition: ImageWrapper.h:347
int channels() const
Dummy image accessor base struct.
Definition: ImageWrapper.h:208
#define MIRA_THROW(ex, msg)
int depth() const
PropertyHint type(const std::string &t)
static boost::python::object getMat(const mira::Img< TPixel, TChannels > &image)
Get image matrix as numpy.ndarray.
Definition: ImageWrapper.h:218
int height() const
int width() const
Include this instead of boost/python.hpp to reduce compile time warning spam from Boost internal inco...
const cv::Mat & getMat() const
PropertyHint step(const T &step)
static cv::Mat arrayToMat(const boost::python::numeric::array &arr)
Definition: ImageWrapper.h:265
static int getNumpyDtype(int cvDtype)
Return numerical numpy dtype identifier corresponding to a numerical dtype from OpenCV.
Definition: ImageWrapper.h:120
static int getCvDtype(int numpyDtype)
Definition: ImageWrapper.h:152
static void setMat(ImgType &image, const boost::python::numeric::array &arr)
Set image matrix from numpy.ndarray.
Definition: ImageWrapper.h:356
typedef Mat
mira::Img< TPixel, TChannels > ImgType
Definition: ImageWrapper.h:327
Dummy image accessor struct.
Definition: ImageWrapper.h:319