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