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

vigra/numpy_array_traits.hxx VIGRA

00001 /************************************************************************/
00002 /*                                                                      */
00003 /*       Copyright 2009 by Ullrich Koethe and Hans Meine                */
00004 /*                                                                      */
00005 /*    This file is part of the VIGRA computer vision library.           */
00006 /*    The VIGRA Website is                                              */
00007 /*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
00008 /*    Please direct questions, bug reports, and contributions to        */
00009 /*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
00010 /*        vigra@informatik.uni-hamburg.de                               */
00011 /*                                                                      */
00012 /*    Permission is hereby granted, free of charge, to any person       */
00013 /*    obtaining a copy of this software and associated documentation    */
00014 /*    files (the "Software"), to deal in the Software without           */
00015 /*    restriction, including without limitation the rights to use,      */
00016 /*    copy, modify, merge, publish, distribute, sublicense, and/or      */
00017 /*    sell copies of the Software, and to permit persons to whom the    */
00018 /*    Software is furnished to do so, subject to the following          */
00019 /*    conditions:                                                       */
00020 /*                                                                      */
00021 /*    The above copyright notice and this permission notice shall be    */
00022 /*    included in all copies or substantial portions of the             */
00023 /*    Software.                                                         */
00024 /*                                                                      */
00025 /*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
00026 /*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
00027 /*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
00028 /*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
00029 /*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
00030 /*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
00031 /*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
00032 /*    OTHER DEALINGS IN THE SOFTWARE.                                   */
00033 /*                                                                      */
00034 /************************************************************************/
00035 
00036 #ifndef VIGRA_NUMPY_ARRAY_TRAITS_HXX
00037 #define VIGRA_NUMPY_ARRAY_TRAITS_HXX
00038 
00039 #include "numerictraits.hxx"
00040 #include "multi_array.hxx"
00041 #include "numpy_array_taggedshape.hxx"
00042 
00043 namespace vigra {
00044 
00045 /********************************************************/
00046 /*                                                      */
00047 /*              Singleband and Multiband                */
00048 /*                                                      */
00049 /********************************************************/
00050 
00051 template <class T>
00052 struct Singleband  // the resulting NumpyArray has no explicit channel axis 
00053                    // (i.e. the number of channels is implicitly one)
00054 {
00055     typedef T value_type;
00056 };
00057 
00058 template <class T>
00059 struct Multiband  // the last axis is explicitly designated as channel axis
00060 {
00061     typedef T value_type;
00062 };
00063 
00064 template<class T>
00065 struct NumericTraits<Singleband<T> >
00066 : public NumericTraits<T>
00067 {};
00068 
00069 template<class T>
00070 struct NumericTraits<Multiband<T> >
00071 {
00072     typedef Multiband<T> Type;
00073 /*
00074     typedef int Promote;
00075     typedef unsigned int UnsignedPromote;
00076     typedef double RealPromote;
00077     typedef std::complex<RealPromote> ComplexPromote;
00078 */
00079     typedef Type ValueType;
00080 
00081     typedef typename NumericTraits<T>::isIntegral isIntegral;
00082     typedef VigraFalseType isScalar;
00083     typedef typename NumericTraits<T>::isSigned isSigned;
00084     typedef typename NumericTraits<T>::isSigned isOrdered;
00085     typedef typename NumericTraits<T>::isSigned isComplex;
00086 /*
00087     static signed char zero() { return 0; }
00088     static signed char one() { return 1; }
00089     static signed char nonZero() { return 1; }
00090     static signed char min() { return SCHAR_MIN; }
00091     static signed char max() { return SCHAR_MAX; }
00092 
00093 #ifdef NO_INLINE_STATIC_CONST_DEFINITION
00094     enum { minConst = SCHAR_MIN, maxConst = SCHAR_MIN };
00095 #else
00096     static const signed char minConst = SCHAR_MIN;
00097     static const signed char maxConst = SCHAR_MIN;
00098 #endif
00099 
00100     static Promote toPromote(signed char v) { return v; }
00101     static RealPromote toRealPromote(signed char v) { return v; }
00102     static signed char fromPromote(Promote v) {
00103         return ((v < SCHAR_MIN) ? SCHAR_MIN : (v > SCHAR_MAX) ? SCHAR_MAX : v);
00104     }
00105     static signed char fromRealPromote(RealPromote v) {
00106         return ((v < 0.0)
00107                    ? ((v < (RealPromote)SCHAR_MIN)
00108                        ? SCHAR_MIN
00109                        : static_cast<signed char>(v - 0.5))
00110                    : (v > (RealPromote)SCHAR_MAX)
00111                        ? SCHAR_MAX
00112                        : static_cast<signed char>(v + 0.5));
00113     }
00114 */
00115 };
00116 
00117 /********************************************************/
00118 /*                                                      */
00119 /*               NumpyArrayValuetypeTraits              */
00120 /*                                                      */
00121 /********************************************************/
00122 
00123 template<class ValueType>
00124 struct ERROR_NumpyArrayValuetypeTraits_not_specialized_for_ { };
00125 
00126 template<class ValueType>
00127 struct NumpyArrayValuetypeTraits
00128 {
00129     static bool isValuetypeCompatible(PyArrayObject const * obj)
00130     {
00131         return ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType>();
00132     }
00133 
00134     static ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> typeCode;
00135 
00136     static std::string typeName()
00137     {
00138         return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
00139     }
00140 
00141     static std::string typeNameImpex()
00142     {
00143         return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
00144     }
00145 
00146     static PyObject * typeObject()
00147     {
00148         return (PyObject *)0;
00149     }
00150 };
00151 
00152 template<class ValueType>
00153 ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> NumpyArrayValuetypeTraits<ValueType>::typeCode;
00154 
00155 #define VIGRA_NUMPY_VALUETYPE_TRAITS(type, typeID, numpyTypeName, impexTypeName) \
00156 template <> \
00157 struct NumpyArrayValuetypeTraits<type > \
00158 { \
00159     static bool isValuetypeCompatible(PyArrayObject const * obj) /* obj must not be NULL */ \
00160     { \
00161         return PyArray_EquivTypenums(typeID, PyArray_DESCR((PyObject *)obj)->type_num) && \
00162                PyArray_ITEMSIZE((PyObject *)obj) == sizeof(type); \
00163     } \
00164     \
00165     static NPY_TYPES const typeCode = typeID; \
00166     \
00167     static std::string typeName() \
00168     { \
00169         return #numpyTypeName; \
00170     } \
00171     \
00172     static std::string typeNameImpex() \
00173     { \
00174         return impexTypeName; \
00175     } \
00176     \
00177     static PyObject * typeObject() \
00178     { \
00179         return PyArray_TypeObjectFromType(typeID); \
00180     } \
00181 };
00182 
00183 VIGRA_NUMPY_VALUETYPE_TRAITS(bool,           NPY_BOOL, bool, "UINT8")
00184 VIGRA_NUMPY_VALUETYPE_TRAITS(signed char,    NPY_INT8, int8, "INT16")
00185 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned char,  NPY_UINT8, uint8, "UINT8")
00186 VIGRA_NUMPY_VALUETYPE_TRAITS(short,          NPY_INT16, int16, "INT16")
00187 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned short, NPY_UINT16, uint16, "UINT16")
00188 
00189 #if VIGRA_BITSOF_LONG == 32
00190 VIGRA_NUMPY_VALUETYPE_TRAITS(long,           NPY_INT32, int32, "INT32")
00191 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long,  NPY_UINT32, uint32, "UINT32")
00192 #elif VIGRA_BITSOF_LONG == 64
00193 VIGRA_NUMPY_VALUETYPE_TRAITS(long,           NPY_INT64, int64, "DOUBLE")
00194 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long,  NPY_UINT64, uint64, "DOUBLE")
00195 #endif
00196 
00197 #if VIGRA_BITSOF_INT == 32
00198 VIGRA_NUMPY_VALUETYPE_TRAITS(int,            NPY_INT32, int32, "INT32")
00199 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int,   NPY_UINT32, uint32, "UINT32")
00200 #elif VIGRA_BITSOF_INT == 64
00201 VIGRA_NUMPY_VALUETYPE_TRAITS(int,            NPY_INT64, int64, "DOUBLE")
00202 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int,   NPY_UINT64, uint64, "DOUBLE")
00203 #endif
00204 
00205 #ifdef PY_LONG_LONG
00206 # if VIGRA_BITSOF_LONG_LONG == 32
00207 VIGRA_NUMPY_VALUETYPE_TRAITS(long long,            NPY_INT32, int32, "INT32")
00208 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long,   NPY_UINT32, uint32, "UINT32")
00209 # elif VIGRA_BITSOF_LONG_LONG == 64
00210 VIGRA_NUMPY_VALUETYPE_TRAITS(long long,          NPY_INT64, int64, "DOUBLE")
00211 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT64, uint64, "DOUBLE")
00212 # endif
00213 #endif
00214 
00215 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float32, NPY_FLOAT32, float32, "FLOAT")
00216 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float64, NPY_FLOAT64, float64, "DOUBLE")
00217 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE
00218 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_longdouble, NPY_LONGDOUBLE, longdouble, "")
00219 #endif
00220 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cfloat, NPY_CFLOAT, complex64, "")
00221 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_float>, NPY_CFLOAT, complex64, "")
00222 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cdouble, NPY_CDOUBLE, complex128, "")
00223 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_double>, NPY_CDOUBLE, complex128, "")
00224 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_clongdouble, NPY_CLONGDOUBLE, clongdouble, "")
00225 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE
00226 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_longdouble>, NPY_CLONGDOUBLE, clongdouble, "")
00227 #endif
00228 
00229 #undef VIGRA_NUMPY_VALUETYPE_TRAITS
00230 
00231 /********************************************************/
00232 /*                                                      */
00233 /*                  NumpyArrayTraits                    */
00234 /*                                                      */
00235 /********************************************************/
00236 
00237 template<unsigned int N, class T, class Stride>
00238 struct NumpyArrayTraits;
00239 
00240 /********************************************************/
00241 
00242 template<unsigned int N, class T>
00243 struct NumpyArrayTraits<N, T, StridedArrayTag>
00244 {
00245     typedef T dtype;
00246     typedef T value_type;
00247     typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
00248     static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
00249 
00250     static bool isArray(PyObject * obj)
00251     {
00252         return obj && PyArray_Check(obj);
00253     }
00254 
00255     static bool isValuetypeCompatible(PyArrayObject * obj)  /* obj must not be NULL */
00256     {
00257         return ValuetypeTraits::isValuetypeCompatible(obj);
00258     }
00259 
00260     static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
00261     {
00262         PyObject * obj = (PyObject *)array;
00263         int ndim = PyArray_NDIM(obj);
00264 
00265         return ndim == N;
00266     }
00267 
00268     // The '*Compatible' functions are called whenever a NumpyArray is to be constructed
00269     // from a Python numpy.ndarray to check whether types and memory layout are
00270     // compatible. During overload resolution, boost::python iterates through the list
00271     // of overloads and invokes the first function where all arguments pass this check.
00272     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00273     {
00274         return isShapeCompatible(obj) && isValuetypeCompatible(obj);
00275     }
00276     
00277     // Construct a tagged shape from a 'shape - axistags' pair (called in 
00278     // NumpyArray::taggedShape()).
00279     template <class U>
00280     static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
00281     {
00282         return TaggedShape(shape, axistags);
00283     }
00284 
00285     // Construct a tagged shape from a 'shape - order' pair by creating
00286     // the appropriate axistags object for that order and NumpyArray type.
00287     // (called in NumpyArray constructors via NumpyArray::init())
00288     template <class U>
00289     static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
00290     {
00291         // we ignore the 'order' parameter, because we don't know the axis meaning
00292         // in a plain array (use Singleband, Multiband, TinyVector etc. instead)
00293         return TaggedShape(shape, PyAxisTags(detail::emptyAxistags(shape.size())));
00294     }
00295 
00296     // Adjust a TaggedShape that was created by another array to the properties of
00297     // the present NumpyArray type (called in NumpyArray::reshapeIfEmpty()).
00298     static void finalizeTaggedShape(TaggedShape & tagged_shape)
00299     {
00300         vigra_precondition(tagged_shape.size() == N,
00301                   "reshapeIfEmpty(): tagged_shape has wrong size.");
00302     }
00303     
00304     // This function is used to synchronize the axis re-ordering of 'data'
00305     // with that of 'array'. For example, when we want to apply Gaussian smoothing
00306     // with a different scale for each axis, 'data' would contains those scales,
00307     // and permuteLikewise() would make sure that the scales are applied to the right
00308     // axes, regardless of axis re-ordering.
00309     template <class ARRAY>
00310     static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
00311     {
00312         vigra_precondition((int)data.size() == N,
00313             "NumpyArray::permuteLikewise(): size mismatch.");
00314         
00315         ArrayVector<npy_intp> permute;
00316         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00317                                        AxisInfo::AllAxes, true);
00318 
00319         if(permute.size() != 0)
00320         {
00321             applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
00322         }
00323     }
00324     
00325     // This function is called in NumpyArray::setupArrayView() to determine the
00326     // desired axis re-ordering.
00327     template <class U>
00328     static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
00329     {
00330         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00331                                        AxisInfo::AllAxes, true);
00332 
00333         if(permute.size() == 0)
00334         {
00335             permute.resize(N);
00336             linearSequence(permute.begin(), permute.end());
00337         }
00338     }
00339 
00340     // This function is called in NumpyArray::makeUnsafeReference() to create
00341     // a numpy.ndarray view for a block of memory managed by C++.
00342     // The term 'unsafe' should remind you that memory management cannot be done
00343     // automatically, bu must be done explicitly by the programmer.
00344     template <class U>
00345     static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
00346                                                 T *data, TinyVector<U, N> const & stride)
00347     {
00348         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00349         return constructNumpyArrayFromData(shape, npyStride.begin(), 
00350                                                     ValuetypeTraits::typeCode, data);
00351     }
00352 };
00353 
00354 /********************************************************/
00355 
00356 template<unsigned int N, class T>
00357 struct NumpyArrayTraits<N, T, UnstridedArrayTag>
00358 : public NumpyArrayTraits<N, T, StridedArrayTag>
00359 {
00360     typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
00361     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00362 
00363     static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
00364     {
00365         PyObject * obj = (PyObject *)array;
00366         int ndim = PyArray_NDIM(obj);
00367         long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
00368         long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
00369         npy_intp * strides = PyArray_STRIDES(obj);
00370         
00371         if(channelIndex < ndim)
00372         {
00373             // When we have a channel axis, it will become the innermost dimension
00374             return (ndim == N && strides[channelIndex] == sizeof(T));
00375         }
00376         else if(majorIndex < ndim)
00377         {
00378             // When we have axistags, but no channel axis, the major spatial
00379             // axis will be the innermost dimension
00380             return (ndim == N && strides[majorIndex] == sizeof(T));
00381         }
00382         else 
00383         {
00384             // When we have no axistags, the first axis will be the innermost dimension
00385             return (ndim == N && strides[0] == sizeof(T));
00386         }
00387     }
00388 
00389     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00390     {
00391         return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
00392     }
00393 };
00394 
00395 /********************************************************/
00396 
00397 template<unsigned int N, class T>
00398 struct NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
00399 : public NumpyArrayTraits<N, T, StridedArrayTag>
00400 {
00401     typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
00402     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00403 
00404     static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
00405     {
00406         PyObject * obj = (PyObject *)array;
00407         int ndim = PyArray_NDIM(obj);
00408         long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
00409         
00410         // If we have no channel axis (because either we don't have axistags, 
00411         // or the tags do not contain a channel axis), ndim must match.
00412         if(channelIndex == ndim)
00413             return ndim == N;
00414             
00415         // Otherwise, the channel axis must be a singleton axis that we can drop.
00416         return ndim == N+1 && PyArray_DIM(obj, channelIndex) == 1;
00417     }
00418 
00419     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00420     {
00421         return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
00422     }
00423 
00424     template <class U>
00425     static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
00426     {
00427         return TaggedShape(shape, axistags).setChannelCount(1);
00428     }
00429 
00430     template <class U>
00431     static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
00432     {
00433         return TaggedShape(shape, 
00434                   PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(1);
00435     }
00436 
00437     static void finalizeTaggedShape(TaggedShape & tagged_shape)
00438     {
00439         if(tagged_shape.axistags.hasChannelAxis())
00440         {
00441             tagged_shape.setChannelCount(1);
00442             vigra_precondition(tagged_shape.size() == N+1,
00443                      "reshapeIfEmpty(): tagged_shape has wrong size.");
00444         }
00445         else
00446         {
00447             tagged_shape.setChannelCount(0);
00448             vigra_precondition(tagged_shape.size() == N,
00449                      "reshapeIfEmpty(): tagged_shape has wrong size.");
00450         }
00451     }
00452     
00453     template <class ARRAY>
00454     static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
00455     {
00456         vigra_precondition((int)data.size() == N,
00457             "NumpyArray::permuteLikewise(): size mismatch.");
00458         
00459         ArrayVector<npy_intp> permute;
00460         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00461                                        AxisInfo::NonChannel, true);
00462 
00463         if(permute.size() == 0)
00464         {
00465             permute.resize(N);
00466             linearSequence(permute.begin(), permute.end());
00467         }
00468         
00469         applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
00470     }
00471     
00472     template <class U>
00473     static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
00474     {
00475         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00476                                        AxisInfo::AllAxes, true);
00477         if(permute.size() == 0)
00478         {
00479             permute.resize(N);
00480             linearSequence(permute.begin(), permute.end());
00481         }
00482         else if(permute.size() == N+1)
00483         {
00484             permute.erase(permute.begin());
00485         }
00486     }
00487 };
00488 
00489 /********************************************************/
00490 
00491 template<unsigned int N, class T>
00492 struct NumpyArrayTraits<N, Singleband<T>, UnstridedArrayTag>
00493 : public NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
00494 {
00495     typedef NumpyArrayTraits<N, T, UnstridedArrayTag> UnstridedTraits;
00496     typedef NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> BaseType;
00497     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00498 
00499     static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
00500     {
00501         PyObject * obj = (PyObject *)array;
00502         int ndim = PyArray_NDIM(obj);
00503         long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
00504         long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
00505         npy_intp * strides = PyArray_STRIDES(obj);
00506         
00507         // If we have no axistags, ndim must match, and axis 0 must be unstrided.
00508         if(majorIndex == ndim) 
00509             return N == ndim && strides[0] == sizeof(T);
00510             
00511         // If we have axistags, but no channel axis, ndim must match, 
00512         // and the major non-channel axis must be unstrided.
00513         if(channelIndex == ndim) 
00514             return N == ndim && strides[majorIndex] == sizeof(T);
00515             
00516         // Otherwise, the channel axis must be a singleton axis that we can drop,
00517         // and the major non-channel axis must be unstrided.
00518         return ndim == N+1 && PyArray_DIM(obj, channelIndex) == 1 && 
00519                 strides[majorIndex] == sizeof(T);
00520     }
00521 
00522     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00523     {
00524         return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
00525     }
00526 };
00527 
00528 /********************************************************/
00529 
00530 template<unsigned int N, class T>
00531 struct NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
00532 : public NumpyArrayTraits<N, T, StridedArrayTag>
00533 {
00534     typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
00535     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00536 
00537     static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
00538     {
00539         PyObject * obj = (PyObject*)array;
00540         int ndim = PyArray_NDIM(obj);
00541         long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
00542         long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
00543         
00544         if(channelIndex < ndim)
00545         {
00546             // When we have a channel axis, ndim must match.
00547             return ndim == N;
00548         }
00549         else if(majorIndex < ndim)
00550         {
00551             // When we have axistags, but no channel axis, we must add a singleton axis.
00552             return ndim == N-1;
00553         }
00554         else
00555         {
00556             // When we have no axistags, we may add a singleton dimension.
00557             return ndim == N || ndim == N-1;
00558         }
00559     }
00560 
00561     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00562     {
00563         return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
00564     }
00565 
00566     template <class U>
00567     static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
00568     {
00569         return TaggedShape(shape, axistags).setChannelIndexLast();
00570     }
00571 
00572     template <class U>
00573     static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
00574     {
00575         return TaggedShape(shape, 
00576                     PyAxisTags(detail::defaultAxistags(shape.size(), order))).setChannelIndexLast();
00577     }
00578 
00579     static void finalizeTaggedShape(TaggedShape & tagged_shape)
00580     {
00581         if(tagged_shape.axistags && 
00582            !tagged_shape.axistags.hasChannelAxis() && tagged_shape.channelCount() == 1)
00583         {
00584             tagged_shape.setChannelCount(0);
00585             vigra_precondition(tagged_shape.size() == N-1,
00586                   "reshapeIfEmpty(): tagged_shape has wrong size.");
00587         }
00588         else
00589         {
00590             vigra_precondition(tagged_shape.size() == N,
00591                   "reshapeIfEmpty(): tagged_shape has wrong size.");
00592         }
00593     }
00594 
00595     template <class ARRAY>
00596     static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
00597     {
00598         ArrayVector<npy_intp> permute;
00599         
00600         if((int)data.size() == N)
00601         {
00602             vigra_precondition(PyArray_NDIM((PyArrayObject*)array.get()) == N,
00603                 "NumpyArray::permuteLikewise(): input array has no channel axis.");
00604 
00605             detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00606                                            AxisInfo::AllAxes, true);
00607 
00608             if(permute.size() == 0)
00609             {
00610                 permute.resize(N);
00611                 linearSequence(permute.begin(), permute.end());
00612             }
00613             else
00614             {
00615                 // rotate channel axis to last position
00616                 int channelIndex = permute[0];
00617                 for(int k=1; k<N; ++k)
00618                     permute[k-1] = permute[k];
00619                 permute[N-1] = channelIndex;
00620             }
00621         }
00622         else
00623         {
00624             vigra_precondition((int)data.size() == N-1,
00625                 "NumpyArray::permuteLikewise(): size mismatch.");
00626 
00627             detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00628                                            AxisInfo::NonChannel, true);
00629 
00630             if(permute.size() == 0)
00631             {
00632                 permute.resize(N-1);
00633                 linearSequence(permute.begin(), permute.end());
00634             }
00635         }
00636         
00637         applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
00638     }
00639     
00640     template <class U>
00641     static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
00642     {
00643         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00644                                        AxisInfo::AllAxes, true);
00645 
00646         if(permute.size() == 0)
00647         {
00648             permute.resize(PyArray_NDIM((PyArrayObject*)array.get()));
00649             linearSequence(permute.begin(), permute.end());
00650         }
00651         else if(permute.size() == N)
00652         {
00653             // if we have a channel axis, rotate it to last position
00654             int channelIndex = permute[0];
00655             for(int k=1; k<N; ++k)
00656                 permute[k-1] = permute[k];
00657             permute[N-1] = channelIndex;
00658         }
00659     }
00660 };
00661 
00662 /********************************************************/
00663 
00664 template<unsigned int N, class T>
00665 struct NumpyArrayTraits<N, Multiband<T>, UnstridedArrayTag>
00666 : public NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
00667 {
00668     typedef NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> BaseType;
00669     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00670 
00671     static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
00672     {
00673         PyObject * obj = (PyObject *)array;
00674         int ndim = PyArray_NDIM(obj);
00675         long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
00676         long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
00677         npy_intp * strides = PyArray_STRIDES(obj);
00678 
00679         if(channelIndex < ndim)
00680         {
00681             // When we have a channel axis, ndim must match, and the major non-channel
00682             // axis must be unstrided.
00683             return ndim == N && strides[majorIndex] == sizeof(T);
00684         }
00685         else if(majorIndex < ndim)
00686         {
00687             // When we have axistags, but no channel axis, we will add a
00688             // singleton channel axis, and the major non-channel axis must be unstrided.
00689             return ndim == N-1 && strides[majorIndex] == sizeof(T);
00690         }
00691         else
00692         {
00693             // When we have no axistags, axis 0 must be unstrided, but we
00694             // may add a singleton dimension at the end.
00695             return (ndim == N || ndim == N-1) && strides[0] == sizeof(T);
00696         }
00697     }
00698 
00699     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00700     {
00701         return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
00702     }
00703 };
00704 
00705 /********************************************************/
00706 
00707 template<unsigned int N, int M, class T>
00708 struct NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
00709 {
00710     typedef T dtype;
00711     typedef TinyVector<T, M> value_type;
00712     typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
00713     static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
00714 
00715     static bool isArray(PyObject * obj)
00716     {
00717         return obj && PyArray_Check(obj);
00718     }
00719 
00720     static bool isValuetypeCompatible(PyArrayObject * obj)  /* obj must not be NULL */
00721     {
00722         return ValuetypeTraits::isValuetypeCompatible(obj);
00723     }
00724 
00725     static bool isShapeCompatible(PyArrayObject * array) /* array must not be NULL */
00726     {
00727         PyObject * obj = (PyObject *)array;
00728         
00729          // We need an extra channel axis.
00730          if(PyArray_NDIM(obj) != N+1)
00731             return false;
00732             
00733         // When there are no axistags, we assume that the last axis represents the channels.
00734         long channelIndex = pythonGetAttr(obj, "channelIndex", N);
00735         npy_intp * strides = PyArray_STRIDES(obj);
00736         
00737         return PyArray_DIM(obj, channelIndex) == M && strides[channelIndex] == sizeof(T);
00738     }
00739 
00740     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00741     {
00742         return isShapeCompatible(obj) && ValuetypeTraits::isValuetypeCompatible(obj);
00743     }
00744 
00745     template <class U>
00746     static TaggedShape taggedShape(TinyVector<U, N> const & shape, PyAxisTags axistags)
00747     {
00748         return TaggedShape(shape, axistags).setChannelCount(M);
00749     }
00750 
00751     template <class U>
00752     static TaggedShape taggedShape(TinyVector<U, N> const & shape, std::string const & order = "")
00753     {
00754         return TaggedShape(shape, 
00755                      PyAxisTags(detail::defaultAxistags(shape.size()+1, order))).setChannelCount(M);
00756     }
00757 
00758     static void finalizeTaggedShape(TaggedShape & tagged_shape)
00759     {
00760         tagged_shape.setChannelCount(M);
00761         vigra_precondition(tagged_shape.size() == N+1,
00762               "reshapeIfEmpty(): tagged_shape has wrong size.");
00763     }
00764 
00765     template <class ARRAY>
00766     static void permuteLikewise(python_ptr array, ARRAY const & data, ARRAY & res)
00767     {
00768         vigra_precondition((int)data.size() == N,
00769             "NumpyArray::permuteLikewise(): size mismatch.");
00770         
00771         ArrayVector<npy_intp> permute;
00772         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00773                                        AxisInfo::NonChannel, true);
00774 
00775         if(permute.size() == 0)
00776         {
00777             permute.resize(N);
00778             linearSequence(permute.begin(), permute.end());
00779         }
00780         
00781         applyPermutation(permute.begin(), permute.end(), data.begin(), res.begin());
00782     }
00783     
00784     template <class U>
00785     static void permutationToSetupOrder(python_ptr array, ArrayVector<U> & permute)
00786     {
00787         detail::getAxisPermutationImpl(permute, array, "permutationToNormalOrder", 
00788                                        AxisInfo::AllAxes, true);
00789         if(permute.size() == 0)
00790         {
00791             permute.resize(N);
00792             linearSequence(permute.begin(), permute.end());
00793         }
00794         else if(permute.size() == N+1)
00795         {
00796             permute.erase(permute.begin());
00797         }
00798     }
00799     
00800     template <class U>
00801     static python_ptr unsafeConstructorFromData(TinyVector<U, N> const & shape,
00802                                                 value_type *data, TinyVector<U, N> const & stride)
00803     {
00804         TinyVector<npy_intp, N+1> npyShape;
00805         std::copy(shape.begin(), shape.end(), npyShape.begin());
00806         npyShape[N] = M;
00807 
00808         TinyVector<npy_intp, N+1> npyStride;
00809         std::transform(
00810             stride.begin(), stride.end(), npyStride.begin(),
00811             std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
00812         npyStride[N] = sizeof(T);
00813 
00814         return constructNumpyArrayFromData(npyShape, npyStride.begin(), 
00815                                                     ValuetypeTraits::typeCode, data);
00816     }
00817 };
00818 
00819 /********************************************************/
00820 
00821 template<unsigned int N, int M, class T>
00822 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag>
00823 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
00824 {
00825     typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType;
00826     typedef typename BaseType::value_type value_type;
00827     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00828 
00829     static bool isShapeCompatible(PyArrayObject * array) /* obj must not be NULL */
00830     {
00831         PyObject * obj = (PyObject *)array;
00832         int ndim = PyArray_NDIM(obj);
00833         
00834          // We need an extra channel axis. 
00835         if(ndim != N+1)
00836             return false;
00837             
00838         long channelIndex = pythonGetAttr(obj, "channelIndex", ndim);
00839         long majorIndex = pythonGetAttr(obj, "innerNonchannelIndex", ndim);
00840         npy_intp * strides = PyArray_STRIDES(obj);
00841         
00842         if(majorIndex < ndim)
00843         {
00844             // We have axistags, but no channel axis => cannot be a TinyVector image
00845             if(channelIndex == ndim)
00846                 return false;
00847                 
00848             // We have an explicit channel axis => shapes and strides must match
00849             return PyArray_DIM(obj, channelIndex) == M && 
00850                    strides[channelIndex] == sizeof(T) &&
00851                    strides[majorIndex] == sizeof(TinyVector<T, M>);
00852             
00853             
00854         }
00855         else
00856         {
00857             // we have no axistags => we assume that the channel axis is last
00858             return PyArray_DIM(obj, N) == M && 
00859                    strides[N] == sizeof(T) &&
00860                    strides[0] == sizeof(TinyVector<T, M>);
00861         }
00862     }
00863 
00864     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00865     {
00866         return isShapeCompatible(obj) && BaseType::isValuetypeCompatible(obj);
00867     }
00868 };
00869 
00870 /********************************************************/
00871 
00872 template<unsigned int N, class T>
00873 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
00874 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag>
00875 {
00876     typedef T dtype;
00877     typedef RGBValue<T> value_type;
00878     typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
00879 };
00880 
00881 /********************************************************/
00882 
00883 template<unsigned int N, class T>
00884 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag>
00885 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
00886 {
00887     typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits;
00888     typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType;
00889     typedef typename BaseType::value_type value_type;
00890     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00891 
00892     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00893     {
00894         return UnstridedTraits::isShapeCompatible(obj);
00895     }
00896 
00897     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00898     {
00899         return UnstridedTraits::isPropertyCompatible(obj);
00900     }
00901 };
00902 
00903 } // namespace vigra
00904 
00905 #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 (20 Sep 2011)