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 #define PY_ARRAY_UNIQUE_SYMBOL vigranumpycore_PyArray_API
37 #define NO_IMPORT_ARRAY
38 
39 #include <typeinfo>
40 #include <vigra/numpy_array.hxx>
41 #include <vigra/axistags.hxx>
42 #include <boost/python.hpp>
43 #include <boost/python/slice.hpp>
44 
45 // disable axistag construction from json for the moment,
46 // because these libraries require boost 1.41, whereas
47 // our Ubuntu longterm support has only boost 1.40
48 //
49 // as a workaround, we will do this in Python using the 'json' module
50 #define VIGRA_DISABLE_FROMJSON
51 
52 #ifndef VIGRA_DISABLE_FROMJSON
53 #include <boost/property_tree/ptree.hpp>
54 #include <boost/property_tree/json_parser.hpp>
55 #endif
56 
57 namespace python = boost::python;
58 
59 namespace vigra {
60 
61 template<class T>
managingPyObject(T * p)62 inline PyObject * managingPyObject(T *p)
63 {
64     return typename python::manage_new_object::apply<T *>::type()(p);
65 }
66 
67 template<class Copyable>
68 python::object
generic__copy__(python::object copyable)69 generic__copy__(python::object copyable)
70 {
71     Copyable* newCopyable(new Copyable(python::extract<const Copyable &>(copyable)()));
72     python::object result(python::detail::new_reference(managingPyObject(newCopyable)));
73 
74     python::extract<python::dict>(result.attr("__dict__"))().update(copyable.attr("__dict__"));
75 
76     return result;
77 }
78 
79 template<class Copyable>
80 python::object
generic__deepcopy__(python::object copyable,python::dict memo)81 generic__deepcopy__(python::object copyable, python::dict memo)
82 {
83     python::object copyMod = python::import("copy");
84     python::object deepcopy = copyMod.attr("deepcopy");
85 #if PY_MAJOR_VERSION < 3
86     python::object builtin = python::import("__builtin__");
87 #else
88     python::object builtin = python::import("builtins");
89 #endif
90     python::object globals = builtin.attr("__dict__");
91 
92     Copyable* newCopyable(new Copyable(python::extract<const Copyable &>(copyable)()));
93     python::object result(python::detail::new_reference(managingPyObject(newCopyable)));
94 
95     python::dict locals;
96     locals["copyable"] = copyable;
97     size_t copyableId = python::extract<size_t>(python::eval("id(copyable)", globals, locals))();
98     memo[copyableId] = result;
99 
100     python::object dict_copy = deepcopy(python::extract<python::dict>(copyable.attr("__dict__"))(),
101                                         memo);
102     python::extract<python::dict>(result.attr("__dict__"))().update(dict_copy);
103     return result;
104 }
105 
AxisInfo__call__(AxisInfo const & i,double resolution,std::string const & description)106 AxisInfo AxisInfo__call__(AxisInfo const & i, double resolution, std::string const & description)
107 {
108     return AxisInfo(i.key(), i.typeFlags(), resolution, description);
109 }
110 
AxisInfo_x()111 AxisInfo AxisInfo_x()
112 {
113     return AxisInfo::x();
114 }
115 
AxisInfo_y()116 AxisInfo AxisInfo_y()
117 {
118     return AxisInfo::y();
119 }
120 
AxisInfo_z()121 AxisInfo AxisInfo_z()
122 {
123     return AxisInfo::z();
124 }
125 
AxisInfo_n()126 AxisInfo AxisInfo_n()
127 {
128     return AxisInfo::n();
129 }
130 
AxisInfo_e()131 AxisInfo AxisInfo_e()
132 {
133     return AxisInfo::e();
134 }
135 
AxisInfo_t()136 AxisInfo AxisInfo_t()
137 {
138     return AxisInfo::t();
139 }
140 
AxisInfo_fx()141 AxisInfo AxisInfo_fx()
142 {
143     return AxisInfo::fx();
144 }
145 
AxisInfo_fy()146 AxisInfo AxisInfo_fy()
147 {
148     return AxisInfo::fy();
149 }
150 
AxisInfo_fz()151 AxisInfo AxisInfo_fz()
152 {
153     return AxisInfo::fz();
154 }
155 
AxisInfo_ft()156 AxisInfo AxisInfo_ft()
157 {
158     return AxisInfo::ft();
159 }
160 
AxisInfo_c()161 AxisInfo AxisInfo_c()
162 {
163     return AxisInfo::c();
164 }
165 
166 #ifndef VIGRA_DISABLE_FROMJSON
fromJSON(std::string const & repr)167 AxisTags AxisTags::fromJSON(std::string const & repr)
168 {
169     using boost::property_tree::ptree;
170 
171     std::istringstream s(repr);
172     ptree pt;
173     read_json(s, pt);
174 
175     AxisTags res;
176     for(ptree::iterator v = pt.get_child("axes").begin();
177                          v != pt.get_child("axes").end(); ++v)
178     {
179         std::string key(v->second.get<std::string>("key"));
180         unsigned int typeFlags(v->second.get<unsigned int>("typeFlags"));
181         double resolution(v->second.get<double>("resolution"));
182         std::string description(v->second.get<std::string>("description"));
183 
184         res.push_back(AxisInfo(key, (AxisInfo::AxisType)typeFlags, resolution, description));
185     }
186     return res;
187 }
188 
AxisTags_readJSON(std::string const & repr)189 AxisTags * AxisTags_readJSON(std::string const & repr)
190 {
191     return new AxisTags(AxisTags::fromJSON(repr));
192 }
193 #endif
194 
195 AxisTags *
AxisTags_create(python::object i1,python::object i2,python::object i3,python::object i4,python::object i5)196 AxisTags_create(python::object i1, python::object i2,
197                 python::object i3, python::object i4, python::object i5)
198 {
199     VIGRA_UNIQUE_PTR<AxisTags> res(new AxisTags());
200 
201     python::extract<AxisTags const &> tags(i1);
202     if(tags.check())
203     {
204         res = VIGRA_UNIQUE_PTR<AxisTags>(new AxisTags(tags()));
205     }
206 #if PY_MAJOR_VERSION < 3
207     else if(PyString_Check(i1.ptr()))
208 #else
209     else if (PyUnicode_Check(i1.ptr()))
210 #endif
211     {
212         res = VIGRA_UNIQUE_PTR<AxisTags>(new AxisTags(python::extract<std::string>(i1)()));
213     }
214     else if(PySequence_Check(i1.ptr()))
215     {
216         int size = len(i1);
217         for(int k=0; k<size; ++k)
218         {
219             python::extract<AxisInfo const &> info(i1[k]);
220             if(!info.check())
221             {
222                 PyErr_SetString(PyExc_TypeError, "AxisTags(): Argument must be a sequence of AxisInfo objects.");
223                 python::throw_error_already_set();
224             }
225             res->push_back(info());
226         }
227     }
228 #if PY_MAJOR_VERSION < 3
229     else if(PyInt_Check(i1.ptr()))
230 #else
231     else if (PyLong_Check(i1.ptr()))
232 #endif
233     {
234         int size = python::extract<int>(i1)();
235         for(int k=0; k<size; ++k)
236             res->push_back(AxisInfo());
237     }
238     else
239     {
240         if(i1 != python::object())
241         {
242             python::extract<AxisInfo const &> info(i1);
243             if(!info.check())
244             {
245                 PyErr_SetString(PyExc_TypeError, "AxisTags(): Argument must be a sequence of AxisInfo objects.");
246                 python::throw_error_already_set();
247             }
248             res->push_back(info());
249         }
250         if(i2 != python::object())
251         {
252             python::extract<AxisInfo const &> info(i2);
253             if(!info.check())
254             {
255                 PyErr_SetString(PyExc_TypeError, "AxisTags(): Argument must be a sequence of AxisInfo objects.");
256                 python::throw_error_already_set();
257             }
258             res->push_back(info());
259         }
260         if(i3 != python::object())
261         {
262             python::extract<AxisInfo const &> info(i3);
263             if(!info.check())
264             {
265                 PyErr_SetString(PyExc_TypeError, "AxisTags(): Argument must be a sequence of AxisInfo objects.");
266                 python::throw_error_already_set();
267             }
268             res->push_back(info());
269         }
270         if(i4 != python::object())
271         {
272             python::extract<AxisInfo const &> info(i4);
273             if(!info.check())
274             {
275                 PyErr_SetString(PyExc_TypeError, "AxisTags(): Argument must be a sequence of AxisInfo objects.");
276                 python::throw_error_already_set();
277             }
278             res->push_back(info());
279         }
280         if(i5 != python::object())
281         {
282             python::extract<AxisInfo const &> info(i5);
283             if(!info.check())
284             {
285                 PyErr_SetString(PyExc_TypeError, "AxisTags(): Argument must be a sequence of AxisInfo objects.");
286                 python::throw_error_already_set();
287             }
288             res->push_back(info());
289         }
290     }
291 
292     return res.release();
293 }
294 
AxisTags_insertChannelAxis(AxisTags & axistags)295 void AxisTags_insertChannelAxis(AxisTags & axistags)
296 {
297     int k = axistags.channelIndex();
298     vigra_precondition(k == (int)axistags.size(),
299          "AxisTags::insertChannelAxis(): already has a channel axis.");
300     if(detail::defaultOrder() == "F")
301         axistags.insert(0, AxisInfo::c());
302     else
303         axistags.push_back(AxisInfo::c());
304 }
305 
AxisTags_getitem(AxisTags & axistags,int index)306 AxisInfo & AxisTags_getitem(AxisTags & axistags, int index)
307 {
308     if(index < 0)
309         index += axistags.size();
310 
311     if(index >= (int)axistags.size())
312     {
313         PyErr_SetString(PyExc_IndexError, "AxisTags.__getitem__(): Invalid index or key.");
314         python::throw_error_already_set();
315     }
316 
317     return axistags.get(index);
318 }
319 
AxisTags_str(AxisTags const & axistags)320 std::string AxisTags_str(AxisTags const & axistags)
321 {
322     std::string res;
323     for(unsigned int k=0; k<axistags.size(); ++k)
324         res += axistags.get(k).repr() + "\n";
325     return res;
326 }
327 
AxisTags_keys(AxisTags const & axistags)328 python::list AxisTags_keys(AxisTags const & axistags)
329 {
330     python::list res;
331     for(unsigned int k=0; k<axistags.size(); ++k)
332         res.append(axistags.get(k).key());
333     return res;
334 }
335 
AxisTags_values(AxisTags const & axistags)336 python::list AxisTags_values(AxisTags const & axistags)
337 {
338     python::list res;
339     for(unsigned int k=0; k<axistags.size(); ++k)
340         res.append(axistags.get(k));
341     return res;
342 }
343 
AxisTags_contains(AxisTags const & axistags,AxisInfo const & axisinfo)344 bool AxisTags_contains(AxisTags const & axistags, AxisInfo const & axisinfo)
345 {
346     return axistags.contains(axisinfo.key());
347 }
348 
349 python::object
AxisTags_permutationToNormalOrder(AxisTags const & axistags)350 AxisTags_permutationToNormalOrder(AxisTags const & axistags)
351 {
352     ArrayVector<npy_intp> permutation;
353     axistags.permutationToNormalOrder(permutation);
354     return python::object(permutation);
355 }
356 
357 python::object
AxisTags_permutationToNormalOrder2(AxisTags const & axistags,unsigned int types)358 AxisTags_permutationToNormalOrder2(AxisTags const & axistags, unsigned int types)
359 {
360     ArrayVector<npy_intp> permutation;
361     axistags.permutationToNormalOrder(permutation, (AxisInfo::AxisType)types);
362     return python::object(permutation);
363 }
364 
365 python::object
AxisTags_permutationFromNormalOrder(AxisTags const & axistags)366 AxisTags_permutationFromNormalOrder(AxisTags const & axistags)
367 {
368     ArrayVector<npy_intp> permutation;
369     axistags.permutationFromNormalOrder(permutation);
370     return python::object(permutation);
371 }
372 
373 python::object
AxisTags_permutationFromNormalOrder2(AxisTags const & axistags,unsigned int types)374 AxisTags_permutationFromNormalOrder2(AxisTags const & axistags, unsigned int types)
375 {
376     ArrayVector<npy_intp> permutation;
377     axistags.permutationFromNormalOrder(permutation, (AxisInfo::AxisType)types);
378     return python::object(permutation);
379 }
380 
381 python::object
AxisTags_permutationToNumpyOrder(AxisTags const & axistags)382 AxisTags_permutationToNumpyOrder(AxisTags const & axistags)
383 {
384     ArrayVector<npy_intp> permutation;
385     axistags.permutationToNumpyOrder(permutation);
386     return python::object(permutation);
387 }
388 
389 python::object
AxisTags_permutationFromNumpyOrder(AxisTags const & axistags)390 AxisTags_permutationFromNumpyOrder(AxisTags const & axistags)
391 {
392     ArrayVector<npy_intp> permutation;
393     axistags.permutationFromNumpyOrder(permutation);
394     return python::object(permutation);
395 }
396 
397 python::object
AxisTags_permutationToVigraOrder(AxisTags const & axistags)398 AxisTags_permutationToVigraOrder(AxisTags const & axistags)
399 {
400     ArrayVector<npy_intp> permutation;
401     axistags.permutationToVigraOrder(permutation);
402     return python::object(permutation);
403 }
404 
405 python::object
AxisTags_permutationFromVigraOrder(AxisTags const & axistags)406 AxisTags_permutationFromVigraOrder(AxisTags const & axistags)
407 {
408     ArrayVector<npy_intp> permutation;
409     axistags.permutationFromVigraOrder(permutation);
410     return python::object(permutation);
411 }
412 
413 python::object
AxisTags_permutationToOrder(AxisTags const & axistags,std::string const & order)414 AxisTags_permutationToOrder(AxisTags const & axistags, std::string const & order)
415 {
416     ArrayVector<npy_intp> permutation;
417     axistags.permutationToOrder(permutation, order);
418     return python::object(permutation);
419 }
420 
421 
422 
423 AxisTags *
AxisTags_transform(AxisTags const & oldTags,python::object index,int lnew)424 AxisTags_transform(AxisTags const & oldTags, python::object index, int lnew)
425 {
426     VIGRA_UNIQUE_PTR<AxisTags> newTags(new AxisTags());
427     python::object ellipsis = python::object(python::detail::borrowed_reference(Py_Ellipsis));
428     int lold = oldTags.size();
429     if(!PySequence_Check(index.ptr()))
430     {
431         index = python::make_tuple(index);
432     }
433     int lindex = len(index);
434     int lnewaxis = 0, lellipsis = 0;
435     for(int k=0; k<lindex; ++k)
436     {
437         python::object item(index[k]);
438         if(item == python::object() || python::extract<AxisInfo const &>(item).check())
439             ++lnewaxis;
440         else if(item == ellipsis)
441             ++lellipsis;
442     }
443     lindex -= lnewaxis;
444     if(lindex < lold && lellipsis == 0)
445     {
446         index += python::make_tuple(ellipsis);
447         ++lindex;
448     }
449     lellipsis = lold - lindex;
450     int knew = 0, kold = 0, kindex = 0;
451     while(knew < lnew)
452     {
453         python::object item = index[kindex];
454 #if PY_MAJOR_VERSION < 3
455         if(PyInt_Check(item.ptr()))
456 #else
457         if(PyLong_Check(item.ptr()))
458 #endif
459         {
460             ++kold;
461             ++kindex;
462         }
463         else
464         {
465             if(item != python::object())
466             {
467                 python::extract<AxisInfo const &> newaxis(item);
468 
469                 if(newaxis.check())
470                 {
471                     newTags->push_back(newaxis);
472                 }
473                 else
474                 {
475                     newTags->push_back(oldTags.get(kold));
476                     // adjust the resolution if item has a valid 'step' member
477                     python::extract<python::slice> slice(item);
478                     if(slice.check())
479                     {
480                         python::extract<int> step(slice().step());
481                         if(step.check())
482                         {
483                             newTags->get(knew).resolution_ *= step();
484                         }
485                     }
486                     ++kold;
487                 }
488             }
489             else
490             {
491                 newTags->push_back(AxisInfo());
492             }
493             ++knew;
494             if(lellipsis > 0 && item == ellipsis)
495                 --lellipsis;
496             else
497                 ++kindex;
498         }
499     }
500     return newTags.release();
501 }
502 
503 // #if 0
504 // void printAxistags(NumpyAnyArray a)
505 // {
506     // python::object array(python::detail::borrowed_reference(a.pyObject()));
507     // python::object tags(getattr(array, "axistags", PyAxisTags()));
508     // std::cerr << "Axistags via boost::python:\n";
509     // std::cerr << python::extract<PyAxisTags const &>(tags)().repr();
510 
511     // std::cerr << "Axistags via C-API:\n";
512     // if(PyObject_HasAttrString(a.pyObject(), "axistags"))
513     // {
514         // python::object tags(python::detail::new_reference(PyObject_GetAttrString(a.pyObject(), "axistags")));
515         // std::cerr << python::extract<PyAxisTags const &>(tags)().repr();
516     // }
517     // else
518     // {
519         // std::cerr << "attribute 'axistags' missing\n";
520     // }
521 // }
522 // #endif
523 
defineAxisTags()524 void defineAxisTags()
525 {
526     using namespace boost::python;
527 
528     docstring_options doc_options(true, false, false);
529 
530     enum_<AxisInfo::AxisType>("AxisType",
531          "\nEnum to encode the type of an axis described by an\n"
532          ":class:`~vigra.AxisInfo` object. Possible values:\n\n"
533          "   ``AxisType.Channels:``\n      a channel axis\n"
534          "   ``AxisType.Space:``\n      a spatial axis\n"
535          "   ``AxisType.Edge:``\n      an edge axis\n"
536          "   ``AxisType.Angle:``\n      an axis encoding angles (e.g. polar coordinates)\n"
537          "   ``AxisType.Time:``\n      a temporal axis\n"
538          "   ``AxisType.Frequency:``\n      an axis in the Fourier domain\n"
539          "   ``AxisType.UnknownAxisType:``\n      type not specified\n"
540          "   ``AxisType.NonChannel:``\n      any type except Channels\n"
541          "   ``AxisType.AllAxes:``\n      any type\n\n"
542          "Types can be combined by the bitwise 'or' operator. For example,\n"
543          "``Space | Frequency`` denotes a spatial axis in the Fourier domain.\n\n")
544         .value("UnknownAxisType", AxisInfo::UnknownAxisType)
545         .value("Channels", AxisInfo::Channels)
546         .value("Space", AxisInfo::Space)
547         .value("Edge", AxisInfo::Edge)
548         .value("Angle", AxisInfo::Angle)
549         .value("Time", AxisInfo::Time)
550         .value("Frequency", AxisInfo::Frequency)
551         .value("NonChannel", AxisInfo::NonChannel)
552         .value("AllAxes", AxisInfo::AllAxes)
553     ;
554 
555     class_<AxisInfo>("AxisInfo",
556          "\n"
557          "An object describing a single axis.\n\nConstructor:\n\n"
558          ".. method:: AxisInfo(key='?', typeFlags=AxisType.UnknownAxisType, resolution=0.0, description='')\n\n"
559          "    :param key: the key of the axis,\n"
560          "                e.g. 'x' (x-axis), 'c' (channel axis), '?' (unknown)\n"
561          "    :type key: string\n"
562          "    :param typeFlags: the type of the axis,\n"
563          "                      e.g. AxisType.Space or AxisType.Time\n"
564          "    :type typeFlags: :class:`~vigra.AxisType`\n"
565          "    :param resolution: the resolution (step size) of the axis\n"
566          "                       (e.g. 0.0 means 'unknown')\n"
567          "    :param description: an arbitrary string giving additional information \n"
568          "                        about the axis.\n\n"
569          "In addition, AxisInfo defines the following factories for the most common\n"
570          "cases:\n\n"
571          "   ``AxisInfo.c`` or ``AxisInfo.c(description='a description')``:\n"
572          "        Factory for an axisinfo object describing the 'c' (channel) axis.\n"
573          "   ``AxisInfo.x`` or ``AxisInfo.x(resolution=0.0, description='')``:\n"
574          "        Factory for an axisinfo object describing the 'x' (spatial) axis.\n"
575          "   ``AxisInfo.y`` or ``AxisInfo.y(resolution=0.0, description='')``:\n"
576          "        Factory for an axisinfo object describing the 'y' (spatial) axis.\n"
577          "   ``AxisInfo.z`` or ``AxisInfo.z(resolution=0.0, description='')``:\n"
578          "        Factory for an axisinfo object describing the 'z' (spatial) axis.\n"
579          "   ``AxisInfo.z`` or ``AxisInfo.n(resolution=0.0, description='')``:\n"
580          "        Factory for an axisinfo object describing the 'n' (spatial) axis.\n"
581          "   ``AxisInfo.e`` or ``AxisInfo.e(resolution=0.0, description='')``:\n"
582          "        Factory for an axisinfo object describing the 'e' (edge) axis.\n"
583          "   ``AxisInfo.t`` or ``AxisInfo.t(resolution=0.0, description='')``:\n"
584          "        Factory for an axisinfo object describing the 't' (time) axis.\n"
585          "   ``AxisInfo.fx`` or ``AxisInfo.fx(resolution=0.0, description='')``:\n"
586          "        Factory for an axisinfo object describing the 'x' axis\n"
587          "        in the Fourier domain.\n"
588          "   ``AxisInfo.fy`` or ``AxisInfo.fy(resolution=0.0, description='')``:\n"
589          "        Factory for an axisinfo object describing the 'y' axis\n"
590          "        in the Fourier domain.\n"
591          "   ``AxisInfo.fz`` or ``AxisInfo.fz(resolution=0.0, description='')``:\n"
592          "        Factory for an axisinfo object describing the 'z' axis\n"
593          "        in the Fourier domain.\n"
594          "   ``AxisInfo.ft`` or ``AxisInfo.ft(resolution=0.0, description='')``:\n"
595          "        Factory for an axisinfo object describing the 't' axis\n"
596          "        in the Fourier domain.\n\n",
597          no_init)
598         .def(init<std::string, AxisInfo::AxisType, double, std::string>(
599              (arg("key")="?", arg("typeFlags")=AxisInfo::UnknownAxisType,
600               arg("resolution")=0.0, arg("description")="")))
601         .def(init<AxisInfo const &>())
602         .def_readonly("key", &AxisInfo::key_,
603              "\n(read-only property, type 'string') the key of the axis.\n")
604         .def_readwrite("description", &AxisInfo::description_,
605              "\n(read/write property, type 'string') the string description of the axis.\n")
606         .def_readwrite("resolution", &AxisInfo::resolution_,
607              "\n(read/write property, type 'float') the resolution of the axis. The resolution\n"
608              "will be automatically adjusted whenever the image size changes, e.g. due to a call\n"
609              "to :func:`~vigra.sampling.resize` or slicing with non-unit step size::\n\n"
610              "    >>> a = vigra.RGBImage((200,100))\n"
611              "    >>> a.axistags['x'].resolution = 1.0\n"
612              "    >>> a.axistags['y'].resolution = 1.2\n"
613              "    >>> print(a.axistags)\n"
614              "    AxisInfo: 'x' (type: Space, resolution=1)\n"
615              "    AxisInfo: 'y' (type: Space, resolution=1.2)\n"
616              "    AxisInfo: 'c' (type: Channels) RGB\n"
617              "    >>> b = a[::2, ::4, :]\n"
618              "    >>> print(b.axistags)\n"
619              "    AxisInfo: 'x' (type: Space, resolution=2)\n"
620              "    AxisInfo: 'y' (type: Space, resolution=4.8)\n"
621              "    AxisInfo: 'c' (type: Channels) RGB\n\n")
622         .def_readonly("typeFlags", &AxisInfo::flags_,
623              "\n(read-only property, type :class:`~vigra.AxisType`) the type of the axis .\n")
624         .def("toFrequencyDomain", &AxisInfo::toFrequencyDomain, (arg("size") = 0, arg("sign") = 1))
625         .def("fromFrequencyDomain", &AxisInfo::fromFrequencyDomain, (arg("size") = 0))
626         .def("isSpatial", &AxisInfo::isSpatial,
627              "\naxisinfo.isSpactial() yields True when :attr:`~vigra.AxisInfo.typeFlags` "
628              "contains AxisType.Space\n")
629         .def("isEdge", &AxisInfo::isEdge,
630              "\naxisinfo.isEdge() yields True when :attr:`~vigra.AxisInfo.typeFlags` "
631              "contains AxisType.Edge\n")
632         .def("isTemporal", &AxisInfo::isTemporal,
633              "\naxisinfo.isTemporal() yields True when :attr:`~vigra.AxisInfo.typeFlags` "
634              "contains AxisType.Time\n")
635         .def("isChannel", &AxisInfo::isChannel,
636              "\naxisinfo.isChannel() yields True when :attr:`~vigra.AxisInfo.typeFlags` "
637              "contains AxisType.Channels\n")
638         .def("isFrequency", &AxisInfo::isFrequency,
639              "\naxisinfo.isFrequency() yields True when :attr:`~vigra.AxisInfo.typeFlags` "
640              "contains AxisType.Frequency\n")
641         .def("isAngular", &AxisInfo::isAngular,
642              "\naxisinfo.isAngular() yields True when :attr:`~vigra.AxisInfo.typeFlags` "
643              "contains AxisType.Angle\n")
644         .def("isType", &AxisInfo::isType,
645              "\naxisinfo.isType(axistype) yields True when :attr:`~vigra.AxisInfo.typeFlags` "
646              "contains the given axistype.\n")
647         .def("compatible", &AxisInfo::compatible,
648              "\naxisinfo1.compatible(axisinfo2) yields True when the two axisinfo objects "
649              "have the same keys and types, or either of the two is 'unknown'.\n")
650         .def(self == self)
651         .def(self != self)
652         .def(self < self)
653         .def(self <= self)
654         .def(self > self)
655         .def(self >= self)
656         .def("__copy__", &generic__copy__<AxisInfo>)
657         .def("__deepcopy__", &generic__deepcopy__<AxisInfo>)
658         .def("__repr__", &AxisInfo::repr)
659         .def("__call__", &AxisInfo__call__, (arg("resolution") = 0.0, arg("description") = ""))
660         .add_static_property("x", &AxisInfo_x)
661         .add_static_property("y", &AxisInfo_y)
662         .add_static_property("z", &AxisInfo_z)
663         .add_static_property("n", &AxisInfo_n)
664         .add_static_property("e", &AxisInfo_e)
665         .add_static_property("t", &AxisInfo_t)
666         .add_static_property("fx", &AxisInfo_fx)
667         .add_static_property("fy", &AxisInfo_fy)
668         .add_static_property("fz", &AxisInfo_fz)
669         .add_static_property("ft", &AxisInfo_ft)
670         .add_static_property("c", &AxisInfo_c)
671     ;
672 
673     class_<AxisTags >("AxisTags",
674             "Object to describe axis properties and axis ordering in a "
675             ":class:`~vigra.VigraArray`. \n\nConstructor:\n\n"
676             ".. method:: AxisTags(i1=None, i2=None, i3=None, i4=None, i5=None)\n\n"
677             "    The parameters 'i1'...'i5' are the :class:`~vigra.AxisInfo` objects\n"
678             "    describing the axes. If all are None, an empty AxisTags object is\n"
679             "    created. Alternatively, 'i1' can also be a Python sequence of\n"
680             "    :class:`~vigra.AxisInfo` objects, or an integer (in which case an\n"
681             "    AxisTags object with that many '?' entries is created).\n\n"
682             "Most AxisTags methods should not be called directly, but via the\n"
683             "corresponding array methods, because this ensures that arrays and\n"
684             "their axistags are always kept in sync (rule of thumb: if an axistags\n"
685             "function is not documented, you call it on your own risk).\n\n"
686             "The entries of an axistags object (i.e. the individual axisinfo objects)\n"
687             "can be accessed via the index operator, where the argument can either be\n"
688             "the axis index or the axis key::\n\n"
689             "    >>> print(array.axistags[0])\n"
690             "    AxisInfo: 'x' (type: Space, resolution=1.2)\n"
691             "    >>> print(array.axistags['x'])\n"
692             "    AxisInfo: 'x' (type: Space, resolution=1.2)\n"
693             "    >>> array.axistags['x'].resolution = 2.0\n"
694             "    >>> print(array.axistags['x'])\n"
695             "    AxisInfo: 'x' (type: Space, resolution=2)\n\n",
696             no_init)
697         .def("__init__", make_constructor(&AxisTags_create,
698             default_call_policies(),
699             (arg("i1")=object(), arg("i2")=object(), arg("i3")=object(),
700              arg("i4")=object(), arg("i5")=object())))
701         .def("__repr__", &AxisTags::repr)
702         .def("__str__", &AxisTags_str)
703         .def("__copy__", &generic__copy__<AxisTags>)
704         .def("__deepcopy__", &generic__deepcopy__<AxisTags>)
705         .def("__len__", &AxisTags::size)
706         .def("__getitem__", &AxisTags_getitem, return_internal_reference<>())
707         .def("__getitem__",
708             (AxisInfo & (AxisTags::*)(std::string const &))&AxisTags::get, return_internal_reference<>())
709         .def("__setitem__", (void (AxisTags::*)(int, AxisInfo const &))&AxisTags::set)
710         .def("__setitem__",
711             (void (AxisTags::*)(std::string const &, AxisInfo const &))&AxisTags::set)
712         .def("__delitem__", (void (AxisTags::*)(int))&AxisTags::dropAxis)
713         .def("__delitem__", (void (AxisTags::*)(std::string const &))&AxisTags::dropAxis)
714         .def("__contains__", &AxisTags_contains)
715         .def("__contains__", &AxisTags::contains)
716         .def("insert", &AxisTags::insert)
717         .def("append", &AxisTags::push_back)
718         .def("dropChannelAxis", &AxisTags::dropChannelAxis)
719         .def("insertChannelAxis", &AxisTags_insertChannelAxis)
720         .def("swapaxes", &AxisTags::swapaxes)
721         .def("keys", &AxisTags_keys)
722         .def("values", &AxisTags_values)
723 
724         // NOTE: in contrast to arrays, AxisTags::transpose() works
725         //       in-place, i.e. changes 'self'
726         .def("transpose", (void (AxisTags::*)())&AxisTags::transpose)
727         .def("transpose", (void (AxisTags::*)(ArrayVector<npy_intp> const &))&AxisTags::transpose)
728         .def("index", &AxisTags::index,
729              "Get the axis index of a given axis key::\n\n"
730              "    >>> axistags.index('x')\n"
731              "    0\n\n"
732              "In this example, the 'x'-axis corresponds to index 0 (i.e. the first index).\n")
733         .add_property("channelIndex", &AxisTags::channelIndex,
734             "(read-only property, type 'int') the index of the channel axis, or ``len(axistags)``\n"
735             "when no channel axis exists (i.e. ``axistags.channelIndex`` is similar to\n"
736             "``axistags.index('c')``, but doesn't throw an exception when there\n"
737             "is no 'c' axis.)\n\n")
738         .add_property("innerNonchannelIndex", &AxisTags::innerNonchannelIndex,
739             "(read-only property, type 'int') the index of the innermost non-channel axis.\n")
740         .def("axisTypeCount", &AxisTags::axisTypeCount,
741             "How many axes of the given type(s) are in this axistags object?::\n\n"
742             "    axistags.axisTypeCount(types) -> int\n\n"
743             "The 'types' of the query must be single :class:`~vigra.AxisType` instances\n"
744             "or a combination of them. Examples::\n\n"
745             "    >>> a = vigra.defaultAxistags('txyc')\n"
746             "    >>> a.axisTypeCount(vigra.AxisType.Space)\n"
747             "    2\n"
748             "    >>> a.axisTypeCount(vigra.AxisType.Time)\n"
749             "    1\n"
750             "    >>> a.axisTypeCount(vigra.AxisType(vigra.AxisType.Space | vigra.AxisType.Time))\n"
751             "    3\n"
752             "    >>> a.axisTypeCount(vigra.AxisType.NonChannel)\n"
753             "    3\n\n")
754 
755         // IMPORTANT: do not remove are rename the following functions,
756         //            they are used by the vigranumpy C++ API
757         .def("resolution", (double (AxisTags::*)(int) const)&AxisTags::resolution)
758         .def("resolution", (double (AxisTags::*)(std::string const &) const)&AxisTags::resolution)
759         .def("setResolution", (void (AxisTags::*)(int, double))&AxisTags::setResolution)
760         .def("setResolution",
761             (void (AxisTags::*)(std::string const &, double))&AxisTags::setResolution)
762         .def("scaleResolution", (void (AxisTags::*)(int, double))&AxisTags::scaleResolution)
763         .def("scaleResolution",
764             (void (AxisTags::*)(std::string const &, double))&AxisTags::scaleResolution)
765         .def("description", (std::string (AxisTags::*)(int) const)&AxisTags::description)
766         .def("description",
767              (std::string (AxisTags::*)(std::string const &) const)&AxisTags::description)
768         .def("setDescription",
769             (void (AxisTags::*)(int, std::string const &))&AxisTags::setDescription)
770         .def("setDescription",
771             (void (AxisTags::*)(std::string const &, std::string const &))&AxisTags::setDescription)
772         .def("setChannelDescription", &AxisTags::setChannelDescription,
773              "Set a description for the channel axis, if one exists::\n\n"
774              "    axistags.setChannelDescription('colors are in Lab color space')\n\n"
775              "It is similar to::\n\n"
776              "    axistags['c'].description = 'colors are in Lab color space'\n\n"
777              "except when the axistags contain no channel axis, in which case\n"
778              "setChannelDescription() is simply ignored, whereas axistags['c']\n"
779              "would cause an exception.\n")
780         .def("toFrequencyDomain", (void (AxisTags::*)(int, int, int))&AxisTags::toFrequencyDomain,
781                 (arg("index"), arg("size")=0, arg("sign")=1))
782         .def("toFrequencyDomain",
783                (void (AxisTags::*)(std::string const &, int, int))&AxisTags::toFrequencyDomain,
784                (arg("key"), arg("size")=0, arg("sign")=1))
785         .def("fromFrequencyDomain", (void (AxisTags::*)(int, int))&AxisTags::fromFrequencyDomain,
786                 (arg("index"), arg("size")=0))
787         .def("fromFrequencyDomain",
788                (void (AxisTags::*)(std::string const &, int))&AxisTags::fromFrequencyDomain,
789                (arg("key"), arg("size")=0))
790         .def("permutationToNormalOrder", &AxisTags_permutationToNormalOrder)
791         .def("permutationToNormalOrder", &AxisTags_permutationToNormalOrder2)
792         .def("permutationFromNormalOrder", &AxisTags_permutationFromNormalOrder)
793         .def("permutationFromNormalOrder", &AxisTags_permutationFromNormalOrder2)
794         .def("permutationToNumpyOrder", &AxisTags_permutationToNumpyOrder)
795         .def("permutationFromNumpyOrder", &AxisTags_permutationFromNumpyOrder)
796         .def("permutationToVigraOrder", &AxisTags_permutationToVigraOrder)
797         .def("permutationFromVigraOrder", &AxisTags_permutationFromVigraOrder)
798         .def("permutationToOrder", &AxisTags_permutationToOrder)
799         .def("transform", &AxisTags_transform,
800                              return_value_policy<manage_new_object>())
801         .def("compatible", &AxisTags::compatible)
802         .def(self == self)
803         .def(self != self)
804         .def("toJSON", &AxisTags::toJSON,
805              "Create a string representation of this axistags in JSON format.")
806 #ifndef VIGRA_DISABLE_FROMJSON
807         .def("fromJSON", &AxisTags_readJSON,
808                              return_value_policy<manage_new_object>())
809         .staticmethod("fromJSON")
810 #endif
811         ;
812 }
813 
814 } // namespace vigra
815 
816