contrib/brl/bseg/sdet/sdet_contour.cxx
Go to the documentation of this file.
00001 // This is brl/bseg/sdet/sdet_contour.cxx
00002 #include "sdet_contour.h"
00003 //:
00004 // \file
00005 #include <vcl_iostream.h>
00006 #include <vcl_cstdlib.h>   // for vcl_abs(int) and vcl_qsort()
00007 #include <vcl_vector.h>
00008 #include <vcl_cmath.h>
00009 #include <vcl_algorithm.h> // for vcl_max()
00010 #include <vul/vul_timer.h>
00011 #include <vxl_config.h>
00012 #include <vnl/vnl_math.h> // for sqrt2
00013 #include <vdgl/vdgl_digital_curve_sptr.h>
00014 #include <vdgl/vdgl_digital_curve.h>
00015 #include <vdgl/vdgl_edgel_chain_sptr.h>
00016 #include <vdgl/vdgl_edgel_chain.h>
00017 #include <vdgl/vdgl_interpolator.h>
00018 #include <vdgl/vdgl_interpolator_linear.h>
00019 #include <vtol/vtol_vertex_2d.h>
00020 #include <vtol/vtol_edge_2d.h>
00021 #include <btol/btol_vertex_algs.h>
00022 #include <btol/btol_edge_algs.h>
00023 #include <bdgl/bdgl_curve_algs.h>
00024 #include <gevd/gevd_bufferxy.h>
00025 #include <gevd/gevd_pixel.h>
00026 
00027 #ifdef DEBUG
00028  bool sdet_contour::talkative_ = true;
00029  bool sdet_contour::debug_ = true;      // Print extensive debug messages
00030 #else
00031  bool sdet_contour::talkative_ = false; // By default contour is not silent.
00032  bool sdet_contour::debug_ = false;
00033 #endif
00034 
00035 const int INVALID = -1;
00036 
00037 // Use 8 directions, with 45 degree angle in between them.
00038 
00039 const vxl_byte TWOPI = 8, /* FULLPI = 4, */ HALFPI = 2 /* , QUARTERPI = 1 */;
00040 //const vxl_byte DIR0 = 8, DIR1 = 9, DIR2 = 10, DIR3 = 11;
00041 const int DIS[] = { 1, 1, 0,-1,-1,-1, 0, 1, // 8-connected neighbors
00042                     1, 1, 0,-1,-1,-1, 0, 1, // wrapped by 2PI to
00043                     1, 1, 0,-1,-1,-1, 0, 1};// avoid modulo operations.
00044 const int DJS[] = { 0, 1, 1, 1, 0,-1,-1,-1,
00045                     0, 1, 1, 1, 0,-1,-1,-1,
00046                     0, 1, 1, 1, 0,-1,-1,-1};
00047 
00048 //const int RDS[] = {0,-1, 1,-2, 2,-3, 3,-4, 4,-5, 5}; // radial search
00049 const int RIS[] = { 1, 0,-1, 0, // spiral search for 4/8-connected
00050                     1,-1,-1, 1, // neighbors
00051                     2, 0,-2, 0,
00052                     2, 1,-1,-2,
00053                     -2,-1, 1, 2,
00054                     2,-2,-2, 2,
00055                     3, 0,-3, 0,
00056                     3, 1,-1,-3,
00057                     -3,-1, 1, 3,
00058                     3, 2,-2,-3,
00059                     -3,-2, 2, 3,
00060                     4, 0,-4, 0};
00061 const int RJS[] = { 0, 1, 0,-1, // rotate CW, increasing radius
00062                     1, 1,-1,-1,
00063                     0, 2, 0,-2,
00064                     1, 2, 2, 1,
00065                     -1,-2,-2,-1,
00066                     2, 2,-2,-2,
00067                     0, 3, 0,-3,
00068                     1, 3, 3, 1,
00069                     -1,-3,-3,-1,
00070                     2, 3, 3, 2,
00071                     -2,-3,-3,-2,
00072                     0, 4, 0,-4};
00073 const int RNS[] = { 4, 8, 12, 20, 24, 28, 36, 44, 48}; // at distinct r
00074 const float RGS[] = { 1.f, 1.414213f, 2.f, 2.236067f, 2.828427f, // values of gap
00075                       3.f, 3.162277f, 3.605551f, 4.f};
00076 
00077 // - win32 - moved to here for MSVC++
00078 const int MINLENGTH = 3;        // minimum number of pixels for a chain
00079 const int FRAME = 4;            // border of image
00080 
00081 //: A container to support sorting of edge lengths
00082 // Will result in descending order according to length
00083 struct sdet_contour_edge
00084 {
00085   sdet_contour_edge () {}
00086 
00087   void set_edge(vtol_edge_2d_sptr const& e) {e_ = e;}
00088   vtol_edge_2d_sptr edge() {return e_;}
00089 
00090   double length() {return e_->curve()->length();}
00091 
00092  private:
00093   vtol_edge_2d_sptr e_;
00094 };
00095 
00096 //The sort compare function
00097 static int compare(sdet_contour_edge*  ea,
00098                    sdet_contour_edge*  eb)
00099 {
00100   if (ea->length() < eb->length())
00101     return +1;
00102   return -1;
00103 }
00104 
00105 //: Save parameters and create workspace for detecting contours.
00106 // Each contour must have at least 1 pixel above min_strength,
00107 // and its number of internal pixels must be above min_length.
00108 // This is a heuristic hysteresis scheme that prunes weak or short
00109 // isolated chains.
00110 // To join a weaker contour to a stronger contour, a junction must
00111 // have a change in response above min_jump on the stronger contour.
00112 // This way, only strong junctions are detected.
00113 sdet_contour::sdet_contour(float min_strength, int min_length,
00114                            float min_jump, float max_gap_in)
00115   : minStrength(min_strength), minLength(min_length),
00116     minJump(min_jump), maxSpiral(0),
00117     edgeMap(), vertexMap()
00118 {
00119   if (minStrength < 0)
00120   {
00121     vcl_cerr << "sdet_contour::sdet_contour -- negative min_strength: "
00122              << minStrength << ". Reset to 0.\n";
00123     minStrength = 0;
00124   }
00125   if (minLength < MINLENGTH)
00126   {
00127     vcl_cerr << "sdet_contour::sdet_contour -- too small min_length: "
00128              << minLength << ". Reset to " << MINLENGTH << ".\n";
00129     minLength = MINLENGTH;
00130   }
00131   if (minJump < 0)
00132   {
00133     vcl_cerr << "sdet_contour::sdet_contour -- negative min_jump: "
00134              << minJump << ". Reset to 0.\n";
00135     minJump = 0;
00136   }
00137   if (minJump > minStrength)
00138   {
00139     vcl_cerr << "sdet_contour::sdet_contour -- too large min_jump: "
00140              << min_jump << ". Reset to " << minStrength << ".\n";
00141     minJump = minStrength;
00142   }
00143   if (max_gap_in < 1)
00144   {
00145     vcl_cerr << "sdet_contour::sdet_contour -- too small max_gap: "
00146              << max_gap_in << ". Reset to 1.\n";
00147     max_gap_in = 1;
00148   }
00149   if (max_gap_in > FRAME)
00150   {
00151     vcl_cerr << "sdet_contour::sdet_contour -- too large max_gap: "
00152              << max_gap_in << ". Reset to " << FRAME << vcl_endl;
00153     max_gap_in = FRAME;
00154   }
00155   max_gap = max_gap_in;
00156   for (int i = 0; i < 9; i++)   // find number of neighbors to search
00157     if (max_gap <= RGS[i])      // for given gap radius
00158       maxSpiral= i+1;
00159 }
00160 
00161 
00162 //: Free space allocated for detecting contours.
00163 sdet_contour::~sdet_contour()
00164 {
00165   delete edgeMap;               // space shared by LinkJunction/Chain
00166   delete vertexMap;
00167 }
00168 
00169 
00170 //: Find network of linked edges and vertices, from 8-connected edge elements.
00171 // The contours must be less than 2 pixel wide,
00172 // for example found from non maximum suppression.
00173 // Isolated edgels and short segments are erased.
00174 bool
00175 sdet_contour::FindNetwork(gevd_bufferxy& edgels, bool junctionp,
00176                           const int njunction,
00177                           const int* junctionx, const int* junctiony,
00178                           vcl_vector<vtol_edge_2d_sptr>*& edges,
00179                           vcl_vector<vtol_vertex_2d_sptr >*& vertices)
00180 {
00181   // make sure that if no edges are found that edges and vertices
00182   // get values, to avoid seg faults, WAH
00183   if (!edges)
00184     edges = new vcl_vector<vtol_edge_2d_sptr>;
00185   else
00186     edges->clear();
00187   if (!vertices)
00188     vertices = new vcl_vector<vtol_vertex_2d_sptr >;
00189   else
00190     vertices->clear();
00191 
00192 
00193   if (this->talkative_)
00194     vcl_cout << "*** Link edge elements into connected edges/vertices.\n";
00195 
00196   // 1. Setup lookup maps based on (x,y) integer location.
00197   vertexMap = new vbl_array_2d<vtol_vertex_2d_sptr>(edgels.GetSizeX(), edgels.GetSizeY());
00198   vertexMap->fill(NULL);
00199   edgeMap = new vbl_array_2d<vtol_edge_2d_sptr>(edgels.GetSizeX(), edgels.GetSizeY());
00200   edgeMap->fill(NULL);
00201 
00202   // 2. Collect 4/8-connected pixels into chains
00203   int n; // = vcl_max(10*njunction, // preallocated size from junctions or
00204   //       edgels.GetSizeX()*edgels.GetSizeY()/100); // image size
00205   vcl_vector<vtol_edge_2d_sptr> *edges2 = new vcl_vector<vtol_edge_2d_sptr>;
00206   n = this->FindChains(edgels, // link pixels into chains
00207                        njunction, // also use junction pixels
00208                        junctionx, junctiony,
00209                        *edges2);
00210   if (!n)
00211     return false;               // empty network
00212   if (junctionp) {
00213     // 3. Sort chains longest first.
00214     if (edges2->size() < 10000)     // don't sort if too many edges
00215     {
00216       sdet_contour_edge* edge_array = new sdet_contour_edge[edges2->size()];
00217       int i =0;
00218       for (vcl_vector<vtol_edge_2d_sptr>::iterator eit = edges2->begin();
00219            eit != edges2->end(); eit++,i++)
00220       {
00221         edge_array[i].set_edge(*eit);
00222       }
00223       vcl_qsort(edge_array, i, sizeof(sdet_contour_edge) ,
00224                 (int (*)(const void *, const void *))&compare);
00225       edges2->clear();
00226       for (int j = 0; j<i; j++)
00227         edges2->push_back(edge_array[j].edge());
00228       delete [] edge_array;
00229     }
00230 
00231     // renumber with order in array
00232     for (unsigned int i= 0; i< edges2->size(); i++)
00233       (*edges2)[i]->set_id(i);
00234 
00235 
00236     // 4. Split/Merge chains from touching end points
00237     vcl_vector<vtol_vertex_2d_sptr > vertices2;
00238 
00239     this->FindJunctions(edgels, // break/merge at junctions of
00240                         *edges2, vertices2); // distinct chains
00241     for (unsigned int i=0; i< vertices2.size(); i++)
00242       vertices->push_back( vertices2[i]);
00243   }
00244 
00245   // 5. Copy back results into global lists
00246   for (unsigned int i= 0; i< edges2->size(); i++)
00247     edges->push_back( (*edges2)[i]);
00248 
00249   //eliminate memory leaks
00250   edges2->clear();
00251   delete edges2;
00252 
00253   return true;
00254 }
00255 
00256 
00257 //: Return TRUE if pixel is a local maximum, and so is right on top of contour.
00258 static bool
00259 on_contour(const gevd_bufferxy& edgels, const int i, const int j)
00260 {
00261   double pix = (1 + vnl_math::sqrt2) * floatPixel(edgels, i, j); // fuzzy threshold
00262   for (vxl_byte dir = 0; dir < TWOPI; dir += HALFPI) // 4-connected only
00263     if (floatPixel(edgels, i+DIS[dir], j+DJS[dir]) > pix)
00264       return false;             // should choose neighbor instead
00265   return true;
00266 }
00267 
00268 
00269 //: Delete pixel from contour, and save its location in xloc/yloc.
00270 static void
00271 RecordPixel(int i, int j, gevd_bufferxy& edgels,
00272             vcl_vector<int>& iloc, vcl_vector<int>& jloc)
00273 {
00274   //JLM
00275   // This doesn't work if we record the same pixel twice
00276   //  floatPixel(edgels, i, j) = -floatPixel(edgels, i, j); // flip sign
00277   //  The following asserts that a pixel has been used and subsequent
00278   //  applications of RecordPixel cannot undo the used state.
00279   floatPixel(edgels, i, j) = -1;
00280   iloc.push_back(i), jloc.push_back(j);
00281 #ifdef DEBUG
00282   if (sdet_contour::debug_)
00283     vcl_cout << "Recording (" << i << ' ' << j << ")\n";
00284 #endif
00285 }
00286 
00287 //:
00288 // Delete the last pixel added to iloc and jloc
00289 //
00290 void
00291 ErasePixel(vcl_vector<int>& xloc, vcl_vector<int>& yloc)
00292 {
00293   vcl_vector<int>::iterator xit = xloc.end();
00294   vcl_vector<int>::iterator yit = yloc.end();
00295   xloc.erase(xit-1);
00296   yloc.erase(yit-1);
00297 }
00298 
00299 
00300 //:
00301 // Find next best pixel on contour, searching for strongest response,
00302 // and favoring 4-connected over 8-connected.
00303 // Return 0, if no pixel is found, or direction in range [2*pi, 4*pi).
00304 static int
00305 NextPixel(int& i, int& j, const gevd_bufferxy& edgels)
00306 {
00307   float maxpix = 0, npix;
00308   int maxdir = 0, dir;
00309   for (dir = 0; dir < TWOPI; dir += HALFPI) // 4-connected first
00310     if ((npix = floatPixel(edgels, i+DIS[dir], j+DJS[dir])) > maxpix)
00311     {
00312       maxpix = npix;
00313       maxdir = dir+TWOPI;
00314     }
00315   if (!maxdir)
00316   {
00317     for (dir = 1; dir < TWOPI; dir += HALFPI) // 8-connected next
00318       if ((npix = floatPixel(edgels, i+DIS[dir], j+DJS[dir])) > maxpix)
00319       {
00320         maxpix = npix;
00321         maxdir = dir+TWOPI;
00322       }
00323   }
00324   if (maxdir)                   // update next strongest pixel
00325     i += DIS[maxdir], j += DJS[maxdir];
00326   return maxdir;
00327 }
00328 
00329 
00330 //:
00331 // Find next best pixel on contour, searching for strongest response,
00332 // and favoring 4-connected over 8-connected.
00333 // Return 0, if no pixel is found, or direction in range [2*pi, 4*pi).
00334 static int
00335 next_pixel(int& i, int& j, const vbl_array_2d<vtol_vertex_2d_sptr>& vertexMap)
00336 {
00337   int maxdir = 0, dir;
00338   for (dir = 0; dir < TWOPI; dir += HALFPI) // 4-connected first
00339     if (vertexMap.get(i+DIS[dir], j+DJS[dir]))
00340     {
00341       maxdir = dir+TWOPI;
00342       break;
00343     }
00344   if (!maxdir)
00345   {
00346     for (dir = 1; dir < TWOPI; dir += HALFPI) // 8-connected next
00347       if (vertexMap.get(i+DIS[dir], j+DJS[dir]))
00348       {
00349         maxdir = dir+TWOPI;
00350         break;
00351       }
00352   }
00353   if (maxdir)                   // update next strongest pixel
00354     i += DIS[maxdir], j += DJS[maxdir];
00355   return maxdir;
00356 }
00357 
00358 
00359 //:
00360 // Trace and collect pixels on thin contours, stronger pixels first,
00361 // and favoring 4-connected over 8-connected. Thinning is not used,
00362 // and so will avoid errors because of square grid tessellation.
00363 // A chain can not cross itself. It can only touch itself or another
00364 // chain, in which case a junction will be found later.
00365 // The pixels of a chain include the 2 end points.
00366 // End points and junctions are created in sdet_contour::FindJunctions.
00367 // Return the number of chains found.  Protected.
00368 int
00369 sdet_contour::FindChains(gevd_bufferxy& edgels, const int njunction,
00370                          const int* junctionx, const int* junctiony,
00371                          vcl_vector<vtol_edge_2d_sptr>& edges)
00372 {
00373   vul_timer t;
00374 
00375   // 1. Save away detected junctions from extending at end points of
00376   // contours, without linking these contours up. This avoids random
00377   // order in the traversal of the contours.
00378   vtol_vertex_2d_sptr mark = new vtol_vertex_2d;      // dummy non zero pointer
00379   for (int k = 0; k < njunction; k++)
00380     vertexMap->put(junctionx[k], junctiony[k], mark);
00381 
00382   // 2. Trace elongated & thinned chains, stronger pixels first.
00383   // Virtual border of image should be inserted last.
00384   const int rmax = FRAME;
00385   const int xmax = edgels.GetSizeX()-rmax-1;
00386   const int ymax = edgels.GetSizeY()-rmax-1;
00387   vcl_vector<int> xloc(xmax+ymax), yloc(xmax+ymax); // work space for
00388 
00389   for (int j = rmax; j <= ymax; j++)
00390     for (int i = rmax; i <= xmax; i++)
00391     {
00392       // 2.0. Start from better pixels above noise+hysteresis
00393       if (floatPixel(edgels, i, j) > minStrength &&
00394           on_contour(edgels, i, j))    // right on the contour
00395       {
00396         int x = i, y = j;
00397 
00398         // 2.1. Prune isolated pixels
00399         if (!NextPixel(x, y, edgels))  // prune isolated pixels
00400         {
00401           floatPixel(edgels, i, j) = 0;
00402           continue;
00403         }
00404         // 2.2. Start collecting first 3 pixels
00405         xloc.clear(), yloc.clear(); // collect pixels on contour
00406         RecordPixel(i, j, edgels, xloc, yloc);  // first pixel
00407         int ii = x, jj = y;
00408         RecordPixel(ii, jj, edgels, xloc, yloc); // second pixel
00409         if (NextPixel(x, y, edgels))
00410           RecordPixel(x, y, edgels, xloc, yloc); // third pixel
00411         else {                  // reach end point
00412           x = i, y = j;         // revert back to start pt
00413           if (NextPixel(x, y, edgels)) { // reverse collection
00414             xloc.clear(), yloc.clear();
00415             RecordPixel(ii, jj, edgels, xloc, yloc); // second pixel
00416             RecordPixel(i, j, edgels, xloc, yloc); // first pixel
00417             RecordPixel(x, y, edgels, xloc, yloc); // third pixel
00418             ii = i, jj = j;
00419           }
00420           else  {             // reach other end point
00421             floatPixel(edgels, i, j) = 0; // prune isolated pixel-pairs
00422             floatPixel(edgels, ii, jj) = 0;
00423             continue;
00424           }
00425         }
00426 
00427         // 2.3. Watch out for zig-zag at 2nd pixel, from LR-TD scans
00428         //Check to see if the contour doubles back on itself
00429         //if so, eliminate the middle pixel causing the jag
00430         int dprod = (x - ii)*(ii - xloc[0]) + (y - jj)*(jj - yloc[0]);
00431         if (dprod < 0)
00432         {
00433           if (sdet_contour::debug_)
00434             vcl_cout << "dps(" << xloc[0] << ' ' << yloc[0] << ")(" << xloc[1]
00435                      << ' ' << yloc[1] << ")(" << x << ' ' << y << ")= "
00436                      << dprod << '\n' << vcl_flush;
00437           //Replace the 2nd pixel in the chain with x-y
00438           ErasePixel(xloc, yloc);
00439           ErasePixel(xloc, yloc);
00440           RecordPixel(x, y, edgels, xloc, yloc);
00441         }
00442         // 2.4. Collect both directions & extension points if 1-chain
00443         // trace along first dir, 4-connected and stronger first
00444         // Scanning forward --->
00445         int niii = ii, njjj = jj;//the i-2 pixel
00446         int nii = x, njj=y;
00447         while (NextPixel(x, y, edgels))
00448         {
00449           //Check to see if the contour doubles back on itself
00450           //if so, eliminate the middle pixel causing the jag
00451           dprod = (x - nii)*(nii - niii) + (y - njj)*(njj - njjj);
00452           if (dprod < 0)
00453           {
00454             if (sdet_contour::debug_)
00455               vcl_cout << "dpf(" << niii << ' ' << njjj << ")(" << nii << ' '
00456                        << njj << ")(" << x << ' ' << y << ")= " << dprod
00457                        << '\n' << vcl_flush;
00458             //Erase the last pixel and replace it with (x, y)
00459             ErasePixel(xloc,  yloc);
00460             RecordPixel(x, y,edgels, xloc, yloc);
00461           }
00462           else
00463             RecordPixel(x, y, edgels, xloc, yloc);
00464           niii = nii; njjj = njj;
00465           nii = x; njj = y;
00466         }
00467         // disjoint first/last pixel
00468         // so must be an open chain with different end points
00469         if (vcl_abs(xloc[0]-x) > 1 ||
00470             vcl_abs(yloc[0]-y) > 1)
00471         {
00472           // include a vertex location if
00473           // there was one detected at the end of the chain
00474           if (next_pixel(x, y, *vertexMap))
00475             xloc.push_back(x), yloc.push_back(y);
00476 
00477           // start again from first pixel
00478 
00479           x = xloc[0], y = yloc[0];
00480 
00481           //reversing the vectors, xloc and yloc
00482           vcl_vector<int> xloctemp( xloc.size()), yloctemp( yloc.size());
00483           for (unsigned int iii=0; iii< xloc.size(); iii++)
00484             xloctemp[iii]= xloc[xloc.size()-1-iii];
00485           for (unsigned int jjj=0; jjj< yloc.size(); jjj++)
00486             yloctemp[jjj]= yloc[yloc.size()-1-jjj];
00487 
00488           //now copy the reversed vector back into xloc, yloc.
00489           for (unsigned int jk=0; jk<xloc.size(); jk++)
00490             xloc[jk]=xloctemp[jk];
00491           for (unsigned int jk=0; jk<yloc.size(); jk++)
00492             yloc[jk]=yloctemp[jk];
00493 
00494           // Scanning backwards from the first point <----
00495           int il = xloc.size();
00496           nii = xloc[il-1];
00497           njj = yloc[il-1];
00498           niii = xloc[il-2];
00499           njjj = yloc[il-2];
00500           while (NextPixel(x, y, edgels)) // trace along other dir
00501           {
00502             //Check to see if the contour doubles back on itself
00503             //if so, eliminate the middle pixel causing the jag
00504             dprod = (x - nii)*(nii - niii)+(y - njj)*(njj - njjj);
00505             if (dprod < 0)
00506             {
00507               if (sdet_contour::debug_)
00508                 vcl_cout << "dpr(" << niii << ' ' << njjj << ")(" << nii << ' '
00509                          << njj << ")(" << x << ' ' << y << ")= " << dprod
00510                          << '\n' << vcl_flush;
00511               //Erase the last pixel and replace it with (x, y)
00512               ErasePixel(xloc, yloc);
00513               RecordPixel(x, y, edgels, xloc, yloc);
00514             }
00515             else
00516               RecordPixel(x, y, edgels, xloc, yloc);
00517             niii = nii; njjj = njj;
00518             nii = x; njj = y;
00519           }
00520           // add in an edgel for the junction at the end
00521           // if it exists.
00522           if (next_pixel(x, y, *vertexMap))
00523             xloc.push_back(x), yloc.push_back(y); // detected junctions
00524         }
00525         int len = xloc.size();
00526         // 2.5. Check for isolated contours that are too short
00527         if (len < minLength) {  // zero or too few internal pixels
00528           for (int k = 0; k < len; k++) // zero or too few internal pixels
00529             floatPixel(edgels, xloc[k], yloc[k]) = 0; // prune short chains
00530           continue;
00531         }
00532 
00533         // 2.6. Create topological network of chains, touching,
00534         //      possibly ending at same junction, but never
00535         //      crossing one another
00536         vtol_edge_2d_sptr edge = new vtol_edge_2d();
00537         vdgl_edgel_chain_sptr ec = new vdgl_edgel_chain;
00538         vdgl_interpolator_sptr it = new vdgl_interpolator_linear(ec);
00539         vdgl_digital_curve_sptr dc = new vdgl_digital_curve(it);
00540 
00541         for (int k=0; k< len; k++)
00542         {
00543           x= xloc[k];
00544           y= yloc[k];
00545           ec->add_edgel( vdgl_edgel( x, y));
00546           edgeMap->put(x, y, edge);
00547         }
00548         edge->set_curve(*dc->cast_to_curve());
00549         LookupTableInsert(edges, edge);
00550       }
00551     }
00552   // 3. Restore cache to original state
00553   //    Placeholder vertices had been added to
00554   //    flag junctions but will be replaced with
00555   //    actual vertices in a later step
00556   for (int k = 0; k < njunction; k++)  // clear all void*/float labels
00557     vertexMap->put(junctionx[k], junctiony[k],NULL);
00558   for (int j = rmax; j <= ymax; j++)
00559     for (int i = rmax; i <= xmax; i++)
00560       if (floatPixel(edgels, i, j) < 0) // undo marks placed by RecordPixel
00561         floatPixel(edgels, i, j) = - floatPixel(edgels, i, j);
00562 
00563   if (talkative_)
00564     vcl_cout << "Find " << edges.size()
00565              << " chains/cycles, with pixels > " << minLength
00566              << " and strength > " << minStrength
00567              << ", in " << t.real() << " msecs.\n";
00568 
00569   return edges.size();  // number of chains found so far
00570 }
00571 
00572 
00573 //:
00574 // The inputs are: endv, edgels, maxSpiral, and edgeMap.
00575 // The outputs are: index, weaker and stronger.
00576 // endv is a vertex corresponding to a dangling end of an edge.
00577 // i.) If the end vertex is bounding more than one edge, the routine
00578 //     returns false.
00579 // ii.) Otherwise a spiral search is carried out around the end with a radius
00580 // given by maxSpiral.  Some of the nearby points on the edge connected to endv,
00581 // are erased so that they are not found in the search.
00582 // iii.) The edgel with maximum strength is found.  If there is none, the
00583 //       routine returns false.
00584 // iv.) The edge containing the found edgel is called "stronger" and the
00585 //      the location on that edge where the edgel was found is "index"
00586 bool sdet_contour:: DetectJunction(vtol_vertex_2d_sptr const& endv, int& index,
00587                                    vtol_edge_2d_sptr& weaker,
00588                                    vtol_edge_2d_sptr& stronger,
00589                                    const int maxSpiral,
00590                                    const gevd_bufferxy& edgels)
00591 {
00592   // 0. Must be an end point of a dangling 1-chain
00593   if (endv->numsup() > 1)         // avoid junction and 1-cycle
00594     return false;
00595   vcl_vector<vtol_edge_sptr> edges; endv->edges(edges);
00596   weaker = edges[0]->cast_to_edge_2d();      // dangling edge must be a weaker contour
00597   vdgl_digital_curve_sptr dc = weaker->curve()->cast_to_vdgl_digital_curve();
00598 
00599   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00600 
00601   // 1. Mark off pixels at end pt to find junction of a contour to itself
00602   //Erase the edge pointers for rfuzz positions in the edgeMap corresponding
00603   //to the last rfuzz edgels in the weaker edge.  It looks like "labels"
00604   //caches the old edge pointers
00605   const int rfuzz = vcl_min(len, 3*MINLENGTH);
00606   vtol_edge_2d_sptr* labels = new vtol_edge_2d_sptr[rfuzz];
00607   //make sure the "end" of the edge corresponds to the correct end of the
00608   //edgel chain, i.e. "endv" might be either v1 or v2 -- the first or last
00609   //point on the edgel chain.
00610   if (endv == weaker->v1()->cast_to_vertex_2d())    //erase the first part
00611     for (int r = 0; r < rfuzz; r++)
00612     {
00613       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel( r);
00614       labels[r] = edgeMap->get( int(edgel.get_x()), int(edgel.get_y()));
00615       edgeMap->put(int(edgel.get_x()), int(edgel.get_y()), NULL);
00616     }
00617   else //erase the last part
00618     for (int r = 0; r < rfuzz; r++)
00619     {
00620       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel(len-1-r);
00621       labels[r] = edgeMap->get( int( edgel.get_x()), int( edgel.get_y()));
00622       edgeMap->put(int(edgel.get_x()), int(edgel.get_y()), NULL);
00623     }
00624 
00625   // 2. Find another stronger contour touched by this end point < gap.
00626   //Sprial around the end location, increasing the radius
00627   //searching for the strongest edge strength on some other chain
00628   //It can't be near the end of the weaker chain nearby since we
00629   //erased the pixels corresponding to it.
00630   stronger = NULL;              // contour can join with itself
00631   int jx = int(endv->x()), jy = int(endv->y());
00632   for (int l = 0, n = 0; l < maxSpiral; l++)    // increasing radius of spiral
00633   {
00634     float maxpix = 0; int maxn = 0;     // strongest strength at this radius
00635     for (; n < RNS[l]; n++)
00636     {
00637       int x = jx+RIS[n], y = jy+RJS[n];
00638       if (edgeMap->get(x, y) && // find another contour or itself
00639           floatPixel(edgels, x, y) > maxpix)
00640       {
00641         maxpix = floatPixel(edgels, x, y);
00642         maxn = n;               // better neighbor
00643       }
00644     }
00645     if (maxpix) {               // location of junction on contour
00646       stronger = edgeMap->get(jx+RIS[maxn], jy+RJS[maxn]);
00647       jx += RIS[maxn], jy += RJS[maxn];
00648       break;
00649     }
00650   }
00651   // restore edgeMap around end point (undo step 1)
00652   if (endv == weaker->v1()->cast_to_vertex_2d())
00653     for (int r=0; r< rfuzz; r++)
00654     {
00655       vdgl_edgel edge= dc->get_interpolator()->get_edgel_chain()->edgel(r);
00656       edgeMap->put(int( edge.get_x()), int( edge.get_y()), labels[r]);
00657     }
00658   else
00659     for (int r=0; r< rfuzz; r++)
00660     {
00661       vdgl_edgel edgel= dc->get_interpolator()->get_edgel_chain()->edgel(len-1-r);
00662       edgeMap->put(int( edgel.get_x()), int( edgel.get_y()),labels[r]);
00663     }
00664   delete [] labels;
00665 
00666   if (!stronger)                // do not find any edge in search region
00667     return false;
00668 
00669   // 3. Find index location of junction on this contour
00670   // We have found an edgel on a "stronger" edge at location maxn on the
00671   // search spiral, i.e. at jx and jy.
00672   index = int(INVALID);
00673   vdgl_digital_curve_sptr dc2 =(stronger->curve()->cast_to_vdgl_digital_curve());
00674   vdgl_edgel_chain_sptr ec = dc2->get_interpolator()->get_edgel_chain();
00675   index = bdgl_curve_algs::closest_point(ec, jx, jy);
00676   //
00677   //If we are within s pixels of either end then don't bother to split the
00678   //edge unless the stronger curve is a cycle without a vertex
00679   //In the case of a cycle there is no real origin to be used as
00680   //a junction later during merging tests
00681   const int s = 3;
00682   if ((index<=s || index+s+1 > (int)ec->size())&&
00683       stronger->v1()&&stronger->v2())
00684     return false;
00685   if (sdet_contour::debug_)
00686     vcl_cout << "Closest index to (" << endv->x() << ' '
00687              << endv->y() << ") is " << index
00688              << " corresponding to " << ec->edgel(index)
00689              << "size = " << ec->size() << vcl_endl;
00690 
00691   return true;
00692 }
00693 
00694 //: Move a junction to lie on the intersecting digital curve
00695 //  Refine the intersection position to double precision
00696 bool sdet_contour::move_junction(vtol_vertex_2d_sptr const& junction,
00697                                  int& index,
00698                                  vdgl_digital_curve_sptr const & dc)
00699 {
00700   if (!junction)
00701     return false;
00702   int jx = int(junction->x()), jy = int(junction->y());
00703   vertexMap->put(jx, jy, NULL); // erase old location
00704   //get new location
00705   vdgl_edgel_chain_sptr ec = dc->get_interpolator()->get_edgel_chain();
00706   jx = int((*ec)[index].x());
00707   jy = int((*ec)[index].y());
00708   //move the junction
00709   junction->set_x(jx), junction->set_y(jy); // update new vertex location
00710 
00711   //fill out the arrays
00712   vertexMap->put(jx, jy, junction);
00713   edgeMap->put(jx, jy, NULL);
00714   return true;
00715 }
00716 
00717 //:
00718 // when a vertex position is moved, an edge's edgel chain must potentially
00719 // be replaced.  old_x and old_y is the original end position.
00720 // It is possible that the vertex can move up to 4 pixels.  The new
00721 // position is the location of v.
00722 // Edgels are added from the end of the old digital_curve to the new
00723 // vertex position.
00724 void sdet_contour::update_edgel_chain(vtol_edge_2d_sptr const& edge,
00725                                       const int old_x, const int old_y,
00726                                       vtol_vertex_2d_sptr& v)
00727 {
00728   if (!edge||!v)
00729   {
00730     vcl_cout << "In update_edgel_chain - null inputs\n";
00731     return;
00732   }
00733   //The new vertex position
00734   double x = v->x(), y = v->y();
00735   //Access the old edgel chain
00736   vdgl_digital_curve_sptr dc_old= edge->curve()->cast_to_vdgl_digital_curve();
00737   vdgl_edgel_chain_sptr ec_old= dc_old->get_interpolator()->get_edgel_chain();
00738   int N = ec_old->size();
00739   //Create a new digital curve
00740   vdgl_edgel_chain_sptr  ec = new vdgl_edgel_chain;
00741   vdgl_interpolator_sptr it = new vdgl_interpolator_linear(ec);
00742   vsol_curve_2d_sptr dc = new vdgl_digital_curve(it);
00743 
00744   // Cases
00745   // A. The edge is a cycle
00746   if (edge->v1()==edge->v2())
00747   {
00748     vcl_cout << "Cycle case not implemented\n";
00749     return;
00750   }
00751   // B. The edge is open
00752   // Determine which end of the digital curve is closest to v
00753   //  int end_index = bdgl_curve_algs::closest_end(ec_old, x, y);
00754   //  if (end_index == 0)
00755   if (v==edge->v1()->cast_to_vertex_2d())
00756   {    //index = 0
00757     //vcl_cout << "update at v1\n";
00758     //start the chain with the new vertex location
00759     vdgl_edgel ed(x, y, bdgl_curve_algs::synthetic);//mark as synthetic edgel
00760 
00761     ec->add_edgel(ed);
00762 
00763     //add in a linear segment to reach old location
00764     int npix =
00765       bdgl_curve_algs::add_straight_edgels(ec, old_x, old_y,
00766                                            sdet_contour::debug_);
00767 
00768     if (!npix)
00769       return;//nothing was needed
00770 
00771     //mark the edge map at the new edgel locations
00772     for (int i=1; i<npix; i++)
00773       edgeMap->put(int((*ec)[i].x()),int((*ec)[i].y()),edge);
00774 
00775     //fill out the rest of the edgel chain
00776     for (int index = 0; index<N; index++)
00777       ec->add_edgel((*ec_old)[index]);
00778 
00779     //replace the curve on the edge
00780     edge->set_curve(*dc);
00781     return;
00782   }
00783 
00784   if (v==edge->v2()->cast_to_vertex_2d())
00785   {
00786     //vcl_cout << "update at v2\n";
00787     //copy the chain
00788     for (int index = 0; index<N; index++)
00789       ec->add_edgel((*ec_old)[index]);
00790 
00791     //Add in a linear segment to reach to new vertex location
00792     //from old vertex location, (*ec_old)[N-1].
00793     int npix =
00794       bdgl_curve_algs::add_straight_edgels(ec, x, y,
00795                                            sdet_contour::debug_);
00796 
00797     if (!npix)
00798       return;//nothing was needed
00799     //mark the edge map at the new locations
00800     for (int i=N; i<N+npix; i++)
00801       edgeMap->put(int((*ec)[i].x()),int((*ec)[i].y()),edge);
00802 
00803     //replace the curve on the edge
00804     edge->set_curve(*dc);
00805     return;
00806   }
00807 }
00808 
00809 void fill_cycle_gap(vdgl_edgel_chain_sptr const & ec)
00810 {
00811   if (!ec)
00812     return;
00813   int x0 = int((*ec)[0].x()), y0 = int((*ec)[0].y());
00814   bdgl_curve_algs::add_straight_edgels(ec, x0, y0,
00815                                        sdet_contour::debug_);
00816 }
00817 
00818 #if 0 // unused local function
00819 static bool
00820 ConfirmJunctionOnCycle(int index, float threshold,
00821                        vtol_edge_2d& cycle, const gevd_bufferxy& edgels)
00822 {
00823   if (sdet_contour::debug_)
00824     vcl_cerr << "ConfirmJunctionOnCycle() not run: returning 'TRUE'\n";
00825 
00826 #if 1 // JLM
00827   return true;
00828 #else
00829   vdgl_digital_curve_sptr dc = cycle.curve()->cast_to_vdgl_digital_curve();
00830   const int len = dc->get_interpolator()->get_edgel_chain()->size();
00831   const int wrap = 10*len;      // for positive index
00832   const int radius = 3;         // gap < 3, around junction pixel
00833 
00834   for (int n = index-radius; n <= index+radius; n++)
00835   {
00836     int nm = (n-1+wrap)%len;    // modulo operations to wrap at borders
00837     int np = (n+1+wrap)%len;
00838 
00839     vdgl_edgel edgel_m= dc->get_interpolator()->get_edgel_chain()->edgel( nm);
00840     vdgl_edgel edgel_p= dc->get_interpolator()->get_edgel_chain()->edgel( np);
00841 
00842     if (vcl_fabs(floatPixel(edgels, int( edgel_p.x()), int( edgel_p.y())) -
00843                  floatPixel(edgels, int( edgel_m.x()), int( edgel_m.y())))
00844         > threshold)
00845       return true;
00846   }
00847   return false;
00848 #endif // 1
00849 }
00850 #endif // 0
00851 
00852 //:
00853 // Break the cycle at given index, and create new cycle from/to
00854 // and not including index pixel.
00855 //
00856 //           stronger (no vertex initially)
00857 //         ------------
00858 //         |          |
00859 //         |          |
00860 // 0-------0 junction |
00861 //         |          |
00862 //         |          |
00863 //         ------------
00864 //             split -  the new edge
00865 //
00866 void sdet_contour::BreakCycle(vtol_vertex_2d_sptr const& junction,
00867                               int& index, vtol_edge_2d_sptr const& stronger,
00868                               vtol_edge_2d_sptr & split)
00869 {
00870   //Get the old curve
00871   vdgl_digital_curve_sptr old_dc =
00872     (stronger->curve()->cast_to_vdgl_digital_curve());
00873   vdgl_edgel_chain_sptr old_ec = old_dc->get_interpolator()->get_edgel_chain();
00874   const int N = old_ec->size();
00875 
00876   // 1. Move location of junction
00877   move_junction(junction, index, old_dc);
00878 
00879   // 2. Create a new edge (a cycle)
00880   split = new vtol_edge_2d();
00881 
00882   //  The new curve
00883   vdgl_edgel_chain* es = new vdgl_edgel_chain;
00884   vdgl_interpolator* it =
00885     new vdgl_interpolator_linear(vdgl_edgel_chain_sptr(es));
00886   vdgl_digital_curve *ds =
00887     new vdgl_digital_curve( vdgl_interpolator_sptr( it));
00888   split->set_curve(*ds);
00889 
00890   //insert edgels from index to N-1
00891   //starting at v1()
00892   for (int k = index; k <N; k++)
00893   {
00894     es->add_edgel((*old_ec)[k]);
00895     int x = int((*old_ec)[k].x()), y = int((*old_ec)[k].y());
00896     edgeMap->put(x, y, split);
00897     if (sdet_contour::debug_)
00898       vcl_cout << "BreakCycle: edge1 edgel at (" << x << ' ' << y << ")\n";
00899   }
00900   //insert edgels from 0 to and including index
00901   for (int k = 0; k <= index; k++)
00902   {
00903     es->add_edgel((*old_ec)[k]);
00904     int x = int((*old_ec)[k].x()), y = int((*old_ec)[k].y());
00905     edgeMap->put(x, y, split);
00906     if (sdet_contour::debug_)
00907       vcl_cout << "BreakCycle: edge1 edgel at (" << x << ' ' << y << ")\n";
00908   }
00909 
00910   split->set_v1(junction->cast_to_vertex());
00911   split->set_v2(junction->cast_to_vertex());
00912   int x = int(junction->x());
00913   int y = int(junction->y());
00914   vertexMap->put(x, y, junction);
00915   //stronger is no longer of interest to v1 and v2.
00916   //  btol_edge_algs::unlink_all_inferiors_twoway(stronger); done in lookup tab
00917 }
00918 
00919 #if 0 // unused local function
00920 //: Confirm there is a strong jump in response near a junction.
00921 // The location of this jump is however inaccurate, and so junctions
00922 // can not be localized accurately along the stronger chain.
00923 static bool
00924 ConfirmJunctionOnChain(int index, float threshold,
00925                        vtol_edge_2d& chain, const gevd_bufferxy& edgels)
00926 {
00927   if (sdet_contour::debug_)
00928     vcl_cerr << "ConfirmJunctionOnChain() not run: returning 'TRUE'\n";
00929 
00930 #if 1 // JLM
00931   return true;
00932 #else
00933   vdgl_digital_curve_sptr dc = chain.curve()->cast_to_vdgl_digital_curve();
00934   const int len = dc->get_interpolator()->get_edgel_chain()->size()-1;
00935 
00936   if (len < 2*MINLENGTH-1) // will merge vertices instead of
00937     return false;          // breaking up chains
00938 
00939   const int fuzz = MINLENGTH-1; // from min length of broken chains
00940   const int radius = 3;         // gap < 3, around junction pixel
00941   //search a neighborhood around index on the input chain
00942   //for long chains this amounts to index+-radius
00943   for (int n = vcl_max(index-radius, fuzz); n <= vcl_min(index+radius,len-1-fuzz); n++)
00944   {
00945     //for each point in the neighborhood of index
00946     //get edgels on each side
00947     vdgl_edgel cp1= dc->get_interpolator()->get_edgel_chain()->edgel(n+1);
00948     vdgl_edgel cm1= dc->get_interpolator()->get_edgel_chain()->edgel(n-1);
00949     //if there is a location where the difference is above
00950     // a threshold then the junction can be formed.
00951     if (vcl_fabs(floatPixel(edgels, int(cp1.x()), int(cp1.y())) -
00952                  floatPixel(edgels, int(cm1.x()), int(cm1.y())))
00953         > threshold)
00954     {
00955       return true;
00956     }
00957   }
00958   return false;
00959 #endif // 1
00960 }
00961 #endif // 0
00962 
00963 vtol_vertex_2d_sptr get_vertex_at_index(vtol_edge_2d_sptr& e, int index)
00964 {
00965   vdgl_digital_curve_sptr dc = e->curve()->cast_to_vdgl_digital_curve();
00966   if (!dc)
00967     return 0;
00968   vdgl_edgel ed = dc->get_interpolator()->get_edgel_chain()->edgel( index);
00969   vtol_vertex_2d_sptr v = new vtol_vertex_2d(ed.x(), ed.y());
00970   return v;
00971 }
00972 
00973 bool find_vertex(vtol_vertex_2d_sptr& v,
00974                  vcl_vector<vtol_vertex_2d_sptr>& vertices)
00975 {
00976   for (vcl_vector<vtol_vertex_2d_sptr>::iterator vit = vertices.begin();
00977        vit != vertices.end(); vit++)
00978     if ((*vit)==v)
00979       return true;
00980   return false;
00981 }
00982 
00983 bool find_edge(vtol_edge_2d_sptr& e,
00984                vcl_vector<vtol_edge_2d_sptr>& edges)
00985 {
00986   for (vcl_vector<vtol_edge_2d_sptr>::iterator eit = edges.begin();
00987        eit != edges.end(); eit++)
00988     if ((*eit)==e)
00989       return true;
00990   return false;
00991 }
00992 
00993 void print_edge_lookup_table(vcl_vector<vtol_edge_2d_sptr>& edges)
00994 {
00995   int ei=0;
00996   for (vcl_vector<vtol_edge_2d_sptr>::iterator eit = edges.begin();
00997        eit != edges.end(); eit++, ei++)
00998   {
00999     if (!*eit)
01000     {
01001       vcl_cout << "edge[" << ei << "]= null\n";
01002       continue;
01003     }
01004     vcl_cout<< "edge["<< ei << "]= " << **eit
01005             << *((*eit)->v1()->cast_to_vertex_2d())
01006             << ' ' << *((*eit)->v2()->cast_to_vertex_2d()) << '\n';
01007   }
01008 }
01009 
01010 //: Break the edge at given index, and create two subchains from it.
01011 //
01012 //               junction
01013 //  0---------------o----------------------0
01014 //                index                  nedgels-1
01015 //       edge1                edge2
01016 //
01017 //
01018 void sdet_contour::BreakChain(vtol_vertex_2d_sptr const& junction,
01019                               int& index,
01020                               vtol_edge_2d_sptr const& stronger,
01021                               vtol_edge_2d_sptr& longer,
01022                               vtol_edge_2d_sptr& shorter)
01023 {
01024   vdgl_digital_curve_sptr dc = stronger->curve()->cast_to_vdgl_digital_curve();
01025 
01026   const int N = dc->get_interpolator()->get_edgel_chain()->size();
01027 
01028   // 1. Move the location of junction to lie on stronger's digital_curve
01029   move_junction(junction, index, dc);
01030 
01031   // 2. Create first subchain up to and including junction pixel.
01032   vtol_edge_2d_sptr edge1 = new vtol_edge_2d();
01033   vdgl_edgel_chain *ec= new vdgl_edgel_chain;
01034   vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
01035   vdgl_digital_curve *dc1 = new vdgl_digital_curve( it);
01036   edge1->set_curve(*dc1);
01037 
01038   //insert the edgels in first subchain, edge1
01039   // include index in edge 1
01040   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01041   vdgl_edgel_chain *cxy1= ec;
01042   for (int k = 0; k <= index; k++)
01043   {
01044     cxy1->add_edgel( (*cxy)[k] );
01045     (*cxy1)[k] = (*cxy)[k];
01046     int x = int((*cxy1)[k].x()), y = int((*cxy1)[k].y());
01047     edgeMap->put(x , y,  edge1);
01048     if (sdet_contour::debug_)
01049       vcl_cout << "BreakChain: edge1 edgel at (" << x << ' ' << y << ")\n";
01050   }
01051 
01052   //set the vertices of edge 1
01053   // have to get rid of stronger from the list of superiors of v1
01054   edge1->set_v1(stronger->v1()->cast_to_vertex());
01055 
01056   // junction keeps its original superiors, + edge1
01057   edge1->set_v2(junction->cast_to_vertex());
01058 
01059   //mark vertex and edge arrays for edge 1
01060   int x11 = int(edge1->v1()->cast_to_vertex_2d()->x());
01061   int y11 = int(edge1->v1()->cast_to_vertex_2d()->y());
01062   int x12 = int(edge1->v2()->cast_to_vertex_2d()->x());
01063   int y12 = int(edge1->v2()->cast_to_vertex_2d()->y());
01064   edgeMap->put(x11, y11, NULL);
01065   edgeMap->put(x12, y12, NULL);
01066   vertexMap->put(x11, y11, edge1->v1()->cast_to_vertex_2d());
01067   vertexMap->put(x12, y12, edge1->v2()->cast_to_vertex_2d());
01068 
01069   // 3. Create second subchain from and including junction pixel.
01070   vtol_edge_2d_sptr edge2 = new vtol_edge_2d();    // create second subchain
01071   vdgl_edgel_chain *ec2= new vdgl_edgel_chain;
01072   vdgl_interpolator *it2= new vdgl_interpolator_linear( ec2);
01073   vdgl_digital_curve *dc2= new vdgl_digital_curve( it2);
01074   edge2->set_curve(*dc2);
01075 
01076   //insert the edgels into edge2
01077   //start at index since that edgel will be v1 of edge2
01078   vdgl_edgel_chain *cxy2= ec2;
01079   for (int k = index; k < N; k++)
01080   {
01081     cxy2->add_edgel((*cxy)[k]);
01082     int x = int((*cxy)[k].x()), y = int((*cxy)[k].y());
01083     edgeMap->put( x, y, edge2);
01084     if (sdet_contour::debug_)
01085       vcl_cout << "BreakChain: edge2 edgel at (" << x << ' ' << y << ")\n";
01086   }
01087 
01088   // have to remove stronger from superiors of v2
01089   edge2->set_v2(stronger->v2()->cast_to_vertex());
01090   // junction keeps the original superiors + edge2
01091   edge2->set_v1(junction->cast_to_vertex());
01092 
01093   //mark vertex and edge arrays for edge 2
01094   int x21 = int(edge2->v1()->cast_to_vertex_2d()->x());
01095   int y21 = int(edge2->v1()->cast_to_vertex_2d()->y());
01096   int x22 = int(edge2->v2()->cast_to_vertex_2d()->x());
01097   int y22 = int(edge2->v2()->cast_to_vertex_2d()->y());
01098   edgeMap->put(x21, y21, NULL);
01099   edgeMap->put(x22, y22, NULL);
01100   vertexMap->put(x21, y21, edge2->v1()->cast_to_vertex_2d());
01101   vertexMap->put(x22, y22, edge2->v2()->cast_to_vertex_2d());
01102 
01103   //Here is where we get rid of the stale superior (stronger) of v1 and v2
01104 
01105   //  btol_edge_algs::unlink_all_inferiors_twoway(stronger);
01106   // done in lookup table
01107   if (cxy1->size() >= cxy2->size())  // sort longer/shorter chains
01108     longer = edge1, shorter = edge2;
01109   else
01110     longer = edge2, shorter = edge1;
01111 }
01112 
01113 
01114 //: Break the chain at given index, and create a loop.
01115 // This case occurs when the junction is caused by a chain touching itself
01116 //
01117 //      straight     junction
01118 //  0-------------------0--------
01119 //                      |       |  curled
01120 //                      |       |
01121 //                      ---------
01122 void
01123 sdet_contour::LoopChain(vtol_vertex_2d_sptr const& junction, int& index,
01124                         vtol_edge_2d_sptr const& chain,
01125                         vtol_edge_2d_sptr& straight,
01126                         vtol_edge_2d_sptr& curled)
01127 {
01128   vdgl_digital_curve_sptr dc = chain->curve()->cast_to_vdgl_digital_curve();
01129   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01130   const int N = cxy->size();
01131 
01132   // 1. Move location of junction
01133   int old_x = int(junction->x()), old_y = int(junction->y());
01134   move_junction(junction, index, dc);
01135 
01136   // 2. Find straight/curled chains
01137   straight = new vtol_edge_2d(), curled = new vtol_edge_2d();
01138 
01139   //
01140   // The touching end of chain is v1 so
01141   // first subchain is curled and forms a cycle
01142   //         v1 index    v2
01143   //   -------0----------0
01144   //  |       x
01145   //  |       | potential gap
01146   //   --------
01147   //    curled
01148   if (junction == chain->v1()->cast_to_vertex_2d())
01149   {
01150     vdgl_edgel_chain *ec= new vdgl_edgel_chain;
01151     vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
01152     vdgl_digital_curve *c= new vdgl_digital_curve( it);
01153     curled->set_curve(*c);
01154     vdgl_edgel_chain *xy= ec;
01155 
01156     //fill in potential gap starting at index
01157     if (!(int(junction->x())==old_x&&int(junction->y())==old_y))
01158     {
01159       //add an edgel at index
01160       ec->add_edgel(vdgl_edgel((*cxy)[index].x(), (*cxy)[index].y()));
01161 
01162       //add in a linear segment to reach old v1's position
01163       int npix =
01164         bdgl_curve_algs::add_straight_edgels(ec, old_x, old_y,
01165                                              sdet_contour::debug_);
01166 
01167       //mark the edge map at the new edgel locations
01168       for (int i=1; i<npix; i++)
01169         edgeMap->put(int((*xy)[i].x()),int((*xy)[i].y()),curled);
01170     }
01171     //include index as other endpoint of curled
01172     for (int k = 0; k <= index; k++)
01173     {
01174       xy->add_edgel( (*cxy)[k] );
01175       edgeMap->put( int((*cxy)[k].x()), int((*cxy)[k].y()), curled);
01176     }
01177     //same v1 and v2 since curled is a cycle
01178     curled->set_v1(junction->cast_to_vertex());
01179     curled->set_v2(junction->cast_to_vertex());
01180     int x = int(junction->x()), y = int(junction->y());
01181     edgeMap->put(x, y, NULL);
01182     vertexMap->put(x, y, junction);
01183     //
01184     //define the straight section (ends in v2)
01185     //
01186     ec= new vdgl_edgel_chain;
01187     it= new vdgl_interpolator_linear( ec);
01188     c = new vdgl_digital_curve( it);    // second subchain is straight
01189     straight->set_curve(*c);
01190     xy= ec;
01191     //start at index
01192     for (int k = index; k < N; k++)
01193     {
01194       xy->add_edgel( (*cxy)[k] );
01195       edgeMap->put( int((*cxy)[k].x()), int((*cxy)[k].y()), straight);
01196     }
01197     //set vertices of new straight section
01198     straight->set_v1(junction->cast_to_vertex());
01199     straight->set_v2(chain->v2());
01200 
01201     //fill out maps
01202     x = int(straight->v2()->cast_to_vertex_2d()->x());
01203     y = int(straight->v2()->cast_to_vertex_2d()->y());
01204     edgeMap->put(x, y, NULL);
01205     vertexMap->put(x, y, straight->v2()->cast_to_vertex_2d());
01206   }
01207   else // The first subchain is straight, second is curled
01208   {
01209     //
01210     // v1         v2 index
01211     //  0-----------0--------
01212     //   potential  x        |
01213     //      gap     |        |
01214     //               --------
01215     //                 curled
01216     vdgl_edgel_chain *ec= new vdgl_edgel_chain;
01217     vdgl_interpolator *it= new vdgl_interpolator_linear( ec);
01218     vdgl_digital_curve *c= new vdgl_digital_curve( it);
01219     straight->set_curve(*c);
01220     vdgl_edgel_chain *xy= ec;
01221     //include index in the straight section
01222     for (int k = 0; k <=index; k++)
01223     {
01224       xy->add_edgel( (*cxy)[k] );
01225       edgeMap->put( int((*cxy)[k].x()), int((*cxy)[k].y()), straight);
01226     }
01227 
01228     //set the vertices of straight
01229     straight->set_v1(chain->v1());
01230     straight->set_v2(junction->cast_to_vertex());
01231 
01232     //fill out the maps
01233     int x = int(straight->v1()->cast_to_vertex_2d()->x());
01234     int y = int(straight->v1()->cast_to_vertex_2d()->y());
01235     edgeMap->put(x, y, NULL);
01236     vertexMap->put(x, y, straight->v1()->cast_to_vertex_2d());
01237 
01238     //construct the curled section
01239     ec= new vdgl_edgel_chain;
01240     it= new vdgl_interpolator_linear( ec);
01241     c = new vdgl_digital_curve( it);    // second subchain is curled
01242     curled->set_curve(*c);
01243     xy = ec;
01244 
01245     int nc = 0;//length of curled section up to gap
01246     // start at index
01247     for (int k = index; k < N; k++, nc++)
01248     {
01249       xy->add_edgel( (*cxy)[k] );
01250       edgeMap->put( int((*cxy)[k].x()), int((*cxy)[k].y()), curled);
01251     }
01252 
01253     //fill in potential gap starting at v2
01254     if (!(int(junction->x())==old_x&&int(junction->y())==old_y))
01255     {
01256       //add in a linear segment to reach new junction position
01257       int new_x = int(junction->x()), new_y = int(junction->y());
01258       int npix =
01259         bdgl_curve_algs::add_straight_edgels(ec, new_x, new_y,
01260                                              sdet_contour::debug_);
01261       //mark the edge map at the new edgel locations
01262       for (int i=0; i<npix; i++)
01263         edgeMap->put(int((*xy)[nc+i].x()),int((*xy)[nc+i].y()),curled);
01264     }
01265 
01266     // set the vertices of curled
01267     curled->set_v1(junction->cast_to_vertex());
01268     curled->set_v2(junction->cast_to_vertex());
01269 
01270     //fill out the maps
01271     x = int(junction->x());
01272     y = int(junction->y());
01273     edgeMap->put(x, y, NULL);
01274     vertexMap->put(x, y, junction);
01275   }
01276   //done in lookup table
01277   //btol_edge_algs::unlink_all_inferiors_twoway(chain);
01278 }
01279 
01280 //: Determine if a vertex is in the border strip.
01281 //  The strip supports the edge detection kernel
01282 //
01283 bool sdet_contour::near_border(vtol_vertex_2d_sptr const&  v)
01284 {
01285   const int xmin = FRAME;
01286   const int ymin = FRAME;
01287   const int xmax = vertexMap->rows()-FRAME-1;
01288   const int ymax = vertexMap->columns()-FRAME-1;
01289   int x = int(v->x()), y = int(v->y());
01290   return x<=xmin||x>=xmax||y<=ymin||y>=ymax;
01291 }
01292 
01293 //:
01294 // Detect touching another junction or end point,
01295 // from an end point of a dangling chain by
01296 // searching in a spiral pattern.
01297 // Find the neighboring vertex with the largest number of
01298 // incident edges.
01299 //
01300 vtol_vertex_2d_sptr
01301 sdet_contour::DetectTouch(vtol_vertex_2d_sptr const& endv,
01302                           const int maxSpiral)
01303 {
01304   const int jx = int(endv->x()), jy = int(endv->y());
01305   for (int l = 0, n = 0; l < maxSpiral; l++)    // increasing radius of spiral
01306   {
01307     int bx=0, by=0;
01308     vtol_vertex_2d_sptr  best_neighbor = NULL;  // prefer junction over endpt
01309     int max_edges = 0;             // max number of edges
01310     for (; n < RNS[l]; n++)    // 4- then 8-connected
01311     {
01312       int x = jx+RIS[n], y = jy+RJS[n];
01313       vtol_vertex_2d_sptr nbr = vertexMap->get(x, y);
01314       int nedges = 0;
01315       if (nbr)
01316         nedges = nbr->numsup();
01317       if (nedges > max_edges)
01318       {
01319         max_edges = nedges;    // number of edges connected to it
01320         best_neighbor = nbr;   // better neighbor
01321         bx = x; by = y; //best detected location, for debug purposes
01322       }
01323     }
01324     if (sdet_contour::debug_)
01325       vcl_cout << "(bx,by) = (" << bx << ' ' << by << ")\n";
01326     if (max_edges)
01327       return best_neighbor;
01328   }
01329   return NULL;
01330 }
01331 
01332 //: If there is only one edge connected to v then return it, otherwise return null
01333 //
01334 vtol_edge_2d_sptr
01335 DanglingEdge(vtol_vertex_2d_sptr const& v)
01336 {
01337   vcl_vector<vtol_edge_sptr> segs; v->edges(segs);
01338   if (segs.size()==1)
01339     return segs[0]->cast_to_edge_2d();
01340   else
01341     return 0;
01342 }
01343 
01344 
01345 //: Merge 2 end points of a same chain creating a cycle
01346 //
01347 //       endpt
01348 //         o------------------
01349 //         o----             |
01350 //      other   \            |
01351 //               ------------
01352 //
01353 //  The removed vertex is the either endpt or other, depending
01354 //  on the direction of the edge
01355 //
01356 bool
01357 sdet_contour::MergeEndPtsOfChain(vtol_vertex_2d_sptr const& endpt,
01358                                  vtol_vertex_2d_sptr const& other,
01359                                  vtol_vertex_2d_sptr& removed_vert)
01360 {
01361   if (sdet_contour::debug_)
01362     vcl_cout << " Merging end points of same edge " << *endpt << ' '
01363              << *other << '\n';
01364 
01365   vcl_vector<vtol_edge_sptr> edges; endpt->edges(edges);
01366   // dangling edge terminating at endpt
01367   vtol_edge_2d_sptr edge = edges[0]->cast_to_edge_2d();
01368   vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01369   vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01370   int N = cxy->size();
01371 
01372   //replace the old edge
01373   //
01374   // need to consider 2 combinations
01375   //          endpt               other         removed_vert
01376   //             0-------------------0
01377   //  case a     v2                 v1             endpt
01378   //  case b     v1                 v2             other
01379 
01380   while (true)
01381   {
01382     //case self_a: v2 corresponds to endpt
01383     //add edgels to reach other
01384     if (edge->v2()->cast_to_vertex_2d() == endpt)
01385     {
01386       //Fill in edges across the gap endpt->other
01387       double xe = other->x();
01388       double ye = other->y();
01389       int nedgls =
01390         bdgl_curve_algs::add_straight_edgels(cxy, xe, ye,
01391                                              sdet_contour::debug_);
01392       for (int i = N; i<N+nedgls; i++)
01393         edgeMap->put( int((*cxy)[i].x()), int((*cxy)[i].y()), edge);
01394       edge->set_v2(other->cast_to_vertex());
01395       removed_vert = endpt;
01396       vertexMap->put(int(endpt->x()), int(endpt->y()), NULL);
01397       break;
01398     }
01399 
01400     //case jb, v1 corresponds to endpt
01401     if (edge->v1()->cast_to_vertex_2d() == endpt)
01402     {
01403       //Fill in edges across the gap other->endpt
01404       double xe = endpt->x();
01405       double ye = endpt->y();
01406       int nedgls =
01407         bdgl_curve_algs::add_straight_edgels(cxy, xe, ye,
01408                                              sdet_contour::debug_);
01409       for (int i = N; i<N+nedgls; i++)
01410         edgeMap->put( int((*cxy)[i].x()), int((*cxy)[i].y()), edge);
01411       edge->set_v2(endpt->cast_to_vertex());
01412       removed_vert = other;
01413       vertexMap->put(int(other->x()), int(other->y()), NULL);
01414       break;
01415     }
01416   }
01417   return true;
01418 }
01419 
01420 
01421 //:
01422 //  Merge two different chains by inserting a link from end1 to end2
01423 //  end1 and end2 are touching according to the predicate DetectTouch
01424 //  end1 and end2 are each incident to a single, different edge
01425 //
01426 //        shorter   gap         longer
01427 //    0-------------o  o----------------------0
01428 //              end1    end2
01429 //
01430 void
01431 sdet_contour::MergeEndPtTouchingEndPt(vtol_vertex_2d_sptr const& end1,
01432                                       vtol_vertex_2d_sptr const& end2,
01433                                       vtol_edge_2d_sptr& merge,
01434                                       vtol_edge_2d_sptr& longer,
01435                                       vtol_edge_2d_sptr& shorter)
01436 {
01437   // 1. Retrieve the dangling edges/chains
01438 
01439   // The single edge connected to end1
01440   vcl_vector<vtol_edge_sptr> edges; end1->edges(edges);
01441   vtol_edge_2d_sptr edge1 = edges[0]->cast_to_edge_2d();
01442 
01443   // The single edge connected to end2
01444   end2->edges(edges);
01445   vtol_edge_2d_sptr edge2 = edges[0]->cast_to_edge_2d();
01446 
01447   // 2. Create merged edge/chain
01448   vdgl_digital_curve_sptr dc1 = edge1->curve()->cast_to_vdgl_digital_curve();
01449   const int l1 = dc1->get_interpolator()->get_edgel_chain()->size();
01450   vdgl_digital_curve_sptr dc2 = edge2->curve()->cast_to_vdgl_digital_curve();
01451   const int l2 = dc2->get_interpolator()->get_edgel_chain()->size();
01452 
01453   //edgel chain for edge1
01454   vdgl_edgel_chain_sptr cxy1= dc1->get_interpolator()->get_edgel_chain();
01455 
01456   //edgel chain for edge2
01457   vdgl_edgel_chain_sptr cxy2= dc2->get_interpolator()->get_edgel_chain();
01458 
01459   //The new edge
01460   merge = new vtol_edge_2d();
01461   vdgl_edgel_chain *ec = new vdgl_edgel_chain;
01462   vdgl_interpolator *it = new vdgl_interpolator_linear( ec);
01463   vdgl_digital_curve *dc = new vdgl_digital_curve(it);
01464 
01465   merge->set_curve(*dc);
01466   //
01467   // need to consider 4 combinations
01468   //                   dc1         end1  end2     dc2
01469   //             0-------------------0    0----------------------0
01470   //  case a     v1                 v2    -                      -
01471   //  case b     v2                 v1    -                      -
01472   //  case c     -                  -     v1                    v2
01473   //  case d     -                  -     v2                    v1
01474   //
01475   //debug
01476 
01477   if (sdet_contour::debug_)
01478   {
01479     vcl_cout << "end1 " << *end1 << '\n'
01480              << "end2 " << *end2 << '\n'
01481              << "edge1-v1 " << *(edge1->v1())
01482              << "  edge1-v2 " << *(edge1->v2()) << '\n'
01483              << "edge2-v1 " << *(edge2->v1())
01484              << "  edge2-v2 " << *(edge2->v2()) << '\n'
01485              << " (*cxy1)[0] ="<< (*cxy1)[0] << '\n'
01486              << " (*cxy1)[l1-1] =" <<(*cxy1)[l1-1] << '\n'
01487 
01488              << " (*cxy2)[0] = "<<(*cxy2)[0] << '\n'
01489              << " (*cxy1)[l2-1] =" << (*cxy2)[l2-1] << '\n';
01490   }
01491 
01492   vdgl_edgel_chain *cxy= ec;    // new edgel chain
01493 
01494   //Fill in edgels up to end1
01495   while (true)
01496   {
01497     //case a: v2 corresponds to end1
01498     if (edge1->v2()->cast_to_vertex_2d() == end1)
01499     {
01500       if (sdet_contour::debug_)
01501         vcl_cout << "Case a\n";
01502       for (int i = 0; i < l1; i++)
01503       {
01504         cxy->add_edgel( (*cxy1)[i] );
01505         if (sdet_contour::debug_)
01506           vcl_cout << "merge edgel " << (*cxy1)[i] << '\n';
01507       }
01508       merge->set_v1(edge1->v1());
01509       break;
01510     }
01511     //case b, v1 corresponds to end1, reverse the original edge1 chain
01512     if (edge1->v1()->cast_to_vertex_2d() == end1)
01513     {
01514       if (sdet_contour::debug_)
01515         vcl_cout << "Case b\n";
01516       for (int i = l1-1; i >= 0; --i)
01517       {
01518         cxy->add_edgel((*cxy1)[i]);
01519         if (sdet_contour::debug_)
01520           vcl_cout << "merge edgel " << (*cxy1)[i] << '\n';
01521       }
01522       merge->set_v1(edge1->v2());
01523       break;
01524     }
01525   }
01526   //
01527   //Fill in edges across the gap end1->end2
01528   double xe = end2->cast_to_vertex_2d()->x();
01529   double ye = end2->cast_to_vertex_2d()->y();
01530   bdgl_curve_algs::add_straight_edgels(cxy, xe, ye,
01531                                        sdet_contour::debug_);
01532   //note that end1 is now accounted for in the merged chain
01533 
01534   while (true)
01535   {
01536     //case c: v1 corresponds to end2
01537     if (edge2->v1()->cast_to_vertex_2d() == end2)
01538     {
01539       if (sdet_contour::debug_)
01540         vcl_cout << "Case c\n";
01541       for (int i = 1; i < l2; i++)//don't need edge2->v1() i=1
01542       {
01543         cxy->add_edgel( (*cxy2)[i] );
01544         if (sdet_contour::debug_)
01545           vcl_cout << "merge edgel " << (*cxy2)[i] << '\n';
01546       }
01547       merge->set_v2(edge2->v2());
01548       break;
01549     }
01550     //case d: v2 corresponds to end2, reverse the chain
01551     if (edge2->v2()->cast_to_vertex_2d() == end2)
01552     {
01553       if (sdet_contour::debug_)
01554         vcl_cout << "Case d\n";
01555       for (int i = l2-2; i >= 0; i--)// don't need edge2->v2() i = l2-2
01556       {
01557         cxy->add_edgel( (*cxy2)[i] );
01558         if (sdet_contour::debug_)
01559           vcl_cout << "merge edgel " << (*cxy2)[i] << '\n';
01560       }
01561       merge->set_v2(edge2->v1());
01562       break;
01563     }
01564   }
01565 
01566   // 3. Update global maps
01567   vertexMap->put(int(end1->x()), int(end1->y()), NULL);
01568   vertexMap->put(int(end2->x()), int(end2->y()), NULL);
01569   const int last = cxy->size()-1;
01570   for (int k = 1; k < last; k++)
01571     edgeMap->put( int((*cxy)[k].x()), int((*cxy)[k].y()), merge);
01572   if (edgeMap->get( int((*cxy)[0].x()), int((*cxy)[0].y())))
01573     edgeMap->put( int((*cxy)[0].x()), int((*cxy)[0].y()), merge);
01574   if (edgeMap->get( int((*cxy)[last].x()), int((*cxy)[last].y())))
01575     edgeMap->put( int((*cxy)[last].x()), int((*cxy)[last].y()), merge);
01576 
01577   if (l1 >= l2)                 // sort out length of deleted subchains
01578     longer = edge1, shorter = edge2;
01579   else
01580     longer = edge2, shorter = edge1;
01581   if (sdet_contour::debug_)
01582   {
01583     vcl_cout << "longer " << *(longer->v1()->cast_to_vertex_2d())
01584              << ' ' << *(longer->v2()->cast_to_vertex_2d()) << '\n'
01585              << "shorter " << *(shorter->v1()->cast_to_vertex_2d())
01586              << ' ' << *(shorter->v2()->cast_to_vertex_2d()) << '\n';
01587   }
01588 }
01589 
01590 
01591 //: Merge an isolated end point into a nearby junction.
01592 //
01593 //                     ------------
01594 //                    |            |
01595 //             endpt  |            |         O
01596 //    0-----------o   0 junction   0         |
01597 //                    |                      |
01598 //                     ----------------------
01599 //
01600 bool sdet_contour::
01601 MergeEndPtTouchingJunction(vtol_vertex_2d_sptr const& endpt,
01602                            vtol_vertex_2d_sptr const& junction,
01603                            vtol_edge_2d_sptr& old_edge,
01604                            vtol_edge_2d_sptr& new_edge)
01605 {
01606   if (sdet_contour::debug_)
01607     vcl_cout << "Merge at Junction e" << *endpt<< " j"  << *junction << '\n';
01608   vcl_vector<vtol_edge_sptr> edges; endpt->edges(edges);
01609   // dangling edge terminating at end pt
01610   old_edge = edges[0]->cast_to_edge_2d();
01611   vdgl_digital_curve_sptr old_dc = old_edge->curve()->cast_to_vdgl_digital_curve();
01612   vdgl_edgel_chain_sptr old_cxy= old_dc->get_interpolator()->get_edgel_chain();
01613   int N = old_cxy->size();
01614 
01615   new_edge = new vtol_edge_2d();
01616   vdgl_edgel_chain *cxy = new vdgl_edgel_chain;
01617   vdgl_interpolator *it = new vdgl_interpolator_linear(cxy);
01618   vdgl_digital_curve *dc = new vdgl_digital_curve(it);
01619   new_edge->set_curve(*dc);
01620 
01621   int xs, ys;
01622   //replace the old edge
01623   while (true)
01624   {
01625     //case ja: v2 corresponds to endpt
01626     //copy edgels up to v2
01627     if (old_edge->v2()->cast_to_vertex_2d() == endpt)
01628     {
01629       if (sdet_contour::debug_)
01630         vcl_cout << "Case ja\n";
01631       for (int i = 0; i+1 < N; ++i)
01632       {
01633         cxy->add_edgel( (*old_cxy)[i] );
01634         if (sdet_contour::debug_)
01635           vcl_cout << "junction edgel " << (*old_cxy)[i] << '\n';
01636         edgeMap->put( int((*old_cxy)[i].x()),
01637                       int((*old_cxy)[i].y()), new_edge);
01638       }
01639       //set v1 to old v1
01640       new_edge->set_v1(old_edge->v1());
01641       xs = int(old_edge->v1()->cast_to_vertex_2d()->x());
01642       ys = int(old_edge->v1()->cast_to_vertex_2d()->y());
01643       vertexMap->put(xs, ys, new_edge->v1()->cast_to_vertex_2d());
01644       edgeMap->put(xs, ys, NULL);
01645       break;
01646     }
01647     //case jb, v1 corresponds to endpt
01648     //must reverse the original edge1 chain
01649     if (old_edge->v1()->cast_to_vertex_2d() == endpt)
01650     {
01651       if (sdet_contour::debug_)
01652         vcl_cout << "Case jb\n";
01653       for (int i = N-1; i >=0; --i)
01654       {
01655         cxy->add_edgel((*old_cxy)[i]);
01656         if (sdet_contour::debug_)
01657           vcl_cout << "junction edgel " << (*old_cxy)[i] << '\n';
01658         edgeMap->put( int((*old_cxy)[i].x()),
01659                       int((*old_cxy)[i].y()), new_edge);
01660       }
01661 
01662       //set new v1 to old v2
01663       new_edge->set_v1(old_edge->v2());
01664       xs = int(old_edge->v2()->cast_to_vertex_2d()->x());
01665       ys = int(old_edge->v2()->cast_to_vertex_2d()->y());
01666       vertexMap->put(xs, ys, new_edge->v1()->cast_to_vertex_2d());
01667       edgeMap->put(xs, ys, NULL);
01668       break;
01669     }
01670   }
01671   //At this point we have copied the old edge up to the gap at endpt
01672   //the new edge has v2 at the position of endpt.
01673   //Now we add edgels to reach the junction
01674   double xe = junction->cast_to_vertex_2d()->x();
01675   double ye = junction->cast_to_vertex_2d()->y();
01676   int nedgls =
01677     bdgl_curve_algs::add_straight_edgels(cxy, xe, ye,
01678                                          sdet_contour::debug_);
01679 
01680   //Check for self-intersection
01681   //Intersections in a 3x3 window around the
01682   //final point (xs, ys) do not count since
01683   //there are intrinsically collisions near the vertex
01684   bool self_intersects = false;//JLM added -1 need to check!
01685   for (int i = N; i<(N+nedgls-1)&&!self_intersects; i++)
01686   {
01687     int x = int((*cxy)[i].x()), y = int((*cxy)[i].y());
01688 #define WARN(x,y) vcl_cerr << "Warning: edgel "<<i<<" is at ("<<x<<','<<y\
01689                            <<") which is outside of edge map of size "\
01690                            <<edgeMap->rows()<<'x'<<edgeMap->cols()<<'\n'
01691     if (x < 0) { WARN(x,y); x = 0; }
01692     if (y < 0) { WARN(x,y); y = 0; }
01693     if (x >= int(edgeMap->rows())) { WARN(x,y); x = edgeMap->rows()-1; }
01694     if (y >= int(edgeMap->cols())) { WARN(x,y); y = edgeMap->cols()-1; }
01695 #undef WARN
01696 
01697     if (sdet_contour::debug_)
01698       vcl_cout << " intersecting (" << i << ")(" << vnl_math_abs(x-xe)
01699                << ' ' << vnl_math_abs(y-ye) << ")\n";
01700 
01701     self_intersects = self_intersects ||
01702       ((edgeMap->get(x, y)==new_edge)&&
01703        ((vnl_math_abs(x-xe)>1)||(vnl_math_abs(y-ye)>1)));
01704 
01705     if (!self_intersects)
01706       edgeMap->put(x, y,new_edge);
01707   }
01708 
01709   if (sdet_contour::debug_&&self_intersects)
01710     vcl_cout << "merge endpoint touching junction - self-intersection\n";
01711 
01712   if (self_intersects)
01713     return false;
01714 
01715   //set v2 of the new edge to be the junction
01716   new_edge->set_v2(junction->cast_to_vertex());
01717   int x = int(junction->x()), y = int(junction->y());
01718   vertexMap->put(x, y, junction);
01719   vertexMap->put(int(endpt->x()), int(endpt->y()), NULL);
01720 
01721   edgeMap->put(x, y, NULL);
01722 
01723   return true;
01724 }
01725 
01726 
01727 //:
01728 // Find junctions from end points touching at an interior point
01729 // of a chain, with detectable jump in filter response.
01730 // Localize these junctions on the stronger contour to pixel accuracy,
01731 // and break stronger chain into subchains.
01732 // Also merge end points touching another end point or junction.
01733 // Return the number of end points and junctions bounding
01734 // all chains/cycles detected in sdet_contour::FindChains.
01735 // Deletion/insertion to the network must be done completely,
01736 // so that the connectivity links are updated.  Protected.
01737 int
01738 sdet_contour::FindJunctions(gevd_bufferxy& edgels,
01739                             vcl_vector<vtol_edge_2d_sptr>& edges,
01740                             vcl_vector<vtol_vertex_2d_sptr >& vertices)
01741 {
01742   vul_timer t;
01743 
01744   if (!edges.size())
01745   {
01746     vcl_cerr << "sdet_contour::FindChains must precede sdet_contour::FindJunctions.\n";
01747     return 0;
01748   }
01749   // 1. Create vertices at the end of edges (digital_curve geometry)
01750   const float connect_fuzz = 2;
01751 
01752   for (unsigned int i=0; i< edges.size(); i++)
01753   {
01754     vtol_edge_2d_sptr edge = edges[i];
01755     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01756     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01757     //the index of the last edgel in the curve
01758     const int last = cxy->size()-1;
01759     //the edge might be a closed cycle
01760     // test for disjoint first/last pixel
01761     if (vcl_fabs((*cxy)[0].x()-(*cxy)[last].x()) > connect_fuzz ||
01762         vcl_fabs((*cxy)[0].y()-(*cxy)[last].y()) > connect_fuzz)
01763     { // so not closed cycle
01764       int x = int((*cxy)[0].x()), y = int((*cxy)[0].y());
01765       vtol_vertex_2d_sptr v1 = vertexMap->get(x, y);
01766       //see if there is a vertex for v1 at x,y
01767       if (!v1)
01768       {
01769         // If not, add a new vertex, for v1
01770         // 1st point in chain
01771         v1 = new vtol_vertex_2d((*cxy)[0].x(), (*cxy)[0].y());
01772         vertexMap->put(x, y, v1);
01773         LookupTableInsert(vertices, v1);
01774       }
01775       else//othewise erase the edgel at x,y
01776       {
01777         edgeMap->put( x, y, NULL); // erase junction point
01778       }
01779 
01780       edge->set_v1(v1->cast_to_vertex());         // link both directions v-e
01781       if (sdet_contour::debug_)
01782         vcl_cout << "adding vertex (" << x << ' ' << y
01783                  << ")(" << v1->numsup() << ")\n";
01784       x = int((*cxy)[last].x()), y = int((*cxy)[last].y());
01785 
01786       vtol_vertex_2d_sptr v2 = vertexMap->get(x, y);
01787       //see if there is a vertex for v2 at x,y
01788       if (!v2)
01789       {
01790         // If not, add a new vertex, for v2
01791         // last point in chain
01792         v2 = new vtol_vertex_2d((*cxy)[last].x(), (*cxy)[last].y());
01793         vertexMap->put(x, y, v2);
01794         LookupTableInsert(vertices, v2);
01795       }
01796       else//othewise erase the edgel at x,y
01797       {
01798         edgeMap->put( x, y, NULL); // erase junction point
01799       }
01800 
01801       edge->set_v2(v2->cast_to_vertex());  // link both directions v-e
01802       if (sdet_contour::debug_)
01803         vcl_cout << "adding vertex (" << x << ' ' << y
01804                  << ")(" << v2->numsup() << ")\n";
01805     }
01806     else // is a closed cycle but with a potential gap of connect_fuzz
01807       fill_cycle_gap(cxy);
01808   }
01809   //
01810   // At this point, all edges have vertices defined at the endpoints
01811   // of the digital curve except for 1-edge cycles
01812   //
01813   // 2. Localize a junction, when an end point of a dangling contour
01814   // touches another contour or itself at an interior point.
01815   //
01816   // If the end vertex does form a junction, e.g. with DetectJunction
01817   // then the digital_curve "weaker" must also be updated
01818   //
01819   int jcycle = 0, jchain = 0;   // number of junctions with cycle/chain
01820   for (unsigned int i=0; i< vertices.size(); i++)
01821   {
01822     //continue; //JLM no junctions
01823     vtol_vertex_2d_sptr  endv = vertices[i];
01824     vtol_edge_2d_sptr weaker = NULL, stronger = NULL;
01825     int index; // location on stronger contour
01826     if (DetectJunction(endv, index, weaker, stronger, maxSpiral, edgels))
01827     {
01828       if (sdet_contour::debug_)
01829         vcl_cout << "detected junction near (" << endv->x() <<' '<< endv->y()
01830                  << ")\n";
01831 
01832       int old_x = int(endv->x()), old_y = int(endv->y());//before end moves
01833 
01834       //If v1 is NULL then the edge is a cycle
01835 
01836       if (!stronger->v1())
01837       {
01838         // cycle is now split at junction
01839         vtol_edge_2d_sptr split = NULL;
01840         BreakCycle(endv, index, stronger, split);
01841         LookupTableReplace(edges, stronger, split);
01842 
01843         //Replace the mutated weaker digital curve
01844         //since the endpoint may have moved
01845         update_edgel_chain(weaker, old_x, old_y, endv);
01846 
01847         if (sdet_contour::debug_) {
01848           vcl_cout << "new position on cycle (" << endv->x() << ' '
01849                    << endv->y() << ")\n";
01850         }
01851         jcycle++;             // remove original edge
01852       }
01853       else if (weaker == stronger)                  // touch itself or another 1-chain
01854       {
01855         vtol_edge_2d_sptr straight = NULL, curled = NULL;
01856         // break own chain and make a loop
01857         // edgel chain gaps are updated internally
01858         LoopChain(endv, index, stronger, straight, curled);
01859 
01860         LookupTableReplace(edges, stronger, straight);
01861         LookupTableInsert(edges, curled);
01862 
01863         if (sdet_contour::debug_)
01864           vcl_cout << "new position on loop chain (" << endv->x()
01865                    << ' ' << endv->y()<< ")\n";
01866         jchain++;
01867       }
01868       else
01869       {
01870         vtol_edge_2d_sptr longer = NULL, shorter = NULL;
01871         BreakChain(endv, index, stronger,longer, shorter);
01872         LookupTableReplace(edges, stronger, longer);
01873         LookupTableInsert(edges, shorter);
01874 
01875         //Replace the mutated weaker digital curve
01876         //since the endpoint may have moved
01877         update_edgel_chain(weaker, old_x, old_y, endv);
01878         if (sdet_contour::debug_)
01879           vcl_cout << "old position on chain (" << old_x
01880                    << ' ' << old_y
01881                    << ")  new position on chain (" << endv->x()
01882                    << ' ' << endv->y()<< ")(" << endv->numsup() <<")\n";
01883 
01884         jchain++;
01885       }
01886     }
01887   }
01888 
01889   if (talkative_)
01890     vcl_cout << "Find junctions with "
01891              << jcycle << " cycles and " << jchain << " chains, with jump > "
01892              << minJump << " and maxSpiral " << maxSpiral << vcl_endl;
01893   // 3. Merge touching end points, into a larger junction/chain.
01894   int dendpt = 0, dchain = 0;   // number of deleted endpt/chain
01895 
01896   for (unsigned int i=0; i< vertices.size(); i++)
01897   {
01898     //continue; //JLM no merge
01899     // search from dangling end pt only
01900     vtol_vertex_2d_sptr  end1 = vertices[i];
01901 
01902     // skip deleted vertices, i.e., !end
01903     // and only consider end point of dangling 1-chain
01904     // but not within border strip
01905     if (end1 && end1->numsup() == 1 && !near_border(end1))
01906     {
01907       if (sdet_contour::debug_)
01908         vcl_cout << "merge target end1(" << end1->x() << ' '
01909                  << end1->y() << ")\n";
01910       // find another vertex nearby
01911       vtol_vertex_2d_sptr  end2 = DetectTouch(end1, maxSpiral);
01912       if (end2)
01913       {
01914         //Case a. The other end point has only one edge
01915         if (end2->numsup() == 1)
01916         {
01917           vtol_edge_2d_sptr seg = DanglingEdge(end1);
01918           //Case a1. The edge on the other endpoint is the same as seg
01919           if (seg == DanglingEdge(end2))
01920           {
01921             vtol_vertex_2d_sptr removed_vert = NULL;
01922             if (MergeEndPtsOfChain(end1, end2, removed_vert))
01923             {
01924               LookupTableRemove(vertices, removed_vert);
01925               if (sdet_contour::debug_)
01926                 vcl_cout << "cycle endpt1=" << *end1 << vcl_endl
01927                          << "cycle endpt2=" << *end2 << vcl_endl;
01928               dendpt++;
01929             }
01930           }
01931           else
01932           {
01933             //Case a2. The edge on the other endpoint is different from seg
01934             if (sdet_contour::debug_)
01935               vcl_cout << "endpt1=" << *end1 << vcl_endl
01936                        << "endpt2=" << *end2 << vcl_endl;
01937 
01938             vtol_edge_2d_sptr merge=NULL, longer=NULL, shorter=NULL;
01939             // merge 2 different edges
01940             MergeEndPtTouchingEndPt(end1, end2,
01941                                     merge, longer, shorter);
01942             if (sdet_contour::debug_)
01943               vcl_cout << "merge=" << *merge << vcl_endl
01944                        << "longer=" << *longer << vcl_endl
01945                        << "shorter=" << *shorter << vcl_endl
01946                        << "merge.v1=" << *merge->v1() << vcl_endl
01947                        << "merge.v2=" << *merge->v2() << vcl_endl;
01948 
01949             LookupTableReplace(edges, longer, merge);
01950             LookupTableRemove(edges, shorter);
01951             LookupTableRemove(vertices, end1);
01952             LookupTableRemove(vertices, end2);
01953             dendpt += 2, dchain += 1;
01954           }
01955         }
01956         else
01957         {
01958           //Case b. The other junction has more than 2 edges
01959           if (sdet_contour::debug_)
01960             vcl_cout << "junction endpt1=" << *end1 << vcl_endl
01961                      << "junction endpt2=" << *end2 << vcl_endl;
01962           vtol_edge_2d_sptr old_edge=NULL, new_edge=NULL;
01963           if (MergeEndPtTouchingJunction(end1, end2, old_edge, new_edge))
01964           {
01965             LookupTableReplace(edges, old_edge, new_edge);
01966             LookupTableRemove(vertices, end1);
01967             dendpt++;
01968           }
01969         }
01970       } //end if (end2->numsup()==1)
01971     } //end if (end2)
01972   }
01973 
01974   if (sdet_contour::debug_)
01975     vcl_cout << "Merge and delete " << dendpt
01976              << " end points and " << dchain << " edges\n";
01977   if (dchain)                   // eliminate holes in global arrays
01978     LookupTableCompress(edges);
01979   if (dendpt)
01980     LookupTableCompress(vertices);
01981 
01982   // 4. Insert virtual junction for isolated 1-cycles
01983   int ncycle = 0;
01984   for (unsigned int i=0; i< edges.size(); i++)
01985   {
01986     //continue; //JLM no virtual junctions
01987     vtol_edge_2d_sptr edge = edges[i];
01988     if (!edge->v1())    // vertices not created from 1.
01989     {
01990       vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
01991       vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
01992 
01993       const int last = cxy->size()-1;
01994       vtol_vertex_2d_sptr  v =
01995         new vtol_vertex_2d(((*cxy)[0].x()+(*cxy)[last].x())/2,
01996                            ((*cxy)[0].y()+(*cxy)[last].y())/2);
01997       edge->set_v1(v->cast_to_vertex()); edge->set_v2(v->cast_to_vertex());
01998       vertexMap->put(int(v->x()), int(v->y()), v);
01999       LookupTableInsert(vertices, v);
02000       ncycle++;
02001     }
02002   }
02003   if (sdet_contour::debug_)
02004     vcl_cout << "Create " << ncycle
02005              << " virtual end points for isolated cycles.\n";
02006 
02007   if (talkative_)
02008     vcl_cout << "All junctions found in " << t.real() << " msecs.\n";
02009 
02010   return vertices.size();
02011 }
02012 
02013 
02014 //: Insert subpixel accuracy into the pixels on the edges/vertices.
02015 // Truncating float locations with int(xy) should map to the original
02016 // pixel locations. No interpolation is done at junctions of 3 or more
02017 // contours, so a junction can have location error up to 1-2 pixel,
02018 // tangential to the strong contour.
02019 void
02020 sdet_contour::SubPixelAccuracy(vcl_vector<vtol_edge_2d_sptr>& edges,
02021                                vcl_vector<vtol_vertex_2d_sptr >& vertices,
02022                                const gevd_bufferxy& locationx,
02023                                const gevd_bufferxy& locationy)
02024 {
02025   //return;//JLM no subpixel
02026   vul_timer t;
02027   if (talkative_)
02028     vcl_cout << "Insert subpixel accuracy into edges/vertices";
02029 
02030   // 1. Subpixel accuracy for end points
02031   for (unsigned int i=0; i< vertices.size(); i++)
02032   {
02033     vtol_vertex_2d_sptr  vert = vertices[i];
02034     int x = int(vert->x()), y = int(vert->y());
02035     vert->set_x(x + floatPixel(locationx, x, y));
02036     vert->set_y(y + floatPixel(locationy, x, y));
02037   }
02038 
02039   // 2. Subpixel accuracy for chain pixels
02040   for (unsigned int i=0; i< edges.size(); i++)
02041   {
02042     vtol_edge_2d_sptr edge = edges[i];
02043     if (!edge) continue;
02044     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
02045     if (!dc) continue;
02046     if (!dc->get_interpolator()) continue;
02047     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
02048     if (!cxy) continue;
02049 
02050     for (unsigned int k = 0; k < cxy->size(); ++k)
02051     {
02052       // if ((*cxy)[k].get_grad()<0)
02053       int x = int((*cxy)[k].x()), y = int((*cxy)[k].y());
02054       double tempx= (*cxy)[k].x()+ floatPixel( locationx, x, y);
02055       double tempy= (*cxy)[k].y()+ floatPixel( locationy, x, y);
02056       (*cxy)[k].set_x( tempx);
02057       (*cxy)[k].set_y( tempy);
02058     }
02059   }
02060 
02061   // 3. Thin zig-zags on the contours? Zig-zags happen at
02062   // the 2 end points because of extension, or inside the contour
02063   // because of 4/8-connected tracing through noisy chain pixels,
02064   // and large shifts for subpixel locations.
02065   // Implement only if experiments prove zig-zags are excessive
02066 
02067   if (talkative_)
02068     vcl_cout << ", in " << t.real() << " msecs.\n";
02069 }
02070 
02071 
02072 //:
02073 //  Generate an Edge with a vdgl_digital_curve representing a straight line
02074 //  between the specified vertices.
02075 vtol_edge_2d_sptr DigitalEdge(vtol_vertex_2d_sptr const& vs,
02076                               vtol_vertex_2d_sptr const& ve)
02077 {
02078   vsol_curve_2d_sptr dc= new vdgl_digital_curve(vs->point(), ve->point());
02079   return new vtol_edge_2d(vs, ve, dc);
02080 }
02081 
02082 
02083 //:
02084 // Insert virtual edges and vertices to enforce closure
02085 // of the regions beyond the rectangular image border.
02086 // The location of the border is at 3 pixels away from the
02087 // real image border, because of kernel radius in convolution
02088 // and non maximum suppression. Virtual border of image should be
02089 // inserted after sdet_contour::FindChains() and sdet_contour::FindJunctions().
02090 //
02091 // JLM - February 1999  Modified this routine extensively to
02092 // move the border to the actual image ROI bounds.  Chain endpoints
02093 // are extended to intersect with the border.  These changes were
02094 // made to support region segmentation from edgels.
02095 void
02096 sdet_contour::InsertBorder(vcl_vector<vtol_edge_2d_sptr>& edges,
02097                            vcl_vector<vtol_vertex_2d_sptr >& vertices)
02098 {
02099   //return;//JLM no borders
02100   vul_timer t;
02101   bool merge = true;//merge close vertices
02102   //1. Save Edges along the border
02103   vcl_vector<vtol_vertex_2d_sptr > xmin_verts;
02104   vcl_vector<vtol_vertex_2d_sptr > xmax_verts;
02105   vcl_vector<vtol_vertex_2d_sptr > ymin_verts;
02106   vcl_vector<vtol_vertex_2d_sptr > ymax_verts;
02107 
02108   if (talkative_)
02109     vcl_cout << "Insert virtual border to enforce closure";
02110 
02111   // 2. Create 4 corners vertices
02112   const int rmax = FRAME;       // border of image
02113   const int xmax = vertexMap->rows()-rmax-1;
02114   const int ymax = vertexMap->columns()-rmax-1;
02115   int cx[] = {rmax, xmax, rmax, xmax}; // coordinates of 4 corners
02116   int cy[] = {rmax, ymax, ymax, rmax};
02117   int d;
02118   // 3. Collect Vertices along each border
02119   //3.0 Generate Corner Vertices
02120   vtol_vertex_2d_sptr  V00 = new vtol_vertex_2d(rmax, rmax);
02121   vtol_vertex_2d_sptr  V01 = new vtol_vertex_2d(rmax, ymax);
02122   vtol_vertex_2d_sptr  V10 = new vtol_vertex_2d(xmax, rmax);
02123   vtol_vertex_2d_sptr  V11 = new vtol_vertex_2d(xmax, ymax);
02124   xmin_verts.push_back(V00);
02125   xmax_verts.push_back(V10);
02126   ymin_verts.push_back(V00);
02127   ymax_verts.push_back(V01);
02128   // 3.1 ymin, ymax edges
02129   for (d = 0; d < 2; d++)
02130   {
02131     int x, y = cy[d];
02132     for (x = rmax; x<=xmax; x++)
02133     {
02134       vtol_vertex_2d_sptr  v = vertexMap->get(x, y);
02135       if (v)
02136         vertexMap->put(x, y, NULL);
02137       else continue;
02138       if (d)
02139         ymax_verts.push_back(v);
02140       else
02141         ymin_verts.push_back(v);
02142     }
02143   }
02144   // 3.2 xmin, xmax edges
02145   for (d = 0; d < 2; d++)
02146   {
02147     int x = cx[d], y;
02148     for (y = rmax; y<=ymax; y++)
02149     {
02150       vtol_vertex_2d_sptr  v = vertexMap->get(x, y);
02151       if (v)
02152         vertexMap->put(x, y, NULL);
02153       else continue;
02154       if (d)
02155         xmax_verts.push_back(v);
02156       else
02157         xmin_verts.push_back(v);
02158     }
02159   }
02160 
02161   xmin_verts.push_back(V01);
02162   xmax_verts.push_back(V11);
02163   ymin_verts.push_back(V10);
02164   ymax_verts.push_back(V11);
02165 
02166   // 4. Merge vertices
02167   // 4.1  along ymin and ymax
02168   for (d = 0; d < 2; d++)
02169   {
02170     vcl_vector<vtol_vertex_2d_sptr >* verts = &ymin_verts;
02171     if (d)
02172       verts = &ymax_verts;
02173     int len = (*verts).size();
02174     if (len<3) continue;
02175     //potential merge at xmin
02176     vtol_vertex_2d_sptr  pre_v = (*verts)[0];
02177     vtol_vertex_2d_sptr  v = (*verts)[1];
02178     int x = int(v->x());
02179     int pre_x = int(pre_v->x());
02180     if (merge&&(x-pre_x)<3)
02181     {
02182       vtol_vertex_sptr pv = pre_v->cast_to_vertex(),
02183         vv = v->cast_to_vertex();
02184       btol_vertex_algs::merge_superiors(pv, vv);
02185 
02186       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin();
02187            it!= verts->end(); ++it)
02188       {
02189         if (*it == v)
02190         {
02191           verts->erase( it);
02192           break;
02193         }
02194       }
02195       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin();
02196            it!= vertices.end(); ++it)
02197       {
02198         if (*it == v)
02199         {
02200           vertices.erase( it);
02201           break;
02202         }
02203       }
02204       len--;
02205     }
02206     //potential merge at xmax
02207     pre_v = (*verts)[len-2];
02208     v = (*verts)[len-1];
02209     pre_x = int(pre_v->x());
02210     if (merge&&(xmax+rmax-pre_x)<3)
02211     {
02212       vtol_vertex_sptr pv = pre_v->cast_to_vertex(),
02213         vv = v->cast_to_vertex();
02214       btol_vertex_algs::merge_superiors(pv, vv);
02215 
02216       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin();
02217            it!= verts->end(); ++it)
02218       {
02219         if (*it == pre_v)
02220         {
02221           verts->erase( it);
02222           break;
02223         }
02224       }
02225       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin();
02226            it!= vertices.end(); ++it)
02227       {
02228         if (*it == pre_v)
02229         {
02230           vertices.erase( it);
02231           break;
02232         }
02233       }
02234       len--;
02235     }
02236   }
02237 
02238   // 4.2  along xmin and xmax
02239   for (d = 0; d < 2; d++)
02240   {
02241     vcl_vector<vtol_vertex_2d_sptr >* verts = &xmin_verts;
02242     if (d)
02243       verts = &xmax_verts;
02244     int len = (*verts).size();
02245     if (len<3) continue;
02246     //potential merge at ymin
02247     vtol_vertex_2d_sptr  pre_v = (*verts)[0];
02248     vtol_vertex_2d_sptr  v = (*verts)[1];
02249 
02250     int y = int(v->y()), pre_y = int(pre_v->y());
02251     if (merge&&(y-pre_y)<3)
02252     {
02253       vtol_vertex_sptr pv = pre_v->cast_to_vertex(),
02254         vv = v->cast_to_vertex();
02255       btol_vertex_algs::merge_superiors(pv, vv);
02256       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin();
02257            it!= verts->end(); ++it)
02258       {
02259         if (*it == v)
02260         {
02261           verts->erase( it);
02262           break;
02263         }
02264       }
02265       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin();
02266            it!= vertices.end(); ++it)
02267       {
02268         if (*it == v)
02269         {
02270           vertices.erase( it);
02271           break;
02272         }
02273       }
02274       len--;
02275     }
02276     //potential merge at ymax
02277     pre_v = (*verts)[len-2];
02278     v = (*verts)[len-1];
02279     pre_y = int(pre_v->y());
02280     if (merge&&(ymax+rmax-pre_y)<3)
02281     {
02282       vtol_vertex_sptr pv = pre_v->cast_to_vertex(),
02283         vv = v->cast_to_vertex();
02284       btol_vertex_algs::merge_superiors(pv, vv);
02285       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= verts->begin();
02286            it!= verts->end(); ++it)
02287       {
02288         if (*it == pre_v)
02289         {
02290           verts->erase( it);
02291           break;
02292         }
02293       }
02294       for (vcl_vector<vtol_vertex_2d_sptr >::iterator it= vertices.begin();
02295            it!= vertices.end(); ++it)
02296       {
02297         if (*it == pre_v)
02298         {
02299           vertices.erase( it);
02300           break;
02301         }
02302       }
02303       len--;
02304     }
02305   }
02306 
02307   // 5. Move the vertices to the bounds of the ROI
02308   float xmi = 0, xmx = float(xmax + rmax);
02309   float ymi = 0, ymx = float(ymax + rmax);
02310   for (unsigned int iv=1; iv+1<xmin_verts.size(); ++iv)
02311   {
02312     vtol_vertex_2d_sptr  v = xmin_verts[iv];
02313     if (!v) continue;
02314     vtol_vertex_2d_sptr  vp = new vtol_vertex_2d(xmi, v->y());
02315     vertices.push_back(vp);// vp->Protect();
02316     xmin_verts[iv] = vp;
02317 
02318     vtol_edge_2d_sptr e = DigitalEdge(v, vp);
02319     edges.push_back(e);//  e->Protect();
02320   }
02321 
02322   for (unsigned int iv=1; iv+1<xmax_verts.size(); ++iv)
02323   {
02324     vtol_vertex_2d_sptr v = xmax_verts[iv];
02325     if (!v) continue;
02326     vtol_vertex_2d_sptr  vp = new vtol_vertex_2d( xmx, v->y());
02327     vertices.push_back(vp); // vp->Protect();
02328     xmax_verts[iv] = vp;
02329     vtol_edge_2d_sptr e = DigitalEdge(v, vp);
02330     edges.push_back(e); // e->Protect();
02331   }
02332   for (unsigned int iv=1; iv+1<ymin_verts.size(); ++iv)
02333   {
02334     vtol_vertex_2d_sptr  v = ymin_verts[iv];
02335     if (!v) continue;
02336     vtol_vertex_2d_sptr  vp = new vtol_vertex_2d(v->x(), ymi);
02337     vertices.push_back(vp); // vp->Protect();
02338     ymin_verts[iv] = vp;
02339     vtol_edge_2d_sptr e = DigitalEdge(v, vp);
02340     edges.push_back(e); // e->Protect();
02341   }
02342   for (unsigned int iv=1; iv+1<ymax_verts.size(); ++iv)
02343   {
02344     vtol_vertex_2d_sptr  v = ymax_verts[iv];
02345     if (!v) continue;
02346     vtol_vertex_2d_sptr  vp = new vtol_vertex_2d( v->x(), ymx);
02347     vertices.push_back(vp); // vp->Protect();
02348     ymax_verts[iv] = vp;
02349     vtol_edge_2d_sptr e = DigitalEdge(v, vp);
02350     edges.push_back(e); // e->Protect();
02351   }
02352   V00->set_x(0);  V00->set_y(0); vertices.push_back(V00);
02353   V01->set_x(0);  V01->set_y(ymax+rmax); vertices.push_back(V01);
02354   V10->set_x(xmax+rmax);  V10->set_y(0); vertices.push_back(V10);
02355   V11->set_x(xmax+rmax);  V11->set_y(ymax+rmax); vertices.push_back(V11);
02356 
02357   //6. Now we have properly placed vertices.  Next we scan and generate
02358   //edges. along the border.
02359   //6.1 along ymin and ymax
02360   for (d = 0; d < 2; ++d)
02361   {
02362     vcl_vector<vtol_vertex_2d_sptr >* verts = &ymin_verts;
02363     if (d)
02364       verts = &ymax_verts;
02365     unsigned int len = (*verts).size();
02366     if (len<2)
02367     {
02368       vcl_cout <<"In sdet_contour::InsertBorder() - too few vertices\n";
02369       return;
02370     }
02371     for (unsigned int i=0; i+1<len; ++i)
02372     {
02373       vtol_vertex_2d_sptr  v = (*verts)[i];
02374       vtol_vertex_2d_sptr  vp = (*verts)[i+1];
02375       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
02376       edges.push_back(e); // e->Protect();
02377     }
02378   }
02379   //6.2 along xmin and xmax
02380   for (d = 0; d < 2; ++d)
02381   {
02382     vcl_vector<vtol_vertex_2d_sptr >* verts = &xmin_verts;
02383     if (d)
02384       verts = &xmax_verts;
02385     unsigned int len = (*verts).size();
02386     if (len<2)
02387     {
02388       vcl_cout <<"In sdet_contour::InsertBorder() - too few vertices\n";
02389       return;
02390     }
02391     for (unsigned int i = 0; i+1<len; ++i)
02392     {
02393       vtol_vertex_2d_sptr  v = (*verts)[i];
02394       vtol_vertex_2d_sptr  vp = (*verts)[i+1];
02395       vtol_edge_2d_sptr e = DigitalEdge(v, vp);
02396       edges.push_back(e); // e->Protect();
02397     }
02398   }
02399 
02400   if (talkative_)
02401     vcl_cout << ", in " << t.real() << " msecs.\n";
02402 }
02403 
02404 
02405 //:
02406 // Convolve array elements with [1 0 1]/2, replacing
02407 // center pixel by average of 2 neighbors.
02408 // This will make the spacing between pixels almost equal
02409 // and prune away small zig-zags.
02410 void
02411 EqualizeElements(double* elmts, int n, double v1, double v2)
02412 {
02413   double p0 = elmts[0], p1 = elmts[1], p2 = elmts[2]; // setup pipeline
02414   elmts[0] = (v1 + p1) / 2;     // touching first vertex
02415   for (int i = 1; i+2 < n; ++i)
02416   {
02417     elmts[i] = (p0 + p2)/2;
02418     p0 = p1; p1 = p2; p2 = elmts[i+2]; // faster with circular list
02419   }
02420   if (n>1) elmts[n-2] = (p0 + p2)/2;   // last convolution
02421   if (n>0) elmts[n-1] = (p1 + v2)/2;   // touching second vertex
02422 }
02423 
02424 
02425 //:
02426 // Make the spacing of the chain pixels nearly equal by
02427 // smoothing their locations with the average filter  [1 0 1]/2.
02428 // This will reduce square grid tessellation artefacts, and
02429 // lead to more accurate estimation of the tangent direction,
02430 // and local curvature angle, from finite differences in location.
02431 // It is also useful to avoid weight artefacts in curve fitting
02432 // caused by the periodic clustering of pixels along the chain.
02433 // Truncating the float locations with int() will no longer map
02434 // to the original pixels of the discrete chains.
02435 void
02436 sdet_contour::EqualizeSpacing(vcl_vector<vtol_edge_2d_sptr>& chains)
02437 {
02438   vul_timer t;
02439 
02440   if (talkative_)
02441     vcl_cout << "Equalize the spacing between pixels in chains\n";
02442 
02443   for (unsigned int i= 0; i< chains.size(); ++i)
02444   {
02445     vtol_edge_2d_sptr e = chains[i];
02446     vdgl_digital_curve_sptr dc = e->curve()->cast_to_vdgl_digital_curve();
02447     const int len = dc->get_interpolator()->get_edgel_chain()->size();
02448     if (len > 2*MINLENGTH)
02449     {   // not necessary for short chains
02450       vtol_vertex_sptr v1 = e->v1(), v2 = e->v2();
02451 
02452       vcl_vector<double> cx(len);
02453       vcl_vector<double> cy(len);
02454 
02455       for (int qq=0; qq<len; ++qq)
02456       {
02457         vdgl_edgel e= dc->get_interpolator()->get_edgel_chain()->edgel( qq);
02458         cx[qq]= e.x();
02459         cy[qq]= e.y();
02460       }
02461 
02462       EqualizeElements(&cx[0], len, v1->cast_to_vertex_2d()->x(), v2->cast_to_vertex_2d()->x());
02463       EqualizeElements(&cy[0], len, v1->cast_to_vertex_2d()->y(), v2->cast_to_vertex_2d()->y());
02464 
02465       for (int qq=0; qq<len; ++qq)
02466       {
02467         vdgl_edgel e( cx[qq], cy[qq]);
02468         dc->get_interpolator()->get_edgel_chain()->set_edgel( qq, e);
02469       }
02470     }
02471   }
02472   if (talkative_)
02473     vcl_cout << ", in " << t.real() << " msecs.\n";
02474 }
02475 
02476 
02477 //: Translate all the pixels in the edges and vertices by (tx, ty).
02478 // If the image is extracted from an ROI, a translation of
02479 // (roi->GetOrigX(), roi->GetOrigY()) must be done to have
02480 // coordinates in the reference frame of the original image.
02481 // Add 0.5 if you want to display location at center of pixel
02482 // instead of upper-left corner.
02483 void
02484 sdet_contour::Translate(vcl_vector<vtol_edge_2d_sptr>& edges, // translate loc to center
02485                         vcl_vector<vtol_vertex_2d_sptr >& vertices,
02486                         float tx, float ty)
02487 {
02488   vul_timer t;
02489 
02490   if (talkative_)
02491     vcl_cout << "Translate edges/vertices\n";
02492 
02493   for (unsigned int i=0; i< vertices.size(); ++i)
02494   {
02495     vtol_vertex_2d_sptr  vert = vertices[i];
02496     vert->set_x(vert->x() + tx);
02497     vert->set_y(vert->y() + ty);
02498   }
02499   for (unsigned int i=0; i< edges.size(); ++i)
02500   {
02501     vtol_edge_2d_sptr edge = edges[i];
02502     vdgl_digital_curve_sptr dc = edge->curve()->cast_to_vdgl_digital_curve();
02503 
02504     vdgl_edgel_chain_sptr cxy= dc->get_interpolator()->get_edgel_chain();
02505     for (unsigned int k = 0; k < cxy->size(); ++k)
02506     {
02507       vdgl_edgel e= (*cxy)[k];
02508 
02509       e.set_x( e.x()+tx);
02510       e.set_y( e.y()+ty);
02511 
02512       cxy->set_edgel( k, e);
02513     }
02514   }
02515 
02516   if (talkative_)
02517     vcl_cout << ", in " << t.real() << " msecs.\n";
02518 }
02519 
02520 
02521 //:
02522 // Remove and delete all elements in global lists, and set
02523 // the global lists to NULL. Remove all digital chains of edges.
02524 // Edges and vertices are removed with UnProtect().
02525 void
02526 sdet_contour::ClearNetwork(vcl_vector<vtol_edge_2d_sptr>*& edges,
02527                            vcl_vector<vtol_vertex_2d_sptr >*& vertices)
02528 {
02529   delete edges; edges = NULL;
02530   delete vertices; vertices = NULL;
02531 }
02532 
02533 
02534 //:
02535 // Set the orientation at each edgel on all digital curves to a continuous
02536 // orientation value, which is consistent with C. Rothwell's EdgeDetector.
02537 // That is theta = (180/M_PI)*atan2(dI/dy, dI/dx)
02538 //
02539 void
02540 sdet_contour::SetEdgelData(gevd_bufferxy& grad_mag, gevd_bufferxy& angle, vcl_vector<vtol_edge_2d_sptr>& edges)
02541 {
02542   for (unsigned int i=0; i< edges.size(); ++i)
02543   {
02544     vtol_edge_2d_sptr e = edges[i];
02545     vdgl_digital_curve_sptr dc= e->curve()->cast_to_vdgl_digital_curve();
02546 
02547     if (dc)
02548     {
02549       vdgl_edgel_chain_sptr xypos= dc->get_interpolator()->get_edgel_chain();
02550 
02551       int len = xypos->size();
02552 
02553       for (int i = 0; i < len; ++i)
02554       {
02555         int ix = int((*xypos)[i].x());
02556         int iy = int((*xypos)[i].y());
02557 
02558         // Debugging : RIH
02559         // Routine crashes with iy < 0.
02560         if (iy < 0 || ix < 0 ||
02561             ix >= grad_mag.GetSizeX() ||
02562             iy >= grad_mag.GetSizeY())
02563         {
02564           vcl_cerr << "***********  ERROR  : (ix, iy) = ("
02565                    << ix << ", " << iy << ")\n";
02566           if (ix < 0) ix = 0;
02567           if (iy < 0) iy = 0;
02568           if (ix >= grad_mag.GetSizeX()) ix = grad_mag.GetSizeX()-1;
02569           if (iy >= grad_mag.GetSizeY()) iy = grad_mag.GetSizeY()-1;
02570         }
02571 
02572         (*xypos)[i].set_grad( floatPixel( grad_mag, ix, iy));
02573         (*xypos)[i].set_theta( floatPixel( angle, ix, iy));
02574 #if 0
02575         gr[i] = floatPixel(grad_mag, ix, iy);
02576         th[i] = floatPixel(angle, ix, iy);
02577 #endif
02578       }
02579     }
02580   }
02581 }
02582 
02583 
02584 //:
02585 // Insert topology object in 2-way lookup table,
02586 // using Id and dynamic array. Protect it in the network.
02587 void
02588 sdet_contour::LookupTableInsert(vcl_vector<vtol_edge_2d_sptr>& set,
02589                                 vtol_edge_2d_sptr elmt)
02590 {
02591   elmt->set_id(set.size());     // index in global array
02592   set.push_back(elmt);          // push_back at end of array
02593 }
02594 
02595 
02596 //: As above for vertices.
02597 void
02598 sdet_contour::LookupTableInsert(vcl_vector<vtol_vertex_2d_sptr >& set,
02599                                 vtol_vertex_2d_sptr  elmt)
02600 {
02601   elmt->set_id(set.size());     // index in global array
02602   set.push_back(elmt);          // push at end of array
02603 }
02604 
02605 
02606 //: Replace deleted by inserted in 2-way lookup table.
02607 // Also remove object from the network.
02608 void
02609 sdet_contour::LookupTableReplace(vcl_vector<vtol_edge_2d_sptr>& set,
02610                                  vtol_edge_2d_sptr deleted, vtol_edge_2d_sptr inserted)
02611 {
02612   const int i = deleted->get_id();
02613   inserted->set_id(i);
02614   set[i] = inserted;            // replace in global array
02615   btol_edge_algs::unlink_all_inferiors_twoway(deleted);
02616 }
02617 
02618 
02619 //: As above for vertices.
02620 void
02621 sdet_contour::LookupTableReplace(vcl_vector<vtol_vertex_2d_sptr >& set,
02622                                  vtol_vertex_2d_sptr  deleted, vtol_vertex_2d_sptr  inserted)
02623 {
02624   const int i = deleted->get_id();
02625   inserted->set_id(i);
02626   set[i] = inserted;            // replace in global array
02627 }
02628 
02629 
02630 //: Remove topology object from 2-way lookup table leaving an empty hole.
02631 // Also remove object from the network.
02632 void
02633 sdet_contour::LookupTableRemove(vcl_vector<vtol_edge_2d_sptr>& set,
02634                                 vtol_edge_2d_sptr elmt)
02635 {
02636   btol_edge_algs::unlink_all_inferiors_twoway(elmt);
02637   set[elmt->get_id()] = NULL;   // remove from global array
02638 }
02639 
02640 
02641 //: As above for vertices.
02642 void
02643 sdet_contour::LookupTableRemove(vcl_vector<vtol_vertex_2d_sptr >& set,
02644                                 vtol_vertex_2d_sptr  elmt)
02645 {
02646   set[elmt->get_id()] = NULL;   // remove from global array
02647 }
02648 
02649 
02650 //: Eliminate empty holes in the lookup table.
02651 void
02652 sdet_contour::LookupTableCompress(vcl_vector<vtol_edge_2d_sptr>& set)
02653 {
02654   vcl_vector<vtol_edge_2d_sptr> temp;
02655   //eliminate null edges
02656   for (vcl_vector<vtol_edge_2d_sptr>::iterator eit = set.begin();
02657        eit != set.end(); eit++)
02658     if (*eit)
02659       temp.push_back(*eit);
02660   int i = 0;
02661   set.clear();
02662   for (vcl_vector<vtol_edge_2d_sptr>::iterator eit = temp.begin();
02663        eit != temp.end(); eit++, i++)
02664   {
02665     (*eit)->set_id(i);
02666     set.push_back(*eit);
02667   }
02668 }
02669 
02670 //: As above for vertices.
02671 void
02672 sdet_contour::LookupTableCompress(vcl_vector<vtol_vertex_2d_sptr>& set)
02673 {
02674   vcl_vector<vtol_vertex_2d_sptr> temp;
02675   //eliminate null edges
02676   for (vcl_vector<vtol_vertex_2d_sptr>::iterator vit = set.begin();
02677        vit != set.end(); vit++)
02678     if (*vit)
02679       temp.push_back(*vit);
02680   int i = 0;
02681   set.clear();
02682   for (vcl_vector<vtol_vertex_2d_sptr>::iterator vit = temp.begin();
02683        vit != temp.end(); vit++, i++)
02684   {
02685     (*vit)->set_id(i);
02686     set.push_back(*vit);
02687   }
02688 }