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