1 /************************************************************************/
2 /*                                                                      */
3 /*                 Copyright 2009 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 vigranumpyanalysis_PyArray_API
37 #define NO_IMPORT_ARRAY
38 
39 #include <vigra/numpy_array.hxx>
40 #include <vigra/numpy_array_converters.hxx>
41 #include <vigra/edgedetection.hxx>
42 #include <vigra/multi_convolution.hxx>
43 #include <vigra/tensorutilities.hxx>
44 
45 namespace python = boost::python;
46 
47 namespace vigra
48 {
49 
Edgel__getitem__(Edgel const & e,unsigned int i)50 double Edgel__getitem__(Edgel const & e, unsigned int i)
51 {
52     if(i > 1)
53     {
54         PyErr_SetString(PyExc_IndexError,
55             "Edgel.__getitem__(): index out of bounds.");
56         python::throw_error_already_set();
57     }
58     return i == 0 ? e.x : e.y;
59 }
60 
Edgel__setitem__(Edgel & e,unsigned int i,double v)61 void Edgel__setitem__(Edgel & e, unsigned int i, double v)
62 {
63     if(i > 1)
64     {
65         PyErr_SetString(PyExc_IndexError,
66             "Edgel.__setitem__(): index out of bounds.");
67         python::throw_error_already_set();
68     }
69     if(i==0)
70         e.x = Edgel::value_type(v);
71     else
72         e.y = Edgel::value_type(v);
73 }
74 
Edgel__len__(Edgel const &)75 unsigned int Edgel__len__(Edgel const &)
76 {
77     return 2;
78 }
79 
Edgel__repr__(Edgel const & e)80 PyObject * Edgel__repr__(Edgel const & e)
81 {
82         std::stringstream s;
83         s << std::setprecision(14)
84           << "Edgel(x=" << e.x << ", y=" << e.y << ", strength=" << e.strength << ", angle=" << e.orientation << ")";
85         return pythonFromData(s.str().c_str());
86 }
87 
88 template < class PixelType>
89 python::list
pythonFindEdgelsFromGrad(NumpyArray<2,TinyVector<PixelType,2>> grad,double threshold)90 pythonFindEdgelsFromGrad(NumpyArray<2, TinyVector<PixelType, 2> > grad,
91                          double threshold)
92 {
93     std::vector<Edgel> edgels;
94     {
95         PyAllowThreads _pythread;
96         cannyEdgelList(srcImageRange(grad), edgels);
97     }
98 
99     python::list pyEdgels;
100     for(unsigned int i = 0; i < edgels.size(); ++i)
101     {
102         if(edgels[i].strength >= threshold)
103             pyEdgels.append(edgels[i]);
104     }
105     return pyEdgels;
106 }
107 
108 template < class PixelType>
109 python::list
pythonFindEdgels(NumpyArray<2,Singleband<PixelType>> image,double scale,double threshold)110 pythonFindEdgels(NumpyArray<2, Singleband<PixelType> > image,
111                  double scale, double threshold)
112 {
113     std::vector<Edgel> edgels;
114     {
115         PyAllowThreads _pythread;
116         cannyEdgelList(srcImageRange(image), edgels, scale);
117     }
118 
119     python::list pyEdgels;
120     for(unsigned int i = 0; i < edgels.size(); ++i)
121     {
122         if(edgels[i].strength >= threshold)
123             pyEdgels.append(edgels[i]);
124     }
125     return pyEdgels;
126 }
127 
128 template < class PixelType>
129 python::list
pythonFindEdgels3x3FromGrad(NumpyArray<2,TinyVector<PixelType,2>> grad,double threshold)130 pythonFindEdgels3x3FromGrad(NumpyArray<2, TinyVector<PixelType, 2> > grad,
131                             double threshold)
132 {
133     std::vector<Edgel> edgels;
134     {
135         PyAllowThreads _pythread;
136         cannyEdgelList3x3(srcImageRange(grad), edgels);
137     }
138 
139     python::list pyEdgels;
140     for(unsigned int i = 0; i < edgels.size(); ++i)
141     {
142         if(edgels[i].strength >= threshold)
143             pyEdgels.append(edgels[i]);
144     }
145     return pyEdgels;
146 }
147 
148 template < class PixelType>
149 python::list
pythonFindEdgels3x3(NumpyArray<2,Singleband<PixelType>> image,double scale,double threshold)150 pythonFindEdgels3x3(NumpyArray<2, Singleband<PixelType> > image,
151                     double scale, double threshold)
152 {
153     std::vector<Edgel> edgels;
154     {
155         PyAllowThreads _pythread;
156         cannyEdgelList3x3(srcImageRange(image), edgels, scale);
157     }
158     python::list pyEdgels;
159     for(unsigned int i = 0; i < edgels.size(); ++i)
160     {
161         if(edgels[i].strength >= threshold)
162             pyEdgels.append(edgels[i]);
163     }
164     return pyEdgels;
165 }
166 
167 template < class SrcPixelType, typename DestPixelType >
168 NumpyAnyArray
pythonCannyEdgeImage(NumpyArray<2,Singleband<SrcPixelType>> image,double scale,double threshold,DestPixelType edgeMarker,NumpyArray<2,Singleband<DestPixelType>> res=python::object ())169 pythonCannyEdgeImage(NumpyArray<2, Singleband<SrcPixelType> > image,
170                      double scale, double threshold, DestPixelType edgeMarker,
171                      NumpyArray<2, Singleband<DestPixelType> > res = python::object())
172 {
173     std::string description("Canny edges, scale=");
174     description += asString(scale) + ", threshold=" + asString(threshold);
175 
176     res.reshapeIfEmpty(image.taggedShape().setChannelDescription(description),
177             "cannyEdgeImage(): Output array has wrong shape.");
178 
179     {
180         PyAllowThreads _pythread;
181         cannyEdgeImage(srcImageRange(image), destImage(res),
182                        scale, threshold, edgeMarker);
183     }
184 
185     return res;
186 }
187 
188 template < class SrcPixelType, typename DestPixelType >
189 NumpyAnyArray
pythonCannyEdgeImageColor(NumpyArray<2,RGBValue<SrcPixelType>> image,double scale,double threshold,DestPixelType edgeMarker,NumpyArray<2,Singleband<DestPixelType>> res=python::object ())190 pythonCannyEdgeImageColor(NumpyArray<2, RGBValue<SrcPixelType> > image,
191                           double scale, double threshold, DestPixelType edgeMarker,
192                           NumpyArray<2, Singleband<DestPixelType> > res = python::object())
193 {
194     std::string description("Canny edges, scale=");
195     description += asString(scale) + ", threshold=" + asString(threshold);
196 
197     res.reshapeIfEmpty(image.taggedShape().setChannelDescription(description),
198             "cannyEdgeImage(): Output array has wrong shape.");
199 
200     {
201         PyAllowThreads _pythread;
202         MultiArray<2, TinyVector<float, 2>> gradient(image.shape());
203         MultiArray<2, TinyVector<float, 3>> tmp(image.shape()),
204                                             gradient_tensor(image.shape());
205         for(int k=0; k<3; ++k)
206         {
207             gaussianGradientMultiArray(image.bindElementChannel(k), gradient, scale);
208             vectorToTensor(gradient, tmp);
209             gradient_tensor += tmp;
210         }
211         tensorEigenRepresentation(gradient_tensor, tmp);
212         transformMultiArray(tmp, gradient, [](TinyVector<float, 3> const & v) {
213             return TinyVector<float, 2>(std::cos(v[2])*sqrt(v[0]), std::sin(v[2])*sqrt(v[0]));
214         });
215         cannyEdgeImageFromGradWithThinning(gradient, res,
216                                            threshold, edgeMarker, false);
217     }
218 
219     return res;
220 }
221 
222 template < class SrcPixelType, typename DestPixelType >
223 NumpyAnyArray
pythonCannyEdgeImageWithThinning(NumpyArray<2,Singleband<SrcPixelType>> image,double scale,double threshold,DestPixelType edgeMarker,bool addBorder=true,NumpyArray<2,Singleband<DestPixelType>> res=python::object ())224 pythonCannyEdgeImageWithThinning(NumpyArray<2, Singleband<SrcPixelType> > image,
225                                  double scale, double threshold,
226                                  DestPixelType edgeMarker, bool addBorder = true,
227                                  NumpyArray<2, Singleband<DestPixelType> > res = python::object())
228 {
229     std::string description("Canny edges with thinning, scale=");
230     description += asString(scale) + ", threshold=" + asString(threshold);
231 
232     res.reshapeIfEmpty(image.taggedShape().setChannelDescription(description),
233             "cannyEdgeImageWithThinning(): Output array has wrong shape.");
234 
235     {
236         PyAllowThreads _pythread;
237         cannyEdgeImageWithThinning(srcImageRange(image), destImage(res),
238                                    scale, threshold, edgeMarker, addBorder);
239     }
240 
241     return res;
242 }
243 
244 template < class SrcPixelType, typename DestPixelType >
245 NumpyAnyArray
pythonShenCastanEdgeImage(NumpyArray<2,Singleband<SrcPixelType>> image,double scale,double threshold,DestPixelType edgeMarker,NumpyArray<2,Singleband<DestPixelType>> res=python::object ())246 pythonShenCastanEdgeImage(NumpyArray<2, Singleband<SrcPixelType> > image,
247                           double scale, double threshold, DestPixelType edgeMarker,
248                           NumpyArray<2, Singleband<DestPixelType> > res = python::object())
249 {
250     std::string description("Shen/Castan edges, scale=");
251     description += asString(scale) + ", threshold=" + asString(threshold);
252 
253     res.reshapeIfEmpty(image.taggedShape().setChannelDescription(description),
254             "shenCastanEdgeImage(): Output array has wrong shape.");
255 
256     {
257         PyAllowThreads _pythread;
258         differenceOfExponentialEdgeImage(srcImageRange(image), destImage(res),
259                                          scale, threshold, edgeMarker);
260     }
261 
262     return res;
263 }
264 
265 template < class SrcPixelType, typename DestPixelType >
266 NumpyAnyArray
pythonShenCastanCrackEdgeImage(NumpyArray<2,Singleband<SrcPixelType>> image,double scale,double threshold,DestPixelType edgeMarker,NumpyArray<2,Singleband<DestPixelType>> res=python::object ())267 pythonShenCastanCrackEdgeImage(NumpyArray<2, Singleband<SrcPixelType> > image,
268                                double scale, double threshold, DestPixelType edgeMarker,
269                                NumpyArray<2, Singleband<DestPixelType> > res = python::object())
270 {
271     std::string description("Shen/Castan crack edges, scale=");
272     description += asString(scale) + ", threshold=" + asString(threshold);
273 
274     MultiArrayShape<2>::type newShape = 2*image.shape() - MultiArrayShape<2>::type(1,1);
275     res.reshapeIfEmpty(image.taggedShape().resize(newShape).setChannelDescription(description),
276                        "shenCastanCrackEdgeImage(): Output array has wrong shape. Needs to be (w,h)*2 - 1.");
277 
278     {
279         PyAllowThreads _pythread;
280         differenceOfExponentialCrackEdgeImage(srcImageRange(image), destImage(res),
281                                               scale, threshold, edgeMarker);
282     }
283 
284     return res;
285 }
286 
287 template < class PixelType>
288 NumpyAnyArray
pythonRemoveShortEdges(NumpyArray<2,Singleband<PixelType>> image,int minEdgeLength,PixelType nonEdgeMarker,NumpyArray<2,Singleband<PixelType>> res=python::object ())289 pythonRemoveShortEdges(NumpyArray<2, Singleband<PixelType> > image,
290                        int minEdgeLength, PixelType nonEdgeMarker,
291                        NumpyArray<2, Singleband<PixelType> > res = python::object())
292 {
293     res.reshapeIfEmpty(image.taggedShape(),
294             "removeShortEdges(): Output array has wrong shape.");
295 
296     {
297         PyAllowThreads _pythread;
298         copyImage(srcImageRange(image), destImage(res));
299         removeShortEdges(destImageRange(res), minEdgeLength, nonEdgeMarker);
300     }
301 
302     return res;
303 }
304 
305 template < class PixelType >
306 NumpyAnyArray
pythonBeautifyCrackEdgeImage(NumpyArray<2,Singleband<PixelType>> image,PixelType edgeMarker,PixelType backgroundMarker,NumpyArray<2,Singleband<PixelType>> res=python::object ())307 pythonBeautifyCrackEdgeImage(NumpyArray<2, Singleband<PixelType> > image,
308                              PixelType edgeMarker,
309                              PixelType backgroundMarker,
310                              NumpyArray<2, Singleband<PixelType> > res = python::object())
311 {
312     res.reshapeIfEmpty(image.taggedShape(),
313             "beautifyCrackEdgeImage(): Output array has wrong shape.");
314 
315     {
316         PyAllowThreads _pythread;
317         copyImage(srcImageRange(image), destImage(res));
318         beautifyCrackEdgeImage(destImageRange(res), edgeMarker, backgroundMarker);
319     }
320 
321     return res;
322 }
323 
324 template < class PixelType >
325 NumpyAnyArray
pythonCloseGapsInCrackEdgeImage(NumpyArray<2,Singleband<PixelType>> image,PixelType edgeMarker,NumpyArray<2,Singleband<PixelType>> res=python::object ())326 pythonCloseGapsInCrackEdgeImage(NumpyArray<2, Singleband<PixelType> > image,
327                                 PixelType edgeMarker,
328                                 NumpyArray<2, Singleband<PixelType> > res = python::object())
329 {
330     res.reshapeIfEmpty(image.taggedShape(),
331             "closeGapsInCrackEdgeImage(): Output array has wrong shape.");
332 
333     {
334         PyAllowThreads _pythread;
335         copyImage(srcImageRange(image), destImage(res));
336         closeGapsInCrackEdgeImage(destImageRange(res), edgeMarker);
337     }
338 
339     return res;
340 }
341 
342 template < class PixelType >
343 NumpyAnyArray
pythonRegionImageToCrackEdgeImage(NumpyArray<2,Singleband<PixelType>> image,PixelType edgeLabel=0,NumpyArray<2,Singleband<PixelType>> res=python::object ())344 pythonRegionImageToCrackEdgeImage(NumpyArray<2, Singleband<PixelType> > image,
345                                   PixelType edgeLabel = 0,
346                                   NumpyArray<2, Singleband<PixelType> > res = python::object())
347 {
348     MultiArrayShape<2>::type newShape = 2*image.shape() - MultiArrayShape<2>::type(1,1);
349     res.reshapeIfEmpty(image.taggedShape().resize(newShape),
350             "regionImageToCrackEdgeImage(): Output array has wrong shape. Needs to be (w,h)*2 - 1.");
351 
352     {
353         PyAllowThreads _pythread;
354         regionImageToCrackEdgeImage(srcImageRange(image), destImage(res), edgeLabel);
355     }
356     return res;
357 }
358 
359 template < class PixelType >
360 NumpyAnyArray
pythonRegionImageToEdgeImage(NumpyArray<2,Singleband<PixelType>> image,PixelType edgeLabel=1,NumpyArray<2,Singleband<PixelType>> res=python::object ())361 pythonRegionImageToEdgeImage(NumpyArray<2, Singleband<PixelType> > image,
362                              PixelType edgeLabel = 1,
363                              NumpyArray<2, Singleband<PixelType> > res = python::object())
364 {
365     res.reshapeIfEmpty(image.taggedShape(),
366             "regionImageToEdgeImage2D(): Output array has wrong shape.");
367 
368     {
369         PyAllowThreads _pythread;
370         regionImageToEdgeImage(srcImageRange(image), destImage(res), edgeLabel);
371     }
372     return res;
373 }
374 
375 
defineEdgedetection()376 void defineEdgedetection()
377 {
378     using namespace python;
379 
380     docstring_options doc_options(true, true, false);
381 
382     class_<Edgel> edgel("Edgel", "Represent an Edgel at a particular subpixel position (x, y), having "
383                                   "given 'strength' and 'orientation'.\n\n"
384                                   "For details, see Edgel_ in the vigra C++ documentation.\n",
385                          init<>("Standard constructor::\n\n   Edgel()\n\n"));
386     edgel
387        .def(init<float, float, float, float>(args("x", "y", "strength", "orientation"),
388             "Constructor::\n\n    Edgel(x, y, strength, orientation)\n\n"))
389        .def_readwrite("x", &Edgel::x, "The edgel's x position.")
390        .def_readwrite("y", &Edgel::y, "The edgel's y position.")
391        .def_readwrite("strength", &Edgel::strength, "The edgel's strength.")
392        .def_readwrite("orientation", &Edgel::orientation, "The edgel's orientation.")
393        .def("__getitem__", &Edgel__getitem__)
394        .def("__setitem__", &Edgel__setitem__)
395        .def("__repr__", &Edgel__repr__)
396        .def("__len__", &Edgel__len__)
397        ;
398 
399     def("cannyEdgelList",
400         registerConverters(&pythonFindEdgelsFromGrad<float>),
401         args("gradient", "threshold"),
402         "Return a list of :class:`Edgel` objects whose strength is at least 'threshold'.\n\n"
403         "The function comes in two forms::\n\n"
404         "    cannyEdgelList(gradient, threshold) -> list\n"
405         "    cannyEdgelList(image, scale, threshold) -> list\n\n"
406         "The first form expects a gradient image (i.e. with two channels) to compute "
407         "edgels, whereas the second form expects a scalar image and computes the "
408         "gradient internally at 'scale'.\n\n"
409         "For details see cannyEdgelList_ in the vigra C++ documentation.\n");
410 
411     def("cannyEdgelList",
412         registerConverters(&pythonFindEdgels<float>),
413         args("image", "scale", "threshold"),
414          "Compute edgels of a 2D scalar image, given the filter scale.\n");
415 
416     def("cannyEdgelList3x3",
417         registerConverters(&pythonFindEdgels3x3FromGrad<float>),
418         args("gradient", "threshold"),
419         "Return a list of :class:`Edgel` objects whose strength is at least 'threshold'.\n\n"
420         "The function comes in two forms::\n\n"
421         "    cannyEdgelList3x3(gradient, threshold) -> list\n"
422         "    cannyEdgelList3x3(image, scale, threshold) -> list\n\n"
423         "The first form expects a gradient image (i.e. with two channels) to compute "
424         "edgels, whereas the second form expects a scalar image and computes the "
425         "gradient internally at 'scale'. The results are slightly better than "
426         "those of :func:`cannyEdgelList`.\n\n"
427         "For details see cannyEdgelList3x3_ in the vigra C++ documentation.\n");
428 
429     def("cannyEdgelList3x3",
430         registerConverters(&pythonFindEdgels3x3<float>),
431         args("image", "scale", "threshold"),
432          "Compute edgels of a 2D scalar image, given the filter scale.\n");
433 
434     def("cannyEdgeImage",
435         registerConverters(&pythonCannyEdgeImage<float, UInt8>),
436         (arg("image"), arg("scale"), arg("threshold"), arg("edgeMarker"),arg("out")=python::object()),
437         "Detect and mark edges in an edge image using Canny's algorithm.\n\n"
438         "For details see cannyEdgeImage_ in the vigra C++ documentation.\n");
439 
440     def("cannyEdgeImage",
441         registerConverters(&pythonCannyEdgeImageColor<float, UInt8>),
442         (arg("image"), arg("scale"), arg("threshold"), arg("edgeMarker"),arg("out")=python::object()),
443         "Detect and mark edges in an edge image using Canny's algorithm.\n\n"
444         "For details see cannyEdgeImage_ in the vigra C++ documentation.\n");
445 
446     def("cannyEdgeImageWithThinning",
447         registerConverters(&pythonCannyEdgeImageWithThinning<float, UInt8>),
448         (arg("image"), arg("scale"), arg("threshold"), arg("edgeMarker"),
449         arg("addBorder")=true,arg("out")=python::object()),
450         "Detect and mark edges in an edge image using Canny's algorithm.\n\n"
451         "For details see cannyEdgeImageWithThinning_ in the vigra C++ documentation.\n");
452 
453     def("shenCastanEdgeImage",
454         registerConverters(&pythonShenCastanEdgeImage<float, UInt8>),
455         (arg("image"), arg("scale"), arg("threshold"), arg("edgeMarker"),arg("out")=python::object()),
456         "Detect and mark edges in an edge image using the Shen/Castan zero-crossing detector.\n\n"
457         "For details see differenceOfExponentialEdgeImage_ in the vigra C++ documentation.\n");
458 
459     def("shenCastanCrackEdgeImage",
460         registerConverters(&pythonShenCastanCrackEdgeImage<float, UInt8>),
461         (arg("image"), arg("scale"), arg("threshold"), arg("edgeMarker"),arg("out")=python::object()),
462         "Detect and mark edges in a crack edge image using the Shen/Castan zero-crossing detector.\n\n"
463         "For details see differenceOfExponentialCrackEdgeImage_ in the vigra C++ documentation.\n");
464 
465     def("removeShortEdges",
466         registerConverters(&pythonRemoveShortEdges<UInt8>),
467         (arg("image"), arg("minEdgeLength"), arg("nonEdgeMarker"),arg("out")=python::object()),
468         "Remove short edges from an edge image.\n\n"
469         "For details see removeShortEdges_ in the vigra C++ documentation.\n");
470 
471     def("beautifyCrackEdgeImage",
472         registerConverters(&pythonBeautifyCrackEdgeImage<UInt8>),
473         (arg("image"), arg("edgeMarker"), arg("backgroundMarker"),arg("out")=python::object()),
474         "Beautify crack edge image for visualization.\n\n"
475         "For details see beautifyCrackEdgeImage_ in the vigra C++ documentation.\n");
476 
477     def("closeGapsInCrackEdgeImage",
478         registerConverters(&pythonCloseGapsInCrackEdgeImage<UInt8>),
479         (arg("image"), arg("edgeMarker"),arg("out")=python::object()),
480         "Close one-pixel wide gaps in a cell grid edge image.\n\n"
481         "For details see closeGapsInCrackEdgeImage_ in the vigra C++ documentation.\n");
482 
483     def("regionImageToEdgeImage",
484         registerConverters(&pythonRegionImageToEdgeImage<npy_uint32>),
485         (arg("image"),
486          arg("edgeLabel") = 1,
487          arg("out")=python::object()),
488         "Transform a labeled uint32 image into an edge image.\n\n"
489         "For details see regionImageToEdgeImage_ in the vigra C++ documentation.\n");
490 
491     def("regionImageToEdgeImage",
492         registerConverters(&pythonRegionImageToEdgeImage<npy_uint64>),
493         (arg("image"),
494          arg("edgeLabel") = 1,
495          arg("out")=python::object()),
496          "Likewise for a uint64 image.\n");
497 
498     def("regionImageToCrackEdgeImage",
499          registerConverters(&pythonRegionImageToCrackEdgeImage<npy_uint32>),
500          (arg("image"),
501           arg("edgeLabel") = 0,
502           arg("out")=python::object()),
503          "Transform a labeled uint32 image into a crack edge image. \n\n"
504          "For details see regionImageToCrackEdgeImage_ in the vigra C++ documentation.\n");
505 
506     def("regionImageToCrackEdgeImage",
507          registerConverters(&pythonRegionImageToCrackEdgeImage<npy_uint64>),
508          (arg("image"),
509           arg("edgeLabel") = 0,
510           arg("out")=python::object()),
511          "Likewise for a uint64 image.\n");
512 
513 }
514 
515 } // namespace vigra
516