contrib/gel/vtol/algo/vtol_extract_topology.txx
Go to the documentation of this file.
00001 #ifndef vtol_extract_topology_txx_
00002 #define vtol_extract_topology_txx_
00003 
00004 #include "vtol_extract_topology.h"
00005 //:
00006 // \file
00007 
00008 #include <vcl_iosfwd.h>
00009 #include <vcl_cassert.h>
00010 
00011 #include <vgl/vgl_point_2d.h>
00012 #include <vgl/vgl_vector_2d.h>
00013 #include <vgl/algo/vgl_line_2d_regression.h>
00014 
00015 #include <vtol/vtol_vertex_2d.h>
00016 #include <vtol/vtol_edge_2d.h>
00017 #include <vtol/vtol_edge_2d_sptr.h>
00018 #include <vtol/vtol_intensity_face.h>
00019 
00020 #include <vsol/vsol_curve_2d_sptr.h>
00021 
00022 #include <vdgl/vdgl_edgel.h>
00023 #include <vdgl/vdgl_edgel_chain.h>
00024 #include <vdgl/vdgl_interpolator_linear.h>
00025 #include <vdgl/vdgl_digital_curve.h>
00026 
00027 #ifndef NDEBUG
00028 #  include <vcl_iostream.h>
00029 #  define DBG( x ) x;
00030 #else
00031 #  define DBG( x ) /*debugging removed*/ do {} while (false)
00032 #endif
00033 
00034 // =============================================================================
00035 //                                                              EXTRACT TOPOLOGY
00036 // =============================================================================
00037 
00038 typedef vtol_extract_topology_vertex_node vertex_node;
00039 
00040 // ---------------------------------------------------------------------------
00041 //                                              static variables and constants
00042 
00043 
00044 #if !VCL_STATIC_CONST_INIT_INT_NO_DEFN
00045 const unsigned
00046 vtol_extract_topology_vertex_node::null_index  VCL_STATIC_CONST_INIT_INT_DEFN( unsigned(-2) );
00047 
00048 const unsigned
00049 vtol_extract_topology_vertex_node::done_index  VCL_STATIC_CONST_INIT_INT_DEFN( unsigned(-1) );
00050 #endif
00051 
00052 
00053 // ---------------------------------------------------------------------------
00054 //                                                                 constructor
00055 
00056 
00057 template< typename T >
00058 vtol_extract_topology<T>::
00059 vtol_extract_topology( label_image_type const& in_image,
00060                        vtol_extract_topology_params const& in_params )
00061   : label_img_( in_image ),
00062     params_( in_params )
00063 {
00064   compute_label_range();
00065   construct_topology();
00066 }
00067 
00068 
00069 // ---------------------------------------------------------------------------
00070 //                                                                    vertices
00071 
00072 template< typename T >
00073 vcl_vector< vtol_vertex_2d_sptr >
00074 vtol_extract_topology<T>::
00075 vertices() const
00076 {
00077   typedef vcl_vector< vertex_node >::const_iterator vertex_iterator_t;
00078 
00079   vcl_vector< vtol_vertex_2d_sptr > verts;
00080 
00081   for ( vertex_iterator_t i = node_list_.begin();
00082         i != node_list_.end(); ++i ) {
00083     verts.push_back( i->vertex );
00084   }
00085 
00086   return verts;
00087 }
00088 
00089 
00090 // ---------------------------------------------------------------------------
00091 //                                                                       faces
00092 
00093 template< typename T >
00094 vcl_vector< vtol_intensity_face_sptr >
00095 vtol_extract_topology<T>::
00096 faces( data_image_type const& data_img ) const
00097 {
00098   region_collection region_list;
00099   collect_regions( region_list );
00100 
00101   // Generate faces for each label. A given label may generate more
00102   // than one face based on containment, etc.
00103   //
00104   vcl_vector< vtol_intensity_face_sptr > faces;
00105   for ( unsigned i = 0; i < region_list.size(); ++i ) {
00106     if ( ! region_list[i].empty() ) {
00107       compute_faces( region_list[i], faces, &data_img );
00108     }
00109   }
00110 
00111   return faces;
00112 }
00113 
00114 
00115 template< typename T >
00116 vcl_vector< vtol_intensity_face_sptr >
00117 vtol_extract_topology<T>::
00118 faces( ) const
00119 {
00120   region_collection region_list;
00121   collect_regions( region_list );
00122 
00123   // Generate faces for each label. A given label may generate more
00124   // than one face based on containment, etc.
00125   //
00126   vcl_vector< vtol_intensity_face_sptr > faces;
00127   for ( unsigned i = 0; i < region_list.size(); ++i ) {
00128     if ( ! region_list[i].empty() ) {
00129       compute_faces( region_list[i], faces, 0 );
00130     }
00131   }
00132 
00133   return faces;
00134 }
00135 
00136 
00137 // ---------------------------------------------------------------------------
00138 //                                                                        init
00139 
00140 template< typename T >
00141 void
00142 vtol_extract_topology<T>::
00143 compute_label_range()
00144 {
00145   assert( label_img_.ni() >= 1 && label_img_.nj() >= 1 );
00146 
00147   // Determine the label ranges
00148   //
00149   min_label_ = label_img_(0,0);
00150   max_label_ = min_label_;
00151   for ( unsigned j = 0; j < label_img_.nj(); ++j ) {
00152     for ( unsigned i = 0; i < label_img_.ni(); ++i ) {
00153       if ( min_label_ > label_img_(i,j) )
00154         min_label_ = label_img_(i,j);
00155       if ( max_label_ < label_img_(i,j) )
00156         max_label_ = label_img_(i,j);
00157     }
00158   }
00159 }
00160 
00161 // ---------------------------------------------------------------------------
00162 //                                                                       label
00163 
00164 template< typename LABEL_TYPE >
00165 typename vtol_extract_topology< LABEL_TYPE >::LabelPoint
00166 vtol_extract_topology< LABEL_TYPE >::
00167 label( unsigned i, unsigned j ) const
00168 {
00169   if ( i < label_img_.ni() && j < label_img_.nj() ) {
00170     return LabelPoint(label_img_( i, j ), true);
00171   }
00172   else {
00173     return LabelPoint(0, false);
00174   }
00175 }
00176 
00177 
00178 // ---------------------------------------------------------------------------
00179 //                                                          is junction vertex
00180 
00181 template< typename T >
00182 bool
00183 vtol_extract_topology<T>::
00184 is_junction_vertex( unsigned i, unsigned j ) const
00185 {
00186   // A junction must have at least three incident boundary edges
00187 
00188   unsigned edge_count = 0;
00189   for ( unsigned dir = 0; dir < 4; ++dir ) {
00190     if ( is_edge( i, j, dir ) ) {
00191       ++edge_count;
00192     }
00193   }
00194 
00195   return edge_count >= 3;
00196 }
00197 
00198 
00199 // ---------------------------------------------------------------------------
00200 //                                                          is boundary vertex
00201 
00202 template< typename LABEL_TYPE >
00203 bool
00204 vtol_extract_topology< LABEL_TYPE >::
00205 is_boundary_vertex( unsigned i, unsigned j ) const
00206 {
00207   // A non-boundary (interior) point is surrounded by pixels of the
00208   // same value.
00209 
00210   LabelPoint pixel1( label( i  , j   ) );
00211   LabelPoint pixel2( label( i  , j-1 ) );
00212   LabelPoint pixel3( label( i-1, j   ) );
00213   LabelPoint pixel4( label( i-1, j-1 ) );
00214 
00215   return pixel1 != pixel2 || pixel2 != pixel3 || pixel3 != pixel4;
00216 }
00217 
00218 
00219 // ---------------------------------------------------------------------------
00220 //                                                                     is edge
00221 
00222 template< typename LABEL_TYPE >
00223 bool
00224 vtol_extract_topology< LABEL_TYPE >::
00225 is_edge( unsigned i, unsigned j, unsigned dir ) const
00226 {
00227   LabelPoint left, right;
00228 
00229   edge_labels( i, j, dir, left, right );
00230 
00231   return left != right;
00232 }
00233 
00234 
00235 // ---------------------------------------------------------------------------
00236 //                                                                 edge labels
00237 
00238 template< typename LABEL_TYPE >
00239 void
00240 vtol_extract_topology< LABEL_TYPE >::
00241 edge_labels( unsigned i, unsigned j, unsigned dir,
00242              LabelPoint& left, LabelPoint& right ) const
00243 {
00244   assert( dir <= 3 );
00245 
00246   // These are the offsets to get the "left" pixel position for each
00247   // direction given the vertex location (i,j).  The vertices occur at
00248   // the corners of the pixels, so that vertex (0,0) is at the
00249   // top-left corner of pixel (0,0).
00250   //
00251   // The offsets for the "right" pixel in direction d is the offset
00252   // for the left pixel in direction (d+1).
00253   //
00254   static const int offsets[4][2] =
00255     { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } };
00256 
00257   unsigned dir2 = (dir+1) % 4;
00258 
00259   left  = label( i+offsets[dir ][0], j+offsets[dir ][1] );
00260   right = label( i+offsets[dir2][0], j+offsets[dir2][1] );
00261 }
00262 
00263 
00264 // ---------------------------------------------------------------------------
00265 //                                                                vertex index
00266 
00267 template< typename T >
00268 unsigned
00269 vtol_extract_topology<T>::
00270 vertex_index( unsigned i, unsigned j ) const
00271 {
00272   assert( i < index_img_.ni() &&
00273           j < index_img_.nj() );
00274 
00275   return index_img_( i, j );
00276 }
00277 
00278 
00279 // ---------------------------------------------------------------------------
00280 //                                                            set vertex index
00281 
00282 template< typename T >
00283 void
00284 vtol_extract_topology<T>::
00285 set_vertex_index( unsigned i, unsigned j, unsigned index )
00286 {
00287   assert( i < index_img_.ni() &&
00288           j < index_img_.nj() );
00289 
00290   index_img_( i, j ) = index;
00291 }
00292 
00293 
00294 // ---------------------------------------------------------------------------
00295 //                                                                        node
00296 
00297 template< typename T >
00298 vtol_extract_topology_vertex_node&
00299 vtol_extract_topology<T>::
00300 node( unsigned index )
00301 {
00302   assert( index < node_list_.size() );
00303 
00304   return node_list_[index];
00305 }
00306 
00307 
00308 template< typename T >
00309 vtol_extract_topology_vertex_node const&
00310 vtol_extract_topology<T>::
00311 node( unsigned index ) const
00312 {
00313   assert( index < node_list_.size() );
00314 
00315   return node_list_[index];
00316 }
00317 
00318 
00319 // ---------------------------------------------------------------------------
00320 //                                                                        move
00321 
00322 template< typename T >
00323 void
00324 vtol_extract_topology<T>::
00325 move( unsigned dir, unsigned& i, unsigned& j )
00326 {
00327   assert( dir < 4 );
00328 
00329   switch ( dir )
00330   {
00331    case 0: // right
00332     ++i;
00333     break;
00334    case 1: // down
00335     ++j;
00336     break;
00337    case 2: // left
00338     --i;
00339     break;
00340    case 3: // up
00341     --j;
00342     break;
00343    default: break; // never reached
00344   }
00345 }
00346 
00347 
00348 // ---------------------------------------------------------------------------
00349 //                                                                    set mark
00350 
00351 template< typename T >
00352 void
00353 vtol_extract_topology<T>::
00354 set_mark( unsigned& marker, unsigned dir ) const
00355 {
00356   marker |= ( 1 << dir );
00357 }
00358 
00359 
00360 // ---------------------------------------------------------------------------
00361 //                                                                   is marked
00362 
00363 template< typename T >
00364 bool
00365 vtol_extract_topology<T>::
00366 is_marked( unsigned marker, unsigned dir ) const
00367 {
00368   return ( marker & ( 1 << dir ) ) != 0;
00369 }
00370 
00371 
00372 // ---------------------------------------------------------------------------
00373 //                                                            trace edge chain
00374 
00375 template< typename T >
00376 void
00377 vtol_extract_topology<T>::
00378 trace_edge_chain( unsigned i, unsigned j, unsigned dir )
00379 {
00380   // Quick exit if there is nothing to trace in this direction.
00381   //
00382   if ( ! is_edge( i, j, dir ) )
00383     return;
00384 
00385 
00386   // When constructing the spatial and topological objects from the
00387   // "pixel corner" vertices, we add the (-.5,-.5) offset required to
00388   // bring everything into pixel coordinates.
00389 
00390   unsigned start_index = vertex_index( i, j );
00391   unsigned start_dir = dir;
00392 
00393   // The chain of "pixel corners" from one vertex to the other.
00394   //
00395   vdgl_edgel_chain_sptr chain = new vdgl_edgel_chain();
00396 
00397   // Start vertex
00398   chain->add_edgel( vdgl_edgel( i-0.5, j-0.5, -1, dir*90 ) );
00399   move( dir, i, j );
00400 
00401   // If we have a complete cycle (i.e. start==end), we have to step
00402   // back one because vtol doesn't like edges with the same start and
00403   // end. When we step back, we need to keep track of the direction
00404   // from which we came to that point. We'll only ever need to step
00405   // back once, so it is sufficient to keep the current direction
00406   // (direction of the last step) in dir) and the previous direction
00407   // in prev_dir.
00408   unsigned int prev_dir = (unsigned int)(-1);
00409 
00410   while ( vertex_index( i, j ) == vertex_node::null_index )
00411   {
00412     set_vertex_index( i, j, vertex_node::done_index );
00413 
00414     chain->add_edgel( vdgl_edgel( i-0.5, j-0.5, -1, dir*90 ) );
00415 
00416     // find and move in the outgoing direction. There should be
00417     // exactly one, since this is not a vertex.
00418     //
00419     DBG( unsigned count = 0 );
00420     prev_dir = dir;
00421     dir = (dir+3) % 4; // same as dir = dir - 1
00422     while ( ! is_edge( i, j, dir ) ) {
00423       dir = (dir+1) % 4;
00424       DBG( ++count );
00425       DBG( assert( count < 3 ) );
00426     }
00427 
00428     move( dir, i, j );
00429 
00430     // The same non-junction vertex should not appear on multiple
00431     // traces. (The "reverse trace" is will be done below by just
00432     // reversing the start and end.)
00433     //
00434     assert( vertex_index( i, j ) != vertex_node::done_index );
00435   }
00436 
00437   unsigned end_index = vertex_index( i, j );
00438 
00439   if ( end_index == start_index ) {
00440     // Construct a new vertex at the just-before-last point to avoid
00441     // having a chain with identical end-points. Move backwards one
00442     // unit and create a vertex.
00443     move( (dir+2)%4, i, j );
00444     set_vertex_index( i, j, node_list_.size() );
00445     node_list_.push_back( vertex_node( i, j ) );
00446     end_index = vertex_index( i, j );
00447     dir = prev_dir; // the direction we came from to the new end point.
00448     // The new end point is already in the edgel chain; no need to add again.
00449   }
00450   else {
00451     // Add the end vertex
00452     chain->add_edgel( vdgl_edgel( i-0.5, j-0.5 ) );
00453   }
00454 
00455   chain = smooth_chain( chain, params_.num_for_smooth );
00456 
00457   vertex_node& start_node = node( start_index );
00458   vertex_node& end_node   = node( end_index );
00459 
00460   vdgl_interpolator_sptr interp = new vdgl_interpolator_linear( chain );
00461   vsol_curve_2d_sptr curve = new vdgl_digital_curve( interp );
00462   vtol_edge_2d_sptr edge = new vtol_edge_2d( start_node.vertex,
00463                                              end_node.vertex,
00464                                              curve );
00465 
00466   edgel_chain_sptr chain2 = new edgel_chain;
00467   chain2->chain = chain;
00468   chain2->edge  = edge;
00469 
00470   // Turn around, because the that is the direction we would begin in
00471   // from the end node for a reverse trace.
00472   //
00473   dir = (dir+2) % 4;
00474 
00475   start_node.link[ start_dir ] = end_index;
00476   start_node.back_dir[ start_dir ] = dir;
00477   start_node.edgel_chain[ start_dir ] = chain2;
00478 
00479   end_node.link[ dir ] = start_index;
00480   end_node.back_dir[ dir ] = start_dir;
00481   end_node.edgel_chain[ dir ] = chain2;
00482 }
00483 
00484 
00485 // ---------------------------------------------------------------------------
00486 //                                                          construct topology
00487 
00488 template< typename T >
00489 void
00490 vtol_extract_topology<T>::
00491 construct_topology( )
00492 {
00493   // Construct the list of vertex nodes from the junctions and
00494   // initialize the vertex index array.
00495   //
00496   index_img_.set_size( label_img_.ni()+1, label_img_.nj()+1 );
00497 
00498   node_list_.clear();
00499   index_img_.fill( vertex_node::null_index );
00500 
00501   for ( unsigned j = 0; j <= label_img_.nj(); ++j ) {
00502     for ( unsigned i = 0; i <= label_img_.ni(); ++i ) {
00503       if ( is_junction_vertex( i, j ) ) {
00504         set_vertex_index( i, j, node_list_.size() );
00505         node_list_.push_back( vertex_node( i, j ) );
00506       }
00507     }
00508   }
00509 
00510   // Construct the edge graph by following each edge from
00511   // each vertex.
00512   //
00513   for ( unsigned index = 0; index < node_list_.size(); ++index ) {
00514     for ( unsigned dir = 0; dir < 4; ++dir ) {
00515       if ( node(index).link[dir] == vertex_node::null_index ) {
00516         trace_edge_chain( node(index).i,
00517                           node(index).j,
00518                           dir );
00519       }
00520     }
00521   }
00522 
00523   // Look for untouched boundary vertices, and make them vertices
00524   // too. Tracing from one of these must lead back to itself. The
00525   // topology classes don't like edges that are themselves
00526   // cycles. They expect that each edge has two distinct
00527   // vertices. Instead of creating two vertices at the same point, we
00528   // create two vertices adjacent to each other. Thus, each of these
00529   // faces (created later) will be bounded by two edges, one
00530   // potentially long edge, and one edge with length one pixel.
00531   //
00532   for ( unsigned j = 0; j <= label_img_.nj(); ++j ) {
00533     for ( unsigned i = 0; i <= label_img_.ni(); ++i ) {
00534       if ( vertex_index( i, j ) == vertex_node::null_index &&
00535            is_boundary_vertex( i, j ) )
00536       {
00537         // Find the two outgoing directions
00538         //
00539         unsigned dir = 0;
00540         while ( dir < 4 && ! is_edge( i, j, dir ) ) {
00541           ++dir;
00542         }
00543         assert( dir < 4 );
00544         unsigned dir2 = dir+1;
00545         while ( dir2 < 4 && ! is_edge( i, j, dir2 ) ) {
00546           ++dir2;
00547         }
00548         assert( dir2 < 4 );
00549 
00550         // Create a vertex at this point
00551         //
00552         set_vertex_index( i, j, node_list_.size() );
00553         node_list_.push_back( vertex_node( i, j ) );
00554 
00555         // Create a vertex at a neighbour
00556         //
00557         unsigned i2 = i, j2 = j;
00558         move( dir, i2, j2 );
00559         assert( vertex_index( i2, j2 ) == vertex_node::null_index &&
00560                 is_boundary_vertex( i2, j2 ) );
00561         set_vertex_index( i2, j2, node_list_.size() );
00562         node_list_.push_back( vertex_node( i2, j2 ) );
00563 
00564         // Trace from here, going both ways
00565         //
00566         trace_edge_chain( i, j, dir );
00567         trace_edge_chain( i, j, dir2 );
00568       }
00569     }
00570   }
00571 
00572 #ifndef NDEBUG
00573   // Verify the integrity of the vertex graph
00574   bool good = true;
00575   for ( unsigned index = 0; index < node_list_.size(); ++index ) {
00576     for ( unsigned dir = 0; dir < 4; ++dir ) {
00577       if ( node(index).link[dir] != vertex_node::null_index ) {
00578         unsigned nbr = node(index).link[dir];
00579         unsigned back_dir = node(index).back_dir[dir];
00580         if ( node(nbr).link[back_dir] != index ) {
00581           vcl_cerr << "Bad back link on vertex " << index << " ("<<node(index).i
00582                    << ',' << node(index).j << " in dir " << dir << '\n'
00583                    << "  link     " << dir << " = " << node(index).link[dir] << ";\n"
00584                    << "  back_dir " << dir << " = " << node(index).back_dir[dir] << '\n';
00585         }
00586       }
00587     }
00588     assert( good );
00589   }
00590 #endif
00591 }
00592 
00593 
00594 // ---------------------------------------------------------------------------
00595 //                                                         trace face boundary
00596 
00597 template< typename LABEL_TYPE >
00598 bool
00599 vtol_extract_topology< LABEL_TYPE >::
00600 trace_face_boundary( vcl_vector<unsigned>& markers,
00601                      unsigned index,
00602                      unsigned dir,
00603                      region_type& chain_list,
00604                      LabelPoint& region_label ) const
00605 {
00606   unsigned start_index = index;
00607   LabelPoint start_left;
00608   edge_labels( node(index).i, node(index).j, dir,
00609                start_left, region_label );
00610 
00611 #ifdef DEBUG
00612   vcl_cout << "start left, region label: " << (int) start_left << ' '
00613            << (int)region_label << " ; i,j: " << node(index).i << ' '
00614            << node(index).j << " ; index,dir " << index << ' '
00615            << dir << " ; label(i,j) = "
00616            << (int)label_img_(node(index).i, node(index).j) << vcl_endl;
00617   if ( region_label + 1 == min_label_ ) {
00618     vcl_cout << "exiting" << vcl_endl;
00619     return false;
00620   }
00621 #endif
00622   if (!region_label.valid) {
00623     return false;
00624   }
00625 
00626   // Find an interior pixel of this face based on the first edge we
00627   // encounter. The vertex (i,j) corresponds to pixel-coordinate-space
00628   // (i-0.5,j-0.5). Use this and the direction to get a point on the
00629   // right of the first edge. The delta_* store the appropriate
00630   // offsets for each direction.
00631   //
00632   static const int delta_i[4] = {  0, -1, -1,  0 };
00633   static const int delta_j[4] = {  0,  0, -1, -1 };
00634   chain_list.i = node(index).i + delta_i[dir];
00635   chain_list.j = node(index).j + delta_j[dir];
00636 
00637 #ifdef DEBUG
00638   vcl_cout << "index " << index << " dir " << dir << "  node " << node(index).i
00639            << " delta " << delta_i[dir] << vcl_endl;
00640 #endif
00641   assert( chain_list.i < label_img_.ni() );
00642   assert( chain_list.j < label_img_.nj() );
00643 
00644   do {
00645     // Mark the current direction of the current node as travelled,
00646     // and go to the next node and find the outgoing direction there.
00647 
00648     assert( ! is_marked( markers[index], dir ) );
00649     set_mark( markers[index], dir );
00650     chain_list.push_back( node(index).edgel_chain[dir] );
00651 
00652     unsigned back_dir = node(index).back_dir[ dir ];
00653     index = node(index).link[ dir ];
00654     assert( index != vertex_node::null_index );
00655 
00656     // Look from right to left, so that we are more conservative at
00657     // corner touches. That is, we will take
00658     //
00659     //     +----+
00660     //     |    |
00661     //     +----+----+
00662     //          |    |
00663     //          +----+
00664     //
00665     // as two regions instead of one figure-8 region.
00666     //
00667     // We keep the object on the right, which actually corresponds to
00668     // the standard "keep the object on the left" in a right-handed
00669     // coordinate system.
00670     //
00671     unsigned i = node(index).i;
00672     unsigned j = node(index).j;
00673     LabelPoint left, right;
00674     dir = back_dir;
00675     DBG( unsigned old_dir = dir % 4 );
00676     do {
00677       dir = (dir+3) % 4; // same as dir = dir - 1
00678 
00679       // Make sure we haven't done a full cycle.
00680       DBG( assert( dir != old_dir ) );
00681 
00682       edge_labels( i, j, dir, left, right );
00683     } while ( left == right || right != region_label );
00684 
00685   } while ( index != start_index );
00686 
00687   return true;
00688 }
00689 
00690 
00691 // ---------------------------------------------------------------------------
00692 //                                                             collect regions
00693 
00694 template< typename LABEL_TYPE >
00695 void
00696 vtol_extract_topology< LABEL_TYPE >::
00697 collect_regions( region_collection& region_list ) const
00698 {
00699   // Use put marks about which nodes (vertices) and directions have
00700   // been processed.
00701   //
00702   vcl_vector< unsigned > markers( node_list_.size(), 0 );
00703 
00704   region_list.resize( max_label_ - min_label_ + 1 );
00705 
00706   // Process each vertex, generating the boundary chains
00707   //
00708   for ( unsigned i = 0; i < node_list_.size(); ++i ) {
00709     for ( unsigned dir = 0; dir < 4; ++dir ) {
00710       if ( ! is_marked( markers[i], dir ) &&
00711            node(i).link[dir] != vertex_node::null_index ) {
00712         region_type_sptr chain = new region_type;
00713         LabelPoint label;
00714         if ( trace_face_boundary( markers, i, dir, *chain, label ) ) {
00715           assert( (label.valid) && (label.label >= min_label_) );
00716           region_list[ label.label - min_label_ ].push_back( chain );
00717         }
00718       }
00719     }
00720   }
00721 
00722 #ifndef NDEBUG
00723   // After we extract all the chains, we must have gone through every
00724   // vertex in every available direction except for the
00725   // counter-clockwise chain at the boundary (which is the border of
00726   // the "outside" region).
00727   //
00728   for ( unsigned i = 0; i < node_list_.size(); ++i ) {
00729     for ( unsigned dir = 0; dir < 4; ++dir ) {
00730       assert( node(i).link[dir] == vertex_node::null_index ||
00731               ( dir==0 && node(i).j==label_img_.nj() ) ||
00732               ( dir==1 && node(i).i==0 ) ||
00733               ( dir==2 && node(i).j==0 ) ||
00734               ( dir==3 && node(i).i==label_img_.ni() ) ||
00735               is_marked( markers[i], dir ) );
00736     }
00737   }
00738 #endif
00739 }
00740 
00741 
00742 // ---------------------------------------------------------------------------
00743 //                                                               compute faces
00744 
00745 template< typename T >
00746 void
00747 vtol_extract_topology<T>::
00748 compute_faces( vcl_vector< region_type_sptr > const& chains,
00749                vcl_vector< vtol_intensity_face_sptr >& faces,
00750                data_image_type const* data_img ) const
00751 {
00752   assert( chains.size() > 0 );
00753 
00754   // Determine the containment tree by repeatedly adding the chains
00755   // into a tree. The adding process makes sure that the chain falls
00756   // into the appropriate place in the containment hierarchy.
00757 
00758   chain_tree_node universe( 0 );
00759   for ( unsigned i = 0; i < chains.size(); ++i ) {
00760     universe.add( chains[i] );
00761   }
00762 
00763   // If we have a data image, use it to add digital region information
00764   // to the face
00765   //
00766   if ( data_img ) {
00767     finder_type* finder = new finder_type( label_img_, vil_region_finder_4_conn );
00768     add_faces( faces, finder, data_img, &universe );
00769     delete finder;
00770   }
00771   else {
00772     add_faces( faces, 0, 0, &universe );
00773   }
00774 }
00775 
00776 // ---------------------------------------------------------------------------
00777 //                                                                   add faces
00778 
00779 template <typename T>
00780 void
00781 vtol_extract_topology<T>::
00782 add_faces( vcl_vector<vtol_intensity_face_sptr>& faces,
00783            typename vtol_extract_topology<T>::finder_type* find,
00784            data_image_type const* img,
00785            typename vtol_extract_topology<T>::chain_tree_node* node,
00786            bool even_level ) const
00787 {
00788   if ( even_level ) {
00789     faces.push_back( node->make_face( find, img ) );
00790   }
00791   for ( unsigned i = 0; i < node->children.size(); ++i ) {
00792     add_faces( faces, find, img, node->children[i], !even_level );
00793   }
00794 }
00795 
00796 // ---------------------------------------------------------------------------
00797 //                                                                    contains
00798 
00799 
00800 template <typename T>
00801 bool
00802 vtol_extract_topology<T>::
00803 contains( region_type_sptr a, region_type_sptr b )
00804 {
00805   assert( b );
00806   if ( !a ) {
00807     return true;
00808   }
00809   else {
00810     // Odd number of crossings => inside.
00811     //
00812     unsigned num_crossings = 0;
00813     for ( unsigned i = 0; i < a->size(); ++i ) {
00814       num_crossings += num_crosses_x_pos_ray( b->i, b->j, *(*a)[i] );
00815     }
00816     return ( num_crossings % 2 ) == 1;
00817   }
00818 }
00819 
00820 
00821 // ---------------------------------------------------------------------------
00822 //                                                       num crosses x pos ray
00823 
00824 template <typename T>
00825 unsigned
00826 vtol_extract_topology<T>::
00827 num_crosses_x_pos_ray( double x, double y, vdgl_edgel_chain const& chain )
00828 {
00829   if ( chain.size() < 2 )
00830     return 0;
00831 
00832   // The edges are vertical or horizontal line segments. Leverage that
00833   // in doing the intersection check. Assume that the ray will *not*
00834   // cross on a vertex.
00835   //
00836   unsigned count = 0;
00837   for ( unsigned int i = 0; i+1 < chain.size(); ++i ) {
00838     vdgl_edgel const* e0 = &chain[i];
00839     vdgl_edgel const* e1 = &chain[i+1];
00840     assert( e0->y() != y && e1->y() != y );
00841     if ( ( e0->y() < y && y < e1->y() ) ||
00842         ( e1->y() < y && y < e0->y() ) ) {
00843       assert( e0->x() == e1->x() );
00844       assert( e0->x() != x );
00845       if ( e0->x() > x ) {
00846         ++count;
00847       }
00848     }
00849   }
00850 
00851   return count;
00852 }
00853 
00854 
00855 // ---------------------------------------------------------------------------
00856 //                                                                smooth chain
00857 
00858 template <typename T>
00859 vdgl_edgel_chain_sptr
00860 vtol_extract_topology<T>::
00861 smooth_chain( vdgl_edgel_chain_sptr chain,
00862               unsigned int num_pts ) const
00863 {
00864   // Can't smooth over more points than we have
00865   if ( num_pts > chain->size() ) {
00866     num_pts = chain->size();
00867   }
00868 
00869   // Need at least two points to fit a line
00870   if ( num_pts < 2)
00871     return chain;
00872 
00873   vdgl_edgel_chain_sptr new_chain = new vdgl_edgel_chain;
00874 
00875   vgl_line_2d_regression<double> reg;
00876 
00877   // These store the indices of the edgel points used to estimate the
00878   // line. The points in [fit_start,fit_end) are used.
00879   //
00880   unsigned fit_start;
00881   unsigned fit_end;
00882 
00883   // This is the index of the edgel point we are smoothing.
00884   //
00885   unsigned curr_ind = 0;
00886 
00887   vgl_point_2d<double> pt1, pt2;
00888   vgl_vector_2d<double> dir;
00889   double slope;
00890 
00891   // Add the first few points to get the first line. We'll use this
00892   // line as the smoothing estimate for the first few edgel pixels. We
00893   // don't add the first point, because we will constrain the line to
00894   // pass through it. This will make sure that the vertices don't move
00895   // because of the smoothing.
00896   //
00897   for ( fit_end = 1; fit_end < num_pts; ++fit_end ) {
00898     reg.increment_partial_sums( chain->edgel(fit_end).x(),
00899                                 chain->edgel(fit_end).y() );
00900   }
00901   assert( reg.get_n_pts() + 1 == num_pts );
00902   if ( !reg.fit_constrained( chain->edgel(0).x(), chain->edgel(0).y() ) ) {
00903     vcl_cerr << "line fit failed at start\n";
00904   }
00905 
00906   // Project the first half of the points used in estimating the line
00907   // onto the line to get the smoothed positions.
00908   //
00909   reg.get_line().get_two_points( pt1, pt2 );
00910   dir = reg.get_line().direction();
00911   slope = reg.get_line().slope_degrees();
00912   for ( ; curr_ind < (fit_end+1)/2; ++curr_ind ) {
00913     pt2.set( chain->edgel(curr_ind).x(), chain->edgel(curr_ind).y() );
00914     pt2 = pt1 + dot_product( pt2-pt1, dir ) * dir;
00915     new_chain->add_edgel( vdgl_edgel( pt2.x(), pt2.y(), -1, slope ) );
00916   }
00917 
00918   // We have all the points from [1,fit_end_] in the regression object.
00919   //
00920   fit_start = 1;
00921 
00922   while ( fit_end + 1 < chain->size() ) {
00923     // Add one more point to get num_pts points
00924     //
00925     reg.increment_partial_sums( chain->edgel(fit_end).x(),
00926                                 chain->edgel(fit_end).y() );
00927     ++fit_end;
00928 
00929     assert( reg.get_n_pts() == num_pts );
00930 
00931     // Estimate a new line
00932     //
00933     if ( !reg.fit() ) {
00934       vcl_cerr << "line fit failed at " << fit_start << '-' << fit_end << '\n';
00935     }
00936 
00937     // Project the current point onto the line to get the smoothed position
00938     //
00939     reg.get_line().get_two_points( pt1, pt2 );
00940     dir = reg.get_line().direction();
00941     slope = reg.get_line().slope_degrees();
00942     pt2.set( chain->edgel(curr_ind).x(), chain->edgel(curr_ind).y() );
00943     pt2 = pt1 + dot_product( pt2-pt1, dir ) * dir;
00944     new_chain->add_edgel( vdgl_edgel( pt2.x(), pt2.y(), -1, slope ) );
00945     ++curr_ind;
00946 
00947     // Remove the start point in preparation for the next line segment
00948     //
00949     reg.decrement_partial_sums( chain->edgel(fit_start).x(),
00950                                 chain->edgel(fit_start).y() );
00951     ++fit_start;
00952   }
00953 
00954   assert( reg.get_n_pts() + 1 == num_pts );
00955 
00956   // The special case when we are using all the points to fit a line,
00957   // the end point is already in the regression object when it
00958   // normally wouldn't be. We have to replace it with the starting
00959   // point (which is not in the regression object) so we can do a
00960   // constrained fit.
00961   //
00962   if ( num_pts == chain->size() ) {
00963     assert( fit_end == chain->size() );
00964     assert( fit_start == 1 );
00965     --fit_end;
00966     reg.decrement_partial_sums( chain->edgel(fit_end).x(),
00967                                 chain->edgel(fit_end).y() );
00968     reg.increment_partial_sums( chain->edgel(0).x(),
00969                                 chain->edgel(0).y() );
00970   }
00971 
00972   // We have num_pts-1 points in the regression object. We do a
00973   // constrained fit to make sure we interpolate the end vertex, and
00974   // use this line for the final few edgel points.
00975   //
00976   if ( !reg.fit_constrained( chain->edgel(fit_end).x(),
00977                             chain->edgel(fit_end).y() ) ) {
00978     vcl_cerr << "line fit failed at end\n";
00979   }
00980 
00981   dir = reg.get_line().direction();
00982   reg.get_line().get_two_points( pt1, pt2 );
00983   slope = reg.get_line().slope_degrees();
00984   for ( ; curr_ind <= fit_end; ++curr_ind ) {
00985     pt2.set( chain->edgel(curr_ind).x(), chain->edgel(curr_ind).y() );
00986     pt2 = pt1 + dot_product( pt2-pt1, dir )*dir;
00987     new_chain->add_edgel( vdgl_edgel( pt2.x(), pt2.y(), -1, slope ) );
00988   }
00989 
00990   return new_chain;
00991 }
00992 
00993 #endif // vtol_extract_topology_txx_