1 /************************************************************************/
2 /*                                                                      */
3 /*               Copyright 2010-2011 by Ullrich Koethe                  */
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_AXISTAGS_HXX
37 #define VIGRA_AXISTAGS_HXX
38 
39 #include "utilities.hxx"
40 #include "array_vector.hxx"
41 #include "algorithm.hxx"
42 #include "error.hxx"
43 #include "functorexpression.hxx"
44 #include <string>
45 #include <sstream>
46 #include <iomanip>
47 
48 namespace vigra {
49 
50 class AxisInfo
51 {
52   public:
53 
54     // this particular assignment of bits to types is crucial for
55     // canonical axis ordering
56     enum AxisType { Channels = 1,
57                     Space = 2,
58                     Angle = 4,
59                     Time = 8,
60                     Frequency = 16,
61                     Edge = 32,
62                     UnknownAxisType = 64,
63                     NonChannel = Space | Angle | Time | Frequency | UnknownAxisType,
64                     AllAxes = 2*UnknownAxisType-1 };
65 
AxisInfo(std::string key="?",AxisType typeFlags=UnknownAxisType,double resolution=0.0,std::string description="")66     AxisInfo(std::string key = "?", AxisType typeFlags = UnknownAxisType,
67              double resolution = 0.0, std::string description = "")
68     : key_(key),
69       description_(description),
70       resolution_(resolution),
71       flags_(typeFlags)
72     {}
73 
key() const74     std::string key() const
75     {
76         return key_;
77     }
78 
description() const79     std::string description() const
80     {
81         return description_;
82     }
83 
setDescription(std::string const & description)84     void setDescription(std::string const & description)
85     {
86         description_ = description;
87     }
88 
resolution() const89     double resolution() const
90     {
91         return resolution_;
92     }
93 
setResolution(double resolution)94     void setResolution(double resolution)
95     {
96         resolution_ = resolution;
97     }
98 
typeFlags() const99     AxisType typeFlags() const
100     {
101         return flags_ == 0
102                   ? UnknownAxisType
103                   : flags_;
104     }
105 
isUnknown() const106     bool isUnknown() const
107     {
108         return isType(UnknownAxisType);
109     }
110 
isSpatial() const111     bool isSpatial() const
112     {
113         return isType(Space);
114     }
115 
isTemporal() const116     bool isTemporal() const
117     {
118         return isType(Time);
119     }
120 
isChannel() const121     bool isChannel() const
122     {
123         return isType(Channels);
124     }
125 
isFrequency() const126     bool isFrequency() const
127     {
128         return isType(Frequency);
129     }
130 
isEdge() const131     bool isEdge() const
132     {
133         return isType(Edge);
134     }
135 
isAngular() const136     bool isAngular() const
137     {
138         return isType(Angle);
139     }
140 
isType(AxisType type) const141     bool isType(AxisType type) const
142     {
143         return (typeFlags() & type) != 0;
144     }
145 
repr() const146     std::string repr() const
147     {
148         std::string res("AxisInfo: '");
149         res += key_ + "' (type:";
150         if(isUnknown())
151         {
152             res += " none";
153         }
154         else
155         {
156             if(isChannel())
157                 res += " Channels";
158             if(isSpatial())
159                 res += " Space";
160             if(isTemporal())
161                 res += " Time";
162             if(isAngular())
163                 res += " Angle";
164             if(isFrequency())
165                 res += " Frequency";
166         }
167         if(resolution_ > 0.0)
168         {
169             res += ", resolution=";
170             res += asString(resolution_);
171         }
172         res += ")";
173         if(description_ != "")
174         {
175             res += " ";
176             res += description_;
177         }
178         return res;
179     }
180 
toFrequencyDomain(unsigned int size=0,int sign=1) const181     AxisInfo toFrequencyDomain(unsigned int size = 0, int sign = 1) const
182     {
183         AxisType type;
184         if(sign == 1)
185         {
186             vigra_precondition(!isFrequency(),
187                 "AxisInfo::toFrequencyDomain(): axis is already in the Fourier domain.");
188             type = AxisType(Frequency | flags_);
189         }
190         else
191         {
192             vigra_precondition(isFrequency(),
193                 "AxisInfo::fromFrequencyDomain(): axis is not in the Fourier domain.");
194             type = AxisType(~Frequency & flags_);
195         }
196         AxisInfo res(key(), type, 0.0, description_);
197         if(resolution_ > 0.0 && size > 0u)
198             res.resolution_ = 1.0 / (resolution_ * size);
199         return res;
200     }
201 
fromFrequencyDomain(unsigned int size=0) const202     AxisInfo fromFrequencyDomain(unsigned int size = 0) const
203     {
204         return toFrequencyDomain(size, -1);
205     }
206 
compatible(AxisInfo const & other) const207     bool compatible(AxisInfo const & other) const
208     {
209         return isUnknown() || other.isUnknown() ||
210                ((typeFlags() & ~Frequency) == (other.typeFlags() & ~Frequency) &&
211                  key() == other.key());
212     }
213 
operator ==(AxisInfo const & other) const214     bool operator==(AxisInfo const & other) const
215     {
216         return typeFlags() == other.typeFlags() && key() == other.key();
217     }
218 
operator !=(AxisInfo const & other) const219     bool operator!=(AxisInfo const & other) const
220     {
221         return !operator==(other);
222     }
223 
224     // the primary ordering is according to axis type:
225     //     Channels < Space < Angle < Time < Frequency < Unknown
226     // the secondary ordering is the lexicographic ordering of the keys
227     //     "x" < "y" < "z"
operator <(AxisInfo const & other) const228     bool operator<(AxisInfo const & other) const
229     {
230         return (typeFlags() < other.typeFlags()) ||
231                 (typeFlags() == other.typeFlags() && key() < other.key());
232     }
233 
operator <=(AxisInfo const & other) const234     bool operator<=(AxisInfo const & other) const
235     {
236         return !(other < *this);
237     }
238 
operator >(AxisInfo const & other) const239     bool operator>(AxisInfo const & other) const
240     {
241         return other < *this;
242     }
243 
operator >=(AxisInfo const & other) const244     bool operator>=(AxisInfo const & other) const
245     {
246         return !(*this < other);
247     }
248 
249     // factory functions for standard tags
x(double resolution=0.0,std::string const & description="")250     static AxisInfo x(double resolution = 0.0, std::string const & description = "")
251     {
252         return AxisInfo("x", Space, resolution, description);
253     }
254 
y(double resolution=0.0,std::string const & description="")255     static AxisInfo y(double resolution = 0.0, std::string const & description = "")
256     {
257         return AxisInfo("y", Space, resolution, description);
258     }
259 
z(double resolution=0.0,std::string const & description="")260     static AxisInfo z(double resolution = 0.0, std::string const & description = "")
261     {
262         return AxisInfo("z", Space, resolution, description);
263     }
264 
n(double resolution=0.0,std::string const & description="")265     static AxisInfo n(double resolution = 0.0, std::string const & description = "")
266     {
267         return AxisInfo("n", Space, resolution, description);
268     }
269 
e(double resolution=0.0,std::string const & description="")270     static AxisInfo e(double resolution = 0.0, std::string const & description = "")
271     {
272         return AxisInfo("e", Edge, resolution, description);
273     }
274 
t(double resolution=0.0,std::string const & description="")275     static AxisInfo t(double resolution = 0.0, std::string const & description = "")
276     {
277         return AxisInfo("t", Time, resolution, description);
278     }
279 
fx(double resolution=0.0,std::string const & description="")280     static AxisInfo fx(double resolution = 0.0, std::string const & description = "")
281     {
282         return AxisInfo("x", AxisType(Space | Frequency), resolution, description);
283     }
284 
fy(double resolution=0.0,std::string const & description="")285     static AxisInfo fy(double resolution = 0.0, std::string const & description = "")
286     {
287         return AxisInfo("y", AxisType(Space | Frequency), resolution, description);
288     }
289 
fz(double resolution=0.0,std::string const & description="")290     static AxisInfo fz(double resolution = 0.0, std::string const & description = "")
291     {
292         return AxisInfo("z", AxisType(Space | Frequency), resolution, description);
293     }
294 
ft(double resolution=0.0,std::string const & description="")295     static AxisInfo ft(double resolution = 0.0, std::string const & description = "")
296     {
297         return AxisInfo("t", AxisType(Time | Frequency), resolution, description);
298     }
299 
c(std::string const & description="")300     static AxisInfo c(std::string const & description = "")
301     {
302         return AxisInfo("c", Channels, 0.0, description);
303     }
304 
305     std::string key_, description_;
306     double resolution_;
307     AxisType flags_;
308 };
309 
310 class AxisTags
311 {
312   public:
313 
AxisTags()314     AxisTags()
315     {}
316 
AxisTags(int size)317     AxisTags(int size)
318     : axes_(size)
319     {}
320 
AxisTags(AxisInfo const & i1)321     AxisTags(AxisInfo const & i1)
322     {
323         push_back(i1);
324     }
325 
AxisTags(AxisInfo const & i1,AxisInfo const & i2)326     AxisTags(AxisInfo const & i1, AxisInfo const & i2)
327     {
328         push_back(i1);
329         push_back(i2);
330     }
331 
AxisTags(AxisInfo const & i1,AxisInfo const & i2,AxisInfo const & i3)332     AxisTags(AxisInfo const & i1, AxisInfo const & i2, AxisInfo const & i3)
333     {
334         push_back(i1);
335         push_back(i2);
336         push_back(i3);
337     }
338 
AxisTags(AxisInfo const & i1,AxisInfo const & i2,AxisInfo const & i3,AxisInfo const & i4)339     AxisTags(AxisInfo const & i1, AxisInfo const & i2,
340              AxisInfo const & i3, AxisInfo const & i4)
341     {
342         push_back(i1);
343         push_back(i2);
344         push_back(i3);
345         push_back(i4);
346     }
347 
AxisTags(AxisInfo const & i1,AxisInfo const & i2,AxisInfo const & i3,AxisInfo const & i4,AxisInfo const & i5)348     AxisTags(AxisInfo const & i1, AxisInfo const & i2,
349              AxisInfo const & i3, AxisInfo const & i4, AxisInfo const & i5)
350     {
351         push_back(i1);
352         push_back(i2);
353         push_back(i3);
354         push_back(i4);
355         push_back(i5);
356     }
357 
AxisTags(std::string const & tags)358     AxisTags(std::string const & tags)
359     {
360         for(std::string::size_type k=0; k<tags.size(); ++k)
361         {
362             switch(tags[k])
363             {
364               case 'x':
365                 push_back(AxisInfo::x());
366                 break;
367               case 'y':
368                 push_back(AxisInfo::y());
369                 break;
370               case 'z':
371                 push_back(AxisInfo::z());
372                 break;
373               case 't':
374                 push_back(AxisInfo::t());
375                 break;
376               case 'c':
377                 push_back(AxisInfo::c());
378                 break;
379               case 'f':
380                 ++k;
381                 vigra_precondition(k < tags.size(),
382                     "AxisTags(string): invalid input");
383                 switch(tags[k])
384                 {
385                   case 'x':
386                     push_back(AxisInfo::fx());
387                     break;
388                   case 'y':
389                     push_back(AxisInfo::fy());
390                     break;
391                   case 'z':
392                     push_back(AxisInfo::fz());
393                     break;
394                   case 't':
395                     push_back(AxisInfo::ft());
396                     break;
397                   default:
398                     vigra_precondition(false,
399                         "AxisTags(string): invalid input");
400                 }
401                 break;
402               default:
403                 vigra_precondition(false,
404                     "AxisTags(string): invalid input");
405             }
406         }
407     }
408 
409     // static AxisTags fromJSON(std::string const & repr);
410 
toJSON() const411     std::string toJSON() const
412     {
413         std::stringstream s;
414         s << "{\n  \"axes\": [";
415         for(unsigned int k=0; k<size(); ++k)
416         {
417             if(k > 0)
418                 s << ",";
419             s << "\n";
420             s << "    {\n";
421             s << "      \"key\": \"" << axes_[k].key() << "\",\n";
422             s << "      \"typeFlags\": " << (unsigned int)axes_[k].typeFlags() << ",\n";
423             s << "      \"resolution\": " << std::setprecision(17) << axes_[k].resolution() << ",\n";
424             s << "      \"description\": \"" << axes_[k].description() << "\"\n";
425             s << "    }";
426         }
427         s << "\n  ]\n}";
428         return s.str();
429     }
430 
size() const431     unsigned int size() const
432     {
433         return axes_.size();
434     }
435 
axisTypeCount(AxisInfo::AxisType type) const436     int axisTypeCount(AxisInfo::AxisType type) const
437     {
438         int res = 0;
439         for(unsigned int k=0; k<size(); ++k)
440             if(axes_[k].isType(type))
441                 ++res;
442         return res;
443     }
444 
repr() const445     std::string repr() const
446     {
447         std::string res;
448         if(size() > 0)
449             res += axes_[0].key();
450         for(unsigned int k=1; k<size(); ++k)
451         {
452             res += " ";
453             res += axes_[k].key();
454         }
455         return res;
456     }
457 
contains(std::string const & key) const458     bool contains(std::string const & key) const
459     {
460         return index(key) < (int)size();
461     }
462 
get(int k)463     AxisInfo & get(int k)
464     {
465         checkIndex(k);
466         if(k < 0)
467             k += size();
468         return axes_[k];
469     }
470 
get(std::string const & key)471     AxisInfo & get(std::string const & key)
472     {
473         return get(index(key));
474     }
475 
get(int k) const476     AxisInfo const & get(int k) const
477     {
478         checkIndex(k);
479         if(k < 0)
480             k += size();
481         return axes_[k];
482     }
483 
get(std::string const & key) const484     AxisInfo const & get(std::string const & key) const
485     {
486         return get(index(key));
487     }
488 
set(int k,AxisInfo const & info)489     void set(int k, AxisInfo const & info)
490     {
491         checkIndex(k);
492         if(k < 0)
493             k += size();
494         checkDuplicates(k, info);
495         axes_[k] = info;
496     }
497 
set(std::string const & key,AxisInfo const & info)498     void set(std::string const & key, AxisInfo const & info)
499     {
500         set(index(key), info);
501     }
502 
insert(int k,AxisInfo const & i)503     void insert(int k, AxisInfo const & i)
504     {
505         if(k == (int)size())
506         {
507             push_back(i);
508         }
509         else
510         {
511             checkIndex(k);
512             if(k < 0)
513                 k += size();
514             checkDuplicates(size(), i);
515             axes_.insert(axes_.begin()+k, i);
516         }
517     }
518 
push_back(AxisInfo const & i)519     void push_back(AxisInfo const & i)
520     {
521         checkDuplicates(size(), i);
522         axes_.push_back(i);
523     }
524 
dropAxis(int k)525     void dropAxis(int k)
526     {
527         checkIndex(k);
528         ArrayVector<AxisInfo>::iterator i = k < 0
529                                                  ? axes_.end() + k
530                                                  : axes_.begin() + k;
531         axes_.erase(i, i+1);
532     }
533 
dropAxis(std::string const & key)534     void dropAxis(std::string const & key)
535     {
536         dropAxis(index(key));
537     }
538 
dropChannelAxis()539     void dropChannelAxis()
540     {
541         int k = channelIndex();
542         if(k < (int)size())
543             axes_.erase(axes_.begin() + k, axes_.begin() + k + 1);
544     }
545 
index(std::string const & key) const546     int index(std::string const & key) const
547     {
548         for(unsigned int k=0; k<size(); ++k)
549             if(axes_[k].key() == key)
550                 return k;
551         return (int)size();
552     }
553 
resolution(int k) const554     double resolution(int k) const
555     {
556         return get(k).resolution_;
557     }
558 
resolution(std::string const & key) const559     double resolution(std::string const & key) const
560     {
561         return resolution(index(key));
562     }
563 
setResolution(int k,double r)564     void setResolution(int k, double r)
565     {
566         get(k).resolution_ = r;
567     }
568 
setResolution(std::string const & key,double r)569     void setResolution(std::string const & key, double r)
570     {
571         setResolution(index(key), r);
572     }
573 
scaleResolution(int k,double factor)574     void scaleResolution(int k, double factor)
575     {
576         get(k).resolution_ *= factor;
577     }
578 
scaleResolution(std::string const & key,double factor)579     void scaleResolution(std::string const & key, double factor)
580     {
581         get(key).resolution_ *= factor;
582     }
583 
description(int k) const584     std::string description(int k) const
585     {
586         return get(k).description_;
587     }
588 
description(std::string const & key) const589     std::string description(std::string const & key) const
590     {
591         return description(index(key));
592     }
593 
setDescription(int k,std::string const & d)594     void setDescription(int k, std::string const & d)
595     {
596         get(k).setDescription(d);
597     }
598 
setDescription(std::string const & key,std::string const & d)599     void setDescription(std::string const & key, std::string const & d)
600     {
601         setDescription(index(key), d);
602     }
603 
setChannelDescription(std::string const & description)604     void setChannelDescription(std::string const & description)
605     {
606         int k = channelIndex();
607         if(k < (int)size())
608             axes_[k].setDescription(description);
609     }
610 
toFrequencyDomain(int k,int size=0,int sign=1)611     void toFrequencyDomain(int k, int size = 0, int sign = 1)
612     {
613         get(k) = get(k).toFrequencyDomain(size, sign);
614     }
615 
toFrequencyDomain(std::string const & key,int size=0,int sign=1)616     void toFrequencyDomain(std::string const & key, int size = 0, int sign = 1)
617     {
618         toFrequencyDomain(index(key), size, sign);
619     }
620 
fromFrequencyDomain(int k,int size=0)621     void fromFrequencyDomain(int k, int size = 0)
622     {
623         toFrequencyDomain(k, size, -1);
624     }
625 
fromFrequencyDomain(std::string const & key,int size=0)626     void fromFrequencyDomain(std::string const & key, int size = 0)
627     {
628         toFrequencyDomain(key, size, -1);
629     }
630 
hasChannelAxis() const631     bool hasChannelAxis() const
632     {
633         return channelIndex() != (int)size();
634     }
635 
636     // FIXME: cache the results of these functions?
channelIndex() const637     int channelIndex() const
638     {
639         for(unsigned int k=0; k<size(); ++k)
640             if(axes_[k].isChannel())
641                 return k;
642         return (int)size();
643     }
644 
innerNonchannelIndex() const645     int innerNonchannelIndex() const
646     {
647         int k = 0;
648         for(; k<(int)size(); ++k)
649             if(!axes_[k].isChannel())
650                 break;
651         for(int i=k+1; i<(int)size(); ++i)
652         {
653             if(axes_[i].isChannel())
654                 continue;
655             if(axes_[i] < axes_[k])
656                 k = i;
657         }
658         return k;
659     }
660 
swapaxes(int i1,int i2)661     void swapaxes(int i1, int i2)
662     {
663         checkIndex(i1);
664         checkIndex(i2);
665         if(i1 < 0)
666             i1 += size();
667         if(i2 < 0)
668             i2 += size();
669         std::swap(axes_[i1], axes_[i2]);
670     }
671 
672     template <class T>
transpose(ArrayVector<T> const & permutation)673     void transpose(ArrayVector<T> const & permutation)
674     {
675         if(permutation.size() == 0)
676         {
677             transpose();
678         }
679         else
680         {
681             vigra_precondition(permutation.size() == size(),
682                 "AxisTags::transpose(): Permutation has wrong size.");
683             ArrayVector<AxisInfo> newAxes(size());
684             applyPermutation(permutation.begin(), permutation.end(), axes_.begin(), newAxes.begin());
685             axes_.swap(newAxes);
686         }
687     }
688 
transpose()689     void transpose()
690     {
691         std::reverse(axes_.begin(), axes_.end());
692     }
693 
694     template <class T>
695     void
permutationToNormalOrder(ArrayVector<T> & permutation) const696     permutationToNormalOrder(ArrayVector<T> & permutation) const
697     {
698         permutation.resize(size());
699         indexSort(axes_.begin(), axes_.end(), permutation.begin());
700     }
701 
702     template <class T>
703     void
permutationToNormalOrder(ArrayVector<T> & permutation,AxisInfo::AxisType types) const704     permutationToNormalOrder(ArrayVector<T> & permutation, AxisInfo::AxisType types) const
705     {
706         ArrayVector<AxisInfo> matchingAxes;
707         for(int k=0; k<(int)size(); ++k)
708             if(axes_[k].isType(types))
709                 matchingAxes.push_back(axes_[k]);
710         permutation.resize(matchingAxes.size());
711         indexSort(matchingAxes.begin(), matchingAxes.end(), permutation.begin());
712     }
713 
714     template <class T>
715     void
permutationFromNormalOrder(ArrayVector<T> & inverse_permutation) const716     permutationFromNormalOrder(ArrayVector<T> & inverse_permutation) const
717     {
718         ArrayVector<T> permutation;
719         permutationToNormalOrder(permutation);
720         inverse_permutation.resize(permutation.size());
721         indexSort(permutation.begin(), permutation.end(), inverse_permutation.begin());
722     }
723 
724     template <class T>
725     void
permutationFromNormalOrder(ArrayVector<T> & inverse_permutation,AxisInfo::AxisType types) const726     permutationFromNormalOrder(ArrayVector<T> & inverse_permutation, AxisInfo::AxisType types) const
727     {
728         ArrayVector<T> permutation;
729         permutationToNormalOrder(permutation, types);
730         inverse_permutation.resize(permutation.size());
731         indexSort(permutation.begin(), permutation.end(), inverse_permutation.begin());
732     }
733 
734     template <class T>
permutationToNumpyOrder(ArrayVector<T> & permutation) const735     void permutationToNumpyOrder(ArrayVector<T> & permutation) const
736     {
737         permutationToNormalOrder(permutation);
738         std::reverse(permutation.begin(), permutation.end());
739     }
740 
741     template <class T>
permutationFromNumpyOrder(ArrayVector<T> & inverse_permutation) const742     void permutationFromNumpyOrder(ArrayVector<T> & inverse_permutation) const
743     {
744         ArrayVector<T> permutation;
745         permutationToNumpyOrder(permutation);
746         inverse_permutation.resize(permutation.size());
747         indexSort(permutation.begin(), permutation.end(), inverse_permutation.begin());
748     }
749 
750     template <class T>
permutationToVigraOrder(ArrayVector<T> & permutation) const751     void permutationToVigraOrder(ArrayVector<T> & permutation) const
752     {
753         permutation.resize(size());
754         indexSort(axes_.begin(), axes_.end(), permutation.begin());
755         int channel = channelIndex();
756         if(channel < (int)size())
757         {
758             for(int k=1; k<(int)size(); ++k)
759                 permutation[k-1] = permutation[k];
760             permutation.back() = channel;
761         }
762     }
763 
764     template <class T>
permutationFromVigraOrder(ArrayVector<T> & inverse_permutation) const765     void permutationFromVigraOrder(ArrayVector<T> & inverse_permutation) const
766     {
767         ArrayVector<T> permutation;
768         permutationToVigraOrder(permutation);
769         inverse_permutation.resize(permutation.size());
770         indexSort(permutation.begin(), permutation.end(), inverse_permutation.begin());
771     }
772 
773     template <class T>
permutationToOrder(ArrayVector<T> & permutation,std::string const & order) const774     void permutationToOrder(ArrayVector<T> & permutation, std::string const & order) const
775     {
776         if(order == "A")
777         {
778             permutation.resize(size());
779             linearSequence(permutation.begin(), permutation.end());
780         }
781         else if(order == "C")
782         {
783             permutationToNumpyOrder(permutation);
784         }
785         else if(order == "F")
786         {
787             permutationToNormalOrder(permutation);
788         }
789         else if(order == "V")
790         {
791             permutationToVigraOrder(permutation);
792         }
793         else
794         {
795             vigra_precondition(false,
796                 "AxisTags::permutationToOrder(): unknown order '" + order + "'.");
797         }
798     }
799 
800 #if 0
801     ArrayVector<UInt32> matchOrdering(AxisTags const & other)
802     {
803         vigra_precondition(size() == other.size(),
804             "AxisTags::matchOrdering(): size mismatch.");
805 
806         ArrayVector<UInt32> permutation(size());
807         for(unsigned int k = 0; k<size(); ++k)
808         {
809             std::string key = other.get(k).key();
810             unsigned int l=0;
811             for(; l<size(); ++l)
812             {
813                 if(key == get(l).key())
814                     break;
815             }
816             vigra_precondition(l < size(),
817                 "AxisTags::matchOrdering(): key mismatch.");
818             permutation[k] = l;
819         }
820         return permutation;
821     }
822 #endif
823 
compatible(AxisTags const & other) const824     bool compatible(AxisTags const & other) const
825     {
826         if(size() == 0 || other.size() == 0)
827             return true;
828         if(size() != other.size())
829             return false;
830         for(unsigned int k=0; k<size(); ++k)
831             if(!axes_[k].compatible(other.axes_[k]))
832                 return false;
833         return true;
834     }
835 
operator ==(AxisTags const & other) const836     bool operator==(AxisTags const & other) const
837     {
838         if(size() != other.size())
839             return false;
840         return std::equal(axes_.begin(), axes_.end(), other.axes_.begin());
841     }
842 
operator !=(AxisTags const & other) const843     bool operator!=(AxisTags const & other) const
844     {
845         return !operator==(other);
846     }
847 
848   protected:
849 
checkIndex(int k) const850     void checkIndex(int k) const
851     {
852         vigra_precondition(k < (int)size() && k >= -(int)size(),
853             "AxisTags::checkIndex(): index out of range.");
854     }
855 
checkDuplicates(int i,AxisInfo const & info)856     void checkDuplicates(int i, AxisInfo const & info)
857     {
858         if(info.isChannel())
859         {
860             for(int k=0; k<(int)size(); ++k)
861             {
862                 vigra_precondition(k == i || !axes_[k].isChannel(),
863                      "AxisTags::checkDuplicates(): can only have one channel axis.");
864             }
865         }
866         else if(!info.isUnknown())
867         {
868             for(int k=0; k<(int)size(); ++k)
869             {
870                 vigra_precondition(k == i || axes_[k].key() != info.key(),
871                      std::string("AxisTags::checkDuplicates(): axis key '" +
872                                   info.key() + "' already exists."));
873             }
874         }
875     }
876 
877     ArrayVector<AxisInfo> axes_;
878 };
879 
880 // #if 0
881 // struct PyGetFunctor
882 // {
883     // AxisInfo const & operator()(python::object const & o) const
884     // {
885         // return python::extract<AxisInfo const &>(o)();
886     // }
887 // };
888 
889 // class PyAxisTags
890 // : public AxisTags<python::object, PyGetFunctor>
891 // {
892     // typedef AxisTags<python::object, PyGetFunctor> BaseType;
893   // public:
894     // PyAxisTags()
895     // {}
896 
897     // PyAxisTags(python::object i1, python::object i2,
898              // python::object i3, python::object i4, python::object i5)
899     // {
900         // if(PySequence_Check(i1.ptr()))
901         // {
902             // int size = len(i1);
903             // for(int k=0; k<size; ++k)
904                 // if(python::extract<AxisInfo const &>(i1[k]).check())
905                     // push_back(i1[k]);
906         // }
907         // else if(PyInt_Check(i1.ptr()))
908         // {
909             // int size = python::extract<int>(i1)();
910             // for(int k=0; k<size; ++k)
911                 // push_back(python::object(AxisInfo()));
912         // }
913         // else
914         // {
915             // if(python::extract<AxisInfo const &>(i1).check())
916                 // push_back(i1);
917             // if(python::extract<AxisInfo const &>(i2).check())
918                 // push_back(i2);
919             // if(python::extract<AxisInfo const &>(i3).check())
920                 // push_back(i3);
921             // if(python::extract<AxisInfo const &>(i4).check())
922                 // push_back(i4);
923             // if(python::extract<AxisInfo const &>(i5).check())
924                 // push_back(i5);
925         // }
926     // }
927 
928     // python::object getitem(int k)
929     // {
930         // if(!checkIndex(k))
931         // {
932             // PyErr_SetString(PyExc_IndexError, "AxisInfo::getitem(): Invalid index or key.");
933             // python::throw_error_already_set();
934         // }
935         // if(k < 0)
936             // k += this->size();
937         // return this->axes_[k];
938     // }
939 
940     // python::object getitem(std::string const & key)
941     // {
942         // return getitem(this->findKey(key));
943     // }
944 
945     // void setitem(int k, python::object i)
946     // {
947         // if(!this->checkIndex(k))
948         // {
949             // PyErr_SetString(PyExc_IndexError, "AxisInfo::setitem(): Invalid index or key.");
950             // python::throw_error_already_set();
951         // }
952         // if(!python::extract<AxisInfo const &>(i).check())
953         // {
954             // PyErr_SetString(PyExc_TypeError, "AxisInfo::setitem(): Item type must be AxisInfo.");
955             // python::throw_error_already_set();
956         // }
957 
958         // if(k < 0)
959             // k += this->size();
960         // this->axes_[k] = i;
961     // }
962 
963     // void setitem(std::string const & key, python::object i)
964     // {
965         // setitem(this->findKey(key), i);
966     // }
967 
968     // void append(python::object i)
969     // {
970         // insert(size(), i);
971     // }
972 
973     // void insert(int k, python::object i)
974     // {
975         // if(k < 0)
976             // k += this->size();
977         // if(k < 0)
978             // k = 0;
979         // if(k > (int)this->size())
980             // k = this->size();
981         // if(!python::extract<AxisInfo const &>(i).check())
982         // {
983             // PyErr_SetString(PyExc_TypeError, "AxisInfo::insert(): Item type must be AxisInfo.");
984             // python::throw_error_already_set();
985         // }
986         // this->axes_.insert(this->axes_.begin()+k, i);
987     // }
988 
989     // void insert(std::string const & key, python::object i)
990     // {
991         // insert(this->findKey(key), i);
992     // }
993 
994     // python::list axesByFlag(AxisType typeFlags) const
995     // {
996         // python::list res;
997         // for(unsigned int k=0; k<this->size(); ++k)
998             // if(this->get(k).typeFlags() == typeFlags)
999                 // res.append(k);
1000         // return res;
1001     // }
1002 
1003     // python::list spatialAxes() const
1004     // {
1005         // python::list res;
1006         // for(unsigned int k=0; k<this->size(); ++k)
1007             // if(this->get(k).isSpatial())
1008                 // res.append(k);
1009         // return res;
1010     // }
1011 
1012     // python::list temporalAxes() const
1013     // {
1014         // python::list res;
1015         // for(unsigned int k=0; k<this->size(); ++k)
1016             // if(this->get(k).isTemporal())
1017                 // res.append(k);
1018         // return res;
1019     // }
1020 
1021     // python::list channelAxes() const
1022     // {
1023         // python::list res;
1024         // for(unsigned int k=0; k<this->size(); ++k)
1025             // if(this->get(k).isChannel())
1026                 // res.append(k);
1027         // return res;
1028     // }
1029 
1030     // python::list frequencyAxes() const
1031     // {
1032         // python::list res;
1033         // for(unsigned int k=0; k<this->size(); ++k)
1034             // if(this->get(k).isFrequency())
1035                 // res.append(k);
1036         // return res;
1037     // }
1038 
1039     // python::list angularAxes() const
1040     // {
1041         // python::list res;
1042         // for(unsigned int k=0; k<this->size(); ++k)
1043             // if(this->get(k).isAngular())
1044                 // res.append(k);
1045         // return res;
1046     // }
1047 
1048     // python::list untaggedAxes() const
1049     // {
1050         // python::list res;
1051         // for(unsigned int k=0; k<this->size(); ++k)
1052             // if(this->get(k).isUnknown())
1053                 // res.append(k);
1054         // return res;
1055     // }
1056 
1057     // template <class U>
1058     // python::list vectorToPython(ArrayVector<U> const & v) const
1059     // {
1060         // python::list res;
1061         // for(unsigned int k=0; k<v.size(); ++k)
1062             // res.append(v[k]);
1063         // return res;
1064     // }
1065 
1066     // python::list canonicalOrdering()
1067     // {
1068         // return vectorToPython(BaseType::canonicalOrdering());
1069     // }
1070 
1071     // python::list matchOrdering(PyAxisTags const & other)
1072     // {
1073         // return vectorToPython(BaseType::matchOrdering(other));
1074     // }
1075 
1076     // void transpose(python::object const & o)
1077     // {
1078         // unsigned int osize = len(o);
1079         // ArrayVector<UInt32> permutation(osize);
1080 
1081         // for(unsigned int k=0; k<this->size(); ++k)
1082             // permutation[k] = python::extract<UInt32>(o[k])();
1083 
1084         // BaseType::transpose(permutation);
1085     // }
1086 
1087     // void transpose()
1088     // {
1089         // BaseType::transpose();
1090     // }
1091 // };
1092 
1093 // class TaggedShape
1094 // {
1095   // public:
1096 
1097     // ArrayVector<npy_intp> shape;
1098     // python_ptr axistags;
1099     // npy_intp channelCount;
1100     // std::string channelDescription;
1101 
1102     // TaggedShape(MultiArrayIndex size)
1103     // : shape(size)
1104     // {}
1105 
1106     // template <int N>
1107     // TaggedShape(typename MultiArrayShape<N>::type const & sh)
1108     // : shape(sh.begin(), sh.end())
1109     // {}
1110 
1111     // npy_intp & operator[](int i)
1112     // {
1113         // // rotate indices so that channels are located at index 0
1114         // return shape[(i+1) % shape.size()];
1115     // }
1116 
1117     // npy_intp operator[](int i) const
1118     // {
1119         // return shape[(i+1) % shape.size()];
1120     // }
1121 
1122     // unsigned int size() const
1123     // {
1124         // return shape.size();
1125     // }
1126 
1127     // // void setChannelDescription(std::string const & description)
1128     // // {
1129         // // if(axistags)
1130         // // {
1131             // // python_ptr func(PyString_FromString("setChannelDescription"),
1132                                          // // python_ptr::keep_count);
1133             // // pythonToCppException(res);
1134 
1135             // // python_ptr d(PyString_FromString(d.c_str()), python_ptr::keep_count);
1136             // // pythonToCppException(d);
1137 
1138             // // python_ptr res(PyObject_CallMethodObjArgs(axistags, func, d.get(), NULL),
1139                            // // python_ptr::keep_count);
1140             // // pythonToCppException(res);
1141         // // }
1142     // // }
1143 
1144     // // void setChannelCount(int channelCount)
1145     // // {
1146         // // shape[0] = channelCount;
1147     // // }
1148 
1149     // void setChannelDescription(std::string const & description)
1150     // {
1151         // channelDescription = description;
1152     // }
1153 
1154     // void setChannelCount(int count)
1155     // {
1156         // channelCount = count;
1157     // }
1158 
1159     // void setChannelConfig(int channelCount, std::string const & description)
1160     // {
1161         // setChannelCount(channelCount);
1162         // setChannelDescription(description);
1163     // }
1164 // };
1165 // #endif
1166 
1167 } // namespace vigra
1168 
1169 #endif /* VIGRA_AXISTAGS_HXX */
1170