00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #ifndef VIGRA_EDGEDETECTION_HXX
00040 #define VIGRA_EDGEDETECTION_HXX
00041
00042 #include <vector>
00043 #include <queue>
00044 #include <cmath>
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
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
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
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
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
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
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
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
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
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
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
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
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
01208
01209
01210
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
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
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
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
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
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
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
01567 if(abs(g[1]) < tan22_5*abs(g[0]))
01568 {
01569
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
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
01582 g2n1 = squaredNorm(grad(sx, Diff2D(1, -1)));
01583 g2n3 = squaredNorm(grad(sx, Diff2D(-1, 1)));
01584 }
01585 else
01586 {
01587
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 }
01601
01602
01603
01604
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
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;
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;
01771
01772 *e = 0;
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;
01779
01780 BImage::traverser eneu = eul + pneu;
01781 if(*eneu == 1)
01782 {
01783 int v = detail::neighborhoodConfiguration(eneu);
01784 if(isSimplePoint[v])
01785 {
01786 pqueue.push(SP(pneu, norm(sa(sul+pneu))));
01787 *eneu = 2;
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
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
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
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
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
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 }
02180
02181 #endif // VIGRA_EDGEDETECTION_HXX