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           } 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& end2,
00890                         vtol_edge_2d_sptr& merge, vtol_edge_2d_sptr& longer, vtol_edge_2d_sptr& shorter,
00891                          vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00892 {
00893   // 1. Retrieve the dangling edges/chains
00894   vcl_vector<vtol_edge_sptr> edges;
00895   end1.edges(edges); vtol_edge_2d_sptr edge1 = edges[0]->cast_to_edge_2d();        // dangling edges
00896   end2.edges(edges); vtol_edge_2d_sptr edge2 = edges[0]->cast_to_edge_2d();
00897 
00898   // 2. Create merged edge/chain
00899   vdgl_digital_curve_sptr dc1 = edge1->curve()->cast_to_vdgl_digital_curve();
00900   const int l1 = dc1->get_interpolator()->get_edgel_chain()->size();
00901   vdgl_digital_curve_sptr dc2 = edge2->curve()->cast_to_vdgl_digital_curve();
00902   const int l2 = dc2->get_interpolator()->get_edgel_chain()->size();
00903   const int len = l1+l2;
00904 
00905   merge = new vtol_edge_2d();
00906 
00907   vdgl_edgel_chain *ec = new vdgl_edgel_chain;
00908   vdgl_interpolator *it = new vdgl_interpolator_linear( ec);
00909   vdgl_digital_curve *dc = new vdgl_digital_curve(it);
00910 
00911   merge->set_curve(*dc);
00912 
00913   vdgl_edgel_chain *cxy= ec;
00914   vtol_vertex_sptr v1, v2;      // vertices of merge edge
00915   int k = 0;                    // index in merge array
00916 
00917   vdgl_edgel_chain_sptr cxy1= dc1->get_interpolator()->get_edgel_chain();
00918   if (edge1->v2() == &end1) {
00919     for (int i = 0; i < l1; i++, k++)
00920       {
00921       cxy->add_edgel ( (*cxy1)[i] );
00922       (*cxy)[k] = (*cxy1)[i];//, cy[k] = cy1[i];
00923       }
00924     v1 = edge1->v1();
00925   } else {                      // reverse collection
00926   for (int i = l1-1; i >= 0; i--, k++)
00927     {
00928     cxy->add_edgel ( (*cxy1)[i] );
00929     (*cxy)[k] = (*cxy1)[i];//, cy[k] = cy1[i];
00930     }
00931     v1 = edge1->v2();
00932   }
00933   merge->set_v1(v1.ptr());
00934 
00935   vdgl_edgel_chain_sptr cxy2= dc2->get_interpolator()->get_edgel_chain();
00936 
00937   if (edge2->v1() == &end2)
00938     {
00939     for (int i = 0; i < l2; i++, k++)
00940       {
00941       cxy->add_edgel ( (*cxy2)[i] );
00942       (*cxy)[k] = (*cxy2)[i];//, cy[k] = cy2[i];
00943       }
00944 //    v2 = edge2->v2().ptr()->cast_to_vertex_2d();
00945     v2 = edge2->v2()->cast_to_vertex_2d();
00946     } else {
00947     // reverse collection
00948     for (int i = l2-1; i >= 0; i--, k++)
00949       {
00950       cxy->add_edgel ( (*cxy2)[i] );
00951       (*cxy)[k] = (*cxy2)[i];//, cy[k] = cy2[i];
00952       }
00953     v2 = edge2->v1()->cast_to_vertex_2d();
00954     }
00955   merge->set_v2(v2.ptr());
00956 
00957   // 3. Update global maps
00958   vertexMap.put(int(end1.x()), int(end1.y()), NULL);
00959   vertexMap.put(int(end2.x()), int(end2.y()), NULL);
00960   const int last = len-1;
00961   for (k = 1; k < last; k++)
00962     edgeMap.put( int((*cxy)[k].x()), int((*cxy)[k].y()), merge);
00963   if (edgeMap.get( int((*cxy)[0].x()), int((*cxy)[0].y())))
00964     edgeMap.put( int((*cxy)[0].x()), int((*cxy)[0].y()), merge);
00965   if (edgeMap.get( int((*cxy)[last].x()), int((*cxy)[last].y())))
00966     edgeMap.put( int((*cxy)[last].x()), int((*cxy)[last].y()), merge);
00967 
00968   if (l1 >= l2)                 // sort out length of deleted subchains
00969     longer = edge1, shorter = edge2;
00970   else
00971     longer = edge2, shorter = edge1;
00972 }
00973 
00974 
00975 //: Merge an end point into a touching junction.
00976 // Update global maps.
00977 void
00978 MergeEndPtTouchingJunction(vtol_vertex_2d &endpt, vtol_vertex_2d& junction,
00979                            vbl_array_2d<vtol_edge_2d_sptr>& edgeMap, vbl_array_2d<vtol_vertex_2d_sptr>&vertexMap)
00980 {
00981   vcl_vector<vtol_edge_sptr> edges; endpt.edges(edges);
00982   vtol_edge_2d_sptr edge = edges[0]->cast_to_edge_2d(); // dangling edge terminating at end pt
00983   int px = int(endpt.x()), py = int(endpt.y());
00984   vertexMap.put(px, py, NULL); // erase old location
00985   edgeMap.put( px, py, edge);
00986   if (edge->v1() == &endpt)     // change the links both directions v-e
00987     edge->set_v1(&junction);    // unlink when endpt.UnProtect()
00988   else
00989     edge->set_v2(&junction);
00990 }
00991 
00992 
00993 //:
00994 // Find junctions from end points touching at an interior point
00995 // of a chain, with detectable jump in filter response.
00996 // Localize these junctions on the stronger contour to pixel accuracy,
00997 // and break stronger chain into subchains.
00998 // Also merge end points touching another end point or junction.
00999 // Return the number of end points and junctions bounding
01000 // all chains/cycles detected in gevd_contour::FindChains.
01001 // Deletion/insertion to the network must be done completely,
01002 // so that the connectivity links are updated.  Protected.
01003 int
01004 gevd_contour::FindJunctions(gevd_bufferxy& edgels,
01005                             vcl_vector<vtol_edge_2d_sptr>& edges,
01006                             vcl_vector<vtol_vertex_2d_sptr >& vertices)
01007 {
01008 #ifdef DEBUG
01009   vul_timer t;
01010 #endif
01011   if (!edges.size())
01012   {
01013     vcl_cerr << "gevd_contour::FindChains must precede gevd_contour::FindJunctions.\n";
01014     return 0;
01015   }
01016   vcl_vector<vtol_edge_2d_sptr>::iterator eit;
01017 
01018   for (eit= edges.begin(); eit!=edges.end(); eit++) {
01019     (*eit)->describe(vcl_cout, 2);
01020   }
01021   vcl_vector<vtol_vertex_2d_sptr>::iterator vit;
01022 
01023   for (vit= vertices.begin(); vit!=vertices.end(); vit++) {
01024     (*vit)->describe(vcl_cout, 2);
01025   }
01026 
01027 
01028   // 1. Create end points or junctions, for all 1-chains.
01029   const float connect_fuzz = 2;
01030 
01031   for (unsigned int i=0; i< edges.size(); i++)
01032   {
01033     vtol_edge_2d_sptr edge = edges[i];
01034     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01035     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01036 
01037     const int last = cxy->size()-1;
01038     if (vcl_fabs((*cxy)[0].x()-(*cxy)[last].x()) > connect_fuzz || // disjoint first/last pixel
01039         vcl_fabs((*cxy)[0].y()-(*cxy)[last].y()) > connect_fuzz)
01040     { // so must be a 1-chain
01041       int x = int((*cxy)[0].x()), y = int((*cxy)[0].y());
01042 
01043       vtol_vertex_2d_sptr v1 = vertexMap->get(x, y);
01044       if (!v1)
01045       {         // check for collision
01046         v1 = new vtol_vertex_2d((*cxy)[0].x(), (*cxy)[0].y()); // 1st point in chain
01047         vertexMap->put(x, y, v1);
01048         LookupTableInsert(vertices, v1);
01049       }
01050       else
01051       {
01052         edgeMap->put( x, y, NULL); // erase junction point
01053       }
01054 
01055       edge->set_v1(v1->cast_to_vertex());         // link both directions v-e
01056       x = int((*cxy)[last].x()), y = int((*cxy)[last].y());
01057 
01058       vtol_vertex_2d_sptr v2 = vertexMap->get(x, y);
01059 
01060       if (!v2)
01061       {         // check for collision
01062         v2 = new vtol_vertex_2d((*cxy)[last].x(), (*cxy)[last].y()); // last point in chain
01063         vertexMap->put(x, y, v2);
01064         LookupTableInsert(vertices, v2);
01065       }
01066       else
01067       {
01068         edgeMap->put( x, y, NULL); // erase junction point
01069       }
01070 
01071       edge->set_v2(v2->cast_to_vertex());         // link both directions v-e
01072     }
01073   }
01074 
01075 
01076   // 2. Localize a junction, when an end point of a dangling contour
01077   // touches another contour or itself at an interior point.
01078   int jcycle = 0, jchain = 0;   // number of junctions with cycle/chain
01079 
01080   for (unsigned int i=0; i< vertices.size(); i++)
01081   {
01082     vtol_vertex_2d_sptr  end = vertices[i];
01083     vtol_edge_2d_sptr weaker = NULL, stronger = NULL; // weaker touches stronger
01084     int index;                  // location on stronger contour
01085     if (DetectJunction(*end, index,
01086                        weaker, stronger, maxSpiral,
01087                        edgels, *edgeMap)) {
01088       if (!stronger->v1()) { // touch 1-cycle
01089         if (ConfirmJunctionOnCycle(index, minJump,
01090                                    *stronger, edgels)) {
01091           vtol_edge_2d_sptr split = NULL;          // cycle is now split at junction
01092           BreakCycle(*end, index,
01093                      *stronger,
01094                      split,     // find split 1-cycle
01095                      *edgeMap, *vertexMap);     // mutate v-e links
01096           LookupTableReplace(edges, stronger, split);
01097           jcycle++;             // remove original edge
01098         }
01099       } else {                  // touch itself or another 1-chain
01100         if (ConfirmJunctionOnChain(index, minJump,
01101                                    *stronger, edgels)) {
01102           if (weaker == stronger) {
01103             vtol_edge_2d_sptr straight = NULL, curled = NULL;
01104             LoopChain(*end, index, // break its own chain
01105                       *stronger, // and make a loop
01106                       straight, curled,
01107                       *edgeMap, *vertexMap);
01108             LookupTableReplace(edges, stronger, straight);
01109             LookupTableInsert(edges, curled);
01110             jchain++;
01111           } else {
01112             vtol_edge_2d_sptr longer = NULL, shorter = NULL;
01113             BreakChain(*end, index, // break another stronger chain in 2
01114                        *stronger,
01115                        longer, shorter, // find sub chains
01116                        *edgeMap, *vertexMap);   // mutate v-e links
01117             LookupTableReplace(edges, stronger, longer);
01118             LookupTableInsert(edges, shorter);
01119             jchain++;
01120           }
01121         }
01122       }
01123     }
01124   }
01125 #if 0
01126   vcl_cout << "Find junctions with "
01127            << jcycle << " cycles and " << jchain << " chains, with jump > "
01128            << minJump << vcl_endl;
01129 #endif
01130 
01131   // 3. Merge touching end points, into a larger junction/chain.
01132   int dendpt = 0, dchain = 0;   // number of deleted endpt/chain
01133 
01134   for (unsigned int i=0; i< vertices.size(); i++)
01135   {
01136     vtol_vertex_2d_sptr  end1 = vertices[i]; // search from dangling end pt only
01137     if (end1 &&          // skip deleted vertices
01138         NumConnectedRays(*end1) == 1) { // end point of dangling 1-chain
01139       vtol_vertex_2d_sptr  end2 = DetectTouch(*end1, maxSpiral, *vertexMap);
01140       if (end2) {       // find end points nearby
01141         if (NumConnectedRays(*end2) == 1) { // found another dangling end point
01142           vtol_edge_2d_sptr seg = DanglingEdge(*end1);
01143           if (seg == DanglingEdge(*end2)) { // end points of 1-cycle
01144             MergeEndPtsOfChain(*end1, *end2, *seg,
01145                                *edgeMap, *vertexMap);
01146             LookupTableRemove(vertices, end2);
01147             dendpt++;
01148           } else {              // end points of 2 distinct 1-chains
01149 #if 0
01150             vcl_cout << "endpt1=" << *end1 << vcl_endl
01151                      << "endpt2=" << *end2 << vcl_endl;
01152 #endif
01153             vtol_edge_2d_sptr merge=NULL, longer=NULL, shorter=NULL; // merge 2 different edges
01154             MergeEndPtTouchingEndPt(*end1, *end2, // merge 2 subchains
01155                                     merge, longer, shorter, // deleting
01156                                     *edgeMap, *vertexMap); // end points
01157 #if 0
01158             vcl_cout << "merge=" << *merge << vcl_endl
01159                      << "longer=" << *longer << vcl_endl
01160                      << "shorter=" << *shorter << vcl_endl
01161                      << "merge.v1=" << *merge->v1() << vcl_endl
01162                      << "merge.v2=" << *merge->v2() << vcl_endl;
01163 #endif
01164             LookupTableReplace(edges, longer, merge);
01165             LookupTableRemove(edges, shorter);
01166             LookupTableRemove(vertices, end1);
01167             LookupTableRemove(vertices, end2);
01168             dendpt += 2, dchain += 1;
01169           }
01170         } else {                // merge into another junction
01171 #if 0
01172           vcl_cout << "endpt1=" << *end1 << vcl_endl
01173                    << "junction2=" << *end2 << vcl_endl;
01174 #endif
01175           MergeEndPtTouchingJunction(*end1, *end2,
01176                                      *edgeMap, *vertexMap);
01177           LookupTableRemove(vertices, end1);
01178           dendpt++;
01179         }
01180       }
01181     }
01182   }
01183 #if 0
01184   vcl_cout << "Merge and delete " << dendpt << " end points and " << dchain << " edges\n";
01185 #endif
01186   if (dchain)                   // eliminate holes in global arrays
01187     LookupTableCompress(edges);
01188   if (dendpt)
01189     LookupTableCompress(vertices);
01190 
01191   // 4. Insert virtual junction for isolated 1-cycles
01192   int ncycle = 0;
01193   for (unsigned int i=0; i< edges.size(); ++i)
01194   {
01195     vtol_edge_2d_sptr edge = edges[i];
01196     if (!edge->v1()) {  // vertices not created from 1.
01197       vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01198       vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01199 
01200       const int last = cxy->size()-1;
01201       vtol_vertex_2d_sptr  v = new vtol_vertex_2d(((*cxy)[0].x()+(*cxy)[last].x())/2, ((*cxy)[0].y()+(*cxy)[last].y())/2);
01202       edge->set_v1(v->cast_to_vertex()); edge->set_v2(v->cast_to_vertex()); // link both directions v-e
01203       vertexMap->put(int(v->x()), int(v->y()), v);
01204       LookupTableInsert(vertices, v);
01205       ncycle++;
01206     }
01207   }
01208 #if 0
01209   vcl_cout << "Create " << ncycle << " virtual end points for isolated cycles.\n";
01210 #endif
01211 #ifdef DEBUG
01212   if (talkative)
01213     vcl_cout << "All junctions found in " << t.real() << " msecs.\n";
01214 #endif
01215   return vertices.size();
01216 }
01217 
01218 
01219 //: Insert subpixel accuracy into the pixels on the edges/vertices.
01220 // Truncating float locations with int(xy) should map to the original
01221 // pixel locations. No interpolation is done at junctions of 3 or more
01222 // contours, so a junction can have location error up to 1-2 pixel,
01223 // tangential to the strong contour.
01224 void
01225 gevd_contour::SubPixelAccuracy(vcl_vector<vtol_edge_2d_sptr>& edges,
01226                                vcl_vector<vtol_vertex_2d_sptr >& vertices,
01227                                const gevd_bufferxy& locationx,
01228                                const gevd_bufferxy& locationy)
01229 {
01230 #ifdef DEBUG
01231   vul_timer t;
01232 #endif
01233   if (talkative)
01234     vcl_cout << "Insert subpixel accuracy into edges/vertices";
01235 
01236   // 1. Subpixel accuracy for end points
01237   for (unsigned int i=0; i< vertices.size(); ++i)
01238   {
01239     vtol_vertex_2d_sptr  vert = vertices[i];
01240     int x = int(vert->x()), y = int(vert->y());
01241     vert->set_x(x + floatPixel(locationx, x, y));
01242     vert->set_y(y + floatPixel(locationy, x, y));
01243   }
01244 
01245   // 2. Subpixel accuracy for chain pixels
01246   for (unsigned int i=0; i< edges.size(); ++i)
01247   {
01248     vtol_edge_2d_sptr edge = edges[i];
01249     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01250     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01251 
01252     for (unsigned int k = 0; k < cxy->size(); ++k)
01253     {
01254       int x = int((*cxy)[k].x()), y = int((*cxy)[k].y());
01255 
01256       double tempx= (*cxy)[k].x()+ floatPixel( locationx, x, y);
01257       double tempy= (*cxy)[k].y()+ floatPixel( locationy, x, y);
01258       (*cxy)[k].set_x( tempx);
01259       (*cxy)[k].set_y( tempy);
01260     }
01261   }
01262 
01263   // 3. Thin zig-zags on the contours? Zig-zags happen at
01264   // the 2 end points because of extension, or inside the contour
01265   // because of 4/8-connected tracing through noisy chain pixels,
01266   // and large shifts for subpixel locations.
01267   // Implement only if experiments prove zig-zags are excessive
01268 
01269 #ifdef DEBUG
01270   if (talkative)
01271     vcl_cout << ", in " << t.real() << " msecs.\n";
01272 #endif
01273 }
01274 
01275 
01276 //:
01277 //  Generate an Edge with a vdgl_digital_curve representing a straight line
01278 //  between the specified vertices.
01279 static vtol_edge_2d_sptr DigitalEdge(vtol_vertex_2d_sptr  vs, vtol_vertex_2d_sptr  ve)
01280 {
01281   double xs= vs->x();
01282   double ys= vs->y();
01283   double xe= ve->x();
01284   double ye= ve->y();
01285 
01286   vdgl_edgel_chain *es= new vdgl_edgel_chain;
01287   vdgl_interpolator *it= new vdgl_interpolator_linear( es);
01288   vdgl_digital_curve *dc= new vdgl_digital_curve( it);
01289 
01290   es->add_edgel( vdgl_edgel( xs, ys));
01291   es->add_edgel( vdgl_edgel( xe, ye));
01292 
01293   vtol_edge_2d_sptr e = new vtol_edge_2d(vs, ve, vsol_curve_2d_sptr(dc));
01294   return e;
01295 }
01296 
01297 
01298 //:
01299 // Insert virtual edges and vertices to enforce closure
01300 // of the regions beyond the rectangular image border.
01301 // The location of the border is at 3 pixels away from the
01302 // real image border, because of kernel radius in convolution
01303 // and non maximum suppression. Virtual border of image should be
01304 // inserted after gevd_contour::FindChains() and gevd_contour::FindJunctions().
01305 //
01306 // JLM - February 1999  Modified this routine extensively to
01307 // move the border to the actual image ROI bounds.  Chain endpoints
01308 // are extended to intersect with the border.  These changes were
01309 // made to support region segmentation from edgels.
01310 void
01311 gevd_contour::InsertBorder(vcl_vector<vtol_edge_2d_sptr>& edges,
01312                            vcl_vector<vtol_vertex_2d_sptr >& vertices)
01313 {
01314 #ifdef DEBUG
01315   vul_timer t;
01316 #endif
01317   //1.00 Save Edges along the border
01318   vcl_vector<vtol_vertex_2d_sptr > xmin_verts;
01319   vcl_vector<vtol_vertex_2d_sptr > xmax_verts;
01320   vcl_vector<vtol_vertex_2d_sptr > ymin_verts;
01321   vcl_vector<vtol_vertex_2d_sptr > ymax_verts;
01322 
01323   if (talkative)
01324     vcl_cout << "Insert virtual border to enforce closure";
01325 
01326   // 0. Create 4 corners vertices
01327   const int rmax = FRAME;       // border of image
01328   const int xmax = vertexMap->rows()-rmax-1;
01329   const int ymax = vertexMap->columns()-rmax-1;
01330   int cx[] = {rmax, xmax, rmax, xmax}; // coordinates of 4 corners
01331   int cy[] = {rmax, ymax, ymax, rmax};
01332 
01333   // 1. Collect Vertices along each border
01334   //1.0 Generate Corner Vertices
01335   vtol_vertex_2d_sptr  V00 = new vtol_vertex_2d(rmax, rmax);
01336   vtol_vertex_2d_sptr  V01 = new vtol_vertex_2d(rmax, ymax);
01337   vtol_vertex_2d_sptr  V10 = new vtol_vertex_2d(xmax, rmax);
01338   vtol_vertex_2d_sptr  V11 = new vtol_vertex_2d(xmax, ymax);
01339   xmin_verts.push_back(V00);
01340   xmax_verts.push_back(V10);
01341   ymin_verts.push_back(V00);
01342   ymax_verts.push_back(V01);
01343   // 1.1 ymin, ymax edges
01344   for (int d = 0; d < 2; d++)
01345   {
01346     int y = cy[d];
01347     for (int x = rmax; x<=xmax; ++x)
01348     {
01349       vtol_vertex_2d_sptr  v = vertexMap->get(x, y);
01350       if (v)
01351         vertexMap->put(x, y, NULL);
01352       else continue;
01353       if (d)
01354         ymax_verts.push_back(v);
01355       else
01356         ymin_verts.push_back(v);
01357     }
01358   }
01359   // 1.2 xmin, xmax edges
01360   for (int d = 0; d < 2; d++)
01361   {
01362     int x = cx[d];
01363     for (int y = rmax; y<=ymax; y++)
01364     {
01365       vtol_vertex_2d_sptr  v = vertexMap->get(x, y);
01366       if (v)
01367         vertexMap->put(x, y, NULL);
01368       else continue;
01369       if (d)
01370         xmax_verts.push_back(v);
01371       else
01372         xmin_verts.push_back(v);
01373     }
01374   }
01375 
01376   xmin_verts.push_back(V01);
01377   xmax_verts.push_back(V11);
01378   ymin_verts.push_back(V10);
01379   ymax_verts.push_back(V11);
01380 
01381   // 2.0 Merge vertices
01382   // 2.1  along ymin and ymax
01383   for (int d = 0; d < 2; d++)
01384   {
01385     vcl_vector<vtol_vertex_2d_sptr >* verts = &ymin_verts;
01386     if (d)
01387       verts = &ymax_verts;
01388     int len = (*verts).size();
01389     if (len<3) continue;
01390     //potential merge at xmin
01391     vtol_vertex_2d_sptr  pre_v = (*verts)[0];
01392     vtol_vertex_2d_sptr  v = (*verts)[1];
01393     int x = int(v->x());
01394     int pre_x = int(pre_v->x());
01395     if ((x-pre_x)<3)
01396     {
01397 #if 0 //GEOFF
01398       merge_references(pre_v,v);
01399 #endif
01400       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01401       {
01402         if (*it == v)
01403         {
01404           verts->erase(it);
01405           break;
01406         }
01407       }
01408       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01409       {
01410         if (*it == v)
01411         {
01412           vertices.erase(it);
01413           break;
01414         }
01415       }
01416       len--;
01417     }
01418     //potential merge at xmax
01419     pre_v = (*verts)[len-2];
01420     v = (*verts)[len-1];
01421     pre_x = int(pre_v->x());
01422     if ((xmax+rmax-pre_x)<3)
01423     {
01424 #if 0 //GEOFF
01425       merge_references(v,pre_v);
01426 #endif
01427       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01428       {
01429         if (*it == pre_v)
01430         {
01431           verts->erase(it);
01432           break;
01433         }
01434       }
01435       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01436       {
01437         if (*it == pre_v)
01438         {
01439           vertices.erase(it);
01440           break;
01441         }
01442       }
01443       len--;
01444     }
01445   }
01446 
01447   // 2.1  along xmin and xmax
01448   for (int d = 0; d < 2; d++)
01449   {
01450     vcl_vector<vtol_vertex_2d_sptr >* verts = &xmin_verts;
01451     if (d)
01452       verts = &xmax_verts;
01453     int len = (*verts).size();
01454     if (len<3) continue;
01455     //potential merge at ymin
01456     vtol_vertex_2d_sptr  pre_v = (*verts)[0];
01457     vtol_vertex_2d_sptr  v = (*verts)[1];
01458 
01459 
01460     int y = int(v->y()), pre_y = int(pre_v->y());
01461     if ((y-pre_y)<3)
01462     {
01463 #if 0 //GEOFF
01464       merge_references(pre_v,v);
01465 #endif
01466       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01467       {
01468         if (*it == v)
01469         {
01470           verts->erase( it);
01471           break;
01472         }
01473       }
01474       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01475       {
01476         if (*it == v)
01477         {
01478           vertices.erase( it);
01479           break;
01480         }
01481       }
01482       len--;
01483     }
01484     //potential merge at ymax
01485     pre_v = (*verts)[len-2];
01486     v = (*verts)[len-1];
01487     pre_y = int(pre_v->y());
01488     if ((ymax+rmax-pre_y)<3)
01489     {
01490 #if 0 //GEOFF
01491       merge_references(v,pre_v);
01492 #endif
01493       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin(); it!= verts->end(); ++it)
01494       {
01495         if (*it == pre_v)
01496         {
01497           verts->erase( it);
01498           break;
01499         }
01500       }
01501       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin(); it!= vertices.end(); ++it)
01502       {
01503         if (*it == pre_v)
01504         {
01505           vertices.erase( it);
01506           break;
01507         }
01508       }
01509       len--;
01510     }
01511   }
01512   // 2.0 Move the vertices to the bounds of the ROI
01513   int iv,  len = xmin_verts.size();
01514   float xmi = 0, xmx = float(xmax + rmax);
01515   float ymi = 0, ymx = float(ymax + rmax);
01516   for (iv=1; iv<len-1; iv++)
01517   {
01518     vtol_vertex_2d_sptr  v = xmin_verts[iv];
01519     vtol_vertex_2d_sptr  vp = new vtol_vertex_2d(xmi, v->y());
01520     vertices.push_back(vp);// vp->Protect();
01521     xmin_verts[iv] = vp;
01522 
01523 
01524     vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01525     edges.push_back(e);//  e->Protect();
01526   }
01527 
01528   len = xmax_verts.size();
01529   for (iv=0; iv<len; iv++)
01530   {
01531     vtol_vertex_2d_sptr  v = xmax_verts[iv];
01532     if (iv!=0&&iv!=(len-1))
01533     {
01534       vtol_vertex_2d_sptr  vp = new vtol_vertex_2d( xmx, v->y());
01535       vertices.push_back(vp); // vp->Protect();
01536       xmax_verts[iv] = vp;
01537       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01538       edges.push_back(e); // e->Protect();
01539     }
01540   }
01541   len = ymin_verts.size();
01542   for (iv=0; iv<len; iv++)
01543   {
01544     vtol_vertex_2d_sptr  v = ymin_verts[iv];
01545     if (iv!=0&&iv!=(len-1))
01546     {
01547       vtol_vertex_2d_sptr  vp = new vtol_vertex_2d(v->x(), ymi);
01548       vertices.push_back(vp); // vp->Protect();
01549       ymin_verts[iv] = vp;
01550       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01551       edges.push_back(e); // e->Protect();
01552     }
01553   }
01554   len = ymax_verts.size();
01555   for (iv=0; iv<len; iv++)
01556   {
01557     vtol_vertex_2d_sptr  v = ymax_verts[iv];
01558     if (iv!=0&&iv!=(len-1))
01559     {
01560       vtol_vertex_2d_sptr  vp = new vtol_vertex_2d( v->x(), ymx);
01561       vertices.push_back(vp); // vp->Protect();
01562       ymax_verts[iv] = vp;
01563       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01564       edges.push_back(e); // e->Protect();
01565     }
01566   }
01567   V00->set_x(0);  V00->set_y(0); vertices.push_back(V00);
01568   V01->set_x(0);  V01->set_y(ymax+rmax); vertices.push_back(V01);
01569   V10->set_x(xmax+rmax);  V10->set_y(0); vertices.push_back(V10);
01570   V11->set_x(xmax+rmax);  V11->set_y(ymax+rmax); vertices.push_back(V11);
01571 
01572   //4.0 Now we have properly placed vertices.  Next we scan and generate
01573   //edges. along the border.
01574   //4.1 along ymin and ymax
01575   for (int d = 0; d < 2; d++)
01576   {
01577     vcl_vector<vtol_vertex_2d_sptr >* verts = &ymin_verts;
01578     if (d)
01579       verts = &ymax_verts;
01580     int len = (*verts).size();
01581     if (len<2)
01582     {
01583       vcl_cout <<"In gevd_contour::InsertBorder() - too few vertices\n";
01584       return;
01585     }
01586     for (int i = 0; i<len-1; i++)
01587     {
01588       vtol_vertex_2d_sptr  v = (*verts)[i];
01589       vtol_vertex_2d_sptr  vp = (*verts)[i+1];
01590       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01591       edges.push_back(e); // e->Protect();
01592     }
01593   }
01594   //4.2 along xmin and xmax
01595   for (int d = 0; d < 2; d++)
01596   {
01597     vcl_vector<vtol_vertex_2d_sptr >* verts = &xmin_verts;
01598     if (d)
01599       verts = &xmax_verts;
01600     int len = (*verts).size();
01601     if (len<2)
01602     {
01603       vcl_cout <<"In gevd_contour::InsertBorder() - too few vertices\n";
01604       return;
01605     }
01606     for (int i = 0; i<len-1; i++)
01607     {
01608       vtol_vertex_2d_sptr  v = (*verts)[i];
01609       vtol_vertex_2d_sptr  vp = (*verts)[i+1];
01610       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
01611       edges.push_back(e); // e->Protect();
01612     }
01613   }
01614 #ifdef DEBUG
01615   if (talkative)
01616     vcl_cout << ", in " << t.real() << " msecs.\n";
01617 #endif
01618 }
01619 
01620 
01621 //:
01622 // Convolve array elements with [1 0 1]/2, replacing
01623 // center pixel by average of 2 neighbors.
01624 // This will make the spacing between pixels almost equal
01625 // and prune away small zig-zags.
01626 static void
01627 EqualizeElements(double* elmts, int n, double v1, double v2)
01628 {
01629   double p0 = elmts[0], p1 = elmts[1], p2 = elmts[2]; // setup pipeline
01630   elmts[0] = (v1 + p1) / 2;     // touching first vertex
01631   for (int i = 1; i < n-2; i++) {
01632     elmts[i] = (p0 + p2)/2;
01633     p0 = p1; p1 = p2; p2 = elmts[i+2]; // faster with circular list
01634   }
01635   if (n>1) elmts[n-2] = (p0 + p2)/2;   // last convolution
01636   if (n>0) elmts[n-1] = (p1 + v2)/2;   // touching second vertex
01637 }
01638 
01639 
01640 //:
01641 // Make the spacing of the chain pixels nearly equal by
01642 // smoothing their locations with the average filter  [1 0 1]/2.
01643 // This will reduce square grid tessellation artifacts, and
01644 // lead to more accurate estimation of the tangent direction,
01645 // and local curvature angle, from finite differences in location.
01646 // It is also useful to avoid weight artifacts in curve fitting
01647 // caused by the periodic clustering of pixels along the chain.
01648 // Truncating the float locations with int() will no longer map
01649 // to the original pixels of the discrete chains.
01650 void
01651 gevd_contour::EqualizeSpacing(vcl_vector<vtol_edge_2d_sptr>& chains)
01652 {
01653 #ifdef DEBUG
01654   vul_timer t;
01655 #endif
01656   if (talkative)
01657     vcl_cout << "Equalize the spacing between pixels in chains";
01658 
01659   for (unsigned int i= 0; i< chains.size(); i++)
01660   {
01661     vtol_edge_2d_sptr e = chains[i];
01662     vdgl_digital_curve_sptr dc = e->curve()->cast_to_vdgl_digital_curve();
01663     const int len = dc->get_interpolator()->get_edgel_chain()->size();
01664     if (len > 2*MINLENGTH)
01665     {   // not necessary for short chains
01666       vtol_vertex_sptr v1 = e->v1(), v2 = e->v2();
01667 
01668       vcl_vector<double> cx(len);
01669       vcl_vector<double> cy(len);
01670 
01671       for (int qq=0; qq<len; qq++)
01672       {
01673         vdgl_edgel e= dc->get_interpolator()->get_edgel_chain()->edgel( qq);
01674         cx[qq]= e.x();
01675         cy[qq]= e.y();
01676       }
01677 
01678       EqualizeElements(&cx[0], len, v1->cast_to_vertex_2d()->x(), v2->cast_to_vertex_2d()->x());
01679       EqualizeElements(&cy[0], len, v1->cast_to_vertex_2d()->y(), v2->cast_to_vertex_2d()->y());
01680 
01681       for (int qq=0; qq<len; qq++)
01682       {
01683         vdgl_edgel e( cx[qq], cy[qq]);
01684         dc->get_interpolator()->get_edgel_chain()->set_edgel( qq, e);
01685       }
01686     }
01687   }
01688 #ifdef DEBUG
01689   if (talkative)
01690     vcl_cout << ", in " << t.real() << " msecs.\n";
01691 #endif
01692 }
01693 
01694 
01695 //: Translate all the pixels in the edges and vertices by (tx, ty).
01696 // If the image is extracted from an ROI, a translation of
01697 // (roi->GetOrigX(), roi->GetOrigY()) must be done to have
01698 // coordinates in the reference frame of the original image.
01699 // Add 0.5 if you want to display location at center of pixel
01700 // instead of upper-left corner.
01701 void
01702 gevd_contour::Translate(vcl_vector<vtol_edge_2d_sptr>& edges, // translate loc to center
01703                         vcl_vector<vtol_vertex_2d_sptr >& vertices,
01704                         const float tx, const float ty)
01705 {
01706 #ifdef DEBUG
01707   vul_timer t;
01708 #endif
01709   if (talkative)
01710     vcl_cout << "Translate edges/vertices";
01711   for (unsigned int i=0; i< vertices.size(); i++) {
01712     vtol_vertex_2d_sptr  vert = vertices[i];
01713     vert->set_x(vert->x() + tx);
01714     vert->set_y(vert->y() + ty);
01715   }
01716   for (unsigned int i=0; i< edges.size(); i++) {
01717     vtol_edge_2d_sptr edge = edges[i];
01718     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01719 
01720     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01721     for (unsigned int k = 0; k < cxy->size(); ++k) {
01722       vdgl_edgel e= (*cxy)[k];
01723 
01724       e.set_x( e.x()+tx);
01725       e.set_y( e.y()+ty);
01726 
01727       cxy->set_edgel( k, e);
01728     }
01729   }
01730 #ifdef DEBUG
01731   if (talkative)
01732     vcl_cout << ", in " << t.real() << " msecs.\n";
01733 #endif
01734 }
01735 
01736 
01737 //:
01738 // Remove and delete all elements in global lists, and set
01739 // the global lists to NULL. Remove all digital chains of edges.
01740 // Edges and vertices are removed with UnProtect().
01741 void
01742 gevd_contour::ClearNetwork(vcl_vector<vtol_edge_2d_sptr>*& edges,
01743                            vcl_vector<vtol_vertex_2d_sptr >*& vertices)
01744 {
01745   if (edges) {
01746     for (unsigned int i=0; i< edges->size(); ++i) {
01747 #if 0 // not (yet) converted
01748       vtol_edge_2d_sptr edge = (*edges)[i];
01749       Curve* dc = NULL;        // retrieve digital chain dc
01750       vsol_curve_2d *dc= 0;
01751 #if 0 //GEOFF
01752       edge->set_curve(0); // and remove it from edge
01753 #endif
01754       edge->UnProtect();        // delete edge
01755       delete (vdgl_digital_curve *) dc; // delete dc
01756 #endif
01757     }
01758     delete edges; edges = NULL;
01759   }
01760   if (vertices) {
01761 #if 0
01762     for (vertices->reset(); vertices->next(); )
01763     for (unsigned int i=0; i< vertices->size(); ++i)
01764       vertices[i]->UnProtect();
01765 #endif
01766     delete vertices; vertices = NULL;
01767   }
01768 }
01769 
01770 //:
01771 // Mask the detected edge elements and junctions with a given
01772 // mask array, using AND operation, for ROI with arbitrary shapes.
01773 void
01774 gevd_contour::MaskEdgels(const gevd_bufferxy& mask,
01775                          gevd_bufferxy& edgels, // edge elements AND with mask
01776                          int& njunction, // junctions AND with mask
01777                          int* junctionx, int* junctiony)
01778 {
01779   int x, y;
01780   for (y = 0; y < edgels.GetSizeY(); y++)
01781     for (x = 0; x < edgels.GetSizeX(); x++)
01782       if (floatPixel(edgels, x, y) && // is edge element
01783           !bytePixel(mask, x, y)) // is not in mask
01784         floatPixel(edgels, x, y) = 0; // remove edgel not in mask
01785   int j = 0;
01786   for (int i = 0; i < njunction; i++) {
01787     x = junctionx[i], y = junctiony[i];
01788     if (bytePixel(mask, x, y)) { // keep junction in mask
01789       junctionx[j] = x, junctiony[j] = y, j++;
01790     }
01791   }
01792   njunction = j;
01793 }
01794 
01795 
01796 //:
01797 // Set the orientation at each edgel on all digital curves to a continuous
01798 // orientation value, which is consistent with C. Rothwell's EdgeDetector.
01799 // That is theta = (180/M_PI)*atan2(dI/dy, dI/dx)
01800 //
01801 void
01802 gevd_contour::SetEdgelData(gevd_bufferxy& grad_mag, gevd_bufferxy& angle, vcl_vector<vtol_edge_2d_sptr>& edges)
01803 {
01804   for (unsigned int i=0; i< edges.size(); i++)
01805   {
01806     vtol_edge_2d_sptr e = edges[i];
01807     vdgl_digital_curve_sptr dc= e->curve()->cast_to_vdgl_digital_curve();
01808 
01809     if (dc)
01810     {
01811       vdgl_edgel_chain_sptr xypos= dc->get_interpolator()->get_edgel_chain();
01812 
01813       int len = xypos->size();
01814 
01815       for (int i = 0; i < len; i++)
01816       {
01817         int ix = int((*xypos)[i].x());
01818         int iy = int((*xypos)[i].y());
01819 
01820         // Debugging : RIH
01821         // Routine crashes with iy < 0.
01822         if (iy < 0 || ix < 0 ||
01823             ix >= grad_mag.GetSizeX() ||
01824             iy >= grad_mag.GetSizeY())
01825         {
01826           vcl_cerr << "***********  ERROR  : (ix, iy) = ("
01827                    << ix << ", " << iy << ")\n";
01828           if (ix < 0) ix = 0;
01829           if (iy < 0) iy = 0;
01830           if (ix >= grad_mag.GetSizeX()) ix = grad_mag.GetSizeX()-1;
01831           if (iy >= grad_mag.GetSizeY()) iy = grad_mag.GetSizeY()-1;
01832         }
01833 
01834         vdgl_edgel edgel= xypos->edgel(i);
01835         edgel.set_grad( floatPixel( grad_mag, ix, iy));
01836         edgel.set_theta( floatPixel( angle, ix, iy));
01837 
01838 #if 0
01839         gr[i] = floatPixel(grad_mag, ix, iy);
01840         th[i] = floatPixel(angle, ix, iy);
01841 #endif
01842       }
01843     }
01844   }
01845 }
01846 
01847 
01848 //: Compare function to sort the edges by their length in pixels, largest first.
01849 int
01850 gevd_contour::LengthCmp(vtol_edge_2d_sptr const& dc1, vtol_edge_2d_sptr const& dc2)
01851 {
01852   vdgl_digital_curve_sptr c1 = ((vtol_edge_2d_sptr)dc1)->curve()->cast_to_vdgl_digital_curve();
01853   vdgl_digital_curve_sptr c2 = ((vtol_edge_2d_sptr)dc2)->curve()->cast_to_vdgl_digital_curve();
01854   return c2->get_interpolator()->get_edgel_chain()->size() - c1->get_interpolator()->get_edgel_chain()->size();
01855 }
01856 
01857 
01858 //: Create a 2-way lookup table from list elements in set, using array and get_id/set_id.
01859 vcl_vector<vtol_edge_2d_sptr>*
01860 gevd_contour::CreateLookupTable(vcl_vector<vtol_edge_2d_sptr>& set)
01861 {
01862   vcl_vector<vtol_edge_2d_sptr>* set2 =
01863     new vcl_vector<vtol_edge_2d_sptr>(2*set.size()); // preallocate space
01864   for (unsigned int i=0; i< set.size(); i++)
01865     gevd_contour::LookupTableInsert(*set2, set[i]);
01866   return set2;
01867 }
01868 
01869 //: As above for vertices.
01870 vcl_vector<vtol_vertex_2d_sptr >*
01871 gevd_contour::CreateLookupTable(vcl_vector<vtol_vertex_2d_sptr >& set)
01872 {
01873   vcl_vector<vtol_vertex_2d_sptr >* set2 =
01874     new vcl_vector<vtol_vertex_2d_sptr >(2*set.size()); // preallocate space
01875   for (unsigned int i=0; i< set.size(); i++)
01876     gevd_contour::LookupTableInsert(*set2, set[i]);
01877   return set2;
01878 }
01879 
01880 
01881 //:
01882 // Insert topology object in 2-way lookup table,
01883 // using Id and dynamic array. Protect it in the network.
01884 void
01885 gevd_contour::LookupTableInsert(vcl_vector<vtol_edge_2d_sptr>& set,
01886                                 vtol_edge_2d_sptr elmt)
01887 {
01888   elmt->set_id(set.size());     // index in global array
01889   set.push_back(elmt);          // push_back at end of array
01890 }
01891 
01892 
01893 //: As above for vertices.
01894 void
01895 gevd_contour::LookupTableInsert(vcl_vector<vtol_vertex_2d_sptr >& set,
01896                                 vtol_vertex_2d_sptr  elmt)
01897 {
01898   elmt->set_id(set.size());     // index in global array
01899   set.push_back(elmt);          // push at end of array
01900 }
01901 
01902 
01903 //: Replace deleted by inserted in 2-way lookup table.
01904 // Also remove object from the network.
01905 void
01906 gevd_contour::LookupTableReplace(vcl_vector<vtol_edge_2d_sptr>& set,
01907                                  vtol_edge_2d_sptr deleted, vtol_edge_2d_sptr inserted)
01908 {
01909   const int i = deleted->get_id();
01910   inserted->set_id(i);
01911   set[i] = inserted;            // replace in global array
01912 #if 0 //GEOFF
01913   deleted->unlink_all_inferiors_twoway(deleted);
01914 #endif
01915 }
01916 
01917 
01918 //: As above for vertices.
01919 void
01920 gevd_contour::LookupTableReplace(vcl_vector<vtol_vertex_2d_sptr >& set,
01921                                  vtol_vertex_2d_sptr  deleted, vtol_vertex_2d_sptr  inserted)
01922 {
01923   const int i = deleted->get_id();
01924   inserted->set_id(i);
01925   set[i] = inserted;            // replace in global array
01926 }
01927 
01928 
01929 //: Remove topology object from 2-way lookup table leaving an empty hole.
01930 // Also remove object from the network.
01931 void
01932 gevd_contour::LookupTableRemove(vcl_vector<vtol_edge_2d_sptr>& set,
01933                                 vtol_edge_2d_sptr elmt)
01934 {
01935   set[elmt->get_id()] = NULL;   // remove from global array
01936 }
01937 
01938 
01939 //: As above for vertices.
01940 void
01941 gevd_contour::LookupTableRemove(vcl_vector<vtol_vertex_2d_sptr >& set,
01942                                 vtol_vertex_2d_sptr  elmt)
01943 {
01944   set[elmt->get_id()] = NULL;   // remove from global array
01945 }
01946 
01947 
01948 //: Eliminate empty holes in the lookup table.
01949 void
01950 gevd_contour::LookupTableCompress(vcl_vector<vtol_edge_2d_sptr>& set)
01951 {
01952   int i = 0;
01953   for (int j = set.size()-1; i <= j; i++)
01954     if (!set[i]) {      // find empty hole
01955       vtol_edge_2d_sptr last = NULL;
01956       for (; i < j; j--)
01957         if (set[j]) {
01958           last = set[j]; j--; // remove from the end
01959           break;
01960         }
01961       if (last) {
01962         last->set_id(i);                // move it to the front
01963         set[i] = last;
01964       } else
01965         break;                  // no more elements
01966     }
01967   set.resize(i - 1);
01968 }
01969 
01970 
01971 //: As above for vertices.
01972 void
01973 gevd_contour::LookupTableCompress(vcl_vector<vtol_vertex_2d_sptr >& set)
01974 {
01975   int i = 0;
01976   for (int j = set.size()-1; i <= j; i++)
01977     if (!set[i]) {              // find empty hole
01978       vtol_vertex_2d_sptr  last = NULL;
01979       for (; i < j; j--)
01980         if (set[j]) {
01981           last = set[j]; j--; // remove from the end
01982           break;
01983         }
01984       if (last) {
01985         last->set_id(i);                // move it to the front
01986         set[i] = last;
01987       } else
01988         break;                  // no more elements
01989     }
01990   set.resize(i - 1);
01991 }
01992 
01993 //: Check a few obvious invariants, and return number of errors.
01994 // 0. Network has closure of all vertices and edges.
01995 // 1. No 2 vertices touch: endpt/endpt, endpt/junction or junction/junction
01996 // 2. No vertex connecting 2 edges, each vertex has 1 or >= 3 edges,
01997 //    except the 4 corners of image border.
01998 // 3. Each edge has >= 3 internal pixels.
01999 // 4. A chain can touch/join with itself or with a stronger chain.
02000 //    Junction is created only if the local change in filter response
02001 //    is greater than some noise threshold, set by the user.
02002 //    Junction is created only if the 2 broken up chains have
02003 //    lengths >= 3.
02004 int
02005 gevd_contour::CheckInvariants(vcl_vector<vtol_edge_2d_sptr>& edges,
02006                               vcl_vector<vtol_vertex_2d_sptr >& vertices)
02007 {
02008   int nerror = 0;
02009 
02010   // 0. Check that vertices of all edges have been listed
02011   const int unmark = -1;
02012   for (unsigned int i=0; i< edges.size(); i++)
02013     edges[i]->set_id(unmark);
02014   for (unsigned int i=0; i< vertices.size(); i++)
02015     vertices[i]->set_id(unmark);
02016   for (unsigned int i=0; i< edges.size(); i++) {
02017     vtol_edge_2d_sptr e = edges[i];
02018     vtol_vertex_sptr v1 = e->v1();
02019     if (v1->get_id() != unmark) {
02020       vcl_cout << *v1 << ": v1 is not in vertex list\n";
02021       nerror++;
02022     }
02023     vtol_vertex_sptr v2 = e->v2();
02024     if (v2->get_id() != unmark) {
02025       vcl_cout << *v2 << ": v2 is not in vertex list\n";
02026       nerror++;
02027     }
02028   }
02029   for (unsigned int i=0; i< vertices.size(); i++) {
02030     vcl_vector<vtol_edge_sptr> es; vertices[i]->edges(es);
02031     for (unsigned int j=0; j< es.size(); j++)
02032       if (es[j]->get_id() != unmark) {
02033         vcl_cout << es[j] << ": e is not in edge list\n";
02034         nerror++;
02035       }
02036   }
02037   // mark id with index in global list
02038   for (unsigned int id=0; id< edges.size(); id++)
02039     edges[id]->set_id(id);
02040   for (unsigned int id=0; id< vertices.size(); id++)
02041     vertices[id]->set_id(id);
02042 
02043   return nerror;
02044 }
02045