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