contrib/gel/gevd/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           }
00330           else  {             // reach other end point
00331             floatPixel(edgels, i, j) = 0; // prune isolated pixel-pairs
00332             floatPixel(edgels, ii, jj) = 0;
00333             continue;
00334           }
00335         }
00336 
00337         // 2.3. Watch out for zig-zag at 2nd pixel, from LR-TD scans
00338         if ((x - ii)*(ii - xloc[0]) +
00339             (y - jj)*(jj - yloc[0]) < 0) {
00340           xloc[1] = xloc[0], yloc[1] = yloc[0]; // swap first 2 points
00341           xloc[0] = ii, yloc[0] = jj; // to eliminate zig-zag
00342         }
00343 
00344         // 2.4. Collect both directions & extension points if 1-chain
00345         while (NextPixel(x, y, edgels))                // trace along first dir, 4-connected
00346           RecordPixel(x, y, edgels, xloc, yloc);       // and stronger first
00347 
00348         if (vcl_abs(xloc[0]-x) > 1 ||                  // disjoint first/last pixel
00349             vcl_abs(yloc[0]-y) > 1) {                  // so must be a 1-chain with end points
00350           if (next_pixel(x, y, *vertexMap))            // search for extra links to
00351             xloc.push_back(x), yloc.push_back(y);      // detected junctions
00352           x = xloc[0], y = yloc[0];                    // start again from first pixel
00353 
00354           vcl_vector<int> xloctemp( xloc.size()), yloctemp( yloc.size());
00355           for (unsigned int iii=0; iii< xloc.size(); iii++)
00356             xloctemp[iii]= xloc[xloc.size()-1-iii];
00357           for (unsigned int jjj=0; jjj< yloc.size(); jjj++)
00358             yloctemp[jjj]= yloc[yloc.size()-1-jjj];
00359 
00360           while (NextPixel(x, y, edgels)) // trace along other dir
00361             RecordPixel(x, y, edgels, xloc, yloc);
00362           if (next_pixel(x, y, *vertexMap)) // search for extra links to
00363             xloc.push_back(x), yloc.push_back(y); // detected junctions
00364         }
00365         int len = xloc.size();
00366 
00367         // 2.5. Check for isolated contours that are too short
00368         if (len < minLength) {  // zero or too few internal pixels
00369           for (int k = 0; k < len; k++) // zero or too few internal pixels
00370             floatPixel(edgels, xloc[k], yloc[k]) = 0; // prune short chains
00371           continue;
00372         }
00373 
00374         // Thin zig-zags on the contours? Zig-zags happen at the 2
00375         // end points because of extension, or inside the contour
00376         // because of 4/8-connected tracing through noisy chain pixels,
00377         // and large shifts for subpixel locations.
00378         // Defer the elimination of zig-zags to gevd_contour::SubPixelAccuracy()
00379         // or gevd_contour::EqualizeSpacing()
00380 
00381         // 2.6. Create network of chains, touching, possibly ending
00382         // at same junction, but never crossing one another
00383         vtol_edge_2d_sptr edge = new vtol_edge_2d();
00384         vdgl_edgel_chain * ec = new vdgl_edgel_chain;
00385         vdgl_interpolator * it = new vdgl_interpolator_linear(ec);
00386         vdgl_digital_curve * dc = new vdgl_digital_curve(it); // include end points
00387 
00388         for (int k=0; k< len; k++)
00389         {
00390           x= xloc[k];
00391           y= yloc[k];
00392           ec->add_edgel( vdgl_edgel( x, y));
00393           edgeMap->put(x, y, edge);
00394         }
00395         edge->set_curve(*dc);
00396         LookupTableInsert(edges, edge);
00397       }
00398   }
00399 
00400   // 3. Restore cache to original state
00401   for (int k = 0; k < njunction; k++)  // clear all void*/float labels
00402     vertexMap->put(junctionx[k], junctiony[k],NULL);
00403   for (int j = rmax; j <= ymax; j++)
00404     for (int i = rmax; i <= xmax; i++)
00405       if (floatPixel(edgels, i, j) < 0) // undo mark put by RecordPixel
00406         floatPixel(edgels, i, j) = - floatPixel(edgels, i, j);
00407 
00408   if (talkative)
00409     vcl_cout << "Find " << edges.size()
00410              << " chains/cycles, with pixels > " << minLength
00411              << " and strength > " << minStrength
00412 #ifdef DEBUG
00413              << ", in " << t.real() << " msecs."
00414 #endif
00415              << vcl_endl;
00416   return edges.size();  // number of chains found so far
00417 }
00418 
00419 
00420 //:
00421 // Check that end point of a weak contour touches another stronger
00422 // contour at an internal pixel. Localize the junction to pixel accuracy
00423 // by searching for shortest distance from end point to chain.
00424 // Gaussian smoothing can put local maximum change in filter response
00425 // 1 pixel away from this junction location.
00426 // Update junction map.
00427 bool
00428 DetectJunction(vtol_vertex_2d& end, int& index,
00429                vtol_edge_2d_sptr& weaker, vtol_edge_2d_sptr& stronger,
00430                const int maxSpiral,
00431                const gevd_bufferxy& edgels, vbl_array_2d<vtol_edge_2d_sptr>& edgeMap)
00432 {
00433   // 0. Must be an end point of a dangling 1-chain
00434   if (end.numsup() > 1)         // avoid junction and 1-cycle
00435     return false;
00436   vcl_vector<vtol_edge_sptr> edges; end.edges(edges);
00437   weaker = edges[0]->cast_to_edge_2d();      // dangling edge must be a weaker contour
00438   vdgl_digital_curve_sptr dc = weaker->curve()->cast_to_vdgl_digital_curve();
00439 
00440   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00441 
00442   // 1. Mark off pixels at end pt to find junction of a contour to itself
00443 
00444   const int rfuzz = vcl_min(len, 3*MINLENGTH);
00445   vtol_edge_2d_sptr* labels = new vtol_edge_2d_sptr[rfuzz];
00446   if (&end == weaker->v1()->cast_to_vertex_2d())
00447     for (int r = 0; r < rfuzz; r++) {
00448       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel( r);
00449       labels[r] = edgeMap.get( int(edgel.get_x()), int(edgel.get_y()));
00450       edgeMap.put(int(edgel.get_x()), int(edgel.get_y()), NULL);
00451     }
00452   else
00453     for (int r = 0; r < rfuzz; r++) {
00454       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel(len-1-r);
00455       labels[r] = edgeMap.get( int( edgel.get_x()), int( edgel.get_y()));
00456       edgeMap.put(int(edgel.get_x()), int(edgel.get_y()), NULL);
00457     }
00458 
00459   // 2. Find another stronger contour touched by this end point < gap.
00460   stronger = NULL;              // contour can join with itself
00461   int jx = int(end.x()), jy = int(end.y());
00462   for (int l = 0, n = 0; l < maxSpiral; l++) {  // increasing radius of spiral
00463     float maxpix = 0; int maxn = 0;     // strongest strength at this radius
00464     for (; n < RNS[l]; n++) {
00465       int x = jx+RIS[n], y = jy+RJS[n];
00466       if (edgeMap.get(x, y) && // find another contour or itself
00467           floatPixel(edgels, x, y) > maxpix) {
00468         maxpix = floatPixel(edgels, x, y);
00469         maxn = n;               // better neighbor
00470       }
00471     }
00472     if (maxpix) {               // location of junction on contour
00473       stronger = edgeMap.get(jx+RIS[maxn], jy+RJS[maxn]);
00474       jx += RIS[maxn], jy += RJS[maxn];
00475       break;
00476     }
00477   }
00478   // restore edgeMap around end point (undo step 1)
00479   if (&end == weaker->v1()->cast_to_vertex_2d())
00480     for (int r=0; r< rfuzz; r++) {
00481       vdgl_edgel edge= dc->get_interpolator()->get_edgel_chain()->edgel(r);
00482       edgeMap.put(int( edge.get_x()), int( edge.get_y()), labels[r]);
00483     }
00484   else
00485     for (int r=0; r< rfuzz; r++) {
00486       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel(len-1-r);
00487       edgeMap.put(int( edgel.get_x()), int( edgel.get_y()),labels[r]);
00488     }
00489   delete[] labels;
00490 
00491   if (!stronger)                // do not find any edge in search region
00492     return false;
00493 
00494   // 3. Find index location of junction on this contour
00495   index = int(INVALID);
00496 
00497   vdgl_digital_curve_sptr dc2 =(stronger->curve()->cast_to_vdgl_digital_curve());
00498 
00499   // find corresponding index on contour
00500   for (unsigned int n = 0; n < dc2->get_interpolator()->get_edgel_chain()->size(); ++n)
00501   {
00502     vdgl_edgel edgel= dc2->get_interpolator()->get_edgel_chain()->edgel(n);
00503 
00504     if ( int( edgel.get_x())== jx && int( edgel.get_y())== jy)
00505     {
00506       index = n;
00507       break;
00508     }
00509   }
00510   return true;
00511 }
00512 
00513 
00514 //: Confirm there is a strong jump in response near a junction.
00515 // The location of this jump is however inaccurate, and so junctions
00516 // can not be localized accurately along the stronger cycle.
00517 static bool
00518 ConfirmJunctionOnCycle(int index, float threshold,
00519                        vtol_edge_2d& cycle, const gevd_bufferxy& edgels)
00520 {
00521   vdgl_digital_curve_sptr dc =(cycle.curve()->cast_to_vdgl_digital_curve());
00522   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00523   const int wrap = 10*len;      // for positive index
00524   const int radius = 3;         // gap < 3, around junction pixel
00525 
00526   for (int n = index-radius; n <= index+radius; n++)
00527   {
00528     int nm = (n-1+wrap)%len;    // modulo operations to wrap at borders
00529     int np = (n+1+wrap)%len;
00530 
00531     vdgl_edgel edgel_m= dc->get_interpolator()->get_edgel_chain()->edgel( nm);
00532     vdgl_edgel edgel_p= dc->get_interpolator()->get_edgel_chain()->edgel( np);
00533 
00534     if (vcl_fabs(floatPixel(edgels, int( edgel_p.x()), int( edgel_p.y())) -
00535                  floatPixel(edgels, int( edgel_m.x()), int( edgel_m.y())))
00536         > threshold)
00537       return true;
00538   }
00539   return false;
00540 }
00541 
00542 
00543 //:
00544 // Break the cycle at given index, and create new cycle from/to
00545 // and not including index pixel. Update the chain map accordingly.
00546 void
00547 BreakCycle(vtol_vertex_2d& junction, const int index,
00548            vtol_edge_2d& stronger, vtol_edge_2d_sptr& split,
00549            vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00550 {
00551   vdgl_digital_curve_sptr dc = (stronger.curve()->cast_to_vdgl_digital_curve());
00552   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00553 
00554   // 1. Move location of junction
00555   int jx = int(junction.x()), jy = int(junction.y());
00556   vertexMap.put(jx, jy, NULL); // erase old location
00557 
00558   vdgl_edgel tempedgel= dc->get_interpolator()->get_edgel_chain()->edgel( index);
00559   jx = int( tempedgel.x()), jy = int( tempedgel.y());
00560   junction.set_x(jx), junction.set_y(jy); // update new location
00561   vertexMap.put(jx, jy, &junction);
00562   edgeMap.put(jx, jy, NULL);
00563 
00564   // 2. Create 1-cycle, including junction pixel
00565   split = new vtol_edge_2d();
00566 
00567   vdgl_edgel_chain *es= new vdgl_edgel_chain;
00568   vdgl_interpolator *it= new vdgl_interpolator_linear( vdgl_edgel_chain_sptr( es));
00569   vdgl_digital_curve *ds = new vdgl_digital_curve( vdgl_interpolator_sptr( it));
00570 
00571   split->set_curve(*ds->cast_to_curve());
00572 
00573   int i=0;
00574   for (int k = index; k < len; i++,k++) {
00575     vdgl_edgel e= dc->get_interpolator()->get_edgel_chain()->edgel( k);
00576     es->add_edgel( e);
00577     edgeMap.put(int(e.x()), int(e.y()), split);
00578   }
00579   for (int k = 0; i < len; i++,k++) {
00580     vdgl_edgel c= dc->get_interpolator()->get_edgel_chain()->edgel( k);
00581     es->add_edgel( c);
00582     edgeMap.put(int(c.x()), int(c.y()), split);
00583   }
00584 
00585   split->set_v1(&junction);     // link both directions v-e
00586   split->set_v2(&junction);
00587 }
00588 
00589 
00590 //: Confirm there is a strong jump in response near a junction.
00591 // The location of this jump is however inaccurate, and so junctions
00592 // can not be localized accurately along the stronger chain.
00593 static bool
00594 ConfirmJunctionOnChain(int index, float threshold,
00595                        vtol_edge_2d& chain, const gevd_bufferxy& edgels)
00596 {
00597   vdgl_digital_curve_sptr dc = chain.curve()->cast_to_vdgl_digital_curve();
00598   const int len = dc->get_interpolator()->get_edgel_chain()->size()-1;
00599 
00600   if (len < 2*MINLENGTH-1) // will merge vertices instead of
00601     return false;               // breaking up chains
00602 
00603   const int fuzz = MINLENGTH-1; // from min length of broken chains
00604   const int radius = 3;         // gap < 3, around junction pixel
00605 
00606   for (int n = vcl_max(index-radius, fuzz); n <= vcl_min(index+radius,len-1-fuzz); n++)
00607   {
00608     vdgl_edgel cp1= dc->get_interpolator()->get_edgel_chain()->edgel(n+1);
00609     vdgl_edgel cm1= dc->get_interpolator()->get_edgel_chain()->edgel(n-1);
00610 
00611     if (vcl_fabs(floatPixel(edgels, int(cp1.x()), int(cp1.y())) -
00612                  floatPixel(edgels, int(cp1.x()), int(cm1.y())))
00613         > threshold)
00614     {
00615       return true;
00616     }
00617   }
00618   return false;
00619 }
00620 
00621 
00622 //: Break the edge at given index, and create two subchains from it.
00623 // Update the chain map accordingly.
00624 void
00625 BreakChain(vtol_vertex_2d& junction, const int index,
00626            vtol_edge_2d& stronger,
00627            vtol_edge_2d_sptr& longer, vtol_edge_2d_sptr& shorter,
00628            vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00629 {
00630   vdgl_digital_curve_sptr dc = stronger.curve()->cast_to_vdgl_digital_curve();
00631   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
00632 
00633   const int l0 = dc->get_interpolator()->get_edgel_chain()->size();
00634   const int l1 = index+1, l2 = l0-index;
00635 
00636   // 1. Move location of junction
00637   int jx = int(junction.x()), jy = int(junction.y());
00638   vertexMap.put(jx, jy, NULL); // erase old location
00639   vdgl_edgel c= dc->get_interpolator()->get_edgel_chain()->edgel( index);
00640   jx = int(c.x()), jy = int(c.y());
00641   junction.set_x(jx), junction.set_y(jy);       // update new location
00642   vertexMap.put(jx, jy, &junction);
00643   edgeMap.put( jx, jy, NULL);
00644 
00645   // 2. Create first subchain up to and including junction pixel.
00646   vtol_edge_2d_sptr edge1 = new vtol_edge_2d();    // create subchains, broken at junction.
00647 
00648   vdgl_edgel_chain *ec= new vdgl_edgel_chain;
00649   vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
00650   vdgl_digital_curve *dc1 = new vdgl_digital_curve( it);
00651 
00652   edge1->set_curve(*dc1);
00653 
00654   vdgl_edgel_chain *cxy1= ec;
00655 
00656   for (int k = 0; k < l1; k++)
00657   {
00658     cxy1->add_edgel ( (*cxy)[k] );
00659     (*cxy1)[k] = (*cxy)[k];
00660     edgeMap.put( int((*cxy1)[k].x()), int((*cxy1)[k].y()),  edge1);
00661   }
00662 
00663   //vtol_vertex_2d_sptr  v1 = stronger.v1().ptr()->cast_to_vertex_2d();
00664   vtol_vertex_sptr  v1 = stronger.v1();
00665 
00666   if (v1->numsup() == 1)        // dangling chain with end pt at v1
00667     edgeMap.put( int((*cxy1)[0].x()), int((*cxy1)[0].y()), NULL);
00668   edge1->set_v1(v1.ptr());            // link both directions v-e
00669   edge1->set_v2(&junction);     // unlink when stronger.UnProtect()
00670 
00671   // 3. Create second subchain from and including junction pixel.
00672   vtol_edge_2d_sptr edge2 = new vtol_edge_2d();    // create second subchain
00673 
00674   vdgl_edgel_chain *ec2= new vdgl_edgel_chain;
00675   vdgl_interpolator *it2= new vdgl_interpolator_linear( ec2);
00676   vdgl_digital_curve *dc2= new vdgl_digital_curve( it2);
00677 
00678   edge2->set_curve(*dc2);
00679 
00680   vdgl_edgel_chain *cxy2= ec2;
00681 
00682 
00683   for (int k = 0; k < l2; k++)
00684   {
00685     cxy2->add_edgel( cxy->edgel( k+index));
00686     edgeMap.put( int((*cxy2)[k].x()), int((*cxy2)[k].y()), edge2);
00687   }
00688 
00689   //vtol_vertex_2d_sptr  v2 = stronger.v2().ptr()->cast_to_vertex_2d();
00690   vtol_vertex_sptr  v2 = stronger.v2();
00691 
00692   if (v2->numsup() == 1)        // dangling chain with end pt at v2
00693     edgeMap.put( int((*cxy2)[l2-1].x()), int((*cxy2)[l2-1].y()), NULL);
00694 
00695   edge2->set_v1(&junction);     // link both directions v-e
00696   edge2->set_v2(v2.ptr());            // unlink when stronger.UnProtect()
00697 
00698   if (l1 >= l2)                 // sort longer/shorter chains
00699     longer = edge1, shorter = edge2;
00700   else
00701     longer = edge2, shorter = edge1;
00702 }
00703 
00704 
00705 //: Break the chain at given index, and create a loop.
00706 // Update the chain map accordingly.
00707 void
00708 LoopChain(vtol_vertex_2d& junction, const int index,
00709           vtol_edge_2d& chain,
00710           vtol_edge_2d_sptr& straight, vtol_edge_2d_sptr& curled,
00711           vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00712 {
00713   vdgl_digital_curve_sptr dc = chain.curve()->cast_to_vdgl_digital_curve();
00714 
00715   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
00716   const int l0 = cxy->size();
00717 
00718   // 1. Move location of junction
00719   int jx = int(junction.x()), jy = int(junction.y());
00720   vertexMap.put(jx, jy, NULL); // erase old location
00721   jx = int((*cxy)[index].x()), jy = int((*cxy)[index].y());
00722   junction.set_x(jx), junction.set_y(jy);       // update new location
00723   vertexMap.put(jx, jy, &junction);
00724   edgeMap.put( jx, jy, NULL);
00725 
00726   // 1. Find straight/curled chains
00727   straight = new vtol_edge_2d(), curled = new vtol_edge_2d();
00728   const int l1 = index+1, l2 = l0-index;
00729 
00730   if (&junction == chain.v1()->cast_to_vertex_2d())
00731   { // first subchain is curled
00732     vdgl_edgel_chain *ec= new vdgl_edgel_chain;
00733     vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
00734     vdgl_digital_curve *c= new vdgl_digital_curve( it);
00735 
00736     curled->set_curve(*c);
00737 
00738     vdgl_edgel_chain *xy= ec;
00739     for (int k = 0; k < l1; k++)
00740     {
00741     xy->add_edgel ( (*cxy)[k] );
00742     (*xy)[k] = (*cxy)[k];//, y[k] = cy[k];
00743       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), curled);
00744     }
00745 
00746     curled->set_v1(&junction);
00747     curled->set_v2(&junction);
00748 
00749 
00750     ec= new vdgl_edgel_chain;
00751     it= new vdgl_interpolator_linear( ec);
00752     c = new vdgl_digital_curve( it);    // second subchain is straight
00753 
00754     straight->set_curve(*c);
00755 
00756     xy= ec;
00757 
00758     for (int k = 0; k < l2; k++)
00759     {
00760       xy->add_edgel ( (*cxy)[k+index] );
00761       (*xy)[k] = (*cxy)[k+index];//, y[k] = dy[k];
00762       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), straight);
00763     }
00764 
00765     if (chain.v2()->numsup()==1)
00766     {
00767       edgeMap.put( int((*xy)[l2-1].x()), int((*xy)[l2-1].y()), NULL);
00768     }
00769 
00770     straight->set_v1(&junction);
00771     straight->set_v2(chain.v2().ptr());
00772   }
00773   else
00774   {                     // first subchain is straight
00775     vdgl_edgel_chain *ec= new vdgl_edgel_chain;
00776     vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
00777     vdgl_digital_curve *c= new vdgl_digital_curve( it);
00778 
00779     straight->set_curve(*c);
00780 
00781 
00782     vdgl_edgel_chain *xy= ec;
00783 
00784     for (int k = 0; k < l1; k++)
00785     {
00786     xy->add_edgel ( (*cxy)[k] );
00787       (*xy)[k] = (*cxy)[k];//, y[k] = cy[k];
00788       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), straight);
00789     }
00790 
00791     if (chain.v1()->numsup()==1)
00792     {
00793       edgeMap.put( int((*xy)[0].x()), int((*xy)[0].y()), NULL);
00794     }
00795 
00796     straight->set_v1(chain.v1().ptr());
00797     straight->set_v2(&junction);
00798 
00799 
00800     ec= new vdgl_edgel_chain;
00801     it= new vdgl_interpolator_linear( ec);
00802     c = new vdgl_digital_curve( it);    // second subchain is curled
00803 
00804     curled->set_curve(*c);
00805 
00806     xy = ec; // ->GetX(), y = c->GetY();
00807     for (int k = 0; k < l2; k++)
00808     {
00809       xy->add_edgel ( (*cxy)[k+index] );
00810       (*xy)[k] = (*cxy)[k+index];//, y[k] = dy[k];
00811       edgeMap.put( int((*xy)[k].x()), int((*xy)[k].y()), curled);
00812     }
00813 
00814     curled->set_v1(&junction);
00815     curled->set_v2(&junction);
00816   }
00817 }
00818 
00819 //: Find number of rays connected to a vertex.
00820 int
00821 NumConnectedRays(vtol_vertex_2d& v)
00822 {
00823   int nray = 0;
00824   vcl_vector<vtol_edge_sptr> segs; v.edges(segs);
00825   for (unsigned int i=0; i< segs.size(); i++)
00826   {
00827     if (segs[i]->v1()->cast_to_vertex_2d() == &v) nray++; // 1 for 1-chain
00828     if (segs[i]->v2()->cast_to_vertex_2d() == &v) nray++; // 2 for 1-cycle
00829   }
00830   return nray;
00831 }
00832 
00833 
00834 //: Detect touching another junction or end point, from an end point of a dangling chain.
00835 vtol_vertex_2d_sptr
00836 DetectTouch(const vtol_vertex_2d& end, const int maxSpiral,
00837             vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00838 {
00839   const int jx = int(end.x()), jy = int(end.y());
00840   for (int l = 0, n = 0; l < maxSpiral; l++) {  // increasing radius of spiral
00841     vtol_vertex_2d_sptr  other = NULL;      // prefer junction over endpt
00842     int maxray = 0;            // largest number of rays
00843     for (; n < RNS[l]; n++) {  // 4- then 8-connected
00844       vtol_vertex_2d_sptr nbr = vertexMap.get(jx+RIS[n], jy+RJS[n]);
00845       int nray = (nbr ? NumConnectedRays(*nbr) : 0);
00846       if (nray > maxray) {
00847         maxray = nray;         // number of rays connected to it
00848         other = nbr;           // better neighbor
00849       }
00850     }
00851     if (maxray)                // find larger/other junction
00852       return other;
00853   }
00854   return NULL;
00855 }
00856 
00857 
00858 //: Find dangling edges connected to vertex
00859 vtol_edge_2d_sptr
00860 DanglingEdge(vtol_vertex_2d& v)
00861 {
00862   vcl_vector<vtol_edge_sptr> segs; v.edges(segs);
00863 
00864   if (segs.size()==1)
00865     return segs[0]->cast_to_edge_2d();
00866   else
00867     return 0;
00868 }
00869 
00870 
00871 //: Merge 2 end points of a same chain.
00872 // Update global maps.
00873 void
00874 MergeEndPtsOfChain(vtol_vertex_2d& endpt, vtol_vertex_2d& other, vtol_edge_2d& common,
00875                    vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>&vertexMap)
00876 {
00877   int px = int(other.x()), py = int(other.y());
00878   vertexMap.put(px, py, NULL); // erase old location
00879   edgeMap.put( px, py, &common);
00880   if (common.v1() == &other)  // remove links to other endpt
00881     common.set_v1(&endpt);
00882   else
00883     common.set_v2(&endpt);
00884 }
00885 
00886 
00887 //: Merge 2 touching chains into 1, deleting the 2 touching end points and their chains.
00888 // Smooth away short kinks is delayed for later.  Update global maps.
00889 void
00890 MergeEndPtTouchingEndPt(vtol_vertex_2d& end1, vtol_vertex_2d& end2,
00891                         vtol_edge_2d_sptr& merge, vtol_edge_2d_sptr& longer, vtol_edge_2d_sptr& shorter,
00892                         vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00893 {
00894   // 1. Retrieve the dangling edges/chains
00895   vcl_vector<vtol_edge_sptr> edges;
00896   end1.edges(edges); vtol_edge_2d_sptr edge1 = edges[0]->cast_to_edge_2d();        // dangling edges
00897   end2.edges(edges); vtol_edge_2d_sptr edge2 = edges[0]->cast_to_edge_2d();
00898 
00899   // 2. Create merged edge/chain
00900   vdgl_digital_curve_sptr dc1 = edge1->curve()->cast_to_vdgl_digital_curve();
00901   const int l1 = dc1->get_interpolator()->get_edgel_chain()->size();
00902   vdgl_digital_curve_sptr dc2 = edge2->curve()->cast_to_vdgl_digital_curve();
00903   const int l2 = dc2->get_interpolator()->get_edgel_chain()->size();
00904   const int len = l1+l2;
00905 
00906   merge = new vtol_edge_2d();
00907 
00908   vdgl_edgel_chain *ec = new vdgl_edgel_chain;
00909   vdgl_interpolator *it = new vdgl_interpolator_linear( ec);
00910   vdgl_digital_curve *dc = new vdgl_digital_curve(it);
00911 
00912   merge->set_curve(*dc);
00913 
00914   vdgl_edgel_chain *cxy= ec;
00915   vtol_vertex_sptr v1, v2;      // vertices of merge edge
00916   int k = 0;                    // index in merge array
00917 
00918   vdgl_edgel_chain_sptr cxy1= dc1->get_interpolator()->get_edgel_chain();
00919   if (edge1->v2() == &end1) {
00920     for (int i = 0; i < l1; i++, k++)
00921     {
00922       cxy->add_edgel ( (*cxy1)[i] );
00923       (*cxy)[k] = (*cxy1)[i];//, cy[k] = cy1[i];
00924     }
00925     v1 = edge1->v1();
00926   }
00927   else {                      // reverse collection
00928     for (int i = l1-1; i >= 0; i--, k++)
00929     {
00930       cxy->add_edgel ( (*cxy1)[i] );
00931       (*cxy)[k] = (*cxy1)[i];//, cy[k] = cy1[i];
00932     }
00933     v1 = edge1->v2();
00934   }
00935   merge->set_v1(v1.ptr());
00936 
00937   vdgl_edgel_chain_sptr cxy2= dc2->get_interpolator()->get_edgel_chain();
00938 
00939   if (edge2->v1() == &end2)
00940   {
00941     for (int i = 0; i < l2; i++, k++)
00942     {
00943       cxy->add_edgel ( (*cxy2)[i] );
00944       (*cxy)[k] = (*cxy2)[i];//, cy[k] = cy2[i];
00945     }
00946 //  v2 = edge2->v2().ptr()->cast_to_vertex_2d();
00947     v2 = edge2->v2()->cast_to_vertex_2d();
00948   }
00949   else {
00950     // reverse collection
00951     for (int i = l2-1; i >= 0; i--, k++)
00952     {
00953       cxy->add_edgel ( (*cxy2)[i] );
00954       (*cxy)[k] = (*cxy2)[i];//, cy[k] = cy2[i];
00955     }
00956     v2 = edge2->v1()->cast_to_vertex_2d();
00957   }
00958   merge->set_v2(v2.ptr());
00959 
00960   // 3. Update global maps
00961   vertexMap.put(int(end1.x()), int(end1.y()), NULL);
00962   vertexMap.put(int(end2.x()), int(end2.y()), NULL);
00963   const int last = len-1;
00964   for (k = 1; k < last; k++)
00965     edgeMap.put( int((*cxy)[k].x()), int((*cxy)[k].y()), merge);
00966   if (edgeMap.get( int((*cxy)[0].x()), int((*cxy)[0].y())))
00967     edgeMap.put( int((*cxy)[0].x()), int((*cxy)[0].y()), merge);
00968   if (edgeMap.get( int((*cxy)[last].x()), int((*cxy)[last].y())))
00969     edgeMap.put( int((*cxy)[last].x()), int((*cxy)[last].y()), merge);
00970 
00971   if (l1 >= l2)                 // sort out length of deleted subchains
00972     longer = edge1, shorter = edge2;
00973   else
00974     longer = edge2, shorter = edge1;
00975 }
00976 
00977 
00978 //: Merge an end point into a touching junction.
00979 // Update global maps.
00980 void
00981 MergeEndPtTouchingJunction(vtol_vertex_2d &endpt, vtol_vertex_2d& junction,
00982                            vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>&vertexMap)
00983 {
00984   vcl_vector<vtol_edge_sptr> edges; endpt.edges(edges);
00985   vtol_edge_2d_sptr edge = edges[0]->cast_to_edge_2d(); // dangling edge terminating at end pt
00986   int px = int(endpt.x()), py = int(endpt.y());
00987   vertexMap.put(px, py, NULL); // erase old location
00988   edgeMap.put( px, py, edge);
00989   if (edge->v1() == &endpt)     // change the links both directions v-e
00990     edge->set_v1(&junction);    // unlink when endpt.UnProtect()
00991   else
00992     edge->set_v2(&junction);
00993 }
00994 
00995 
00996 //:
00997 // Find junctions from end points touching at an interior point
00998 // of a chain, with detectable jump in filter response.
00999 // Localize these junctions on the stronger contour to pixel accuracy,
01000 // and break stronger chain into subchains.
01001 // Also merge end points touching another end point or junction.
01002 // Return the number of end points and junctions bounding
01003 // all chains/cycles detected in gevd_contour::FindChains.
01004 // Deletion/insertion to the network must be done completely,
01005 // so that the connectivity links are updated.  Protected.
01006 int
01007 gevd_contour::FindJunctions(gevd_bufferxy& edgels,
01008                             vcl_vector<vtol_edge_2d_sptr>& edges,
01009                             vcl_vector<vtol_vertex_2d_sptr >& vertices)
01010 {
01011 #ifdef DEBUG
01012   vul_timer t;
01013 #endif
01014   if (!edges.size())
01015   {
01016     vcl_cerr << "gevd_contour::FindChains must precede gevd_contour::FindJunctions.\n";
01017     return 0;
01018   }
01019   vcl_vector<vtol_edge_2d_sptr>::iterator eit;
01020 
01021   for (eit= edges.begin(); eit!=edges.end(); eit++) {
01022     (*eit)->describe(vcl_cout, 2);
01023   }
01024   vcl_vector<vtol_vertex_2d_sptr>::iterator vit;
01025 
01026   for (vit= vertices.begin(); vit!=vertices.end(); vit++) {
01027     (*vit)->describe(vcl_cout, 2);
01028   }
01029 
01030 
01031   // 1. Create end points or junctions, for all 1-chains.
01032   const float connect_fuzz = 2;
01033 
01034   for (unsigned int i=0; i< edges.size(); i++)
01035   {
01036     vtol_edge_2d_sptr edge = edges[i];
01037     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01038     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01039 
01040     const int last = cxy->size()-1;
01041     if (vcl_fabs((*cxy)[0].x()-(*cxy)[last].x()) > connect_fuzz || // disjoint first/last pixel
01042         vcl_fabs((*cxy)[0].y()-(*cxy)[last].y()) > connect_fuzz)
01043     { // so must be a 1-chain
01044       int x = int((*cxy)[0].x()), y = int((*cxy)[0].y());
01045 
01046       vtol_vertex_2d_sptr v1 = vertexMap->get(x, y);
01047       if (!v1)
01048       {         // check for collision
01049         v1 = new vtol_vertex_2d((*cxy)[0].x(), (*cxy)[0].y()); // 1st point in chain
01050         vertexMap->put(x, y, v1);
01051         LookupTableInsert(vertices, v1);
01052       }
01053       else
01054       {
01055         edgeMap->put( x, y, NULL); // erase junction point
01056       }
01057 
01058       edge->set_v1(v1->cast_to_vertex());         // link both directions v-e
01059       x = int((*cxy)[last].x()), y = int((*cxy)[last].y());
01060 
01061       vtol_vertex_2d_sptr v2 = vertexMap->get(x, y);
01062 
01063       if (!v2)
01064       {         // check for collision
01065         v2 = new vtol_vertex_2d((*cxy)[last].x(), (*cxy)[last].y()); // last point in chain
01066         vertexMap->put(x, y, v2);
01067         LookupTableInsert(vertices, v2);
01068       }
01069       else
01070       {
01071         edgeMap->put( x, y, NULL); // erase junction point
01072       }
01073 
01074       edge->set_v2(v2->cast_to_vertex());         // link both directions v-e
01075     }
01076   }
01077 
01078 
01079   // 2. Localize a junction, when an end point of a dangling contour
01080   // touches another contour or itself at an interior point.
01081   int jcycle = 0, jchain = 0;   // number of junctions with cycle/chain
01082 
01083   for (unsigned int i=0; i< vertices.size(); i++)
01084   {
01085     vtol_vertex_2d_sptr  end = vertices[i];
01086     vtol_edge_2d_sptr weaker = NULL, stronger = NULL; // weaker touches stronger
01087     int index;                  // location on stronger contour
01088     if (DetectJunction(*end, index,
01089                        weaker, stronger, maxSpiral,
01090                        edgels, *edgeMap)) {
01091       if (!stronger->v1()) { // touch 1-cycle
01092         if (ConfirmJunctionOnCycle(index, minJump,
01093                                    *stronger, edgels)) {
01094           vtol_edge_2d_sptr split = NULL;          // cycle is now split at junction
01095           BreakCycle(*end, index,
01096                      *stronger,
01097                      split,     // find split 1-cycle
01098                      *edgeMap, *vertexMap);     // mutate v-e links
01099           LookupTableReplace(edges, stronger, split);
01100           jcycle++;             // remove original edge
01101         }
01102       }
01103       else {                  // touch itself or another 1-chain
01104         if (ConfirmJunctionOnChain(index, minJump,
01105                                    *stronger, edgels)) {
01106           if (weaker == stronger) {
01107             vtol_edge_2d_sptr straight = NULL, curled = NULL;
01108             LoopChain(*end, index, // break its own chain
01109                       *stronger, // and make a loop
01110                       straight, curled,
01111                       *edgeMap, *vertexMap);
01112             LookupTableReplace(edges, stronger, straight);
01113             LookupTableInsert(edges, curled);
01114             jchain++;
01115           }
01116           else {
01117             vtol_edge_2d_sptr longer = NULL, shorter = NULL;
01118             BreakChain(*end, index, // break another stronger chain in 2
01119                        *stronger,
01120                        longer, shorter, // find sub chains
01121                        *edgeMap, *vertexMap);   // mutate v-e links
01122             LookupTableReplace(edges, stronger, longer);
01123             LookupTableInsert(edges, shorter);
01124             jchain++;
01125           }
01126         }
01127       }
01128     }
01129   }
01130 #if 0
01131   vcl_cout << "Find junctions with "
01132            << jcycle << " cycles and " << jchain << " chains, with jump > "
01133            << minJump << vcl_endl;
01134 #endif
01135 
01136   // 3. Merge touching end points, into a larger junction/chain.
01137   int dendpt = 0, dchain = 0;   // number of deleted endpt/chain
01138 
01139   for (unsigned int i=0; i< vertices.size(); i++)
01140   {
01141     vtol_vertex_2d_sptr  end1 = vertices[i]; // search from dangling end pt only
01142     if (end1 &&          // skip deleted vertices
01143         NumConnectedRays(*end1) == 1) { // end point of dangling 1-chain
01144       vtol_vertex_2d_sptr  end2 = DetectTouch(*end1, maxSpiral, *vertexMap);
01145       if (end2) {       // find end points nearby
01146         if (NumConnectedRays(*end2) == 1) { // found another dangling end point
01147           vtol_edge_2d_sptr seg = DanglingEdge(*end1);
01148           if (seg == DanglingEdge(*end2)) { // end points of 1-cycle
01149             MergeEndPtsOfChain(*end1, *end2, *seg,
01150                                *edgeMap, *vertexMap);
01151             LookupTableRemove(vertices, end2);
01152             dendpt++;
01153           }
01154           else {              // end points of 2 distinct 1-chains
01155 #if 0
01156             vcl_cout << "endpt1=" << *end1 << vcl_endl
01157                      << "endpt2=" << *end2 << vcl_endl;
01158 #endif
01159             vtol_edge_2d_sptr merge=NULL, longer=NULL, shorter=NULL; // merge 2 different edges
01160             MergeEndPtTouchingEndPt(*end1, *end2, // merge 2 subchains
01161                                     merge, longer, shorter, // deleting
01162                                     *edgeMap, *vertexMap); // end points
01163 #if 0
01164             vcl_cout << "merge=" << *merge << vcl_endl
01165                      << "longer=" << *longer << vcl_endl
01166                      << "shorter=" << *shorter << vcl_endl
01167                      << "merge.v1=" << *merge->v1() << vcl_endl
01168                      << "merge.v2=" << *merge->v2() << vcl_endl;
01169 #endif
01170             LookupTableReplace(edges, longer, merge);
01171             LookupTableRemove(edges, shorter);
01172             LookupTableRemove(vertices, end1);
01173             LookupTableRemove(vertices, end2);
01174             dendpt += 2, dchain += 1;
01175           }
01176         }
01177         else {                // merge into another junction
01178 #if 0
01179           vcl_cout << "endpt1=" << *end1 << vcl_endl
01180                    << "junction2=" << *end2 << vcl_endl;
01181 #endif
01182           MergeEndPtTouchingJunction(*end1, *end2,
01183                                      *edgeMap, *vertexMap);
01184           LookupTableRemove(vertices, end1);
01185           dendpt++;
01186         }
01187       }
01188     }
01189   }
01190 #if 0
01191   vcl_cout << "Merge and delete " << dendpt << " end points and " << dchain << " edges\n";
01192 #endif
01193   if (dchain)                   // eliminate holes in global arrays
01194     LookupTableCompress(edges);
01195   if (dendpt)
01196     LookupTableCompress(vertices);
01197 
01198   // 4. Insert virtual junction for isolated 1-cycles
01199   int ncycle = 0;
01200   for (unsigned int i=0; i< edges.size(); ++i)
01201   {
01202     vtol_edge_2d_sptr edge = edges[i];
01203     if (!edge->v1()) {  // vertices not created from 1.
01204       vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01205       vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01206 
01207       const int last = cxy->size()-1;
01208       vtol_vertex_2d_sptr  v = new vtol_vertex_2d(((*cxy)[0].x()+(*cxy)[last].x())/2, ((*cxy)[0].y()+(*cxy)[last].y())/2);
01209       edge->set_v1(v->cast_to_vertex()); edge->set_v2(v->cast_to_vertex()); // link both directions v-e
01210       vertexMap->put(int(v->x()), int(v->y()), v);
01211       LookupTableInsert(vertices, v);
01212       ncycle++;
01213     }
01214   }
01215 #if 0
01216   vcl_cout << "Create " << ncycle << " virtual end points for isolated cycles.\n";
01217 #endif
01218 #ifdef DEBUG
01219   if (talkative)
01220     vcl_cout << "All junctions found in " << t.real() << " msecs.\n";
01221 #endif
01222   return vertices.size();
01223 }
01224 
01225 
01226 //: Insert subpixel accuracy into the pixels on the edges/vertices.
01227 // Truncating float locations with int(xy) should map to the original
01228 // pixel locations. No interpolation is done at junctions of 3 or more
01229 // contours, so a junction can have location error up to 1-2 pixel,
01230 // tangential to the strong contour.
01231 void
01232 gevd_contour::SubPixelAccuracy(vcl_vector<vtol_edge_2d_sptr>& edges,
01233                                vcl_vector<vtol_vertex_2d_sptr >& vertices,
01234                                const gevd_bufferxy& locationx,
01235                                const gevd_bufferxy& locationy)
01236 {
01237 #ifdef DEBUG
01238   vul_timer t;
01239 #endif
01240   if (talkative)
01241     vcl_cout << "Insert subpixel accuracy into edges/vertices";
01242 
01243   // 1. Subpixel accuracy for end points
01244   for (unsigned int i=0; i< vertices.size(); ++i)
01245   {
01246     vtol_vertex_2d_sptr  vert = vertices[i];
01247     int x = int(vert->x()), y = int(vert->y());
01248     vert->set_x(x + floatPixel(locationx, x, y));
01249     vert->set_y(y + floatPixel(locationy, x, y));
01250   }
01251 
01252   // 2. Subpixel accuracy for chain pixels
01253   for (unsigned int i=0; i< edges.size(); ++i)
01254   {
01255     vtol_edge_2d_sptr edge = edges[i];
01256     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01257     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01258 
01259     for (unsigned int k = 0; k < cxy->size(); ++k)
01260     {
01261       int x = int((*cxy)[k].x()), y = int((*cxy)[k].y());
01262 
01263       double tempx= (*cxy)[k].x()+ floatPixel( locationx, x, y);
01264       double tempy= (*cxy)[k].y()+ floatPixel( locationy, x, y);
01265       (*cxy)[k].set_x( tempx);
01266       (*cxy)[k].set_y( tempy);
01267     }
01268   }
01269 
01270   // 3. Thin zig-zags on the contours? Zig-zags happen at
01271   // the 2 end points because of extension, or inside the contour
01272   // because of 4/8-connected tracing through noisy chain pixels,
01273   // and large shifts for subpixel locations.
01274   // Implement only if experiments prove zig-zags are excessive
01275 
01276 #ifdef DEBUG
01277   if (talkative)
01278     vcl_cout << ", in " << t.real() << " msecs.\n";
01279 #endif
01280 }
01281 
01282 
01283 //:
01284 //  Generate an Edge with a vdgl_digital_curve representing a straight line
01285 //  between the specified vertices.
01286 static vtol_edge_2d_sptr DigitalEdge(vtol_vertex_2d_sptr  vs, vtol_vertex_2d_sptr  ve)
01287 {
01288   double xs= vs->x();
01289   double ys= vs->y();
01290   double xe= ve->x();
01291   double ye= ve->y();
01292 
01293   vdgl_edgel_chain *es= new vdgl_edgel_chain;
01294   vdgl_interpolator *it= new vdgl_interpolator_linear( es);
01295   vdgl_digital_curve *dc= new vdgl_digital_curve( it);
01296 
01297   es->add_edgel( vdgl_edgel( xs, ys));
01298   es->add_edgel( vdgl_edgel( xe, ye));
01299 
01300   vtol_edge_2d_sptr e = new vtol_edge_2d(vs, ve, vsol_curve_2d_sptr(dc));
01301   return e;
01302 }
01303 
01304 
01305 //:
01306 // Insert virtual edges and vertices to enforce closure
01307 // of the regions beyond the rectangular image border.
01308 // The location of the border is at 3 pixels away from the
01309 // real image border, because of kernel radius in convolution
01310 // and non maximum suppression. Virtual border of image should be
01311 // inserted after gevd_contour::FindChains() and gevd_contour::FindJunctions().
01312 //
01313 // JLM - February 1999  Modified this routine extensively to
01314 // move the border to the actual image ROI bounds.  Chain endpoints
01315 // are extended to intersect with the border.  These changes were
01316 // made to support region segmentation from edgels.
01317 void
01318 gevd_contour::InsertBorder(vcl_vector<vtol_edge_2d_sptr>& edges,
01319                            vcl_vector<vtol_vertex_2d_sptr >& vertices)
01320 {
01321 #ifdef DEBUG
01322   vul_timer t;
01323 #endif
01324   //1.00 Save Edges along the border
01325   vcl_vector<vtol_vertex_2d_sptr > xmin_verts;
01326   vcl_vector<vtol_vertex_2d_sptr > xmax_verts;
01327   vcl_vector<vtol_vertex_2d_sptr > ymin_verts;
01328   vcl_vector<vtol_vertex_2d_sptr > ymax_verts;
01329 
01330   if (talkative)
01331     vcl_cout << "Insert virtual border to enforce closure";
01332 
01333   // 0. Create 4 corners vertices
01334   const int rmax = FRAME;       // border of image
01335   const int xmax = vertexMap->rows()-rmax-1;
01336   const int ymax = vertexMap->columns()-rmax-1;
01337   int cx[] = {rmax, xmax, rmax, xmax}; // coordinates of 4 corners
01338   int cy[] = {rmax, ymax, ymax, rmax};
01339 
01340   // 1. Collect Vertices along each border
01341   //1.0 Generate Corner Vertices
01342   vtol_vertex_2d_sptr  V00 = new vtol_vertex_2d(rmax, rmax);
01343   vtol_vertex_2d_sptr  V01 = new vtol_vertex_2d(rmax, ymax);
01344   vtol_vertex_2d_sptr  V10 = new vtol_vertex_2d(xmax, rmax);
01345   vtol_vertex_2d_sptr  V11 = new vtol_vertex_2d(xmax, ymax);
01346   xmin_verts.push_back(V00);
01347   xmax_verts.push_back(V10);
01348   ymin_verts.push_back(V00);
01349   ymax_verts.push_back(V01);
01350   // 1.1 ymin, ymax edges
01351   for (int d = 0; d < 2; d++)
01352   {
01353     int y = cy[d];
01354     for (int x = rmax; x<=xmax; ++x)
01355     {
01356       vtol_vertex_2d_sptr  v = vertexMap->get(x, y);
01357       if (v)
01358         vertexMap->put(x, y, NULL);
01359       else continue;
01360       if (d)
01361         ymax_verts.push_back(v);
01362       else
01363         ymin_verts.push_back(v);
01364     }
01365   }
01366   // 1.2 xmin, xmax edges
01367   for (int d = 0; d < 2; d++)
01368   {
01369     int x = cx[d];
01370     for (int y = rmax; y<=ymax; y++)
01371     {
01372       vtol_vertex_2d_sptr  v = vertexMap->get(x, y);
01373       if (v)
01374         vertexMap->put(x, y, NULL);
01375       else continue;
01376       if (d)
01377         xmax_verts.push_back(v);
01378       else
01379         xmin_verts.push_back(v);
01380     }
01381   }
01382 
01383   xmin_verts.push_back(V01);
01384   xmax_verts.push_back(V11);
01385   ymin_verts.push_back(V10);
01386   ymax_verts.push_back(V11);
01387 
01388   // 2.0 Merge vertices
01389   // 2.1  along ymin and ymax
01390   for (int d = 0; d < 2; d++)
01391   {
01392     vcl_vector<vtol_vertex_2d_sptr >* verts = &ymin_verts;
01393     if (d)
01394       verts = &ymax_verts;
01395     int len = (*verts).size();
01396     if (len<3) continue;
01397     //potential merge at xmin
01398     vtol_vertex_2d_sptr  pre_v = (*verts)[0];
01399     vtol_vertex_2d_sptr  v = (*verts)[1];
01400     int x = int(v->x());
01401     int pre_x = int(pre_v->x());
01402     if ((x-pre_x)<3)
01403     {
01404 #if 0 //GEOFF
01405       merge_references(pre_v,v);
01406 #endif
01407       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01408       {
01409         if (*it == v)
01410         {
01411           verts->erase(it);
01412           break;
01413         }
01414       }
01415       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01416       {
01417         if (*it == v)
01418         {
01419           vertices.erase(it);
01420           break;
01421         }
01422       }
01423       len--;
01424     }
01425     //potential merge at xmax
01426     pre_v = (*verts)[len-2];
01427     v = (*verts)[len-1];
01428     pre_x = int(pre_v->x());
01429     if ((xmax+rmax-pre_x)<3)
01430     {
01431 #if 0 //GEOFF
01432       merge_references(v,pre_v);
01433 #endif
01434       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01435       {
01436         if (*it == pre_v)
01437         {
01438           verts->erase(it);
01439           break;
01440         }
01441       }
01442       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01443       {
01444         if (*it == pre_v)
01445         {
01446           vertices.erase(it);
01447           break;
01448         }
01449       }
01450       len--;
01451     }
01452   }
01453 
01454   // 2.1  along xmin and xmax
01455   for (int d = 0; d < 2; d++)
01456   {
01457     vcl_vector<vtol_vertex_2d_sptr >* verts = &xmin_verts;
01458     if (d)
01459       verts = &xmax_verts;
01460     int len = (*verts).size();
01461     if (len<3) continue;
01462     //potential merge at ymin
01463     vtol_vertex_2d_sptr  pre_v = (*verts)[0];
01464     vtol_vertex_2d_sptr  v = (*verts)[1];
01465 
01466 
01467     int y = int(v->y()), pre_y = int(pre_v->y());
01468     if ((y-pre_y)<3)
01469     {
01470 #if 0 //GEOFF
01471       merge_references(pre_v,v);
01472 #endif
01473       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01474       {
01475         if (*it == v)
01476         {
01477           verts->erase( it);
01478           break;
01479         }
01480       }
01481       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01482       {
01483         if (*it == v)
01484         {
01485           vertices.erase( it);
01486           break;
01487         }
01488       }
01489       len--;
01490     }
01491     //potential merge at ymax
01492     pre_v = (*verts)[len-2];
01493     v = (*verts)[len-1];
01494     pre_y = int(pre_v->y());
01495     if ((ymax+rmax-pre_y)<3)
01496     {
01497 #if 0 //GEOFF
01498       merge_references(v,pre_v);
01499 #endif
01500       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01501       {
01502         if (*it == pre_v)
01503         {
01504           verts->erase( it);
01505           break;
01506         }
01507       }
01508       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01509       {
01510         if (*it == pre_v)
01511         {
01512           vertices.erase( it);
01513           break;
01514         }
01515       }
01516       len--;
01517     }
01518   }
01519   // 2.0 Move the vertices to the bounds of the ROI
01520   int iv,  len = xmin_verts.size();
01521   float xmi = 0, xmx = float(xmax + rmax);
01522   float ymi = 0, ymx = float(ymax + rmax);
01523   for (iv=1; iv<len-1; iv++)
01524   {
01525     vtol_vertex_2d_sptr  v = xmin_verts[iv];
01526     vtol_vertex_2d_sptr  vp = new vtol_vertex_2d(xmi, v->y());
01527     vertices.push_back(vp);// vp->Protect();
01528     xmin_verts[iv] = vp;
01529 
01530 
01531     vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01532     edges.push_back(e);//  e->Protect();
01533   }
01534 
01535   len = xmax_verts.size();
01536   for (iv=0; iv<len; iv++)
01537   {
01538     vtol_vertex_2d_sptr  v = xmax_verts[iv];
01539     if (iv!=0&&iv!=(len-1))
01540     {
01541       vtol_vertex_2d_sptr  vp = new vtol_vertex_2d( xmx, v->y());
01542       vertices.push_back(vp); // vp->Protect();
01543       xmax_verts[iv] = vp;
01544       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01545       edges.push_back(e); // e->Protect();
01546     }
01547   }
01548   len = ymin_verts.size();
01549   for (iv=0; iv<len; iv++)
01550   {
01551     vtol_vertex_2d_sptr  v = ymin_verts[iv];
01552     if (iv!=0&&iv!=(len-1))
01553     {
01554       vtol_vertex_2d_sptr  vp = new vtol_vertex_2d(v->x(), ymi);
01555       vertices.push_back(vp); // vp->Protect();
01556       ymin_verts[iv] = vp;
01557       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01558       edges.push_back(e); // e->Protect();
01559     }
01560   }
01561   len = ymax_verts.size();
01562   for (iv=0; iv<len; iv++)
01563   {
01564     vtol_vertex_2d_sptr  v = ymax_verts[iv];
01565     if (iv!=0&&iv!=(len-1))
01566     {
01567       vtol_vertex_2d_sptr  vp = new vtol_vertex_2d( v->x(), ymx);
01568       vertices.push_back(vp); // vp->Protect();
01569       ymax_verts[iv] = vp;
01570       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01571       edges.push_back(e); // e->Protect();
01572     }
01573   }
01574   V00->set_x(0);  V00->set_y(0); vertices.push_back(V00);
01575   V01->set_x(0);  V01->set_y(ymax+rmax); vertices.push_back(V01);
01576   V10->set_x(xmax+rmax);  V10->set_y(0); vertices.push_back(V10);
01577   V11->set_x(xmax+rmax);  V11->set_y(ymax+rmax); vertices.push_back(V11);
01578 
01579   //4.0 Now we have properly placed vertices.  Next we scan and generate
01580   //edges. along the border.
01581   //4.1 along ymin and ymax
01582   for (int d = 0; d < 2; d++)
01583   {
01584     vcl_vector<vtol_vertex_2d_sptr >* verts = &ymin_verts;
01585     if (d)
01586       verts = &ymax_verts;
01587     int len = (*verts).size();
01588     if (len<2)
01589     {
01590       vcl_cout <<"In gevd_contour::InsertBorder() - too few vertices\n";
01591       return;
01592     }
01593     for (int i = 0; i<len-1; i++)
01594     {
01595       vtol_vertex_2d_sptr  v = (*verts)[i];
01596       vtol_vertex_2d_sptr  vp = (*verts)[i+1];
01597       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01598       edges.push_back(e); // e->Protect();
01599     }
01600   }
01601   //4.2 along xmin and xmax
01602   for (int d = 0; d < 2; d++)
01603   {
01604     vcl_vector<vtol_vertex_2d_sptr >* verts = &xmin_verts;
01605     if (d)
01606       verts = &xmax_verts;
01607     int len = (*verts).size();
01608     if (len<2)
01609     {
01610       vcl_cout <<"In gevd_contour::InsertBorder() - too few vertices\n";
01611       return;
01612     }
01613     for (int i = 0; i<len-1; i++)
01614     {
01615       vtol_vertex_2d_sptr  v = (*verts)[i];
01616       vtol_vertex_2d_sptr  vp = (*verts)[i+1];
01617       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01618       edges.push_back(e); // e->Protect();
01619     }
01620   }
01621 #ifdef DEBUG
01622   if (talkative)
01623     vcl_cout << ", in " << t.real() << " msecs.\n";
01624 #endif
01625 }
01626 
01627 
01628 //:
01629 // Convolve array elements with [1 0 1]/2, replacing
01630 // center pixel by average of 2 neighbors.
01631 // This will make the spacing between pixels almost equal
01632 // and prune away small zig-zags.
01633 static void
01634 EqualizeElements(double* elmts, int n, double v1, double v2)
01635 {
01636   double p0 = elmts[0], p1 = elmts[1], p2 = elmts[2]; // setup pipeline
01637   elmts[0] = (v1 + p1) / 2;     // touching first vertex
01638   for (int i = 1; i < n-2; i++) {
01639     elmts[i] = (p0 + p2)/2;
01640     p0 = p1; p1 = p2; p2 = elmts[i+2]; // faster with circular list
01641   }
01642   if (n>1) elmts[n-2] = (p0 + p2)/2;   // last convolution
01643   if (n>0) elmts[n-1] = (p1 + v2)/2;   // touching second vertex
01644 }
01645 
01646 
01647 //:
01648 // Make the spacing of the chain pixels nearly equal by
01649 // smoothing their locations with the average filter  [1 0 1]/2.
01650 // This will reduce square grid tessellation artefacts, and
01651 // lead to more accurate estimation of the tangent direction,
01652 // and local curvature angle, from finite differences in location.
01653 // It is also useful to avoid weight artefacts in curve fitting
01654 // caused by the periodic clustering of pixels along the chain.
01655 // Truncating the float locations with int() will no longer map
01656 // to the original pixels of the discrete chains.
01657 void
01658 gevd_contour::EqualizeSpacing(vcl_vector<vtol_edge_2d_sptr>& chains)
01659 {
01660 #ifdef DEBUG
01661   vul_timer t;
01662 #endif
01663   if (talkative)
01664     vcl_cout << "Equalize the spacing between pixels in chains";
01665 
01666   for (unsigned int i= 0; i< chains.size(); i++)
01667   {
01668     vtol_edge_2d_sptr e = chains[i];
01669     vdgl_digital_curve_sptr dc = e->curve()->cast_to_vdgl_digital_curve();
01670     const int len = dc->get_interpolator()->get_edgel_chain()->size();
01671     if (len > 2*MINLENGTH)
01672     {   // not necessary for short chains
01673       vtol_vertex_sptr v1 = e->v1(), v2 = e->v2();
01674 
01675       vcl_vector<double> cx(len);
01676       vcl_vector<double> cy(len);
01677 
01678       for (int qq=0; qq<len; qq++)
01679       {
01680         vdgl_edgel e= dc->get_interpolator()->get_edgel_chain()->edgel( qq);
01681         cx[qq]= e.x();
01682         cy[qq]= e.y();
01683       }
01684 
01685       EqualizeElements(&cx[0], len, v1->cast_to_vertex_2d()->x(), v2->cast_to_vertex_2d()->x());
01686       EqualizeElements(&cy[0], len, v1->cast_to_vertex_2d()->y(), v2->cast_to_vertex_2d()->y());
01687 
01688       for (int qq=0; qq<len; qq++)
01689       {
01690         vdgl_edgel e( cx[qq], cy[qq]);
01691         dc->get_interpolator()->get_edgel_chain()->set_edgel( qq, e);
01692       }
01693     }
01694   }
01695 #ifdef DEBUG
01696   if (talkative)
01697     vcl_cout << ", in " << t.real() << " msecs.\n";
01698 #endif
01699 }
01700 
01701 
01702 //: Translate all the pixels in the edges and vertices by (tx, ty).
01703 // If the image is extracted from an ROI, a translation of
01704 // (roi->GetOrigX(), roi->GetOrigY()) must be done to have
01705 // coordinates in the reference frame of the original image.
01706 // Add 0.5 if you want to display location at center of pixel
01707 // instead of upper-left corner.
01708 void
01709 gevd_contour::Translate(vcl_vector<vtol_edge_2d_sptr>& edges, // translate loc to center
01710                         vcl_vector<vtol_vertex_2d_sptr >& vertices,
01711                         const float tx, const float ty)
01712 {
01713 #ifdef DEBUG
01714   vul_timer t;
01715 #endif
01716   if (talkative)
01717     vcl_cout << "Translate edges/vertices";
01718   for (unsigned int i=0; i< vertices.size(); i++) {
01719     vtol_vertex_2d_sptr  vert = vertices[i];
01720     vert->set_x(vert->x() + tx);
01721     vert->set_y(vert->y() + ty);
01722   }
01723   for (unsigned int i=0; i< edges.size(); i++) {
01724     vtol_edge_2d_sptr edge = edges[i];
01725     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01726 
01727     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01728     for (unsigned int k = 0; k < cxy->size(); ++k) {
01729       vdgl_edgel e= (*cxy)[k];
01730 
01731       e.set_x( e.x()+tx);
01732       e.set_y( e.y()+ty);
01733 
01734       cxy->set_edgel( k, e);
01735     }
01736   }
01737 #ifdef DEBUG
01738   if (talkative)
01739     vcl_cout << ", in " << t.real() << " msecs.\n";
01740 #endif
01741 }
01742 
01743 
01744 //:
01745 // Remove and delete all elements in global lists, and set
01746 // the global lists to NULL. Remove all digital chains of edges.
01747 // Edges and vertices are removed with UnProtect().
01748 void
01749 gevd_contour::ClearNetwork(vcl_vector<vtol_edge_2d_sptr>*& edges,
01750                            vcl_vector<vtol_vertex_2d_sptr >*& vertices)
01751 {
01752   if (edges) {
01753     for (unsigned int i=0; i< edges->size(); ++i) {
01754 #if 0 // not (yet) converted
01755       vtol_edge_2d_sptr edge = (*edges)[i];
01756       Curve* dc = NULL;        // retrieve digital chain dc
01757       vsol_curve_2d *dc= 0;
01758 #if 0 //GEOFF
01759       edge->set_curve(0); // and remove it from edge
01760 #endif
01761       edge->UnProtect();        // delete edge
01762       delete (vdgl_digital_curve *) dc; // delete dc
01763 #endif
01764     }
01765     delete edges; edges = NULL;
01766   }
01767   if (vertices) {
01768 #if 0
01769     for (vertices->reset(); vertices->next(); )
01770     for (unsigned int i=0; i< vertices->size(); ++i)
01771       vertices[i]->UnProtect();
01772 #endif
01773     delete vertices; vertices = NULL;
01774   }
01775 }
01776 
01777 //:
01778 // Mask the detected edge elements and junctions with a given
01779 // mask array, using AND operation, for ROI with arbitrary shapes.
01780 void
01781 gevd_contour::MaskEdgels(const gevd_bufferxy& mask,
01782                          gevd_bufferxy& edgels, // edge elements AND with mask
01783                          int& njunction, // junctions AND with mask
01784                          int* junctionx, int* junctiony)
01785 {
01786   int x, y;
01787   for (y = 0; y < edgels.GetSizeY(); y++)
01788     for (x = 0; x < edgels.GetSizeX(); x++)
01789       if (floatPixel(edgels, x, y) && // is edge element
01790           !bytePixel(mask, x, y)) // is not in mask
01791         floatPixel(edgels, x, y) = 0; // remove edgel not in mask
01792   int j = 0;
01793   for (int i = 0; i < njunction; i++) {
01794     x = junctionx[i], y = junctiony[i];
01795     if (bytePixel(mask, x, y)) { // keep junction in mask
01796       junctionx[j] = x, junctiony[j] = y, j++;
01797     }
01798   }
01799   njunction = j;
01800 }
01801 
01802 
01803 //:
01804 // Set the orientation at each edgel on all digital curves to a continuous
01805 // orientation value, which is consistent with C. Rothwell's EdgeDetector.
01806 // That is theta = (180/M_PI)*atan2(dI/dy, dI/dx)
01807 //
01808 void
01809 gevd_contour::SetEdgelData(gevd_bufferxy& grad_mag, gevd_bufferxy& angle, vcl_vector<vtol_edge_2d_sptr>& edges)
01810 {
01811   for (unsigned int i=0; i< edges.size(); i++)
01812   {
01813     vtol_edge_2d_sptr e = edges[i];
01814     vdgl_digital_curve_sptr dc= e->curve()->cast_to_vdgl_digital_curve();
01815 
01816     if (dc)
01817     {
01818       vdgl_edgel_chain_sptr xypos= dc->get_interpolator()->get_edgel_chain();
01819 
01820       int len = xypos->size();
01821 
01822       for (int i = 0; i < len; i++)
01823       {
01824         int ix = int((*xypos)[i].x());
01825         int iy = int((*xypos)[i].y());
01826 
01827         // Debugging : RIH
01828         // Routine crashes with iy < 0.
01829         if (iy < 0 || ix < 0 ||
01830             ix >= grad_mag.GetSizeX() ||
01831             iy >= grad_mag.GetSizeY())
01832         {
01833           vcl_cerr << "***********  ERROR  : (ix, iy) = ("
01834                    << ix << ", " << iy << ")\n";
01835           if (ix < 0) ix = 0;
01836           if (iy < 0) iy = 0;
01837           if (ix >= grad_mag.GetSizeX()) ix = grad_mag.GetSizeX()-1;
01838           if (iy >= grad_mag.GetSizeY()) iy = grad_mag.GetSizeY()-1;
01839         }
01840 
01841         vdgl_edgel edgel= xypos->edgel(i);
01842         edgel.set_grad( floatPixel( grad_mag, ix, iy));
01843         edgel.set_theta( floatPixel( angle, ix, iy));
01844 
01845 #if 0
01846         gr[i] = floatPixel(grad_mag, ix, iy);
01847         th[i] = floatPixel(angle, ix, iy);
01848 #endif
01849       }
01850     }
01851   }
01852 }
01853 
01854 
01855 //: Compare function to sort the edges by their length in pixels, largest first.
01856 int
01857 gevd_contour::LengthCmp(vtol_edge_2d_sptr const& dc1, vtol_edge_2d_sptr const& dc2)
01858 {
01859   vdgl_digital_curve_sptr c1 = ((vtol_edge_2d_sptr)dc1)->curve()->cast_to_vdgl_digital_curve();
01860   vdgl_digital_curve_sptr c2 = ((vtol_edge_2d_sptr)dc2)->curve()->cast_to_vdgl_digital_curve();
01861   return c2->get_interpolator()->get_edgel_chain()->size() - c1->get_interpolator()->get_edgel_chain()->size();
01862 }
01863 
01864 
01865 //: Create a 2-way lookup table from list elements in set, using array and get_id/set_id.
01866 vcl_vector<vtol_edge_2d_sptr>*
01867 gevd_contour::CreateLookupTable(vcl_vector<vtol_edge_2d_sptr>& set)
01868 {
01869   vcl_vector<vtol_edge_2d_sptr>* set2 =
01870     new vcl_vector<vtol_edge_2d_sptr>(2*set.size()); // preallocate space
01871   for (unsigned int i=0; i< set.size(); i++)
01872     gevd_contour::LookupTableInsert(*set2, set[i]);
01873   return set2;
01874 }
01875 
01876 //: As above for vertices.
01877 vcl_vector<vtol_vertex_2d_sptr >*
01878 gevd_contour::CreateLookupTable(vcl_vector<vtol_vertex_2d_sptr >& set)
01879 {
01880   vcl_vector<vtol_vertex_2d_sptr >* set2 =
01881     new vcl_vector<vtol_vertex_2d_sptr >(2*set.size()); // preallocate space
01882   for (unsigned int i=0; i< set.size(); i++)
01883     gevd_contour::LookupTableInsert(*set2, set[i]);
01884   return set2;
01885 }
01886 
01887 
01888 //:
01889 // Insert topology object in 2-way lookup table,
01890 // using Id and dynamic array. Protect it in the network.
01891 void
01892 gevd_contour::LookupTableInsert(vcl_vector<vtol_edge_2d_sptr>& set,
01893                                 vtol_edge_2d_sptr elmt)
01894 {
01895   elmt->set_id(set.size());     // index in global array
01896   set.push_back(elmt);          // push_back at end of array
01897 }
01898 
01899 
01900 //: As above for vertices.
01901 void
01902 gevd_contour::LookupTableInsert(vcl_vector<vtol_vertex_2d_sptr >& set,
01903                                 vtol_vertex_2d_sptr  elmt)
01904 {
01905   elmt->set_id(set.size());     // index in global array
01906   set.push_back(elmt);          // push at end of array
01907 }
01908 
01909 
01910 //: Replace deleted by inserted in 2-way lookup table.
01911 // Also remove object from the network.
01912 void
01913 gevd_contour::LookupTableReplace(vcl_vector<vtol_edge_2d_sptr>& set,
01914                                  vtol_edge_2d_sptr deleted, vtol_edge_2d_sptr inserted)
01915 {
01916   const int i = deleted->get_id();
01917   inserted->set_id(i);
01918   set[i] = inserted;            // replace in global array
01919 #if 0 //GEOFF
01920   deleted->unlink_all_inferiors_twoway(deleted);
01921 #endif
01922 }
01923 
01924 
01925 //: As above for vertices.
01926 void
01927 gevd_contour::LookupTableReplace(vcl_vector<vtol_vertex_2d_sptr >& set,
01928                                  vtol_vertex_2d_sptr  deleted, vtol_vertex_2d_sptr  inserted)
01929 {
01930   const int i = deleted->get_id();
01931   inserted->set_id(i);
01932   set[i] = inserted;            // replace in global array
01933 }
01934 
01935 
01936 //: Remove topology object from 2-way lookup table leaving an empty hole.
01937 // Also remove object from the network.
01938 void
01939 gevd_contour::LookupTableRemove(vcl_vector<vtol_edge_2d_sptr>& set,
01940                                 vtol_edge_2d_sptr elmt)
01941 {
01942   set[elmt->get_id()] = NULL;   // remove from global array
01943 }
01944 
01945 
01946 //: As above for vertices.
01947 void
01948 gevd_contour::LookupTableRemove(vcl_vector<vtol_vertex_2d_sptr >& set,
01949                                 vtol_vertex_2d_sptr  elmt)
01950 {
01951   set[elmt->get_id()] = NULL;   // remove from global array
01952 }
01953 
01954 
01955 //: Eliminate empty holes in the lookup table.
01956 void
01957 gevd_contour::LookupTableCompress(vcl_vector<vtol_edge_2d_sptr>& set)
01958 {
01959   int i = 0;
01960   for (int j = set.size()-1; i <= j; i++)
01961     if (!set[i]) {      // find empty hole
01962       vtol_edge_2d_sptr last = NULL;
01963       for (; i < j; j--)
01964         if (set[j]) {
01965           last = set[j]; j--; // remove from the end
01966           break;
01967         }
01968       if (last) {
01969         last->set_id(i);                // move it to the front
01970         set[i] = last;
01971       }
01972       else
01973         break;                  // no more elements
01974     }
01975   set.resize(i - 1);
01976 }
01977 
01978 
01979 //: As above for vertices.
01980 void
01981 gevd_contour::LookupTableCompress(vcl_vector<vtol_vertex_2d_sptr >& set)
01982 {
01983   int i = 0;
01984   for (int j = set.size()-1; i <= j; i++)
01985     if (!set[i]) {              // find empty hole
01986       vtol_vertex_2d_sptr  last = NULL;
01987       for (; i < j; j--)
01988         if (set[j]) {
01989           last = set[j]; j--; // remove from the end
01990           break;
01991         }
01992       if (last) {
01993         last->set_id(i);                // move it to the front
01994         set[i] = last;
01995       }
01996       else
01997         break;                  // no more elements
01998     }
01999   set.resize(i - 1);
02000 }
02001 
02002 //: Check a few obvious invariants, and return number of errors.
02003 // 0. Network has closure of all vertices and edges.
02004 // 1. No 2 vertices touch: endpt/endpt, endpt/junction or junction/junction
02005 // 2. No vertex connecting 2 edges, each vertex has 1 or >= 3 edges,
02006 //    except the 4 corners of image border.
02007 // 3. Each edge has >= 3 internal pixels.
02008 // 4. A chain can touch/join with itself or with a stronger chain.
02009 //    Junction is created only if the local change in filter response
02010 //    is greater than some noise threshold, set by the user.
02011 //    Junction is created only if the 2 broken up chains have
02012 //    lengths >= 3.
02013 int
02014 gevd_contour::CheckInvariants(vcl_vector<vtol_edge_2d_sptr>& edges,
02015                               vcl_vector<vtol_vertex_2d_sptr >& vertices)
02016 {
02017   int nerror = 0;
02018 
02019   // 0. Check that vertices of all edges have been listed
02020   const int unmark = -1;
02021   for (unsigned int i=0; i< edges.size(); i++)
02022     edges[i]->set_id(unmark);
02023   for (unsigned int i=0; i< vertices.size(); i++)
02024     vertices[i]->set_id(unmark);
02025   for (unsigned int i=0; i< edges.size(); i++) {
02026     vtol_edge_2d_sptr e = edges[i];
02027     vtol_vertex_sptr v1 = e->v1();
02028     if (v1->get_id() != unmark) {
02029       vcl_cout << *v1 << ": v1 is not in vertex list\n";
02030       nerror++;
02031     }
02032     vtol_vertex_sptr v2 = e->v2();
02033     if (v2->get_id() != unmark) {
02034       vcl_cout << *v2 << ": v2 is not in vertex list\n";
02035       nerror++;
02036     }
02037   }
02038   for (unsigned int i=0; i< vertices.size(); i++) {
02039     vcl_vector<vtol_edge_sptr> es; vertices[i]->edges(es);
02040     for (unsigned int j=0; j< es.size(); j++)
02041       if (es[j]->get_id() != unmark) {
02042         vcl_cout << es[j] << ": e is not in edge list\n";
02043         nerror++;
02044       }
02045   }
02046   // mark id with index in global list
02047   for (unsigned int id=0; id< edges.size(); id++)
02048     edges[id]->set_id(id);
02049   for (unsigned int id=0; id< vertices.size(); id++)
02050     vertices[id]->set_id(id);
02051 
02052   return nerror;
02053 }
02054