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

vigra/numpy_array.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_HXX
00037 #define VIGRA_NUMPY_ARRAY_HXX
00038 
00039 #include <iostream>
00040 #include <algorithm>
00041 #include <complex>
00042 #include <string>
00043 #include <sstream>
00044 #include <map>
00045 #include <vigra/multi_array.hxx>
00046 #include <vigra/array_vector.hxx>
00047 #include <vigra/sized_int.hxx>
00048 #include <vigra/python_utility.hxx>
00049 #include <Python.h>
00050 #include <numpy/arrayobject.h>
00051 
00052 int _import_array();
00053 
00054 namespace vigra {
00055 
00056 /********************************************************/
00057 /*                                                      */
00058 /*              Singleband and Multiband                */
00059 /*                                                      */
00060 /********************************************************/
00061 
00062 typedef float NumpyValueType;
00063 
00064 template <class T>
00065 struct Singleband  // the last array dimension is not to be interpreted as a channel dimension
00066 {
00067     typedef T value_type;
00068 };
00069 
00070 template <class T>
00071 struct Multiband  // the last array dimension is a channel dimension
00072 {
00073     typedef T value_type;
00074 };
00075 
00076 template<class T>
00077 struct NumericTraits<Singleband<T> >
00078 : public NumericTraits<T>
00079 {};
00080 
00081 template<class T>
00082 struct NumericTraits<Multiband<T> >
00083 {
00084     typedef Multiband<T> Type;
00085 /*
00086     typedef int Promote;
00087     typedef unsigned int UnsignedPromote;
00088     typedef double RealPromote;
00089     typedef std::complex<RealPromote> ComplexPromote;
00090 */
00091     typedef Type ValueType;
00092 
00093     typedef typename NumericTraits<T>::isIntegral isIntegral;
00094     typedef VigraFalseType isScalar;
00095     typedef typename NumericTraits<T>::isSigned isSigned;
00096     typedef typename NumericTraits<T>::isSigned isOrdered;
00097     typedef typename NumericTraits<T>::isSigned isComplex;
00098 /*
00099     static signed char zero() { return 0; }
00100     static signed char one() { return 1; }
00101     static signed char nonZero() { return 1; }
00102     static signed char min() { return SCHAR_MIN; }
00103     static signed char max() { return SCHAR_MAX; }
00104 
00105 #ifdef NO_INLINE_STATIC_CONST_DEFINITION
00106     enum { minConst = SCHAR_MIN, maxConst = SCHAR_MIN };
00107 #else
00108     static const signed char minConst = SCHAR_MIN;
00109     static const signed char maxConst = SCHAR_MIN;
00110 #endif
00111 
00112     static Promote toPromote(signed char v) { return v; }
00113     static RealPromote toRealPromote(signed char v) { return v; }
00114     static signed char fromPromote(Promote v) {
00115         return ((v < SCHAR_MIN) ? SCHAR_MIN : (v > SCHAR_MAX) ? SCHAR_MAX : v);
00116     }
00117     static signed char fromRealPromote(RealPromote v) {
00118         return ((v < 0.0)
00119                    ? ((v < (RealPromote)SCHAR_MIN)
00120                        ? SCHAR_MIN
00121                        : static_cast<signed char>(v - 0.5))
00122                    : (v > (RealPromote)SCHAR_MAX)
00123                        ? SCHAR_MAX
00124                        : static_cast<signed char>(v + 0.5));
00125     }
00126 */
00127 };
00128 
00129 template <class T>
00130 class MultibandVectorAccessor
00131 {
00132     MultiArrayIndex size_, stride_;
00133 
00134   public:
00135     MultibandVectorAccessor(MultiArrayIndex size, MultiArrayIndex stride)
00136     : size_(size),
00137       stride_(stride)
00138     {}
00139 
00140 
00141     typedef Multiband<T> value_type;
00142 
00143         /** the vector's value_type
00144         */
00145     typedef T component_type;
00146 
00147     typedef VectorElementAccessor<MultibandVectorAccessor<T> > ElementAccessor;
00148 
00149         /** Read the component data at given vector index
00150             at given iterator position
00151         */
00152     template <class ITERATOR>
00153     component_type const & getComponent(ITERATOR const & i, int idx) const
00154     {
00155         return *(&*i+idx*stride_);
00156     }
00157 
00158         /** Set the component data at given vector index
00159             at given iterator position. The type <TT>V</TT> of the passed
00160             in <TT>value</TT> is automatically converted to <TT>component_type</TT>.
00161             In case of a conversion floating point -> intergral this includes rounding and clipping.
00162         */
00163     template <class V, class ITERATOR>
00164     void setComponent(V const & value, ITERATOR const & i, int idx) const
00165     {
00166         *(&*i+idx*stride_) = detail::RequiresExplicitCast<component_type>::cast(value);
00167     }
00168 
00169         /** Read the component data at given vector index
00170             at an offset of given iterator position
00171         */
00172     template <class ITERATOR, class DIFFERENCE>
00173     component_type const & getComponent(ITERATOR const & i, DIFFERENCE const & diff, int idx) const
00174     {
00175         return *(&i[diff]+idx*stride_);
00176     }
00177 
00178     /** Set the component data at given vector index
00179         at an offset of given iterator position. The type <TT>V</TT> of the passed
00180         in <TT>value</TT> is automatically converted to <TT>component_type</TT>.
00181             In case of a conversion floating point -> intergral this includes rounding and clipping.
00182     */
00183     template <class V, class ITERATOR, class DIFFERENCE>
00184     void
00185     setComponent(V const & value, ITERATOR const & i, DIFFERENCE const & diff, int idx) const
00186     {
00187         *(&i[diff]+idx*stride_) = detail::RequiresExplicitCast<component_type>::cast(value);
00188     }
00189 
00190     template <class U>
00191     MultiArrayIndex size(U) const
00192     {
00193         return size_;
00194     }
00195 };
00196 
00197 /********************************************************/
00198 /*                                                      */
00199 /*                a few Python utilities                */
00200 /*                                                      */
00201 /********************************************************/
00202 
00203 namespace detail {
00204 
00205 inline long spatialDimensions(PyObject * obj)
00206 {
00207     static python_ptr key(PyString_FromString("spatialDimensions"), python_ptr::keep_count);
00208     python_ptr pres(PyObject_GetAttr(obj, key), python_ptr::keep_count);
00209     long res = pres && PyInt_Check(pres)
00210                  ? PyInt_AsLong(pres)
00211                  : -1;
00212     return res;
00213 }
00214 
00215 /*
00216  * The registry is used to optionally map specific C++ types to
00217  * specific python sub-classes of numpy.ndarray (for example,
00218  * MultiArray<2, Singleband<int> > to a user-defined Python class 'ScalarImage').
00219  *
00220  * One needs to use NUMPY_ARRAY_INITIALIZE_REGISTRY once in a python
00221  * extension module using this technique, in order to actually provide
00222  * the registry (this is done by vigranumpycmodule and will then be
00223  * available for other modules, too).  Alternatively,
00224  * NUMPY_ARRAY_DUMMY_REGISTRY may be used to disable this feature
00225  * completely.  In both cases, the macro must not be enclosed by any
00226  * namespace, so it is best put right at the beginning of the file
00227  * (e.g. below the #includes).
00228  */
00229 
00230 typedef std::map<std::string, std::pair<python_ptr, python_ptr> > ArrayTypeMap;
00231 
00232 VIGRA_EXPORT ArrayTypeMap * getArrayTypeMap();
00233 
00234 #define NUMPY_ARRAY_INITIALIZE_REGISTRY                                 \
00235     namespace vigra { namespace detail {                                \
00236     ArrayTypeMap * getArrayTypeMap()                                    \
00237     {                                                                   \
00238         static ArrayTypeMap arrayTypeMap;                               \
00239         return &arrayTypeMap;                                           \
00240     }                                                                   \
00241     }} // namespace vigra::detail
00242 
00243 #define NUMPY_ARRAY_DUMMY_REGISTRY                      \
00244     namespace vigra { namespace detail {                \
00245     ArrayTypeMap * getArrayTypeMap()                    \
00246     {                                                   \
00247         return NULL;                                    \
00248     }                                                   \
00249     }} // namespace vigra::detail
00250 
00251 inline
00252 void registerPythonArrayType(std::string const & name, PyObject * obj, PyObject * typecheck)
00253 {
00254     ArrayTypeMap *types = getArrayTypeMap();
00255     vigra_precondition(
00256         types != NULL,
00257         "registerPythonArrayType(): module was compiled without array type registry.");
00258     vigra_precondition(
00259         obj && PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, &PyArray_Type),
00260         "registerPythonArrayType(obj): obj is not a subtype of numpy.ndarray.");
00261     if(typecheck && PyCallable_Check(typecheck))
00262         (*types)[name] = std::make_pair(python_ptr(obj), python_ptr(typecheck));
00263     else
00264         (*types)[name] = std::make_pair(python_ptr(obj), python_ptr());
00265 //    std::cerr << "Registering " << ((PyTypeObject *)obj)->tp_name << " for " << name << "\n";
00266 }
00267 
00268 inline
00269 python_ptr getArrayTypeObject(std::string const & name, PyTypeObject * def = 0)
00270 {
00271     ArrayTypeMap *types = getArrayTypeMap();
00272     if(!types)
00273         // dummy registry -> handle like empty registry
00274         return python_ptr((PyObject *)def);
00275 
00276     python_ptr res;
00277     ArrayTypeMap::iterator i = types->find(name);
00278     if(i != types->end())
00279         res = i->second.first;
00280     else
00281         res = python_ptr((PyObject *)def);
00282 //    std::cerr << "Requested " << name << ", got " << ((PyTypeObject *)res.get())->tp_name << "\n";
00283     return res;
00284 }
00285 
00286 // there are two cases for the return:
00287 // * if a typecheck function was registered, it is returned
00288 // * a null pointer is returned if nothing was registered for either key, or if
00289 //   a type was registered without typecheck function
00290 inline python_ptr
00291 getArrayTypecheckFunction(std::string const & keyFull, std::string const & key)
00292 {
00293     python_ptr res;
00294     ArrayTypeMap *types = getArrayTypeMap();
00295     if(types)
00296     {
00297         ArrayTypeMap::iterator i = types->find(keyFull);
00298         if(i == types->end())
00299             i = types->find(key);
00300         if(i != types->end())
00301             res = i->second.second;
00302     }
00303     return res;
00304 }
00305 
00306 inline bool
00307 performCustomizedArrayTypecheck(PyObject * obj, std::string const & keyFull, std::string const & key)
00308 {
00309     if(obj == 0 || !PyArray_Check(obj))
00310         return false;
00311     python_ptr typecheck = getArrayTypecheckFunction(keyFull, key);
00312     if(typecheck == 0)
00313         return true; // no custom test registered
00314     python_ptr args(PyTuple_Pack(1, obj), python_ptr::keep_count);
00315     pythonToCppException(args);
00316     python_ptr res(PyObject_Call(typecheck.get(), args.get(), 0), python_ptr::keep_count);
00317     pythonToCppException(res);
00318     vigra_precondition(PyBool_Check(res),
00319            "NumpyArray conversion: registered typecheck function did not return a boolean.");
00320     return res.get() == Py_True;
00321 }
00322 
00323 inline
00324 python_ptr constructNumpyArrayImpl(
00325     PyTypeObject * type,
00326     ArrayVector<npy_intp> const & shape, npy_intp *strides,
00327     NPY_TYPES typeCode, bool init)
00328 {
00329     python_ptr array;
00330 
00331     if(strides == 0)
00332     {
00333         array = python_ptr(PyArray_New(type, shape.size(), (npy_intp *)shape.begin(), typeCode, 0, 0, 0, 1 /* Fortran order */, 0),
00334                            python_ptr::keep_count);
00335     }
00336     else
00337     {
00338         int N = shape.size();
00339         ArrayVector<npy_intp> pshape(N);
00340         for(int k=0; k<N; ++k)
00341             pshape[strides[k]] = shape[k];
00342 
00343         array = python_ptr(PyArray_New(type, N, pshape.begin(), typeCode, 0, 0, 0, 1 /* Fortran order */, 0),
00344                            python_ptr::keep_count);
00345         pythonToCppException(array);
00346 
00347         PyArray_Dims permute = { strides, N };
00348         array = python_ptr(PyArray_Transpose((PyArrayObject*)array.get(), &permute), python_ptr::keep_count);
00349     }
00350     pythonToCppException(array);
00351 
00352     if(init)
00353         PyArray_FILLWBYTE((PyArrayObject *)array.get(), 0);
00354 
00355     return array;
00356 }
00357 
00358 // strideOrdering will be ignored unless order == "A"
00359 // TODO: this function should receive some refactoring in order to make
00360 //       the rules clear from the code rather than from comments
00361 inline python_ptr 
00362 constructNumpyArrayImpl(PyTypeObject * type, ArrayVector<npy_intp> const & shape, 
00363                        unsigned int spatialDimensions, unsigned int channels,
00364                        NPY_TYPES typeCode, std::string order, bool init,
00365                        ArrayVector<npy_intp> strideOrdering = ArrayVector<npy_intp>())
00366 {
00367     // shape must have at least length spatialDimensions, but can also have a channel dimension
00368     vigra_precondition(shape.size() == spatialDimensions || shape.size() == spatialDimensions + 1,
00369            "constructNumpyArray(type, shape, ...): shape has wrong length.");
00370 
00371     // if strideOrdering is given, it must have at least length spatialDimensions, 
00372     // but can also have a channel dimension
00373     vigra_precondition(strideOrdering.size() == 0 || strideOrdering.size() == spatialDimensions ||
00374                        strideOrdering.size() == spatialDimensions + 1,
00375            "constructNumpyArray(type, ..., strideOrdering): strideOrdering has wrong length.");
00376         
00377     if(channels == 0) // if the requested number of channels is not given ...
00378     {
00379         // ... deduce it
00380         if(shape.size() == spatialDimensions)
00381             channels = 1;
00382         else
00383             channels = shape.back();
00384     }
00385     else
00386     {
00387         // otherwise, if the shape object also contains a channel dimension, they must be consistent
00388         if(shape.size() > spatialDimensions)
00389             vigra_precondition(channels == (unsigned int)shape[spatialDimensions],
00390                    "constructNumpyArray(type, ...): shape contradicts requested number of channels.");
00391     }
00392     
00393     // if we have only one channel, no explicit channel dimension should be in the shape
00394     unsigned int shapeSize = channels == 1
00395                                   ? spatialDimensions
00396                                   : spatialDimensions + 1;
00397     
00398     // create the shape object with optional channel dimension
00399     ArrayVector<npy_intp> pshape(shapeSize);
00400     std::copy(shape.begin(), shape.begin()+std::min(shape.size(), pshape.size()), pshape.begin());
00401     if(shapeSize > spatialDimensions)
00402         pshape[spatialDimensions] = channels;
00403 
00404     // order "A" means "preserve order" when an array is copied, and 
00405     // defaults to "V" when a new array is created without explicit strideOrdering
00406     // 
00407     if(order == "A")
00408     {
00409         if(strideOrdering.size() == 0)
00410         {
00411             order = "V";
00412         }
00413         else if(strideOrdering.size() > shapeSize)
00414         {
00415             // make sure that strideOrdering length matches shape length
00416             ArrayVector<npy_intp> pstride(strideOrdering.begin(), strideOrdering.begin()+shapeSize);
00417 
00418             // adjust the ordering when the channel dimension has been dropped because channel == 1
00419             if(strideOrdering[shapeSize] == 0)
00420                 for(unsigned int k=0; k<shapeSize; ++k)
00421                     pstride[k] -= 1;
00422             pstride.swap(strideOrdering);
00423         }
00424         else if(strideOrdering.size() < shapeSize)
00425         {
00426             // make sure that strideOrdering length matches shape length
00427             ArrayVector<npy_intp> pstride(shapeSize);
00428 
00429             // adjust the ordering when the channel dimension has been dropped because channel == 1
00430             for(unsigned int k=0; k<shapeSize-1; ++k)
00431                 pstride[k] = strideOrdering[k] + 1;
00432             pstride[shapeSize-1] = 0;
00433             pstride.swap(strideOrdering);
00434         }
00435     }
00436     
00437     // create the appropriate strideOrdering objects for the other memory orders
00438     // (when strideOrdering already contained data, it is ignored because order != "A")
00439     if(order == "C")
00440     {
00441         strideOrdering.resize(shapeSize);
00442         for(unsigned int k=0; k<shapeSize; ++k)
00443             strideOrdering[k] = shapeSize-1-k;
00444     }
00445     else if(order == "F" || (order == "V" && channels == 1))
00446     {
00447         strideOrdering.resize(shapeSize);
00448         for(unsigned int k=0; k<shapeSize; ++k)
00449             strideOrdering[k] = k;
00450     }
00451     else if(order == "V")
00452     {
00453         strideOrdering.resize(shapeSize);
00454         for(unsigned int k=0; k<shapeSize-1; ++k)
00455             strideOrdering[k] = k+1;
00456         strideOrdering[shapeSize-1] = 0;
00457     }
00458 
00459     return constructNumpyArrayImpl(type, pshape, strideOrdering.begin(), typeCode, init);
00460 }
00461 
00462 inline
00463 python_ptr constructNumpyArrayFromData(
00464     PyTypeObject * type,
00465     ArrayVector<npy_intp> const & shape, npy_intp *strides,
00466     NPY_TYPES typeCode, void *data)
00467 {
00468     python_ptr array(PyArray_New(type, shape.size(), (npy_intp *)shape.begin(), typeCode, strides, data, 0, NPY_WRITEABLE, 0),
00469                      python_ptr::keep_count);
00470     pythonToCppException(array);
00471 
00472     return array;
00473 }
00474 
00475 
00476 } // namespace detail
00477 
00478 /********************************************************/
00479 /*                                                      */
00480 /*               NumpyArrayValuetypeTraits              */
00481 /*                                                      */
00482 /********************************************************/
00483 
00484 template<class ValueType>
00485 struct ERROR_NumpyArrayValuetypeTraits_not_specialized_for_ { };
00486 
00487 template<class ValueType>
00488 struct NumpyArrayValuetypeTraits
00489 {
00490     static bool isValuetypeCompatible(PyArrayObject const * obj)
00491     {
00492         return ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType>();
00493     }
00494 
00495     static ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> typeCode;
00496 
00497     static std::string typeName()
00498     {
00499         return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
00500     }
00501 
00502     static std::string typeNameImpex()
00503     {
00504         return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case");
00505     }
00506 
00507     static PyObject * typeObject()
00508     {
00509         return (PyObject *)0;
00510     }
00511 };
00512 
00513 template<class ValueType>
00514 ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> NumpyArrayValuetypeTraits<ValueType>::typeCode;
00515 
00516 #define VIGRA_NUMPY_VALUETYPE_TRAITS(type, typeID, numpyTypeName, impexTypeName) \
00517 template <> \
00518 struct NumpyArrayValuetypeTraits<type > \
00519 { \
00520     static bool isValuetypeCompatible(PyArrayObject const * obj) /* obj must not be NULL */ \
00521     { \
00522         return PyArray_EquivTypenums(typeID, PyArray_DESCR((PyObject *)obj)->type_num) && \
00523                PyArray_ITEMSIZE((PyObject *)obj) == sizeof(type); \
00524     } \
00525     \
00526     static NPY_TYPES const typeCode = typeID; \
00527     \
00528     static std::string typeName() \
00529     { \
00530         return #numpyTypeName; \
00531     } \
00532     \
00533     static std::string typeNameImpex() \
00534     { \
00535         return impexTypeName; \
00536     } \
00537     \
00538     static PyObject * typeObject() \
00539     { \
00540         return PyArray_TypeObjectFromType(typeID); \
00541     } \
00542 };
00543 
00544 VIGRA_NUMPY_VALUETYPE_TRAITS(bool,           NPY_BOOL, bool, "UINT8")
00545 VIGRA_NUMPY_VALUETYPE_TRAITS(signed char,    NPY_INT8, int8, "INT16")
00546 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned char,  NPY_UINT8, uint8, "UINT8")
00547 VIGRA_NUMPY_VALUETYPE_TRAITS(short,          NPY_INT16, int16, "INT16")
00548 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned short, NPY_UINT16, uint16, "UINT16")
00549 
00550 #if VIGRA_BITSOF_LONG == 32
00551 VIGRA_NUMPY_VALUETYPE_TRAITS(long,           NPY_INT32, int32, "INT32")
00552 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long,  NPY_UINT32, uint32, "UINT32")
00553 #elif VIGRA_BITSOF_LONG == 64
00554 VIGRA_NUMPY_VALUETYPE_TRAITS(long,           NPY_INT64, int64, "DOUBLE")
00555 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long,  NPY_UINT64, uint64, "DOUBLE")
00556 #endif
00557 
00558 #if VIGRA_BITSOF_INT == 32
00559 VIGRA_NUMPY_VALUETYPE_TRAITS(int,            NPY_INT32, int32, "INT32")
00560 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int,   NPY_UINT32, uint32, "UINT32")
00561 #elif VIGRA_BITSOF_INT == 64
00562 VIGRA_NUMPY_VALUETYPE_TRAITS(int,            NPY_INT64, int64, "DOUBLE")
00563 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int,   NPY_UINT64, uint64, "DOUBLE")
00564 #endif
00565 
00566 #ifdef PY_LONG_LONG
00567 # if VIGRA_BITSOF_LONG_LONG == 32
00568 VIGRA_NUMPY_VALUETYPE_TRAITS(long long,            NPY_INT32, int32, "INT32")
00569 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long,   NPY_UINT32, uint32, "UINT32")
00570 # elif VIGRA_BITSOF_LONG_LONG == 64
00571 VIGRA_NUMPY_VALUETYPE_TRAITS(long long,          NPY_INT64, int64, "DOUBLE")
00572 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT64, uint64, "DOUBLE")
00573 # endif
00574 #endif
00575 
00576 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float32, NPY_FLOAT32, float32, "FLOAT")
00577 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float64, NPY_FLOAT64, float64, "DOUBLE")
00578 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_longdouble, NPY_LONGDOUBLE, longdouble, "")
00579 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cfloat, NPY_CFLOAT, complex64, "")
00580 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_float>, NPY_CFLOAT, complex64, "")
00581 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cdouble, NPY_CDOUBLE, complex128, "")
00582 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_double>, NPY_CDOUBLE, complex128, "")
00583 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_clongdouble, NPY_CLONGDOUBLE, clongdouble, "")
00584 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_longdouble>, NPY_CLONGDOUBLE, clongdouble, "")
00585 
00586 #undef VIGRA_NUMPY_VALUETYPE_TRAITS
00587 
00588 /********************************************************/
00589 /*                                                      */
00590 /*                  NumpyArrayTraits                    */
00591 /*                                                      */
00592 /********************************************************/
00593 
00594 template <class U, int N>
00595 bool stridesAreAscending(TinyVector<U, N> const & strides)
00596 {
00597     for(int k=1; k<N; ++k)
00598         if(strides[k] < strides[k-1])
00599             return false;
00600     return true;
00601 }
00602 
00603 template<unsigned int N, class T, class Stride>
00604 struct NumpyArrayTraits;
00605 
00606 template<unsigned int N, class T>
00607 struct NumpyArrayTraits<N, T, StridedArrayTag>
00608 {
00609     typedef T dtype;
00610     typedef T value_type;
00611     typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
00612     static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
00613     
00614     enum { spatialDimensions = N, channels = 1 };
00615 
00616     static bool isArray(PyObject * obj)
00617     {
00618         return obj && PyArray_Check(obj);
00619     }
00620 
00621     static bool isClassCompatible(PyObject * obj)
00622     {
00623         return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
00624     }
00625 
00626     static bool isValuetypeCompatible(PyArrayObject * obj)  /* obj must not be NULL */
00627     {
00628         return ValuetypeTraits::isValuetypeCompatible(obj);
00629     }
00630 
00631     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00632     {
00633         return PyArray_NDIM((PyObject *)obj) == N-1 ||
00634                PyArray_NDIM((PyObject *)obj) == N ||
00635                (PyArray_NDIM((PyObject *)obj) == N+1 && PyArray_DIM((PyObject *)obj, N) == 1);
00636     }
00637 
00638     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00639     {
00640         return ValuetypeTraits::isValuetypeCompatible(obj) &&
00641                isShapeCompatible(obj);
00642     }
00643 
00644     template <class U>
00645     static python_ptr constructor(TinyVector<U, N> const & shape,
00646                                   T *data, TinyVector<U, N> const & stride)
00647     {
00648         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00649         return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, ValuetypeTraits::typeCode, data, npyStride.begin());
00650     }
00651 
00652     static std::string typeKey()
00653     {
00654         static std::string key = std::string("NumpyArray<") + asString(N) + ", *>";
00655         return key;
00656     }
00657 
00658     static std::string typeKeyFull()
00659     {
00660         static std::string key = std::string("NumpyArray<") + asString(N) + ", " +
00661                                  ValuetypeTraits::typeName() + ", StridedArrayTag>";
00662         return key;
00663     }
00664 };
00665 
00666 /********************************************************/
00667 
00668 template<unsigned int N, class T>
00669 struct NumpyArrayTraits<N, T, UnstridedArrayTag>
00670 : public NumpyArrayTraits<N, T, StridedArrayTag>
00671 {
00672     typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
00673     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00674 
00675     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00676     {
00677         return BaseType::isShapeCompatible(obj) &&
00678                PyArray_STRIDES((PyObject *)obj)[0] == PyArray_ITEMSIZE((PyObject *)obj);
00679     }
00680 
00681     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00682     {
00683         return BaseType::isValuetypeCompatible(obj) &&
00684                isShapeCompatible(obj);
00685     }
00686 
00687     template <class U>
00688     static python_ptr constructor(TinyVector<U, N> const & shape,
00689                                   T *data, TinyVector<U, N> const & stride)
00690     {
00691         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00692         return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, ValuetypeTraits::typeCode, data, npyStride.begin());
00693     }
00694 
00695     static std::string typeKeyFull()
00696     {
00697         static std::string key = std::string("NumpyArray<") + asString(N) + ", " +
00698                                  ValuetypeTraits::typeName() + ", UnstridedArrayTag>";
00699         return key;
00700     }
00701 };
00702 
00703 /********************************************************/
00704 
00705 template<unsigned int N, class T>
00706 struct NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
00707 : public NumpyArrayTraits<N, T, StridedArrayTag>
00708 {
00709     typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
00710     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00711 
00712     static bool isClassCompatible(PyObject * obj)
00713     {
00714         return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
00715     }
00716 
00717     template <class U>
00718     static python_ptr constructor(TinyVector<U, N> const & shape,
00719                                   T *data, TinyVector<U, N> const & stride)
00720     {
00721         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00722         return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, ValuetypeTraits::typeCode, data, npyStride.begin());
00723     }
00724 
00725     static std::string typeKey()
00726     {
00727         static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<*> >";
00728         return key;
00729     }
00730 
00731     static std::string typeKeyFull()
00732     {
00733         static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<" +
00734                                  ValuetypeTraits::typeName() + ">, StridedArrayTag>";
00735         return key;
00736     }
00737 };
00738 
00739 /********************************************************/
00740 
00741 template<unsigned int N, class T>
00742 struct NumpyArrayTraits<N, Singleband<T>, UnstridedArrayTag>
00743 : public NumpyArrayTraits<N, Singleband<T>, StridedArrayTag>
00744 {
00745     typedef NumpyArrayTraits<N, T, UnstridedArrayTag> UnstridedTraits;
00746     typedef NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> BaseType;
00747     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00748 
00749     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00750     {
00751         return UnstridedTraits::isShapeCompatible(obj);
00752     }
00753 
00754     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00755     {
00756         return UnstridedTraits::isPropertyCompatible(obj);
00757     }
00758 
00759     template <class U>
00760     static python_ptr constructor(TinyVector<U, N> const & shape,
00761                                   T *data, TinyVector<U, N> const & stride)
00762     {
00763         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00764         return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, ValuetypeTraits::typeCode, data, npyStride.begin());
00765     }
00766 
00767     static std::string typeKeyFull()
00768     {
00769         static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<" +
00770                                  ValuetypeTraits::typeName() + ">, UnstridedArrayTag>";
00771         return key;
00772     }
00773 };
00774 
00775 /********************************************************/
00776 
00777 template<unsigned int N, class T>
00778 struct NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
00779 : public NumpyArrayTraits<N, T, StridedArrayTag>
00780 {
00781     typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType;
00782     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00783 
00784     enum { spatialDimensions = N-1, channels = 0 };
00785 
00786     static bool isClassCompatible(PyObject * obj)
00787     {
00788         return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
00789     }
00790 
00791     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00792     {
00793         return PyArray_NDIM(obj) == N || PyArray_NDIM(obj) == N-1;
00794     }
00795 
00796     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00797     {
00798         return ValuetypeTraits::isValuetypeCompatible(obj) &&
00799                isShapeCompatible(obj);
00800     }
00801 
00802     template <class U>
00803     static python_ptr constructor(TinyVector<U, N> const & shape,
00804                                   T *data, TinyVector<U, N> const & stride)
00805     {
00806         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00807         return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, ValuetypeTraits::typeCode, data, npyStride.begin());
00808     }
00809 
00810     static std::string typeKey()
00811     {
00812         static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<*> >";
00813         return key;
00814     }
00815 
00816     static std::string typeKeyFull()
00817     {
00818         static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<" +
00819                                  ValuetypeTraits::typeName() + ">, StridedArrayTag>";
00820         return key;
00821     }
00822 };
00823 
00824 /********************************************************/
00825 
00826 template<unsigned int N, class T>
00827 struct NumpyArrayTraits<N, Multiband<T>, UnstridedArrayTag>
00828 : public NumpyArrayTraits<N, Multiband<T>, StridedArrayTag>
00829 {
00830     typedef NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> BaseType;
00831     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00832 
00833     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00834     {
00835         return BaseType::isShapeCompatible(obj) &&
00836                PyArray_STRIDES((PyObject *)obj)[0] == PyArray_ITEMSIZE((PyObject *)obj);
00837     }
00838 
00839     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00840     {
00841         return BaseType::isValuetypeCompatible(obj) &&
00842                isShapeCompatible(obj);
00843     }
00844 
00845     template <class U>
00846     static python_ptr constructor(TinyVector<U, N> const & shape,
00847                                   T *data, TinyVector<U, N> const & stride)
00848     {
00849         TinyVector<npy_intp, N> npyStride(stride * sizeof(T));
00850         return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, ValuetypeTraits::typeCode, data, npyStride.begin());
00851     }
00852 
00853     static std::string typeKeyFull()
00854     {
00855         static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<" +
00856                                  ValuetypeTraits::typeName() + ">, UnstridedArrayTag>";
00857         return key;
00858     }
00859 };
00860 
00861 /********************************************************/
00862 
00863 template<unsigned int N, int M, class T>
00864 struct NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
00865 {
00866     typedef T dtype;
00867     typedef TinyVector<T, M> value_type;
00868     typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
00869     static NPY_TYPES const typeCode = ValuetypeTraits::typeCode;
00870 
00871     enum { spatialDimensions = N, channels = M };
00872 
00873     static bool isArray(PyObject * obj)
00874     {
00875         return obj && PyArray_Check(obj);
00876     }
00877 
00878     static bool isClassCompatible(PyObject * obj)
00879     {
00880         return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
00881     }
00882 
00883     static bool isValuetypeCompatible(PyArrayObject * obj)  /* obj must not be NULL */
00884     {
00885         return ValuetypeTraits::isValuetypeCompatible(obj);
00886     }
00887 
00888     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00889     {
00890         return PyArray_NDIM((PyObject *)obj) == N+1 &&
00891                PyArray_DIM((PyObject *)obj, N) == M &&
00892                PyArray_STRIDES((PyObject *)obj)[N] == PyArray_ITEMSIZE((PyObject *)obj);
00893     }
00894 
00895     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00896     {
00897         return ValuetypeTraits::isValuetypeCompatible(obj) &&
00898                isShapeCompatible(obj);
00899     }
00900 
00901     template <class U>
00902     static python_ptr constructor(TinyVector<U, N> const & shape,
00903                                   T *data, TinyVector<U, N> const & stride)
00904     {
00905         TinyVector<npy_intp, N+1> npyShape;
00906         std::copy(shape.begin(), shape.end(), npyShape.begin());
00907         npyShape[N] = M;
00908 
00909         TinyVector<npy_intp, N+1> npyStride;
00910         std::transform(
00911             stride.begin(), stride.end(), npyStride.begin(),
00912             std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
00913         npyStride[N] = sizeof(T);
00914 
00915         return detail::constructNumpyArrayFromData(
00916             typeKeyFull(), typeKey(), npyShape,
00917             ValuetypeTraits::typeCode, data, npyStride.begin());
00918     }
00919 
00920     static std::string typeKey()
00921     {
00922         static std::string key = std::string("NumpyArray<") + asString(N) + ", TinyVector<*, " + asString(M) + "> >";
00923         return key;
00924     }
00925 
00926     static std::string typeKeyFull()
00927     {
00928         static std::string key = std::string("NumpyArray<") + asString(N) +
00929                       ", TinyVector<" + ValuetypeTraits::typeName() + ", " + asString(M) + ">, StridedArrayTag>";
00930         return key;
00931     }
00932 };
00933 
00934 /********************************************************/
00935 
00936 template<unsigned int N, int M, class T>
00937 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag>
00938 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag>
00939 {
00940     typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType;
00941     typedef typename BaseType::value_type value_type;
00942     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
00943 
00944     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
00945     {
00946         return BaseType::isShapeCompatible(obj) &&
00947                PyArray_STRIDES((PyObject *)obj)[0] == sizeof(TinyVector<T, M>);
00948     }
00949 
00950     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
00951     {
00952         return BaseType::isValuetypeCompatible(obj) &&
00953                isShapeCompatible(obj);
00954     }
00955 
00956     template <class U>
00957     static python_ptr constructor(TinyVector<U, N> const & shape,
00958                                   T *data, TinyVector<U, N> const & stride)
00959     {
00960         TinyVector<npy_intp, N+1> npyShape;
00961         std::copy(shape.begin(), shape.end(), npyShape.begin());
00962         npyShape[N] = M;
00963 
00964         TinyVector<npy_intp, N+1> npyStride;
00965         std::transform(
00966             stride.begin(), stride.end(), npyStride.begin(),
00967             std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
00968         npyStride[N] = sizeof(T);
00969 
00970         return detail::constructNumpyArrayFromData(
00971             typeKeyFull(), BaseType::typeKey(), npyShape,
00972             ValuetypeTraits::typeCode, data, npyStride.begin());
00973     }
00974 
00975     static std::string typeKeyFull()
00976     {
00977         static std::string key = std::string("NumpyArray<") + asString(N) +
00978                       ", TinyVector<" + ValuetypeTraits::typeName() + ", " + asString(M) + ">, UnstridedArrayTag>";
00979         return key;
00980     }
00981 };
00982 
00983 /********************************************************/
00984 
00985 template<unsigned int N, class T>
00986 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
00987 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag>
00988 {
00989     typedef T dtype;
00990     typedef RGBValue<T> value_type;
00991     typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits;
00992 
00993     static bool isClassCompatible(PyObject * obj)
00994     {
00995         return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey());
00996     }
00997 
00998     template <class U>
00999     static python_ptr constructor(TinyVector<U, N> const & shape,
01000                                   T *data, TinyVector<U, N> const & stride)
01001     {
01002         TinyVector<npy_intp, N+1> npyShape;
01003         std::copy(shape.begin(), shape.end(), npyShape.begin());
01004         npyShape[N] = 3;
01005 
01006         TinyVector<npy_intp, N+1> npyStride;
01007         std::transform(
01008             stride.begin(), stride.end(), npyStride.begin(),
01009             std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
01010         npyStride[N] = sizeof(T);
01011 
01012         return detail::constructNumpyArrayFromData(
01013             typeKeyFull(), typeKey(), npyShape,
01014             ValuetypeTraits::typeCode, data, npyStride.begin());
01015     }
01016 
01017     static std::string typeKey()
01018     {
01019         static std::string key = std::string("NumpyArray<") + asString(N) + ", RGBValue<*> >";
01020         return key;
01021     }
01022 
01023     static std::string typeKeyFull()
01024     {
01025         static std::string key = std::string("NumpyArray<") + asString(N) +
01026                       ", RGBValue<" + ValuetypeTraits::typeName() + ">, StridedArrayTag>";
01027         return key;
01028     }
01029 };
01030 
01031 /********************************************************/
01032 
01033 template<unsigned int N, class T>
01034 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag>
01035 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag>
01036 {
01037     typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits;
01038     typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType;
01039     typedef typename BaseType::value_type value_type;
01040     typedef typename BaseType::ValuetypeTraits ValuetypeTraits;
01041 
01042     static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */
01043     {
01044         return UnstridedTraits::isShapeCompatible(obj);
01045     }
01046 
01047     static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */
01048     {
01049         return UnstridedTraits::isPropertyCompatible(obj);
01050     }
01051 
01052     template <class U>
01053     static python_ptr constructor(TinyVector<U, N> const & shape,
01054                                   T *data, TinyVector<U, N> const & stride)
01055     {
01056         TinyVector<npy_intp, N+1> npyShape;
01057         std::copy(shape.begin(), shape.end(), npyShape.begin());
01058         npyShape[N] = 3;
01059 
01060         TinyVector<npy_intp, N+1> npyStride;
01061         std::transform(
01062             stride.begin(), stride.end(), npyStride.begin(),
01063             std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type)));
01064         npyStride[N] = sizeof(T);
01065 
01066         return detail::constructNumpyArrayFromData(
01067             typeKeyFull(), BaseType::typeKey(), npyShape,
01068             ValuetypeTraits::typeCode, data, npyStride.begin());
01069     }
01070 
01071     static std::string typeKeyFull()
01072     {
01073         static std::string key = std::string("NumpyArray<") + asString(N) +
01074                       ", RGBValue<" + ValuetypeTraits::typeName() + ">, UnstridedArrayTag>";
01075         return key;
01076     }
01077 };
01078 
01079 /********************************************************/
01080 /*                                                      */
01081 /*                    NumpyAnyArray                     */
01082 /*                                                      */
01083 /********************************************************/
01084 
01085 /** Wrapper class for a Python array.
01086     
01087     This class stores a reference-counted pointer to an Python numpy array object, 
01088     i.e. an object where <tt>PyArray_Check(object)</tt> returns true (in Python, the
01089     object is then a subclass of <tt>numpy.ndarray</tt>). This class is mainly used 
01090     as a smart pointer to these arrays, but some basic access and conversion functions
01091     are also provided.
01092 
01093     <b>\#include</b> <<a href="numpy__array_8hxx-source.html">vigra/numpy_array.hxx</a>><br>
01094     Namespace: vigra
01095 */
01096 class NumpyAnyArray
01097 {
01098   protected:
01099     python_ptr pyArray_;
01100     
01101     // We want to apply broadcasting to the channel dimension.
01102     // Since only leading dimensions can be added during numpy
01103     // broadcasting, we permute the array accordingly.
01104     NumpyAnyArray permuteChannelsToFront() const
01105     {
01106         MultiArrayIndex M = ndim();
01107         ArrayVector<npy_intp> permutation(M);
01108         for(int k=0; k<M; ++k)
01109             permutation[k] = M-1-k;
01110         // explicit cast to int is neede here to avoid gcc c++0x compilation
01111         // error: narrowing conversion of ‘M’ from ‘vigra::MultiArrayIndex’
01112         //        to ‘int’ inside { }
01113         // int overflow should not occur here because PyArray_NDIM returns
01114         // an integer which is converted to long in NumpyAnyArray::ndim()
01115         PyArray_Dims permute = { permutation.begin(), (int) M };
01116         python_ptr array(PyArray_Transpose(pyArray(), &permute), python_ptr::keep_count);
01117         pythonToCppException(array);
01118         return NumpyAnyArray(array.ptr());
01119     }
01120 
01121   public:
01122 
01123         /// difference type
01124     typedef ArrayVector<npy_intp> difference_type;
01125 
01126         /**
01127          Construct from a Python object. If \a obj is NULL, or is not a subclass
01128          of numpy.ndarray, the resulting NumpyAnyArray will have no data (i.e.
01129          hasData() returns false). Otherwise, it creates a new reference to the array
01130          \a obj, unless \a createCopy is true, where a new array is created by calling
01131          the C-equivalent of obj->copy().
01132          */
01133     explicit NumpyAnyArray(PyObject * obj = 0, bool createCopy = false, PyTypeObject * type = 0)
01134     {
01135         if(obj == 0)
01136             return;
01137         vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type),
01138              "NumpyAnyArray(obj, createCopy, type): type must be numpy.ndarray or a subclass thereof.");
01139         if(createCopy)
01140             makeCopy(obj, type);
01141         else
01142             vigra_precondition(makeReference(obj, type), "NumpyAnyArray(obj): obj isn't a numpy array.");
01143     }
01144 
01145         /**
01146          Copy constructor. By default, it creates a new reference to the array
01147          \a other. When \a createCopy is true, a new array is created by calling
01148          the C-equivalent of other.copy().
01149          */
01150     NumpyAnyArray(NumpyAnyArray const & other, bool createCopy = false, PyTypeObject * type = 0)
01151     {
01152         if(!other.hasData())
01153             return;
01154         vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type),
01155              "NumpyAnyArray(obj, createCopy, type): type must be numpy.ndarray or a subclass thereof.");
01156         if(createCopy)
01157             makeCopy(other.pyObject(), type);
01158         else
01159             makeReference(other.pyObject(), type);
01160     }
01161 
01162     // auto-generated destructor is ok
01163 
01164         /**
01165          * Assignment operator. If this is already a view with data
01166          * (i.e. hasData() is true) and the shapes match, the RHS
01167          * array contents are copied via the C-equivalent of 
01168          * 'self[...] = other[...]'. If the shapes don't matched,
01169          * broadcasting is tried on the trailing (i.e. channel)
01170          * dimension.
01171          * If the LHS is an empty view, assignment is identical to
01172          * makeReference(other.pyObject()).
01173          */
01174     NumpyAnyArray & operator=(NumpyAnyArray const & other)
01175     {
01176         if(hasData())
01177         {
01178             vigra_precondition(other.hasData(),
01179                 "NumpyArray::operator=(): Cannot assign from empty array.");
01180             if(PyArray_CopyInto(permuteChannelsToFront().pyArray(), other.permuteChannelsToFront().pyArray()) == -1)
01181                 pythonToCppException(0);
01182         }
01183         else
01184         {
01185             pyArray_ = other.pyArray_;
01186         }
01187         return *this;
01188     }
01189 
01190         /**
01191          Returns the number of dimensions of this array, or 0 if
01192          hasData() is false.
01193          */
01194     MultiArrayIndex ndim() const
01195     {
01196         if(hasData())
01197             return PyArray_NDIM(pyObject());
01198         return 0;
01199     }
01200 
01201         /**
01202          Returns the number of spatial dimensions of this array, or 0 if
01203          hasData() is false. If the enclosed Python array does not define
01204          the attribute spatialDimensions, ndim() is returned.
01205          */
01206     MultiArrayIndex spatialDimensions() const
01207     {
01208         if(!hasData())
01209             return 0;
01210         MultiArrayIndex s = detail::spatialDimensions(pyObject());
01211         if(s == -1)
01212             s = ndim();
01213         return s;
01214     }
01215 
01216         /**
01217          Returns the shape of this array. The size of
01218          the returned shape equals ndim().
01219          */
01220     difference_type shape() const
01221     {
01222         if(hasData())
01223             return difference_type(PyArray_DIMS(pyObject()), PyArray_DIMS(pyObject()) + ndim());
01224         return difference_type();
01225     }
01226 
01227         /** Compute the ordering of the strides of this array.
01228             The result is describes the current permutation of the axes relative
01229             to an ascending stride order.
01230         */
01231     difference_type strideOrdering() const
01232     {
01233         if(!hasData())
01234             return difference_type();
01235         MultiArrayIndex N = ndim();
01236         difference_type stride(PyArray_STRIDES(pyObject()), PyArray_STRIDES(pyObject()) + N),
01237                         permutation(N);
01238         for(MultiArrayIndex k=0; k<N; ++k)
01239             permutation[k] = k;
01240         for(MultiArrayIndex k=0; k<N-1; ++k)
01241         {
01242             MultiArrayIndex smallest = k;
01243             for(MultiArrayIndex j=k+1; j<N; ++j)
01244             {
01245                 if(stride[j] < stride[smallest])
01246                     smallest = j;
01247             }
01248             if(smallest != k)
01249             {
01250                 std::swap(stride[k], stride[smallest]);
01251                 std::swap(permutation[k], permutation[smallest]);
01252             }
01253         }
01254         difference_type ordering(N);
01255         for(MultiArrayIndex k=0; k<N; ++k)
01256             ordering[permutation[k]] = k;
01257         return ordering;
01258     }
01259 
01260         /**
01261          Returns the value type of the elements in this array, or -1
01262          when hasData() is false.
01263          */
01264     int dtype() const
01265     {
01266         if(hasData())
01267             return PyArray_DESCR(pyObject())->type_num;
01268         return -1;
01269     }
01270 
01271         /**
01272          * Return a borrowed reference to the internal PyArrayObject.
01273          */
01274     PyArrayObject * pyArray() const
01275     {
01276         return (PyArrayObject *)pyArray_.get();
01277     }
01278 
01279         /**
01280          * Return a borrowed reference to the internal PyArrayObject
01281          * (see pyArray()), cast to PyObject for your convenience.
01282          */
01283     PyObject * pyObject() const
01284     {
01285         return pyArray_.get();
01286     }
01287 
01288         /**
01289            Reset the NumpyAnyArray to the given object. If \a obj is a numpy array object,
01290            a new reference to that array is created, and the function returns
01291            true. Otherwise, it returns false and the NumpyAnyArray remains unchanged.
01292            If \a type is given, the new reference will be a view with that type, provided
01293            that \a type is a numpy ndarray or a subclass thereof. Otherwise, an
01294            exception is thrown.
01295          */
01296     bool makeReference(PyObject * obj, PyTypeObject * type = 0)
01297     {
01298         if(obj == 0 || !PyArray_Check(obj))
01299             return false;
01300         if(type != 0)
01301         {
01302             vigra_precondition(PyType_IsSubtype(type, &PyArray_Type) != 0,
01303                 "NumpyAnyArray::makeReference(obj, type): type must be numpy.ndarray or a subclass thereof.");
01304             obj = PyArray_View((PyArrayObject*)obj, 0, type);
01305             pythonToCppException(obj);
01306         }
01307         pyArray_.reset(obj);
01308         return true;
01309     }
01310 
01311         /**
01312            Create a copy of the given array object. If \a obj is a numpy array object,
01313            a copy is created via the C-equivalent of 'obj->copy()'. If
01314            this call fails, or obj was not an array, an exception is thrown
01315            and the NumpyAnyArray remains unchanged.
01316          */
01317     void makeCopy(PyObject * obj, PyTypeObject * type = 0)
01318     {
01319         vigra_precondition(obj && PyArray_Check(obj),
01320              "NumpyAnyArray::makeCopy(obj): obj is not an array.");
01321         vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type),
01322              "NumpyAnyArray::makeCopy(obj, type): type must be numpy.ndarray or a subclass thereof.");
01323         python_ptr array(PyArray_NewCopy((PyArrayObject*)obj, NPY_ANYORDER), python_ptr::keep_count);
01324         pythonToCppException(array);
01325         makeReference(array, type);
01326     }
01327 
01328          /**
01329            Check whether this NumpyAnyArray actually points to a Python array.
01330          */
01331     bool hasData() const
01332     {
01333         return pyArray_ != 0;
01334     }
01335 };
01336 
01337 /********************************************************/
01338 /*                                                      */
01339 /*                     NumpyArray                       */
01340 /*                                                      */
01341 /********************************************************/
01342 
01343 /** Provide the MultiArrayView interface for a Python array.
01344     
01345     This class inherits from both \ref vigra::MultiArrayView and \ref vigra::NumpyAnyArray 
01346     in order to support easy and save application of VIGRA functions to Python arrays.
01347 
01348     <b>\#include</b> <<a href="numpy__array_8hxx-source.html">vigra/numpy_array.hxx</a>><br>
01349     Namespace: vigra
01350 */
01351 template <unsigned int N, class T, class Stride = StridedArrayTag>
01352 class NumpyArray
01353 : public MultiArrayView<N, typename NumpyArrayTraits<N, T, Stride>::value_type, Stride>,
01354   public NumpyAnyArray
01355 {
01356   public:
01357     typedef NumpyArrayTraits<N, T, Stride> ArrayTraits;
01358     typedef typename ArrayTraits::dtype dtype;
01359     typedef T pseudo_value_type;
01360     
01361     static NPY_TYPES const typeCode = ArrayTraits::typeCode;
01362 
01363         /** the view type associated with this array.
01364          */
01365     typedef MultiArrayView<N, typename ArrayTraits::value_type, Stride> view_type;
01366 
01367     enum { actual_dimension = view_type::actual_dimension };
01368 
01369         /** the array's value type
01370          */
01371     typedef typename view_type::value_type value_type;
01372 
01373         /** pointer type
01374          */
01375     typedef typename view_type::pointer pointer;
01376 
01377         /** const pointer type
01378          */
01379     typedef typename view_type::const_pointer const_pointer;
01380 
01381         /** reference type (result of operator[])
01382          */
01383     typedef typename view_type::reference reference;
01384 
01385         /** const reference type (result of operator[] const)
01386          */
01387     typedef typename view_type::const_reference const_reference;
01388 
01389         /** size type
01390          */
01391     typedef typename view_type::size_type size_type;
01392 
01393         /** difference type (used for multi-dimensional offsets and indices)
01394          */
01395     typedef typename view_type::difference_type difference_type;
01396 
01397         /** difference and index type for a single dimension
01398          */
01399     typedef typename view_type::difference_type_1 difference_type_1;
01400 
01401         /** traverser type
01402          */
01403     typedef typename view_type::traverser traverser;
01404 
01405         /** traverser type to const data
01406          */
01407     typedef typename view_type::const_traverser const_traverser;
01408 
01409         /** sequential (random access) iterator type
01410          */
01411     typedef value_type * iterator;
01412 
01413         /** sequential (random access) const iterator type
01414          */
01415     typedef value_type * const_iterator;
01416 
01417     using view_type::shape;   // resolve ambiguity of multiple inheritance
01418     using view_type::hasData; // resolve ambiguity of multiple inheritance
01419     using view_type::strideOrdering; // resolve ambiguity of multiple inheritance
01420 
01421   protected:
01422 
01423     // this function assumes that pyArray_ has already been set, and compatibility been checked
01424     void setupArrayView();
01425     
01426     static python_ptr getArrayTypeObject()
01427     {
01428         python_ptr type = detail::getArrayTypeObject(ArrayTraits::typeKeyFull());
01429         if(type == 0)
01430             type = detail::getArrayTypeObject(ArrayTraits::typeKey(), &PyArray_Type);
01431         return type;
01432     }
01433        
01434     static python_ptr init(difference_type const & shape, bool init = true)
01435     {
01436         ArrayVector<npy_intp> pshape(shape.begin(), shape.end());
01437         return detail::constructNumpyArrayImpl((PyTypeObject *)getArrayTypeObject().ptr(), pshape, 
01438                        ArrayTraits::spatialDimensions, ArrayTraits::channels,
01439                        typeCode, "V", init);
01440     }
01441 
01442     static python_ptr init(difference_type const & shape, difference_type const & strideOrdering, bool init = true)
01443     {
01444         ArrayVector<npy_intp> pshape(shape.begin(), shape.end()), 
01445                               pstrideOrdering(strideOrdering.begin(), strideOrdering.end());
01446         return detail::constructNumpyArrayImpl((PyTypeObject *)getArrayTypeObject().ptr(), pshape, 
01447                        ArrayTraits::spatialDimensions, ArrayTraits::channels,
01448                        typeCode, "A", init, pstrideOrdering);
01449     }
01450 
01451   public:
01452   
01453     using view_type::init;
01454     
01455         /**
01456          * Construct from a given PyObject pointer. When the given
01457          * python object is NULL, the internal python array will be
01458          * NULL and hasData() will return false.
01459          *
01460          * Otherwise, the function attempts to create a
01461          * new reference to the given Python object, unless
01462          * copying is forced by setting \a createCopy to true.
01463          * If either of this fails, the function throws an exception.
01464          * This will not happen if isStrictlyCompatible(obj) (in case
01465          * of creating a new reference) or isCopyCompatible(obj)
01466          * (in case of copying) have returned true beforehand.
01467          */
01468     explicit NumpyArray(PyObject *obj = 0, bool createCopy = false)
01469     {
01470         if(obj == 0)
01471             return;
01472         if(createCopy)
01473             makeCopy(obj);
01474         else
01475             vigra_precondition(makeReference(obj),
01476                   "NumpyArray(obj): Cannot construct from incompatible array.");
01477     }
01478 
01479        /**
01480          * Copy constructor; does not copy the memory, but creates a
01481          * new reference to the same underlying python object, unless
01482          * a copy is forced by setting \a createCopy to true.
01483          * (If the source object has no data, this one will have
01484          * no data, too.)
01485          */
01486     NumpyArray(const NumpyArray &other, bool createCopy = false) :
01487             MultiArrayView<N, typename NumpyArrayTraits<N, T, Stride>::value_type, Stride>(other),
01488             NumpyAnyArray(other, createCopy)
01489     {
01490         if(!other.hasData())
01491             return;
01492         if(createCopy)
01493             makeCopy(other.pyObject());
01494         else
01495             makeReferenceUnchecked(other.pyObject());
01496     }
01497 
01498        /**
01499          * Allocate new memory and copy data from a MultiArrayView.
01500          */
01501     explicit NumpyArray(const view_type &other)
01502     {
01503         if(!other.hasData())
01504             return;
01505         vigra_postcondition(makeReference(init(other.shape(), false)),
01506                   "NumpyArray(view_type): Python constructor did not produce a compatible array.");
01507         static_cast<view_type &>(*this) = other;
01508     }
01509 
01510         /**
01511          * Construct a new array object, allocating an internal python
01512          * ndarray of the given shape (in fortran order), initialized
01513          * with zeros.
01514          *
01515          * An exception is thrown when construction fails.
01516          */
01517     explicit NumpyArray(difference_type const & shape)
01518     {
01519         vigra_postcondition(makeReference(init(shape)),
01520                      "NumpyArray(shape): Python constructor did not produce a compatible array.");
01521     }
01522 
01523         /**
01524          * Construct a new array object, allocating an internal python
01525          * ndarray of the given shape and given stride ordering, initialized
01526          * with zeros.
01527          *
01528          * An exception is thrown when construction fails.
01529          */
01530     NumpyArray(difference_type const & shape, difference_type const & strideOrdering)
01531     {
01532         vigra_postcondition(makeReference(init(shape, strideOrdering)),
01533                      "NumpyArray(shape): Python constructor did not produce a compatible array.");
01534     }
01535 
01536         /**
01537          * Constructor from NumpyAnyArray.
01538          * Equivalent to NumpyArray(other.pyObject())
01539          */
01540     NumpyArray(const NumpyAnyArray &other, bool createCopy = false)
01541     {
01542         if(!other.hasData())
01543             return;
01544         if(createCopy)
01545             makeCopy(other.pyObject());
01546         else
01547             vigra_precondition(makeReference(other.pyObject()), //, false),
01548                    "NumpyArray(NumpyAnyArray): Cannot construct from incompatible or empty array.");
01549     }
01550 
01551         /**
01552          * Assignment operator. If this is already a view with data
01553          * (i.e. hasData() is true) and the shapes match, the RHS
01554          * array contents are copied.  If this is an empty view,
01555          * assignment is identical to makeReferenceUnchecked(other.pyObject()).
01556          * See MultiArrayView::operator= for further information on
01557          * semantics.
01558          */
01559     NumpyArray &operator=(const NumpyArray &other)
01560     {
01561         if(hasData())
01562             view_type::operator=(other);
01563         else
01564             makeReferenceUnchecked(other.pyObject());
01565         return *this;
01566     }
01567 
01568         /**
01569          * Assignment operator. If this is already a view with data
01570          * (i.e. hasData() is true) and the shapes match, the RHS
01571          * array contents are copied.
01572          * If this is an empty view, assignment is identical to
01573          * makeReference(other.pyObject()).
01574          * Otherwise, an exception is thrown.
01575          */
01576     NumpyArray &operator=(const NumpyAnyArray &other)
01577     {
01578         if(hasData())
01579         {
01580             NumpyAnyArray::operator=(other);
01581         }
01582         else if(isStrictlyCompatible(other.pyObject()))
01583         {
01584             makeReferenceUnchecked(other.pyObject());
01585         }
01586         else
01587         {
01588             vigra_precondition(false,
01589                 "NumpyArray::operator=(): Cannot assign from incompatible array.");
01590         }
01591         return *this;
01592     }
01593 
01594         /**
01595          * Test whether a given python object is a numpy array that can be
01596          * converted (copied) into an array compatible to this NumpyArray type.
01597          * This means that the array's shape conforms to the requirements of
01598          * makeCopy().
01599          */
01600     static bool isCopyCompatible(PyObject *obj)
01601     {
01602         return ArrayTraits::isArray(obj) &&
01603                ArrayTraits::isShapeCompatible((PyArrayObject *)obj);
01604     }
01605 
01606         /**
01607          * Test whether a given python object is a numpy array with a
01608          * compatible dtype and the correct shape and strides, so that it
01609          * can be referenced as a view by this NumpyArray type (i.e.
01610          * it conforms to the requirements of makeReference()).
01611          */
01612     static bool isReferenceCompatible(PyObject *obj)
01613     {
01614         return ArrayTraits::isArray(obj) &&
01615                ArrayTraits::isPropertyCompatible((PyArrayObject *)obj);
01616     }
01617 
01618         /**
01619          * Like isReferenceCompatible(obj), but also executes a customized type compatibility
01620          * check when such a check has been registered for this class via
01621          * registerPythonArrayType().
01622          *
01623          * This facilitates proper overload resolution between
01624          * NumpyArray<3, Multiband<T> > (a multiband image) and NumpyArray<3, Singleband<T> > (a scalar volume).
01625          */
01626     static bool isStrictlyCompatible(PyObject *obj)
01627     {
01628 #if VIGRA_CONVERTER_DEBUG
01629         std::cerr << "class " << typeid(NumpyArray).name() << " got " << obj->ob_type->tp_name << "\n";
01630         bool isClassCompatible=ArrayTraits::isClassCompatible(obj);
01631         bool isPropertyCompatible((PyArrayObject *)obj);
01632         std::cerr<<"isClassCompatible: "<<isClassCompatible<<std::endl;
01633         std::cerr<<"isPropertyCompatible: "<<isPropertyCompatible<<std::endl;
01634 #endif
01635         return ArrayTraits::isClassCompatible(obj) &&
01636                ArrayTraits::isPropertyCompatible((PyArrayObject *)obj);
01637     }
01638 
01639         /**
01640          * Create a vector representing the standard stride ordering of a NumpyArray.
01641          * That is, we get a vector representing the range [0,...,N-1], which
01642          * denotes the stride ordering for Fortran order.
01643          */
01644     static difference_type standardStrideOrdering()
01645     {
01646         difference_type strideOrdering;
01647         for(unsigned int k=0; k<N; ++k)
01648             strideOrdering[k] = k;
01649         return strideOrdering;
01650     }
01651 
01652         /**
01653          * Set up a view to the given object without checking compatibility.
01654          * This function must not be used unless isReferenceCompatible(obj) returned
01655          * true on the given object (otherwise, a crash is likely).
01656          */
01657     void makeReferenceUnchecked(PyObject *obj)
01658     {
01659         NumpyAnyArray::makeReference(obj);
01660         setupArrayView();
01661     }
01662 
01663         /**
01664          * Try to set up a view referencing the given PyObject.
01665          * Returns false if the python object is not a compatible
01666          * numpy array (see isReferenceCompatible() or
01667          * isStrictlyCompatible(), according to the parameter \a
01668          * strict).
01669          */
01670     bool makeReference(PyObject *obj, bool strict = true)
01671     {
01672         if(strict)
01673         {
01674             if(!isStrictlyCompatible(obj))
01675                 return false;
01676         }
01677         else
01678         {
01679             if(!isReferenceCompatible(obj))
01680                 return false;
01681         }
01682         makeReferenceUnchecked(obj);
01683         return true;
01684     }
01685 
01686         /**
01687          * Try to set up a view referencing the same data as the given
01688          * NumpyAnyArray.  This overloaded variant simply calls
01689          * makeReference() on array.pyObject().
01690          */
01691     bool makeReference(const NumpyAnyArray &array, bool strict = true)
01692     {
01693         return makeReference(array.pyObject(), strict);
01694     }
01695 
01696         /**
01697          * Set up an unsafe reference to the given MultiArrayView.
01698          * ATTENTION: This creates a numpy.ndarray that points to the
01699          * same data, but does not own it, so it must be ensured by
01700          * other means that the memory does not get freed before the
01701          * end of the ndarray's lifetime!  (One elegant way would be
01702          * to set the 'base' attribute of the resulting ndarray to a
01703          * python object which directly or indirectly holds the memory
01704          * of the given MultiArrayView.)
01705          */
01706     void makeReference(const view_type &multiArrayView)
01707     {
01708         vigra_precondition(!hasData(), "makeReference(): cannot replace existing view with given buffer");
01709 
01710         // construct an ndarray that points to our data (taking strides into account):
01711         python_ptr array(ArrayTraits::constructor(multiArrayView.shape(), multiArrayView.data(), multiArrayView.stride()));
01712 
01713         view_type::operator=(multiArrayView);
01714         pyArray_ = array;
01715     }
01716 
01717         /**
01718          Try to create a copy of the given PyObject.
01719          Raises an exception when obj is not a compatible array
01720          (see isCopyCompatible() or isStrictlyCompatible(), according to the
01721          parameter \a strict) or the Python constructor call failed.
01722          */
01723     void makeCopy(PyObject *obj, bool strict = false)
01724     {
01725         vigra_precondition(strict ? isStrictlyCompatible(obj) : isCopyCompatible(obj),
01726                      "NumpyArray::makeCopy(obj): Cannot copy an incompatible array.");
01727 
01728         int M = PyArray_NDIM(obj);
01729         TinyVector<npy_intp, N> shape;
01730         std::copy(PyArray_DIMS(obj), PyArray_DIMS(obj)+M, shape.begin());
01731         if(M == N-1)
01732             shape[M] = 1;
01733         vigra_postcondition(makeReference(init(shape, false)),
01734                      "NumpyArray::makeCopy(obj): Copy created an incompatible array.");
01735         NumpyAnyArray::operator=(NumpyAnyArray(obj));
01736 //        if(PyArray_CopyInto(pyArray(), (PyArrayObject*)obj) == -1)
01737 //            pythonToCppException(0);
01738     }
01739 
01740         /**
01741             Allocate new memory with the given shape and initialize with zeros.<br>
01742             If a stride ordering is given, the resulting array will have this stride
01743             ordering, when it is compatible with the array's memory layout (unstrided
01744             arrays only permit the standard ascending stride ordering).
01745 
01746             <em>Note:</em> this operation invalidates dependent objects
01747             (MultiArrayViews and iterators)
01748          */
01749     void reshape(difference_type const & shape, difference_type const & strideOrdering = standardStrideOrdering())
01750     {
01751         vigra_postcondition(makeReference(init(shape, strideOrdering)),
01752                      "NumpyArray(shape): Python constructor did not produce a compatible array.");
01753     }
01754 
01755         /**
01756             When this array has no data, allocate new memory with the given \a shape and
01757             initialize with zeros. Otherwise, check if the new shape matches the old shape
01758             and throw a precondition exception with the given \a message if not.
01759          */
01760     void reshapeIfEmpty(difference_type const & shape, std::string message = "")
01761     {
01762         reshapeIfEmpty(shape, standardStrideOrdering(), message);
01763     }
01764 
01765         /**
01766             When this array has no data, allocate new memory with the given \a shape and
01767             initialize with zeros. Otherwise, check if the new shape matches the old shape
01768             and throw a precondition exception with the given \a message if not. If strict
01769             is true, the given stride ordering must also match that of the existing data.
01770          */
01771     void reshapeIfEmpty(difference_type const & shape, difference_type const & strideOrdering,
01772                         std::string message = "", bool strict = false)
01773     {
01774         if(hasData())
01775         {
01776             if(strict)
01777             {
01778                 if(message == "")
01779                     message = "NumpyArray::reshapeIfEmpty(shape): array was not empty, and shape or stride ordering did not match.";
01780                 vigra_precondition(shape == this->shape() && strideOrdering == this->strideOrdering(), message.c_str());
01781             }
01782             else
01783             {
01784                 if(message == "")
01785                     message = "NumpyArray::reshapeIfEmpty(shape): array was not empty, and shape did not match.";
01786                 vigra_precondition(shape == this->shape(), message.c_str());
01787             }
01788         }
01789         else
01790         {
01791             reshape(shape, strideOrdering);
01792         }
01793     }
01794 };
01795 
01796     // this function assumes that pyArray_ has already been set, and compatibility been checked
01797 template <unsigned int N, class T, class Stride>
01798 void NumpyArray<N, T, Stride>::setupArrayView()
01799 {
01800     if(NumpyAnyArray::hasData())
01801     {
01802         unsigned int dimension = std::min<unsigned int>(actual_dimension, pyArray()->nd);
01803         std::copy(pyArray()->dimensions, pyArray()->dimensions + dimension, this->m_shape.begin());
01804         std::copy(pyArray()->strides, pyArray()->strides + dimension, this->m_stride.begin());
01805         if(pyArray()->nd < actual_dimension)
01806         {
01807             this->m_shape[dimension] = 1;
01808             this->m_stride[dimension] = sizeof(value_type);
01809         }
01810         this->m_stride /= sizeof(value_type);
01811         this->m_ptr = reinterpret_cast<pointer>(pyArray()->data);
01812     }
01813     else
01814     {
01815         this->m_ptr = 0;
01816     }
01817 }
01818 
01819 
01820 typedef NumpyArray<2, float >  NumpyFArray2;
01821 typedef NumpyArray<3, float >  NumpyFArray3;
01822 typedef NumpyArray<4, float >  NumpyFArray4;
01823 typedef NumpyArray<2, Singleband<float> >  NumpyFImage;
01824 typedef NumpyArray<3, Singleband<float> >  NumpyFVolume;
01825 typedef NumpyArray<2, RGBValue<float> >  NumpyFRGBImage;
01826 typedef NumpyArray<3, RGBValue<float> >  NumpyFRGBVolume;
01827 typedef NumpyArray<3, Multiband<float> >  NumpyFMultibandImage;
01828 typedef NumpyArray<4, Multiband<float> >  NumpyFMultibandVolume;
01829 
01830 inline void import_vigranumpy()
01831 {
01832     if(_import_array() < 0)
01833         pythonToCppException(0);
01834     python_ptr module(PyImport_ImportModule("vigra.vigranumpycore"), python_ptr::keep_count);
01835     pythonToCppException(module);
01836 }
01837 
01838 /********************************************************/
01839 /*                                                      */
01840 /*   NumpyArray Multiband Argument Object Factories     */
01841 /*                                                      */
01842 /********************************************************/
01843 
01844 template <class PixelType, class Stride>
01845 inline triple<ConstStridedImageIterator<PixelType>,
01846               ConstStridedImageIterator<PixelType>,
01847               MultibandVectorAccessor<PixelType> >
01848 srcImageRange(NumpyArray<3, Multiband<PixelType>, Stride> const & img)
01849 {
01850     ConstStridedImageIterator<PixelType>
01851         ul(img.data(), 1, img.stride(0), img.stride(1));
01852     return triple<ConstStridedImageIterator<PixelType>,
01853                   ConstStridedImageIterator<PixelType>,
01854                   MultibandVectorAccessor<PixelType> >
01855         (ul, ul + Size2D(img.shape(0), img.shape(1)), MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
01856 }
01857 
01858 template <class PixelType, class Stride>
01859 inline pair< ConstStridedImageIterator<PixelType>,
01860              MultibandVectorAccessor<PixelType> >
01861 srcImage(NumpyArray<3, Multiband<PixelType>, Stride> const & img)
01862 {
01863     ConstStridedImageIterator<PixelType>
01864         ul(img.data(), 1, img.stride(0), img.stride(1));
01865     return pair<ConstStridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> >
01866         (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
01867 }
01868 
01869 template <class PixelType, class Stride>
01870 inline triple< StridedImageIterator<PixelType>,
01871                StridedImageIterator<PixelType>,
01872                MultibandVectorAccessor<PixelType> >
01873 destImageRange(NumpyArray<3, Multiband<PixelType>, Stride> & img)
01874 {
01875     StridedImageIterator<PixelType>
01876         ul(img.data(), 1, img.stride(0), img.stride(1));
01877     typedef typename AccessorTraits<PixelType>::default_accessor Accessor;
01878     return triple<StridedImageIterator<PixelType>,
01879                   StridedImageIterator<PixelType>,
01880                   MultibandVectorAccessor<PixelType> >
01881         (ul, ul + Size2D(img.shape(0), img.shape(1)),
01882         MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
01883 }
01884 
01885 template <class PixelType, class Stride>
01886 inline pair< StridedImageIterator<PixelType>,
01887              MultibandVectorAccessor<PixelType> >
01888 destImage(NumpyArray<3, Multiband<PixelType>, Stride> & img)
01889 {
01890     StridedImageIterator<PixelType>
01891         ul(img.data(), 1, img.stride(0), img.stride(1));
01892     return pair<StridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> >
01893         (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
01894 }
01895 
01896 template <class PixelType, class Stride>
01897 inline pair< ConstStridedImageIterator<PixelType>,
01898              MultibandVectorAccessor<PixelType> >
01899 maskImage(NumpyArray<3, Multiband<PixelType>, Stride> const & img)
01900 {
01901     ConstStridedImageIterator<PixelType>
01902         ul(img.data(), 1, img.stride(0), img.stride(1));
01903     typedef typename AccessorTraits<PixelType>::default_accessor Accessor;
01904     return pair<ConstStridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> >
01905         (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2)));
01906 }
01907 
01908 } // namespace vigra
01909 
01910 #endif // VIGRA_NUMPY_ARRAY_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.7.0 (20 Apr 2010)