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

numpy_array_traits.hxx VIGRA

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_TRAITS_HXX
37 #define VIGRA_NUMPY_ARRAY_TRAITS_HXX
38 
39 #ifndef NPY_NO_DEPRECATED_API
40 # define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
41 #endif
42 
43 #include "numerictraits.hxx"
44 #include "multi_array.hxx"
45 #include "numpy_array_taggedshape.hxx"
46 namespace vigra {
47 
48 /********************************************************/
49 /* */
50 /* NumpyArrayValuetypeTraits */
51 /* */
52 /********************************************************/
53 
54 template<class ValueType>
55 struct ERROR_NumpyArrayValuetypeTraits_not_specialized_for_ { };
56 
57 template<class ValueType>
58 struct NumpyArrayValuetypeTraits
59 {
60  static bool isValuetypeCompatible(PyArrayObject const *)
61  {
62  return ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType>();
63  }
64 
65  static ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> typeCode;
66 
67  static std::string typeName()
68  {
69  return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
70  }
71 
72  static std::string typeNameImpex()
73  {
74  return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
75  }
76 
77  static PyObject * typeObject()
78  {
79  return (PyObject *)0;
80  }
81 };
82 
83 template<class ValueType>
84 ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> NumpyArrayValuetypeTraits<ValueType>::typeCode;
85 
86 #define VIGRA_NUMPY_VALUETYPE_TRAITS(type, typeID, numpyTypeName, impexTypeName) \
87 template <> \
88 struct NumpyArrayValuetypeTraits<type > \
89 { \
90  static bool isValuetypeCompatible(PyArrayObject const * obj) /* obj must not be NULL */ \
91  { \
92  return PyArray_EquivTypenums(typeID, PyArray_DESCR((PyArrayObject *)obj)->type_num) && \
93  PyArray_ITEMSIZE((PyArrayObject *)obj) == sizeof(type); \
94  } \
95  \
96  static NPY_TYPES const typeCode = typeID; \
97  \
98  static std::string typeName() \
99  { \
100  return #numpyTypeName; \
101  } \
102  \
103  static std::string typeNameImpex() \
104  { \
105  return impexTypeName; \
106  } \
107  \
108  static PyObject * typeObject() \
109  { \
110  return PyArray_TypeObjectFromType(typeID); \
111  } \
112 };
113 
114 
115 VIGRA_NUMPY_VALUETYPE_TRAITS(bool, NPY_BOOL, bool, "UINT8")
116 VIGRA_NUMPY_VALUETYPE_TRAITS(signed char, NPY_INT8, int8, "INT16")
117 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned char, NPY_UINT8, uint8, "UINT8")
118 VIGRA_NUMPY_VALUETYPE_TRAITS(short, NPY_INT16, int16, "INT16")
119 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned short, NPY_UINT16, uint16, "UINT16")
120 
121 #if VIGRA_BITSOF_LONG == 32
122 VIGRA_NUMPY_VALUETYPE_TRAITS(long, NPY_INT32, int32, "INT32")
123 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long, NPY_UINT32, uint32, "UINT32")
124 #elif VIGRA_BITSOF_LONG == 64
125 VIGRA_NUMPY_VALUETYPE_TRAITS(long, NPY_INT64, int64, "DOUBLE")
126 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long, NPY_UINT64, uint64, "DOUBLE")
127 #endif
128 
129 #if VIGRA_BITSOF_INT == 32
130 VIGRA_NUMPY_VALUETYPE_TRAITS(int, NPY_INT32, int32, "INT32")
131 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int, NPY_UINT32, uint32, "UINT32")
132 #elif VIGRA_BITSOF_INT == 64
133 VIGRA_NUMPY_VALUETYPE_TRAITS(int, NPY_INT64, int64, "DOUBLE")
134 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int, NPY_UINT64, uint64, "DOUBLE")
135 #endif
136 
137 #ifdef PY_LONG_LONG
138 # if VIGRA_BITSOF_LONG_LONG == 32
139 VIGRA_NUMPY_VALUETYPE_TRAITS(long long, NPY_INT32, int32, "INT32")
140 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT32, uint32, "UINT32")
141 # elif VIGRA_BITSOF_LONG_LONG == 64
142 VIGRA_NUMPY_VALUETYPE_TRAITS(long long, NPY_INT64, int64, "DOUBLE")
143 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT64, uint64, "DOUBLE")
144 # endif
145 #endif
146 
147 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float32, NPY_FLOAT32, float32, "FLOAT")
148 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float64, NPY_FLOAT64, float64, "DOUBLE")
149 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE
150 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_longdouble, NPY_LONGDOUBLE, longdouble, "")
151 #endif
152 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cfloat, NPY_CFLOAT, complex64, "")
153 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_float>, NPY_CFLOAT, complex64, "")
154 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cdouble, NPY_CDOUBLE, complex128, "")
155 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_double>, NPY_CDOUBLE, complex128, "")
156 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_clongdouble, NPY_CLONGDOUBLE, clongdouble, "")
157 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE
158 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_longdouble>, NPY_CLONGDOUBLE, clongdouble, "")
159 #endif
160 
161 #undef VIGRA_NUMPY_VALUETYPE_TRAITS
162 
163 /********************************************************/
164 /* */
165 /* NumpyArrayTraits */
166 /* */
167 /********************************************************/
168 
169 template<unsigned int N, class T, class Stride>
170 struct NumpyArrayTraits;
171 
172 /********************************************************/
173 
174 template<unsigned int N, class T>
175 struct NumpyArrayTraits<N, T, StridedArrayTag>
176 {
177  typedef T dtype;
178  typedef T value_type;
179  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
180  static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
181 
182  static bool isArray(PyObject * obj)
183  {
184  return obj && PyArray_Check(obj);
185  }
186 
187  static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */
188  {
189  return ValuetypeTraits::isValuetypeCompatible(obj);
190  }
191 
192  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
193  {
194  int ndim = PyArray_NDIM(array);
195 
196  return ndim == N;
197  }
198 
199  // The '*Compatible' functions are called whenever a NumpyArray is to be constructed
200  // from a Python numpy.ndarray to check whether types and memory layout are
201  // compatible. During overload resolution, boost::python iterates through the list
202  // of overloads and invokes the first function where all arguments pass this check.
203  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
204  {
205  return isShapeCompatible(obj) && isValuetypeCompatible(obj);
206  }
207 
208  // Construct a tagged shape from a 'shape - axistags' pair (called in
209  // NumpyArray::taggedShape()).
210  template <class U>
211  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
212  {
213  return TaggedShape(shape, axistags);
214  }
215 
216  // Construct a tagged shape from a 'shape - order' pair by creating
217  // the appropriate axistags object for that order and NumpyArray type.
218  // (called in NumpyArray constructors via NumpyArray::init())
219  template <class U>
220  static TaggedShape taggedShape(TinyVector<U, N> const & shape,
221  std::string const & /* order */ = "")
222  {
223  // We ignore the 'order' parameter, because we don't know the axis meaning
224  // in a plain array (use Singleband, Multiband, TinyVector etc. instead).
225  // Since we also have no useful axistags in this case, we enforce
226  // the result array to be a plain numpy.ndarray by passing empty axistags.
227  return TaggedShape(shape, PyAxisTags());
228  }
229 
230  // Adjust a TaggedShape that was created by another array to the properties of
231  // the present NumpyArray type (called in NumpyArray::reshapeIfEmpty()).
232  static void finalizeTaggedShape(TaggedShape & tagged_shape)
233  {
234  vigra_precondition(tagged_shape.size() == N,
235  "reshapeIfEmpty(): tagged_shape has wrong size.");
236  }
237 
238  // This function is used to synchronize the axis re-ordering of 'data'
239  // with that of 'array'. For example, when we want to apply Gaussian smoothing
240  // with a different scale for each axis, 'data' would contains those scales,
241  // and permuteLikewise() would make sure that the scales are applied to the right
242  // axes, regardless of axis re-ordering.
243  template <class ARRAY>
244  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
245  {
246  vigra_precondition((int)data.size() == N,
247  "NumpyArray::permuteLikewise(): size mismatch.");
248 
249  ArrayVector<npy_intp> permute;
250  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
251  AxisInfo::AllAxes, true);
252 
253  if(permute.size() != 0)
254  {
255  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
256  }
257  }
258 
259  // This function is called in NumpyArray::setupArrayView() to determine the
260  // desired axis re-ordering.
261  template <class U>
262  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
263  {
264  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
265  AxisInfo::AllAxes, true);
266 
267  if(permute.size() == 0)
268  {
269  permute.resize(N);
270  linearSequence(permute.begin(), permute.end());
271  }
272  }
273 
274  // This function is called in NumpyArray::makeUnsafeReference() to create
275  // a numpy.ndarray view for a block of memory managed by C++.
276  // The term 'unsafe' should remind you that memory management cannot be done
277  // automatically, bu must be done explicitly by the programmer.
278  template <class U>
279  static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
280  T *data, TinyVector<U, N> const & stride)
281  {
282  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
283  return constructNumpyArrayFromData(shape, npyStride.begin(),
284  ValuetypeTraits::typeCode, data);
285  }
286 };
287 
288 /********************************************************/
289 
290 template<unsigned int N, class T>
291 struct NumpyArrayTraits<N, T, UnstridedArrayTag>
292 : public NumpyArrayTraits<N, T, StridedArrayTag>
293 {
294  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
295  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
296 
297  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
298  {
299  PyObject * obj = (PyObject *)array;
300  int ndim = PyArray_NDIM(array);
301  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
302  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
303  npy_intp * strides = PyArray_STRIDES(array);
304 
305  if(channelIndex < ndim)
306  {
307  // When we have a channel axis, it will become the innermost dimension
308  return (ndim == N && strides[channelIndex] == sizeof(T));
309  }
310  else if(majorIndex < ndim)
311  {
312  // When we have axistags, but no channel axis, the major spatial
313  // axis will be the innermost dimension
314  return (ndim == N && strides[majorIndex] == sizeof(T));
315  }
316  else
317  {
318  // When we have no axistags, the first axis will be the innermost dimension
319  return (ndim == N && strides[0] == sizeof(T));
320  }
321  }
322 
323  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
324  {
325  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
326  }
327 };
328 
329 /********************************************************/
330 
331 template<unsigned int N, class T>
332 struct NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
333 : public NumpyArrayTraits<N, T, StridedArrayTag>
334 {
335  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
336  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
337 
338  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
339  {
340  PyObject * obj = (PyObject *)array;
341  int ndim = PyArray_NDIM(array);
342  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
343 
344  // If we have no channel axis (because either we don't have axistags,
345  // or the tags do not contain a channel axis), ndim must match.
346  if(channelIndex == ndim)
347  return ndim == N;
348 
349  // Otherwise, the channel axis must be a singleton axis that we can drop.
350  return ndim == N+1 && PyArray_DIM(array, channelIndex) == 1;
351  }
352 
353  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
354  {
355  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
356  }
357 
358  template <class U>
359  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
360  {
361  return TaggedShape(shape, axistags).setChannelCount(1);
362  }
363 
364  template <class U>
365  static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
366  {
367  return TaggedShape(shape,
368  PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(1);
369  }
370 
371  static void finalizeTaggedShape(TaggedShape & tagged_shape)
372  {
373  if(tagged_shape.axistags.hasChannelAxis())
374  {
375  tagged_shape.setChannelCount(1);
376  vigra_precondition(tagged_shape.size() == N+1,
377  "reshapeIfEmpty(): tagged_shape has wrong size.");
378  }
379  else
380  {
381  tagged_shape.setChannelCount(0);
382  vigra_precondition(tagged_shape.size() == N,
383  "reshapeIfEmpty(): tagged_shape has wrong size.");
384  }
385  }
386 
387  template <class ARRAY>
388  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
389  {
390  vigra_precondition((int)data.size() == N,
391  "NumpyArray::permuteLikewise(): size mismatch.");
392 
393  ArrayVector<npy_intp> permute;
394  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
395  AxisInfo::NonChannel, true);
396 
397  if(permute.size() == 0)
398  {
399  permute.resize(N);
400  linearSequence(permute.begin(), permute.end());
401  }
402 
403  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
404  }
405 
406  template <class U>
407  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
408  {
409  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
410  AxisInfo::AllAxes, true);
411  if(permute.size() == 0)
412  {
413  permute.resize(N);
414  linearSequence(permute.begin(), permute.end());
415  }
416  else if(permute.size() == N+1)
417  {
418  permute.erase(permute.begin());
419  }
420  }
421 };
422 
423 /********************************************************/
424 
425 template<unsigned int N, class T>
426 struct NumpyArrayTraits<N, Singleband<T>, UnstridedArrayTag>
427 : public NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
428 {
429  typedef NumpyArrayTraits<N, T, UnstridedArrayTag> UnstridedTraits;
430  typedef NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> BaseType;
431  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
432 
433  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
434  {
435  PyObject * obj = (PyObject *)array;
436  int ndim = PyArray_NDIM(array);
437  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
438  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
439  npy_intp * strides = PyArray_STRIDES(array);
440 
441  // If we have no axistags, ndim must match, and axis 0 must be unstrided.
442  if(majorIndex == ndim)
443  return N == ndim && strides[0] == sizeof(T);
444 
445  // If we have axistags, but no channel axis, ndim must match,
446  // and the major non-channel axis must be unstrided.
447  if(channelIndex == ndim)
448  return N == ndim && strides[majorIndex] == sizeof(T);
449 
450  // Otherwise, the channel axis must be a singleton axis that we can drop,
451  // and the major non-channel axis must be unstrided.
452  return ndim == N+1 && PyArray_DIM(array, channelIndex) == 1 &&
453  strides[majorIndex] == sizeof(T);
454  }
455 
456  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
457  {
458  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
459  }
460 };
461 
462 /********************************************************/
463 
464 template<unsigned int N, class T>
465 struct NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
466 : public NumpyArrayTraits<N, T, StridedArrayTag>
467 {
468  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
469  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
470 
471  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
472  {
473  PyObject * obj = (PyObject*)array;
474  int ndim = PyArray_NDIM(array);
475  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
476  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
477 
478  if(channelIndex < ndim)
479  {
480  // When we have a channel axis, ndim must match.
481  return ndim == N;
482  }
483  else if(majorIndex < ndim)
484  {
485  // When we have axistags, but no channel axis, we must add a singleton axis.
486  return ndim == N-1;
487  }
488  else
489  {
490  // When we have no axistags, we may add a singleton dimension.
491  return ndim == N || ndim == N-1;
492  }
493  }
494 
495  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
496  {
497  return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
498  }
499 
500  template <class U>
501  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
502  {
503  return TaggedShape(shape, axistags).setChannelIndexLast();
504  }
505 
506  template <class U>
507  static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
508  {
509  return TaggedShape(shape,
510  PyAxisTags(detail::defaultAxistags(shape.size(), order))).setChannelIndexLast();
511  }
512 
513  static void finalizeTaggedShape(TaggedShape & tagged_shape)
514  {
515  // When there is only one channel, and the axistags don't enforce an
516  // explicit channel axis, we return an array without explicit channel axis.
517  if(tagged_shape.channelCount() == 1 && !tagged_shape.axistags.hasChannelAxis())
518  {
519  tagged_shape.setChannelCount(0);
520  vigra_precondition(tagged_shape.size() == N-1,
521  "reshapeIfEmpty(): tagged_shape has wrong size.");
522  }
523  else
524  {
525  vigra_precondition(tagged_shape.size() == N,
526  "reshapeIfEmpty(): tagged_shape has wrong size.");
527  }
528  }
529 
530  template <class ARRAY>
531  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
532  {
533  ArrayVector<npy_intp> permute;
534 
535  if((int)data.size() == N)
536  {
537  vigra_precondition(PyArray_NDIM((PyArrayObject*)array.get()) == N,
538  "NumpyArray::permuteLikewise(): input array has no channel axis.");
539 
540  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
541  AxisInfo::AllAxes, true);
542 
543  if(permute.size() == 0)
544  {
545  permute.resize(N);
546  linearSequence(permute.begin(), permute.end());
547  }
548  else
549  {
550  // rotate channel axis to last position
551  int channelIndex = permute[0];
552  for(unsigned k=1; k<N; ++k)
553  permute[k-1] = permute[k];
554  permute[N-1] = channelIndex;
555  }
556  }
557  else
558  {
559  vigra_precondition((int)data.size() == N-1,
560  "NumpyArray::permuteLikewise(): size mismatch.");
561 
562  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
563  AxisInfo::NonChannel, true);
564 
565  if(permute.size() == 0)
566  {
567  permute.resize(N-1);
568  linearSequence(permute.begin(), permute.end());
569  }
570  }
571 
572  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
573  }
574 
575  template <class U>
576  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
577  {
578  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
579  AxisInfo::AllAxes, true);
580 
581  if(permute.size() == 0)
582  {
583  permute.resize(PyArray_NDIM((PyArrayObject*)array.get()));
584  linearSequence(permute.begin(), permute.end());
585  }
586  else if(permute.size() == N)
587  {
588  // if we have a channel axis, rotate it to last position
589  int channelIndex = permute[0];
590  for(decltype(permute.size()) k=1; k<N; ++k)
591  permute[k-1] = permute[k];
592  permute[N-1] = channelIndex;
593  }
594  }
595 };
596 
597 /********************************************************/
598 
599 template<unsigned int N, class T>
600 struct NumpyArrayTraits<N, Multiband<T>, UnstridedArrayTag>
601 : public NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
602 {
603  typedef NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> BaseType;
604  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
605 
606  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
607  {
608  PyObject * obj = (PyObject *)array;
609  int ndim = PyArray_NDIM(array);
610  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
611  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
612  npy_intp * strides = PyArray_STRIDES(array);
613 
614  if(channelIndex < ndim)
615  {
616  // When we have a channel axis, ndim must match, and the major non-channel
617  // axis must be unstrided.
618  return ndim == N && strides[majorIndex] == sizeof(T);
619  }
620  else if(majorIndex < ndim)
621  {
622  // When we have axistags, but no channel axis, we will add a
623  // singleton channel axis, and the major non-channel axis must be unstrided.
624  return ndim == N-1 && strides[majorIndex] == sizeof(T);
625  }
626  else
627  {
628  // When we have no axistags, axis 0 must be unstrided, but we
629  // may add a singleton dimension at the end.
630  return (ndim == N || ndim == N-1) && strides[0] == sizeof(T);
631  }
632  }
633 
634  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
635  {
636  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
637  }
638 };
639 
640 /********************************************************/
641 
642 template<unsigned int N, int M, class T>
643 struct NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
644 {
645  typedef T dtype;
646  typedef TinyVector<T, M> value_type;
647  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
648  static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
649 
650  static bool isArray(PyObject * obj)
651  {
652  return obj && PyArray_Check(obj);
653  }
654 
655  static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */
656  {
657  return ValuetypeTraits::isValuetypeCompatible(obj);
658  }
659 
660  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
661  {
662  PyObject * obj = (PyObject *)array;
663 
664  // We need an extra channel axis.
665  if(PyArray_NDIM(array) != N+1)
666  return false;
667 
668  // When there are no axistags, we assume that the last axis represents the channels.
669  long channelIndex = pythonGetAttr(obj, "channelIndex", N);
670  npy_intp * strides = PyArray_STRIDES(array);
671 
672  // find the non-channel axis with smallest stride
673  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", N+1);
674  if(majorIndex >= N+1)
675  {
676  npy_intp smallest = NumericTraits<npy_intp>::max();
677  for(unsigned int k=0; k<N+1; ++k)
678  {
679  if(k == channelIndex)
680  continue;
681  if(strides[k] < smallest)
682  {
683  smallest = strides[k];
684  majorIndex = k;
685  }
686  }
687  }
688 
689  return PyArray_DIM(array, channelIndex) == M &&
690  strides[channelIndex] == sizeof(T) &&
691  strides[majorIndex] % (M*sizeof(T)) == 0;
692  }
693 
694  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
695  {
696  return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
697  }
698 
699  template <class U>
700  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
701  {
702  return TaggedShape(shape, axistags).setChannelCount(M);
703  }
704 
705  template <class U>
706  static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
707  {
708  return TaggedShape(shape,
709  PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(M);
710  }
711 
712  static void finalizeTaggedShape(TaggedShape & tagged_shape)
713  {
714  tagged_shape.setChannelCount(M);
715  vigra_precondition(tagged_shape.size() == N+1,
716  "reshapeIfEmpty(): tagged_shape has wrong size.");
717  }
718 
719  template <class ARRAY>
720  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
721  {
722  vigra_precondition((int)data.size() == N,
723  "NumpyArray::permuteLikewise(): size mismatch.");
724 
725  ArrayVector<npy_intp> permute;
726  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
727  AxisInfo::NonChannel, true);
728 
729  if(permute.size() == 0)
730  {
731  permute.resize(N);
732  linearSequence(permute.begin(), permute.end());
733  }
734 
735  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
736  }
737 
738  template <class U>
739  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
740  {
741  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
742  AxisInfo::AllAxes, true);
743  if(permute.size() == 0)
744  {
745  permute.resize(N);
746  linearSequence(permute.begin(), permute.end());
747  }
748  else if(permute.size() == N+1)
749  {
750  permute.erase(permute.begin());
751  }
752  }
753 
754  template <class U>
755  static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
756  value_type *data, TinyVector<U, N> const & stride)
757  {
758  TinyVector<npy_intp, N+1> npyShape;
759  std::copy(shape.begin(), shape.end(), npyShape.begin());
760  npyShape[N] = M;
761 
762  TinyVector<npy_intp, N+1> npyStride;
763  std::transform(
764  stride.begin(), stride.end(), npyStride.begin(),
765  std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
766  npyStride[N] = sizeof(T);
767 
768  return constructNumpyArrayFromData(npyShape, npyStride.begin(),
769  ValuetypeTraits::typeCode, data);
770  }
771 };
772 
773 /********************************************************/
774 
775 template<unsigned int N, int M, class T>
776 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag>
777 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
778 {
779  typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType;
780  typedef typename BaseType::value_type value_type;
781  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
782 
783  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
784  {
785  PyObject * obj = (PyObject *)array;
786  int ndim = PyArray_NDIM(array);
787 
788  // We need an extra channel axis.
789  if(ndim != N+1)
790  return false;
791 
792  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
793  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
794  npy_intp * strides = PyArray_STRIDES(array);
795 
796  if(majorIndex < ndim)
797  {
798  // We have axistags, but no channel axis => cannot be a TinyVector image
799  if(channelIndex == ndim)
800  return false;
801 
802  // We have an explicit channel axis => shapes and strides must match
803  return PyArray_DIM(array, channelIndex) == M &&
804  strides[channelIndex] == sizeof(T) &&
805  strides[majorIndex] == sizeof(TinyVector<T, M>);
806 
807 
808  }
809  else
810  {
811  // we have no axistags => we assume that the channel axis is last
812  return PyArray_DIM(array, N) == M &&
813  strides[N] == sizeof(T) &&
814  strides[0] == sizeof(TinyVector<T, M>);
815  }
816  }
817 
818  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
819  {
820  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
821  }
822 };
823 
824 /********************************************************/
825 
826 template<unsigned int N, class T>
827 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
828 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag>
829 {
830  typedef T dtype;
831  typedef RGBValue<T> value_type;
832  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
833 };
834 
835 /********************************************************/
836 
837 template<unsigned int N, class T>
838 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag>
839 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
840 {
841  typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits;
842  typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType;
843  typedef typename BaseType::value_type value_type;
844  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
845 
846  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
847  {
848  return UnstridedTraits::isShapeCompatible(obj);
849  }
850 
851  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
852  {
853  return UnstridedTraits::isPropertyCompatible(obj);
854  }
855 };
856 
857 } // namespace vigra
858 
859 #endif // VIGRA_NUMPY_ARRAY_TRAITS_HXX
void applyPermutation(IndexIterator index_first, IndexIterator index_last, InIterator in, OutIterator out)
Sort an array according to the given index permutation.
Definition: algorithm.hxx:456
void linearSequence(Iterator first, Iterator last, Value start, Value step)
Fill an array with a sequence of numbers.
Definition: algorithm.hxx:208

© 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.11.1 (Fri May 19 2017)