[an error occurred while processing this directive]
Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

matchpoint/edgedetection.hxx

Go to the documentation of this file.
00001 /************************************************************************/
00002 /*                                                                      */
00003 /*               Copyright 1998-2002 by Ullrich Koethe                  */
00004 /*       Cognitive Systems Group, University of Hamburg, Germany        */
00005 /*                                                                      */
00006 /*    This file is part of the VIGRA computer vision library.           */
00007 /*    ( Version 1.5.0, Dec 07 2006 )                                    */
00008 /*    The VIGRA Website is                                              */
00009 /*        http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/      */
00010 /*    Please direct questions, bug reports, and contributions to        */
00011 /*        koethe@informatik.uni-hamburg.de          or                  */
00012 /*        vigra@kogs1.informatik.uni-hamburg.de                         */
00013 /*                                                                      */
00014 /*    Permission is hereby granted, free of charge, to any person       */
00015 /*    obtaining a copy of this software and associated documentation    */
00016 /*    files (the "Software"), to deal in the Software without           */
00017 /*    restriction, including without limitation the rights to use,      */
00018 /*    copy, modify, merge, publish, distribute, sublicense, and/or      */
00019 /*    sell copies of the Software, and to permit persons to whom the    */
00020 /*    Software is furnished to do so, subject to the following          */
00021 /*    conditions:                                                       */
00022 /*                                                                      */
00023 /*    The above copyright notice and this permission notice shall be    */
00024 /*    included in all copies or substantial portions of the             */
00025 /*    Software.                                                         */
00026 /*                                                                      */
00027 /*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
00028 /*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
00029 /*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
00030 /*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
00031 /*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
00032 /*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
00033 /*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
00034 /*    OTHER DEALINGS IN THE SOFTWARE.                                   */
00035 /*                                                                      */
00036 /************************************************************************/
00037 
00038 
00039 #ifndef VIGRA_EDGEDETECTION_HXX
00040 #define VIGRA_EDGEDETECTION_HXX
00041 
00042 #include <vector>
00043 #include <queue>
00044 #include <cmath>     // sqrt(), abs()
00045 #include "vigra/utilities.hxx"
00046 #include "vigra/numerictraits.hxx"
00047 #include "vigra/stdimage.hxx"
00048 #include "vigra/stdimagefunctions.hxx"
00049 #include "vigra/recursiveconvolution.hxx"
00050 #include "vigra/separableconvolution.hxx"
00051 #include "vigra/labelimage.hxx"
00052 #include "vigra/mathutil.hxx"
00053 #include "vigra/pixelneighborhood.hxx"
00054 #include "vigra/linear_solve.hxx"
00055 
00056 
00057 namespace vigra {
00058 
00064 
00065 /********************************************************/
00066 /*                                                      */
00067 /*           differenceOfExponentialEdgeImage           */
00068 /*                                                      */
00069 /********************************************************/
00070 
00181 template <class SrcIterator, class SrcAccessor,
00182           class DestIterator, class DestAccessor,
00183           class GradValue, class DestValue>
00184 void differenceOfExponentialEdgeImage(
00185                SrcIterator sul, SrcIterator slr, SrcAccessor sa,
00186            DestIterator dul, DestAccessor da,
00187            double scale, GradValue gradient_threshold, DestValue edge_marker)
00188 {
00189     vigra_precondition(scale > 0,
00190                  "differenceOfExponentialEdgeImage(): scale > 0 required.");
00191 
00192     vigra_precondition(gradient_threshold > 0,
00193                  "differenceOfExponentialEdgeImage(): "
00194          "gradient_threshold > 0 required.");
00195 
00196     int w = slr.x - sul.x;
00197     int h = slr.y - sul.y;
00198     int x,y;
00199 
00200     typedef typename
00201         NumericTraits<typename SrcAccessor::value_type>::RealPromote
00202     TMPTYPE;
00203     typedef BasicImage<TMPTYPE> TMPIMG;
00204 
00205     TMPIMG tmp(w,h);
00206     TMPIMG smooth(w,h);
00207 
00208     recursiveSmoothX(srcIterRange(sul, slr, sa), destImage(tmp), scale / 2.0);
00209     recursiveSmoothY(srcImageRange(tmp), destImage(tmp), scale / 2.0);
00210 
00211     recursiveSmoothX(srcImageRange(tmp), destImage(smooth), scale);
00212     recursiveSmoothY(srcImageRange(smooth), destImage(smooth), scale);
00213 
00214     typename TMPIMG::Iterator iy = smooth.upperLeft();
00215     typename TMPIMG::Iterator ty = tmp.upperLeft();
00216     DestIterator              dy = dul;
00217 
00218     static const Diff2D right(1, 0);
00219     static const Diff2D bottom(0, 1);
00220 
00221 
00222     TMPTYPE thresh = (gradient_threshold * gradient_threshold) *
00223                      NumericTraits<TMPTYPE>::one();
00224     TMPTYPE zero = NumericTraits<TMPTYPE>::zero();
00225 
00226     for(y=0; y<h-1; ++y, ++iy.y, ++ty.y, ++dy.y)
00227     {
00228         typename TMPIMG::Iterator ix = iy;
00229         typename TMPIMG::Iterator tx = ty;
00230         DestIterator              dx = dy;
00231 
00232         for(x=0; x<w-1; ++x, ++ix.x, ++tx.x, ++dx.x)
00233         {
00234             TMPTYPE diff = *tx - *ix;
00235             TMPTYPE gx = tx[right] - *tx;
00236             TMPTYPE gy = tx[bottom] - *tx;
00237 
00238             if((gx * gx > thresh) &&
00239                 (diff * (tx[right] - ix[right]) < zero))
00240             {
00241                 if(gx < zero)
00242                 {
00243                     da.set(edge_marker, dx, right);
00244                 }
00245                 else
00246                 {
00247                     da.set(edge_marker, dx);
00248                 }
00249             }
00250             if(((gy * gy > thresh) &&
00251                 (diff * (tx[bottom] - ix[bottom]) < zero)))
00252             {
00253                 if(gy < zero)
00254                 {
00255                     da.set(edge_marker, dx, bottom);
00256                 }
00257                 else
00258                 {
00259                     da.set(edge_marker, dx);
00260                 }
00261             }
00262         }
00263     }
00264 
00265     typename TMPIMG::Iterator ix = iy;
00266     typename TMPIMG::Iterator tx = ty;
00267     DestIterator              dx = dy;
00268 
00269     for(x=0; x<w-1; ++x, ++ix.x, ++tx.x, ++dx.x)
00270     {
00271         TMPTYPE diff = *tx - *ix;
00272         TMPTYPE gx = tx[right] - *tx;
00273 
00274         if((gx * gx > thresh) &&
00275            (diff * (tx[right] - ix[right]) < zero))
00276         {
00277             if(gx < zero)
00278             {
00279                 da.set(edge_marker, dx, right);
00280             }
00281             else
00282             {
00283                 da.set(edge_marker, dx);
00284             }
00285         }
00286     }
00287 }
00288 
00289 template <class SrcIterator, class SrcAccessor,
00290           class DestIterator, class DestAccessor,
00291           class GradValue>
00292 inline
00293 void differenceOfExponentialEdgeImage(
00294                SrcIterator sul, SrcIterator slr, SrcAccessor sa,
00295            DestIterator dul, DestAccessor da,
00296            double scale, GradValue gradient_threshold)
00297 {
00298     differenceOfExponentialEdgeImage(sul, slr, sa, dul, da,
00299                                         scale, gradient_threshold, 1);
00300 }
00301 
00302 template <class SrcIterator, class SrcAccessor,
00303           class DestIterator, class DestAccessor,
00304       class GradValue, class DestValue>
00305 inline
00306 void differenceOfExponentialEdgeImage(
00307            triple<SrcIterator, SrcIterator, SrcAccessor> src,
00308        pair<DestIterator, DestAccessor> dest,
00309        double scale, GradValue gradient_threshold,
00310        DestValue edge_marker)
00311 {
00312     differenceOfExponentialEdgeImage(src.first, src.second, src.third,
00313                                         dest.first, dest.second,
00314                     scale, gradient_threshold,
00315                     edge_marker);
00316 }
00317 
00318 template <class SrcIterator, class SrcAccessor,
00319           class DestIterator, class DestAccessor,
00320           class GradValue>
00321 inline
00322 void differenceOfExponentialEdgeImage(
00323            triple<SrcIterator, SrcIterator, SrcAccessor> src,
00324        pair<DestIterator, DestAccessor> dest,
00325        double scale, GradValue gradient_threshold)
00326 {
00327     differenceOfExponentialEdgeImage(src.first, src.second, src.third,
00328                                         dest.first, dest.second,
00329                     scale, gradient_threshold, 1);
00330 }
00331 
00332 /********************************************************/
00333 /*                                                      */
00334 /*        differenceOfExponentialCrackEdgeImage         */
00335 /*                                                      */
00336 /********************************************************/
00337 
00465 template <class SrcIterator, class SrcAccessor,
00466           class DestIterator, class DestAccessor,
00467           class GradValue, class DestValue>
00468 void differenceOfExponentialCrackEdgeImage(
00469                SrcIterator sul, SrcIterator slr, SrcAccessor sa,
00470            DestIterator dul, DestAccessor da,
00471            double scale, GradValue gradient_threshold,
00472            DestValue edge_marker)
00473 {
00474     vigra_precondition(scale > 0,
00475                  "differenceOfExponentialCrackEdgeImage(): scale > 0 required.");
00476 
00477     vigra_precondition(gradient_threshold > 0,
00478                  "differenceOfExponentialCrackEdgeImage(): "
00479          "gradient_threshold > 0 required.");
00480 
00481     int w = slr.x - sul.x;
00482     int h = slr.y - sul.y;
00483     int x, y;
00484 
00485     typedef typename
00486         NumericTraits<typename SrcAccessor::value_type>::RealPromote
00487     TMPTYPE;
00488     typedef BasicImage<TMPTYPE> TMPIMG;
00489 
00490     TMPIMG tmp(w,h);
00491     TMPIMG smooth(w,h);
00492 
00493     TMPTYPE zero = NumericTraits<TMPTYPE>::zero();
00494 
00495     static const Diff2D right(1,0);
00496     static const Diff2D bottom(0,1);
00497     static const Diff2D left(-1,0);
00498     static const Diff2D top(0,-1);
00499 
00500     recursiveSmoothX(srcIterRange(sul, slr, sa), destImage(tmp), scale / 2.0);
00501     recursiveSmoothY(srcImageRange(tmp), destImage(tmp), scale / 2.0);
00502 
00503     recursiveSmoothX(srcImageRange(tmp), destImage(smooth), scale);
00504     recursiveSmoothY(srcImageRange(smooth), destImage(smooth), scale);
00505 
00506     typename TMPIMG::Iterator iy = smooth.upperLeft();
00507     typename TMPIMG::Iterator ty = tmp.upperLeft();
00508     DestIterator              dy = dul;
00509 
00510     TMPTYPE thresh = (gradient_threshold * gradient_threshold) *
00511                      NumericTraits<TMPTYPE>::one();
00512 
00513     // find zero crossings above threshold
00514     for(y=0; y<h-1; ++y, ++iy.y, ++ty.y, dy.y+=2)
00515     {
00516         typename TMPIMG::Iterator ix = iy;
00517         typename TMPIMG::Iterator tx = ty;
00518         DestIterator              dx = dy;
00519 
00520         for(int x=0; x<w-1; ++x, ++ix.x, ++tx.x, dx.x+=2)
00521         {
00522             TMPTYPE diff = *tx - *ix;
00523             TMPTYPE gx = tx[right] - *tx;
00524             TMPTYPE gy = tx[bottom] - *tx;
00525 
00526             if((gx * gx > thresh) &&
00527                (diff * (tx[right] - ix[right]) < zero))
00528             {
00529                 da.set(edge_marker, dx, right);
00530             }
00531             if((gy * gy > thresh) &&
00532                (diff * (tx[bottom] - ix[bottom]) < zero))
00533             {
00534                 da.set(edge_marker, dx, bottom);
00535             }
00536         }
00537 
00538         TMPTYPE diff = *tx - *ix;
00539         TMPTYPE gy = tx[bottom] - *tx;
00540 
00541         if((gy * gy > thresh) &&
00542            (diff * (tx[bottom] - ix[bottom]) < zero))
00543         {
00544             da.set(edge_marker, dx, bottom);
00545         }
00546     }
00547 
00548     typename TMPIMG::Iterator ix = iy;
00549     typename TMPIMG::Iterator tx = ty;
00550     DestIterator              dx = dy;
00551 
00552     for(x=0; x<w-1; ++x, ++ix.x, ++tx.x, dx.x+=2)
00553     {
00554         TMPTYPE diff = *tx - *ix;
00555         TMPTYPE gx = tx[right] - *tx;
00556 
00557         if((gx * gx > thresh) &&
00558            (diff * (tx[right] - ix[right]) < zero))
00559         {
00560             da.set(edge_marker, dx, right);
00561         }
00562     }
00563 
00564     iy = smooth.upperLeft() + Diff2D(0,1);
00565     ty = tmp.upperLeft() + Diff2D(0,1);
00566     dy = dul + Diff2D(1,2);
00567 
00568     static const Diff2D topleft(-1,-1);
00569     static const Diff2D topright(1,-1);
00570     static const Diff2D bottomleft(-1,1);
00571     static const Diff2D bottomright(1,1);
00572 
00573     // find missing 1-cells below threshold (x-direction)
00574     for(y=0; y<h-2; ++y, ++iy.y, ++ty.y, dy.y+=2)
00575     {
00576         typename TMPIMG::Iterator ix = iy;
00577         typename TMPIMG::Iterator tx = ty;
00578         DestIterator              dx = dy;
00579 
00580         for(int x=0; x<w-2; ++x, ++ix.x, ++tx.x, dx.x+=2)
00581         {
00582             if(da(dx) == edge_marker) continue;
00583 
00584             TMPTYPE diff = *tx - *ix;
00585 
00586             if((diff * (tx[right] - ix[right]) < zero) &&
00587                (((da(dx, bottomright) == edge_marker) &&
00588                  (da(dx, topleft) == edge_marker)) ||
00589                 ((da(dx, bottomleft) == edge_marker) &&
00590                  (da(dx, topright) == edge_marker))))
00591 
00592             {
00593                 da.set(edge_marker, dx);
00594             }
00595         }
00596     }
00597 
00598     iy = smooth.upperLeft() + Diff2D(1,0);
00599     ty = tmp.upperLeft() + Diff2D(1,0);
00600     dy = dul + Diff2D(2,1);
00601 
00602     // find missing 1-cells below threshold (y-direction)
00603     for(y=0; y<h-2; ++y, ++iy.y, ++ty.y, dy.y+=2)
00604     {
00605         typename TMPIMG::Iterator ix = iy;
00606         typename TMPIMG::Iterator tx = ty;
00607         DestIterator              dx = dy;
00608 
00609         for(int x=0; x<w-2; ++x, ++ix.x, ++tx.x, dx.x+=2)
00610         {
00611             if(da(dx) == edge_marker) continue;
00612 
00613             TMPTYPE diff = *tx - *ix;
00614 
00615             if((diff * (tx[bottom] - ix[bottom]) < zero) &&
00616                (((da(dx, bottomright) == edge_marker) &&
00617                  (da(dx, topleft) == edge_marker)) ||
00618                 ((da(dx, bottomleft) == edge_marker) &&
00619                  (da(dx, topright) == edge_marker))))
00620 
00621             {
00622                 da.set(edge_marker, dx);
00623             }
00624         }
00625     }
00626 
00627     dy = dul + Diff2D(1,1);
00628 
00629     // find missing 0-cells
00630     for(y=0; y<h-1; ++y, dy.y+=2)
00631     {
00632         DestIterator              dx = dy;
00633 
00634         for(int x=0; x<w-1; ++x, dx.x+=2)
00635         {
00636             static const Diff2D dist[] = {right, top, left, bottom };
00637 
00638             int i;
00639             for(i=0; i<4; ++i)
00640             {
00641             if(da(dx, dist[i]) == edge_marker) break;
00642             }
00643 
00644             if(i < 4) da.set(edge_marker, dx);
00645         }
00646     }
00647 }
00648 
00649 template <class SrcIterator, class SrcAccessor,
00650           class DestIterator, class DestAccessor,
00651           class GradValue, class DestValue>
00652 inline
00653 void differenceOfExponentialCrackEdgeImage(
00654            triple<SrcIterator, SrcIterator, SrcAccessor> src,
00655        pair<DestIterator, DestAccessor> dest,
00656        double scale, GradValue gradient_threshold,
00657        DestValue edge_marker)
00658 {
00659     differenceOfExponentialCrackEdgeImage(src.first, src.second, src.third,
00660                                         dest.first, dest.second,
00661                     scale, gradient_threshold,
00662                     edge_marker);
00663 }
00664 
00665 /********************************************************/
00666 /*                                                      */
00667 /*                  removeShortEdges                    */
00668 /*                                                      */
00669 /********************************************************/
00670 
00748 template <class Iterator, class Accessor, class Value>
00749 void removeShortEdges(
00750                Iterator sul, Iterator slr, Accessor sa,
00751            unsigned int min_edge_length, Value non_edge_marker)
00752 {
00753     int w = slr.x - sul.x;
00754     int h = slr.y - sul.y;
00755     int x,y;
00756 
00757     IImage labels(w, h);
00758     labels = 0;
00759 
00760     int number_of_regions =
00761                 labelImageWithBackground(srcIterRange(sul,slr,sa),
00762                                      destImage(labels), true, non_edge_marker);
00763 
00764     ArrayOfRegionStatistics<FindROISize<int> >
00765                                          region_stats(number_of_regions);
00766 
00767     inspectTwoImages(srcImageRange(labels), srcImage(labels), region_stats);
00768 
00769     IImage::Iterator ly = labels.upperLeft();
00770     Iterator oy = sul;
00771 
00772     for(y=0; y<h; ++y, ++oy.y, ++ly.y)
00773     {
00774         Iterator ox(oy);
00775         IImage::Iterator lx(ly);
00776 
00777         for(x=0; x<w; ++x, ++ox.x, ++lx.x)
00778         {
00779             if(sa(ox) == non_edge_marker) continue;
00780             if((region_stats[*lx].count) < min_edge_length)
00781             {
00782                  sa.set(non_edge_marker, ox);
00783             }
00784         }
00785     }
00786 }
00787 
00788 template <class Iterator, class Accessor, class Value>
00789 inline
00790 void removeShortEdges(
00791            triple<Iterator, Iterator, Accessor> src,
00792        unsigned int min_edge_length, Value non_edge_marker)
00793 {
00794     removeShortEdges(src.first, src.second, src.third,
00795                      min_edge_length, non_edge_marker);
00796 }
00797 
00798 /********************************************************/
00799 /*                                                      */
00800 /*             closeGapsInCrackEdgeImage                */
00801 /*                                                      */
00802 /********************************************************/
00803 
00883 template <class SrcIterator, class SrcAccessor, class SrcValue>
00884 void closeGapsInCrackEdgeImage(
00885                SrcIterator sul, SrcIterator slr, SrcAccessor sa,
00886            SrcValue edge_marker)
00887 {
00888     int w = (slr.x - sul.x) / 2;
00889     int h = (slr.y - sul.y) / 2;
00890     int x, y;
00891 
00892     int count1, count2, count3;
00893 
00894     static const Diff2D right(1,0);
00895     static const Diff2D bottom(0,1);
00896     static const Diff2D left(-1,0);
00897     static const Diff2D top(0,-1);
00898 
00899     static const Diff2D leftdist[] = {
00900         Diff2D(0, 0), Diff2D(-1, 1), Diff2D(-2, 0), Diff2D(-1, -1)};
00901     static const Diff2D rightdist[] = {
00902         Diff2D(2, 0), Diff2D(1, 1), Diff2D(0, 0), Diff2D(1, -1)};
00903     static const Diff2D topdist[] = {
00904         Diff2D(1, -1), Diff2D(0, 0), Diff2D(-1, -1), Diff2D(0, -2)};
00905     static const Diff2D bottomdist[] = {
00906         Diff2D(1, 1), Diff2D(0, 2), Diff2D(-1, 1), Diff2D(0, 0)};
00907 
00908     int i;
00909 
00910     SrcIterator sy = sul + Diff2D(0,1);
00911     SrcIterator sx;
00912 
00913     // close 1-pixel wide gaps (x-direction)
00914     for(y=0; y<h; ++y, sy.y+=2)
00915     {
00916         sx = sy + Diff2D(2,0);
00917 
00918         for(x=2; x<w; ++x, sx.x+=2)
00919         {
00920             if(sa(sx) == edge_marker) continue;
00921 
00922             if(sa(sx, left) != edge_marker) continue;
00923             if(sa(sx, right) != edge_marker) continue;
00924 
00925             count1 = 0;
00926             count2 = 0;
00927             count3 = 0;
00928 
00929             for(i=0; i<4; ++i)
00930             {
00931                 if(sa(sx, leftdist[i]) == edge_marker)
00932                 {
00933                     ++count1;
00934                     count3 ^= 1 << i;
00935                 }
00936                 if(sa(sx, rightdist[i]) == edge_marker)
00937                 {
00938                     ++count2;
00939                     count3 ^= 1 << i;
00940                 }
00941             }
00942 
00943             if(count1 <= 1 || count2 <= 1 || count3 == 15)
00944             {
00945                 sa.set(edge_marker, sx);
00946             }
00947         }
00948    }
00949 
00950     sy = sul + Diff2D(1,2);
00951 
00952     // close 1-pixel wide gaps (y-direction)
00953     for(y=2; y<h; ++y, sy.y+=2)
00954     {
00955         sx = sy;
00956 
00957         for(x=0; x<w; ++x, sx.x+=2)
00958         {
00959             if(sa(sx) == edge_marker) continue;
00960 
00961             if(sa(sx, top) != edge_marker) continue;
00962             if(sa(sx, bottom) != edge_marker) continue;
00963 
00964             count1 = 0;
00965             count2 = 0;
00966             count3 = 0;
00967 
00968             for(i=0; i<4; ++i)
00969             {
00970                 if(sa(sx, topdist[i]) == edge_marker)
00971                 {
00972                     ++count1;
00973                     count3 ^= 1 << i;
00974                 }
00975                 if(sa(sx, bottomdist[i]) == edge_marker)
00976                 {
00977                     ++count2;
00978                     count3 ^= 1 << i;
00979                 }
00980             }
00981 
00982             if(count1 <= 1 || count2 <= 1 || count3 == 15)
00983             {
00984                 sa.set(edge_marker, sx);
00985             }
00986         }
00987     }
00988 }
00989 
00990 template <class SrcIterator, class SrcAccessor, class SrcValue>
00991 inline
00992 void closeGapsInCrackEdgeImage(
00993            triple<SrcIterator, SrcIterator, SrcAccessor> src,
00994        SrcValue edge_marker)
00995 {
00996     closeGapsInCrackEdgeImage(src.first, src.second, src.third,
00997                     edge_marker);
00998 }
00999 
01000 /********************************************************/
01001 /*                                                      */
01002 /*              beautifyCrackEdgeImage                  */
01003 /*                                                      */
01004 /********************************************************/
01005 
01095 template <class SrcIterator, class SrcAccessor, class SrcValue>
01096 void beautifyCrackEdgeImage(
01097                SrcIterator sul, SrcIterator slr, SrcAccessor sa,
01098            SrcValue edge_marker, SrcValue background_marker)
01099 {
01100     int w = (slr.x - sul.x) / 2;
01101     int h = (slr.y - sul.y) / 2;
01102     int x, y;
01103 
01104     SrcIterator sy = sul + Diff2D(1,1);
01105     SrcIterator sx;
01106 
01107     static const Diff2D right(1,0);
01108     static const Diff2D bottom(0,1);
01109     static const Diff2D left(-1,0);
01110     static const Diff2D top(0,-1);
01111 
01112     //  delete 0-cells at corners
01113     for(y=0; y<h; ++y, sy.y+=2)
01114     {
01115         sx = sy;
01116 
01117         for(x=0; x<w; ++x, sx.x+=2)
01118         {
01119             if(sa(sx) != edge_marker) continue;
01120 
01121             if(sa(sx, right) == edge_marker && sa(sx, left) == edge_marker) continue;
01122             if(sa(sx, bottom) == edge_marker && sa(sx, top) == edge_marker) continue;
01123 
01124             sa.set(background_marker, sx);
01125         }
01126     }
01127 }
01128 
01129 template <class SrcIterator, class SrcAccessor, class SrcValue>
01130 inline
01131 void beautifyCrackEdgeImage(
01132            triple<SrcIterator, SrcIterator, SrcAccessor> src,
01133            SrcValue edge_marker, SrcValue background_marker)
01134 {
01135     beautifyCrackEdgeImage(src.first, src.second, src.third,
01136                     edge_marker, background_marker);
01137 }
01138 
01139 
01142 class Edgel
01143 {
01144   public:
01147     float x;
01148 
01151     float y;
01152 
01155     float strength;
01156 
01186     float orientation;
01187 
01188     Edgel()
01189     : x(0.0f), y(0.0f), strength(0.0f), orientation(0.0f)
01190     {}
01191 
01192     Edgel(float ix, float iy, float is, float io)
01193     : x(ix), y(iy), strength(is), orientation(io)
01194     {}
01195 };
01196 
01197 template <class Image1, class Image2, class BackInsertable>
01198 void internalCannyFindEdgels(Image1 const & gx,
01199                              Image1 const & gy,
01200                              Image2 const & magnitude,
01201                              BackInsertable & edgels, std::vector<int> p)
01202 {
01203 
01204     typedef typename Image1::value_type PixelType;
01205     double t = 0.5 / VIGRA_CSTD::sin(M_PI/8.0);
01206 
01207     //last element in edgel list is edgel that holds orientation
01208     //of interest point
01209 
01210     //orientation assignment
01211     std::vector<int > point= p;
01212 
01213     PixelType gradx = gx(p[0],p[1]);
01214     PixelType grady = gy(p[0],p[1]);
01215 
01216     double orientation = VIGRA_CSTD::atan2(-grady, gradx) - M_PI * 1.5;
01217     if(orientation < 0.0)
01218         orientation += 2.0*M_PI;
01219     Edgel edgel1;
01220     edgel1.orientation=orientation;
01221     edgels.push_back(edgel1);
01222     //EOF orientation assignment
01223 
01224     for(int y=1; y<gx.height()-1; ++y)
01225     {
01226         for(int x=1; x<gx.width()-1; ++x)
01227         {
01228             gradx = gx(x,y);
01229             grady = gy(x,y);
01230             double mag = magnitude(x, y);
01231 
01232             int dx = (int)VIGRA_CSTD::floor(gradx*t/mag + 0.5);
01233             int dy = (int)VIGRA_CSTD::floor(grady*t/mag + 0.5);
01234 
01235             int x1 = x - dx,
01236                 x2 = x + dx,
01237                 y1 = y - dy,
01238                 y2 = y + dy;
01239 
01240             PixelType m1 = magnitude(x1, y1);
01241             PixelType m3 = magnitude(x2, y2);
01242 
01243             if(m1 < mag && m3 <= mag)
01244             {
01245                 Edgel edgel;
01246 
01247                 // local maximum => quadratic interpolation of sub-pixel location
01248                 PixelType del = (m1 - m3) / 2.0 / (m1 + m3 - 2.0*mag);
01249                 edgel.x = x + dx*del;
01250                 edgel.y = y + dy*del;
01251                 edgel.strength = mag;
01252                 orientation = VIGRA_CSTD::atan2(-grady, gradx) - M_PI * 1.5;
01253                 if(orientation < 0.0)
01254                     orientation += 2.0*M_PI;
01255                 edgel.orientation = orientation;
01256                 edgels.push_back(edgel);
01257             }
01258         }
01259     }
01260 }
01261 
01262 /********************************************************/
01263 /*                                                      */
01264 /*                      cannyEdgelList                  */
01265 /*                                                      */
01266 /********************************************************/
01267 
01339 template <class SrcIterator, class SrcAccessor, class BackInsertable>
01340 void cannyEdgelList(SrcIterator ul, SrcIterator lr, SrcAccessor src,
01341                         BackInsertable & edgels, double scale, std::vector<int> p)
01342 {
01343     int w = lr.x - ul.x;
01344     int h = lr.y - ul.y;
01345 
01346     // calculate image gradients
01347     typedef typename
01348         NumericTraits<typename SrcAccessor::value_type>::RealPromote
01349         TmpType;
01350 
01351     BasicImage<TmpType> tmp(w,h), dx(w,h), dy(w,h);
01352 
01353     Kernel1D<double> smooth, grad;
01354 
01355     smooth.initGaussian(scale);
01356     grad.initGaussianDerivative(scale, 1);
01357 
01358     separableConvolveX(srcIterRange(ul, lr, src), destImage(tmp), kernel1d(grad));
01359     separableConvolveY(srcImageRange(tmp), destImage(dx), kernel1d(smooth));
01360 
01361     separableConvolveY(srcIterRange(ul, lr, src), destImage(tmp), kernel1d(grad));
01362     separableConvolveX(srcImageRange(tmp), destImage(dy), kernel1d(smooth));
01363 
01364     combineTwoImages(srcImageRange(dx), srcImage(dy), destImage(tmp),
01365                      MagnitudeFunctor<TmpType>());
01366 
01367 
01368     // find edgels
01369     internalCannyFindEdgels(dx, dy, tmp, edgels, p);
01370 }
01371 
01372 template <class SrcIterator, class SrcAccessor, class BackInsertable>
01373 inline void
01374 cannyEdgelList(triple<SrcIterator, SrcIterator, SrcAccessor> src,
01375                BackInsertable & edgels, double scale, std::vector<int> p)
01376 {
01377     cannyEdgelList(src.first, src.second, src.third, edgels, scale,p);
01378 }
01379 
01380 /********************************************************/
01381 /*                                                      */
01382 /*                       cannyEdgeImage                 */
01383 /*                                                      */
01384 /********************************************************/
01385 
01458 template <class SrcIterator, class SrcAccessor,
01459           class DestIterator, class DestAccessor,
01460           class GradValue, class DestValue>
01461 void cannyEdgeImage(
01462            SrcIterator sul, SrcIterator slr, SrcAccessor sa,
01463            DestIterator dul, DestAccessor da,
01464            double scale, GradValue gradient_threshold, DestValue edge_marker)
01465 {
01466     std::vector<Edgel> edgels;
01467 
01468     cannyEdgelList(sul, slr, sa, edgels, scale);
01469 
01470     for(unsigned int i=0; i<edgels.size(); ++i)
01471     {
01472         if(gradient_threshold < edgels[i].strength)
01473         {
01474             Diff2D pix((int)(edgels[i].x + 0.5), (int)(edgels[i].y + 0.5));
01475 
01476             da.set(edge_marker, dul, pix);
01477         }
01478     }
01479 }
01480 
01481 template <class SrcIterator, class SrcAccessor,
01482           class DestIterator, class DestAccessor,
01483           class GradValue, class DestValue>
01484 inline void cannyEdgeImage(
01485            triple<SrcIterator, SrcIterator, SrcAccessor> src,
01486            pair<DestIterator, DestAccessor> dest,
01487            double scale, GradValue gradient_threshold, DestValue edge_marker)
01488 {
01489     cannyEdgeImage(src.first, src.second, src.third,
01490                    dest.first, dest.second,
01491                    scale, gradient_threshold, edge_marker);
01492 }
01493 
01494 /********************************************************/
01495 
01496 namespace detail {
01497 
01498 template <class DestIterator>
01499 int neighborhoodConfiguration(DestIterator dul)
01500 {
01501     int v = 0;
01502     NeighborhoodCirculator<DestIterator, EightNeighborCode> c(dul, EightNeighborCode::SouthEast);
01503     for(int i=0; i<8; ++i, --c)
01504     {
01505         v = (v << 1) | ((*c != 0) ? 1 : 0);
01506     }
01507 
01508     return v;
01509 }
01510 
01511 template <class GradValue>
01512 struct SimplePoint
01513 {
01514     Diff2D point;
01515     GradValue grad;
01516 
01517     SimplePoint(Diff2D const & p, GradValue g)
01518     : point(p), grad(g)
01519     {}
01520 
01521     bool operator<(SimplePoint const & o) const
01522     {
01523         return grad < o.grad;
01524     }
01525 
01526     bool operator>(SimplePoint const & o) const
01527     {
01528         return grad > o.grad;
01529     }
01530 };
01531 
01532 template <class SrcIterator, class SrcAccessor,
01533           class DestIterator, class DestAccessor,
01534           class GradValue, class DestValue>
01535 void cannyEdgeImageFromGrad(
01536            SrcIterator sul, SrcIterator slr, SrcAccessor grad,
01537            DestIterator dul, DestAccessor da,
01538            GradValue gradient_threshold, DestValue edge_marker)
01539 {
01540     typedef typename SrcAccessor::value_type PixelType;
01541     typedef typename NormTraits<PixelType>::SquaredNormType NormType;
01542 
01543     NormType zero = NumericTraits<NormType>::zero();
01544     double tan22_5 = M_SQRT2 - 1.0;
01545     typename NormTraits<GradValue>::SquaredNormType g2thresh = squaredNorm(gradient_threshold);
01546 
01547     int w = slr.x - sul.x;
01548     int h = slr.y - sul.y;
01549 
01550     sul += Diff2D(1,1);
01551     dul += Diff2D(1,1);
01552     Diff2D p(0,0);
01553 
01554     for(int y = 1; y < h-1; ++y, ++sul.y, ++dul.y)
01555     {
01556         SrcIterator sx = sul;
01557         DestIterator dx = dul;
01558         for(int x = 1; x < w-1; ++x, ++sx.x, ++dx.x)
01559         {
01560             PixelType g = grad(sx);
01561             NormType g2n = squaredNorm(g);
01562             if(g2n < g2thresh)
01563                 continue;
01564 
01565             NormType g2n1, g2n3;
01566             // find out quadrant
01567             if(abs(g[1]) < tan22_5*abs(g[0]))
01568             {
01569                 // north-south edge
01570                 g2n1 = squaredNorm(grad(sx, Diff2D(-1, 0)));
01571                 g2n3 = squaredNorm(grad(sx, Diff2D(1, 0)));
01572             }
01573             else if(abs(g[0]) < tan22_5*abs(g[1]))
01574             {
01575                 // west-east edge
01576                 g2n1 = squaredNorm(grad(sx, Diff2D(0, -1)));
01577                 g2n3 = squaredNorm(grad(sx, Diff2D(0, 1)));
01578             }
01579             else if(g[0]*g[1] < zero)
01580             {
01581                 // north-west-south-east edge
01582                 g2n1 = squaredNorm(grad(sx, Diff2D(1, -1)));
01583                 g2n3 = squaredNorm(grad(sx, Diff2D(-1, 1)));
01584             }
01585             else
01586             {
01587                 // north-east-south-west edge
01588                 g2n1 = squaredNorm(grad(sx, Diff2D(-1, -1)));
01589                 g2n3 = squaredNorm(grad(sx, Diff2D(1, 1)));
01590             }
01591 
01592             if(g2n1 < g2n && g2n3 <= g2n)
01593             {
01594                 da.set(edge_marker, dx);
01595             }
01596         }
01597     }
01598 }
01599 
01600 } // namespace detail
01601 
01602 /********************************************************/
01603 /*                                                      */
01604 /*              cannyEdgeImageWithThinning              */
01605 /*                                                      */
01606 /********************************************************/
01607 
01697 template <class SrcIterator, class SrcAccessor,
01698           class DestIterator, class DestAccessor,
01699           class GradValue, class DestValue>
01700 void cannyEdgeImageFromGradWithThinning(
01701            SrcIterator sul, SrcIterator slr, SrcAccessor sa,
01702            DestIterator dul, DestAccessor da,
01703            GradValue gradient_threshold,
01704            DestValue edge_marker, bool addBorder)
01705 {
01706     int w = slr.x - sul.x;
01707     int h = slr.y - sul.y;
01708 
01709     BImage edgeImage(w, h, BImage::value_type(0));
01710     BImage::traverser eul = edgeImage.upperLeft();
01711     BImage::Accessor ea = edgeImage.accessor();
01712     if(addBorder)
01713         initImageBorder(destImageRange(edgeImage), 1, 1);
01714     detail::cannyEdgeImageFromGrad(sul, slr, sa, eul, ea, gradient_threshold, 1);
01715 
01716     static bool isSimplePoint[256] = {
01717         0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
01718         0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
01719         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
01720         1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
01721         0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1,
01722         0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0,
01723         0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0,
01724         1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1,
01725         0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
01726         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
01727         0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
01728         0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
01729         1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
01730         0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1,
01731         1, 0, 1, 0 };
01732 
01733     eul += Diff2D(1,1);
01734     sul += Diff2D(1,1);
01735     int w2 = w-2;
01736     int h2 = h-2;
01737 
01738     typedef detail::SimplePoint<GradValue> SP;
01739     // use std::greater becaus we need the smallest gradients at the top of the queue
01740     std::priority_queue<SP, std::vector<SP>, std::greater<SP> >  pqueue;
01741 
01742     Diff2D p(0,0);
01743     for(; p.y < h2; ++p.y)
01744     {
01745         for(p.x = 0; p.x < w2; ++p.x)
01746         {
01747             BImage::traverser e = eul + p;
01748             if(*e == 0)
01749                 continue;
01750             int v = detail::neighborhoodConfiguration(e);
01751             if(isSimplePoint[v])
01752             {
01753                 pqueue.push(SP(p, norm(sa(sul+p))));
01754                 *e = 2; // remember that it is already in queue
01755             }
01756         }
01757     }
01758 
01759     static const Diff2D dist[] = { Diff2D(-1,0), Diff2D(0,-1),
01760                                    Diff2D(1,0),  Diff2D(0,1) };
01761 
01762     while(pqueue.size())
01763     {
01764         p = pqueue.top().point;
01765         pqueue.pop();
01766 
01767         BImage::traverser e = eul + p;
01768         int v = detail::neighborhoodConfiguration(e);
01769         if(!isSimplePoint[v])
01770             continue; // point may no longer be simple because its neighbors changed
01771 
01772         *e = 0; // delete simple point
01773 
01774         for(int i=0; i<4; ++i)
01775         {
01776             Diff2D pneu = p + dist[i];
01777             if(pneu.x == -1 || pneu.y == -1 || pneu.x == w2 || pneu.y == h2)
01778                 continue; // do not remove points at the border
01779 
01780             BImage::traverser eneu = eul + pneu;
01781             if(*eneu == 1) // point is boundary and not yet in the queue
01782             {
01783                 int v = detail::neighborhoodConfiguration(eneu);
01784                 if(isSimplePoint[v])
01785                 {
01786                     pqueue.push(SP(pneu, norm(sa(sul+pneu))));
01787                     *eneu = 2; // remember that it is already in queue
01788                 }
01789             }
01790         }
01791     }
01792 
01793     initImageIf(destIterRange(dul, dul+Diff2D(w,h), da),
01794                 maskImage(edgeImage), edge_marker);
01795 }
01796 
01797 template <class SrcIterator, class SrcAccessor,
01798           class DestIterator, class DestAccessor,
01799           class GradValue, class DestValue>
01800 inline void cannyEdgeImageFromGradWithThinning(
01801            triple<SrcIterator, SrcIterator, SrcAccessor> src,
01802            pair<DestIterator, DestAccessor> dest,
01803            GradValue gradient_threshold,
01804            DestValue edge_marker, bool addBorder)
01805 {
01806     cannyEdgeImageFromGradWithThinning(src.first, src.second, src.third,
01807                                dest.first, dest.second,
01808                                gradient_threshold, edge_marker, addBorder);
01809 }
01810 
01811 template <class SrcIterator, class SrcAccessor,
01812           class DestIterator, class DestAccessor,
01813           class GradValue, class DestValue>
01814 inline void cannyEdgeImageFromGradWithThinning(
01815            SrcIterator sul, SrcIterator slr, SrcAccessor sa,
01816            DestIterator dul, DestAccessor da,
01817            GradValue gradient_threshold, DestValue edge_marker)
01818 {
01819     cannyEdgeImageFromGradWithThinning(sul, slr, sa,
01820                                dul, da,
01821                                gradient_threshold, edge_marker, true);
01822 }
01823 
01824 template <class SrcIterator, class SrcAccessor,
01825           class DestIterator, class DestAccessor,
01826           class GradValue, class DestValue>
01827 inline void cannyEdgeImageFromGradWithThinning(
01828            triple<SrcIterator, SrcIterator, SrcAccessor> src,
01829            pair<DestIterator, DestAccessor> dest,
01830            GradValue gradient_threshold, DestValue edge_marker)
01831 {
01832     cannyEdgeImageFromGradWithThinning(src.first, src.second, src.third,
01833                                dest.first, dest.second,
01834                                gradient_threshold, edge_marker, true);
01835 }
01836 
01837 /********************************************************/
01838 /*                                                      */
01839 /*              cannyEdgeImageWithThinning              */
01840 /*                                                      */
01841 /********************************************************/
01842 
01915 template <class SrcIterator, class SrcAccessor,
01916           class DestIterator, class DestAccessor,
01917           class GradValue, class DestValue>
01918 void cannyEdgeImageWithThinning(
01919            SrcIterator sul, SrcIterator slr, SrcAccessor sa,
01920            DestIterator dul, DestAccessor da,
01921            double scale, GradValue gradient_threshold,
01922            DestValue edge_marker, bool addBorder)
01923 {
01924     // mark pixels that are higher than their neighbors in gradient direction
01925     typedef typename NumericTraits<typename SrcAccessor::value_type>::RealPromote TmpType;
01926     BasicImage<TinyVector<TmpType, 2> > grad(slr-sul);
01927     gaussianGradient(srcIterRange(sul, slr, sa), destImage(grad), scale);
01928     cannyEdgeImageFromGradWithThinning(srcImageRange(grad), destIter(dul, da),
01929                                gradient_threshold, edge_marker, addBorder);
01930 }
01931 
01932 template <class SrcIterator, class SrcAccessor,
01933           class DestIterator, class DestAccessor,
01934           class GradValue, class DestValue>
01935 inline void cannyEdgeImageWithThinning(
01936            triple<SrcIterator, SrcIterator, SrcAccessor> src,
01937            pair<DestIterator, DestAccessor> dest,
01938            double scale, GradValue gradient_threshold,
01939            DestValue edge_marker, bool addBorder)
01940 {
01941     cannyEdgeImageWithThinning(src.first, src.second, src.third,
01942                                dest.first, dest.second,
01943                                scale, gradient_threshold, edge_marker, addBorder);
01944 }
01945 
01946 template <class SrcIterator, class SrcAccessor,
01947           class DestIterator, class DestAccessor,
01948           class GradValue, class DestValue>
01949 inline void cannyEdgeImageWithThinning(
01950            SrcIterator sul, SrcIterator slr, SrcAccessor sa,
01951            DestIterator dul, DestAccessor da,
01952            double scale, GradValue gradient_threshold, DestValue edge_marker)
01953 {
01954     cannyEdgeImageWithThinning(sul, slr, sa,
01955                                dul, da,
01956                                scale, gradient_threshold, edge_marker, true);
01957 }
01958 
01959 template <class SrcIterator, class SrcAccessor,
01960           class DestIterator, class DestAccessor,
01961           class GradValue, class DestValue>
01962 inline void cannyEdgeImageWithThinning(
01963            triple<SrcIterator, SrcIterator, SrcAccessor> src,
01964            pair<DestIterator, DestAccessor> dest,
01965            double scale, GradValue gradient_threshold, DestValue edge_marker)
01966 {
01967     cannyEdgeImageWithThinning(src.first, src.second, src.third,
01968                                dest.first, dest.second,
01969                                scale, gradient_threshold, edge_marker, true);
01970 }
01971 
01972 /********************************************************/
01973 
01974 template <class Image1, class Image2, class BackInsertable>
01975 void internalCannyFindEdgels3x3(Image1 const & grad,
01976                                 Image2 const & mask,
01977                                 BackInsertable & edgels)
01978 {
01979     typedef typename Image1::value_type PixelType;
01980     typedef typename PixelType::value_type ValueType;
01981 
01982     for(int y=1; y<grad.height()-1; ++y)
01983     {
01984         for(int x=1; x<grad.width()-1; ++x)
01985         {
01986             if(!mask(x,y))
01987                 continue;
01988 
01989             ValueType gradx = grad(x,y)[0];
01990             ValueType grady = grad(x,y)[1];
01991             double mag = hypot(gradx, grady),
01992                    c = gradx / mag,
01993                    s = grady / mag;
01994 
01995             Matrix<double> ml(3,3), mr(3,1), l(3,1), r(3,1);
01996             l(0,0) = 1.0;
01997 
01998             for(int yy = -1; yy <= 1; ++yy)
01999             {
02000                 for(int xx = -1; xx <= 1; ++xx)
02001                 {
02002                     double u = c*xx + s*yy;
02003                     double v = norm(grad(x+xx, y+yy));
02004                     l(1,0) = u;
02005                     l(2,0) = u*u;
02006                     ml += outer(l);
02007                     mr += v*l;
02008                 }
02009             }
02010 
02011             linearSolve(ml, mr, r);
02012 
02013             Edgel edgel;
02014 
02015             // local maximum => quadratic interpolation of sub-pixel location
02016             ValueType del = -r(1,0) / 2.0 / r(2,0);
02017             edgel.x = x + c*del;
02018             edgel.y = y + s*del;
02019             edgel.strength = mag;
02020             double orientation = VIGRA_CSTD::atan2(-grady, gradx) - M_PI * 1.5;
02021             if(orientation < 0.0)
02022                 orientation += 2.0*M_PI;
02023             edgel.orientation = orientation;
02024             edgels.push_back(edgel);
02025         }
02026     }
02027 }
02028 
02029 
02030 /********************************************************/
02031 /*                                                      */
02032 /*                   cannyEdgelList3x3                  */
02033 /*                                                      */
02034 /********************************************************/
02035 
02103 template <class SrcIterator, class SrcAccessor, class BackInsertable>
02104 void cannyEdgelList3x3(SrcIterator ul, SrcIterator lr, SrcAccessor src,
02105                         BackInsertable & edgels, double scale)
02106 {
02107     int w = lr.x - ul.x;
02108     int h = lr.y - ul.y;
02109 
02110     typedef typename NumericTraits<typename SrcAccessor::value_type>::RealPromote TmpType;
02111     BasicImage<TinyVector<TmpType, 2> > grad(lr-ul);
02112     gaussianGradient(srcIterRange(ul, lr, src), destImage(grad), scale);
02113 
02114     UInt8Image edges(lr-ul);
02115     cannyEdgeImageFromGradWithThinning(srcImageRange(grad), destImage(edges),
02116                                        0.0, 1, false);
02117 
02118     // find edgels
02119     internalCannyFindEdgels3x3(grad, edges, edgels);
02120 }
02121 
02122 template <class SrcIterator, class SrcAccessor, class BackInsertable>
02123 inline void
02124 cannyEdgelList3x3(triple<SrcIterator, SrcIterator, SrcAccessor> src,
02125                BackInsertable & edgels, double scale)
02126 {
02127     cannyEdgelList3x3(src.first, src.second, src.third, edgels, scale);
02128 }
02129 
02130 
02131 
02133 
02179 } // namespace vigra
02180 
02181 #endif // VIGRA_EDGEDETECTION_HXX

Generated on Mon Sep 20 01:01:27 2010 for Hugintrunk by doxygen 1.3.9.1