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

© 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.12.1