[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

numpy_array.hxx
1 /************************************************************************/
2 /* */
3 /* Copyright 2009 by Ullrich Koethe and Hans Meine */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_NUMPY_ARRAY_HXX
37 #define VIGRA_NUMPY_ARRAY_HXX
38 
39 #include <Python.h>
40 #include <iostream>
41 #include <algorithm>
42 #include <complex>
43 #include <string>
44 #include <sstream>
45 #include <map>
46 #include <vigra/multi_array.hxx>
47 #include <vigra/array_vector.hxx>
48 #include <vigra/sized_int.hxx>
49 #include <vigra/python_utility.hxx>
50 #include <numpy/arrayobject.h>
51 
52 int _import_array();
53 
54 namespace vigra {
55 
56 /********************************************************/
57 /* */
58 /* Singleband and Multiband */
59 /* */
60 /********************************************************/
61 
62 typedef float NumpyValueType;
63 
64 template <class T>
65 struct Singleband // the last array dimension is not to be interpreted as a channel dimension
66 {
67  typedef T value_type;
68 };
69 
70 template <class T>
71 struct Multiband // the last array dimension is a channel dimension
72 {
73  typedef T value_type;
74 };
75 
76 template<class T>
77 struct NumericTraits<Singleband<T> >
78 : public NumericTraits<T>
79 {};
80 
81 template<class T>
82 struct NumericTraits<Multiband<T> >
83 {
84  typedef Multiband<T> Type;
85 /*
86  typedef int Promote;
87  typedef unsigned int UnsignedPromote;
88  typedef double RealPromote;
89  typedef std::complex<RealPromote> ComplexPromote;
90 */
91  typedef Type ValueType;
92 
93  typedef typename NumericTraits<T>::isIntegral isIntegral;
94  typedef VigraFalseType isScalar;
95  typedef typename NumericTraits<T>::isSigned isSigned;
96  typedef typename NumericTraits<T>::isSigned isOrdered;
97  typedef typename NumericTraits<T>::isSigned isComplex;
98 /*
99  static signed char zero() { return 0; }
100  static signed char one() { return 1; }
101  static signed char nonZero() { return 1; }
102  static signed char min() { return SCHAR_MIN; }
103  static signed char max() { return SCHAR_MAX; }
104 
105 #ifdef NO_INLINE_STATIC_CONST_DEFINITION
106  enum { minConst = SCHAR_MIN, maxConst = SCHAR_MIN };
107 #else
108  static const signed char minConst = SCHAR_MIN;
109  static const signed char maxConst = SCHAR_MIN;
110 #endif
111 
112  static Promote toPromote(signed char v) { return v; }
113  static RealPromote toRealPromote(signed char v) { return v; }
114  static signed char fromPromote(Promote v) {
115  return ((v < SCHAR_MIN) ? SCHAR_MIN : (v > SCHAR_MAX) ? SCHAR_MAX : v);
116  }
117  static signed char fromRealPromote(RealPromote v) {
118  return ((v < 0.0)
119  ? ((v < (RealPromote)SCHAR_MIN)
120  ? SCHAR_MIN
121  : static_cast<signed char>(v - 0.5))
122  : (v > (RealPromote)SCHAR_MAX)
123  ? SCHAR_MAX
124  : static_cast<signed char>(v + 0.5));
125  }
126 */
127 };
128 
129 template <class T>
130 class MultibandVectorAccessor
131 {
132  MultiArrayIndex size_, stride_;
133 
134  public:
135  MultibandVectorAccessor(MultiArrayIndex size, MultiArrayIndex stride)
136  : size_(size),
137  stride_(stride)
138  {}
139 
140 
141  typedef Multiband<T> value_type;
142 
143  /** the vector's value_type
144  */
145  typedef T component_type;
146 
147  typedef VectorElementAccessor<MultibandVectorAccessor<T> > ElementAccessor;
148 
149  /** Read the component data at given vector index
150  at given iterator position
151  */
152  template <class ITERATOR>
153  component_type const & getComponent(ITERATOR const & i, int idx) const
154  {
155  return *(&*i+idx*stride_);
156  }
157 
158  /** Set the component data at given vector index
159  at given iterator position. The type <TT>V</TT> of the passed
160  in <TT>value</TT> is automatically converted to <TT>component_type</TT>.
161  In case of a conversion floating point -> intergral this includes rounding and clipping.
162  */
163  template <class V, class ITERATOR>
164  void setComponent(V const & value, ITERATOR const & i, int idx) const
165  {
166  *(&*i+idx*stride_) = detail::RequiresExplicitCast<component_type>::cast(value);
167  }
168 
169  /** Read the component data at given vector index
170  at an offset of given iterator position
171  */
172  template <class ITERATOR, class DIFFERENCE>
173  component_type const & getComponent(ITERATOR const & i, DIFFERENCE const & diff, int idx) const
174  {
175  return *(&i[diff]+idx*stride_);
176  }
177 
178  /** Set the component data at given vector index
179  at an offset of given iterator position. The type <TT>V</TT> of the passed
180  in <TT>value</TT> is automatically converted to <TT>component_type</TT>.
181  In case of a conversion floating point -> intergral this includes rounding and clipping.
182  */
183  template <class V, class ITERATOR, class DIFFERENCE>
184  void
185  setComponent(V const & value, ITERATOR const & i, DIFFERENCE const & diff, int idx) const
186  {
187  *(&i[diff]+idx*stride_) = detail::RequiresExplicitCast<component_type>::cast(value);
188  }
189 
190  template <class U>
191  MultiArrayIndex size(U) const
192  {
193  return size_;
194  }
195 };
196 
197 /********************************************************/
198 /* */
199 /* a few Python utilities */
200 /* */
201 /********************************************************/
202 
203 namespace detail {
204 
205 inline long spatialDimensions(PyObject * obj)
206 {
207  static python_ptr key(PyString_FromString("spatialDimensions"), python_ptr::keep_count);
208  python_ptr pres(PyObject_GetAttr(obj, key), python_ptr::keep_count);
209  long res = pres && PyInt_Check(pres)
210  ? PyInt_AsLong(pres)
211  : -1;
212  return res;
213 }
214 
215 /*
216  * The registry is used to optionally map specific C++ types to
217  * specific python sub-classes of numpy.ndarray (for example,
218  * MultiArray<2, Singleband<int> > to a user-defined Python class 'ScalarImage').
219  *
220  * One needs to use NUMPY_ARRAY_INITIALIZE_REGISTRY once in a python
221  * extension module using this technique, in order to actually provide
222  * the registry (this is done by vigranumpycmodule and will then be
223  * available for other modules, too). Alternatively,
224  * NUMPY_ARRAY_DUMMY_REGISTRY may be used to disable this feature
225  * completely. In both cases, the macro must not be enclosed by any
226  * namespace, so it is best put right at the beginning of the file
227  * (e.g. below the #includes).
228  */
229 
230 typedef std::map<std::string, std::pair<python_ptr, python_ptr> > ArrayTypeMap;
231 
232 VIGRA_EXPORT ArrayTypeMap * getArrayTypeMap();
233 
234 #define NUMPY_ARRAY_INITIALIZE_REGISTRY \
235  namespace vigra { namespace detail { \
236  ArrayTypeMap * getArrayTypeMap() \
237  { \
238  static ArrayTypeMap arrayTypeMap; \
239  return &arrayTypeMap; \
240  } \
241  }} // namespace vigra::detail
242 
243 #define NUMPY_ARRAY_DUMMY_REGISTRY \
244  namespace vigra { namespace detail { \
245  ArrayTypeMap * getArrayTypeMap() \
246  { \
247  return NULL; \
248  } \
249  }} // namespace vigra::detail
250 
251 inline
252 void registerPythonArrayType(std::string const & name, PyObject * obj, PyObject * typecheck)
253 {
254  ArrayTypeMap *types = getArrayTypeMap();
255  vigra_precondition(
256  types != NULL,
257  "registerPythonArrayType(): module was compiled without array type registry.");
258  vigra_precondition(
259  obj && PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, &PyArray_Type),
260  "registerPythonArrayType(obj): obj is not a subtype of numpy.ndarray.");
261  if(typecheck && PyCallable_Check(typecheck))
262  (*types)[name] = std::make_pair(python_ptr(obj), python_ptr(typecheck));
263  else
264  (*types)[name] = std::make_pair(python_ptr(obj), python_ptr());
265 // std::cerr << "Registering " << ((PyTypeObject *)obj)->tp_name << " for " << name << "\n";
266 }
267 
268 inline
269 python_ptr getArrayTypeObject(std::string const & name, PyTypeObject * def = 0)
270 {
271  ArrayTypeMap *types = getArrayTypeMap();
272  if(!types)
273  // dummy registry -> handle like empty registry
274  return python_ptr((PyObject *)def);
275 
276  python_ptr res;
277  ArrayTypeMap::iterator i = types->find(name);
278  if(i != types->end())
279  res = i->second.first;
280  else
281  res = python_ptr((PyObject *)def);
282 // std::cerr << "Requested " << name << ", got " << ((PyTypeObject *)res.get())->tp_name << "\n";
283  return res;
284 }
285 
286 // there are two cases for the return:
287 // * if a typecheck function was registered, it is returned
288 // * a null pointer is returned if nothing was registered for either key, or if
289 // a type was registered without typecheck function
290 inline python_ptr
291 getArrayTypecheckFunction(std::string const & keyFull, std::string const & key)
292 {
293  python_ptr res;
294  ArrayTypeMap *types = getArrayTypeMap();
295  if(types)
296  {
297  ArrayTypeMap::iterator i = types->find(keyFull);
298  if(i == types->end())
299  i = types->find(key);
300  if(i != types->end())
301  res = i->second.second;
302  }
303  return res;
304 }
305 
306 inline bool
307 performCustomizedArrayTypecheck(PyObject * obj, std::string const & keyFull, std::string const & key)
308 {
309  if(obj == 0 || !PyArray_Check(obj))
310  return false;
311  python_ptr typecheck = getArrayTypecheckFunction(keyFull, key);
312  if(typecheck == 0)
313  return true; // no custom test registered
314  python_ptr args(PyTuple_Pack(1, obj), python_ptr::keep_count);
315  pythonToCppException(args);
316  python_ptr res(PyObject_Call(typecheck.get(), args.get(), 0), python_ptr::keep_count);
317  pythonToCppException(res);
318  vigra_precondition(PyBool_Check(res),
319  "NumpyArray conversion: registered typecheck function did not return a boolean.");
320  return (void*)res.get() == (void*)Py_True;
321 }
322 
323 inline
324 python_ptr constructNumpyArrayImpl(
325  PyTypeObject * type,
326  ArrayVector<npy_intp> const & shape, npy_intp *strides,
327  NPY_TYPES typeCode, bool init)
328 {
329  python_ptr array;
330 
331  if(strides == 0)
332  {
333  array = python_ptr(PyArray_New(type, shape.size(), (npy_intp *)shape.begin(), typeCode, 0, 0, 0, 1 /* Fortran order */, 0),
334  python_ptr::keep_count);
335  }
336  else
337  {
338  int N = shape.size();
339  ArrayVector<npy_intp> pshape(N);
340  for(int k=0; k<N; ++k)
341  pshape[strides[k]] = shape[k];
342 
343  array = python_ptr(PyArray_New(type, N, pshape.begin(), typeCode, 0, 0, 0, 1 /* Fortran order */, 0),
344  python_ptr::keep_count);
345  pythonToCppException(array);
346 
347  PyArray_Dims permute = { strides, N };
348  array = python_ptr(PyArray_Transpose((PyArrayObject*)array.get(), &permute), python_ptr::keep_count);
349  }
350  pythonToCppException(array);
351 
352  if(init)
353  PyArray_FILLWBYTE((PyArrayObject *)array.get(), 0);
354 
355  return array;
356 }
357 
358 // strideOrdering will be ignored unless order == "A"
359 // TODO: this function should receive some refactoring in order to make
360 // the rules clear from the code rather than from comments
361 inline python_ptr
362 constructNumpyArrayImpl(PyTypeObject * type, ArrayVector<npy_intp> const & shape,
363  unsigned int spatialDimensions, unsigned int channels,
364  NPY_TYPES typeCode, std::string order, bool init,
365  ArrayVector<npy_intp> strideOrdering = ArrayVector<npy_intp>())
366 {
367  // shape must have at least length spatialDimensions, but can also have a channel dimension
368  vigra_precondition(shape.size() == spatialDimensions || shape.size() == spatialDimensions + 1,
369  "constructNumpyArray(type, shape, ...): shape has wrong length.");
370 
371  // if strideOrdering is given, it must have at least length spatialDimensions,
372  // but can also have a channel dimension
373  vigra_precondition(strideOrdering.size() == 0 || strideOrdering.size() == spatialDimensions ||
374  strideOrdering.size() == spatialDimensions + 1,
375  "constructNumpyArray(type, ..., strideOrdering): strideOrdering has wrong length.");
376 
377  if(channels == 0) // if the requested number of channels is not given ...
378  {
379  // ... deduce it
380  if(shape.size() == spatialDimensions)
381  channels = 1;
382  else
383  channels = shape.back();
384  }
385  else
386  {
387  // otherwise, if the shape object also contains a channel dimension, they must be consistent
388  if(shape.size() > spatialDimensions)
389  vigra_precondition(channels == (unsigned int)shape[spatialDimensions],
390  "constructNumpyArray(type, ...): shape contradicts requested number of channels.");
391  }
392 
393  // if we have only one channel, no explicit channel dimension should be in the shape
394  unsigned int shapeSize = channels == 1
395  ? spatialDimensions
396  : spatialDimensions + 1;
397 
398  // create the shape object with optional channel dimension
399  ArrayVector<npy_intp> pshape(shapeSize);
400  std::copy(shape.begin(), shape.begin()+std::min(shape.size(), pshape.size()), pshape.begin());
401  if(shapeSize > spatialDimensions)
402  pshape[spatialDimensions] = channels;
403 
404  // order "A" means "preserve order" when an array is copied, and
405  // defaults to "V" when a new array is created without explicit strideOrdering
406  //
407  if(order == "A")
408  {
409  if(strideOrdering.size() == 0)
410  {
411  order = "V";
412  }
413  else if(strideOrdering.size() > shapeSize)
414  {
415  // make sure that strideOrdering length matches shape length
416  ArrayVector<npy_intp> pstride(strideOrdering.begin(), strideOrdering.begin()+shapeSize);
417 
418  // adjust the ordering when the channel dimension has been dropped because channel == 1
419  if(strideOrdering[shapeSize] == 0)
420  for(unsigned int k=0; k<shapeSize; ++k)
421  pstride[k] -= 1;
422  pstride.swap(strideOrdering);
423  }
424  else if(strideOrdering.size() < shapeSize)
425  {
426  // make sure that strideOrdering length matches shape length
427  ArrayVector<npy_intp> pstride(shapeSize);
428 
429  // adjust the ordering when the channel dimension has been dropped because channel == 1
430  for(unsigned int k=0; k<shapeSize-1; ++k)
431  pstride[k] = strideOrdering[k] + 1;
432  pstride[shapeSize-1] = 0;
433  pstride.swap(strideOrdering);
434  }
435  }
436 
437  // create the appropriate strideOrdering objects for the other memory orders
438  // (when strideOrdering already contained data, it is ignored because order != "A")
439  if(order == "C")
440  {
441  strideOrdering.resize(shapeSize);
442  for(unsigned int k=0; k<shapeSize; ++k)
443  strideOrdering[k] = shapeSize-1-k;
444  }
445  else if(order == "F" || (order == "V" && channels == 1))
446  {
447  strideOrdering.resize(shapeSize);
448  for(unsigned int k=0; k<shapeSize; ++k)
449  strideOrdering[k] = k;
450  }
451  else if(order == "V")
452  {
453  strideOrdering.resize(shapeSize);
454  for(unsigned int k=0; k<shapeSize-1; ++k)
455  strideOrdering[k] = k+1;
456  strideOrdering[shapeSize-1] = 0;
457  }
458 
459  return constructNumpyArrayImpl(type, pshape, strideOrdering.begin(), typeCode, init);
460 }
461 
462 template <class TINY_VECTOR>
463 inline
464 python_ptr constructNumpyArrayFromData(
465  std::string const & typeKeyFull,
466  std::string const & typeKey,
467  TINY_VECTOR const & shape, npy_intp *strides,
468  NPY_TYPES typeCode, void *data)
469 {
470  ArrayVector<npy_intp> pyShape(shape.begin(), shape.end());
471 
472  python_ptr type = detail::getArrayTypeObject(typeKeyFull);
473  if(type == 0)
474  type = detail::getArrayTypeObject(typeKey, &PyArray_Type);
475 
476  python_ptr array(PyArray_New((PyTypeObject *)type.ptr(), shape.size(), pyShape.begin(), typeCode, strides, data, 0, NPY_WRITEABLE, 0),
477  python_ptr::keep_count);
478  pythonToCppException(array);
479 
480  return array;
481 }
482 
483 
484 } // namespace detail
485 
486 /********************************************************/
487 /* */
488 /* NumpyArrayValuetypeTraits */
489 /* */
490 /********************************************************/
491 
492 template<class ValueType>
493 struct ERROR_NumpyArrayValuetypeTraits_not_specialized_for_ { };
494 
495 template<class ValueType>
496 struct NumpyArrayValuetypeTraits
497 {
498  static bool isValuetypeCompatible(PyArrayObject const * obj)
499  {
500  return ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType>();
501  }
502 
503  static ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> typeCode;
504 
505  static std::string typeName()
506  {
507  return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
508  }
509 
510  static std::string typeNameImpex()
511  {
512  return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
513  }
514 
515  static PyObject * typeObject()
516  {
517  return (PyObject *)0;
518  }
519 };
520 
521 template<class ValueType>
522 ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> NumpyArrayValuetypeTraits<ValueType>::typeCode;
523 
524 #define VIGRA_NUMPY_VALUETYPE_TRAITS(type, typeID, numpyTypeName, impexTypeName) \
525 template <> \
526 struct NumpyArrayValuetypeTraits<type > \
527 { \
528  static bool isValuetypeCompatible(PyArrayObject const * obj) /* obj must not be NULL */ \
529  { \
530  return PyArray_EquivTypenums(typeID, PyArray_DESCR((PyObject *)obj)->type_num) && \
531  PyArray_ITEMSIZE((PyObject *)obj) == sizeof(type); \
532  } \
533  \
534  static NPY_TYPES const typeCode = typeID; \
535  \
536  static std::string typeName() \
537  { \
538  return #numpyTypeName; \
539  } \
540  \
541  static std::string typeNameImpex() \
542  { \
543  return impexTypeName; \
544  } \
545  \
546  static PyObject * typeObject() \
547  { \
548  return PyArray_TypeObjectFromType(typeID); \
549  } \
550 };
551 
552 VIGRA_NUMPY_VALUETYPE_TRAITS(bool, NPY_BOOL, bool, "UINT8")
553 VIGRA_NUMPY_VALUETYPE_TRAITS(signed char, NPY_INT8, int8, "INT16")
554 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned char, NPY_UINT8, uint8, "UINT8")
555 VIGRA_NUMPY_VALUETYPE_TRAITS(short, NPY_INT16, int16, "INT16")
556 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned short, NPY_UINT16, uint16, "UINT16")
557 
558 #if VIGRA_BITSOF_LONG == 32
559 VIGRA_NUMPY_VALUETYPE_TRAITS(long, NPY_INT32, int32, "INT32")
560 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long, NPY_UINT32, uint32, "UINT32")
561 #elif VIGRA_BITSOF_LONG == 64
562 VIGRA_NUMPY_VALUETYPE_TRAITS(long, NPY_INT64, int64, "DOUBLE")
563 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long, NPY_UINT64, uint64, "DOUBLE")
564 #endif
565 
566 #if VIGRA_BITSOF_INT == 32
567 VIGRA_NUMPY_VALUETYPE_TRAITS(int, NPY_INT32, int32, "INT32")
568 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int, NPY_UINT32, uint32, "UINT32")
569 #elif VIGRA_BITSOF_INT == 64
570 VIGRA_NUMPY_VALUETYPE_TRAITS(int, NPY_INT64, int64, "DOUBLE")
571 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int, NPY_UINT64, uint64, "DOUBLE")
572 #endif
573 
574 #ifdef PY_LONG_LONG
575 # if VIGRA_BITSOF_LONG_LONG == 32
576 VIGRA_NUMPY_VALUETYPE_TRAITS(long long, NPY_INT32, int32, "INT32")
577 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT32, uint32, "UINT32")
578 # elif VIGRA_BITSOF_LONG_LONG == 64
579 VIGRA_NUMPY_VALUETYPE_TRAITS(long long, NPY_INT64, int64, "DOUBLE")
580 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT64, uint64, "DOUBLE")
581 # endif
582 #endif
583 
584 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float32, NPY_FLOAT32, float32, "FLOAT")
585 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float64, NPY_FLOAT64, float64, "DOUBLE")
586 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE
587 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_longdouble, NPY_LONGDOUBLE, longdouble, "")
588 #endif
589 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cfloat, NPY_CFLOAT, complex64, "")
590 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_float>, NPY_CFLOAT, complex64, "")
591 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cdouble, NPY_CDOUBLE, complex128, "")
592 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_double>, NPY_CDOUBLE, complex128, "")
593 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_clongdouble, NPY_CLONGDOUBLE, clongdouble, "")
594 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE
595 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_longdouble>, NPY_CLONGDOUBLE, clongdouble, "")
596 #endif
597 
598 #undef VIGRA_NUMPY_VALUETYPE_TRAITS
599 
600 /********************************************************/
601 /* */
602 /* NumpyArrayTraits */
603 /* */
604 /********************************************************/
605 
606 template <class U, int N>
607 bool stridesAreAscending(TinyVector<U, N> const & strides)
608 {
609  for(int k=1; k<N; ++k)
610  if(strides[k] < strides[k-1])
611  return false;
612  return true;
613 }
614 
615 template<unsigned int N, class T, class Stride>
616 struct NumpyArrayTraits;
617 
618 template<unsigned int N, class T>
619 struct NumpyArrayTraits<N, T, StridedArrayTag>
620 {
621  typedef T dtype;
622  typedef T value_type;
623  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
624  static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
625 
626  enum { spatialDimensions = N, channels = 1 };
627 
628  static bool isArray(PyObject * obj)
629  {
630  return obj && PyArray_Check(obj);
631  }
632 
633  static bool isClassCompatible(PyObject * obj)
634  {
635  return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
636  }
637 
638  static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */
639  {
640  return ValuetypeTraits::isValuetypeCompatible(obj);
641  }
642 
643  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
644  {
645  return PyArray_NDIM((PyObject *)obj) == N-1 ||
646  PyArray_NDIM((PyObject *)obj) == N ||
647  (PyArray_NDIM((PyObject *)obj) == N+1 && PyArray_DIM((PyObject *)obj, N) == 1);
648  }
649 
650  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
651  {
652  return ValuetypeTraits::isValuetypeCompatible(obj) &&
653  isShapeCompatible(obj);
654  }
655 
656  template <class U>
657  static python_ptr constructor(TinyVector<U, N> const & shape,
658  T *data, TinyVector<U, N> const & stride)
659  {
660  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
661  return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data);
662  }
663 
664  static std::string typeKey()
665  {
666  static std::string key = std::string("NumpyArray<") + asString(N) + ", *>";
667  return key;
668  }
669 
670  static std::string typeKeyFull()
671  {
672  static std::string key = std::string("NumpyArray<") + asString(N) + ", " +
673  ValuetypeTraits::typeName() + ", StridedArrayTag>";
674  return key;
675  }
676 };
677 
678 /********************************************************/
679 
680 template<unsigned int N, class T>
681 struct NumpyArrayTraits<N, T, UnstridedArrayTag>
682 : public NumpyArrayTraits<N, T, StridedArrayTag>
683 {
684  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
685  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
686 
687  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
688  {
689  return BaseType::isShapeCompatible(obj) &&
690  PyArray_STRIDES((PyObject *)obj)[0] == PyArray_ITEMSIZE((PyObject *)obj);
691  }
692 
693  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
694  {
695  return BaseType::isValuetypeCompatible(obj) &&
696  isShapeCompatible(obj);
697  }
698 
699  template <class U>
700  static python_ptr constructor(TinyVector<U, N> const & shape,
701  T *data, TinyVector<U, N> const & stride)
702  {
703  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
704  return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data);
705  }
706 
707  static std::string typeKeyFull()
708  {
709  static std::string key = std::string("NumpyArray<") + asString(N) + ", " +
710  ValuetypeTraits::typeName() + ", UnstridedArrayTag>";
711  return key;
712  }
713 };
714 
715 /********************************************************/
716 
717 template<unsigned int N, class T>
718 struct NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
719 : public NumpyArrayTraits<N, T, StridedArrayTag>
720 {
721  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
722  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
723 
724  static bool isClassCompatible(PyObject * obj)
725  {
726  return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
727  }
728 
729  template <class U>
730  static python_ptr constructor(TinyVector<U, N> const & shape,
731  T *data, TinyVector<U, N> const & stride)
732  {
733  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
734  return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data);
735  }
736 
737  static std::string typeKey()
738  {
739  static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<*> >";
740  return key;
741  }
742 
743  static std::string typeKeyFull()
744  {
745  static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<" +
746  ValuetypeTraits::typeName() + ">, StridedArrayTag>";
747  return key;
748  }
749 };
750 
751 /********************************************************/
752 
753 template<unsigned int N, class T>
754 struct NumpyArrayTraits<N, Singleband<T>, UnstridedArrayTag>
755 : public NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
756 {
757  typedef NumpyArrayTraits<N, T, UnstridedArrayTag> UnstridedTraits;
758  typedef NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> BaseType;
759  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
760 
761  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
762  {
763  return UnstridedTraits::isShapeCompatible(obj);
764  }
765 
766  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
767  {
768  return UnstridedTraits::isPropertyCompatible(obj);
769  }
770 
771  template <class U>
772  static python_ptr constructor(TinyVector<U, N> const & shape,
773  T *data, TinyVector<U, N> const & stride)
774  {
775  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
776  return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data);
777  }
778 
779  static std::string typeKeyFull()
780  {
781  static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<" +
782  ValuetypeTraits::typeName() + ">, UnstridedArrayTag>";
783  return key;
784  }
785 };
786 
787 /********************************************************/
788 
789 template<unsigned int N, class T>
790 struct NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
791 : public NumpyArrayTraits<N, T, StridedArrayTag>
792 {
793  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
794  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
795 
796  enum { spatialDimensions = N-1, channels = 0 };
797 
798  static bool isClassCompatible(PyObject * obj)
799  {
800  return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
801  }
802 
803  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
804  {
805  return PyArray_NDIM(obj) == N || PyArray_NDIM(obj) == N-1;
806  }
807 
808  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
809  {
810  return ValuetypeTraits::isValuetypeCompatible(obj) &&
811  isShapeCompatible(obj);
812  }
813 
814  template <class U>
815  static python_ptr constructor(TinyVector<U, N> const & shape,
816  T *data, TinyVector<U, N> const & stride)
817  {
818  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
819  return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data);
820  }
821 
822  static std::string typeKey()
823  {
824  static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<*> >";
825  return key;
826  }
827 
828  static std::string typeKeyFull()
829  {
830  static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<" +
831  ValuetypeTraits::typeName() + ">, StridedArrayTag>";
832  return key;
833  }
834 };
835 
836 /********************************************************/
837 
838 template<unsigned int N, class T>
839 struct NumpyArrayTraits<N, Multiband<T>, UnstridedArrayTag>
840 : public NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
841 {
842  typedef NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> BaseType;
843  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
844 
845  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
846  {
847  return BaseType::isShapeCompatible(obj) &&
848  PyArray_STRIDES((PyObject *)obj)[0] == PyArray_ITEMSIZE((PyObject *)obj);
849  }
850 
851  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
852  {
853  return BaseType::isValuetypeCompatible(obj) &&
854  isShapeCompatible(obj);
855  }
856 
857  template <class U>
858  static python_ptr constructor(TinyVector<U, N> const & shape,
859  T *data, TinyVector<U, N> const & stride)
860  {
861  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
862  return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data);
863  }
864 
865  static std::string typeKeyFull()
866  {
867  static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<" +
868  ValuetypeTraits::typeName() + ">, UnstridedArrayTag>";
869  return key;
870  }
871 };
872 
873 /********************************************************/
874 
875 template<unsigned int N, int M, class T>
876 struct NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
877 {
878  typedef T dtype;
879  typedef TinyVector<T, M> value_type;
880  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
881  static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
882 
883  enum { spatialDimensions = N, channels = M };
884 
885  static bool isArray(PyObject * obj)
886  {
887  return obj && PyArray_Check(obj);
888  }
889 
890  static bool isClassCompatible(PyObject * obj)
891  {
892  return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
893  }
894 
895  static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */
896  {
897  return ValuetypeTraits::isValuetypeCompatible(obj);
898  }
899 
900  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
901  {
902  return PyArray_NDIM((PyObject *)obj) == N+1 &&
903  PyArray_DIM((PyObject *)obj, N) == M &&
904  PyArray_STRIDES((PyObject *)obj)[N] == PyArray_ITEMSIZE((PyObject *)obj);
905  }
906 
907  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
908  {
909  return ValuetypeTraits::isValuetypeCompatible(obj) &&
910  isShapeCompatible(obj);
911  }
912 
913  template <class U>
914  static python_ptr constructor(TinyVector<U, N> const & shape,
915  T *data, TinyVector<U, N> const & stride)
916  {
917  TinyVector<npy_intp, N+1> npyShape;
918  std::copy(shape.begin(), shape.end(), npyShape.begin());
919  npyShape[N] = M;
920 
921  TinyVector<npy_intp, N+1> npyStride;
922  std::transform(
923  stride.begin(), stride.end(), npyStride.begin(),
924  std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
925  npyStride[N] = sizeof(T);
926 
927  return detail::constructNumpyArrayFromData(
928  typeKeyFull(), typeKey(), npyShape,
929  npyStride.begin(), ValuetypeTraits::typeCode, data);
930  }
931 
932  static std::string typeKey()
933  {
934  static std::string key = std::string("NumpyArray<") + asString(N) + ", TinyVector<*, " + asString(M) + "> >";
935  return key;
936  }
937 
938  static std::string typeKeyFull()
939  {
940  static std::string key = std::string("NumpyArray<") + asString(N) +
941  ", TinyVector<" + ValuetypeTraits::typeName() + ", " + asString(M) + ">, StridedArrayTag>";
942  return key;
943  }
944 };
945 
946 /********************************************************/
947 
948 template<unsigned int N, int M, class T>
949 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag>
950 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
951 {
952  typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType;
953  typedef typename BaseType::value_type value_type;
954  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
955 
956  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
957  {
958  return BaseType::isShapeCompatible(obj) &&
959  PyArray_STRIDES((PyObject *)obj)[0] == sizeof(TinyVector<T, M>);
960  }
961 
962  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
963  {
964  return BaseType::isValuetypeCompatible(obj) &&
965  isShapeCompatible(obj);
966  }
967 
968  template <class U>
969  static python_ptr constructor(TinyVector<U, N> const & shape,
970  T *data, TinyVector<U, N> const & stride)
971  {
972  TinyVector<npy_intp, N+1> npyShape;
973  std::copy(shape.begin(), shape.end(), npyShape.begin());
974  npyShape[N] = M;
975 
976  TinyVector<npy_intp, N+1> npyStride;
977  std::transform(
978  stride.begin(), stride.end(), npyStride.begin(),
979  std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
980  npyStride[N] = sizeof(T);
981 
982  return detail::constructNumpyArrayFromData(
983  typeKeyFull(), BaseType::typeKey(), npyShape,
984  npyStride.begin(), ValuetypeTraits::typeCode, data);
985  }
986 
987  static std::string typeKeyFull()
988  {
989  static std::string key = std::string("NumpyArray<") + asString(N) +
990  ", TinyVector<" + ValuetypeTraits::typeName() + ", " + asString(M) + ">, UnstridedArrayTag>";
991  return key;
992  }
993 };
994 
995 /********************************************************/
996 
997 template<unsigned int N, class T>
998 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
999 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag>
1000 {
1001  typedef T dtype;
1002  typedef RGBValue<T> value_type;
1003  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
1004 
1005  static bool isClassCompatible(PyObject * obj)
1006  {
1007  return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
1008  }
1009 
1010  template <class U>
1011  static python_ptr constructor(TinyVector<U, N> const & shape,
1012  T *data, TinyVector<U, N> const & stride)
1013  {
1014  TinyVector<npy_intp, N+1> npyShape;
1015  std::copy(shape.begin(), shape.end(), npyShape.begin());
1016  npyShape[N] = 3;
1017 
1018  TinyVector<npy_intp, N+1> npyStride;
1019  std::transform(
1020  stride.begin(), stride.end(), npyStride.begin(),
1021  std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
1022  npyStride[N] = sizeof(T);
1023 
1024  return detail::constructNumpyArrayFromData(
1025  typeKeyFull(), typeKey(), npyShape,
1026  npyStride.begin(), ValuetypeTraits::typeCode, data);
1027  }
1028 
1029  static std::string typeKey()
1030  {
1031  static std::string key = std::string("NumpyArray<") + asString(N) + ", RGBValue<*> >";
1032  return key;
1033  }
1034 
1035  static std::string typeKeyFull()
1036  {
1037  static std::string key = std::string("NumpyArray<") + asString(N) +
1038  ", RGBValue<" + ValuetypeTraits::typeName() + ">, StridedArrayTag>";
1039  return key;
1040  }
1041 };
1042 
1043 /********************************************************/
1044 
1045 template<unsigned int N, class T>
1046 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag>
1047 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
1048 {
1049  typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits;
1050  typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType;
1051  typedef typename BaseType::value_type value_type;
1052  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
1053 
1054  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
1055  {
1056  return UnstridedTraits::isShapeCompatible(obj);
1057  }
1058 
1059  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
1060  {
1061  return UnstridedTraits::isPropertyCompatible(obj);
1062  }
1063 
1064  template <class U>
1065  static python_ptr constructor(TinyVector<U, N> const & shape,
1066  T *data, TinyVector<U, N> const & stride)
1067  {
1068  TinyVector<npy_intp, N+1> npyShape;
1069  std::copy(shape.begin(), shape.end(), npyShape.begin());
1070  npyShape[N] = 3;
1071 
1072  TinyVector<npy_intp, N+1> npyStride;
1073  std::transform(
1074  stride.begin(), stride.end(), npyStride.begin(),
1075  std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
1076  npyStride[N] = sizeof(T);
1077 
1078  return detail::constructNumpyArrayFromData(
1079  typeKeyFull(), BaseType::typeKey(), npyShape,
1080  npyStride.begin(), ValuetypeTraits::typeCode, data);
1081  }
1082 
1083  static std::string typeKeyFull()
1084  {
1085  static std::string key = std::string("NumpyArray<") + asString(N) +
1086  ", RGBValue<" + ValuetypeTraits::typeName() + ">, UnstridedArrayTag>";
1087  return key;
1088  }
1089 };
1090 
1091 /********************************************************/
1092 /* */
1093 /* NumpyAnyArray */
1094 /* */
1095 /********************************************************/
1096 
1097 /** Wrapper class for a Python array.
1098 
1099  This class stores a reference-counted pointer to an Python numpy array object,
1100  i.e. an object where <tt>PyArray_Check(object)</tt> returns true (in Python, the
1101  object is then a subclass of <tt>numpy.ndarray</tt>). This class is mainly used
1102  as a smart pointer to these arrays, but some basic access and conversion functions
1103  are also provided.
1104 
1105  <b>\#include</b> <<a href="numpy__array_8hxx-source.html">vigra/numpy_array.hxx</a>><br>
1106  Namespace: vigra
1107 */
1109 {
1110  protected:
1111  python_ptr pyArray_;
1112 
1113  // We want to apply broadcasting to the channel dimension.
1114  // Since only leading dimensions can be added during numpy
1115  // broadcasting, we permute the array accordingly.
1116  NumpyAnyArray permuteChannelsToFront() const
1117  {
1118  MultiArrayIndex M = ndim();
1119  ArrayVector<npy_intp> permutation(M);
1120  for(int k=0; k<M; ++k)
1121  permutation[k] = M-1-k;
1122  // explicit cast to int is neede here to avoid gcc c++0x compilation
1123  // error: narrowing conversion of ‘M’ from ‘vigra::MultiArrayIndex’
1124  // to ‘int’ inside { }
1125  // int overflow should not occur here because PyArray_NDIM returns
1126  // an integer which is converted to long in NumpyAnyArray::ndim()
1127  PyArray_Dims permute = { permutation.begin(), (int) M };
1128  python_ptr array(PyArray_Transpose(pyArray(), &permute), python_ptr::keep_count);
1129  pythonToCppException(array);
1130  return NumpyAnyArray(array.ptr());
1131  }
1132 
1133  public:
1134 
1135  /// difference type
1137 
1138  /**
1139  Construct from a Python object. If \a obj is NULL, or is not a subclass
1140  of numpy.ndarray, the resulting NumpyAnyArray will have no data (i.e.
1141  hasData() returns false). Otherwise, it creates a new reference to the array
1142  \a obj, unless \a createCopy is true, where a new array is created by calling
1143  the C-equivalent of obj->copy().
1144  */
1145  explicit NumpyAnyArray(PyObject * obj = 0, bool createCopy = false, PyTypeObject * type = 0)
1146  {
1147  if(obj == 0)
1148  return;
1149  vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type),
1150  "NumpyAnyArray(obj, createCopy, type): type must be numpy.ndarray or a subclass thereof.");
1151  if(createCopy)
1152  makeCopy(obj, type);
1153  else
1154  vigra_precondition(makeReference(obj, type), "NumpyAnyArray(obj): obj isn't a numpy array.");
1155  }
1156 
1157  /**
1158  Copy constructor. By default, it creates a new reference to the array
1159  \a other. When \a createCopy is true, a new array is created by calling
1160  the C-equivalent of other.copy().
1161  */
1162  NumpyAnyArray(NumpyAnyArray const & other, bool createCopy = false, PyTypeObject * type = 0)
1163  {
1164  if(!other.hasData())
1165  return;
1166  vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type),
1167  "NumpyAnyArray(obj, createCopy, type): type must be numpy.ndarray or a subclass thereof.");
1168  if(createCopy)
1169  makeCopy(other.pyObject(), type);
1170  else
1171  makeReference(other.pyObject(), type);
1172  }
1173 
1174  // auto-generated destructor is ok
1175 
1176  /**
1177  * Assignment operator. If this is already a view with data
1178  * (i.e. hasData() is true) and the shapes match, the RHS
1179  * array contents are copied via the C-equivalent of
1180  * 'self[...] = other[...]'. If the shapes don't matched,
1181  * broadcasting is tried on the trailing (i.e. channel)
1182  * dimension.
1183  * If the LHS is an empty view, assignment is identical to
1184  * makeReference(other.pyObject()).
1185  */
1187  {
1188  if(hasData())
1189  {
1190  vigra_precondition(other.hasData(),
1191  "NumpyArray::operator=(): Cannot assign from empty array.");
1192  if(PyArray_CopyInto(permuteChannelsToFront().pyArray(), other.permuteChannelsToFront().pyArray()) == -1)
1193  pythonToCppException(0);
1194  }
1195  else
1196  {
1197  pyArray_ = other.pyArray_;
1198  }
1199  return *this;
1200  }
1201 
1202  /**
1203  Returns the number of dimensions of this array, or 0 if
1204  hasData() is false.
1205  */
1207  {
1208  if(hasData())
1209  return PyArray_NDIM(pyObject());
1210  return 0;
1211  }
1212 
1213  /**
1214  Returns the number of spatial dimensions of this array, or 0 if
1215  hasData() is false. If the enclosed Python array does not define
1216  the attribute spatialDimensions, ndim() is returned.
1217  */
1219  {
1220  if(!hasData())
1221  return 0;
1223  if(s == -1)
1224  s = ndim();
1225  return s;
1226  }
1227 
1228  /**
1229  Returns the shape of this array. The size of
1230  the returned shape equals ndim().
1231  */
1232  difference_type shape() const
1233  {
1234  if(hasData())
1235  return difference_type(PyArray_DIMS(pyObject()), PyArray_DIMS(pyObject()) + ndim());
1236  return difference_type();
1237  }
1238 
1239  /** Compute the ordering of the strides of this array.
1240  The result is describes the current permutation of the axes relative
1241  to an ascending stride order.
1242  */
1243  difference_type strideOrdering() const
1244  {
1245  if(!hasData())
1246  return difference_type();
1247  MultiArrayIndex N = ndim();
1248  difference_type stride(PyArray_STRIDES(pyObject()), PyArray_STRIDES(pyObject()) + N),
1249  permutation(N);
1250  for(MultiArrayIndex k=0; k<N; ++k)
1251  permutation[k] = k;
1252  for(MultiArrayIndex k=0; k<N-1; ++k)
1253  {
1254  MultiArrayIndex smallest = k;
1255  for(MultiArrayIndex j=k+1; j<N; ++j)
1256  {
1257  if(stride[j] < stride[smallest])
1258  smallest = j;
1259  }
1260  if(smallest != k)
1261  {
1262  std::swap(stride[k], stride[smallest]);
1263  std::swap(permutation[k], permutation[smallest]);
1264  }
1265  }
1266  difference_type ordering(N);
1267  for(MultiArrayIndex k=0; k<N; ++k)
1268  ordering[permutation[k]] = k;
1269  return ordering;
1270  }
1271 
1272  /**
1273  Returns the value type of the elements in this array, or -1
1274  when hasData() is false.
1275  */
1276  int dtype() const
1277  {
1278  if(hasData())
1279  return PyArray_DESCR(pyObject())->type_num;
1280  return -1;
1281  }
1282 
1283  /**
1284  * Return a borrowed reference to the internal PyArrayObject.
1285  */
1286  PyArrayObject * pyArray() const
1287  {
1288  return (PyArrayObject *)pyArray_.get();
1289  }
1290 
1291  /**
1292  * Return a borrowed reference to the internal PyArrayObject
1293  * (see pyArray()), cast to PyObject for your convenience.
1294  */
1295  PyObject * pyObject() const
1296  {
1297  return pyArray_.get();
1298  }
1299 
1300  /**
1301  Reset the NumpyAnyArray to the given object. If \a obj is a numpy array object,
1302  a new reference to that array is created, and the function returns
1303  true. Otherwise, it returns false and the NumpyAnyArray remains unchanged.
1304  If \a type is given, the new reference will be a view with that type, provided
1305  that \a type is a numpy ndarray or a subclass thereof. Otherwise, an
1306  exception is thrown.
1307  */
1308  bool makeReference(PyObject * obj, PyTypeObject * type = 0)
1309  {
1310  if(obj == 0 || !PyArray_Check(obj))
1311  return false;
1312  if(type != 0)
1313  {
1314  vigra_precondition(PyType_IsSubtype(type, &PyArray_Type) != 0,
1315  "NumpyAnyArray::makeReference(obj, type): type must be numpy.ndarray or a subclass thereof.");
1316  obj = PyArray_View((PyArrayObject*)obj, 0, type);
1317  pythonToCppException(obj);
1318  }
1319  pyArray_.reset(obj);
1320  return true;
1321  }
1322 
1323  /**
1324  Create a copy of the given array object. If \a obj is a numpy array object,
1325  a copy is created via the C-equivalent of 'obj->copy()'. If
1326  this call fails, or obj was not an array, an exception is thrown
1327  and the NumpyAnyArray remains unchanged.
1328  */
1329  void makeCopy(PyObject * obj, PyTypeObject * type = 0)
1330  {
1331  vigra_precondition(obj && PyArray_Check(obj),
1332  "NumpyAnyArray::makeCopy(obj): obj is not an array.");
1333  vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type),
1334  "NumpyAnyArray::makeCopy(obj, type): type must be numpy.ndarray or a subclass thereof.");
1335  python_ptr array(PyArray_NewCopy((PyArrayObject*)obj, NPY_ANYORDER), python_ptr::keep_count);
1336  pythonToCppException(array);
1337  makeReference(array, type);
1338  }
1339 
1340  /**
1341  Check whether this NumpyAnyArray actually points to a Python array.
1342  */
1343  bool hasData() const
1344  {
1345  return pyArray_ != 0;
1346  }
1347 };
1348 
1349 /********************************************************/
1350 /* */
1351 /* NumpyArray */
1352 /* */
1353 /********************************************************/
1354 
1355 /** Provide the MultiArrayView interface for a Python array.
1356 
1357  This class inherits from both \ref vigra::MultiArrayView and \ref vigra::NumpyAnyArray
1358  in order to support easy and save application of VIGRA functions to Python arrays.
1359 
1360  <b>\#include</b> <<a href="numpy__array_8hxx-source.html">vigra/numpy_array.hxx</a>><br>
1361  Namespace: vigra
1362 */
1363 template <unsigned int N, class T, class Stride = StridedArrayTag>
1365 : public MultiArrayView<N, typename NumpyArrayTraits<N, T, Stride>::value_type, Stride>,
1366  public NumpyAnyArray
1367 {
1368  public:
1369  typedef NumpyArrayTraits<N, T, Stride> ArrayTraits;
1370  typedef typename ArrayTraits::dtype dtype;
1371  typedef T pseudo_value_type;
1372 
1373  static NPY_TYPES const typeCode = ArrayTraits::typeCode;
1374 
1375  /** the view type associated with this array.
1376  */
1378 
1379  enum { actual_dimension = view_type::actual_dimension };
1380 
1381  /** the array's value type
1382  */
1384 
1385  /** pointer type
1386  */
1387  typedef typename view_type::pointer pointer;
1388 
1389  /** const pointer type
1390  */
1392 
1393  /** reference type (result of operator[])
1394  */
1396 
1397  /** const reference type (result of operator[] const)
1398  */
1400 
1401  /** size type
1402  */
1404 
1405  /** difference type (used for multi-dimensional offsets and indices)
1406  */
1408 
1409  /** difference and index type for a single dimension
1410  */
1412 
1413  /** traverser type
1414  */
1416 
1417  /** traverser type to const data
1418  */
1420 
1421  /** sequential (random access) iterator type
1422  */
1424 
1425  /** sequential (random access) const iterator type
1426  */
1428 
1429  using view_type::shape; // resolve ambiguity of multiple inheritance
1430  using view_type::hasData; // resolve ambiguity of multiple inheritance
1431  using view_type::strideOrdering; // resolve ambiguity of multiple inheritance
1432 
1433  protected:
1434 
1435  // this function assumes that pyArray_ has already been set, and compatibility been checked
1436  void setupArrayView();
1437 
1438  static python_ptr getArrayTypeObject()
1439  {
1440  python_ptr type = detail::getArrayTypeObject(ArrayTraits::typeKeyFull());
1441  if(type == 0)
1442  type = detail::getArrayTypeObject(ArrayTraits::typeKey(), &PyArray_Type);
1443  return type;
1444  }
1445 
1446  static python_ptr init(difference_type const & shape, bool init = true)
1447  {
1448  ArrayVector<npy_intp> pshape(shape.begin(), shape.end());
1449  return detail::constructNumpyArrayImpl((PyTypeObject *)getArrayTypeObject().ptr(), pshape,
1450  ArrayTraits::spatialDimensions, ArrayTraits::channels,
1451  typeCode, "V", init);
1452  }
1453 
1454  static python_ptr init(difference_type const & shape, difference_type const & strideOrdering, bool init = true)
1455  {
1456  ArrayVector<npy_intp> pshape(shape.begin(), shape.end()),
1457  pstrideOrdering(strideOrdering.begin(), strideOrdering.end());
1458  return detail::constructNumpyArrayImpl((PyTypeObject *)getArrayTypeObject().ptr(), pshape,
1459  ArrayTraits::spatialDimensions, ArrayTraits::channels,
1460  typeCode, "A", init, pstrideOrdering);
1461  }
1462 
1463  public:
1464 
1465  using view_type::init;
1466 
1467  /**
1468  * Construct from a given PyObject pointer. When the given
1469  * python object is NULL, the internal python array will be
1470  * NULL and hasData() will return false.
1471  *
1472  * Otherwise, the function attempts to create a
1473  * new reference to the given Python object, unless
1474  * copying is forced by setting \a createCopy to true.
1475  * If either of this fails, the function throws an exception.
1476  * This will not happen if isStrictlyCompatible(obj) (in case
1477  * of creating a new reference) or isCopyCompatible(obj)
1478  * (in case of copying) have returned true beforehand.
1479  */
1480  explicit NumpyArray(PyObject *obj = 0, bool createCopy = false)
1481  {
1482  if(obj == 0)
1483  return;
1484  if(createCopy)
1485  makeCopy(obj);
1486  else
1487  vigra_precondition(makeReference(obj),
1488  "NumpyArray(obj): Cannot construct from incompatible array.");
1489  }
1490 
1491  /**
1492  * Copy constructor; does not copy the memory, but creates a
1493  * new reference to the same underlying python object, unless
1494  * a copy is forced by setting \a createCopy to true.
1495  * (If the source object has no data, this one will have
1496  * no data, too.)
1497  */
1498  NumpyArray(const NumpyArray &other, bool createCopy = false) :
1499  MultiArrayView<N, typename NumpyArrayTraits<N, T, Stride>::value_type, Stride>(other),
1500  NumpyAnyArray(other, createCopy)
1501  {
1502  if(!other.hasData())
1503  return;
1504  if(createCopy)
1505  makeCopy(other.pyObject());
1506  else
1507  makeReferenceUnchecked(other.pyObject());
1508  }
1509 
1510  /**
1511  * Allocate new memory and copy data from a MultiArrayView.
1512  */
1513  explicit NumpyArray(const view_type &other)
1514  {
1515  if(!other.hasData())
1516  return;
1517  vigra_postcondition(makeReference(init(other.shape(), false)),
1518  "NumpyArray(view_type): Python constructor did not produce a compatible array.");
1519  static_cast<view_type &>(*this) = other;
1520  }
1521 
1522  /**
1523  * Construct a new array object, allocating an internal python
1524  * ndarray of the given shape (in fortran order), initialized
1525  * with zeros.
1526  *
1527  * An exception is thrown when construction fails.
1528  */
1529  explicit NumpyArray(difference_type const & shape)
1530  {
1531  vigra_postcondition(makeReference(init(shape)),
1532  "NumpyArray(shape): Python constructor did not produce a compatible array.");
1533  }
1534 
1535  /**
1536  * Construct a new array object, allocating an internal python
1537  * ndarray of the given shape and given stride ordering, initialized
1538  * with zeros.
1539  *
1540  * An exception is thrown when construction fails.
1541  */
1542  NumpyArray(difference_type const & shape, difference_type const & strideOrdering)
1543  {
1544  vigra_postcondition(makeReference(init(shape, strideOrdering)),
1545  "NumpyArray(shape): Python constructor did not produce a compatible array.");
1546  }
1547 
1548  /**
1549  * Constructor from NumpyAnyArray.
1550  * Equivalent to NumpyArray(other.pyObject())
1551  */
1552  NumpyArray(const NumpyAnyArray &other, bool createCopy = false)
1553  {
1554  if(!other.hasData())
1555  return;
1556  if(createCopy)
1557  makeCopy(other.pyObject());
1558  else
1559  vigra_precondition(makeReference(other.pyObject()), //, false),
1560  "NumpyArray(NumpyAnyArray): Cannot construct from incompatible or empty array.");
1561  }
1562 
1563  /**
1564  * Assignment operator. If this is already a view with data
1565  * (i.e. hasData() is true) and the shapes match, the RHS
1566  * array contents are copied. If this is an empty view,
1567  * assignment is identical to makeReferenceUnchecked(other.pyObject()).
1568  * See MultiArrayView::operator= for further information on
1569  * semantics.
1570  */
1572  {
1573  if(hasData())
1574  view_type::operator=(other);
1575  else
1576  makeReferenceUnchecked(other.pyObject());
1577  return *this;
1578  }
1579 
1580  /**
1581  * Assignment operator. If this is already a view with data
1582  * (i.e. hasData() is true) and the shapes match, the RHS
1583  * array contents are copied.
1584  * If this is an empty view, assignment is identical to
1585  * makeReference(other.pyObject()).
1586  * Otherwise, an exception is thrown.
1587  */
1589  {
1590  if(hasData())
1591  {
1592  NumpyAnyArray::operator=(other);
1593  }
1594  else if(isStrictlyCompatible(other.pyObject()))
1595  {
1596  makeReferenceUnchecked(other.pyObject());
1597  }
1598  else
1599  {
1600  vigra_precondition(false,
1601  "NumpyArray::operator=(): Cannot assign from incompatible array.");
1602  }
1603  return *this;
1604  }
1605 
1606  /**
1607  * Test whether a given python object is a numpy array that can be
1608  * converted (copied) into an array compatible to this NumpyArray type.
1609  * This means that the array's shape conforms to the requirements of
1610  * makeCopy().
1611  */
1612  static bool isCopyCompatible(PyObject *obj)
1613  {
1614  return ArrayTraits::isArray(obj) &&
1615  ArrayTraits::isShapeCompatible((PyArrayObject *)obj);
1616  }
1617 
1618  /**
1619  * Test whether a given python object is a numpy array with a
1620  * compatible dtype and the correct shape and strides, so that it
1621  * can be referenced as a view by this NumpyArray type (i.e.
1622  * it conforms to the requirements of makeReference()).
1623  */
1624  static bool isReferenceCompatible(PyObject *obj)
1625  {
1626  return ArrayTraits::isArray(obj) &&
1627  ArrayTraits::isPropertyCompatible((PyArrayObject *)obj);
1628  }
1629 
1630  /**
1631  * Like isReferenceCompatible(obj), but also executes a customized type compatibility
1632  * check when such a check has been registered for this class via
1633  * registerPythonArrayType().
1634  *
1635  * This facilitates proper overload resolution between
1636  * NumpyArray<3, Multiband<T> > (a multiband image) and NumpyArray<3, Singleband<T> > (a scalar volume).
1637  */
1638  static bool isStrictlyCompatible(PyObject *obj)
1639  {
1640 #if VIGRA_CONVERTER_DEBUG
1641  std::cerr << "class " << typeid(NumpyArray).name() << " got " << obj->ob_type->tp_name << "\n";
1642  bool isClassCompatible=ArrayTraits::isClassCompatible(obj);
1643  bool isPropertyCompatible((PyArrayObject *)obj);
1644  std::cerr<<"isClassCompatible: "<<isClassCompatible<<std::endl;
1645  std::cerr<<"isPropertyCompatible: "<<isPropertyCompatible<<std::endl;
1646 #endif
1647  return ArrayTraits::isClassCompatible(obj) &&
1648  ArrayTraits::isPropertyCompatible((PyArrayObject *)obj);
1649  }
1650 
1651  /**
1652  * Create a vector representing the standard stride ordering of a NumpyArray.
1653  * That is, we get a vector representing the range [0,...,N-1], which
1654  * denotes the stride ordering for Fortran order.
1655  */
1656  static difference_type standardStrideOrdering()
1657  {
1659  for(unsigned int k=0; k<N; ++k)
1660  strideOrdering[k] = k;
1661  return strideOrdering;
1662  }
1663 
1664  /**
1665  * Set up a view to the given object without checking compatibility.
1666  * This function must not be used unless isReferenceCompatible(obj) returned
1667  * true on the given object (otherwise, a crash is likely).
1668  */
1669  void makeReferenceUnchecked(PyObject *obj)
1670  {
1672  setupArrayView();
1673  }
1674 
1675  /**
1676  * Try to set up a view referencing the given PyObject.
1677  * Returns false if the python object is not a compatible
1678  * numpy array (see isReferenceCompatible() or
1679  * isStrictlyCompatible(), according to the parameter \a
1680  * strict).
1681  */
1682  bool makeReference(PyObject *obj, bool strict = true)
1683  {
1684  if(strict)
1685  {
1686  if(!isStrictlyCompatible(obj))
1687  return false;
1688  }
1689  else
1690  {
1691  if(!isReferenceCompatible(obj))
1692  return false;
1693  }
1694  makeReferenceUnchecked(obj);
1695  return true;
1696  }
1697 
1698  /**
1699  * Try to set up a view referencing the same data as the given
1700  * NumpyAnyArray. This overloaded variant simply calls
1701  * makeReference() on array.pyObject().
1702  */
1703  bool makeReference(const NumpyAnyArray &array, bool strict = true)
1704  {
1705  return makeReference(array.pyObject(), strict);
1706  }
1707 
1708  /**
1709  * Set up an unsafe reference to the given MultiArrayView.
1710  * ATTENTION: This creates a numpy.ndarray that points to the
1711  * same data, but does not own it, so it must be ensured by
1712  * other means that the memory does not get freed before the
1713  * end of the ndarray's lifetime! (One elegant way would be
1714  * to set the 'base' attribute of the resulting ndarray to a
1715  * python object which directly or indirectly holds the memory
1716  * of the given MultiArrayView.)
1717  */
1718  void makeReference(const view_type &multiArrayView)
1719  {
1720  vigra_precondition(!hasData(), "makeReference(): cannot replace existing view with given buffer");
1721 
1722  // construct an ndarray that points to our data (taking strides into account):
1723  python_ptr array(ArrayTraits::constructor(multiArrayView.shape(), multiArrayView.data(), multiArrayView.stride()));
1724 
1725  view_type::operator=(multiArrayView);
1726  pyArray_ = array;
1727  }
1728 
1729  /**
1730  Try to create a copy of the given PyObject.
1731  Raises an exception when obj is not a compatible array
1732  (see isCopyCompatible() or isStrictlyCompatible(), according to the
1733  parameter \a strict) or the Python constructor call failed.
1734  */
1735  void makeCopy(PyObject *obj, bool strict = false)
1736  {
1737  vigra_precondition(strict ? isStrictlyCompatible(obj) : isCopyCompatible(obj),
1738  "NumpyArray::makeCopy(obj): Cannot copy an incompatible array.");
1739 
1740  int M = PyArray_NDIM(obj);
1742  std::copy(PyArray_DIMS(obj), PyArray_DIMS(obj)+M, shape.begin());
1743  if(M == N-1)
1744  shape[M] = 1;
1745  vigra_postcondition(makeReference(init(shape, false)),
1746  "NumpyArray::makeCopy(obj): Copy created an incompatible array.");
1748 // if(PyArray_CopyInto(pyArray(), (PyArrayObject*)obj) == -1)
1749 // pythonToCppException(0);
1750  }
1751 
1752  /**
1753  Allocate new memory with the given shape and initialize with zeros.<br>
1754  If a stride ordering is given, the resulting array will have this stride
1755  ordering, when it is compatible with the array's memory layout (unstrided
1756  arrays only permit the standard ascending stride ordering).
1757 
1758  <em>Note:</em> this operation invalidates dependent objects
1759  (MultiArrayViews and iterators)
1760  */
1761  void reshape(difference_type const & shape, difference_type const & strideOrdering = standardStrideOrdering())
1762  {
1763  vigra_postcondition(makeReference(init(shape, strideOrdering)),
1764  "NumpyArray(shape): Python constructor did not produce a compatible array.");
1765  }
1766 
1767  /**
1768  When this array has no data, allocate new memory with the given \a shape and
1769  initialize with zeros. Otherwise, check if the new shape matches the old shape
1770  and throw a precondition exception with the given \a message if not.
1771  */
1772  void reshapeIfEmpty(difference_type const & shape, std::string message = "")
1773  {
1774  reshapeIfEmpty(shape, standardStrideOrdering(), message);
1775  }
1776 
1777  /**
1778  When this array has no data, allocate new memory with the given \a shape and
1779  initialize with zeros. Otherwise, check if the new shape matches the old shape
1780  and throw a precondition exception with the given \a message if not. If strict
1781  is true, the given stride ordering must also match that of the existing data.
1782  */
1783  void reshapeIfEmpty(difference_type const & shape, difference_type const & strideOrdering,
1784  std::string message = "", bool strict = false)
1785  {
1786  if(hasData())
1787  {
1788  if(strict)
1789  {
1790  if(message == "")
1791  message = "NumpyArray::reshapeIfEmpty(shape): array was not empty, and shape or stride ordering did not match.";
1792  vigra_precondition(shape == this->shape() && strideOrdering == this->strideOrdering(), message.c_str());
1793  }
1794  else
1795  {
1796  if(message == "")
1797  message = "NumpyArray::reshapeIfEmpty(shape): array was not empty, and shape did not match.";
1798  vigra_precondition(shape == this->shape(), message.c_str());
1799  }
1800  }
1801  else
1802  {
1803  reshape(shape, strideOrdering);
1804  }
1805  }
1806 };
1807 
1808  // this function assumes that pyArray_ has already been set, and compatibility been checked
1809 template <unsigned int N, class T, class Stride>
1810 void NumpyArray<N, T, Stride>::setupArrayView()
1811 {
1813  {
1814  unsigned int dimension = std::min<unsigned int>(actual_dimension, pyArray()->nd);
1815  std::copy(pyArray()->dimensions, pyArray()->dimensions + dimension, this->m_shape.begin());
1816  std::copy(pyArray()->strides, pyArray()->strides + dimension, this->m_stride.begin());
1817  if(pyArray()->nd < actual_dimension)
1818  {
1819  this->m_shape[dimension] = 1;
1820  this->m_stride[dimension] = sizeof(value_type);
1821  }
1822  this->m_stride /= sizeof(value_type);
1823  this->m_ptr = reinterpret_cast<pointer>(pyArray()->data);
1824  }
1825  else
1826  {
1827  this->m_ptr = 0;
1828  }
1829 }
1830 
1831 
1832 typedef NumpyArray<2, float > NumpyFArray2;
1833 typedef NumpyArray<3, float > NumpyFArray3;
1834 typedef NumpyArray<4, float > NumpyFArray4;
1835 typedef NumpyArray<2, Singleband<float> > NumpyFImage;
1836 typedef NumpyArray<3, Singleband<float> > NumpyFVolume;
1837 typedef NumpyArray<2, RGBValue<float> > NumpyFRGBImage;
1838 typedef NumpyArray<3, RGBValue<float> > NumpyFRGBVolume;
1839 typedef NumpyArray<3, Multiband<float> > NumpyFMultibandImage;
1840 typedef NumpyArray<4, Multiband<float> > NumpyFMultibandVolume;
1841 
1842 inline void import_vigranumpy()
1843 {
1844  if(_import_array() < 0)
1845  pythonToCppException(0);
1846  python_ptr module(PyImport_ImportModule("vigra.vigranumpycore"), python_ptr::keep_count);
1847  pythonToCppException(module);
1848 }
1849 
1850 /********************************************************/
1851 /* */
1852 /* NumpyArray Multiband Argument Object Factories */
1853 /* */
1854 /********************************************************/
1855 
1856 template <class PixelType, class Stride>
1857 inline triple<ConstStridedImageIterator<PixelType>,
1858  ConstStridedImageIterator<PixelType>,
1859  MultibandVectorAccessor<PixelType> >
1860 srcImageRange(NumpyArray<3, Multiband<PixelType>, Stride> const & img)
1861 {
1862  ConstStridedImageIterator<PixelType>
1863  ul(img.data(), 1, img.stride(0), img.stride(1));
1864  return triple<ConstStridedImageIterator<PixelType>,
1865  ConstStridedImageIterator<PixelType>,
1866  MultibandVectorAccessor<PixelType> >
1867  (ul, ul + Size2D(img.shape(0), img.shape(1)), MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
1868 }
1869 
1870 template <class PixelType, class Stride>
1871 inline pair< ConstStridedImageIterator<PixelType>,
1872  MultibandVectorAccessor<PixelType> >
1873 srcImage(NumpyArray<3, Multiband<PixelType>, Stride> const & img)
1874 {
1875  ConstStridedImageIterator<PixelType>
1876  ul(img.data(), 1, img.stride(0), img.stride(1));
1877  return pair<ConstStridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> >
1878  (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
1879 }
1880 
1881 template <class PixelType, class Stride>
1882 inline triple< StridedImageIterator<PixelType>,
1883  StridedImageIterator<PixelType>,
1884  MultibandVectorAccessor<PixelType> >
1885 destImageRange(NumpyArray<3, Multiband<PixelType>, Stride> & img)
1886 {
1887  StridedImageIterator<PixelType>
1888  ul(img.data(), 1, img.stride(0), img.stride(1));
1889  typedef typename AccessorTraits<PixelType>::default_accessor Accessor;
1890  return triple<StridedImageIterator<PixelType>,
1891  StridedImageIterator<PixelType>,
1892  MultibandVectorAccessor<PixelType> >
1893  (ul, ul + Size2D(img.shape(0), img.shape(1)),
1894  MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
1895 }
1896 
1897 template <class PixelType, class Stride>
1898 inline pair< StridedImageIterator<PixelType>,
1899  MultibandVectorAccessor<PixelType> >
1900 destImage(NumpyArray<3, Multiband<PixelType>, Stride> & img)
1901 {
1902  StridedImageIterator<PixelType>
1903  ul(img.data(), 1, img.stride(0), img.stride(1));
1904  return pair<StridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> >
1905  (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
1906 }
1907 
1908 template <class PixelType, class Stride>
1909 inline pair< ConstStridedImageIterator<PixelType>,
1910  MultibandVectorAccessor<PixelType> >
1911 maskImage(NumpyArray<3, Multiband<PixelType>, Stride> const & img)
1912 {
1913  ConstStridedImageIterator<PixelType>
1914  ul(img.data(), 1, img.stride(0), img.stride(1));
1915  typedef typename AccessorTraits<PixelType>::default_accessor Accessor;
1916  return pair<ConstStridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> >
1917  (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
1918 }
1919 
1920 } // namespace vigra
1921 
1922 #endif // VIGRA_NUMPY_ARRAY_HXX

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.7.1 (Tue Jul 10 2012)