contrib/gel/vtol/algo/vtol_extract_topology.h
Go to the documentation of this file.
00001 #ifndef vtol_extract_topology_h_
00002 #define vtol_extract_topology_h_
00003 //:
00004 // \file
00005 // \author Amitha Perera
00006 // \date   July 2003
00007 //
00008 // \verbatim
00009 //  Modifications:
00010 //   29sep04 - templated over label image type for >256 labels;
00011 //             some bug fixes found w/ Amitha [roddy collins]
00012 //
00013 // \endverbatim
00014 
00015 #include <vxl_config.h>
00016 #include <vcl_vector.h>
00017 #include <vcl_cassert.h>
00018 #include <vcl_iostream.h>
00019 
00020 #include <vbl/vbl_ref_count.h>
00021 
00022 #include <vil/vil_image_view.h>
00023 #include <vil/algo/vil_region_finder.h>
00024 
00025 #include <vdgl/vdgl_edgel_chain_sptr.h>
00026 #include <vdgl/vdgl_digital_region.h>
00027 
00028 #include <vtol/vtol_vertex_2d_sptr.h>
00029 #include <vtol/vtol_intensity_face.h>
00030 #include <vtol/vtol_intensity_face_sptr.h>
00031 #include <vtol/vtol_edge_2d_sptr.h>
00032 #include <vtol/vtol_one_chain_sptr.h>
00033 
00034 //: A class in the test harness that will test some of the internal methods
00035 //
00036 class test_vtol_extract_topology;
00037 
00038 //: Some data types, further aliased in the definitions and implementations.
00039 
00040 typedef vxl_byte vtol_extract_topology_data_pixel_type;
00041 typedef vil_image_view< vtol_extract_topology_data_pixel_type >
00042          vtol_extract_topology_data_image_type;
00043 
00044 //: Controls the behaviour of vtol_extract_topology
00045 struct vtol_extract_topology_params
00046 {
00047   //: The number of pixels used in smoothing.
00048   //
00049   // The edgel curves will be smoothed by fitting a line at each edgel
00050   // point to the \a num_for_smooth neighbouring edgel points and
00051   // projecting onto that line. A value of 0 means no smoothing will
00052   // occur.
00053   //
00054   // Default: 0 (no smoothing)
00055   //
00056   unsigned num_for_smooth;
00057 
00058   vtol_extract_topology_params&
00059   set_smooth( unsigned s ) { num_for_smooth = s; return *this; }
00060 
00061   // Please don't add a constructor that takes arguments.
00062 
00063   //: Construct with the default values for the parameters.
00064   //
00065   // The constructor does not take parameters by design. Use the
00066   // explicit set_* functions to set the parameters you wish to
00067   // change. This will make code more robust against changes to the
00068   // code and parameter set, because we don't have a bunch of unnamed
00069   // arguments to change or worry about.
00070   vtol_extract_topology_params() : num_for_smooth( 0 ) {}
00071 };
00072 
00073 //: Stores an edgel chain and a corresponding topological edge
00074 //
00075 // Although the edgel chain can be recovered from the edge, we will
00076 // need the edgel chain often enough that it is worthwhile to cache
00077 // the information.
00078 //
00079 struct vtol_extract_topology_edgel_chain
00080   : public vbl_ref_count
00081 {
00082   vdgl_edgel_chain_sptr chain;
00083   vtol_edge_2d_sptr edge;
00084 };
00085 
00086 //: Stores the boundary of a region
00087 //
00088 // This stores the one chain corresponding to a complete region
00089 // boundary. It also stores a point completely inside that
00090 // region. This point is used to perform containment checks between
00091 // regions.
00092 //
00093 // \sa contains
00094 //
00095 class vtol_extract_topology_region_type
00096   : public vbl_ref_count
00097 {
00098  public:
00099   typedef vbl_smart_ptr< vtol_extract_topology_edgel_chain > edgel_chain_sptr;
00100 
00101   //: Add an edge to this region
00102   void
00103   push_back( edgel_chain_sptr chain );
00104 
00105   //: The number of edges in the boundary
00106   unsigned
00107   size() const;
00108 
00109   //: Extract segment \a i of the boundary one chain
00110   vdgl_edgel_chain_sptr const&
00111   operator[]( unsigned i ) const;
00112 
00113   //: Create a vtol_one_chain describing the boundary
00114   vtol_one_chain_sptr
00115   make_one_chain() const;
00116 
00117   //: Location of a pixel inside the region
00118   unsigned i, j;
00119 
00120  private:
00121 
00122   //: The list of bounday edges (which are edgel chains)
00123   vcl_vector< edgel_chain_sptr > list_;
00124 };
00125 
00126 //: A node in the graph of vertices.
00127 //
00128 // The links correspond to edges between the vertices. Each vertex
00129 // is located at the corner of the pixel.
00130 //
00131 struct vtol_extract_topology_vertex_node
00132 {
00133   //: Create a node for vertex at (i,j).
00134   vtol_extract_topology_vertex_node( unsigned i, unsigned j );
00135 
00136   //: Location
00137   unsigned i, j;
00138 
00139   //: vtol vertex in pixel coordinates.
00140   vtol_vertex_2d_sptr vertex;
00141 
00142   //: Neighbouring vertices in the graph.
00143   unsigned link[4];
00144 
00145   //: Direction in which we exit the neighbouring node to get back to this node.
00146   //
00147   // That is, (this->link)[n].link[ this->back_dir[n] ] == indexof(this).
00148   //
00149   unsigned back_dir[4];
00150 
00151   //: Edgel chains leading to neighbouring vertices.
00152   vbl_smart_ptr< vtol_extract_topology_edgel_chain > edgel_chain[4];
00153 
00154   //: Null index value.
00155   //
00156   // A vertex with an index value >= this value does not correspond to
00157   // a node in the graph.
00158   //
00159   static const unsigned null_index   VCL_STATIC_CONST_INIT_INT_DECL( unsigned(-2) );
00160 
00161   //: "Processed" index value
00162   //
00163   // This is used to indicate that the boundary edge following went
00164   // through a vertex.
00165   //
00166   static const unsigned done_index   VCL_STATIC_CONST_INIT_INT_DECL( unsigned(-1) );
00167 };
00168 
00169 //: Extracts the topology from a segmentation label image.
00170 //
00171 // This class contains the functionality to extract a set of regions
00172 // from an image of region labels. Vertices are created only at
00173 // junctions where possible. The edges connecting these vertices
00174 // follow the boundary of the regions, and hence are often not
00175 // straight lines.
00176 //
00177 // Vertices are formed at the corners of pixels, and edges are formed
00178 // along the "cracks" between pixels. In this implementation, the
00179 // vertices are indexed by the top-left corner of the pixel. That is,
00180 // if the image has M x N pixels, it will have M+1 x N+1 vertices,
00181 // indexed (0,0) to (M,N). A vertex (i,j) is positioned at
00182 // (i-0.5,j-0.5) in pixel coordinates.
00183 //
00184 // Now templated; LABEL_TYPE can be vxl_byte, vxl_uint_16, etc.
00185 //
00186 
00187 template< typename LABEL_TYPE >
00188 class vtol_extract_topology
00189 {
00190  public: // public types
00191 
00192   //: alias for the region type
00193   typedef vtol_extract_topology_region_type region_type;
00194   typedef vbl_smart_ptr< region_type > region_type_sptr;
00195 
00196   //: Input label image type
00197   typedef vil_image_view< LABEL_TYPE > label_image_type;
00198   typedef vil_region_finder< LABEL_TYPE > finder_type;
00199 
00200   //: Input data image type
00201   typedef vtol_extract_topology_data_image_type data_image_type;
00202 
00203 
00204   // Holds the tree describing the containment structure for a set of
00205   // chains bounding regions with the same label.
00206   //
00207   struct chain_tree_node
00208   {
00209     // The region for the current node. Can be null only for the root
00210     // node. The root node represents the universe. All regions are
00211     // contained in the universe.
00212     //
00213     region_type_sptr region;
00214 
00215     // The regions from all child nodes are spatially contained in the
00216     // region for this node.
00217     //
00218     vcl_vector<chain_tree_node*> children;
00219 
00220     chain_tree_node( region_type_sptr in_region ) : region( in_region ) {}
00221 
00222     ~chain_tree_node() {
00223       typename vcl_vector<chain_tree_node*>::const_iterator itr;
00224       for (itr = children.begin(); itr != children.end(); ++itr ) {
00225         delete *itr;
00226       }
00227     }
00228 
00229     // Add a new region below this node. Prerequiste: the new region is
00230     // contained within this chain.
00231     //
00232     void
00233     add( region_type_sptr new_region )
00234     {
00235       typename vcl_vector<chain_tree_node*>::iterator itr;
00236 
00237       // First, determine if it should go further down the tree. If so,
00238       // add it to the appropriate child and exit immediately.
00239       //
00240       for ( itr = children.begin(); itr != children.end(); ++itr ) {
00241         if ( vtol_extract_topology<LABEL_TYPE>::contains( (*itr)->region, new_region ) ) {
00242           (*itr)->add( new_region );
00243           return;
00244         }
00245       }
00246 
00247       // It belongs at this level. Create a new node for it, and find out
00248       // if this new node swallows up any of the existing children. Then
00249       // add the new node as a child of this.
00250       //
00251       chain_tree_node* new_node = new chain_tree_node( new_region );
00252       itr = children.begin();
00253       while ( itr != children.end() ) {
00254         if ( contains( new_region, (*itr)->region ) ) {
00255           new_node->children.push_back( *itr );
00256           itr = this->children.erase( itr );
00257         } else {
00258           ++itr;
00259         }
00260       }
00261       this->children.push_back( new_node );
00262     }
00263 
00264 
00265     // Create a face from the regions at this node and its children.
00266     //
00267     vtol_intensity_face_sptr
00268     make_face( finder_type* find, data_image_type const* img ) const
00269     {
00270       vcl_vector< vtol_one_chain_sptr > face_chains;
00271       face_chains.push_back( region->make_one_chain() );
00272       for ( unsigned i = 0; i < children.size(); ++i ) {
00273         face_chains.push_back( children[i]->region->make_one_chain() );
00274       }
00275       if ( find ) {
00276         assert( img );
00277 
00278         vcl_vector<unsigned> ri, rj;
00279         find->same_int_region( region->i, region->j, ri, rj );
00280         assert( ri.size() == rj.size() && !ri.empty() );
00281 
00282         vcl_vector<float> x, y;
00283         vcl_vector<unsigned short> intensity;
00284         for ( unsigned c = 0; c < ri.size(); ++c ) {
00285           x.push_back( static_cast<float>(ri[c]) );
00286           y.push_back( static_cast<float>(rj[c]) );
00287           intensity.push_back( (*img)( ri[c], rj[c] ) );
00288         }
00289         vdgl_digital_region r( x.size(), &x[0], &y[0], &intensity[0]  );
00290         return new vtol_intensity_face( &face_chains, r );
00291       } else {
00292         // create a face without a digital geometry
00293         vcl_clog << "Creating region with NO pixels"  << vcl_endl;
00294         return new vtol_intensity_face( face_chains );
00295       }
00296     }
00297 
00298 
00299     void
00300     print( vcl_ostream& ostr, unsigned indent ) const
00301     {
00302       for ( unsigned i = 0; i < indent; ++i ) {
00303         ostr << ' ';
00304       }
00305       ostr << '['<<children.size()<<']';
00306       if ( ! children.empty() ) {
00307         ostr << "___\n";
00308         for ( unsigned i = 0; i < indent; ++i ) {
00309           ostr << ' ';
00310         }
00311         ostr << "      \\\n";
00312         for ( unsigned i = 0; i < children.size(); ++i ) {
00313           children[i]->print( ostr, indent+7 );
00314         }
00315       } else {
00316         ostr << '\n';
00317       }
00318     }
00319   };
00320 
00321 
00322  public: // public methods
00323 
00324   //: Prepare to extract the topology from \a image.
00325   vtol_extract_topology( label_image_type const& image,
00326                          vtol_extract_topology_params const& params = vtol_extract_topology_params() );
00327 
00328   //: List of vertices in the segmentation
00329   //
00330   vcl_vector< vtol_vertex_2d_sptr >
00331   vertices() const;
00332 
00333   //: List of all the faces in the segmentation
00334   //
00335   // These will be intensity faces without a digital region. This
00336   // function should probably return vtol_face_2d objects, not
00337   // vtol_intensity_face objects.
00338   //
00339   vcl_vector< vtol_intensity_face_sptr >
00340   faces() const;
00341 
00342   //: List of all the faces in the segmentation
00343   //
00344   // The faces will have digital regions formed using \a data_img.
00345   // \a data_img must have the same size as the label image.
00346   //
00347   vcl_vector< vtol_intensity_face_sptr >
00348   faces( data_image_type const& data_img ) const;
00349 
00350   // Adds the faces contained in the tree rooted at node. Essentially,
00351   // it will create a face from each node at an even depth. (The root
00352   // node, the universe, is at an odd depth.) At an even depth, the
00353   // chain for the node represents the outer boundary while the chains
00354   // of the children represent inner boundaries. (Grandchildren
00355   // represent the outer boundaries of smaller faces.)
00356   //
00357 
00358   void
00359   add_faces( vcl_vector<vtol_intensity_face_sptr>& faces,
00360              finder_type* find,
00361              data_image_type const* img,
00362              chain_tree_node* node,
00363              bool even_level = false ) const ;
00364 
00365 
00366   // Returns true if the region bounded by a contains the region bounded
00367   // by b.  Assumes that (1) the chains are cycles and thus bound a
00368   // region, (2) that a containment relationship holds (i.e. no partial
00369   // overlap). A special case allows for a "universe": if a is null,
00370   // then the function will return true. Currently, b cannot be null.
00371   //
00372   static
00373   bool
00374   contains( region_type_sptr a, region_type_sptr b );
00375 
00376   // Computes the number of times that a ray in the positive x direction
00377   // originating from (x,y) intersects the edgel chain.
00378   //
00379   // The implementation assumes that (1) the neighbouring edgels form
00380   // vertical or horizontal lines only, and (2) the ray does not go
00381   // through a vertex (i.e. \a y is not equal to the y-coordinate of any
00382   // edgel).
00383   //
00384   static
00385   unsigned
00386   num_crosses_x_pos_ray( double x, double y, vdgl_edgel_chain const& chain );
00387 
00388   // Smoothes an edgel chain by fitting a line to the local
00389   // neighbourhood and projecting onto that line
00390   //
00391   vdgl_edgel_chain_sptr
00392   smooth_chain( vdgl_edgel_chain_sptr chain,
00393                 unsigned int num_pts ) const;
00394 
00395  private:   // internal classes and constants
00396 
00397   //: Queries into label_img_ return either (label, true) or (0, false)
00398   // ...this avoids using one of the possible labels as an off-image flag
00399 
00400   struct LabelPoint
00401   {
00402     LABEL_TYPE label;
00403     bool valid;
00404     LabelPoint(): label(0), valid(false) {}
00405     LabelPoint(LABEL_TYPE const& lt, bool v): label(lt), valid(v) {}
00406     bool operator==( LabelPoint const& lp ) {
00407       return (lp.valid == this->valid) && ( (lp.valid) ? lp.label == this->label : true );
00408     }
00409     bool operator!=( LabelPoint const& lp ) {
00410       return !(*this == lp);
00411     }
00412   };
00413 
00414   typedef vtol_extract_topology_edgel_chain edgel_chain;
00415   typedef vbl_smart_ptr< edgel_chain > edgel_chain_sptr;
00416 
00417   //: Image of indices into the vertex node list
00418   typedef vil_image_view< unsigned > index_image_type;
00419 
00420   // For VC6, to give access to the constants
00421   friend struct vtol_extract_topology_vertex_node;
00422 
00423   // Allow the test harness to call on the "internal" member function is_edge()
00424   // for thorough testing.
00425   //
00426   friend class test_vtol_extract_topology;
00427 
00428   //: Determine the max and min labels in the label image
00429   void
00430   compute_label_range();
00431 
00432   //: The label at pixel position (i,j).
00433   //
00434   // If (i,j) falls inside the input image, return the value of pixel (i,j) of the
00435   // input image. Otherwise, it will return min_label_ - 1.
00436   //
00437   LabelPoint
00438   label( unsigned i, unsigned j ) const;
00439 
00440   //: Is this a vertex bordering at least three regions?
00441   bool
00442   is_junction_vertex( unsigned i, unsigned j ) const;
00443 
00444   //: Is this a non-interior vertex?
00445   bool
00446   is_boundary_vertex( unsigned i, unsigned j ) const;
00447 
00448   //: True iff there is an edge from vertex (i,j) in direction dir.
00449   //
00450   // The directions are
00451   // \verbatim
00452   //             3
00453   //
00454   //             ^
00455   //             |
00456   //             |
00457   //    2 <--- (i,j) ---> 0           +----->
00458   //             |                    |       i
00459   //             |                    |
00460   //             v                    v
00461   //                                    j
00462   //             1
00463   // \endverbatim
00464   //
00465   // The `crack' between two pixels forms an edge iff the pixels have
00466   // different labels.
00467   //
00468   bool
00469   is_edge( unsigned i, unsigned j, unsigned dir ) const;
00470 
00471   //: The labels of the pixels on either side of the edge.
00472   //
00473   // Left and right correspond to the displayed image. That is, left
00474   // and right are defined on a left-handed coordinate system.
00475   //
00476   void
00477   edge_labels( unsigned i, unsigned j, unsigned dir,
00478                LabelPoint& left, LabelPoint& right ) const;
00479 
00480   //: The node index of the vertex at coordinate (i,j)
00481   unsigned
00482   vertex_index( unsigned i, unsigned j ) const;
00483 
00484   //: Marks vertex (i,j) as having the given index
00485   void
00486   set_vertex_index( unsigned i, unsigned j, unsigned index );
00487 
00488   //: The vertex node structure given by \a index
00489   vtol_extract_topology_vertex_node&
00490   node( unsigned index );
00491 
00492   //: The vertex node structure given by \a index
00493   vtol_extract_topology_vertex_node const&
00494   node( unsigned index ) const;
00495 
00496   //: Move ( \a i, \a j ) in direction \a dir
00497   void
00498   move( unsigned dir, unsigned& i, unsigned& j );
00499 
00500   //: Mark direction \a dir as travelled
00501   //
00502   // \a marker is updated to reflect the new movement.
00503   //
00504   void
00505   set_mark( unsigned& marker, unsigned dir ) const;
00506 
00507   //: Check if a direction has been travelled
00508   //
00509   // The directions that have been travelled is extracted from \a marker.
00510   //
00511   bool
00512   is_marked( unsigned marker, unsigned dir ) const;
00513 
00514   //: Add an edge chain into the graph structure.
00515   //
00516   // This will trace the edgel chain starting at the vertex at (i,j),
00517   // starting in direction \a dir. It will update the graph nodes to
00518   // reflect the new chain.
00519   //
00520   void
00521   trace_edge_chain( unsigned i, unsigned j, unsigned dir );
00522 
00523   //: This will create the full graph structure of the vertices.
00524   //
00525   // It will find all the vertices and connecting edges necessary to
00526   // describe the topology of the segmentation. It will try to create
00527   // as few vertices as possible to do so.
00528   //
00529   void
00530   construct_topology();
00531 
00532   //: Trace the boundary of a region starting at vertex \a index going \a dir.
00533   //
00534   // It will output, in \a chain, the one chain of edges corresponding
00535   // to the closed contour bounding a region. It will also return the
00536   // label of the enclosed region.
00537   //
00538   // It will only trace regions interior to the image. That is, it
00539   // will not trace a "face" containing the region outside the image
00540   // boundaries. The return value will indicate this. A return value
00541   // of "true" indicates that the a region was successfully extracted,
00542   // and that \a chain_list and \a region_label are valid. A return
00543   // value of "false" indicates that a region was not traaced.
00544   //
00545   bool
00546   trace_face_boundary( vcl_vector<unsigned>& markers,
00547                        unsigned index,
00548                        unsigned dir,
00549                        region_type& chain,
00550                        LabelPoint& region_label ) const;
00551 
00552   typedef vcl_vector< vcl_vector< region_type_sptr > > region_collection;
00553 
00554   //: Trace the boundary curves and collect up a set of regions.
00555   //
00556   // The output in \a out_region_list is a set of regions indexed by
00557   // the label of the region. So, out_region_list[x] will be a set of
00558   // closed boundaries bounding regions with label x.
00559   //
00560   void
00561   collect_regions( region_collection& out_region_list ) const;
00562 
00563   //: Create a set of faces given a set of boundary chains.
00564   //
00565   // The boundary chains must all bound regions with the same
00566   // label. This routine will determine which chains should be
00567   // interior boundaries and which should be exterior. It will add a
00568   // set of faces to \a faces based on this determination.
00569   //
00570   // Each face so added will form a single connected component.
00571   //
00572   // If data_img is not null, each face will have a digital region
00573   // (vdgl_digital_region).
00574   //
00575   void
00576   compute_faces( vcl_vector< region_type_sptr > const& chains,
00577                  vcl_vector< vtol_intensity_face_sptr >& faces,
00578                  data_image_type const* data_img ) const;
00579 
00580  private: // internal data
00581 
00582   //: The input label image
00583   label_image_type const& label_img_;
00584 
00585   //: Parameters
00586   vtol_extract_topology_params params_;
00587 
00588   //: The label ranges in the image
00589   LABEL_TYPE min_label_, max_label_;
00590 
00591   //: List of vertices (which form the nodes of the graph)
00592   vcl_vector< vtol_extract_topology_vertex_node > node_list_;
00593 
00594   //: Quick conversion from vertex coordinates to vertex node indices
00595   index_image_type index_img_;
00596 };
00597 
00598 #endif // vtol_extract_topology_h_