Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

gevd_contour.cxx

Go to the documentation of this file.
00001 // This is gel/gevd/gevd_contour.cxx
00002 #include "gevd_contour.h"
00003 //:
00004 // \file
00005 #include <vcl_iostream.h>
00006 #include <vcl_cstdlib.h>   // for vcl_abs(int)
00007 #include <vcl_vector.h>
00008 #include <vcl_algorithm.h> // for vcl_max()
00009 #include <vxl_config.h>
00010 #include <vnl/vnl_math.h> // for sqrt(2)
00011 #include <vdgl/vdgl_digital_curve.h>
00012 #include <vdgl/vdgl_edgel_chain.h>
00013 #include <vdgl/vdgl_interpolator.h>
00014 #include <vdgl/vdgl_interpolator_linear.h>
00015 #include <vtol/vtol_vertex_2d.h>
00016 #include <vtol/vtol_edge_2d.h>
00017 #include <gevd/gevd_pixel.h>
00018 #ifdef DEBUG
00019 # include <vul/vul_timer.h>
00020 #endif
00021 
00022 const int INVALID = -1;
00023 
00024 // Use 8 directions, with 45 degree angle in between them.
00025 
00026 const vxl_byte TWOPI = 8, /* FULLPI = 4, */ HALFPI = 2 /* , QUARTERPI = 1 */;
00027 //const vxl_byte DIR0 = 8, DIR1 = 9, DIR2 = 10, DIR3 = 11;
00028 const int DIS[] = { 1, 1, 0,-1,-1,-1, 0, 1, // 8-connected neighbors
00029                     1, 1, 0,-1,-1,-1, 0, 1, // wrapped by 2PI to
00030                     1, 1, 0,-1,-1,-1, 0, 1};// avoid modulo operations.
00031 const int DJS[] = { 0, 1, 1, 1, 0,-1,-1,-1,
00032                     0, 1, 1, 1, 0,-1,-1,-1,
00033                     0, 1, 1, 1, 0,-1,-1,-1};
00034 
00035 //const int RDS[] = {0,-1, 1,-2, 2,-3, 3,-4, 4,-5, 5}; // radial search
00036 const int RIS[] = { 1, 0,-1, 0, // spiral search for 4/8-connected
00037                     1,-1,-1, 1, // neighbors
00038                     2, 0,-2, 0,
00039                     2, 1,-1,-2,-2,-1, 1, 2,
00040                     2,-2,-2, 2,
00041                     3, 0,-3, 0,
00042                     3, 1,-1,-3,-3,-1, 1, 3,
00043                     3, 2,-2,-3,-3,-2, 2, 3,
00044                     4, 0,-4, 0};
00045 const int RJS[] = { 0, 1, 0,-1, // rotate CW, increasing radius
00046                     1, 1,-1,-1,
00047                     0, 2, 0,-2,
00048                     1, 2, 2, 1,-1,-2,-2,-1,
00049                     2, 2,-2,-2,
00050                     0, 3, 0,-3,
00051                     1, 3, 3, 1,-1,-3,-3,-1,
00052                     2, 3, 3, 2,-2,-3,-3,-2,
00053                     0, 4, 0,-4};
00054 const int RNS[] = { 4, 8, 12, 20, 24, 28, 36, 44, 48}; // at distinct r
00055 const float RGS[] = { 1.f, 1.414213f, 2.f, 2.236067f, 2.828427f, // values of gap
00056                       3.f, 3.162277f, 3.605551f, 4.f};
00057 
00058 // - win32 - moved to here for MSVC++
00059 const int MINLENGTH = 3;        // minimum number of pixels for a chain
00060 const int FRAME = 4;            // border of image
00061 
00062 bool gevd_contour::talkative = true;    // By default contour is not silent.
00063 
00064 
00065 //: Save parameters and create workspace for detecting contours.
00066 // Each contour must have at least 1 pixel above min_strength,
00067 // and its number of internal pixels must be above min_length.
00068 // This is a heuristic hysteresis scheme that prunes weak or short
00069 // isolated chains.
00070 // To join a weaker contour to a stronger contour, a junction must
00071 // have a change in response above min_jump on the stronger contour.
00072 // This way, only strong junctions are detected.
00073 gevd_contour::gevd_contour(float min_strength, int min_length,
00074                            float min_jump, float max_gap)
00075   : minStrength(min_strength), minLength(min_length),
00076     minJump(min_jump), maxSpiral(0),
00077     edgeMap(), vertexMap()
00078 {
00079   if (minStrength < 0) {
00080     vcl_cerr << "gevd_contour::gevd_contour -- negative min_strength: "
00081              << minStrength << ". Reset to 0.\n";
00082     minStrength = 0;
00083   }
00084   if (minLength < MINLENGTH) {
00085     vcl_cerr << "gevd_contour::gevd_contour -- too small min_length: "
00086              << minLength << ". Reset to " << MINLENGTH << ".\n";
00087     minLength = MINLENGTH;
00088   }
00089   if (minJump < 0) {
00090     vcl_cerr << "gevd_contour::gevd_contour -- negative min_jump: "
00091              << minJump << ". Reset to 0.\n";
00092     minJump = 0;
00093   }
00094   if (minJump > minStrength) {
00095     vcl_cerr << "gevd_contour::gevd_contour -- too large min_jump: "
00096              << min_jump << ". Reset to " << minStrength << ".\n";
00097     minJump = minStrength;
00098   }
00099   if (max_gap < 1) {
00100     vcl_cerr << "gevd_contour::gevd_contour -- too small max_gap: "
00101              << max_gap << ". Reset to 1.\n";
00102     max_gap = 1;
00103   }
00104   if (max_gap > FRAME) {
00105     vcl_cerr << "gevd_contour::gevd_contour -- too large max_gap: "
00106              << max_gap << ". Reset to " << FRAME << vcl_endl;
00107     max_gap = FRAME;
00108   }
00109   for (int i = 0; i < 9; i++)   // find number of neighbors to search
00110     if (max_gap <= RGS[i])      // for given gap radius
00111       maxSpiral= i+1;
00112 }
00113 
00114 
00115 //: Free space allocated for detecting contours.
00116 gevd_contour::~gevd_contour()
00117 {
00118   delete edgeMap;               // space shared by LinkJunction/Chain
00119   delete vertexMap;
00120 }
00121 
00122 
00123 //: Find network of linked edges and vertices, from 8-connected edge elements.
00124 // The contours must be less than 2 pixel wide,
00125 // for example found from non maximum suppression.
00126 // Isolated edgels and short segments are erased.
00127 bool
00128 gevd_contour::FindNetwork(gevd_bufferxy& edgels,
00129                           const int njunction,
00130                           const int* junctionx, const int* junctiony,
00131                           vcl_vector<vtol_edge_2d_sptr>*& edges,
00132                           vcl_vector<vtol_vertex_2d_sptr >*& vertices)
00133 {
00134   // make sure that if no edges are found that edges and vertices
00135   // get values, to avoid seg faults, WAH
00136   if (!edges)
00137     edges = new vcl_vector<vtol_edge_2d_sptr>;
00138   else
00139     edges->clear();
00140   if (!vertices)
00141     vertices = new vcl_vector<vtol_vertex_2d_sptr >;
00142   else
00143     vertices->clear();
00144 
00145 
00146   if (talkative)
00147     vcl_cout << "*** Link edge elements into connected edges/vertices.\n";
00148 
00149   // 1. Setup lookup maps based on (x,y) integer location.
00150   vertexMap = new vbl_array_2d<vtol_vertex_2d_sptr>(edgels.GetSizeX(), edgels.GetSizeY());
00151   vertexMap->fill(NULL);
00152   edgeMap = new vbl_array_2d<vtol_edge_2d_sptr>(edgels.GetSizeX(), edgels.GetSizeY());
00153   edgeMap->fill(NULL);
00154 
00155   // 2. Collect 4/8-connected pixels into chains
00156   int n; // = vcl_max(10*njunction, // preallocated size from junctions or
00157   //       edgels.GetSizeX()*edgels.GetSizeY()/100); // image size
00158   vcl_vector<vtol_edge_2d_sptr> *edges2 = new vcl_vector<vtol_edge_2d_sptr>;
00159   n = this->FindChains(edgels, // link pixels into chains
00160                        njunction, // also use junction pixels
00161                        junctionx, junctiony,
00162                        *edges2);
00163   if (!n)
00164     return false;               // empty network
00165 
00166   // 3. Sort chains longest first.
00167 
00168   if (edges2->size() < 1000)     // avoid O(nlogn) for very large n
00169     vcl_sort (edges2->begin(), edges2->end());
00170   //    edges2.sort(&gevd_contour::LengthCmp); // sort longest/strongest first
00171 
00172   // renumber with order in array
00173   for (unsigned int i= 0; i< edges2->size(); i++)
00174     (*edges2)[i]->set_id(i);
00175 
00176   // 4. Split/Merge chains from touching end points
00177   vcl_vector<vtol_vertex_2d_sptr > vertices2;
00178   this->FindJunctions(edgels, // break/merge at junctions of
00179                       *edges2, vertices2); // distinct chains
00180 
00181   // 5. Copy back results into global lists
00182   for (unsigned int i= 0; i< edges2->size(); i++)
00183     edges->push_back( (*edges2)[i]);
00184 
00185   for (unsigned int i=0; i< vertices2.size(); i++)
00186     vertices->push_back( vertices2[i]);
00187 
00188   return true;
00189 }
00190 
00191 
00192 //: Return TRUE if pixel is a local maximum, and so is right on top of contour.
00193 static bool
00194 on_contour(const gevd_bufferxy& edgels, const int i, const int j)
00195 {
00196   double pix = (1 + vnl_math::sqrt2) * floatPixel(edgels, i, j); // fuzzy threshold
00197   for (vxl_byte dir = 0; dir < TWOPI; dir += HALFPI) // 4-connected only
00198     if (floatPixel(edgels, i+DIS[dir], j+DJS[dir]) > pix)
00199       return false;             // should choose neighbor instead
00200   return true;
00201 }
00202 
00203 
00204 //: Delete pixel from contour, and save its location in xloc/yloc.
00205 static void
00206 RecordPixel(int i, int j, gevd_bufferxy& edgels,
00207             vcl_vector<int>& iloc, vcl_vector<int>& jloc)
00208 {
00209   floatPixel(edgels, i, j) = -floatPixel(edgels, i, j); // flip sign
00210   iloc.push_back(i), jloc.push_back(j);
00211 }
00212 
00213 
00214 //:
00215 // Find next best pixel on contour, searching for strongest response,
00216 // and favoring 4-connected over 8-connected.
00217 // Return 0, if no pixel is found, or direction in range [2*pi, 4*pi).
00218 static int
00219 NextPixel(int& i, int& j, const gevd_bufferxy& edgels)
00220 {
00221   float maxpix = 0, npix;
00222   int maxdir = 0, dir;
00223   for (dir = 0; dir < TWOPI; dir += HALFPI) // 4-connected first
00224     if ((npix = floatPixel(edgels, i+DIS[dir], j+DJS[dir])) > maxpix) {
00225       maxpix = npix;
00226       maxdir = dir+TWOPI;
00227     }
00228   if (!maxdir) {
00229     for (dir = 1; dir < TWOPI; dir += HALFPI) // 8-connected next
00230       if ((npix = floatPixel(edgels, i+DIS[dir], j+DJS[dir])) > maxpix) {
00231         maxpix = npix;
00232         maxdir = dir+TWOPI;
00233       }
00234   }
00235   if (maxdir)                   // update next strongest pixel
00236     i += DIS[maxdir], j += DJS[maxdir];
00237   return maxdir;
00238 }
00239 
00240 
00241 //:
00242 // Find next best pixel on contour, searching for strongest response,
00243 // and favoring 4-connected over 8-connected.
00244 // Return 0, if no pixel is found, or direction in range [2*pi, 4*pi).
00245 static int
00246 next_pixel(int& i, int& j, const vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00247 {
00248   int maxdir = 0, dir;
00249   for (dir = 0; dir < TWOPI; dir += HALFPI) // 4-connected first
00250     if (vertexMap.get(i+DIS[dir], j+DJS[dir])) {
00251       maxdir = dir+TWOPI;
00252       break;
00253     }
00254   if (!maxdir) {
00255     for (dir = 1; dir < TWOPI; dir += HALFPI) // 8-connected next
00256       if (vertexMap.get(i+DIS[dir], j+DJS[dir])) {
00257         maxdir = dir+TWOPI;
00258         break;
00259       }
00260   }
00261   if (maxdir)                   // update next strongest pixel
00262     i += DIS[maxdir], j += DJS[maxdir];
00263   return maxdir;
00264 }
00265 
00266 
00267 //:
00268 // Trace and collect pixels on thin contours, stronger pixels first,
00269 // and favoring 4-connected over 8-connected. Thinning is not used,
00270 // and so will avoid errors because of square grid tessellation.
00271 // A chain can not cross itself. It can only touch itself or another
00272 // chain, in which case a junction will be found later.
00273 // The pixels of a chain include the 2 end points.
00274 // End points and junctions are created in gevd_contour::FindJunctions.
00275 // Return the number of chains found.  Protected.
00276 int
00277 gevd_contour::FindChains(gevd_bufferxy& edgels, const int njunction,
00278                          const int* junctionx, const int* junctiony,
00279                          vcl_vector<vtol_edge_2d_sptr>& edges)
00280 {
00281 #ifdef DEBUG
00282   vul_timer t;
00283 #endif
00284 
00285   // 1. Save away detected junctions from extending at end points of
00286   // contours, without linking these contours up. This avoids random
00287   // order in the traversal of the contours.
00288   vtol_vertex_2d_sptr mark = new vtol_vertex_2d;         // dummy non zero pointer
00289   for (int k = 0; k < njunction; k++) {
00290     vertexMap->put(junctionx[k], junctiony[k], mark);
00291   }
00292 
00293   // 2. Trace elongated & thinned chains, stronger pixels first.
00294   // Virtual border of image should be inserted last.
00295   const int rmax = FRAME;
00296   const int xmax = edgels.GetSizeX()-rmax-1;
00297   const int ymax = edgels.GetSizeY()-rmax-1;
00298   vcl_vector<int> xloc(xmax+ymax), yloc(xmax+ymax); // work space for
00299 
00300   for (int j = rmax; j <= ymax; j++)
00301     for (int i = rmax; i <= xmax; i++)
00302     {
00303       // 2.0. Start from better pixels above noise+hysteresis
00304       if (floatPixel(edgels, i, j) > minStrength &&
00305           on_contour(edgels, i, j)) { // right on the contour
00306         int x = i, y = j;
00307 
00308         // 2.1. Prune isolated pixels
00309         if (!NextPixel(x, y, edgels)) {// prune isolated pixels
00310           floatPixel(edgels, i, j) = 0;
00311           continue;
00312         }
00313 
00314         // 2.2. Start collecting first 3 pixels
00315         xloc.clear(), yloc.clear(); // collect pixels on contour
00316         RecordPixel(i, j, edgels, xloc, yloc);  // first pixel
00317         int ii = x, jj = y;
00318         RecordPixel(ii, jj, edgels, xloc, yloc); // second pixel
00319         if (NextPixel(x, y, edgels))
00320           RecordPixel(x, y, edgels, xloc, yloc); // third pixel
00321         else {                  // reach end point
00322           x = i, y = j;         // revert back to start pt
00323           if (NextPixel(x, y, edgels)) { // reverse collection
00324             xloc.clear(), yloc.clear();
00325             RecordPixel(ii, jj, edgels, xloc, yloc); // second pixel
00326             RecordPixel(i, j, edgels, xloc, yloc); // first pixel
00327             RecordPixel(x, y, edgels, xloc, yloc); // third pixel
00328             ii = i, jj = j;
00329           } else  {             // reach other end point
00330             floatPixel(edgels, i, j) = 0; // prune isolated pixel-pairs
00331             floatPixel(edgels, ii, jj) = 0;
00332             continue;
00333           }
00334         }
00335 
00336         // 2.3. Watch out for zig-zag at 2nd pixel, from LR-TD scans
00337         if ((x - ii)*(ii - xloc[0]) +
00338             (y - jj)*(jj - yloc[0]) < 0) {
00339           xloc[1] = xloc[0], yloc[1] = yloc[0]; // swap first 2 points
00340           xloc[0] = ii, yloc[0] = jj; // to eliminate zig-zag
00341         }
00342 
00343         // 2.4. Collect both directions & extension points if 1-chain
00344         while (NextPixel(x, y, edgels))                // trace along first dir, 4-connected
00345           RecordPixel(x, y, edgels, xloc, yloc);       // and stronger first
00346 
00347         if (vcl_abs(xloc[0]-x) > 1 ||                  // disjoint first/last pixel
00348             vcl_abs(yloc[0]-y) > 1) {                  // so must be a 1-chain with end points
00349           if (next_pixel(x, y, *vertexMap))            // search for extra links to
00350             xloc.push_back(x), yloc.push_back(y);      // detected junctions
00351           x = xloc[0], y = yloc[0];                    // start again from first pixel
00352 
00353           vcl_vector<int> xloctemp( xloc.size()), yloctemp( yloc.size());
00354           for (unsigned int iii=0; iii< xloc.size(); iii++)
00355             xloctemp[iii]= xloc[xloc.size()-1-iii];
00356           for (unsigned int jjj=0; jjj< yloc.size(); jjj++)
00357             yloctemp[jjj]= yloc[yloc.size()-1-jjj];
00358 
00359           while (NextPixel(x, y, edgels)) // trace along other dir
00360             RecordPixel(x, y, edgels, xloc, yloc);
00361           if (next_pixel(x, y, *vertexMap)) // search for extra links to
00362             xloc.push_back(x), yloc.push_back(y); // detected junctions
00363         }
00364         int len = xloc.size();
00365 
00366         // 2.5. Check for isolated contours that are too short
00367         if (len < minLength) {  // zero or too few internal pixels
00368           for (int k = 0; k < len; k++) // zero or too few internal pixels
00369             floatPixel(edgels, xloc[k], yloc[k]) = 0; // prune short chains
00370           continue;
00371         }
00372 
00373         // Thin zig-zags on the contours? Zig-zags happen at the 2
00374         // end points because of extension, or inside the contour
00375         // because of 4/8-connected tracing through noisy chain pixels,
00376         // and large shifts for subpixel locations.
00377         // Defer the elimination of zig-zags to gevd_contour::SubPixelAccuracy()
00378         // or gevd_contour::EqualizeSpacing()
00379 
00380         // 2.6. Create network of chains, touching, possibly ending
00381         // at same junction, but never crossing one another
00382         vtol_edge_2d_sptr edge = new vtol_edge_2d();
00383         vdgl_edgel_chain * ec = new vdgl_edgel_chain;
00384         vdgl_interpolator * it = new vdgl_interpolator_linear(ec);
00385         vdgl_digital_curve * dc = new vdgl_digital_curve(it); // include end points
00386 
00387         for (int k=0; k< len; k++)
00388         {
00389           x= xloc[k];
00390           y= yloc[k];
00391           ec->add_edgel( vdgl_edgel( x, y));
00392           edgeMap->put(x, y, edge);
00393         }
00394         edge->set_curve(*dc);
00395         LookupTableInsert(edges, edge);
00396       }
00397   }
00398 
00399   // 3. Restore cache to original state
00400   for (int k = 0; k < njunction; k++)  // clear all void*/float labels
00401     vertexMap->put(junctionx[k], junctiony[k],NULL);
00402   for (int j = rmax; j <= ymax; j++)
00403     for (int i = rmax; i <= xmax; i++)
00404       if (floatPixel(edgels, i, j) < 0) // undo mark put by RecordPixel
00405         floatPixel(edgels, i, j) = - floatPixel(edgels, i, j);
00406 
00407   if (talkative)
00408     vcl_cout << "Find " << edges.size()
00409              << " chains/cycles, with pixels > " << minLength
00410              << " and strength > " << minStrength
00411 #ifdef DEBUG
00412              << ", in " << t.real() << " msecs."
00413 #endif
00414              << vcl_endl;
00415   return edges.size();  // number of chains found so far
00416 }
00417 
00418 
00419 //:
00420 // Check that end point of a weak contour touches another stronger
00421 // contour at an internal pixel. Localize the junction to pixel accuracy
00422 // by searching for shortest distance from end point to chain.
00423 // Gaussian smoothing can put local maximum change in filter response
00424 // 1 pixel away from this junction location.
00425 // Update junction map.
00426 bool
00427 DetectJunction(vtol_vertex_2d& end, int& index,
00428                vtol_edge_2d_sptr& weaker, vtol_edge_2d_sptr& stronger,
00429                const int maxSpiral,
00430                const gevd_bufferxy& edgels, vbl_array_2d<vtol_edge_2d_sptr>& edgeMap)
00431 {
00432   // 0. Must be an end point of a dangling 1-chain
00433   if (end.numsup() > 1)         // avoid junction and 1-cycle
00434     return false;
00435   vcl_vector<vtol_edge_sptr> edges; end.edges(edges);
00436   weaker = edges[0]->cast_to_edge_2d();      // dangling edge must be a weaker contour
00437   vdgl_digital_curve_sptr dc = weaker->curve()->cast_to_vdgl_digital_curve();
00438 
00439   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00440 
00441   // 1. Mark off pixels at end pt to find junction of a contour to itself
00442 
00443   const int rfuzz = vcl_min(len, 3*MINLENGTH);
00444   vtol_edge_2d_sptr* labels = new vtol_edge_2d_sptr[rfuzz];
00445   if (&end == weaker->v1()->cast_to_vertex_2d())
00446     for (int r = 0; r < rfuzz; r++) {
00447       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel( r);
00448       labels[r] = edgeMap.get( int(edgel.get_x()), int(edgel.get_y()));
00449       edgeMap.put(int(edgel.get_x()), int(edgel.get_y()), NULL);
00450     }
00451   else
00452     for (int r = 0; r < rfuzz; r++) {
00453       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel(len-1-r);
00454       labels[r] = edgeMap.get( int( edgel.get_x()), int( edgel.get_y()));
00455       edgeMap.put(int(edgel.get_x()), int(edgel.get_y()), NULL);
00456     }
00457 
00458   // 2. Find another stronger contour touched by this end point < gap.
00459   stronger = NULL;              // contour can join with itself
00460   int jx = int(end.x()), jy = int(end.y());
00461   for (int l = 0, n = 0; l < maxSpiral; l++) {  // increasing radius of spiral
00462     float maxpix = 0; int maxn = 0;     // strongest strength at this radius
00463     for (; n < RNS[l]; n++) {
00464       int x = jx+RIS[n], y = jy+RJS[n];
00465       if (edgeMap.get(x, y) && // find another contour or itself
00466           floatPixel(edgels, x, y) > maxpix) {
00467         maxpix = floatPixel(edgels, x, y);
00468         maxn = n;               // better neighbor
00469       }
00470     }
00471     if (maxpix) {               // location of junction on contour
00472       stronger = edgeMap.get(jx+RIS[maxn], jy+RJS[maxn]);
00473       jx += RIS[maxn], jy += RJS[maxn];
00474       break;
00475     }
00476   }
00477   // restore edgeMap around end point (undo step 1)
00478   if (&end == weaker->v1()->cast_to_vertex_2d())
00479     for (int r=0; r< rfuzz; r++) {
00480       vdgl_edgel edge= dc->get_interpolator()->get_edgel_chain()->edgel(r);
00481       edgeMap.put(int( edge.get_x()), int( edge.get_y()), labels[r]);
00482     }
00483   else
00484     for (int r=0; r< rfuzz; r++) {
00485       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel(len-1-r);
00486       edgeMap.put(int( edgel.get_x()), int( edgel.get_y()),labels[r]);
00487     }
00488   delete[] labels;
00489 
00490   if (!stronger)                // do not find any edge in search region
00491     return false;
00492 
00493   // 3. Find index location of junction on this contour
00494   index = int(INVALID);
00495 
00496   vdgl_digital_curve_sptr dc2 =(stronger->curve()->cast_to_vdgl_digital_curve());
00497 
00498   // find corresponding index on contour
00499   for (unsigned int n = 0; n < dc2->get_interpolator()->get_edgel_chain()->size(); ++n)
00500   {
00501     vdgl_edgel edgel= dc2->get_interpolator()->get_edgel_chain()->edgel(n);
00502 
00503     if ( int( edgel.get_x())== jx && int( edgel.get_y())== jy)
00504     {
00505       index = n;
00506       break;
00507     }
00508   }
00509   return true;
00510 }
00511 
00512 
00513 //: Confirm there is a strong jump in response near a junction.
00514 // The location of this jump is however inaccurate, and so junctions
00515 // can not be localized accurately along the stronger cycle.
00516 static bool
00517 ConfirmJunctionOnCycle(int index, float threshold,
00518                        vtol_edge_2d& cycle, const gevd_bufferxy& edgels)
00519 {
00520   vdgl_digital_curve_sptr dc =(cycle.curve()->cast_to_vdgl_digital_curve());
00521   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00522   const int wrap = 10*len;      // for positive index
00523   const int radius = 3;         // gap < 3, around junction pixel
00524 
00525   for (int n = index-radius; n <= index+radius; n++)
00526   {
00527     int nm = (n-1+wrap)%len;    // modulo operations to wrap at borders
00528     int np = (n+1+wrap)%len;
00529 
00530     vdgl_edgel edgel_m= dc->get_interpolator()->get_edgel_chain()->edgel( nm);
00531     vdgl_edgel edgel_p= dc->get_interpolator()->get_edgel_chain()->edgel( np);
00532 
00533     if (vcl_fabs(floatPixel(edgels, int( edgel_p.x()), int( edgel_p.y())) -
00534                  floatPixel(edgels, int( edgel_m.x()), int( edgel_m.y())))
00535         > threshold)
00536       return true;
00537   }
00538   return false;
00539 }
00540 
00541 
00542 //:
00543 // Break the cycle at given index, and create new cycle from/to
00544 // and not including index pixel. Update the chain map accordingly.
00545 void
00546 BreakCycle(vtol_vertex_2d& junction, const int index,
00547            vtol_edge_2d& stronger, vtol_edge_2d_sptr& split,
00548            vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00549 {
00550   vdgl_digital_curve_sptr dc = (stronger.curve()->cast_to_vdgl_digital_curve());
00551   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00552 
00553   // 1. Move location of junction
00554   int jx = int(junction.x()), jy = int(junction.y());
00555   vertexMap.put(jx, jy, NULL); // erase old location
00556 
00557   vdgl_edgel tempedgel= dc->get_interpolator()->get_edgel_chain()->edgel( index);
00558   jx = int( tempedgel.x()), jy = int( tempedgel.y());
00559   junction.set_x(jx), junction.set_y(jy); // update new location
00560   vertexMap.put(jx, jy, &junction);
00561   edgeMap.put(jx, jy, NULL);
00562 
00563   // 2. Create 1-cycle, including junction pixel
00564   split = new vtol_edge_2d();
00565 
00566   vdgl_edgel_chain *es= new vdgl_edgel_chain;
00567   vdgl_interpolator *it= new vdgl_interpolator_linear( vdgl_edgel_chain_sptr( es));
00568   vdgl_digital_curve *ds = new vdgl_digital_curve( vdgl_interpolator_sptr( it));
00569 
00570   split->set_curve(*ds->cast_to_curve());
00571 
00572   int i=0;
00573   for (int k = index; k < len; i++,k++) {
00574     vdgl_edgel e= dc->get_interpolator()->get_edgel_chain()->edgel( k);
00575     es->add_edgel( e);
00576     edgeMap.put(int(e.x()), int(e.y()), split);
00577   }
00578   for (int k = 0; i < len; i++,k++) {
00579     vdgl_edgel c= dc->get_interpolator()->get_edgel_chain()->edgel( k);
00580     es->add_edgel( c);
00581     edgeMap.put(int(c.x()), int(c.y()), split);
00582   }
00583 
00584   split->set_v1(&junction);     // link both directions v-e
00585   split->set_v2(&junction);
00586 }
00587 
00588 
00589 //: Confirm there is a strong jump in response near a junction.
00590 // The location of this jump is however inaccurate, and so junctions
00591 // can not be localized accurately along the stronger chain.
00592 static bool
00593 ConfirmJunctionOnChain(int index, float threshold,
00594                        vtol_edge_2d& chain, const gevd_bufferxy& edgels)
00595 {
00596   vdgl_digital_curve_sptr dc = chain.curve()->cast_to_vdgl_digital_curve();
00597   const int len = dc->get_interpolator()->get_edgel_chain()->size()-1;
00598 
00599   if (len < 2*MINLENGTH-1) // will merge vertices instead of
00600     return false;               // breaking up chains
00601 
00602   const int fuzz = MINLENGTH-1; // from min length of broken chains
00603   const int radius = 3;         // gap < 3, around junction pixel
00604 
00605   for (int n = vcl_max(index-radius, fuzz); n <= vcl_min(index+radius,len-1-fuzz); n++)
00606   {
00607     vdgl_edgel cp1= dc->get_interpolator()->get_edgel_chain()->edgel(n+1);
00608     vdgl_edgel cm1= dc->get_interpolator()->get_edgel_chain()->edgel(n-1);
00609 
00610     if (vcl_fabs(floatPixel(edgels, int(cp1.x()), int(cp1.y())) -
00611                  floatPixel(edgels, int(cp1.x()), int(cm1.y())))
00612         > threshold)
00613     {
00614       return true;
00615     }
00616   }
00617   return false;
00618 }
00619 
00620 
00621 //: Break the edge at given index, and create two subchains from it.
00622 // Update the chain map accordingly.
00623 void
00624 BreakChain(vtol_vertex_2d& junction, const int index,
00625            vtol_edge_2d& stronger,
00626            vtol_edge_2d_sptr& longer, vtol_edge_2d_sptr& shorter,
00627            vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00628 {
00629   vdgl_digital_curve_sptr dc = stronger.curve()->cast_to_vdgl_digital_curve();
00630   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
00631 
00632   const int l0 = dc->get_interpolator()->get_edgel_chain()->size();
00633   const int l1 = index+1, l2 = l0-index;
00634 
00635   // 1. Move location of junction
00636   int jx = int(junction.x()), jy = int(junction.y());
00637   vertexMap.put(jx, jy, NULL); // erase old location
00638   vdgl_edgel c= dc->get_interpolator()->get_edgel_chain()->edgel( index);
00639   jx = int(c.x()), jy = int(c.y());
00640   junction.set_x(jx), junction.set_y(jy);       // update new location
00641   vertexMap.put(jx, jy, &junction);
00642   edgeMap.put( jx, jy, NULL);
00643 
00644   // 2. Create first subchain up to and including junction pixel.
00645   vtol_edge_2d_sptr edge1 = new vtol_edge_2d();    // create subchains, broken at junction.
00646 
00647   vdgl_edgel_chain *ec= new vdgl_edgel_chain;
00648   vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
00649   vdgl_digital_curve *dc1 = new vdgl_digital_curve( it);
00650 
00651   edge1->set_curve(*dc1);
00652 
00653   vdgl_edgel_chain *cxy1= ec;
00654 
00655   for (int k = 0; k < l1; k++)
00656   {
00657     cxy1->add_edgel ( (*cxy)[k] );
00658     (*cxy1)[k] = (*cxy)[k];
00659     edgeMap.put( int((*cxy1)[k].x()), int((*cxy1)[k].y()),  edge1);
00660   }
00661 
00662   //vtol_vertex_2d_sptr  v1 = stronger.v1().ptr()->cast_to_vertex_2d();
00663   vtol_vertex_sptr  v1 = stronger.v1();
00664 
00665   if (v1->numsup() == 1)        // dangling chain with end pt at v1
00666     edgeMap.put( int((*cxy1)[0].x()), int((*cxy1)[0].y()), NULL);
00667   edge1->set_v1(v1.ptr());            // link both directions v-e
00668   edge1->set_v2(&junction);     // unlink when stronger.UnProtect()
00669 
00670   // 3. Create second subchain from and including junction pixel.
00671   vtol_edge_2d_sptr edge2 = new vtol_edge_2d();    // create second subchain
00672 
00673   vdgl_edgel_chain *ec2= new vdgl_edgel_chain;
00674   vdgl_interpolator *it2= new vdgl_interpolator_linear( ec2);
00675   vdgl_digital_curve *dc2= new vdgl_digital_curve( it2);
00676 
00677   edge2->set_curve(*dc2);
00678 
00679   vdgl_edgel_chain *cxy2= ec2;
00680 
00681 
00682   for (int k = 0; k < l2; k++)
00683   {
00684     cxy2->add_edgel( cxy->edgel( k+index));
00685     edgeMap.put( int((*cxy2)[k].x()), int((*cxy2)[k].y()), edge2);
00686   }
00687 
00688   //vtol_vertex_2d_sptr  v2 = stronger.v2().ptr()->cast_to_vertex_2d();
00689   vtol_vertex_sptr  v2 = stronger.v2();
00690 
00691   if (v2->numsup() == 1)        // dangling chain with end pt at v2
00692     edgeMap.put( int((*cxy2)[l2-1].x()), int((*cxy2)[l2-1].y()), NULL);
00693 
00694   edge2->set_v1(&junction);     // link both directions v-e
00695   edge2->set_v2(v2.ptr());            // unlink when stronger.UnProtect()
00696 
00697   if (l1 >= l2)                 // sort longer/shorter chains
00698     longer = edge1, shorter = edge2;
00699   else
00700     longer = edge2, shorter = edge1;
00701 }
00702 
00703 
00704 //: Break the chain at given index, and create a loop.
00705 // Update the chain map accordingly.
00706 void
00707 LoopChain(vtol_vertex_2d& junction, const int index,
00708           vtol_edge_2d& chain,
00709           vtol_edge_2d_sptr& straight, vtol_edge_2d_sptr& curled,
00710           vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00711 {
00712   vdgl_digital_curve_sptr dc = chain.curve()->cast_to_vdgl_digital_curve();
00713 
00714   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
00715   const int l0 = cxy->size();
00716 
00717   // 1. Move location of junction
00718   int jx = int(junction.x()), jy = int(junction.y());
00719   vertexMap.put(jx, jy, NULL); // erase old location
00720   jx = int((*cxy)[index].x()), jy = int((*cxy)[index].y());
00721   junction.set_x(jx), junction.set_y(jy);       // update new location
00722   vertexMap.put(jx, jy, &junction);
00723   edgeMap.put( jx, jy, NULL);
00724 
00725   // 1. Find straight/curled chains
00726   straight = new vtol_edge_2d(), curled = new vtol_edge_2d();
00727   const int l1 = index+1, l2 = l0-index;
00728 
00729   if (&junction == chain.v1()->cast_to_vertex_2d())
00730   { // first subchain is curled
00731     vdgl_edgel_chain *ec= new vdgl_edgel_chain;
00732     vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
00733     vdgl_digital_curve *c= new vdgl_digital_curve( it);
00734 
00735     curled->set_curve(*c);
00736 
00737     vdgl_edgel_chain *xy= ec;
00738     for (int k = 0; k < l1; k++)
00739     {
00740     xy->add_edgel ( (*cxy)[k] );
00741     (*xy)[k] = (*cxy)[k];//, y[k] = cy[k];
00742       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), curled);
00743     }
00744 
00745     curled->set_v1(&junction);
00746     curled->set_v2(&junction);
00747 
00748 
00749     ec= new vdgl_edgel_chain;
00750     it= new vdgl_interpolator_linear( ec);
00751     c = new vdgl_digital_curve( it);    // second subchain is straight
00752 
00753     straight->set_curve(*c);
00754 
00755     xy= ec;
00756 
00757     for (int k = 0; k < l2; k++)
00758     {
00759       xy->add_edgel ( (*cxy)[k+index] );
00760       (*xy)[k] = (*cxy)[k+index];//, y[k] = dy[k];
00761       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), straight);
00762     }
00763 
00764     if (chain.v2()->numsup()==1)
00765     {
00766       edgeMap.put( int((*xy)[l2-1].x()), int((*xy)[l2-1].y()), NULL);
00767     }
00768 
00769     straight->set_v1(&junction);
00770     straight->set_v2(chain.v2().ptr());
00771   }
00772   else
00773   {                     // first subchain is straight
00774     vdgl_edgel_chain *ec= new vdgl_edgel_chain;
00775     vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
00776     vdgl_digital_curve *c= new vdgl_digital_curve( it);
00777 
00778     straight->set_curve(*c);
00779 
00780 
00781     vdgl_edgel_chain *xy= ec;
00782 
00783     for (int k = 0; k < l1; k++)
00784     {
00785     xy->add_edgel ( (*cxy)[k] );
00786       (*xy)[k] = (*cxy)[k];//, y[k] = cy[k];
00787       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), straight);
00788     }
00789 
00790     if (chain.v1()->numsup()==1)
00791     {
00792       edgeMap.put( int((*xy)[0].x()), int((*xy)[0].y()), NULL);
00793     }
00794 
00795     straight->set_v1(chain.v1().ptr());
00796     straight->set_v2(&junction);
00797 
00798 
00799     ec= new vdgl_edgel_chain;
00800     it= new vdgl_interpolator_linear( ec);
00801     c = new vdgl_digital_curve( it);    // second subchain is curled
00802 
00803     curled->set_curve(*c);
00804 
00805     xy = ec; // ->GetX(), y = c->GetY();
00806     for (int k = 0; k < l2; k++)
00807     {
00808       xy->add_edgel ( (*cxy)[k+index] );
00809       (*xy)[k] = (*cxy)[k+index];//, y[k] = dy[k];
00810       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), curled);
00811     }
00812 
00813     curled->set_v1(&junction);
00814     curled->set_v2(&junction);
00815   }
00816 }
00817 
00818 //: Find number of rays connected to a vertex.
00819 int
00820 NumConnectedRays(vtol_vertex_2d& v)
00821 {
00822   int nray = 0;
00823   vcl_vector<vtol_edge_sptr> segs; v.edges(segs);
00824   for (unsigned int i=0; i< segs.size(); i++)
00825   {
00826     if (segs[i]->v1()->cast_to_vertex_2d() == &v) nray++; // 1 for 1-chain
00827     if (segs[i]->v2()->cast_to_vertex_2d() == &v) nray++; // 2 for 1-cycle
00828   }
00829   return nray;
00830 }
00831 
00832 
00833 //: Detect touching another junction or end point, from an end point of a dangling chain.
00834 vtol_vertex_2d_sptr
00835 DetectTouch(const vtol_vertex_2d& end, const int maxSpiral,
00836             vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00837 {
00838   const int jx = int(end.x()), jy = int(end.y());
00839   for (int l = 0, n = 0; l < maxSpiral; l++) {  // increasing radius of spiral
00840     vtol_vertex_2d_sptr  other = NULL;      // prefer junction over endpt
00841     int maxray = 0;            // largest number of rays
00842     for (; n < RNS[l]; n++) {  // 4- then 8-connected
00843       vtol_vertex_2d_sptr nbr = vertexMap.get(jx+RIS[n], jy+RJS[n]);
00844       int nray = (nbr ? NumConnectedRays(*nbr) : 0);
00845       if (nray > maxray) {
00846         maxray = nray;         // number of rays connected to it
00847         other = nbr;           // better neighbor
00848       }
00849     }
00850     if (maxray)                // find larger/other junction
00851       return other;
00852   }
00853   return NULL;
00854 }
00855 
00856 
00857 //: Find dangling edges connected to vertex
00858 vtol_edge_2d_sptr
00859 DanglingEdge(vtol_vertex_2d& v)
00860 {
00861   vcl_vector<vtol_edge_sptr> segs; v.edges(segs);
00862 
00863   if (segs.size()==1)
00864     return segs[0]->cast_to_edge_2d();
00865   else
00866     return 0;
00867 }
00868 
00869 
00870 //: Merge 2 end points of a same chain.
00871 // Update global maps.
00872 void
00873 MergeEndPtsOfChain(vtol_vertex_2d& endpt, vtol_vertex_2d& other, vtol_edge_2d& common,
00874                    vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>&vertexMap)
00875 {
00876   int px = int(other.x()), py = int(other.y());
00877   vertexMap.put(px, py, NULL); // erase old location
00878   edgeMap.put( px, py, &common);
00879   if (common.v1() == &other)  // remove links to other endpt
00880     common.set_v1(&endpt);
00881   else
00882     common.set_v2(&endpt);
00883 }
00884 
00885 
00886 //: Merge 2 touching chains into 1, deleting the 2 touching end points and their chains.
00887 // Smooth away short kinks is delayed for later.  Update global maps.
00888 void
00889 MergeEndPtTouchingEndPt(vtol_vertex_2d& end1, vtol_vertex_2d