[ 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
48namespace vigra {
49
50namespace detail {
51
52inline
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
62inline
63std::string defaultOrder(std::string defaultValue = "C")
64{
65 python_ptr arraytype = getArrayTypeObject();
66 return pythonGetAttr(arraytype, "defaultOrder", defaultValue);
67}
68
69inline
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
86inline
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
100inline
101void
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
151inline
152void
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.
172class 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
406class 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
737inline
738void 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
765inline
766void 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
837inline
838ArrayVector<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
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)
subtract-assignment
Definition: fftw3.hxx:867
T sign(T t)
The sign function.
Definition: mathutil.hxx:591
FFTWComplex< R > & operator*=(FFTWComplex< R > &a, const FFTWComplex< R > &b)
multiply-assignment
Definition: fftw3.hxx:875
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.11.1