contrib/oxl/osl/osl_canny_ox.cxx
Go to the documentation of this file.
00001 //:
00002 //  \file
00003 
00004 #include "osl_canny_ox.h"
00005 #include <osl/osl_canny_port.h>
00006 #include <osl/osl_canny_ox_params.h>
00007 #include <osl/osl_kernel.h>
00008 #include <osl/osl_canny_smooth.h>
00009 #include <osl/osl_canny_gradient.h>
00010 #include <osl/osl_canny_nms.h>
00011 
00012 #include <vcl_cmath.h>
00013 #include <vcl_cstdlib.h>
00014 #include <vcl_list.h>
00015 #include <vcl_iostream.h>
00016 #include <vcl_cassert.h>
00017 #include <vnl/vnl_math.h>
00018 
00019 //#define proddy(n) { delete [] new char [n]; }
00020 //#define prod_heap { for (int n=1; n <= 0x100000; n *= 2) proddy(n); }
00021 
00022 //-----------------------------------------------------------------------------
00023 
00024 osl_canny_ox::osl_canny_ox(osl_canny_ox_params const &params)
00025   : osl_canny_base(params.sigma, params.low, params.high, params.verbose)
00026 {
00027   // Set the maximum allowable convolution kernel
00028   gauss_tail_ = params.gauss_tail;
00029   max_width_OX_ = params.max_width;
00030   kernel_ = new float[max_width_OX_];
00031   sub_area_OX_ = new float[max_width_OX_];
00032 
00033   edge_min_OX_ = params.edge_min;
00034   min_length_OX_ = params.min_length;
00035   join_flag_OX_ = params.join_flag;
00036   border_size_OX_ = params.border_size;
00037   border_value_OX_ = params.border_value;
00038   scale_OX_ = params.scale;
00039   follow_strategy_OX_ = params.follow_strategy;
00040   junction_option_OX_ = params.junction_option;
00041 
00042   xjunc_ = new vcl_list<int>;
00043   yjunc_ = new vcl_list<int>;
00044   vlist_ = new vcl_list<osl_Vertex*>;
00045 
00046   jval_ = 2000.0;
00047 }
00048 
00049 //-----------------------------------------------------------------------------
00050 
00051 osl_canny_ox::~osl_canny_ox()
00052 {
00053   osl_canny_base_free_raw_image(smooth_);
00054   osl_canny_base_free_raw_image(dx_);
00055   osl_canny_base_free_raw_image(dy_);
00056   osl_canny_base_free_raw_image(grad_);
00057   osl_canny_base_free_raw_image(thick_);
00058   osl_canny_base_free_raw_image(thin_);
00059   osl_canny_base_free_raw_image(theta_);
00060 
00061   osl_canny_base_free_raw_image(junction_);
00062   osl_canny_base_free_raw_image(jx_);
00063   osl_canny_base_free_raw_image(jy_);
00064 
00065   fsm_delete_array kernel_;
00066   fsm_delete_array sub_area_OX_;
00067   //no point vlist_->clear();
00068   fsm_delete vlist_;
00069   fsm_delete xjunc_;
00070   fsm_delete yjunc_;
00071 }
00072 
00073 //-----------------------------------------------------------------------------
00074 
00075 void osl_canny_ox::detect_edges(vil1_image const &image_in, vcl_list<osl_edge*> *edges)
00076 {
00077   assert(edges!=0);
00078 
00079   // Get the image size
00080   xsize_ = image_in.height();
00081   ysize_ = image_in.width();
00082   xstart_ = 0;
00083   ystart_ = 0;
00084 
00085   if (verbose)
00086     vcl_cerr << "Doing Canny on image region "
00087              << xsize_ << " by " << ysize_ << vcl_endl
00088              << "Sigma               = " << sigma_ << vcl_endl
00089              << "Gaussian tail       = " << gauss_tail_ << vcl_endl
00090              << "Max kernel size     = " << max_width_OX_ << vcl_endl
00091              << "Upper threshold     = " << high_ << vcl_endl
00092              << "Lower threshold     = " << low_ << vcl_endl
00093              << "Min edgel intensity = " << edge_min_OX_ << vcl_endl
00094              << "Min edge length     = " << min_length_OX_ << vcl_endl
00095              << "Image border size   = " << border_size_OX_ << vcl_endl << vcl_endl;
00096 
00097   // Allocate internal bitmaps ..
00098   smooth_ = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00099   dx_     = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00100   dy_     = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00101   grad_   = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00102   thick_  = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00103   thin_   = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00104   theta_  = osl_canny_base_make_raw_image(xsize_, ysize_, (float*)0);
00105   junction_ = osl_canny_base_make_raw_image(xsize_, ysize_, (int*)0);
00106   jx_       = osl_canny_base_make_raw_image(xsize_, ysize_, (int*)0);
00107   jy_       = osl_canny_base_make_raw_image(xsize_, ysize_, (int*)0);
00108   //image_ = 0;
00109 
00110   // .. and initialize them.
00111   osl_canny_base_fill_raw_image(grad_, xsize_, ysize_, 0.0f);
00112   osl_canny_base_fill_raw_image(thick_, xsize_, ysize_, 0.0f);
00113   osl_canny_base_fill_raw_image(thin_, xsize_, ysize_, 0.0f);
00114   osl_canny_base_fill_raw_image(theta_, xsize_, ysize_, 10000.0f);
00115   osl_canny_base_fill_raw_image(junction_, xsize_, ysize_, 0);
00116   osl_canny_base_fill_raw_image(jx_, xsize_, ysize_, 0);
00117   osl_canny_base_fill_raw_image(jy_, xsize_, ysize_, 0);
00118 
00119   // Do the traditional Canny parts
00120 
00121   if (verbose) vcl_cerr << "setting convolution kernel and zeroing images\n";
00122   osl_kernel_DOG(kernel_, sub_area_OX_, k_size_,
00123                  sigma_, gauss_tail_,
00124                  max_width_OX_, width_);
00125   if (verbose) vcl_cerr << "Kernel size     = " << k_size_ << vcl_endl;
00126 
00127 
00128   if (verbose) vcl_cerr << "smoothing the image\n";
00129   osl_canny_smooth(image_in,
00130                    kernel_, width_, sub_area_OX_,
00131                    smooth_);
00132 
00133   if (verbose)
00134     vcl_cerr << "computing x derivatives, y derivatives and norm of gradient\n";
00135   osl_canny_gradient(xsize_, ysize_, smooth_, dx_, dy_, grad_);
00136 
00137   if (verbose) vcl_cerr << "doing non-maximal suppression\n";
00138   unsigned int n_edgels_NMS = osl_canny_nms(xsize_, ysize_, dx_, dy_, grad_, thick_, theta_);
00139   if (verbose) vcl_cerr << "Number of edgels after NMS = " << n_edgels_NMS << vcl_endl;
00140 
00141 
00142   // (x_,y_) holds the pixel location (and not the sub pixel accuracy)
00143   // of the edges.
00144   // Needed in Get_hysteresis_edgelsOX() to fill the thin_ image
00145   // at the edgels locations
00146   // (x_,y_)'s are initialised in Get_NMS_edgelsOX()
00147   int *x_ = new int[n_edgels_NMS];
00148   int *y_ = new int[n_edgels_NMS];
00149 
00150 
00151   // Copy edgels from thick_ image into an osl_edgel_chain class.
00152   // edgels after NMS are not connected, but still are stored in osl_edgel_chain.
00153   // edgels_NMS is the input to Hysteresis
00154   osl_edgel_chain *edgels_NMS = Get_NMS_edgelsOX(n_edgels_NMS, x_, y_);
00155 
00156 
00157   if (verbose) vcl_cerr << "doing hysteresis\n";
00158   int *status = new int[n_edgels_NMS];
00159   int n_edgels_Hysteresis = HysteresisOX(edgels_NMS, status);
00160   if (verbose) vcl_cerr << "Number of edgels after Hysteresis = " << n_edgels_Hysteresis << vcl_endl;
00161 
00162   osl_edgel_chain *edgels_Hysteresis = new osl_edgel_chain(n_edgels_Hysteresis);
00163   Get_hysteresis_edgelsOX(edgels_NMS,status,edgels_Hysteresis, x_, y_);
00164 
00165   // delete the edgels that are output of Non_maximal_suppression
00166   fsm_delete_array status;
00167   fsm_delete edgels_NMS; edgels_NMS = 0;
00168 
00169   // delete *x_ and *y_; they are not needed anymore
00170   fsm_delete_array x_;
00171   fsm_delete_array y_;
00172 
00173   if (follow_strategy_OX_ == 0) // Don't do the follow stage of canny
00174   {
00175     edges->push_front( NO_FollowerOX(edgels_Hysteresis) );
00176     // delete the edgels that are output of Hysteresis
00177     fsm_delete edgels_Hysteresis;
00178     return;
00179   }
00180 
00181   // delete the edgels that are output of Hysteresis
00182   fsm_delete edgels_Hysteresis;
00183 
00184   // Multiply thin_ image by scaleOX_. Values that are above 255 are set to 255
00185   Scale_imageOX(thin_, scale_OX_);
00186   // Set image border to border_valueOX_ (default = 0) so follow can't overrun
00187   Set_image_borderOX(thin_, border_size_OX_, border_value_OX_);
00188 
00189   if (junction_option_OX_)
00190   {
00191     // Locate junctions in the edge image
00192     if (verbose) vcl_cerr << "locating junctions in the edge image - ";
00193     Find_junctionsOX();
00194     if (verbose) vcl_cerr << xjunc_->size() << " junctions found\n";
00195     Find_junction_clustersOX();
00196     if (verbose)  vcl_cerr << vlist_->size() << " junction clusters found\n";
00197   }
00198 
00199   // Finally do edge following to extract the edge data from the thin_ image
00200   if (verbose) vcl_cerr << "doing final edge following\n";
00201   FollowerOX(edges);
00202   if (verbose) vcl_cerr << "finished osl_canny_ox\n";
00203 }
00204 
00205 
00206 //-----------------------------------------------------------------------------
00207 //
00208 //: Copy edgels from thick_ image. These edgels are the result of NMS.
00209 // Although the edgels are stored in osl_edgel_chain edgels_NMS, they are
00210 // not actually connected.
00211 // Also initialises x_ and y_: the pixel locations of the edgels. These
00212 // are needed later in filling the image thin_ with the edgels after
00213 // hysteresis.
00214 osl_edgel_chain *osl_canny_ox::Get_NMS_edgelsOX(int n_edgels_NMS, int *x_, int *y_)
00215 {
00216   // the number of edges must be given in advance!
00217   osl_edgel_chain *edgels_NMS = new osl_edgel_chain(n_edgels_NMS);
00218 
00219   int i = 0;
00220 
00221   for (unsigned int y=1; y+1<ysize_; ++y)
00222     for (unsigned int x=1; x+1<xsize_; ++x)
00223       if ( thick_[x][y] != 0 /*&& i < n_edgels_NMS*/)
00224       {
00225         assert(i < n_edgels_NMS);
00226         // fill edgels_NMS
00227         edgels_NMS->SetX(dx_[x][y],i);
00228         edgels_NMS->SetY(dy_[x][y],i);
00229         edgels_NMS->SetGrad(thick_[x][y],i);
00230         edgels_NMS->SetTheta(theta_[x][y],i);
00231         // remember the pixel locations (x,y) of the edgels
00232         x_[i] = x; y_[i] = y;
00233         i++;
00234       }
00235 
00236   return edgels_NMS;
00237 }
00238 
00239 
00240 //-----------------------------------------------------------------------------
00241 //
00242 //: Hysteresis follows edgels that lie above the low threshold and have at least one edgel above the high threshold.
00243 //
00244 int osl_canny_ox::HysteresisOX(osl_edgel_chain *&edgels_NMS,
00245                                int *&status)
00246 {
00247   unsigned int n_edgels_NMS = edgels_NMS->size();
00248   if (!n_edgels_NMS)
00249     return 0;
00250 
00251   // Allocate arrays ..
00252   vcl_vector<unsigned> rows(ysize_+1); // rows[i] will contain the index in 'edgels_NMS' of the first
00253   //                                      edgel after start of row i. Thus, the edgels in row i are exactly
00254   //                                      those with indices j in the range rows[i] <= j < rows[i+1].
00255   vcl_vector<unsigned> row(n_edgels_NMS);  // (row[i], col[i]) will be the position of the ith
00256   vcl_vector<unsigned> col(n_edgels_NMS);  // edgel in 'edgels_NMS'.
00257   vcl_vector<osl_LINK *> links(n_edgels_NMS);  //
00258   // .. initialize arrays.
00259   for (unsigned int i=0; i<n_edgels_NMS; ++i) {
00260     links[i]  = 0; // null pointer
00261     row[i]    = (int) edgels_NMS->GetY(i);
00262     col[i]    = (int) edgels_NMS->GetX(i);
00263     status[i] = 0;
00264   }
00265   for (unsigned int i=0,j=0; i<=ysize_; ++i) { // Note: rows[ysize_] is one more than last edgel index
00266     while (j<n_edgels_NMS && row[j]<i)
00267       ++j;
00268     rows[i]=j;   // index of first edgel after start of row i
00269   }
00270 
00271 
00272   // Create a list of links for each edgel.
00273   Link_edgelsOX(col, rows, &links[0]); //vector<>::iterator
00274 
00275 
00276   // Perform Hysteresis part of canny.
00277   double low  = (32.0/vcl_log(2.0)) * vcl_log(low_/100+1.0);     // compute lower threshold
00278   double high = (32.0/vcl_log(2.0)) * vcl_log(high_/100+1.0);    // compute upper threshold
00279   //formerly "Do_hysteresisOX(edgels_NMS,links,status,low,high);"
00280   for (unsigned int i=0; i<n_edgels_NMS; ++i)
00281     if (!status[i] && edgels_NMS->GetGrad(i)>high) {
00282       status[i]=1;
00283       for (osl_LINK *lptr=links[i]; lptr; lptr=lptr->nextl)
00284         Initial_followOX(lptr->to, i, edgels_NMS, &links[0], status, (float)low); //vector<>::iterator
00285     }
00286 
00287 
00288   // and this?
00289   int n_edgels_Hysteresis = Get_n_edgels_hysteresisOX(edgels_NMS,status);
00290 
00291   // delete the osl_LINK * in the array 'links' :
00292   for (unsigned int i=0; i<n_edgels_NMS; ++i)
00293     for (osl_LINK *link1 = links[i]; link1; ) {
00294       osl_LINK *link2 = link1->nextl;
00295       fsm_delete link1;
00296       link1 = link2;
00297     }
00298 
00299   return n_edgels_Hysteresis;
00300 }
00301 
00302 
00303 //-------------------------------------------------------------------------
00304 //
00305 //: Initial follow. Used for the hysteresis part of canny.
00306 void osl_canny_ox::Initial_followOX(int to,
00307                                     int from,
00308                                     osl_edgel_chain *&edgels_NMS,
00309                                     osl_LINK *links[],
00310                                     int *&status,
00311                                     float low)
00312 {
00313   if (!status[to] && edgels_NMS->GetGrad(to) > low)
00314     status[to]=1;
00315   else
00316     return;
00317 
00318   for (osl_LINK *lptr=links[to]; lptr; lptr=lptr->nextl)
00319     if (lptr->to!=from)
00320       Initial_followOX(lptr->to,to,edgels_NMS,links,status,low);
00321 }
00322 
00323 
00324 //-------------------------------------------------------------------------
00325 //
00326 //: Add link. 'edgel' and 'to' are the indices of the two edges to be linked.
00327 // A link with value 'to'    becomes the new head of the link-list for 'edgel'.
00328 // A link with value 'edgel' becomes the new head of the link-list for 'to'.
00329 void osl_canny_ox::Add_linkOX(int edgel,
00330                               int to,
00331                               osl_LINK *links[])
00332 {
00333   osl_LINK *lptr1=new osl_LINK;
00334   lptr1->to=to;
00335   lptr1->nextl=links[edgel];
00336   links[edgel]=lptr1;
00337 
00338   osl_LINK *lptr2=new osl_LINK;
00339   lptr2->to=edgel;
00340   lptr2->nextl=links[to];
00341   links[to]=lptr2;
00342 }
00343 
00344 
00345 //-------------------------------------------------------------------------
00346 //
00347 //: Link edgels.
00348 // First try pixels at distance 1 (direct neighbours),
00349 // then at sqrt(2) (diagonal), then at 2 (horizontal or vertical), then
00350 // at sqrt(5) (chess horse). I.e. in the following order:
00351 // \verbatim
00352 //         4 3 4
00353 //       4 2 1 2 4
00354 //       3 1 0 1 3
00355 //       4 2 1 2 4
00356 //         4 3 4
00357 // \endverbatim
00358 void osl_canny_ox::Link_edgelsOX(vcl_vector<unsigned> const &col,
00359                                  vcl_vector<unsigned> const &rows,
00360                                  osl_LINK *links[])
00361   // Rewritten and inline-documented by Peter Vanroose, 30 Dec. 1999.
00362 {
00363   for (unsigned int i=0; i<ysize_; ++i) // for each image row
00364   {
00365     for (unsigned j=rows[i]; j<rows[i+1]; ++j) // for each edgel in this row
00366     {
00367       bool e=false; // set to true if next edgel is (horiz) neighbour
00368       bool s=false; // set to true if edgels are (vertical) neighbours
00369 
00370       // First link horizontal, direct neighbours:
00371 
00372       if (j+1<rows[i+1] && col[j]+1==col[j+1])  {  // next edgel is (horiz) neighbour
00373         Add_linkOX(j, j+1, links); e=true; }
00374 
00375       bool w=(j>rows[i] && col[j]==col[j-1]+1);// previous edgel was (horiz) neighbour
00376 
00377       // Don't go on (except for distance 2 neighbour) if there is certainly
00378       // no vertical neighbour edgel:
00379       if (rows[i+1] == rows[ysize_])
00380       {
00381         if (e) continue;
00382         // Verify that there was no diagonal north-east link:
00383         if (i > 0) {
00384           unsigned int k=rows[i-1]; // candidate diagonal neighbour edgel
00385           while (k < rows[i] && col[k] < col[j]+1) ++k;
00386           if (k < rows[i] && col[k] == col[j]+1) continue; // found n-e link
00387         }
00388         // Now verify the east-2 link:
00389         if (j+1<rows[i+1] && col[j]+2==col[j+1])
00390           Add_linkOX(j, j+1, links);
00391         continue; // go on with the next value of j
00392       }
00393 
00394       // Now try to link vertically:
00395 
00396       unsigned int k=rows[i+1]; // candidate vertical (/ | or \) neighbour edgel
00397 
00398       while (k < rows[i+2] && col[k]+1 < col[j]) ++k; // skip the early ones
00399 
00400       // Note that rows[i+2] makes sense when rows[i+1]<rows[ysize_]
00401       if (k == rows[i+2]) continue; // no vertical or diagonal neighbour
00402 
00403       if (k+1 < rows[i+2] && col[k+1] == col[j]) ++k;
00404       if (col[j]==col[k])  {  // j and k are (vertical) neighbours
00405         Add_linkOX(j, k, links); s = true; }
00406 
00407       // Diagonal neighbours (distance sqrt(2)):
00408 
00409       bool se = false;
00410       if (!e && !s && col[j]+1==col[k])  {  // j and k are diagonal \ neighbours
00411         Add_linkOX(j,k,links); se = true; }
00412       if (!w && !s && col[j]==col[k]+1)  {  // j and k are diagonal / neighbours
00413         Add_linkOX(j,k,links); s = w = true; }
00414       if (se) s = e = true;
00415 
00416       // Verify if there was a diagonal north-east or north-west link:
00417       if (i > 0) {
00418         k=rows[i-1];
00419         while (k < rows[i] && col[k]+1 < col[j]) ++k;
00420         if (k < rows[i] && col[k]+1 == col[j])  w = true;
00421         while (k < rows[i] && col[k] < col[j]+1) ++k;
00422         if (k < rows[i] && col[k] == col[j]+1)  e = true;
00423       }
00424 
00425       if (e && w && s) continue;
00426 
00427       // Horizontal neighbours at distance 2:
00428 
00429       if (!e && j+1<rows[i+1] && col[j]+2==col[j+1]) {
00430         Add_linkOX(j, j+1, links); e = true; }
00431 
00432       if (j>rows[i] && col[j]==col[j-1]+2)
00433         w = true;
00434 
00435       // Vertical neighbour at distance 2:
00436 
00437       k=rows[i+2];
00438       if (!s && k < rows[ysize_]) {
00439         while (k < rows[i+3] && col[k] < col[j]) ++k;
00440         // Note that rows[i+3] makes sense if rows[i+2]<rows[ysize_]
00441         if (k < rows[i+3] && col[j] == col[k]) {
00442           Add_linkOX(j, k, links); s = true; }
00443       }
00444 
00445       if (e && w && s) continue;
00446 
00447       // Neighbours at distance sqrt(5):
00448 
00449       // First find the e-s-e and w-s-w neighbours:
00450       k=rows[i+1];
00451       while (k < rows[i+2] && col[k]+2 < col[j]) ++k;
00452       if (!w && k < rows[i+2] && col[j] == col[k]+2) {
00453         Add_linkOX(j, k, links); s = w = true; }
00454       while (k < rows[i+2] && col[k] < col[j]+2) ++k;
00455       if (!e && k < rows[i+2] && col[j]+2 == col[k]) {
00456         Add_linkOX(j, k, links); s = e = true; }
00457 
00458       if (s) continue;
00459 
00460       // And finally the s-s-e and s-s-w neighbours:
00461       k=rows[i+2]; if (k == rows[ysize_]) continue;
00462       while (k < rows[i+3] && col[k]+1 < col[j]) ++k;
00463       if (k < rows[i+3] && (col[j] == col[k]+1 || col[j]+1 == col[k]))
00464         Add_linkOX(j, k, links);
00465 
00466     } // end for j
00467   } // end for i
00468 }
00469 
00470 
00471 //-------------------------------------------------------------------------
00472 //
00473 //: Returns the number of edgels after hysteresis.
00474 //
00475 int osl_canny_ox::Get_n_edgels_hysteresisOX(osl_edgel_chain *&edgels_NMS,
00476                                             int *&status)
00477 {
00478   int n_edgels_Hysteresis = 0;
00479   for (int i=edgels_NMS->size()-1; i>=0; --i)
00480     if (status[i])
00481       ++n_edgels_Hysteresis;
00482   return n_edgels_Hysteresis;
00483 }
00484 
00485 
00486 //-------------------------------------------------------------------------
00487 //
00488 //: Returns the edgels after hysteresis.
00489 // Also fill the image thin_ with the edgels after hysteresis for
00490 // further processing by the FollowerOX() part.
00491 //
00492 void osl_canny_ox::Get_hysteresis_edgelsOX(osl_edgel_chain *& edgels_NMS,
00493                                            int *&status,
00494                                            osl_edgel_chain *& edgels_Hysteresis,
00495                                            int *x_, int *y_)
00496 {
00497   // Initialise thin_ to zero's
00498   osl_canny_base_fill_raw_image(thin_, xsize_, ysize_, 0.0f);
00499 
00500   unsigned int n_edgels_NMS = edgels_NMS->size();
00501 
00502   for (unsigned int i=0,j=0; i<n_edgels_NMS; ++i)
00503   {
00504     if (status[i])
00505     {
00506       // Fill edgels_Hysteresis
00507       edgels_Hysteresis->SetX(edgels_NMS->GetX(i),j);
00508       edgels_Hysteresis->SetY(edgels_NMS->GetY(i),j);
00509       edgels_Hysteresis->SetGrad(edgels_NMS->GetGrad(i),j);
00510       edgels_Hysteresis->SetTheta(edgels_NMS->GetTheta(i),j);
00511       // Re-fill image thin_ for further processing
00512       thin_[x_[i]][y_[i]] = edgels_NMS->GetGrad(i);
00513       j++;
00514     } // end if
00515   } // end for i
00516 }
00517 
00518 //----------------------------------------------------------------------------
00519 //
00520 //:
00521 // In the case of follow_strategy_OX_ = 0, this function
00522 // returns an osl_edge * filled from osl_edgel_chain *edgels_Hysteresis.
00523 // i.e., the result of the Hysteresis part of Canny is returned in the osl_edge *.
00524 // The Follow part (FollowerOX) is not executed.
00525 osl_edge *osl_canny_ox::NO_FollowerOX(osl_edgel_chain *edgels_Hysteresis)
00526 {
00527   int n_edgels_Hysteresis = edgels_Hysteresis->size();
00528 
00529   // Define a digital curve to store the edgels data
00530   //  dc only stores the list of edgels after the Hysteresis stage of canny.
00531   //  dc is used later in this function to define an osl_edge *edge
00532   osl_edge *dc = new osl_edge(n_edgels_Hysteresis,
00533                               new osl_Vertex(edgels_Hysteresis->GetX(0)+xstart_,
00534                                              edgels_Hysteresis->GetY(0)+ystart_),
00535                               new osl_Vertex(edgels_Hysteresis->GetX(n_edgels_Hysteresis-1)+xstart_,
00536                                              edgels_Hysteresis->GetY(n_edgels_Hysteresis-1)+ystart_));
00537 
00538   float* px = dc->GetX();    float* py = dc->GetY();
00539   float* pg = dc->GetGrad(); float* pt = dc->GetTheta();
00540 
00541   for (int i=0; i<n_edgels_Hysteresis; ++i) {
00542     *(px++) = edgels_Hysteresis->GetX(i) + xstart_;
00543     *(py++) = edgels_Hysteresis->GetY(i) + ystart_;
00544     *(pg++) = edgels_Hysteresis->GetGrad(i);
00545     *(pt++) = edgels_Hysteresis->GetTheta(i);
00546   }
00547 
00548   return dc; //new osl_edge(dc);
00549 }
00550 
00551 
00552 //----------------------------------------------------------------------------
00553 //
00554 //: Returns the first osl_Vertex* in l which matches (i.e. compares equal to) v.
00555 // returns 0 if none found.
00556 osl_Vertex *osl_find(vcl_list<osl_Vertex*> const *l, osl_Vertex const &v)
00557 {
00558   for (vcl_list<osl_Vertex*>::const_iterator i=l->begin(); i!=l->end(); ++i)
00559     if (v == *(*i))
00560       return *i;
00561   return 0;
00562 }
00563 
00564 osl_Vertex *osl_find(vcl_list<osl_Vertex*> const *l, float x, float y)
00565 {
00566   for (vcl_list<osl_Vertex*>::const_iterator i=l->begin(); i!=l->end(); ++i)
00567     if ((*i)->x == x && (*i)->y == y)
00568       return *i;
00569   return 0;
00570 }
00571 
00572 
00573 //----------------------------------------------------------------------------
00574 //
00575 //:
00576 // Go through every point in the image and for every one which is above a
00577 // threshold:   follow from the point, in one direction and then the other.
00578 //
00579 void osl_canny_ox::FollowerOX(vcl_list<osl_edge*> *edges)
00580 {
00581   if (junction_option_OX_)
00582     chain_no_ = 10;    // Must be set to a number >= 1
00583 
00584   // temporaries used in loop
00585   vcl_list<int> xcoords, ycoords;
00586   vcl_list<float> grad;
00587 
00588   edges->clear();
00589   for (unsigned int x=border_size_OX_; x<xsize_-border_size_OX_; ++x)
00590     for (unsigned int y=border_size_OX_; y<ysize_-border_size_OX_; ++y)
00591     {
00592       // Due to Initial_hysteresis we can follow everything > edge_min_OX_
00593       if ( (thin_[x][y] < edge_min_OX_) || junction_[x][y] )
00594         continue;
00595 
00596       if (junction_option_OX_)
00597         chain_no_++;
00598 
00599       // clear lists before accumulating edgels.
00600       xcoords.clear();
00601       ycoords.clear();
00602       grad.clear();
00603 
00604       // forward follow [? fsm]
00605       Final_followOX(x,y, &xcoords, &ycoords, &grad,0);
00606 
00607       // We may have picked up the edgel chain somewhere
00608       // away from its ends. Therefore, reverse the list
00609       // and try to follow again.
00610       xcoords.reverse();
00611       ycoords.reverse();
00612       grad.reverse();
00613       Final_followOX(x,y,&xcoords,&ycoords,&grad,1);
00614 
00615       int count=xcoords.size();
00616       if (count < min_length_OX_ || count < 1)
00617 #ifdef DEBUG
00618         vcl_cerr << "short list found in Final_followOX\n";
00619 #endif
00620         continue;
00621 
00622       // Create an osl_edgel_chain and add to the list
00623       osl_edgel_chain * dc = new osl_edgel_chain(count);
00624       float *px = dc->GetX();
00625       float *py = dc->GetY();
00626       float *pg = dc->GetGrad();
00627       float *pt = dc->GetTheta();
00628 
00629       // Write the points to the osl_edgel_chain and the end points to the Curve
00630       //dc->SetStart(xcoords.front()+xstart_, ycoords.front()+ystart_);
00631       while (count--)
00632       {
00633         int tmpx = xcoords.front(); xcoords.pop_front();
00634         int tmpy = ycoords.front(); ycoords.pop_front();
00635         float val = grad.front(); grad.pop_front();
00636         // If we are not at a junction use sub-pixel value
00637         if ( val != jval_ ) {
00638           *(px++) = dx_[tmpx][tmpy] + xstart_;
00639           *(py++) = dy_[tmpx][tmpy] + ystart_;
00640           *(pg++) = val;
00641         }
00642         else {
00643           *(px++) = float(tmpx + xstart_);
00644           *(py++) = float(tmpy + ystart_);
00645           *(pg++) = 0.0f; // Mark the gradient as zero at a junction
00646         }
00647         *(pt++) = theta_[tmpx][tmpy];
00648 //      dc->SetEndX(tmpx+xstart_, tmpy+ystart_);
00649       }
00650 
00651       // Just check whether we have created a trivial edgechain
00652       // can happen if min_length_OX_ = 2
00653       if ( (dc->size()==2) &&
00654            (dc->GetX(0)==dc->GetX(1)) &&
00655            (dc->GetY(0)==dc->GetY(1)) ) {
00656 #ifdef DEBUG
00657         vcl_cerr << "trivial edgechain\n";
00658 #endif
00659       }
00660       else if ( dc->size() > 1 )
00661       {
00662         // Create an edge for the image topology
00663         osl_Vertex *v1 = new osl_Vertex(dc->GetX(0),dc->GetY(0));
00664         osl_Vertex *v2 = new osl_Vertex(dc->GetX(dc->size()-1),dc->GetY(dc->size()-1));
00665 
00666         if (junction_option_OX_)
00667         {
00668           // Check whether each vertex is a junction
00669           osl_Vertex *V1 = osl_find(vlist_, *v1);
00670           osl_Vertex *V2 = osl_find(vlist_, *v2);
00671 
00672           // If neither are junctions we may have formed a single
00673           // isolated chain that should have common vertex endpoints.
00674           bool single_chain = false;
00675           if ( !V1 && !V2 )
00676           {
00677             float dx = dc->GetX(0) - dc->GetX(dc->size()-1);
00678             float dy = dc->GetY(0) - dc->GetY(dc->size()-1);
00679             if (dc->size() < 1 || dx*dx+dy*dy < 4) {// ie. dist < 2 pixels it is closed
00680               //edge = new osl_edge(v1,v1);
00681               delete v2; // osl_IUDelete(v2);
00682               v2 = v1;
00683               single_chain = true;
00684             }
00685           }
00686 
00687           if (!single_chain) {
00688             if (V1) { delete v1; v1 = V1; }//was: { osl_IUDelete(v1); v1 = V1; }
00689             if (V2) { delete v2; v2 = V2; }//was: { osl_IUDelete(v2); v2 = V2; }
00690             //edge = new osl_edge(v1,v2);
00691           }
00692         }
00693         else {
00694           // We may have formed a single isolated
00695           // chain that should have common vertex endpoints.
00696           float dx = dc->GetX(0) - dc->GetX(dc->size()-1);
00697           float dy = dc->GetY(0) - dc->GetY(dc->size()-1);
00698           if ((dx*dx+dy*dy) < 4.0) {// ie. dist < 2 pixels it is closed
00699             //edge = new osl_edge(v1,v1);
00700             delete v2; // osl_IUDelete(v2);
00701             v2 = v1;
00702           }
00703           //else
00704           //  edge = new osl_edge(v1,v2);
00705         }
00706 
00707         // Note that the edge can start and end in the same osl_Vertex.
00708         // However, if this is so the DigitalCurve has positive length
00709         //dc->SetStart(dc->GetX(0), dc->GetY(0));
00710         //dc->SetEnd(dc->GetX(dc->size()-1),dc->GetY(dc->size()-1));
00711 
00712 #ifdef DEBUG
00713         vcl_cerr << __FILE__ ": push\n";
00714 #endif
00715         edges->push_front(new osl_edge(*dc, v1, v2));
00716       }
00717       delete dc;
00718     }
00719 #ifdef DEBUG
00720   vcl_cerr << "edges->size() : " << edges->size() << vcl_endl;
00721 #endif
00722 }
00723 
00724 
00725 //-----------------------------------------------------------------------------
00726 //
00727 //:
00728 // Adds point (x, y) to the current curve, sets its value in the image to
00729 // zero (any point may be included at most once in at most one curve)
00730 // then searches for adjacent pixels (in an order intended to make closed
00731 // curves ordered in a clockwise direction).
00732 // If none are found, and we are not at a junction Join_dotsOX() is called to
00733 // search for non-adjacent pixels.
00734 // If a new point is found, a recursive call is made to Final_followOX() with
00735 // that point.
00736 //
00737 // Two strategies can be followed according to follow_strategy_OX_
00738 // if follow_strategy_OX_ = 1 then neighbours are checked in the following
00739 //    order (Charlie Rothwell's way)
00740 // \verbatim
00741 //         8  7  6
00742 //         1  x  5
00743 //         2  3  4
00744 // \endverbatim
00745 // but if follow_strategy_OX_ = 2 then neighbours are checked in the following
00746 //    order (Nic Pillow's way)
00747 // \verbatim
00748 //         8  6  7
00749 //         1  x  4
00750 //         3  2  5
00751 // \endverbatim
00752 //
00753 void osl_canny_ox::Final_followOX(int x,
00754                                   int y,
00755                                   vcl_list<int> *xc,
00756                                   vcl_list<int> *yc,
00757                                   vcl_list<float> *grad,
00758                                   int reverse)
00759 {
00760   // Make sure that we do not overrun the border of the image
00761   assert ( x>0 && y>0 );
00762   assert ( (unsigned)x+1<xsize_ );
00763   assert ( (unsigned)y+1<ysize_ );
00764 
00765   // Add the current point to the coordinate lists, and delete from
00766   // the edge image
00767   if (!reverse) {
00768     xc->push_front(x);
00769     yc->push_front(y);
00770     grad->push_front(thin_[x][y]);
00771   }
00772   thin_[x][y] = 0.0;
00773 
00774   bool junction_or_jump = false;
00775 
00776   switch (follow_strategy_OX_)
00777   {
00778    case 1: // charlie rothwell way
00779     // Find one adjacent pixel;  for a closed curve, the method `guarantees'
00780     // a clockwise ordering of the points in the image sense
00781     if (false) { }
00782 #define smoo(a, b) \
00783     else if ( (thin_[a][b] >= edge_min_OX_) && (!junction_[a][b]) ) \
00784       Final_followOX(a,b,xc,yc,grad,0);
00785     smoo(x-1, y  )
00786     smoo(x-1, y+1)
00787     smoo(x  , y+1)
00788     smoo(x+1, y+1)
00789     smoo(x+1, y  )
00790     smoo(x+1, y-1)
00791     smoo(x  , y-1)
00792     smoo(x-1, y-1)
00793 #undef smoo
00794     else
00795       junction_or_jump = true;
00796     break;
00797 
00798    case 2:
00799    default: // nic pillow way
00800     if (false) { }
00801 #define smoo(a, b) \
00802     else if ( (thin_[a][b] >= edge_min_OX_) && (!junction_[a][b]) ) Final_followOX(a,b ,xc,yc,grad,0);
00803     smoo(x-1, y  )
00804     smoo(x  , y+1)
00805     smoo(x-1, y+1)
00806     smoo(x+1, y  )
00807     smoo(x+1, y+1)
00808     smoo(x  , y-1)
00809     smoo(x+1, y-1)
00810     smoo(x-1, y-1)
00811 #undef smoo
00812     else
00813       junction_or_jump = true;
00814     break;
00815   } // end switch
00816 
00817   if (!junction_or_jump)
00818     return;
00819 
00820   // Else see if there is a junction nearby, and record it. The chain_no_
00821   // variable is used to prevent the same junction being inserted at both
00822   // ends of the edgel chains when reversel occurs next to the junction
00823   // (in that case there will only be two stored points: the edge and the junction)
00824   if (false) { }
00825 #define smoo(a, b) \
00826   else if ( junction_[a][b] && ((xc->size()>2) || (junction_[a][b]!=chain_no_)) ) { \
00827     xc->push_front(jx_[a][b]); \
00828     yc->push_front(jy_[a][b]); \
00829     grad->push_front(jval_); \
00830     junction_[a][b] = chain_no_; \
00831   }
00832   smoo(x  , y-1)
00833   smoo(x-1, y  )
00834   smoo(x  , y+1)
00835   smoo(x+1, y  )
00836   smoo(x+1, y-1)
00837   smoo(x-1, y-1)
00838   smoo(x-1, y+1)
00839   smoo(x+1, y+1)
00840 #undef smoo
00841   else if ( join_flag_OX_ && (xc->size() > 1) )
00842   {
00843     // Try to find a pixel to jump to,
00844     //  and if successful, follow from it
00845     int x_c = xc->front(); xc->pop_front();
00846     int x_p = xc->front(); xc->pop_front();
00847     int y_c = yc->front(); yc->pop_front();
00848     int y_p = yc->front(); yc->pop_front();
00849     int dx = x_c - x_p;
00850     int dy = y_c - y_p;
00851     xc->push_front(x_p);  xc->push_front(x_c);
00852     yc->push_front(y_p);  yc->push_front(y_c);
00853     int xNew, yNew;
00854     if (Join_dotsOX(x, y, dx, dy, xNew, yNew))
00855       Final_followOX(xNew,yNew,xc,yc,grad,0);
00856   }
00857 }
00858 
00859 
00860 //-----------------------------------------------------------------------------
00861 
00862 #if 0
00863 void Set_intsOX(int& int1, int& int2, int val1, int val2)
00864 {
00865   int1 = val1;
00866   int2 = val2;
00867 }
00868 #endif
00869 #define Set_intsOX(d1, d2, v1, v2) ((d1)=(v1), (d2)=(v2))
00870 
00871 
00872 //----------------------------------------------------------------------------
00873 //
00874 //:
00875 // The point (x, y) is the current curve point;
00876 // (dx, dy) is the increment in position to (x, y) from the preceding point.
00877 // Based on dx and dy, it looks ahead in the same direction (or failing
00878 // that,  similar directions) for another point.
00879 // On success, xNew and yNew are set to the coordinates of the point found
00880 // and true is returned;  otherwise false is returned.
00881 //
00882 int osl_canny_ox::Join_dotsOX(int x, int y, int dx, int dy, int& xNew, int& yNew)
00883 {
00884   if ((vcl_abs(dx) > 1) || (vcl_abs(dy) > 1)) {
00885     //  If dx or dy is too large (> 1), meaning the last
00886     //   point was found by jumping, then jumping again
00887     //   will be too unreliable
00888     return false;
00889   }
00890 
00891   // Make sure that we do not overrun the border of the image
00892   assert( x>1 && y>1 );
00893   assert( (unsigned int)x+2<xsize_ );
00894   assert( (unsigned int)y+2<ysize_ );
00895 
00896   if (!dx && (vcl_abs(dy) == 1))
00897   {
00898     if      (thin_[x  ][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x  , y+2*dy);
00899     else if (thin_[x+1][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+1, y+2*dy);
00900     else if (thin_[x-1][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x-1, y+2*dy);
00901     else if (thin_[x+2][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2, y+2*dy);
00902     else if (thin_[x-2][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x-2, y+2*dy);
00903     else if (thin_[x+2][y+  dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2, y+dy  );
00904     else if (thin_[x-2][y+  dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x-2, y+dy  );
00905     else if (thin_[x+2][y     ] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2, y     );
00906     else if (thin_[x-2][y     ] >= edge_min_OX_) Set_intsOX(xNew, yNew, x-2, y     );
00907     else return false;
00908   }
00909   else if ((vcl_abs(dx) == 1) && !dy)
00910   {
00911     if      (thin_[x+2*dx][y  ] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y  );
00912     else if (thin_[x+2*dx][y+1] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y+1);
00913     else if (thin_[x+2*dx][y-1] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y-1);
00914     else if (thin_[x+2*dx][y+2] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y+2);
00915     else if (thin_[x+2*dx][y-2] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y-2);
00916     else if (thin_[x+  dx][y+2] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+  dx, y+2);
00917     else if (thin_[x+  dx][y-2] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+  dx, y-2);
00918     else if (thin_[x     ][y+2] >= edge_min_OX_) Set_intsOX(xNew, yNew, x     , y+2);
00919     else if (thin_[x     ][y-2] >= edge_min_OX_) Set_intsOX(xNew, yNew, x     , y-2);
00920     else return false;
00921   }
00922   else if (vcl_abs(dx*dy) == 1)
00923   {
00924     if      (thin_[x+2*dx][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y+2*dy);
00925     else if (thin_[x+2*dx][y+  dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y+  dy);
00926     else if (thin_[x+  dx][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+  dx, y+2*dy);
00927     else if (thin_[x+2*dx][y     ] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y     );
00928     else if (thin_[x     ][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x     , y+2*dy);
00929     else if (thin_[x+2*dx][y-  dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x+2*dx, y-  dy);
00930     else if (thin_[x-  dx][y+2*dy] >= edge_min_OX_) Set_intsOX(xNew, yNew, x-  dx, y+2*dy);
00931     else return false;
00932   }
00933   else
00934     return false;             // Should never be reached, but just in case...
00935 
00936   return true;
00937 }
00938 
00939 
00940 //-----------------------------------------------------------------------------
00941 //
00942 //: Multiply image by scale, and clip at 255.
00943 //
00944 void osl_canny_ox::Scale_imageOX(float **image, float scale)
00945 {
00946   for (unsigned int x=0; x<xsize_; ++x)
00947     for (unsigned int y=0; y<ysize_; ++y)
00948       image[x][y] = vnl_math_min( image[x][y]*scale, 255.0f );
00949 }
00950 
00951 
00952 //-----------------------------------------------------------------------------
00953 //
00954 //: Set size of pixels around image (border) to value, so follow can't overrun.
00955 //
00956 void osl_canny_ox::Set_image_borderOX(float **image, int border_size, float value)
00957 {
00958   assert(border_size >= 0);
00959   assert((unsigned int)border_size <= xsize_);
00960   assert((unsigned int)border_size <= ysize_);
00961 
00962   for (int i=0; i<border_size; ++i) {
00963     for (unsigned int x=0; x<xsize_; ++x)
00964       image[x][i] = image[x][ysize_-1-i] = value;
00965     for (unsigned int y=0; y<ysize_; ++y)
00966       image[i][y] = image[xsize_-1-i][y] = value;
00967   }
00968 }
00969 
00970 
00971 //-----------------------------------------------------------------------------
00972 //
00973 //: Searches for the junctions in the image.
00974 //
00975 void osl_canny_ox::Find_junctionsOX()
00976 {
00977   // Reset the junction variables
00978   xjunc_->clear();     yjunc_->clear();
00979   osl_canny_base_fill_raw_image(junction_, xsize_, ysize_, 0);
00980 
00981   for (unsigned int x=border_size_OX_; x+border_size_OX_<xsize_; ++x)
00982     for (unsigned int y=border_size_OX_; y+border_size_OX_<ysize_; ++y)
00983     {
00984       if ( thin_[x][y] < edge_min_OX_ )
00985         continue;
00986 
00987       int a = ( thin_[x-1][y-1] >= edge_min_OX_ ) ? 1 : 0;
00988       a +=    ( thin_[x  ][y-1] >= edge_min_OX_ ) ? 1 : 0;
00989       a +=    ( thin_[x+1][y-1] >= edge_min_OX_ ) ? 1 : 0;
00990       a +=    ( thin_[x+1][y  ] >= edge_min_OX_ ) ? 1 : 0;
00991       a +=    ( thin_[x+1][y+1] >= edge_min_OX_ ) ? 1 : 0;
00992       a +=    ( thin_[x  ][y+1] >= edge_min_OX_ ) ? 1 : 0;
00993       a +=    ( thin_[x-1][y+1] >= edge_min_OX_ ) ? 1 : 0;
00994       a +=    ( thin_[x-1][y  ] >= edge_min_OX_ ) ? 1 : 0;
00995 
00996       if ( a > 2 )  {
00997         xjunc_->push_front(x);  yjunc_->push_front(y);
00998         junction_[x][y] = 1;
00999       }
01000     }
01001 }
01002 
01003 
01004 //-----------------------------------------------------------------------------
01005 //
01006 //: Locate junction clusters using the following method of hysteresis.
01007 //
01008 void osl_canny_ox::Find_junction_clustersOX()
01009 {
01010   vcl_list<int> xvertices,yvertices,xjunc,yjunc;
01011 
01012   // Find a junction and follow
01013   xvertices.clear();  yvertices.clear();
01014   xjunc.clear();      yjunc.clear();
01015   for (unsigned int x=border_size_OX_; x+border_size_OX_<xsize_; ++x)
01016     for (unsigned int y=border_size_OX_; y+border_size_OX_<ysize_; ++y)
01017       if ( junction_[x][y] )
01018       {
01019         // Each cluster is written to (xcoords,ycooords)
01020         vcl_list<int> xcoords,ycoords;
01021         Follow_junctions(junction_, x,y,&xcoords,&ycoords);
01022 
01023         // Find the `centre' of the cluster. This is defined as the
01024         // junction closest to the centre of gravity of the cluster
01025         int x0,y0;
01026         Cluster_centre_of_gravity(jx_, jy_, xcoords,ycoords,x0,y0);
01027 
01028         // Add both the junctions and the new cluster centre to
01029         // the main lists
01030         xvertices.push_front(x0);
01031         yvertices.push_front(y0);
01032         xjunc.insert(xjunc.begin(), xcoords.begin(), xcoords.end()); //xjunc.prepend(xcoords);
01033         yjunc.insert(yjunc.begin(), ycoords.begin(), ycoords.end()); //yjunc.prepend(ycoords);
01034       }
01035 
01036   // Reset the junction image - this is order dependent because
01037   // the cluster centres appear in both lists
01038   // xjunc.reset();  yjunc.reset();
01039   while ( xjunc.size() ) {
01040     junction_[xjunc.front()][yjunc.front()] = 1;
01041     xjunc.pop_front();
01042     yjunc.pop_front();
01043   }
01044 
01045   // Construct the list of junction cluster centres
01046   typedef vcl_list<int>::iterator it;
01047   for (it i=xvertices.begin(), j=yvertices.begin(); i!=xvertices.end() && j!=yvertices.end(); ++i, ++j) {
01048     osl_Vertex *v = new osl_Vertex( float((*i)+xstart_), float((*j)+ystart_));
01049     vlist_->push_front(v);
01050     junction_[*i][*j] = 2;
01051   }
01052 }