[ 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, std::string const & order = "")
290  {
291  // we ignore the 'order' parameter, because we don't know the axis meaning
292  // in a plain array (use Singleband, Multiband, TinyVector etc. instead)
293  return TaggedShape(shape, PyAxisTags(detail::emptyAxistags(shape.size())));
294  }
295 
296  // Adjust a TaggedShape that was created by another array to the properties of
297  // the present NumpyArray type (called in NumpyArray::reshapeIfEmpty()).
298  static void finalizeTaggedShape(TaggedShape & tagged_shape)
299  {
300  vigra_precondition(tagged_shape.size() == N,
301  "reshapeIfEmpty(): tagged_shape has wrong size.");
302  }
303 
304  // This function is used to synchronize the axis re-ordering of 'data'
305  // with that of 'array'. For example, when we want to apply Gaussian smoothing
306  // with a different scale for each axis, 'data' would contains those scales,
307  // and permuteLikewise() would make sure that the scales are applied to the right
308  // axes, regardless of axis re-ordering.
309  template <class ARRAY>
310  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
311  {
312  vigra_precondition((int)data.size() == N,
313  "NumpyArray::permuteLikewise(): size mismatch.");
314 
315  ArrayVector<npy_intp> permute;
316  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
317  AxisInfo::AllAxes, true);
318 
319  if(permute.size() != 0)
320  {
321  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
322  }
323  }
324 
325  // This function is called in NumpyArray::setupArrayView() to determine the
326  // desired axis re-ordering.
327  template <class U>
328  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
329  {
330  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
331  AxisInfo::AllAxes, true);
332 
333  if(permute.size() == 0)
334  {
335  permute.resize(N);
336  linearSequence(permute.begin(), permute.end());
337  }
338  }
339 
340  // This function is called in NumpyArray::makeUnsafeReference() to create
341  // a numpy.ndarray view for a block of memory managed by C++.
342  // The term 'unsafe' should remind you that memory management cannot be done
343  // automatically, bu must be done explicitly by the programmer.
344  template <class U>
345  static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
346  T *data, TinyVector<U, N> const & stride)
347  {
348  TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
349  return constructNumpyArrayFromData(shape, npyStride.begin(),
350  ValuetypeTraits::typeCode, data);
351  }
352 };
353 
354 /********************************************************/
355 
356 template<unsigned int N, class T>
357 struct NumpyArrayTraits<N, T, UnstridedArrayTag>
358 : public NumpyArrayTraits<N, T, StridedArrayTag>
359 {
360  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
361  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
362 
363  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
364  {
365  PyObject * obj = (PyObject *)array;
366  int ndim = PyArray_NDIM(obj);
367  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
368  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
369  npy_intp * strides = PyArray_STRIDES(obj);
370 
371  if(channelIndex < ndim)
372  {
373  // When we have a channel axis, it will become the innermost dimension
374  return (ndim == N && strides[channelIndex] == sizeof(T));
375  }
376  else if(majorIndex < ndim)
377  {
378  // When we have axistags, but no channel axis, the major spatial
379  // axis will be the innermost dimension
380  return (ndim == N && strides[majorIndex] == sizeof(T));
381  }
382  else
383  {
384  // When we have no axistags, the first axis will be the innermost dimension
385  return (ndim == N && strides[0] == sizeof(T));
386  }
387  }
388 
389  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
390  {
391  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
392  }
393 };
394 
395 /********************************************************/
396 
397 template<unsigned int N, class T>
398 struct NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
399 : public NumpyArrayTraits<N, T, StridedArrayTag>
400 {
401  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
402  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
403 
404  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
405  {
406  PyObject * obj = (PyObject *)array;
407  int ndim = PyArray_NDIM(obj);
408  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
409 
410  // If we have no channel axis (because either we don't have axistags,
411  // or the tags do not contain a channel axis), ndim must match.
412  if(channelIndex == ndim)
413  return ndim == N;
414 
415  // Otherwise, the channel axis must be a singleton axis that we can drop.
416  return ndim == N+1 && PyArray_DIM(obj, channelIndex) == 1;
417  }
418 
419  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
420  {
421  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
422  }
423 
424  template <class U>
425  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
426  {
427  return TaggedShape(shape, axistags).setChannelCount(1);
428  }
429 
430  template <class U>
431  static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
432  {
433  return TaggedShape(shape,
434  PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(1);
435  }
436 
437  static void finalizeTaggedShape(TaggedShape & tagged_shape)
438  {
439  if(tagged_shape.axistags.hasChannelAxis())
440  {
441  tagged_shape.setChannelCount(1);
442  vigra_precondition(tagged_shape.size() == N+1,
443  "reshapeIfEmpty(): tagged_shape has wrong size.");
444  }
445  else
446  {
447  tagged_shape.setChannelCount(0);
448  vigra_precondition(tagged_shape.size() == N,
449  "reshapeIfEmpty(): tagged_shape has wrong size.");
450  }
451  }
452 
453  template <class ARRAY>
454  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
455  {
456  vigra_precondition((int)data.size() == N,
457  "NumpyArray::permuteLikewise(): size mismatch.");
458 
459  ArrayVector<npy_intp> permute;
460  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
461  AxisInfo::NonChannel, true);
462 
463  if(permute.size() == 0)
464  {
465  permute.resize(N);
466  linearSequence(permute.begin(), permute.end());
467  }
468 
469  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
470  }
471 
472  template <class U>
473  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
474  {
475  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
476  AxisInfo::AllAxes, true);
477  if(permute.size() == 0)
478  {
479  permute.resize(N);
480  linearSequence(permute.begin(), permute.end());
481  }
482  else if(permute.size() == N+1)
483  {
484  permute.erase(permute.begin());
485  }
486  }
487 };
488 
489 /********************************************************/
490 
491 template<unsigned int N, class T>
492 struct NumpyArrayTraits<N, Singleband<T>, UnstridedArrayTag>
493 : public NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
494 {
495  typedef NumpyArrayTraits<N, T, UnstridedArrayTag> UnstridedTraits;
496  typedef NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> BaseType;
497  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
498 
499  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
500  {
501  PyObject * obj = (PyObject *)array;
502  int ndim = PyArray_NDIM(obj);
503  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
504  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
505  npy_intp * strides = PyArray_STRIDES(obj);
506 
507  // If we have no axistags, ndim must match, and axis 0 must be unstrided.
508  if(majorIndex == ndim)
509  return N == ndim && strides[0] == sizeof(T);
510 
511  // If we have axistags, but no channel axis, ndim must match,
512  // and the major non-channel axis must be unstrided.
513  if(channelIndex == ndim)
514  return N == ndim && strides[majorIndex] == sizeof(T);
515 
516  // Otherwise, the channel axis must be a singleton axis that we can drop,
517  // and the major non-channel axis must be unstrided.
518  return ndim == N+1 && PyArray_DIM(obj, channelIndex) == 1 &&
519  strides[majorIndex] == sizeof(T);
520  }
521 
522  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
523  {
524  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
525  }
526 };
527 
528 /********************************************************/
529 
530 template<unsigned int N, class T>
531 struct NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
532 : public NumpyArrayTraits<N, T, StridedArrayTag>
533 {
534  typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
535  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
536 
537  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
538  {
539  PyObject * obj = (PyObject*)array;
540  int ndim = PyArray_NDIM(obj);
541  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
542  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
543 
544  if(channelIndex < ndim)
545  {
546  // When we have a channel axis, ndim must match.
547  return ndim == N;
548  }
549  else if(majorIndex < ndim)
550  {
551  // When we have axistags, but no channel axis, we must add a singleton axis.
552  return ndim == N-1;
553  }
554  else
555  {
556  // When we have no axistags, we may add a singleton dimension.
557  return ndim == N || ndim == N-1;
558  }
559  }
560 
561  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
562  {
563  return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
564  }
565 
566  template <class U>
567  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
568  {
569  return TaggedShape(shape, axistags).setChannelIndexLast();
570  }
571 
572  template <class U>
573  static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
574  {
575  return TaggedShape(shape,
576  PyAxisTags(detail::defaultAxistags(shape.size(), order))).setChannelIndexLast();
577  }
578 
579  static void finalizeTaggedShape(TaggedShape & tagged_shape)
580  {
581  if(tagged_shape.axistags &&
582  !tagged_shape.axistags.hasChannelAxis() && tagged_shape.channelCount() == 1)
583  {
584  tagged_shape.setChannelCount(0);
585  vigra_precondition(tagged_shape.size() == N-1,
586  "reshapeIfEmpty(): tagged_shape has wrong size.");
587  }
588  else
589  {
590  vigra_precondition(tagged_shape.size() == N,
591  "reshapeIfEmpty(): tagged_shape has wrong size.");
592  }
593  }
594 
595  template <class ARRAY>
596  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
597  {
598  ArrayVector<npy_intp> permute;
599 
600  if((int)data.size() == N)
601  {
602  vigra_precondition(PyArray_NDIM((PyArrayObject*)array.get()) == N,
603  "NumpyArray::permuteLikewise(): input array has no channel axis.");
604 
605  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
606  AxisInfo::AllAxes, true);
607 
608  if(permute.size() == 0)
609  {
610  permute.resize(N);
611  linearSequence(permute.begin(), permute.end());
612  }
613  else
614  {
615  // rotate channel axis to last position
616  int channelIndex = permute[0];
617  for(int k=1; k<N; ++k)
618  permute[k-1] = permute[k];
619  permute[N-1] = channelIndex;
620  }
621  }
622  else
623  {
624  vigra_precondition((int)data.size() == N-1,
625  "NumpyArray::permuteLikewise(): size mismatch.");
626 
627  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
628  AxisInfo::NonChannel, true);
629 
630  if(permute.size() == 0)
631  {
632  permute.resize(N-1);
633  linearSequence(permute.begin(), permute.end());
634  }
635  }
636 
637  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
638  }
639 
640  template <class U>
641  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
642  {
643  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
644  AxisInfo::AllAxes, true);
645 
646  if(permute.size() == 0)
647  {
648  permute.resize(PyArray_NDIM((PyArrayObject*)array.get()));
649  linearSequence(permute.begin(), permute.end());
650  }
651  else if(permute.size() == N)
652  {
653  // if we have a channel axis, rotate it to last position
654  int channelIndex = permute[0];
655  for(int k=1; k<N; ++k)
656  permute[k-1] = permute[k];
657  permute[N-1] = channelIndex;
658  }
659  }
660 };
661 
662 /********************************************************/
663 
664 template<unsigned int N, class T>
665 struct NumpyArrayTraits<N, Multiband<T>, UnstridedArrayTag>
666 : public NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
667 {
668  typedef NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> BaseType;
669  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
670 
671  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
672  {
673  PyObject * obj = (PyObject *)array;
674  int ndim = PyArray_NDIM(obj);
675  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
676  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
677  npy_intp * strides = PyArray_STRIDES(obj);
678 
679  if(channelIndex < ndim)
680  {
681  // When we have a channel axis, ndim must match, and the major non-channel
682  // axis must be unstrided.
683  return ndim == N && strides[majorIndex] == sizeof(T);
684  }
685  else if(majorIndex < ndim)
686  {
687  // When we have axistags, but no channel axis, we will add a
688  // singleton channel axis, and the major non-channel axis must be unstrided.
689  return ndim == N-1 && strides[majorIndex] == sizeof(T);
690  }
691  else
692  {
693  // When we have no axistags, axis 0 must be unstrided, but we
694  // may add a singleton dimension at the end.
695  return (ndim == N || ndim == N-1) && strides[0] == sizeof(T);
696  }
697  }
698 
699  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
700  {
701  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
702  }
703 };
704 
705 /********************************************************/
706 
707 template<unsigned int N, int M, class T>
708 struct NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
709 {
710  typedef T dtype;
711  typedef TinyVector<T, M> value_type;
712  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
713  static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
714 
715  static bool isArray(PyObject * obj)
716  {
717  return obj && PyArray_Check(obj);
718  }
719 
720  static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */
721  {
722  return ValuetypeTraits::isValuetypeCompatible(obj);
723  }
724 
725  static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
726  {
727  PyObject * obj = (PyObject *)array;
728 
729  // We need an extra channel axis.
730  if(PyArray_NDIM(obj) != N+1)
731  return false;
732 
733  // When there are no axistags, we assume that the last axis represents the channels.
734  long channelIndex = pythonGetAttr(obj, "channelIndex", N);
735  npy_intp * strides = PyArray_STRIDES(obj);
736 
737  return PyArray_DIM(obj, channelIndex) == M && strides[channelIndex] == sizeof(T);
738  }
739 
740  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
741  {
742  return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
743  }
744 
745  template <class U>
746  static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
747  {
748  return TaggedShape(shape, axistags).setChannelCount(M);
749  }
750 
751  template <class U>
752  static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
753  {
754  return TaggedShape(shape,
755  PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(M);
756  }
757 
758  static void finalizeTaggedShape(TaggedShape & tagged_shape)
759  {
760  tagged_shape.setChannelCount(M);
761  vigra_precondition(tagged_shape.size() == N+1,
762  "reshapeIfEmpty(): tagged_shape has wrong size.");
763  }
764 
765  template <class ARRAY>
766  static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
767  {
768  vigra_precondition((int)data.size() == N,
769  "NumpyArray::permuteLikewise(): size mismatch.");
770 
771  ArrayVector<npy_intp> permute;
772  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
773  AxisInfo::NonChannel, true);
774 
775  if(permute.size() == 0)
776  {
777  permute.resize(N);
778  linearSequence(permute.begin(), permute.end());
779  }
780 
781  applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
782  }
783 
784  template <class U>
785  static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
786  {
787  detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
788  AxisInfo::AllAxes, true);
789  if(permute.size() == 0)
790  {
791  permute.resize(N);
792  linearSequence(permute.begin(), permute.end());
793  }
794  else if(permute.size() == N+1)
795  {
796  permute.erase(permute.begin());
797  }
798  }
799 
800  template <class U>
801  static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
802  value_type *data, TinyVector<U, N> const & stride)
803  {
804  TinyVector<npy_intp, N+1> npyShape;
805  std::copy(shape.begin(), shape.end(), npyShape.begin());
806  npyShape[N] = M;
807 
808  TinyVector<npy_intp, N+1> npyStride;
809  std::transform(
810  stride.begin(), stride.end(), npyStride.begin(),
811  std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
812  npyStride[N] = sizeof(T);
813 
814  return constructNumpyArrayFromData(npyShape, npyStride.begin(),
815  ValuetypeTraits::typeCode, data);
816  }
817 };
818 
819 /********************************************************/
820 
821 template<unsigned int N, int M, class T>
822 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag>
823 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
824 {
825  typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType;
826  typedef typename BaseType::value_type value_type;
827  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
828 
829  static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
830  {
831  PyObject * obj = (PyObject *)array;
832  int ndim = PyArray_NDIM(obj);
833 
834  // We need an extra channel axis.
835  if(ndim != N+1)
836  return false;
837 
838  long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
839  long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
840  npy_intp * strides = PyArray_STRIDES(obj);
841 
842  if(majorIndex < ndim)
843  {
844  // We have axistags, but no channel axis => cannot be a TinyVector image
845  if(channelIndex == ndim)
846  return false;
847 
848  // We have an explicit channel axis => shapes and strides must match
849  return PyArray_DIM(obj, channelIndex) == M &&
850  strides[channelIndex] == sizeof(T) &&
851  strides[majorIndex] == sizeof(TinyVector<T, M>);
852 
853 
854  }
855  else
856  {
857  // we have no axistags => we assume that the channel axis is last
858  return PyArray_DIM(obj, N) == M &&
859  strides[N] == sizeof(T) &&
860  strides[0] == sizeof(TinyVector<T, M>);
861  }
862  }
863 
864  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
865  {
866  return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
867  }
868 };
869 
870 /********************************************************/
871 
872 template<unsigned int N, class T>
873 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
874 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag>
875 {
876  typedef T dtype;
877  typedef RGBValue<T> value_type;
878  typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
879 };
880 
881 /********************************************************/
882 
883 template<unsigned int N, class T>
884 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag>
885 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
886 {
887  typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits;
888  typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType;
889  typedef typename BaseType::value_type value_type;
890  typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
891 
892  static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
893  {
894  return UnstridedTraits::isShapeCompatible(obj);
895  }
896 
897  static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
898  {
899  return UnstridedTraits::isPropertyCompatible(obj);
900  }
901 };
902 
903 } // namespace vigra
904 
905 #endif // VIGRA_NUMPY_ARRAY_TRAITS_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.8.0 (Wed Sep 26 2012)