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

© 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.11.1 (Fri May 19 2017)