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

© 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.12.1