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

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

© 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.9.0 (Sun Aug 10 2014)