vigra - vigra/numpy_array_traits.hxx Source File

[ 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 #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
47 namespace vigra {
48
49 /********************************************************/
50 /* */
51 /* NumpyArrayValuetypeTraits */
52 /* */
53 /********************************************************/
54
55 template<class ValueType>
56 struct ERROR_NumpyArrayValuetypeTraits_not_specialized_for_ { };
57
58 template<class ValueType>
59 struct NumpyArrayValuetypeTraits
60 {
61 static bool isValuetypeCompatible(PyArrayObject const * obj)
62 {
63 return ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType>();
64 }
65
66 static ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> typeCode;
67
68 static std::string typeName()
69 {
70 return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
71 }
72
73 static std::string typeNameImpex()
74 {
75 return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
76 }
77
78 static PyObject * typeObject()
79 {
80 return (PyObject *)0;
81 }
82 };
83
84 template<class ValueType>
85 ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> NumpyArrayValuetypeTraits<ValueType>::typeCode;
86
87 #define VIGRA_NUMPY_VALUETYPE_TRAITS(type, typeID, numpyTypeName, impexTypeName) \
88 template <> \
89 struct NumpyArrayValuetypeTraits<type > \
90 { \
91 static bool isValuetypeCompatible(PyArrayObject const * obj) /* obj must not be NULL */ \
92 { \
93 return PyArray_EquivTypenums(typeID, PyArray_DESCR((PyArrayObject *)obj)->type_num) && \
94 PyArray_ITEMSIZE((PyArrayObject *)obj) == sizeof(type); \
95 } \
96 \
97 static NPY_TYPES const typeCode = typeID; \
98 \
99 static std::string typeName() \
100 { \
101 return #numpyTypeName; \
102 } \
103 \
104 static std::string typeNameImpex() \
105 { \
106 return impexTypeName; \
107 } \
108 \
109 static PyObject * typeObject() \
110 { \
111 return PyArray_TypeObjectFromType(typeID); \
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(int 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(int 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 return PyArray_DIM(array, channelIndex) == M && strides[channelIndex] == sizeof(T);
673 }
674
675 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
676 {
677 return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
678 }
679
680 template <class U>
681 static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
682 {
683 return TaggedShape(shape, axistags).setChannelCount(M);
684 }
685
686 template <class U>
687 static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
688 {
689 return TaggedShape(shape,
690 PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(M);
691 }
692
693 static void finalizeTaggedShape(TaggedShape & tagged_shape)
694 {
695 tagged_shape.setChannelCount(M);
696 vigra_precondition(tagged_shape.size() == N+1,
697 "reshapeIfEmpty(): tagged_shape has wrong size.");
698 }
699
700 template <class ARRAY>
701 static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
702 {
703 vigra_precondition((int)data.size() == N,
704 "NumpyArray::permuteLikewise(): size mismatch.");
705
706 ArrayVector<npy_intp> permute;
707 detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
708 AxisInfo::NonChannel, true);
709
710 if(permute.size() == 0)
711 {
712 permute.resize(N);
713 linearSequence(permute.begin(), permute.end());
714 }
715
716 applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
717 }
718
719 template <class U>
720 static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
721 {
722 detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder",
723 AxisInfo::AllAxes, true);
724 if(permute.size() == 0)
725 {
726 permute.resize(N);
727 linearSequence(permute.begin(), permute.end());
728 }
729 else if(permute.size() == N+1)
730 {
731 permute.erase(permute.begin());
732 }
733 }
734
735 template <class U>
736 static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
737 value_type *data, TinyVector<U, N> const & stride)
738 {
739 TinyVector<npy_intp, N+1> npyShape;
740 std::copy(shape.begin(), shape.end(), npyShape.begin());
741 npyShape[N] = M;
742
743 TinyVector<npy_intp, N+1> npyStride;
744 std::transform(
745 stride.begin(), stride.end(), npyStride.begin(),
746 std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
747 npyStride[N] = sizeof(T);
748
749 return constructNumpyArrayFromData(npyShape, npyStride.begin(),
750 ValuetypeTraits::typeCode, data);
751 }
752 };
753
754 /********************************************************/
755
756 template<unsigned int N, int M, class T>
757 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag>
758 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
759 {
760 typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType;
761 typedef typename BaseType::value_type value_type;
762 typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
763
764 static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
765 {
766 PyObject * obj = (PyObject *)array;
767 int ndim = PyArray_NDIM(array);
768
769 // We need an extra channel axis.
770 if(ndim != N+1)
771 return false;
772
773 long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
774 long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
775 npy_intp * strides = PyArray_STRIDES(array);
776
777 if(majorIndex < ndim)
778 {
779 // We have axistags, but no channel axis => cannot be a TinyVector image
780 if(channelIndex == ndim)
781 return false;
782
783 // We have an explicit channel axis => shapes and strides must match
784 return PyArray_DIM(array, channelIndex) == M &&
785 strides[channelIndex] == sizeof(T) &&
786 strides[majorIndex] == sizeof(TinyVector<T, M>);
787
788
789 }
790 else
791 {
792 // we have no axistags => we assume that the channel axis is last
793 return PyArray_DIM(array, N) == M &&
794 strides[N] == sizeof(T) &&
795 strides[0] == sizeof(TinyVector<T, M>);
796 }
797 }
798
799 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
800 {
801 return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
802 }
803 };
804
805 /********************************************************/
806
807 template<unsigned int N, class T>
808 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
809 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag>
810 {
811 typedef T dtype;
812 typedef RGBValue<T> value_type;
813 typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
814 };
815
816 /********************************************************/
817
818 template<unsigned int N, class T>
819 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag>
820 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
821 {
822 typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits;
823 typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType;
824 typedef typename BaseType::value_type value_type;
825 typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
826
827 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
828 {
829 return UnstridedTraits::isShapeCompatible(obj);
830 }
831
832 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
833 {
834 return UnstridedTraits::isPropertyCompatible(obj);
835 }
836 };
837
838 } // namespace vigra
839
840 #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.10.0 (Mon Nov 18 2013)