[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]
vigra/numpy_array.hxx | ![]() |
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 <Python.h> 00040 #include <iostream> 00041 #include <algorithm> 00042 #include <complex> 00043 #include <string> 00044 #include <sstream> 00045 #include <map> 00046 #include <vigra/multi_array.hxx> 00047 #include <vigra/array_vector.hxx> 00048 #include <vigra/sized_int.hxx> 00049 #include <vigra/python_utility.hxx> 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 (void*)res.get() == (void*)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 template <class TINY_VECTOR> 00463 inline 00464 python_ptr constructNumpyArrayFromData( 00465 std::string const & typeKeyFull, 00466 std::string const & typeKey, 00467 TINY_VECTOR const & shape, npy_intp *strides, 00468 NPY_TYPES typeCode, void *data) 00469 { 00470 ArrayVector<npy_intp> pyShape(shape.begin(), shape.end()); 00471 00472 python_ptr type = detail::getArrayTypeObject(typeKeyFull); 00473 if(type == 0) 00474 type = detail::getArrayTypeObject(typeKey, &PyArray_Type); 00475 00476 python_ptr array(PyArray_New((PyTypeObject *)type.ptr(), shape.size(), pyShape.begin(), typeCode, strides, data, 0, NPY_WRITEABLE, 0), 00477 python_ptr::keep_count); 00478 pythonToCppException(array); 00479 00480 return array; 00481 } 00482 00483 00484 } // namespace detail 00485 00486 /********************************************************/ 00487 /* */ 00488 /* NumpyArrayValuetypeTraits */ 00489 /* */ 00490 /********************************************************/ 00491 00492 template<class ValueType> 00493 struct ERROR_NumpyArrayValuetypeTraits_not_specialized_for_ { }; 00494 00495 template<class ValueType> 00496 struct NumpyArrayValuetypeTraits 00497 { 00498 static bool isValuetypeCompatible(PyArrayObject const * obj) 00499 { 00500 return ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType>(); 00501 } 00502 00503 static ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> typeCode; 00504 00505 static std::string typeName() 00506 { 00507 return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case"); 00508 } 00509 00510 static std::string typeNameImpex() 00511 { 00512 return std::string("ERROR: NumpyArrayValuetypeTraits not specialized for this case"); 00513 } 00514 00515 static PyObject * typeObject() 00516 { 00517 return (PyObject *)0; 00518 } 00519 }; 00520 00521 template<class ValueType> 00522 ERROR_NumpyArrayValuetypeTraits_not_specialized_for_<ValueType> NumpyArrayValuetypeTraits<ValueType>::typeCode; 00523 00524 #define VIGRA_NUMPY_VALUETYPE_TRAITS(type, typeID, numpyTypeName, impexTypeName) \ 00525 template <> \ 00526 struct NumpyArrayValuetypeTraits<type > \ 00527 { \ 00528 static bool isValuetypeCompatible(PyArrayObject const * obj) /* obj must not be NULL */ \ 00529 { \ 00530 return PyArray_EquivTypenums(typeID, PyArray_DESCR((PyObject *)obj)->type_num) && \ 00531 PyArray_ITEMSIZE((PyObject *)obj) == sizeof(type); \ 00532 } \ 00533 \ 00534 static NPY_TYPES const typeCode = typeID; \ 00535 \ 00536 static std::string typeName() \ 00537 { \ 00538 return #numpyTypeName; \ 00539 } \ 00540 \ 00541 static std::string typeNameImpex() \ 00542 { \ 00543 return impexTypeName; \ 00544 } \ 00545 \ 00546 static PyObject * typeObject() \ 00547 { \ 00548 return PyArray_TypeObjectFromType(typeID); \ 00549 } \ 00550 }; 00551 00552 VIGRA_NUMPY_VALUETYPE_TRAITS(bool, NPY_BOOL, bool, "UINT8") 00553 VIGRA_NUMPY_VALUETYPE_TRAITS(signed char, NPY_INT8, int8, "INT16") 00554 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned char, NPY_UINT8, uint8, "UINT8") 00555 VIGRA_NUMPY_VALUETYPE_TRAITS(short, NPY_INT16, int16, "INT16") 00556 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned short, NPY_UINT16, uint16, "UINT16") 00557 00558 #if VIGRA_BITSOF_LONG == 32 00559 VIGRA_NUMPY_VALUETYPE_TRAITS(long, NPY_INT32, int32, "INT32") 00560 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long, NPY_UINT32, uint32, "UINT32") 00561 #elif VIGRA_BITSOF_LONG == 64 00562 VIGRA_NUMPY_VALUETYPE_TRAITS(long, NPY_INT64, int64, "DOUBLE") 00563 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long, NPY_UINT64, uint64, "DOUBLE") 00564 #endif 00565 00566 #if VIGRA_BITSOF_INT == 32 00567 VIGRA_NUMPY_VALUETYPE_TRAITS(int, NPY_INT32, int32, "INT32") 00568 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int, NPY_UINT32, uint32, "UINT32") 00569 #elif VIGRA_BITSOF_INT == 64 00570 VIGRA_NUMPY_VALUETYPE_TRAITS(int, NPY_INT64, int64, "DOUBLE") 00571 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned int, NPY_UINT64, uint64, "DOUBLE") 00572 #endif 00573 00574 #ifdef PY_LONG_LONG 00575 # if VIGRA_BITSOF_LONG_LONG == 32 00576 VIGRA_NUMPY_VALUETYPE_TRAITS(long long, NPY_INT32, int32, "INT32") 00577 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT32, uint32, "UINT32") 00578 # elif VIGRA_BITSOF_LONG_LONG == 64 00579 VIGRA_NUMPY_VALUETYPE_TRAITS(long long, NPY_INT64, int64, "DOUBLE") 00580 VIGRA_NUMPY_VALUETYPE_TRAITS(unsigned long long, NPY_UINT64, uint64, "DOUBLE") 00581 # endif 00582 #endif 00583 00584 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float32, NPY_FLOAT32, float32, "FLOAT") 00585 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_float64, NPY_FLOAT64, float64, "DOUBLE") 00586 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE 00587 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_longdouble, NPY_LONGDOUBLE, longdouble, "") 00588 #endif 00589 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cfloat, NPY_CFLOAT, complex64, "") 00590 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_float>, NPY_CFLOAT, complex64, "") 00591 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_cdouble, NPY_CDOUBLE, complex128, "") 00592 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_double>, NPY_CDOUBLE, complex128, "") 00593 VIGRA_NUMPY_VALUETYPE_TRAITS(npy_clongdouble, NPY_CLONGDOUBLE, clongdouble, "") 00594 #if NPY_SIZEOF_LONGDOUBLE != NPY_SIZEOF_DOUBLE 00595 VIGRA_NUMPY_VALUETYPE_TRAITS(std::complex<npy_longdouble>, NPY_CLONGDOUBLE, clongdouble, "") 00596 #endif 00597 00598 #undef VIGRA_NUMPY_VALUETYPE_TRAITS 00599 00600 /********************************************************/ 00601 /* */ 00602 /* NumpyArrayTraits */ 00603 /* */ 00604 /********************************************************/ 00605 00606 template <class U, int N> 00607 bool stridesAreAscending(TinyVector<U, N> const & strides) 00608 { 00609 for(int k=1; k<N; ++k) 00610 if(strides[k] < strides[k-1]) 00611 return false; 00612 return true; 00613 } 00614 00615 template<unsigned int N, class T, class Stride> 00616 struct NumpyArrayTraits; 00617 00618 template<unsigned int N, class T> 00619 struct NumpyArrayTraits<N, T, StridedArrayTag> 00620 { 00621 typedef T dtype; 00622 typedef T value_type; 00623 typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits; 00624 static NPY_TYPES const typeCode = ValuetypeTraits::typeCode; 00625 00626 enum { spatialDimensions = N, channels = 1 }; 00627 00628 static bool isArray(PyObject * obj) 00629 { 00630 return obj && PyArray_Check(obj); 00631 } 00632 00633 static bool isClassCompatible(PyObject * obj) 00634 { 00635 return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey()); 00636 } 00637 00638 static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00639 { 00640 return ValuetypeTraits::isValuetypeCompatible(obj); 00641 } 00642 00643 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00644 { 00645 return PyArray_NDIM((PyObject *)obj) == N-1 || 00646 PyArray_NDIM((PyObject *)obj) == N || 00647 (PyArray_NDIM((PyObject *)obj) == N+1 && PyArray_DIM((PyObject *)obj, N) == 1); 00648 } 00649 00650 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00651 { 00652 return ValuetypeTraits::isValuetypeCompatible(obj) && 00653 isShapeCompatible(obj); 00654 } 00655 00656 template <class U> 00657 static python_ptr constructor(TinyVector<U, N> const & shape, 00658 T *data, TinyVector<U, N> const & stride) 00659 { 00660 TinyVector<npy_intp, N> npyStride(stride * sizeof(T)); 00661 return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data); 00662 } 00663 00664 static std::string typeKey() 00665 { 00666 static std::string key = std::string("NumpyArray<") + asString(N) + ", *>"; 00667 return key; 00668 } 00669 00670 static std::string typeKeyFull() 00671 { 00672 static std::string key = std::string("NumpyArray<") + asString(N) + ", " + 00673 ValuetypeTraits::typeName() + ", StridedArrayTag>"; 00674 return key; 00675 } 00676 }; 00677 00678 /********************************************************/ 00679 00680 template<unsigned int N, class T> 00681 struct NumpyArrayTraits<N, T, UnstridedArrayTag> 00682 : public NumpyArrayTraits<N, T, StridedArrayTag> 00683 { 00684 typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType; 00685 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 00686 00687 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00688 { 00689 return BaseType::isShapeCompatible(obj) && 00690 PyArray_STRIDES((PyObject *)obj)[0] == PyArray_ITEMSIZE((PyObject *)obj); 00691 } 00692 00693 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00694 { 00695 return BaseType::isValuetypeCompatible(obj) && 00696 isShapeCompatible(obj); 00697 } 00698 00699 template <class U> 00700 static python_ptr constructor(TinyVector<U, N> const & shape, 00701 T *data, TinyVector<U, N> const & stride) 00702 { 00703 TinyVector<npy_intp, N> npyStride(stride * sizeof(T)); 00704 return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data); 00705 } 00706 00707 static std::string typeKeyFull() 00708 { 00709 static std::string key = std::string("NumpyArray<") + asString(N) + ", " + 00710 ValuetypeTraits::typeName() + ", UnstridedArrayTag>"; 00711 return key; 00712 } 00713 }; 00714 00715 /********************************************************/ 00716 00717 template<unsigned int N, class T> 00718 struct NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> 00719 : public NumpyArrayTraits<N, T, StridedArrayTag> 00720 { 00721 typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType; 00722 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 00723 00724 static bool isClassCompatible(PyObject * obj) 00725 { 00726 return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey()); 00727 } 00728 00729 template <class U> 00730 static python_ptr constructor(TinyVector<U, N> const & shape, 00731 T *data, TinyVector<U, N> const & stride) 00732 { 00733 TinyVector<npy_intp, N> npyStride(stride * sizeof(T)); 00734 return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data); 00735 } 00736 00737 static std::string typeKey() 00738 { 00739 static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<*> >"; 00740 return key; 00741 } 00742 00743 static std::string typeKeyFull() 00744 { 00745 static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<" + 00746 ValuetypeTraits::typeName() + ">, StridedArrayTag>"; 00747 return key; 00748 } 00749 }; 00750 00751 /********************************************************/ 00752 00753 template<unsigned int N, class T> 00754 struct NumpyArrayTraits<N, Singleband<T>, UnstridedArrayTag> 00755 : public NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> 00756 { 00757 typedef NumpyArrayTraits<N, T, UnstridedArrayTag> UnstridedTraits; 00758 typedef NumpyArrayTraits<N, Singleband<T>, StridedArrayTag> BaseType; 00759 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 00760 00761 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00762 { 00763 return UnstridedTraits::isShapeCompatible(obj); 00764 } 00765 00766 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00767 { 00768 return UnstridedTraits::isPropertyCompatible(obj); 00769 } 00770 00771 template <class U> 00772 static python_ptr constructor(TinyVector<U, N> const & shape, 00773 T *data, TinyVector<U, N> const & stride) 00774 { 00775 TinyVector<npy_intp, N> npyStride(stride * sizeof(T)); 00776 return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data); 00777 } 00778 00779 static std::string typeKeyFull() 00780 { 00781 static std::string key = std::string("NumpyArray<") + asString(N) + ", Singleband<" + 00782 ValuetypeTraits::typeName() + ">, UnstridedArrayTag>"; 00783 return key; 00784 } 00785 }; 00786 00787 /********************************************************/ 00788 00789 template<unsigned int N, class T> 00790 struct NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> 00791 : public NumpyArrayTraits<N, T, StridedArrayTag> 00792 { 00793 typedef NumpyArrayTraits<N, T, StridedArrayTag> BaseType; 00794 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 00795 00796 enum { spatialDimensions = N-1, channels = 0 }; 00797 00798 static bool isClassCompatible(PyObject * obj) 00799 { 00800 return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey()); 00801 } 00802 00803 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00804 { 00805 return PyArray_NDIM(obj) == N || PyArray_NDIM(obj) == N-1; 00806 } 00807 00808 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00809 { 00810 return ValuetypeTraits::isValuetypeCompatible(obj) && 00811 isShapeCompatible(obj); 00812 } 00813 00814 template <class U> 00815 static python_ptr constructor(TinyVector<U, N> const & shape, 00816 T *data, TinyVector<U, N> const & stride) 00817 { 00818 TinyVector<npy_intp, N> npyStride(stride * sizeof(T)); 00819 return detail::constructNumpyArrayFromData(typeKeyFull(), typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data); 00820 } 00821 00822 static std::string typeKey() 00823 { 00824 static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<*> >"; 00825 return key; 00826 } 00827 00828 static std::string typeKeyFull() 00829 { 00830 static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<" + 00831 ValuetypeTraits::typeName() + ">, StridedArrayTag>"; 00832 return key; 00833 } 00834 }; 00835 00836 /********************************************************/ 00837 00838 template<unsigned int N, class T> 00839 struct NumpyArrayTraits<N, Multiband<T>, UnstridedArrayTag> 00840 : public NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> 00841 { 00842 typedef NumpyArrayTraits<N, Multiband<T>, StridedArrayTag> BaseType; 00843 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 00844 00845 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00846 { 00847 return BaseType::isShapeCompatible(obj) && 00848 PyArray_STRIDES((PyObject *)obj)[0] == PyArray_ITEMSIZE((PyObject *)obj); 00849 } 00850 00851 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00852 { 00853 return BaseType::isValuetypeCompatible(obj) && 00854 isShapeCompatible(obj); 00855 } 00856 00857 template <class U> 00858 static python_ptr constructor(TinyVector<U, N> const & shape, 00859 T *data, TinyVector<U, N> const & stride) 00860 { 00861 TinyVector<npy_intp, N> npyStride(stride * sizeof(T)); 00862 return detail::constructNumpyArrayFromData(typeKeyFull(), BaseType::typeKey(), shape, npyStride.begin(), ValuetypeTraits::typeCode, data); 00863 } 00864 00865 static std::string typeKeyFull() 00866 { 00867 static std::string key = std::string("NumpyArray<") + asString(N) + ", Multiband<" + 00868 ValuetypeTraits::typeName() + ">, UnstridedArrayTag>"; 00869 return key; 00870 } 00871 }; 00872 00873 /********************************************************/ 00874 00875 template<unsigned int N, int M, class T> 00876 struct NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> 00877 { 00878 typedef T dtype; 00879 typedef TinyVector<T, M> value_type; 00880 typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits; 00881 static NPY_TYPES const typeCode = ValuetypeTraits::typeCode; 00882 00883 enum { spatialDimensions = N, channels = M }; 00884 00885 static bool isArray(PyObject * obj) 00886 { 00887 return obj && PyArray_Check(obj); 00888 } 00889 00890 static bool isClassCompatible(PyObject * obj) 00891 { 00892 return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey()); 00893 } 00894 00895 static bool isValuetypeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00896 { 00897 return ValuetypeTraits::isValuetypeCompatible(obj); 00898 } 00899 00900 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00901 { 00902 return PyArray_NDIM((PyObject *)obj) == N+1 && 00903 PyArray_DIM((PyObject *)obj, N) == M && 00904 PyArray_STRIDES((PyObject *)obj)[N] == PyArray_ITEMSIZE((PyObject *)obj); 00905 } 00906 00907 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00908 { 00909 return ValuetypeTraits::isValuetypeCompatible(obj) && 00910 isShapeCompatible(obj); 00911 } 00912 00913 template <class U> 00914 static python_ptr constructor(TinyVector<U, N> const & shape, 00915 T *data, TinyVector<U, N> const & stride) 00916 { 00917 TinyVector<npy_intp, N+1> npyShape; 00918 std::copy(shape.begin(), shape.end(), npyShape.begin()); 00919 npyShape[N] = M; 00920 00921 TinyVector<npy_intp, N+1> npyStride; 00922 std::transform( 00923 stride.begin(), stride.end(), npyStride.begin(), 00924 std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type))); 00925 npyStride[N] = sizeof(T); 00926 00927 return detail::constructNumpyArrayFromData( 00928 typeKeyFull(), typeKey(), npyShape, 00929 npyStride.begin(), ValuetypeTraits::typeCode, data); 00930 } 00931 00932 static std::string typeKey() 00933 { 00934 static std::string key = std::string("NumpyArray<") + asString(N) + ", TinyVector<*, " + asString(M) + "> >"; 00935 return key; 00936 } 00937 00938 static std::string typeKeyFull() 00939 { 00940 static std::string key = std::string("NumpyArray<") + asString(N) + 00941 ", TinyVector<" + ValuetypeTraits::typeName() + ", " + asString(M) + ">, StridedArrayTag>"; 00942 return key; 00943 } 00944 }; 00945 00946 /********************************************************/ 00947 00948 template<unsigned int N, int M, class T> 00949 struct NumpyArrayTraits<N, TinyVector<T, M>, UnstridedArrayTag> 00950 : public NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> 00951 { 00952 typedef NumpyArrayTraits<N, TinyVector<T, M>, StridedArrayTag> BaseType; 00953 typedef typename BaseType::value_type value_type; 00954 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 00955 00956 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00957 { 00958 return BaseType::isShapeCompatible(obj) && 00959 PyArray_STRIDES((PyObject *)obj)[0] == sizeof(TinyVector<T, M>); 00960 } 00961 00962 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 00963 { 00964 return BaseType::isValuetypeCompatible(obj) && 00965 isShapeCompatible(obj); 00966 } 00967 00968 template <class U> 00969 static python_ptr constructor(TinyVector<U, N> const & shape, 00970 T *data, TinyVector<U, N> const & stride) 00971 { 00972 TinyVector<npy_intp, N+1> npyShape; 00973 std::copy(shape.begin(), shape.end(), npyShape.begin()); 00974 npyShape[N] = M; 00975 00976 TinyVector<npy_intp, N+1> npyStride; 00977 std::transform( 00978 stride.begin(), stride.end(), npyStride.begin(), 00979 std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type))); 00980 npyStride[N] = sizeof(T); 00981 00982 return detail::constructNumpyArrayFromData( 00983 typeKeyFull(), BaseType::typeKey(), npyShape, 00984 npyStride.begin(), ValuetypeTraits::typeCode, data); 00985 } 00986 00987 static std::string typeKeyFull() 00988 { 00989 static std::string key = std::string("NumpyArray<") + asString(N) + 00990 ", TinyVector<" + ValuetypeTraits::typeName() + ", " + asString(M) + ">, UnstridedArrayTag>"; 00991 return key; 00992 } 00993 }; 00994 00995 /********************************************************/ 00996 00997 template<unsigned int N, class T> 00998 struct NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> 00999 : public NumpyArrayTraits<N, TinyVector<T, 3>, StridedArrayTag> 01000 { 01001 typedef T dtype; 01002 typedef RGBValue<T> value_type; 01003 typedef NumpyArrayValuetypeTraits<T> ValuetypeTraits; 01004 01005 static bool isClassCompatible(PyObject * obj) 01006 { 01007 return detail::performCustomizedArrayTypecheck(obj, typeKeyFull(), typeKey()); 01008 } 01009 01010 template <class U> 01011 static python_ptr constructor(TinyVector<U, N> const & shape, 01012 T *data, TinyVector<U, N> const & stride) 01013 { 01014 TinyVector<npy_intp, N+1> npyShape; 01015 std::copy(shape.begin(), shape.end(), npyShape.begin()); 01016 npyShape[N] = 3; 01017 01018 TinyVector<npy_intp, N+1> npyStride; 01019 std::transform( 01020 stride.begin(), stride.end(), npyStride.begin(), 01021 std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type))); 01022 npyStride[N] = sizeof(T); 01023 01024 return detail::constructNumpyArrayFromData( 01025 typeKeyFull(), typeKey(), npyShape, 01026 npyStride.begin(), ValuetypeTraits::typeCode, data); 01027 } 01028 01029 static std::string typeKey() 01030 { 01031 static std::string key = std::string("NumpyArray<") + asString(N) + ", RGBValue<*> >"; 01032 return key; 01033 } 01034 01035 static std::string typeKeyFull() 01036 { 01037 static std::string key = std::string("NumpyArray<") + asString(N) + 01038 ", RGBValue<" + ValuetypeTraits::typeName() + ">, StridedArrayTag>"; 01039 return key; 01040 } 01041 }; 01042 01043 /********************************************************/ 01044 01045 template<unsigned int N, class T> 01046 struct NumpyArrayTraits<N, RGBValue<T>, UnstridedArrayTag> 01047 : public NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> 01048 { 01049 typedef NumpyArrayTraits<N, TinyVector<T, 3>, UnstridedArrayTag> UnstridedTraits; 01050 typedef NumpyArrayTraits<N, RGBValue<T>, StridedArrayTag> BaseType; 01051 typedef typename BaseType::value_type value_type; 01052 typedef typename BaseType::ValuetypeTraits ValuetypeTraits; 01053 01054 static bool isShapeCompatible(PyArrayObject * obj) /* obj must not be NULL */ 01055 { 01056 return UnstridedTraits::isShapeCompatible(obj); 01057 } 01058 01059 static bool isPropertyCompatible(PyArrayObject * obj) /* obj must not be NULL */ 01060 { 01061 return UnstridedTraits::isPropertyCompatible(obj); 01062 } 01063 01064 template <class U> 01065 static python_ptr constructor(TinyVector<U, N> const & shape, 01066 T *data, TinyVector<U, N> const & stride) 01067 { 01068 TinyVector<npy_intp, N+1> npyShape; 01069 std::copy(shape.begin(), shape.end(), npyShape.begin()); 01070 npyShape[N] = 3; 01071 01072 TinyVector<npy_intp, N+1> npyStride; 01073 std::transform( 01074 stride.begin(), stride.end(), npyStride.begin(), 01075 std::bind2nd(std::multiplies<npy_intp>(), sizeof(value_type))); 01076 npyStride[N] = sizeof(T); 01077 01078 return detail::constructNumpyArrayFromData( 01079 typeKeyFull(), BaseType::typeKey(), npyShape, 01080 npyStride.begin(), ValuetypeTraits::typeCode, data); 01081 } 01082 01083 static std::string typeKeyFull() 01084 { 01085 static std::string key = std::string("NumpyArray<") + asString(N) + 01086 ", RGBValue<" + ValuetypeTraits::typeName() + ">, UnstridedArrayTag>"; 01087 return key; 01088 } 01089 }; 01090 01091 /********************************************************/ 01092 /* */ 01093 /* NumpyAnyArray */ 01094 /* */ 01095 /********************************************************/ 01096 01097 /** Wrapper class for a Python array. 01098 01099 This class stores a reference-counted pointer to an Python numpy array object, 01100 i.e. an object where <tt>PyArray_Check(object)</tt> returns true (in Python, the 01101 object is then a subclass of <tt>numpy.ndarray</tt>). This class is mainly used 01102 as a smart pointer to these arrays, but some basic access and conversion functions 01103 are also provided. 01104 01105 <b>\#include</b> <<a href="numpy__array_8hxx-source.html">vigra/numpy_array.hxx</a>><br> 01106 Namespace: vigra 01107 */ 01108 class NumpyAnyArray 01109 { 01110 protected: 01111 python_ptr pyArray_; 01112 01113 // We want to apply broadcasting to the channel dimension. 01114 // Since only leading dimensions can be added during numpy 01115 // broadcasting, we permute the array accordingly. 01116 NumpyAnyArray permuteChannelsToFront() const 01117 { 01118 MultiArrayIndex M = ndim(); 01119 ArrayVector<npy_intp> permutation(M); 01120 for(int k=0; k<M; ++k) 01121 permutation[k] = M-1-k; 01122 // explicit cast to int is neede here to avoid gcc c++0x compilation 01123 // error: narrowing conversion of ‘M’ from ‘vigra::MultiArrayIndex’ 01124 // to ‘int’ inside { } 01125 // int overflow should not occur here because PyArray_NDIM returns 01126 // an integer which is converted to long in NumpyAnyArray::ndim() 01127 PyArray_Dims permute = { permutation.begin(), (int) M }; 01128 python_ptr array(PyArray_Transpose(pyArray(), &permute), python_ptr::keep_count); 01129 pythonToCppException(array); 01130 return NumpyAnyArray(array.ptr()); 01131 } 01132 01133 public: 01134 01135 /// difference type 01136 typedef ArrayVector<npy_intp> difference_type; 01137 01138 /** 01139 Construct from a Python object. If \a obj is NULL, or is not a subclass 01140 of numpy.ndarray, the resulting NumpyAnyArray will have no data (i.e. 01141 hasData() returns false). Otherwise, it creates a new reference to the array 01142 \a obj, unless \a createCopy is true, where a new array is created by calling 01143 the C-equivalent of obj->copy(). 01144 */ 01145 explicit NumpyAnyArray(PyObject * obj = 0, bool createCopy = false, PyTypeObject * type = 0) 01146 { 01147 if(obj == 0) 01148 return; 01149 vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type), 01150 "NumpyAnyArray(obj, createCopy, type): type must be numpy.ndarray or a subclass thereof."); 01151 if(createCopy) 01152 makeCopy(obj, type); 01153 else 01154 vigra_precondition(makeReference(obj, type), "NumpyAnyArray(obj): obj isn't a numpy array."); 01155 } 01156 01157 /** 01158 Copy constructor. By default, it creates a new reference to the array 01159 \a other. When \a createCopy is true, a new array is created by calling 01160 the C-equivalent of other.copy(). 01161 */ 01162 NumpyAnyArray(NumpyAnyArray const & other, bool createCopy = false, PyTypeObject * type = 0) 01163 { 01164 if(!other.hasData()) 01165 return; 01166 vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type), 01167 "NumpyAnyArray(obj, createCopy, type): type must be numpy.ndarray or a subclass thereof."); 01168 if(createCopy) 01169 makeCopy(other.pyObject(), type); 01170 else 01171 makeReference(other.pyObject(), type); 01172 } 01173 01174 // auto-generated destructor is ok 01175 01176 /** 01177 * Assignment operator. If this is already a view with data 01178 * (i.e. hasData() is true) and the shapes match, the RHS 01179 * array contents are copied via the C-equivalent of 01180 * 'self[...] = other[...]'. If the shapes don't matched, 01181 * broadcasting is tried on the trailing (i.e. channel) 01182 * dimension. 01183 * If the LHS is an empty view, assignment is identical to 01184 * makeReference(other.pyObject()). 01185 */ 01186 NumpyAnyArray & operator=(NumpyAnyArray const & other) 01187 { 01188 if(hasData()) 01189 { 01190 vigra_precondition(other.hasData(), 01191 "NumpyArray::operator=(): Cannot assign from empty array."); 01192 if(PyArray_CopyInto(permuteChannelsToFront().pyArray(), other.permuteChannelsToFront().pyArray()) == -1) 01193 pythonToCppException(0); 01194 } 01195 else 01196 { 01197 pyArray_ = other.pyArray_; 01198 } 01199 return *this; 01200 } 01201 01202 /** 01203 Returns the number of dimensions of this array, or 0 if 01204 hasData() is false. 01205 */ 01206 MultiArrayIndex ndim() const 01207 { 01208 if(hasData()) 01209 return PyArray_NDIM(pyObject()); 01210 return 0; 01211 } 01212 01213 /** 01214 Returns the number of spatial dimensions of this array, or 0 if 01215 hasData() is false. If the enclosed Python array does not define 01216 the attribute spatialDimensions, ndim() is returned. 01217 */ 01218 MultiArrayIndex spatialDimensions() const 01219 { 01220 if(!hasData()) 01221 return 0; 01222 MultiArrayIndex s = detail::spatialDimensions(pyObject()); 01223 if(s == -1) 01224 s = ndim(); 01225 return s; 01226 } 01227 01228 /** 01229 Returns the shape of this array. The size of 01230 the returned shape equals ndim(). 01231 */ 01232 difference_type shape() const 01233 { 01234 if(hasData()) 01235 return difference_type(PyArray_DIMS(pyObject()), PyArray_DIMS(pyObject()) + ndim()); 01236 return difference_type(); 01237 } 01238 01239 /** Compute the ordering of the strides of this array. 01240 The result is describes the current permutation of the axes relative 01241 to an ascending stride order. 01242 */ 01243 difference_type strideOrdering() const 01244 { 01245 if(!hasData()) 01246 return difference_type(); 01247 MultiArrayIndex N = ndim(); 01248 difference_type stride(PyArray_STRIDES(pyObject()), PyArray_STRIDES(pyObject()) + N), 01249 permutation(N); 01250 for(MultiArrayIndex k=0; k<N; ++k) 01251 permutation[k] = k; 01252 for(MultiArrayIndex k=0; k<N-1; ++k) 01253 { 01254 MultiArrayIndex smallest = k; 01255 for(MultiArrayIndex j=k+1; j<N; ++j) 01256 { 01257 if(stride[j] < stride[smallest]) 01258 smallest = j; 01259 } 01260 if(smallest != k) 01261 { 01262 std::swap(stride[k], stride[smallest]); 01263 std::swap(permutation[k], permutation[smallest]); 01264 } 01265 } 01266 difference_type ordering(N); 01267 for(MultiArrayIndex k=0; k<N; ++k) 01268 ordering[permutation[k]] = k; 01269 return ordering; 01270 } 01271 01272 /** 01273 Returns the value type of the elements in this array, or -1 01274 when hasData() is false. 01275 */ 01276 int dtype() const 01277 { 01278 if(hasData()) 01279 return PyArray_DESCR(pyObject())->type_num; 01280 return -1; 01281 } 01282 01283 /** 01284 * Return a borrowed reference to the internal PyArrayObject. 01285 */ 01286 PyArrayObject * pyArray() const 01287 { 01288 return (PyArrayObject *)pyArray_.get(); 01289 } 01290 01291 /** 01292 * Return a borrowed reference to the internal PyArrayObject 01293 * (see pyArray()), cast to PyObject for your convenience. 01294 */ 01295 PyObject * pyObject() const 01296 { 01297 return pyArray_.get(); 01298 } 01299 01300 /** 01301 Reset the NumpyAnyArray to the given object. If \a obj is a numpy array object, 01302 a new reference to that array is created, and the function returns 01303 true. Otherwise, it returns false and the NumpyAnyArray remains unchanged. 01304 If \a type is given, the new reference will be a view with that type, provided 01305 that \a type is a numpy ndarray or a subclass thereof. Otherwise, an 01306 exception is thrown. 01307 */ 01308 bool makeReference(PyObject * obj, PyTypeObject * type = 0) 01309 { 01310 if(obj == 0 || !PyArray_Check(obj)) 01311 return false; 01312 if(type != 0) 01313 { 01314 vigra_precondition(PyType_IsSubtype(type, &PyArray_Type) != 0, 01315 "NumpyAnyArray::makeReference(obj, type): type must be numpy.ndarray or a subclass thereof."); 01316 obj = PyArray_View((PyArrayObject*)obj, 0, type); 01317 pythonToCppException(obj); 01318 } 01319 pyArray_.reset(obj); 01320 return true; 01321 } 01322 01323 /** 01324 Create a copy of the given array object. If \a obj is a numpy array object, 01325 a copy is created via the C-equivalent of 'obj->copy()'. If 01326 this call fails, or obj was not an array, an exception is thrown 01327 and the NumpyAnyArray remains unchanged. 01328 */ 01329 void makeCopy(PyObject * obj, PyTypeObject * type = 0) 01330 { 01331 vigra_precondition(obj && PyArray_Check(obj), 01332 "NumpyAnyArray::makeCopy(obj): obj is not an array."); 01333 vigra_precondition(type == 0 || PyType_IsSubtype(type, &PyArray_Type), 01334 "NumpyAnyArray::makeCopy(obj, type): type must be numpy.ndarray or a subclass thereof."); 01335 python_ptr array(PyArray_NewCopy((PyArrayObject*)obj, NPY_ANYORDER), python_ptr::keep_count); 01336 pythonToCppException(array); 01337 makeReference(array, type); 01338 } 01339 01340 /** 01341 Check whether this NumpyAnyArray actually points to a Python array. 01342 */ 01343 bool hasData() const 01344 { 01345 return pyArray_ != 0; 01346 } 01347 }; 01348 01349 /********************************************************/ 01350 /* */ 01351 /* NumpyArray */ 01352 /* */ 01353 /********************************************************/ 01354 01355 /** Provide the MultiArrayView interface for a Python array. 01356 01357 This class inherits from both \ref vigra::MultiArrayView and \ref vigra::NumpyAnyArray 01358 in order to support easy and save application of VIGRA functions to Python arrays. 01359 01360 <b>\#include</b> <<a href="numpy__array_8hxx-source.html">vigra/numpy_array.hxx</a>><br> 01361 Namespace: vigra 01362 */ 01363 template <unsigned int N, class T, class Stride = StridedArrayTag> 01364 class NumpyArray 01365 : public MultiArrayView<N, typename NumpyArrayTraits<N, T, Stride>::value_type, Stride>, 01366 public NumpyAnyArray 01367 { 01368 public: 01369 typedef NumpyArrayTraits<N, T, Stride> ArrayTraits; 01370 typedef typename ArrayTraits::dtype dtype; 01371 typedef T pseudo_value_type; 01372 01373 static NPY_TYPES const typeCode = ArrayTraits::typeCode; 01374 01375 /** the view type associated with this array. 01376 */ 01377 typedef MultiArrayView<N, typename ArrayTraits::value_type, Stride> view_type; 01378 01379 enum { actual_dimension = view_type::actual_dimension }; 01380 01381 /** the array's value type 01382 */ 01383 typedef typename view_type::value_type value_type; 01384 01385 /** pointer type 01386 */ 01387 typedef typename view_type::pointer pointer; 01388 01389 /** const pointer type 01390 */ 01391 typedef typename view_type::const_pointer const_pointer; 01392 01393 /** reference type (result of operator[]) 01394 */ 01395 typedef typename view_type::reference reference; 01396 01397 /** const reference type (result of operator[] const) 01398 */ 01399 typedef typename view_type::const_reference const_reference; 01400 01401 /** size type 01402 */ 01403 typedef typename view_type::size_type size_type; 01404 01405 /** difference type (used for multi-dimensional offsets and indices) 01406 */ 01407 typedef typename view_type::difference_type difference_type; 01408 01409 /** difference and index type for a single dimension 01410 */ 01411 typedef typename view_type::difference_type_1 difference_type_1; 01412 01413 /** traverser type 01414 */ 01415 typedef typename view_type::traverser traverser; 01416 01417 /** traverser type to const data 01418 */ 01419 typedef typename view_type::const_traverser const_traverser; 01420 01421 /** sequential (random access) iterator type 01422 */ 01423 typedef value_type * iterator; 01424 01425 /** sequential (random access) const iterator type 01426 */ 01427 typedef value_type * const_iterator; 01428 01429 using view_type::shape; // resolve ambiguity of multiple inheritance 01430 using view_type::hasData; // resolve ambiguity of multiple inheritance 01431 using view_type::strideOrdering; // resolve ambiguity of multiple inheritance 01432 01433 protected: 01434 01435 // this function assumes that pyArray_ has already been set, and compatibility been checked 01436 void setupArrayView(); 01437 01438 static python_ptr getArrayTypeObject() 01439 { 01440 python_ptr type = detail::getArrayTypeObject(ArrayTraits::typeKeyFull()); 01441 if(type == 0) 01442 type = detail::getArrayTypeObject(ArrayTraits::typeKey(), &PyArray_Type); 01443 return type; 01444 } 01445 01446 static python_ptr init(difference_type const & shape, bool init = true) 01447 { 01448 ArrayVector<npy_intp> pshape(shape.begin(), shape.end()); 01449 return detail::constructNumpyArrayImpl((PyTypeObject *)getArrayTypeObject().ptr(), pshape, 01450 ArrayTraits::spatialDimensions, ArrayTraits::channels, 01451 typeCode, "V", init); 01452 } 01453 01454 static python_ptr init(difference_type const & shape, difference_type const & strideOrdering, bool init = true) 01455 { 01456 ArrayVector<npy_intp> pshape(shape.begin(), shape.end()), 01457 pstrideOrdering(strideOrdering.begin(), strideOrdering.end()); 01458 return detail::constructNumpyArrayImpl((PyTypeObject *)getArrayTypeObject().ptr(), pshape, 01459 ArrayTraits::spatialDimensions, ArrayTraits::channels, 01460 typeCode, "A", init, pstrideOrdering); 01461 } 01462 01463 public: 01464 01465 using view_type::init; 01466 01467 /** 01468 * Construct from a given PyObject pointer. When the given 01469 * python object is NULL, the internal python array will be 01470 * NULL and hasData() will return false. 01471 * 01472 * Otherwise, the function attempts to create a 01473 * new reference to the given Python object, unless 01474 * copying is forced by setting \a createCopy to true. 01475 * If either of this fails, the function throws an exception. 01476 * This will not happen if isStrictlyCompatible(obj) (in case 01477 * of creating a new reference) or isCopyCompatible(obj) 01478 * (in case of copying) have returned true beforehand. 01479 */ 01480 explicit NumpyArray(PyObject *obj = 0, bool createCopy = false) 01481 { 01482 if(obj == 0) 01483 return; 01484 if(createCopy) 01485 makeCopy(obj); 01486 else 01487 vigra_precondition(makeReference(obj), 01488 "NumpyArray(obj): Cannot construct from incompatible array."); 01489 } 01490 01491 /** 01492 * Copy constructor; does not copy the memory, but creates a 01493 * new reference to the same underlying python object, unless 01494 * a copy is forced by setting \a createCopy to true. 01495 * (If the source object has no data, this one will have 01496 * no data, too.) 01497 */ 01498 NumpyArray(const NumpyArray &other, bool createCopy = false) : 01499 MultiArrayView<N, typename NumpyArrayTraits<N, T, Stride>::value_type, Stride>(other), 01500 NumpyAnyArray(other, createCopy) 01501 { 01502 if(!other.hasData()) 01503 return; 01504 if(createCopy) 01505 makeCopy(other.pyObject()); 01506 else 01507 makeReferenceUnchecked(other.pyObject()); 01508 } 01509 01510 /** 01511 * Allocate new memory and copy data from a MultiArrayView. 01512 */ 01513 explicit NumpyArray(const view_type &other) 01514 { 01515 if(!other.hasData()) 01516 return; 01517 vigra_postcondition(makeReference(init(other.shape(), false)), 01518 "NumpyArray(view_type): Python constructor did not produce a compatible array."); 01519 static_cast<view_type &>(*this) = other; 01520 } 01521 01522 /** 01523 * Construct a new array object, allocating an internal python 01524 * ndarray of the given shape (in fortran order), initialized 01525 * with zeros. 01526 * 01527 * An exception is thrown when construction fails. 01528 */ 01529 explicit NumpyArray(difference_type const & shape) 01530 { 01531 vigra_postcondition(makeReference(init(shape)), 01532 "NumpyArray(shape): Python constructor did not produce a compatible array."); 01533 } 01534 01535 /** 01536 * Construct a new array object, allocating an internal python 01537 * ndarray of the given shape and given stride ordering, initialized 01538 * with zeros. 01539 * 01540 * An exception is thrown when construction fails. 01541 */ 01542 NumpyArray(difference_type const & shape, difference_type const & strideOrdering) 01543 { 01544 vigra_postcondition(makeReference(init(shape, strideOrdering)), 01545 "NumpyArray(shape): Python constructor did not produce a compatible array."); 01546 } 01547 01548 /** 01549 * Constructor from NumpyAnyArray. 01550 * Equivalent to NumpyArray(other.pyObject()) 01551 */ 01552 NumpyArray(const NumpyAnyArray &other, bool createCopy = false) 01553 { 01554 if(!other.hasData()) 01555 return; 01556 if(createCopy) 01557 makeCopy(other.pyObject()); 01558 else 01559 vigra_precondition(makeReference(other.pyObject()), //, false), 01560 "NumpyArray(NumpyAnyArray): Cannot construct from incompatible or empty array."); 01561 } 01562 01563 /** 01564 * Assignment operator. If this is already a view with data 01565 * (i.e. hasData() is true) and the shapes match, the RHS 01566 * array contents are copied. If this is an empty view, 01567 * assignment is identical to makeReferenceUnchecked(other.pyObject()). 01568 * See MultiArrayView::operator= for further information on 01569 * semantics. 01570 */ 01571 NumpyArray &operator=(const NumpyArray &other) 01572 { 01573 if(hasData()) 01574 view_type::operator=(other); 01575 else 01576 makeReferenceUnchecked(other.pyObject()); 01577 return *this; 01578 } 01579 01580 /** 01581 * Assignment operator. If this is already a view with data 01582 * (i.e. hasData() is true) and the shapes match, the RHS 01583 * array contents are copied. 01584 * If this is an empty view, assignment is identical to 01585 * makeReference(other.pyObject()). 01586 * Otherwise, an exception is thrown. 01587 */ 01588 NumpyArray &operator=(const NumpyAnyArray &other) 01589 { 01590 if(hasData()) 01591 { 01592 NumpyAnyArray::operator=(other); 01593 } 01594 else if(isStrictlyCompatible(other.pyObject())) 01595 { 01596 makeReferenceUnchecked(other.pyObject()); 01597 } 01598 else 01599 { 01600 vigra_precondition(false, 01601 "NumpyArray::operator=(): Cannot assign from incompatible array."); 01602 } 01603 return *this; 01604 } 01605 01606 /** 01607 * Test whether a given python object is a numpy array that can be 01608 * converted (copied) into an array compatible to this NumpyArray type. 01609 * This means that the array's shape conforms to the requirements of 01610 * makeCopy(). 01611 */ 01612 static bool isCopyCompatible(PyObject *obj) 01613 { 01614 return ArrayTraits::isArray(obj) && 01615 ArrayTraits::isShapeCompatible((PyArrayObject *)obj); 01616 } 01617 01618 /** 01619 * Test whether a given python object is a numpy array with a 01620 * compatible dtype and the correct shape and strides, so that it 01621 * can be referenced as a view by this NumpyArray type (i.e. 01622 * it conforms to the requirements of makeReference()). 01623 */ 01624 static bool isReferenceCompatible(PyObject *obj) 01625 { 01626 return ArrayTraits::isArray(obj) && 01627 ArrayTraits::isPropertyCompatible((PyArrayObject *)obj); 01628 } 01629 01630 /** 01631 * Like isReferenceCompatible(obj), but also executes a customized type compatibility 01632 * check when such a check has been registered for this class via 01633 * registerPythonArrayType(). 01634 * 01635 * This facilitates proper overload resolution between 01636 * NumpyArray<3, Multiband<T> > (a multiband image) and NumpyArray<3, Singleband<T> > (a scalar volume). 01637 */ 01638 static bool isStrictlyCompatible(PyObject *obj) 01639 { 01640 #if VIGRA_CONVERTER_DEBUG 01641 std::cerr << "class " << typeid(NumpyArray).name() << " got " << obj->ob_type->tp_name << "\n"; 01642 bool isClassCompatible=ArrayTraits::isClassCompatible(obj); 01643 bool isPropertyCompatible((PyArrayObject *)obj); 01644 std::cerr<<"isClassCompatible: "<<isClassCompatible<<std::endl; 01645 std::cerr<<"isPropertyCompatible: "<<isPropertyCompatible<<std::endl; 01646 #endif 01647 return ArrayTraits::isClassCompatible(obj) && 01648 ArrayTraits::isPropertyCompatible((PyArrayObject *)obj); 01649 } 01650 01651 /** 01652 * Create a vector representing the standard stride ordering of a NumpyArray. 01653 * That is, we get a vector representing the range [0,...,N-1], which 01654 * denotes the stride ordering for Fortran order. 01655 */ 01656 static difference_type standardStrideOrdering() 01657 { 01658 difference_type strideOrdering; 01659 for(unsigned int k=0; k<N; ++k) 01660 strideOrdering[k] = k; 01661 return strideOrdering; 01662 } 01663 01664 /** 01665 * Set up a view to the given object without checking compatibility. 01666 * This function must not be used unless isReferenceCompatible(obj) returned 01667 * true on the given object (otherwise, a crash is likely). 01668 */ 01669 void makeReferenceUnchecked(PyObject *obj) 01670 { 01671 NumpyAnyArray::makeReference(obj); 01672 setupArrayView(); 01673 } 01674 01675 /** 01676 * Try to set up a view referencing the given PyObject. 01677 * Returns false if the python object is not a compatible 01678 * numpy array (see isReferenceCompatible() or 01679 * isStrictlyCompatible(), according to the parameter \a 01680 * strict). 01681 */ 01682 bool makeReference(PyObject *obj, bool strict = true) 01683 { 01684 if(strict) 01685 { 01686 if(!isStrictlyCompatible(obj)) 01687 return false; 01688 } 01689 else 01690 { 01691 if(!isReferenceCompatible(obj)) 01692 return false; 01693 } 01694 makeReferenceUnchecked(obj); 01695 return true; 01696 } 01697 01698 /** 01699 * Try to set up a view referencing the same data as the given 01700 * NumpyAnyArray. This overloaded variant simply calls 01701 * makeReference() on array.pyObject(). 01702 */ 01703 bool makeReference(const NumpyAnyArray &array, bool strict = true) 01704 { 01705 return makeReference(array.pyObject(), strict); 01706 } 01707 01708 /** 01709 * Set up an unsafe reference to the given MultiArrayView. 01710 * ATTENTION: This creates a numpy.ndarray that points to the 01711 * same data, but does not own it, so it must be ensured by 01712 * other means that the memory does not get freed before the 01713 * end of the ndarray's lifetime! (One elegant way would be 01714 * to set the 'base' attribute of the resulting ndarray to a 01715 * python object which directly or indirectly holds the memory 01716 * of the given MultiArrayView.) 01717 */ 01718 void makeReference(const view_type &multiArrayView) 01719 { 01720 vigra_precondition(!hasData(), "makeReference(): cannot replace existing view with given buffer"); 01721 01722 // construct an ndarray that points to our data (taking strides into account): 01723 python_ptr array(ArrayTraits::constructor(multiArrayView.shape(), multiArrayView.data(), multiArrayView.stride())); 01724 01725 view_type::operator=(multiArrayView); 01726 pyArray_ = array; 01727 } 01728 01729 /** 01730 Try to create a copy of the given PyObject. 01731 Raises an exception when obj is not a compatible array 01732 (see isCopyCompatible() or isStrictlyCompatible(), according to the 01733 parameter \a strict) or the Python constructor call failed. 01734 */ 01735 void makeCopy(PyObject *obj, bool strict = false) 01736 { 01737 vigra_precondition(strict ? isStrictlyCompatible(obj) : isCopyCompatible(obj), 01738 "NumpyArray::makeCopy(obj): Cannot copy an incompatible array."); 01739 01740 int M = PyArray_NDIM(obj); 01741 TinyVector<npy_intp, N> shape; 01742 std::copy(PyArray_DIMS(obj), PyArray_DIMS(obj)+M, shape.begin()); 01743 if(M == N-1) 01744 shape[M] = 1; 01745 vigra_postcondition(makeReference(init(shape, false)), 01746 "NumpyArray::makeCopy(obj): Copy created an incompatible array."); 01747 NumpyAnyArray::operator=(NumpyAnyArray(obj)); 01748 // if(PyArray_CopyInto(pyArray(), (PyArrayObject*)obj) == -1) 01749 // pythonToCppException(0); 01750 } 01751 01752 /** 01753 Allocate new memory with the given shape and initialize with zeros.<br> 01754 If a stride ordering is given, the resulting array will have this stride 01755 ordering, when it is compatible with the array's memory layout (unstrided 01756 arrays only permit the standard ascending stride ordering). 01757 01758 <em>Note:</em> this operation invalidates dependent objects 01759 (MultiArrayViews and iterators) 01760 */ 01761 void reshape(difference_type const & shape, difference_type const & strideOrdering = standardStrideOrdering()) 01762 { 01763 vigra_postcondition(makeReference(init(shape, strideOrdering)), 01764 "NumpyArray(shape): Python constructor did not produce a compatible array."); 01765 } 01766 01767 /** 01768 When this array has no data, allocate new memory with the given \a shape and 01769 initialize with zeros. Otherwise, check if the new shape matches the old shape 01770 and throw a precondition exception with the given \a message if not. 01771 */ 01772 void reshapeIfEmpty(difference_type const & shape, std::string message = "") 01773 { 01774 reshapeIfEmpty(shape, standardStrideOrdering(), message); 01775 } 01776 01777 /** 01778 When this array has no data, allocate new memory with the given \a shape and 01779 initialize with zeros. Otherwise, check if the new shape matches the old shape 01780 and throw a precondition exception with the given \a message if not. If strict 01781 is true, the given stride ordering must also match that of the existing data. 01782 */ 01783 void reshapeIfEmpty(difference_type const & shape, difference_type const & strideOrdering, 01784 std::string message = "", bool strict = false) 01785 { 01786 if(hasData()) 01787 { 01788 if(strict) 01789 { 01790 if(message == "") 01791 message = "NumpyArray::reshapeIfEmpty(shape): array was not empty, and shape or stride ordering did not match."; 01792 vigra_precondition(shape == this->shape() && strideOrdering == this->strideOrdering(), message.c_str()); 01793 } 01794 else 01795 { 01796 if(message == "") 01797 message = "NumpyArray::reshapeIfEmpty(shape): array was not empty, and shape did not match."; 01798 vigra_precondition(shape == this->shape(), message.c_str()); 01799 } 01800 } 01801 else 01802 { 01803 reshape(shape, strideOrdering); 01804 } 01805 } 01806 }; 01807 01808 // this function assumes that pyArray_ has already been set, and compatibility been checked 01809 template <unsigned int N, class T, class Stride> 01810 void NumpyArray<N, T, Stride>::setupArrayView() 01811 { 01812 if(NumpyAnyArray::hasData()) 01813 { 01814 unsigned int dimension = std::min<unsigned int>(actual_dimension, pyArray()->nd); 01815 std::copy(pyArray()->dimensions, pyArray()->dimensions + dimension, this->m_shape.begin()); 01816 std::copy(pyArray()->strides, pyArray()->strides + dimension, this->m_stride.begin()); 01817 if(pyArray()->nd < actual_dimension) 01818 { 01819 this->m_shape[dimension] = 1; 01820 this->m_stride[dimension] = sizeof(value_type); 01821 } 01822 this->m_stride /= sizeof(value_type); 01823 this->m_ptr = reinterpret_cast<pointer>(pyArray()->data); 01824 } 01825 else 01826 { 01827 this->m_ptr = 0; 01828 } 01829 } 01830 01831 01832 typedef NumpyArray<2, float > NumpyFArray2; 01833 typedef NumpyArray<3, float > NumpyFArray3; 01834 typedef NumpyArray<4, float > NumpyFArray4; 01835 typedef NumpyArray<2, Singleband<float> > NumpyFImage; 01836 typedef NumpyArray<3, Singleband<float> > NumpyFVolume; 01837 typedef NumpyArray<2, RGBValue<float> > NumpyFRGBImage; 01838 typedef NumpyArray<3, RGBValue<float> > NumpyFRGBVolume; 01839 typedef NumpyArray<3, Multiband<float> > NumpyFMultibandImage; 01840 typedef NumpyArray<4, Multiband<float> > NumpyFMultibandVolume; 01841 01842 inline void import_vigranumpy() 01843 { 01844 if(_import_array() < 0) 01845 pythonToCppException(0); 01846 python_ptr module(PyImport_ImportModule("vigra.vigranumpycore"), python_ptr::keep_count); 01847 pythonToCppException(module); 01848 } 01849 01850 /********************************************************/ 01851 /* */ 01852 /* NumpyArray Multiband Argument Object Factories */ 01853 /* */ 01854 /********************************************************/ 01855 01856 template <class PixelType, class Stride> 01857 inline triple<ConstStridedImageIterator<PixelType>, 01858 ConstStridedImageIterator<PixelType>, 01859 MultibandVectorAccessor<PixelType> > 01860 srcImageRange(NumpyArray<3, Multiband<PixelType>, Stride> const & img) 01861 { 01862 ConstStridedImageIterator<PixelType> 01863 ul(img.data(), 1, img.stride(0), img.stride(1)); 01864 return triple<ConstStridedImageIterator<PixelType>, 01865 ConstStridedImageIterator<PixelType>, 01866 MultibandVectorAccessor<PixelType> > 01867 (ul, ul + Size2D(img.shape(0), img.shape(1)), MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2))); 01868 } 01869 01870 template <class PixelType, class Stride> 01871 inline pair< ConstStridedImageIterator<PixelType>, 01872 MultibandVectorAccessor<PixelType> > 01873 srcImage(NumpyArray<3, Multiband<PixelType>, Stride> const & img) 01874 { 01875 ConstStridedImageIterator<PixelType> 01876 ul(img.data(), 1, img.stride(0), img.stride(1)); 01877 return pair<ConstStridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> > 01878 (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2))); 01879 } 01880 01881 template <class PixelType, class Stride> 01882 inline triple< StridedImageIterator<PixelType>, 01883 StridedImageIterator<PixelType>, 01884 MultibandVectorAccessor<PixelType> > 01885 destImageRange(NumpyArray<3, Multiband<PixelType>, Stride> & img) 01886 { 01887 StridedImageIterator<PixelType> 01888 ul(img.data(), 1, img.stride(0), img.stride(1)); 01889 typedef typename AccessorTraits<PixelType>::default_accessor Accessor; 01890 return triple<StridedImageIterator<PixelType>, 01891 StridedImageIterator<PixelType>, 01892 MultibandVectorAccessor<PixelType> > 01893 (ul, ul + Size2D(img.shape(0), img.shape(1)), 01894 MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2))); 01895 } 01896 01897 template <class PixelType, class Stride> 01898 inline pair< StridedImageIterator<PixelType>, 01899 MultibandVectorAccessor<PixelType> > 01900 destImage(NumpyArray<3, Multiband<PixelType>, Stride> & img) 01901 { 01902 StridedImageIterator<PixelType> 01903 ul(img.data(), 1, img.stride(0), img.stride(1)); 01904 return pair<StridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> > 01905 (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2))); 01906 } 01907 01908 template <class PixelType, class Stride> 01909 inline pair< ConstStridedImageIterator<PixelType>, 01910 MultibandVectorAccessor<PixelType> > 01911 maskImage(NumpyArray<3, Multiband<PixelType>, Stride> const & img) 01912 { 01913 ConstStridedImageIterator<PixelType> 01914 ul(img.data(), 1, img.stride(0), img.stride(1)); 01915 typedef typename AccessorTraits<PixelType>::default_accessor Accessor; 01916 return pair<ConstStridedImageIterator<PixelType>, MultibandVectorAccessor<PixelType> > 01917 (ul, MultibandVectorAccessor<PixelType>(img.shape(2), img.stride(2))); 01918 } 01919 01920 } // namespace vigra 01921 01922 #endif // VIGRA_NUMPY_ARRAY_HXX
© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de) |
html generated using doxygen and Python
|