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

numpy_array_taggedshape.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 2009 by Ullrich Koethe and Hans Meine */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_NUMPY_ARRAY_TAGGEDSHAPE_HXX
37 #define VIGRA_NUMPY_ARRAY_TAGGEDSHAPE_HXX
38 
39 #ifndef NPY_NO_DEPRECATED_API
40 # define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
41 #endif
42 
43 #include <string>
44 #include "array_vector.hxx"
45 #include "python_utility.hxx"
46 #include "axistags.hxx"
47 
48 namespace vigra {
49 
50 namespace detail {
51 
52 inline
53 python_ptr getArrayTypeObject()
54 {
55  python_ptr arraytype((PyObject*)&PyArray_Type);
56  python_ptr vigra(PyImport_ImportModule("vigra"));
57  if(!vigra)
58  PyErr_Clear();
59  return pythonGetAttr(vigra, "standardArrayType", arraytype);
60 }
61 
62 inline
63 std::string defaultOrder(std::string defaultValue = "C")
64 {
65  python_ptr arraytype = getArrayTypeObject();
66  return pythonGetAttr(arraytype, "defaultOrder", defaultValue);
67 }
68 
69 inline
70 python_ptr defaultAxistags(int ndim, std::string order = "")
71 {
72  if(order == "")
73  order = defaultOrder();
74  python_ptr arraytype = getArrayTypeObject();
75  python_ptr func(PyString_FromString("defaultAxistags"), python_ptr::keep_count);
76  python_ptr d(PyInt_FromLong(ndim), python_ptr::keep_count);
77  python_ptr o(PyString_FromString(order.c_str()), python_ptr::keep_count);
78  python_ptr axistags(PyObject_CallMethodObjArgs(arraytype, func.get(), d.get(), o.get(), NULL),
79  python_ptr::keep_count);
80  if(axistags)
81  return axistags;
82  PyErr_Clear();
83  return python_ptr();
84 }
85 
86 inline
87 python_ptr emptyAxistags(int ndim)
88 {
89  python_ptr arraytype = getArrayTypeObject();
90  python_ptr func(PyString_FromString("_empty_axistags"), python_ptr::keep_count);
91  python_ptr d(PyInt_FromLong(ndim), python_ptr::keep_count);
92  python_ptr axistags(PyObject_CallMethodObjArgs(arraytype, func.get(), d.get(), NULL),
93  python_ptr::keep_count);
94  if(axistags)
95  return axistags;
96  PyErr_Clear();
97  return python_ptr();
98 }
99 
100 inline
101 void
102 getAxisPermutationImpl(ArrayVector<npy_intp> & permute,
103  python_ptr object, const char * name,
104  AxisInfo::AxisType type, bool ignoreErrors)
105 {
106  python_ptr func(PyString_FromString(name), python_ptr::keep_count);
107  python_ptr t(PyInt_FromLong((long)type), python_ptr::keep_count);
108  python_ptr permutation(PyObject_CallMethodObjArgs(object, func.get(), t.get(), NULL),
109  python_ptr::keep_count);
110  if(!permutation && ignoreErrors)
111  {
112  PyErr_Clear();
113  return;
114  }
115  pythonToCppException(permutation);
116 
117  if(!PySequence_Check(permutation))
118  {
119  if(ignoreErrors)
120  return;
121  std::string message = std::string(name) + "() did not return a sequence.";
122  PyErr_SetString(PyExc_ValueError, message.c_str());
123  pythonToCppException(false);
124  }
125 
126  ArrayVector<npy_intp> res(PySequence_Length(permutation));
127  for(int k=0; k<(int)res.size(); ++k)
128  {
129  python_ptr i(PySequence_GetItem(permutation, k), python_ptr::keep_count);
130  if(!PyInt_Check(i))
131  {
132  if(ignoreErrors)
133  return;
134  std::string message = std::string(name) + "() did not return a sequence of int.";
135  PyErr_SetString(PyExc_ValueError, message.c_str());
136  pythonToCppException(false);
137  }
138  res[k] = PyInt_AsLong(i);
139  }
140  res.swap(permute);
141 }
142 
143 inline
144 void
145 getAxisPermutationImpl(ArrayVector<npy_intp> & permute,
146  python_ptr object, const char * name, bool ignoreErrors)
147 {
148  getAxisPermutationImpl(permute, object, name, AxisInfo::AllAxes, ignoreErrors);
149 }
150 
151 } // namespace detail
152 
153 /********************************************************/
154 /* */
155 /* PyAxisTags */
156 /* */
157 /********************************************************/
158 
159 // FIXME: right now, we implement this class using the standard
160 // Python C-API only. It would be easier and more efficient
161 // to use boost::python here, but it would cause NumpyArray
162 // to depend on boost, making it more difficult to use
163 // NumpyArray in connection with other glue code generators.
164 class PyAxisTags
165 {
166  public:
167  typedef PyObject * pointer;
168 
169  python_ptr axistags;
170 
171  PyAxisTags(python_ptr tags = python_ptr(), bool createCopy = false)
172  {
173  if(!tags)
174  return;
175  // FIXME: do a more elaborate type check here?
176  if(!PySequence_Check(tags))
177  {
178  PyErr_SetString(PyExc_TypeError,
179  "PyAxisTags(tags): tags argument must have type 'AxisTags'.");
180  pythonToCppException(false);
181  }
182  else if(PySequence_Length(tags) == 0)
183  {
184  return;
185  }
186 
187  if(createCopy)
188  {
189  python_ptr func(PyString_FromString("__copy__"), python_ptr::keep_count);
190  axistags = python_ptr(PyObject_CallMethodObjArgs(tags, func.get(), NULL),
191  python_ptr::keep_count);
192  }
193  else
194  {
195  axistags = tags;
196  }
197  }
198 
199  PyAxisTags(PyAxisTags const & other, bool createCopy = false)
200  {
201  if(!other.axistags)
202  return;
203  if(createCopy)
204  {
205  python_ptr func(PyString_FromString("__copy__"), python_ptr::keep_count);
206  axistags = python_ptr(PyObject_CallMethodObjArgs(other.axistags, func.get(), NULL),
207  python_ptr::keep_count);
208  }
209  else
210  {
211  axistags = other.axistags;
212  }
213  }
214 
215  PyAxisTags(int ndim, std::string const & order = "")
216  {
217  if(order != "")
218  axistags = detail::defaultAxistags(ndim, order);
219  else
220  axistags = detail::emptyAxistags(ndim);
221  }
222 
223  long size() const
224  {
225  return axistags
226  ? PySequence_Length(axistags)
227  : 0;
228  }
229 
230  long channelIndex(long defaultVal) const
231  {
232  return pythonGetAttr(axistags, "channelIndex", defaultVal);
233  }
234 
235  long channelIndex() const
236  {
237  return channelIndex(size());
238  }
239 
240  bool hasChannelAxis() const
241  {
242  return channelIndex() != size();
243  }
244 
245  long innerNonchannelIndex(long defaultVal) const
246  {
247  return pythonGetAttr(axistags, "innerNonchannelIndex", defaultVal);
248  }
249 
250  long innerNonchannelIndex() const
251  {
252  return innerNonchannelIndex(size());
253  }
254 
255  void setChannelDescription(std::string const & description)
256  {
257  if(!axistags)
258  return;
259  python_ptr d(PyString_FromString(description.c_str()), python_ptr::keep_count);
260  python_ptr func(PyString_FromString("setChannelDescription"), python_ptr::keep_count);
261  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), d.get(), NULL),
262  python_ptr::keep_count);
263  pythonToCppException(res);
264  }
265 
266  double resolution(long index)
267  {
268  if(!axistags)
269  return 0.0;
270  python_ptr func(PyString_FromString("resolution"), python_ptr::keep_count);
271  python_ptr i(PyInt_FromLong(index), python_ptr::keep_count);
272  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), i.get(), NULL),
273  python_ptr::keep_count);
274  pythonToCppException(res);
275  if(!PyFloat_Check(res))
276  {
277  PyErr_SetString(PyExc_TypeError, "AxisTags.resolution() did not return float.");
278  pythonToCppException(false);
279  }
280  return PyFloat_AsDouble(res);
281  }
282 
283  void setResolution(long index, double resolution)
284  {
285  if(!axistags)
286  return;
287  python_ptr func(PyString_FromString("setResolution"), python_ptr::keep_count);
288  python_ptr i(PyInt_FromLong(index), python_ptr::keep_count);
289  python_ptr r(PyFloat_FromDouble(resolution), python_ptr::keep_count);
290  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), i.get(), r.get(), NULL),
291  python_ptr::keep_count);
292  pythonToCppException(res);
293  }
294 
295  void scaleResolution(long index, double factor)
296  {
297  if(!axistags)
298  return;
299  python_ptr func(PyString_FromString("scaleResolution"), python_ptr::keep_count);
300  python_ptr i(PyInt_FromLong(index), python_ptr::keep_count);
301  python_ptr f(PyFloat_FromDouble(factor), python_ptr::keep_count);
302  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), i.get(), f.get(), NULL),
303  python_ptr::keep_count);
304  pythonToCppException(res);
305  }
306 
307  void toFrequencyDomain(long index, int size, int sign = 1)
308  {
309  if(!axistags)
310  return;
311  python_ptr func(sign == 1
312  ? PyString_FromString("toFrequencyDomain")
313  : PyString_FromString("fromFrequencyDomain"),
314  python_ptr::keep_count);
315  python_ptr i(PyInt_FromLong(index), python_ptr::keep_count);
316  python_ptr s(PyInt_FromLong(size), python_ptr::keep_count);
317  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), i.get(), s.get(), NULL),
318  python_ptr::keep_count);
319  pythonToCppException(res);
320  }
321 
322  void fromFrequencyDomain(long index, int size)
323  {
324  toFrequencyDomain(index, size, -1);
325  }
326 
327  ArrayVector<npy_intp>
328  permutationToNormalOrder(bool ignoreErrors = false) const
329  {
330  ArrayVector<npy_intp> permute;
331  detail::getAxisPermutationImpl(permute, axistags, "permutationToNormalOrder", ignoreErrors);
332  return permute;
333  }
334 
335  ArrayVector<npy_intp>
336  permutationToNormalOrder(AxisInfo::AxisType types, bool ignoreErrors = false) const
337  {
338  ArrayVector<npy_intp> permute;
339  detail::getAxisPermutationImpl(permute, axistags,
340  "permutationToNormalOrder", types, ignoreErrors);
341  return permute;
342  }
343 
344  ArrayVector<npy_intp>
345  permutationFromNormalOrder(bool ignoreErrors = false) const
346  {
347  ArrayVector<npy_intp> permute;
348  detail::getAxisPermutationImpl(permute, axistags,
349  "permutationFromNormalOrder", ignoreErrors);
350  return permute;
351  }
352 
353  ArrayVector<npy_intp>
354  permutationFromNormalOrder(AxisInfo::AxisType types, bool ignoreErrors = false) const
355  {
356  ArrayVector<npy_intp> permute;
357  detail::getAxisPermutationImpl(permute, axistags,
358  "permutationFromNormalOrder", types, ignoreErrors);
359  return permute;
360  }
361 
362  void dropChannelAxis()
363  {
364  if(!axistags)
365  return;
366  python_ptr func(PyString_FromString("dropChannelAxis"),
367  python_ptr::keep_count);
368  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), NULL),
369  python_ptr::keep_count);
370  pythonToCppException(res);
371  }
372 
373  void insertChannelAxis()
374  {
375  if(!axistags)
376  return;
377  python_ptr func(PyString_FromString("insertChannelAxis"),
378  python_ptr::keep_count);
379  python_ptr res(PyObject_CallMethodObjArgs(axistags, func.get(), NULL),
380  python_ptr::keep_count);
381  pythonToCppException(res);
382  }
383 
384  operator pointer()
385  {
386  return axistags.get();
387  }
388 
389  bool operator!() const
390  {
391  return !axistags;
392  }
393 };
394 
395 /********************************************************/
396 /* */
397 /* TaggedShape */
398 /* */
399 /********************************************************/
400 
401 class TaggedShape
402 {
403  public:
404  enum ChannelAxis { first, last, none };
405 
406  ArrayVector<npy_intp> shape, original_shape;
407  PyAxisTags axistags;
408  ChannelAxis channelAxis;
409  std::string channelDescription;
410 
411  explicit TaggedShape(MultiArrayIndex size)
412  : shape(size),
413  axistags(size),
414  channelAxis(none)
415  {}
416 
417  template <class U, int N>
418  TaggedShape(TinyVector<U, N> const & sh, PyAxisTags tags)
419  : shape(sh.begin(), sh.end()),
420  original_shape(sh.begin(), sh.end()),
421  axistags(tags),
422  channelAxis(none)
423  {}
424 
425  template <class T>
426  TaggedShape(ArrayVector<T> const & sh, PyAxisTags tags)
427  : shape(sh.begin(), sh.end()),
428  original_shape(sh.begin(), sh.end()),
429  axistags(tags),
430  channelAxis(none)
431  {}
432 
433  template <class U, int N>
434  explicit TaggedShape(TinyVector<U, N> const & sh)
435  : shape(sh.begin(), sh.end()),
436  original_shape(sh.begin(), sh.end()),
437  channelAxis(none)
438  {}
439 
440  template <class T>
441  explicit TaggedShape(ArrayVector<T> const & sh)
442  : shape(sh.begin(), sh.end()),
443  original_shape(sh.begin(), sh.end()),
444  channelAxis(none)
445  {}
446 
447  template <class U, int N>
448  TaggedShape & resize(TinyVector<U, N> const & sh)
449  {
450  int start = channelAxis == first
451  ? 1
452  : 0,
453  stop = channelAxis == last
454  ? (int)size()-1
455  : (int)size();
456 
457  vigra_precondition(N == stop - start || size() == 0,
458  "TaggedShape.resize(): size mismatch.");
459 
460  if(size() == 0)
461  shape.resize(N);
462 
463  for(int k=0; k<N; ++k)
464  shape[k+start] = sh[k];
465 
466  return *this;
467  }
468 
469  TaggedShape & resize(MultiArrayIndex v1)
470  {
471  return resize(TinyVector<MultiArrayIndex, 1>(v1));
472  }
473 
474  TaggedShape & resize(MultiArrayIndex v1, MultiArrayIndex v2)
475  {
476  return resize(TinyVector<MultiArrayIndex, 2>(v1, v2));
477  }
478 
479  TaggedShape & resize(MultiArrayIndex v1, MultiArrayIndex v2, MultiArrayIndex v3)
480  {
481  return resize(TinyVector<MultiArrayIndex, 3>(v1, v2, v3));
482  }
483 
484  TaggedShape & resize(MultiArrayIndex v1, MultiArrayIndex v2,
486  {
487  return resize(TinyVector<MultiArrayIndex, 4>(v1, v2, v3, v4));
488  }
489 
490  npy_intp & operator[](int i)
491  {
492  return shape[i];
493  }
494 
495  npy_intp operator[](int i) const
496  {
497  return shape[i];
498  }
499 
500  unsigned int size() const
501  {
502  return shape.size();
503  }
504 
505  TaggedShape & operator+=(int v)
506  {
507  int start = channelAxis == first
508  ? 1
509  : 0,
510  stop = channelAxis == last
511  ? (int)size()-1
512  : (int)size();
513  for(int k=start; k<stop; ++k)
514  shape[k] += v;
515 
516  return *this;
517  }
518 
519  TaggedShape & operator-=(int v)
520  {
521  return operator+=(-v);
522  }
523 
524  TaggedShape & operator*=(int factor)
525  {
526  int start = channelAxis == first
527  ? 1
528  : 0,
529  stop = channelAxis == last
530  ? (int)size()-1
531  : (int)size();
532  for(int k=start; k<stop; ++k)
533  shape[k] *= factor;
534 
535  return *this;
536  }
537 
538  void rotateToNormalOrder()
539  {
540  if(axistags && channelAxis == last)
541  {
542  int ndim = (int)size();
543 
544  npy_intp channelCount = shape[ndim-1];
545  for(int k=ndim-1; k>0; --k)
546  shape[k] = shape[k-1];
547  shape[0] = channelCount;
548 
549  channelCount = original_shape[ndim-1];
550  for(int k=ndim-1; k>0; --k)
551  original_shape[k] = original_shape[k-1];
552  original_shape[0] = channelCount;
553 
554  channelAxis = first;
555  }
556  }
557 
558  TaggedShape & setChannelDescription(std::string const & description)
559  {
560  // we only remember the description here, and will actually set
561  // it in the finalize function
562  channelDescription = description;
563  return *this;
564  }
565 
566  TaggedShape & setChannelIndexLast()
567  {
568  // FIXME: add some checks?
569  channelAxis = last;
570  return *this;
571  }
572 
573  // transposeShape() means: only shape and resolution are transposed, not the axis keys
574  template <class U, int N>
575  TaggedShape & transposeShape(TinyVector<U, N> const & p)
576  {
577  if(axistags)
578  {
579  int ntags = axistags.size();
580  ArrayVector<npy_intp> permute = axistags.permutationToNormalOrder();
581 
582  int tstart = (axistags.channelIndex(ntags) < ntags)
583  ? 1
584  : 0;
585  int sstart = (channelAxis == first)
586  ? 1
587  : 0;
588  int ndim = ntags - tstart;
589 
590  vigra_precondition(N == ndim,
591  "TaggedShape.transposeShape(): size mismatch.");
592 
593  PyAxisTags newAxistags(axistags.axistags); // force copy
594  for(int k=0; k<ndim; ++k)
595  {
596  original_shape[k+sstart] = shape[p[k]+sstart];
597  newAxistags.setResolution(permute[k+tstart], axistags.resolution(permute[p[k]+tstart]));
598  }
599  axistags = newAxistags;
600  }
601  else
602  {
603  for(int k=0; k<N; ++k)
604  {
605  original_shape[k] = shape[p[k]];
606  }
607  }
608  shape = original_shape;
609 
610  return *this;
611  }
612 
613  TaggedShape & toFrequencyDomain(int sign = 1)
614  {
615  if(axistags)
616  {
617  int ntags = axistags.size();
618 
619  ArrayVector<npy_intp> permute = axistags.permutationToNormalOrder();
620 
621  int tstart = (axistags.channelIndex(ntags) < ntags)
622  ? 1
623  : 0;
624  int sstart = (channelAxis == first)
625  ? 1
626  : 0;
627  int send = (channelAxis == last)
628  ? (int)size()-1
629  : (int)size();
630  int size = send - sstart;
631 
632  for(int k=0; k<size; ++k)
633  {
634  axistags.toFrequencyDomain(permute[k+tstart], shape[k+sstart], sign);
635  }
636  }
637  return *this;
638  }
639 
640  bool hasChannelAxis() const
641  {
642  return channelAxis !=none;
643  }
644 
645  TaggedShape & fromFrequencyDomain()
646  {
647  return toFrequencyDomain(-1);
648  }
649 
650  bool compatible(TaggedShape const & other) const
651  {
652  if(channelCount() != other.channelCount())
653  return false;
654 
655  int start = channelAxis == first
656  ? 1
657  : 0,
658  stop = channelAxis == last
659  ? (int)size()-1
660  : (int)size();
661  int ostart = other.channelAxis == first
662  ? 1
663  : 0,
664  ostop = other.channelAxis == last
665  ? (int)other.size()-1
666  : (int)other.size();
667 
668  int len = stop - start;
669  if(len != ostop - ostart)
670  return false;
671 
672  for(int k=0; k<len; ++k)
673  if(shape[k+start] != other.shape[k+ostart])
674  return false;
675  return true;
676  }
677 
678  TaggedShape & setChannelCount(int count)
679  {
680  switch(channelAxis)
681  {
682  case first:
683  if(count > 0)
684  {
685  shape[0] = count;
686  }
687  else
688  {
689  shape.erase(shape.begin());
690  original_shape.erase(original_shape.begin());
691  channelAxis = none;
692  }
693  break;
694  case last:
695  if(count > 0)
696  {
697  shape[size()-1] = count;
698  }
699  else
700  {
701  shape.pop_back();
702  original_shape.pop_back();
703  channelAxis = none;
704  }
705  break;
706  case none:
707  if(count > 0)
708  {
709  shape.push_back(count);
710  original_shape.push_back(count);
711  channelAxis = last;
712  }
713  break;
714  }
715  return *this;
716  }
717 
718  int channelCount() const
719  {
720  switch(channelAxis)
721  {
722  case first:
723  return shape[0];
724  case last:
725  return shape[size()-1];
726  default:
727  return 1;
728  }
729  }
730 };
731 
732 inline
733 void scaleAxisResolution(TaggedShape & tagged_shape)
734 {
735  if(tagged_shape.size() != tagged_shape.original_shape.size())
736  return;
737 
738  int ntags = tagged_shape.axistags.size();
739 
740  ArrayVector<npy_intp> permute = tagged_shape.axistags.permutationToNormalOrder();
741 
742  int tstart = (tagged_shape.axistags.channelIndex(ntags) < ntags)
743  ? 1
744  : 0;
745  int sstart = (tagged_shape.channelAxis == TaggedShape::first)
746  ? 1
747  : 0;
748  int size = (int)tagged_shape.size() - sstart;
749 
750  for(int k=0; k<size; ++k)
751  {
752  int sk = k + sstart;
753  if(tagged_shape.shape[sk] == tagged_shape.original_shape[sk])
754  continue;
755  double factor = (tagged_shape.original_shape[sk] - 1.0) / (tagged_shape.shape[sk] - 1.0);
756  tagged_shape.axistags.scaleResolution(permute[k+tstart], factor);
757  }
758 }
759 
760 inline
761 void unifyTaggedShapeSize(TaggedShape & tagged_shape)
762 {
763  PyAxisTags axistags = tagged_shape.axistags;
764  ArrayVector<npy_intp> & shape = tagged_shape.shape;
765 
766  int ndim = (int)shape.size();
767  int ntags = axistags.size();
768 
769  long channelIndex = axistags.channelIndex();
770 
771  if(tagged_shape.channelAxis == TaggedShape::none)
772  {
773  // shape has no channel axis
774  if(channelIndex == ntags)
775  {
776  // std::cerr << "branch (shape, axitags) 0 0\n";
777  // axistags have no channel axis either => sizes should match
778  vigra_precondition(ndim == ntags,
779  "constructArray(): size mismatch between shape and axistags.");
780  }
781  else
782  {
783  // std::cerr << "branch (shape, axitags) 0 1\n";
784  if(ndim+1 == ntags)
785  {
786  // std::cerr << " drop channel axis\n";
787  // axistags have one additional element => drop the channel tag
788  // FIXME: would it be cleaner to make this an error ?
789  axistags.dropChannelAxis();
790  }
791  else
792  {
793  vigra_precondition(ndim == ntags,
794  "constructArray(): size mismatch between shape and axistags.");
795  }
796  }
797  }
798  else
799  {
800  // shape has a channel axis
801  if(channelIndex == ntags)
802  {
803  // std::cerr << "branch (shape, axitags) 1 0\n";
804  // axistags have no channel axis => should be one element shorter
805  vigra_precondition(ndim == ntags+1,
806  "constructArray(): size mismatch between shape and axistags.");
807 
808  if(shape[0] == 1)
809  {
810  // std::cerr << " drop channel axis\n";
811  // we have a singleband image => drop the channel axis
812  shape.erase(shape.begin());
813  ndim -= 1;
814  }
815  else
816  {
817  // std::cerr << " insert channel axis\n";
818  // we have a multiband image => add a channel tag
819  axistags.insertChannelAxis();
820  }
821  }
822  else
823  {
824  // std::cerr << "branch (shape, axitags) 1 1\n";
825  // axistags have channel axis => sizes should match
826  vigra_precondition(ndim == ntags,
827  "constructArray(): size mismatch between shape and axistags.");
828  }
829  }
830 }
831 
832 inline
833 ArrayVector<npy_intp> finalizeTaggedShape(TaggedShape & tagged_shape)
834 {
835  if(tagged_shape.axistags)
836  {
837  tagged_shape.rotateToNormalOrder();
838 
839  // we assume here that the axistag object belongs to the array to be created
840  // so that we can freely edit it
841  scaleAxisResolution(tagged_shape);
842 
843  // this must be after scaleAxisResolution(), because the latter requires
844  // shape and original_shape to be still in sync
845  unifyTaggedShapeSize(tagged_shape);
846 
847  if(tagged_shape.channelDescription != "")
848  tagged_shape.axistags.setChannelDescription(tagged_shape.channelDescription);
849  }
850  return tagged_shape.shape;
851 }
852 
853 } // namespace vigra
854 
855 #endif // VIGRA_NUMPY_ARRAY_TAGGEDSHAPE_HXX
std::ptrdiff_t MultiArrayIndex
Definition: multi_fwd.hxx:60
Definition: accessor.hxx:43
FFTWComplex< R > & operator-=(FFTWComplex< R > &a, const FFTWComplex< R > &b)
subtract-assignment
Definition: fftw3.hxx:867
FFTWComplex< R > & operator+=(FFTWComplex< R > &a, const FFTWComplex< R > &b)
add-assignment
Definition: fftw3.hxx:859
FFTWComplex< R > & operator*=(FFTWComplex< R > &a, const FFTWComplex< R > &b)
multiply-assignment
Definition: fftw3.hxx:875
T sign(T t)
The sign function.
Definition: mathutil.hxx:574

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.10.0