contrib/oxl/osl/osl_edge_detector.cxx
Go to the documentation of this file.
00001 // This is oxl/osl/osl_edge_detector.cxx
00002 #include "osl_edge_detector.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_cmath.h>
00007 #include <vcl_cstdlib.h>
00008 #include <vcl_cassert.h>
00009 #include <vcl_list.h>
00010 #include <vcl_iostream.h>
00011 #include <vcl_algorithm.h>
00012 
00013 #include <osl/osl_canny_base.h>
00014 #include <osl/osl_kernel.h>
00015 #include <osl/osl_canny_smooth.h>
00016 #include <osl/osl_canny_gradient.h>
00017 #include <osl/osl_chamfer.h>
00018 #include <osl/internals/osl_reorder_chain.h>
00019 #include <vnl/vnl_math.h>
00020 
00021 const float DUMMYTHETA = 10000.0f;
00022 const float ALPHA = 0.9f;
00023 
00024 #define FAR 65535
00025 
00026 #define Make_float_image(w, h) osl_canny_base_make_raw_image(w, h, (float*)0)
00027 #define Make_int_image(w, h) osl_canny_base_make_raw_image(w, h, (int*)0)
00028 #define Free_float_image(i,dummy) osl_canny_base_free_raw_image(i)
00029 #define Free_int_image(i,dummy) osl_canny_base_free_raw_image(i)
00030 
00031 //-----------------------------------------------------------------------
00032 
00033 osl_edge_detector::osl_edge_detector(osl_edge_detector_params const &params)
00034   : osl_edge_detector_params(params)
00035 {
00036   //Set up histogram stuff -  old style maintained for compatability
00037   gradient_histogram_ = false; //Do we need to compute a histogram?
00038   histogram_resolution_ = 15; // The number of buckets
00039 
00040  width_ = int(sigma_*vcl_sqrt(2*vcl_log(1.0/gauss_tail_))+1); // round up to int
00041   k_size_ = 2*width_+ 1;
00042   kernel_ = new float[k_size_];
00043   max_gradient_ = low_;
00044   xjunc_ = new vcl_list<int>;
00045   yjunc_ = new vcl_list<int>;
00046   vlist_ = new vcl_list<osl_Vertex*>;
00047 
00048   jval_ = 2000.0;
00049 
00050   vertidcount_ = 0;
00051 }
00052 
00053 //-----------------------------------------------------------------------------
00054 
00055 osl_edge_detector::~osl_edge_detector()
00056 {
00057   Free_float_image(dx_,xsize_);
00058   Free_float_image(dy_,xsize_);
00059   Free_float_image(grad_,xsize_);
00060 
00061   Free_float_image(thin_,xsize_);
00062   Free_float_image(theta_,xsize_);
00063   Free_float_image(thresh_,xsize_);
00064 
00065   Free_int_image(dist_,xsize_);
00066   Free_int_image(jx_,xsize_);
00067   Free_int_image(jy_,xsize_);
00068   Free_int_image(junction_,xsize_);
00069 
00070   //no point vlist_->clear();
00071   delete vlist_;
00072   delete [] kernel_;
00073   delete xjunc_;
00074   delete yjunc_;
00075 }
00076 
00077 
00078 //-----------------------------------------------------------------------------
00079 
00080 void osl_edge_detector::detect_edges(vil1_image const &image,
00081                                      vcl_list<osl_edge*> *edges,
00082                                      bool maintain_topology)
00083 {
00084   assert(edges!=0);
00085 
00086   //
00087   xsize_ = image.height();
00088   ysize_ = image.width();
00089   xstart_ = 0;
00090   ystart_ = 0;
00091 
00092   //vcl_cerr << "xstart_ = " << xstart_ << " ystart_ = " << ystart_ << vcl_endl
00093   //         << "xsize_ = " << xsize_ << " ysize_ = " << ysize_ << vcl_endl;
00094 
00095   dx_ = Make_float_image(xsize_,ysize_);
00096   dy_ = Make_float_image(xsize_,ysize_);
00097   grad_ = Make_float_image(xsize_,ysize_);
00098   smooth_ = Make_float_image(xsize_,ysize_);
00099 
00100   thin_ = Make_float_image(xsize_,ysize_);
00101   thresh_ = Make_float_image(xsize_,ysize_);
00102   theta_ = Make_float_image(xsize_,ysize_);
00103 
00104   dist_ = Make_int_image(xsize_,ysize_);
00105   junction_ = Make_int_image(xsize_,ysize_);
00106   jx_ = Make_int_image(xsize_,ysize_);
00107   jy_ = Make_int_image(xsize_,ysize_);
00108 
00109   if (verbose_)
00110     vcl_cerr << "Doing canny on image region "
00111              << xsize_ << " by " << ysize_ << vcl_endl
00112              << "Gaussian tail = " << gauss_tail_ << vcl_endl
00113              << "Sigma         = " << sigma_ << vcl_endl
00114              << "Kernel size   = " << k_size_ << vcl_endl
00115              << "Threshold     = " << low_ << vcl_endl;
00116 
00117   if (verbose_)
00118     vcl_cerr << "setting convolution kernel and zeroing images\n";
00119   osl_kernel_DOG(sigma_, kernel_, k_size_, width_);
00120 
00121   osl_canny_base_fill_raw_image(thin_, xsize_, ysize_, 0.0f);
00122   osl_canny_base_fill_raw_image(thresh_, xsize_, ysize_, low_);
00123   osl_canny_base_fill_raw_image(theta_, xsize_, ysize_, DUMMYTHETA);
00124 
00125   // A suitably large value (FAR) - perhaps should be more. This represents the
00126   // distance of a point to the nearest edgel
00127   osl_canny_base_fill_raw_image(dist_, xsize_, ysize_, FAR);
00128 
00129   // Do the traditional Canny parts, and use non-maximal suppression to
00130   // set the thresholds.
00131   if (verbose_) vcl_cerr << "smoothing the image\n";
00132   osl_canny_smooth_rothwell(image, kernel_, width_, k_size_, smooth_);
00133 
00134   if (verbose_)
00135     vcl_cerr << "computing x,y derivatives and norm of gradient\n";
00136   osl_canny_gradient(xsize_, ysize_, smooth_, dx_, dy_, grad_);
00137 
00138   if (verbose_)
00139     vcl_cerr << "doing sub-pixel interpolation\n";
00140   Sub_pixel_interpolation();
00141 
00142   if (verbose_) vcl_cerr << "assigning thresholds\n";
00143   Set_thresholds(); // ghist_ is computed here
00144 
00145   // If we don't want to maintain the strict measurement of the topology
00146   // (ie. we want to stop the junction regions becoming too extensive), we
00147   // fill in single pixel holes in the edge description.
00148   if ( !maintain_topology ) {
00149     vcl_cerr << "Filling holes\n";
00150     Fill_holes();
00151   }
00152 
00153   // Thin the edge image, though keep the original thick one
00154   if (verbose_) vcl_cerr << "thinning edges\n";
00155   Thin_edges();
00156 
00157   // Locate junctions in the edge image and joint the clusters together
00158   // as we have no confidence in the geometry around them.
00159   if (verbose_)
00160     vcl_cerr << "locating junctions in the edge image - ";
00161   Find_junctions();
00162   if (verbose_)
00163     vcl_cerr << xjunc_->size() << " junctions found\n";
00164 
00165   Find_junction_clusters();
00166   if (verbose_)
00167     vcl_cerr << vlist_->size() << " junction clusters found\n";
00168 
00169   // Finally do edge following to extract the edge data from the thin_ image
00170   if (verbose_) vcl_cerr << "doing final edge following\n";
00171   Follow_curves(edges);
00172 
00173   if (verbose_) vcl_cerr << "finished osl_edge_detector\n";
00174 }
00175 
00176 //-----------------------------------------------------------------------------
00177 //:
00178 // A procedure that performs sub-pixel interpolation for all edges greater
00179 // than the threshold by parabolic fitting. Writes edges into the thresh_ image
00180 // if they are maxima and above low_. This gives a good indication of the local
00181 // edge strengths. Stores sub-pixel positions in dx_ and dy_, and set the
00182 // orientations in theta_.
00183 //
00184 void osl_edge_detector::Sub_pixel_interpolation()
00185 {
00186   float h1=0.0,h2=0.0; // dummy initialisation values
00187   float k = float(vnl_math::deg_per_rad);
00188   int orient;
00189   float theta,grad;
00190   float fraction,dnewx=0.0,dnewy=0.0; // dummy initialisation values
00191 
00192   // Add 1 to get rid of border effects.
00193   for (unsigned int x=width_+1; x+width_+1<xsize_; ++x)
00194   {
00195     float *g0 = grad_[x-1];
00196     float *g1 = grad_[x];
00197     float *g2 = grad_[x+1];
00198     float *dx = dx_[x];
00199     float *dy = dy_[x];
00200 
00201     for (unsigned int y=width_+1; y+width_+1<ysize_; ++y)
00202     {
00203       // First check that we have a potential edge
00204       if ( g1[y] > low_ ) {
00205         theta = k*(float)vcl_atan2(dy[y],dx[y]);
00206 
00207         // Now work out which direction wrt the eight-way
00208         // neighbours the edge normal points
00209         if ( theta >= 0.0 )
00210           orient = int(theta/45.0);
00211         else
00212           orient = int(theta/45.0+4);
00213         // if theta == 180.0 we will have orient = 4
00214         orient = orient%4;
00215 
00216         // And now compute the interpolated heights
00217         switch ( orient ) {
00218         case 0:
00219           grad = dy[y]/dx[y];
00220           h1 = grad*g0[y-1] + (1 - grad)*g0[y];
00221           h2 = grad*g2[y+1] + (1 - grad)*g2[y];
00222           break;
00223 
00224         case 1:
00225           grad = dx[y]/dy[y];
00226           h1 = grad*g0[y-1] + (1 - grad)*g1[y-1];
00227           h2 = grad*g2[y+1] + (1 - grad)*g1[y+1];
00228           break;
00229 
00230         case 2:
00231           grad = -dx[y]/dy[y];
00232           h1 = grad*g2[y-1] + (1 - grad)*g1[y-1];
00233           h2 = grad*g0[y+1] + (1 - grad)*g1[y+1];
00234           break;
00235 
00236         case 3:
00237           grad = -dy[y]/dx[y];
00238           h1 = grad*g2[y-1] + (1 - grad)*g2[y];
00239           h2 = grad*g0[y+1] + (1 - grad)*g0[y];
00240           break;
00241 
00242         default:
00243           vcl_abort();
00244           //vcl_cerr << "*** ERROR ON SWITCH IN NMS ***\n";
00245         }
00246 
00247         // Do subpixel interpolation by fitting a parabola
00248         // along the NMS line and finding its peak
00249         fraction = (h1-h2)/(2*(h1-2*g1[y]+h2));
00250         switch ( orient ) {
00251         case 0:
00252           dnewx = fraction;
00253           dnewy = dy[y]/dx[y]*fraction;
00254           break;
00255 
00256         case 1:
00257           dnewx = dx[y]/dy[y]*fraction;
00258           dnewy = fraction;
00259           break;
00260 
00261         case 2:
00262           dnewx = dx[y]/dy[y]*fraction;
00263           dnewy = fraction;
00264           break;
00265 
00266         case 3:
00267           dnewx = - fraction;
00268           dnewy = - dy[y]/dx[y]*fraction;
00269           break;
00270 
00271         default:
00272           vcl_abort();
00273           //vcl_cerr << "*** ERROR ON SWITCH IN NMS ***\n";
00274         }
00275 
00276         // Now store the edge data, re-use dx_[][] and dy_[][]
00277         // for sub-pixel locations (don't worry about the junk
00278         // that is already in them). Use any edgels that get
00279         // non-maximal suppression to bootstrap the image
00280         // thresholds. The >= is used rather than > for reasons
00281         // involving non-generic images. Should this be interpolated
00282         // height  = g1[y] + frac*(h2-h1)/4 ?
00283         if ( g1[y]>=h1 && g1[y]>=h2 && vcl_fabs(dnewx)<=0.5 && vcl_fabs(dnewy)<=0.5 )
00284         {
00285           if ( g1[y]*ALPHA > low_ )
00286             thresh_[x][y] = ALPHA * g1[y]; // Use an ALPHA% bound
00287           // thresh_ image starts off as being equal to low_
00288           // else
00289           //   thresh_[x][y] = low_;
00290           Thicken_threshold(x,y);
00291         }
00292 
00293         // + 0.5 is to account for targetjr display offset
00294         if ( (vcl_fabs(dnewx)<=0.5) && (vcl_fabs(dnewy)<=0.5) ) {
00295           dx[y] = x + dnewx + 0.5f;
00296           dy[y] = y + dnewy + 0.5f;
00297         }
00298         else {
00299           dx[y] = x + 0.5f;
00300           dy[y] = y + 0.5f;
00301         }
00302         theta_[x][y] = theta;
00303       }
00304       // For consistency assign these values even though the
00305       // edge is below strength.
00306       else {
00307         dx[y] = x + 0.5f;
00308         dy[y] = y + 0.5f;
00309       }
00310     }
00311   }
00312 
00313   // Clean up around the border to ensure consistency in the dx_ and dy_ values.
00314   for (unsigned int x=0; x<xsize_; ++x) {
00315     for (unsigned int y=0; y<=width_; ++y) {
00316       dx_[x][y] = x + 0.5f;
00317       dy_[x][y] = y + 0.5f;
00318     }
00319     for (int y=ysize_-width_-1; y<int(ysize_); ++y)
00320     {
00321       dx_[x][y] = x + 0.5f;
00322       dy_[x][y] = y + 0.5f;
00323     }
00324   }
00325 
00326   for (unsigned int y=width_+1; y+width_+1<ysize_; ++y)
00327   {
00328     for (unsigned int x=0; x<=width_; ++x)  {
00329       dx_[x][y] = x + 0.5f;
00330       dy_[x][y] = y + 0.5f;
00331     }
00332     for (int x=xsize_-width_-1; x<int(xsize_); ++x)
00333     {
00334       dx_[x][y] = x + 0.5f;
00335       dy_[x][y] = y + 0.5f;
00336     }
00337   }
00338 }
00339 
00340 
00341 //-----------------------------------------------------------------------------
00342 //:
00343 // Thickens the threshold image around each good pixel to take account for
00344 // the smoothing kernel (almost a dilation with a square structuring element).
00345 //
00346 void osl_edge_detector::Thicken_threshold(int x, int y)
00347 {
00348   // Experimental change 13 Apr 1995 by CAR
00349   int width = width_;
00350   //    int width = 0;
00351 
00352   for (int i=x-width; i<=x+width; ++i)
00353     for (int j=y-width; j<=y+width; ++j)  {
00354 
00355       dist_[i][j] = 0;
00356       if ( thresh_[i][j] != low_ )
00357         thresh_[i][j] = vcl_min(thresh_[x][y], thresh_[i][j]);
00358       else
00359         thresh_[i][j] = thresh_[x][y];
00360     }
00361 }
00362 
00363 
00364 //-----------------------------------------------------------------------------
00365 //:
00366 // Takes the thresh_ image that contains threshold values near to where
00367 // non-maximal suppression succeeded, and zero elsewhere, and extend the
00368 // values to all areas of the image. This is done using chamfer masks so that
00369 // the final threshold assigned at any one point (ie. a point that was
00370 // initially zero) is functionally dependent on the strengths of the
00371 // nearest good edges. At present we linearly interpolate between the two
00372 // (approximately) closest edges.
00373 //
00374 // Try to do the same process using Delauney triangulation (CAR, March 1995), in
00375 // an attempt to image the efficiency from a memory management point of view.
00376 // However, the triangulation becomes so complex that the computation time
00377 // becomes incredibly long. Therefore putting up with the Chamfer method for
00378 // the moment.
00379 //
00380 // The histogram calculation was added to support
00381 // edgel change detection-JLM May 1995
00382 //
00383 void osl_edge_detector::Set_thresholds()
00384 {
00385   int** fdist = Make_int_image(xsize_,ysize_);
00386   int** bdist = Make_int_image(xsize_,ysize_);
00387   int**a1dist = Make_int_image(xsize_,ysize_);
00388   int**a2dist = Make_int_image(xsize_,ysize_);
00389   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(int const*const*, dist_), fdist, xsize_, ysize_);
00390   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(int const*const*, dist_), bdist, xsize_, ysize_);
00391   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(int const*const*, dist_),a1dist, xsize_, ysize_);
00392   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(int const*const*, dist_),a2dist, xsize_, ysize_);
00393 
00394   float** fth = Make_float_image(xsize_,ysize_);
00395   float** bth = Make_float_image(xsize_,ysize_);
00396   float**a1th = Make_float_image(xsize_,ysize_);
00397   float**a2th = Make_float_image(xsize_,ysize_);
00398   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(float const *const*, thresh_), fth, xsize_, ysize_);
00399   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(float const *const*, thresh_), bth, xsize_, ysize_);
00400   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(float const *const*, thresh_),a1th, xsize_, ysize_);
00401   osl_canny_base_copy_raw_image(VCL_OVERLOAD_CAST(float const *const*, thresh_),a2th, xsize_, ysize_);
00402 
00403   osl_chamfer_Forward (xsize_, ysize_, fdist, fth);
00404   osl_chamfer_Backward(xsize_, ysize_, bdist, bth);
00405   osl_chamfer_Alt1(xsize_, ysize_, a1dist, a1th);
00406   osl_chamfer_Alt2(xsize_, ysize_, a2dist, a2th);
00407 
00408   // The range of the effect of the smoothing kernel, including the scale factor
00409   // we have ignored up to now for the chamfer masks
00410   //    int range = 3*width_;
00411   //    float max_gradient = low_;  //Commented out May 1997- JLM
00412   //Replaced by global max_gradient_
00413   for (unsigned int x=0; x<xsize_; ++x) {
00414     for (unsigned int y=0; y<ysize_; ++y) {
00415 
00416       if ( thresh_[x][y] == low_ ) {
00417 
00418         // Determine the two closest edge points.
00419         int option = osl_Minimum4(fdist[x][y],
00420                                   bdist[x][y],
00421                                   a1dist[x][y],
00422                                   a2dist[x][y]);
00423         float num=1.0f; int den=1; // dummy initialisation values
00424         switch (option) {
00425         case 1:
00426         case 2:
00427           den = fdist[x][y]+bdist[x][y];
00428           num = bdist[x][y]*fth[x][y]+fdist[x][y]*bth[x][y];
00429           break;
00430 
00431         case 3:
00432         case 4:
00433           den = a1dist[x][y]+a2dist[x][y];
00434           num = a2dist[x][y]*a1th[x][y]+a1dist[x][y]*a2th[x][y];
00435           break;
00436 
00437         default:
00438           vcl_abort();
00439           //break;
00440         }
00441         if ( den != 0.0 )
00442           thresh_[x][y] = num / den;
00443         else if ( thresh_[x][y] <= low_ )
00444           thresh_[x][y] = low_;
00445       }
00446 
00447       if ( grad_[x][y]>thresh_[x][y] ) {
00448         //changed to max_gradient_, global - May 1997 JLM
00449         if (grad_[x][y]>max_gradient_)
00450           max_gradient_=grad_[x][y];
00451         thin_[x][y] = grad_[x][y];
00452       }
00453     }
00454   }
00455   // Noticed that all gradient values are used in edgel Strength Histogram - May 1997
00456   // So defer to actual edgel chain formation.
00457 #if 0 // commented out
00458   if (gradient_histogram_)
00459   {
00460     ghist_ = new Histogram(histogram_resolution_, low_, max_gradient);
00461     for (x=0; x<xsize_; ++x)
00462       for (y=0; y<ysize_; ++y)
00463         //ghist_->UpCount(grad_[x][y]); //All Pixels (Used since 1995)
00464         ghist_->UpCount(thin_[x][y]);   //Just at edgels (First check
00465   }                                     //for significant differences)
00466 #endif
00467 
00468   Free_int_image(fdist,xsize_);
00469   Free_int_image(bdist,xsize_);
00470   Free_int_image(a1dist,xsize_);
00471   Free_int_image(a2dist,xsize_);
00472   Free_float_image(fth,xsize_);
00473   Free_float_image(bth,xsize_);
00474   Free_float_image(a1th,xsize_);
00475   Free_float_image(a2th,xsize_);
00476 }
00477 
00478 struct osl_edge_detector_xyfloat
00479 {
00480   int x;
00481   int y;
00482   float thin;
00483 };
00484 
00485 static int compare(osl_edge_detector_xyfloat* xyf1, osl_edge_detector_xyfloat* xyf2)
00486 {
00487   if (xyf1->thin < xyf2->thin)
00488     return -1;
00489   if (xyf1->thin == xyf2->thin)
00490     return 0;
00491   return 1;
00492 }
00493 
00494 
00495 //-----------------------------------------------------------------------------
00496 //:
00497 // Method to thin the image using the variation of Tsai-Fu thinning used
00498 // by Van-Duc Nguyen in Geo-Calc. This relies on computing the genus of
00499 // an edge location, and removing it if it is not a dangling chain as has
00500 // genus zero. We also order the edges by strength and try to remove the weaker
00501 // ones first. This accounts for non-maximal suppression, and does it in a
00502 // topology preserving way. Note that we are creating a vcl_list with a large
00503 // number of elements, and then sorting it - this is likely to be quite slow.
00504 // An alternative implementation would be better.
00505 //
00506 void osl_edge_detector::Thin_edges()
00507 {
00508   // Find all of the edgels with a strength > low_
00509   bool do_output = true;
00510 
00511   vcl_cerr << __FILE__ ": Fast Sort\n";
00512   osl_edge_detector_xyfloat* edgel_array = new osl_edge_detector_xyfloat[xsize_ * ysize_];
00513   int count = 1;     // count set to dummy, nonzero value
00514   while ( count!=0 ) //  Thin until no Pixels are removed
00515   {
00516     count = 0;
00517     int edgel_array_len = 0;
00518     for (unsigned int x=width_; x+width_<xsize_; ++x)
00519       for (unsigned int y=width_; y+width_<ysize_; ++y)
00520         if ( thin_[x][y] > thresh_[x][y] )
00521         {
00522           edgel_array[edgel_array_len].x = x;
00523           edgel_array[edgel_array_len].y = y;
00524           edgel_array[edgel_array_len].thin = thin_[x][y];
00525           edgel_array_len++;
00526         }
00527 
00528     // Now sort the list; this could be slow if we have a lot of potential.
00529     // edges - surely we have to do number of elements (not -1)?
00530     //      qsort(edgel_array, edgel_array_len-1, sizeof(osl_edge_detector_xyfloat), &compare);
00531     vcl_qsort(edgel_array,
00532               edgel_array_len,
00533               sizeof(osl_edge_detector_xyfloat),
00534               (int (*)(const void *, const void *))&compare);
00535 
00536     // To assist in setting the thresholds:
00537     if (  do_output && (edgel_array_len > 0) ) {
00538 
00539       vcl_cerr << "edgel strengths range from "
00540                << edgel_array[0].thin << " to "
00541                << edgel_array[edgel_array_len-1].thin << vcl_endl;
00542       do_output = false;
00543     }
00544 
00545     // Do the thinning taking the weakest edges first and works
00546     // up through the list strengthwise.
00547     for (int pos=0; pos<edgel_array_len; ++pos)
00548     {
00549       int x = edgel_array[pos].x;
00550       int y = edgel_array[pos].y;
00551 
00552       int a = ( thin_[x-1][y-1] > thresh_[x-1][y-1] ) ? 1 : 0;
00553       int b = ( thin_[x  ][y-1] > thresh_[x  ][y-1] ) ? 1 : 0;
00554       int c = ( thin_[x+1][y-1] > thresh_[x+1][y-1] ) ? 1 : 0;
00555       int d = ( thin_[x+1][y  ] > thresh_[x+1][y  ] ) ? 1 : 0;
00556       int e = ( thin_[x+1][y+1] > thresh_[x+1][y+1] ) ? 1 : 0;
00557       int f = ( thin_[x  ][y+1] > thresh_[x  ][y+1] ) ? 1 : 0;
00558       int g = ( thin_[x-1][y+1] > thresh_[x-1][y+1] ) ? 1 : 0;
00559       int h = ( thin_[x-1][y  ] > thresh_[x-1][y  ] ) ? 1 : 0;
00560 
00561       int genus = a+b+c+d+e+f+g+h;
00562 
00563       // Continue if the pixel is not dangling.
00564       if ( (genus!=1) && (genus!=8) ) {
00565 
00566         genus += h*a*b+b*c*d+d*e*f+f*g*h
00567                - a*b-b*c-c*d-d*e-e*f-f*g
00568                - g*h-h*a-h*b-b*d-d*f-f*h-1;
00569 
00570         // If the genus is zero delete the edge
00571         if ( genus == 0 ) {
00572           count++;
00573           thin_[x][y] = 0.0;
00574         }
00575       }
00576     }
00577   }
00578 
00579   delete [] edgel_array;
00580 }
00581 
00582 
00583 //-----------------------------------------------------------------------------
00584 //:
00585 // Finds all pixels that are surrounded by four edgels, but which are
00586 // themselves not edgels. These `holes' cause the construction of complex
00587 // topological descriptions. To simplify matters, we raise the thin_ value
00588 // of the central pixel and so force it to be an edgel.
00589 //
00590 void osl_edge_detector::Fill_holes()
00591 {
00592   // Find all of the edgels with a strength <= thresh_
00593   float SMALL = 0.0001f;
00594 
00595   for (unsigned int x=width_; x+width_<xsize_; ++x)
00596     for (unsigned int y=width_; y+width_<ysize_; ++y)
00597       if ( thin_[x][y] <= thresh_[x][y] )
00598       {
00599         int count = 0;
00600         if ( thin_[x  ][y-1] > thresh_[x  ][y-1] ) count++;
00601         if ( thin_[x  ][y+1] > thresh_[x  ][y+1] ) count++;
00602         if ( thin_[x+1][y  ] > thresh_[x+1][y  ] ) count++;
00603         if ( thin_[x-1][y  ] > thresh_[x-1][y  ] ) count++;
00604 
00605         if ( count == 4 )
00606           thin_[x][y] = thresh_[x][y] + SMALL;
00607       }
00608 }
00609 
00610 
00611 //-----------------------------------------------------------------------------
00612 
00613 // see osl_canny_ox.cxx
00614 extern osl_Vertex *osl_find(vcl_list<osl_Vertex*> const *l, osl_Vertex const &v);
00615 
00616 //:
00617 // Follow all edgel chains that have pixel values above their corresponding
00618 // threshold values (thin_[x][y] > thresh_[x][y]).
00619 //
00620 void osl_edge_detector::Follow_curves(vcl_list<osl_edge*> *edges)
00621 {
00622   //  //Added May 1997 to restrict histogram to actual detected edgels -JLM
00623   //  if (gradient_histogram_)
00624   //    ghist_ = new Histogram(histogram_resolution_, low_, max_gradient_);
00625 
00626   vcl_list<int> xcoords,ycoords;
00627   vcl_list<float> grad;
00628 
00629   chain_no_ = 10;  // Must be set to a number >= 1
00630 
00631   // Find an edgel point and start to follow it.
00632   edges->clear();
00633   for (unsigned int x=width_; x+width_<xsize_; ++x)
00634   {
00635     float *thin = thin_[x];
00636     for (unsigned int y=width_; y+width_<ysize_; ++y)
00637     {
00638       if ( (thin[y]<=thresh_[x][y]) || junction_[x][y] )
00639         continue;
00640 
00641       // Set the following variable [what a pointless comment]
00642       chain_no_++;
00643 
00644       // clear lists before following
00645       xcoords.clear();
00646       ycoords.clear();
00647       grad.clear();
00648 
00649       Follow(x,y,&xcoords,&ycoords,&grad,0);
00650 
00651       // We may have picked up the edgel chain somewhere
00652       // away from its ends. Therefore, reverse the list
00653       // and try to follow again.
00654       xcoords.reverse();  ycoords.reverse();  grad.reverse();
00655       Follow(x,y,&xcoords,&ycoords,&grad,1);
00656 
00657       // Check that we have at least two endpoints to
00658       // the list, otherwise go to next loop
00659       if ( xcoords.size() < 2 )
00660         // vcl_cerr << "short list found in Final_follow\n";
00661         continue;
00662 
00663       int count=0; // isn't this just "count = grad.size()" ?
00664       for (vcl_list<float>::iterator i=grad.begin(); i!=grad.end(); ++i)
00665         count++;
00666 
00667       // If the count is less than two we cannot accept
00668       // the edgelchain. Smallest chain must either be `ee',
00669       // `je', `ej' or `jj' (for `e'dge and `j'unction)
00670       if ( count < 2 )
00671         continue;
00672 
00673       // Create an osl_edgel_chain
00674       osl_edgel_chain *dc = new osl_edgel_chain(count);
00675       float *px = dc->GetX();
00676       float *py = dc->GetY();
00677       float *pg = dc->GetGrad();
00678       float *pt = dc->GetTheta();
00679 
00680       // Write the points to the osl_edgel_chain
00681       while (count) {
00682         int tmpx = xcoords.front(); xcoords.pop_front();
00683         int tmpy = ycoords.front(); ycoords.pop_front();
00684         float val = grad.front(); grad.pop_front();
00685         count--;
00686 
00687         // If we are not at a junction use sub-pixel value.
00688         if ( val != jval_ ) {
00689           *(px++) = dx_[tmpx][tmpy] + xstart_;
00690           *(py++) = dy_[tmpx][tmpy] + ystart_;
00691           *(pg++) = val;
00692           //     if (ghist_)
00693           //       ghist_->UpCount(val); //Added edgel histogram here -May 1997
00694         }
00695         else {
00696           *(px++) = float(tmpx + xstart_);
00697           *(py++) = float(tmpy + ystart_);
00698           *(pg++) = 0.0f;   // Mark the gradient as zero at a junction
00699         }
00700         if (theta_[tmpx][tmpy] == DUMMYTHETA) {
00701           const float k = float(vnl_math::deg_per_rad);
00702           theta_[tmpx][tmpy]  = k*(float)vcl_atan2(dy_[tmpx][y],dx_[tmpx][y]);
00703         }
00704 
00705         *(pt++) = theta_[tmpx][tmpy];
00706       }
00707 
00708       // Just check whether we have created a trivial edgechain
00709       // (can happen due to the presence of dummy points)
00710       if ( (dc->size()==2) &&
00711            (dc->GetX(0)==dc->GetX(1)) &&
00712            (dc->GetY(0)==dc->GetY(1)) ) {
00713         delete dc;
00714         continue;
00715       }
00716 
00717       else if ( dc->size() > 1 ) {
00718         // Create an edge for the image topology
00719         osl_Vertex *v1 = new osl_Vertex(dc->GetX(0),dc->GetY(0));
00720         v1->SetId(vertidcount_++);
00721         osl_Vertex *v2 = new osl_Vertex(dc->GetX(dc->size()-1),dc->GetY(dc->size()-1));
00722         v2->SetId(vertidcount_++);
00723         // Check whether each vertex is a junction
00724         osl_Vertex *V1 = osl_find(vlist_, *v1);
00725         osl_Vertex *V2 = osl_find(vlist_, *v2);
00726 
00727         // If neither are junctions we may have formed a single isolated
00728         // chain that should have common vertex endpoints.
00729         int single_chain = false;
00730         if ( !V1 && !V2 ) {
00731           float dx = dc->GetX(0) - dc->GetX(dc->size()-1);
00732           float dy = dc->GetY(0) - dc->GetY(dc->size()-1);
00733           if ( dx*dx+dy*dy<4 )  { //  ie. dist < 2 pixels it is closed
00734             osl_reorder_chain(dc);
00735             osl_IUDelete(v1);
00736             osl_IUDelete(v2);
00737             v1 = new osl_Vertex(dc->GetX(0),dc->GetY(0));
00738             v1->SetId(vertidcount_++);
00739             V1 = v1;  V2 = v1;
00740             single_chain = true;
00741           }
00742         }
00743         if ( !single_chain ) {
00744           if ( !V1 )
00745             V1 = v1;
00746           else
00747           { osl_IUDelete(v1); }
00748           if ( !V2 )
00749             V2 = v2;
00750           else
00751           { osl_IUDelete(v2); }
00752         }
00753         //edge = new osl_edge(V1,V2);
00754 
00755         // Note that the edge can start and end in the same place.
00756         // However, if this is so the DigitalCurve has positive length
00757         //dc->SetStart(dc->GetX(0), dc->GetY(0));
00758         //dc->SetEnd(dc->GetX(dc->size()-1), dc->GetY(dc->size()-1));
00759 
00760         //edge->SetCurve(dc);
00761         edges->push_front(new osl_edge(*dc, V1, V2));
00762         delete dc;
00763       }
00764     }
00765   }
00766 }
00767 
00768 //-----------------------------------------------------------------------------
00769 //:
00770 // Following routine looking for connectiveness of edgel chains, and
00771 // accounts for single pixel gaps in the chains.
00772 //
00773 void osl_edge_detector::Follow(int x, int y,
00774                                vcl_list<int> *xc,
00775                                vcl_list<int> *yc,
00776                                vcl_list<float> *grad,
00777                                int reverse)
00778 {
00779   // Make sure that we do not overun the border of the image
00780   assert( x>0 && y>0 );
00781   assert( (unsigned int)x+1<xsize_ );
00782   assert( (unsigned int)y+1<ysize_ );
00783 
00784   // Add the current point to the coordinate lists, and delete from
00785   // the edge image
00786   if (!reverse) {
00787     xc->push_front(x);
00788     yc->push_front(y);
00789     grad->push_front(thin_[x][y]);
00790   }
00791   thin_[x][y] = 0.0;
00792 
00793   // The order of traversal is (x axis to the right and y axis down) :
00794   //    5 0 4
00795   //    1 * 3
00796   //    6 2 7
00797   // Have to be careful with the ; in the following macros. Don't
00798   // emacs indent this function!
00799   if (false) { }
00800 
00801   // Now recursively look for connected eight-neighbours.
00802 #define smoo(a, b) \
00803   else if ( (thin_[a][b]>thresh_[a][b]) && (junction_[a][b]==0) ) \
00804     Follow(a, b, xc,yc, grad, 0);
00805   smoo(x  , y-1)
00806   smoo(x-1, y  )
00807   smoo(x  , y+1)
00808   smoo(x+1, y  )
00809   smoo(x+1, y-1)
00810   smoo(x-1, y-1)
00811   smoo(x-1, y+1)
00812   smoo(x+1, y+1)
00813 #undef smoo
00814 
00815   // Else see if there is a junction nearby, and record it. The chain_no_
00816   // variable is used to prevent the same junction being inserted at both
00817   // ends of the edgel chains when reversal occurs next to the junction
00818   // (in that case there will only be two stored points: the edge and the junction)
00819 #define smoo(a, b) \
00820   else if ( junction_[a][b] && ((xc->size()>2) || (junction_[a][b]!=chain_no_)) ) { \
00821     xc->push_front(jx_[a][b]); \
00822     yc->push_front(jy_[a][b]); \
00823     grad->push_front(jval_); \
00824     junction_[a][b] = chain_no_; \
00825   }
00826   smoo(x  , y-1)
00827   smoo(x-1, y  )
00828   smoo(x  , y+1)
00829   smoo(x+1, y  )
00830   smoo(x+1, y-1)
00831   smoo(x-1, y-1)
00832   smoo(x-1, y+1)
00833   smoo(x+1, y+1)
00834 #undef smoo
00835   else {
00836     // ? FIXME
00837   }
00838 }
00839 
00840 
00841 //-----------------------------------------------------------------------------
00842 //
00843 //: Searches for the junctions in the image.
00844 //
00845 void osl_edge_detector::Find_junctions()
00846 {
00847   // Reset the junction variables
00848   xjunc_->clear();
00849   yjunc_->clear();
00850   osl_canny_base_fill_raw_image(junction_, xsize_, ysize_, 0);
00851 
00852   for (unsigned int x=width_; x+width_<xsize_; ++x)
00853     for (unsigned int y=width_; y+width_<ysize_; ++y)
00854     {
00855       if ( thin_[x][y] <= thresh_[x][y] )
00856         continue;
00857 
00858       int count = 0;
00859       if ( thin_[x-1][y-1] > thresh_[x-1][y-1] )  count++;
00860       if ( thin_[x  ][y-1] > thresh_[x  ][y-1] )  count++;
00861       if ( thin_[x+1][y-1] > thresh_[x+1][y-1] )  count++;
00862       if ( thin_[x+1][y  ] > thresh_[x+1][y  ] )  count++;
00863       if ( thin_[x+1][y+1] > thresh_[x+1][y+1] )  count++;
00864       if ( thin_[x  ][y+1] > thresh_[x  ][y+1] )  count++;
00865       if ( thin_[x-1][y+1] > thresh_[x-1][y+1] )  count++;
00866       if ( thin_[x-1][y  ] > thresh_[x-1][y  ] )  count++;
00867 
00868       if ( count > 2 ) {
00869         xjunc_->push_front(x);
00870         yjunc_->push_front(y);
00871         junction_[x][y] = 1;
00872       }
00873     }
00874 }
00875 
00876 
00877 //-----------------------------------------------------------------------------
00878 //
00879 //: Locate junction clusters using the following method of hysteresis.
00880 //
00881 //
00882 void osl_edge_detector::Find_junction_clusters()
00883 {
00884   vcl_list<int> xcoords,ycoords,xvertices,yvertices,xjunc,yjunc;
00885 
00886   // Find a junction and follow
00887   xvertices.clear();
00888   yvertices.clear();
00889   xjunc.clear();
00890   yjunc.clear();
00891   for (unsigned int x=width_; x+width_<xsize_; ++x)
00892   {
00893     for (unsigned int y=width_; y+width_<ysize_; ++y)
00894     {
00895       if ( junction_[x][y] )
00896       {
00897         // Each cluster is written to (xcoords,ycooords)
00898         xcoords.clear();  ycoords.clear();
00899         Follow_junctions(x,y,&xcoords,&ycoords);
00900 
00901         // Find the `centre' of the cluster. Look at the method
00902         // Cluster_centre to see how `centre' is defined.
00903         int x0, y0;
00904         Cluster_centre(xcoords,ycoords,x0,y0);
00905 
00906         // Add both the junctions and the new cluster centre to
00907         // the main lists
00908         xvertices.push_front(x0);
00909         yvertices.push_front(y0);
00910         xjunc.insert(xjunc.begin(), xcoords.begin(), xcoords.end()); //xjunc.prepend(xcoords);
00911         yjunc.insert(yjunc.begin(), ycoords.begin(), ycoords.end()); //yjunc.prepend(ycoords);
00912       }
00913     }
00914   }
00915 
00916   // Reset the junction image - this is order dependent because
00917   // the cluster centres appear in both lists
00918   // xjunc.reset();  yjunc.reset();
00919   while ( xjunc.size() ) {
00920     junction_[xjunc.front()][yjunc.front()] = 1;
00921     xjunc.pop_front();
00922     yjunc.pop_front();
00923   }
00924 
00925   // Construct the list of junction cluster centres
00926   vlist_->clear();
00927   for (vcl_list<int>::iterator i=xvertices.begin(), j=yvertices.begin();
00928        i!=xvertices.end() && j!=yvertices.end();
00929        ++i, ++j) {
00930 
00931     osl_Vertex *v = new osl_Vertex( float((*i)/*xvertices.value()*/+xstart_),
00932                                     float((*j)/*yvertices.value()*/+ystart_));
00933     vlist_->push_front(v);
00934     junction_[(*i)/*xvertices.value()*/][(*j)/*yvertices.value()*/] = 2;
00935   }
00936 
00937   xvertices.clear();
00938   yvertices.clear();
00939 }
00940 
00941 
00942 //-----------------------------------------------------------------------------
00943 //
00944 //: Following routine looking for searching out junction clusters.
00945 //
00946 void osl_edge_detector::Follow_junctions(int x, int y, vcl_list<int> *xc, vcl_list<int> *yc)
00947 {
00948   // Add the current junction to the coordinate lists, and delete from
00949   // the junction image
00950   xc->push_front(x);
00951   yc->push_front(y);
00952   junction_[x][y] = 0;
00953 
00954   // Now recursively look for connected eight-neighbours
00955   //    5 0 4
00956   //    1 * 3
00957   //    6 2 7
00958 #define smoo(a, b) \
00959   if ( junction_[a][b] ) Follow_junctions(a,b, xc,yc);
00960   smoo(x  , y-1)
00961   smoo(x-1, y  )
00962   smoo(x  , y+1)
00963   smoo(x+1, y  )
00964   smoo(x+1, y-1)
00965   smoo(x-1, y-1)
00966   smoo(x-1, y+1)
00967   smoo(x+1, y+1)
00968 #undef smoo
00969 }
00970 
00971 
00972 //-----------------------------------------------------------------------------
00973 //
00974 //: Finds which member of the lists lies closest to the centre of the list.
00975 //
00976 //
00977 void osl_edge_detector::Cluster_centre(vcl_list<int> &xc,
00978                                        vcl_list<int> &yc,
00979                                        int &x0,
00980                                        int &y0)
00981 {
00982   if ( xc.empty() )
00983     return;
00984 
00985 #if 0 // commented out
00986   // First find the CofG
00987   double x=0.0,y=0.0;
00988   for (xc.reset(),yc.reset(); xc.next(),yc.next(); )
00989   {
00990     x += xc.value();  y += yc.value();
00991   }
00992   x /= xc.size();  y /= yc.size();
00993 
00994   // Now find the point closest to the CofG
00995   float dist,newdist;
00996   dist = xsize_*ysize_; // A number larger than the image size
00997   for (xc.reset(),yc.reset(); xc.next(),yc.next(); )
00998     if ( (newdist=hypot(x-xc.value(),y-yc.value())) < dist )
00999     {
01000       x0 = xc.value();  y0 = yc.value();
01001       dist = newdist;
01002     }
01003 #endif
01004 
01005   typedef vcl_list<int>::iterator it;
01006 
01007   // Define the centre as the point with the highest gradient value.
01008   float grad = -1.0;  // Negative is smaller than the smallest norm of gradient
01009   for (it i=xc.begin(),j=yc.begin(); i!=xc.end() && j!=yc.end(); ++i, ++j)
01010     //xc.reset(),yc.reset(); xc.next(),yc.next(); )
01011     if ( grad_[(*i)/*xc.value()*/][(*j)/*yc.value()*/] > grad ) {
01012       grad = grad_[(*i)/*xc.value()*/][(*j)/*yc.value()*/];
01013       x0 = (*i);//xc.value();
01014       y0 = (*j);//yc.value();
01015     }
01016 
01017   // Set up the (jx_,jy_) arrays to point to the cluster centre
01018   for (it i=xc.begin(), j=yc.begin(); i!=xc.end() && j!=yc.end(); ++i, ++j) {
01019     //xc.reset(),yc.reset(); xc.next(),yc.next(); )  {
01020     jx_[(*i)/*xc.value()*/][(*j)/*yc.value()*/] = x0;
01021     jy_[(*i)/*xc.value()*/][(*j)/*yc.value()*/] = y0;
01022   }
01023 }
01024 
01025 //-----------------------------------------------------------------------------
01026