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

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

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

html generated using doxygen and Python
vigra 1.9.0 (Tue Sep 24 2013)