vigra - vigra/numpy_array_taggedshape.hxx Source File

[ 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 #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,
485 MultiArrayIndex v3, MultiArrayIndex v4)
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 int ntags = axistags.size();
578 ArrayVector<npy_intp> permute = axistags.permutationToNormalOrder();
579
580 int tstart = (axistags.channelIndex(ntags) < ntags)
581 ? 1
582 : 0;
583 int sstart = (channelAxis == first)
584 ? 1
585 : 0;
586 int ndim = ntags - tstart;
587
588 vigra_precondition(N == ndim,
589 "TaggedShape.transposeShape(): size mismatch.");
590
591 PyAxisTags newAxistags(axistags.axistags); // force copy
592 for(int k=0; k<ndim; ++k)
593 {
594 original_shape[k+sstart] = shape[p[k]+sstart];
595 newAxistags.setResolution(permute[k+tstart], axistags.resolution(permute[p[k]+tstart]));
596 }
597 shape = original_shape;
598 axistags = newAxistags;
599
600 return *this;
601 }
602
603 TaggedShape & toFrequencyDomain(int sign = 1)
604 {
605 int ntags = axistags.size();
606
607 ArrayVector<npy_intp> permute = axistags.permutationToNormalOrder();
608
609 int tstart = (axistags.channelIndex(ntags) < ntags)
610 ? 1
611 : 0;
612 int sstart = (channelAxis == first)
613 ? 1
614 : 0;
615 int send = (channelAxis == last)
616 ? (int)size()-1
617 : (int)size();
618 int size = send - sstart;
619
620 for(int k=0; k<size; ++k)
621 {
622 axistags.toFrequencyDomain(permute[k+tstart], shape[k+sstart], sign);
623 }
624
625 return *this;
626 }
627
628 TaggedShape & fromFrequencyDomain()
629 {
630 return toFrequencyDomain(-1);
631 }
632
633 bool compatible(TaggedShape const & other) const
634 {
635 if(channelCount() != other.channelCount())
636 return false;
637
638 int start = channelAxis == first
639 ? 1
640 : 0,
641 stop = channelAxis == last
642 ? (int)size()-1
643 : (int)size();
644 int ostart = other.channelAxis == first
645 ? 1
646 : 0,
647 ostop = other.channelAxis == last
648 ? (int)other.size()-1
649 : (int)other.size();
650
651 int len = stop - start;
652 if(len != ostop - ostart)
653 return false;
654
655 for(int k=0; k<len; ++k)
656 if(shape[k+start] != other.shape[k+ostart])
657 return false;
658 return true;
659 }
660
661 TaggedShape & setChannelCount(int count)
662 {
663 switch(channelAxis)
664 {
665 case first:
666 if(count > 0)
667 {
668 shape[0] = count;
669 }
670 else
671 {
672 shape.erase(shape.begin());
673 original_shape.erase(original_shape.begin());
674 channelAxis = none;
675 }
676 break;
677 case last:
678 if(count > 0)
679 {
680 shape[size()-1] = count;
681 }
682 else
683 {
684 shape.pop_back();
685 original_shape.pop_back();
686 channelAxis = none;
687 }
688 break;
689 case none:
690 if(count > 0)
691 {
692 shape.push_back(count);
693 original_shape.push_back(count);
694 channelAxis = last;
695 }
696 break;
697 }
698 return *this;
699 }
700
701 int channelCount() const
702 {
703 switch(channelAxis)
704 {
705 case first:
706 return shape[0];
707 case last:
708 return shape[size()-1];
709 default:
710 return 1;
711 }
712 }
713 };
714
715 inline
716 void scaleAxisResolution(TaggedShape & tagged_shape)
717 {
718 if(tagged_shape.size() != tagged_shape.original_shape.size())
719 return;
720
721 int ntags = tagged_shape.axistags.size();
722
723 ArrayVector<npy_intp> permute = tagged_shape.axistags.permutationToNormalOrder();
724
725 int tstart = (tagged_shape.axistags.channelIndex(ntags) < ntags)
726 ? 1
727 : 0;
728 int sstart = (tagged_shape.channelAxis == TaggedShape::first)
729 ? 1
730 : 0;
731 int size = (int)tagged_shape.size() - sstart;
732
733 for(int k=0; k<size; ++k)
734 {
735 int sk = k + sstart;
736 if(tagged_shape.shape[sk] == tagged_shape.original_shape[sk])
737 continue;
738 double factor = (tagged_shape.original_shape[sk] - 1.0) / (tagged_shape.shape[sk] - 1.0);
739 tagged_shape.axistags.scaleResolution(permute[k+tstart], factor);
740 }
741 }
742
743 inline
744 void unifyTaggedShapeSize(TaggedShape & tagged_shape)
745 {
746 PyAxisTags axistags = tagged_shape.axistags;
747 ArrayVector<npy_intp> & shape = tagged_shape.shape;
748
749 int ndim = (int)shape.size();
750 int ntags = axistags.size();
751
752 long channelIndex = axistags.channelIndex();
753
754 if(tagged_shape.channelAxis == TaggedShape::none)
755 {
756 // shape has no channel axis
757 if(channelIndex == ntags)
758 {
759 // std::cerr << "branch (shape, axitags) 0 0\n";
760 // axistags have no channel axis either => sizes should match
761 vigra_precondition(ndim == ntags,
762 "constructArray(): size mismatch between shape and axistags.");
763 }
764 else
765 {
766 // std::cerr << "branch (shape, axitags) 0 1\n";
767 if(ndim+1 == ntags)
768 {
769 // std::cerr << " drop channel axis\n";
770 // axistags have one additional element => drop the channel tag
771 // FIXME: would it be cleaner to make this an error ?
772 axistags.dropChannelAxis();
773 }
774 else
775 {
776 vigra_precondition(ndim == ntags,
777 "constructArray(): size mismatch between shape and axistags.");
778 }
779 }
780 }
781 else
782 {
783 // shape has a channel axis
784 if(channelIndex == ntags)
785 {
786 // std::cerr << "branch (shape, axitags) 1 0\n";
787 // axistags have no channel axis => should be one element shorter
788 vigra_precondition(ndim == ntags+1,
789 "constructArray(): size mismatch between shape and axistags.");
790
791 if(shape[0] == 1)
792 {
793 // std::cerr << " drop channel axis\n";
794 // we have a singleband image => drop the channel axis
795 shape.erase(shape.begin());
796 ndim -= 1;
797 }
798 else
799 {
800 // std::cerr << " insert channel axis\n";
801 // we have a multiband image => add a channel tag
802 axistags.insertChannelAxis();
803 }
804 }
805 else
806 {
807 // std::cerr << "branch (shape, axitags) 1 1\n";
808 // axistags have channel axis => sizes should match
809 vigra_precondition(ndim == ntags,
810 "constructArray(): size mismatch between shape and axistags.");
811 }
812 }
813 }
814
815 inline
816 ArrayVector<npy_intp> finalizeTaggedShape(TaggedShape & tagged_shape)
817 {
818 if(tagged_shape.axistags)
819 {
820 tagged_shape.rotateToNormalOrder();
821
822 // we assume here that the axistag object belongs to the array to be created
823 // so that we can freely edit it
824 scaleAxisResolution(tagged_shape);
825
826 // this must be after scaleAxisResolution(), because the latter requires
827 // shape and original_shape to be still in sync
828 unifyTaggedShapeSize(tagged_shape);
829
830 if(tagged_shape.channelDescription != "")
831 tagged_shape.axistags.setChannelDescription(tagged_shape.channelDescription);
832 }
833 return tagged_shape.shape;
834 }
835
836 } // namespace vigra
837
838 #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.10.0 (Mon Nov 18 2013)