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 boundary 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. Prerequisite: 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         }
00258         else {
00259           ++itr;
00260         }
00261       }
00262       this->children.push_back( new_node );
00263     }
00264 
00265 
00266     // Create a face from the regions at this node and its children.
00267     //
00268     vtol_intensity_face_sptr
00269     make_face( finder_type* find, data_image_type const* img ) const
00270     {
00271       vcl_vector< vtol_one_chain_sptr > face_chains;
00272       face_chains.push_back( region->make_one_chain() );
00273       for ( unsigned i = 0; i < children.size(); ++i ) {
00274         face_chains.push_back( children[i]->region->make_one_chain() );
00275       }
00276       if ( find ) {
00277         assert( img );
00278 
00279         vcl_vector<unsigned> ri, rj;
00280         find->same_int_region( region->i, region->j, ri, rj );
00281         assert( ri.size() == rj.size() && !ri.empty() );
00282 
00283         vcl_vector<float> x, y;
00284         vcl_vector<unsigned short> intensity;
00285         for ( unsigned c = 0; c < ri.size(); ++c ) {
00286           x.push_back( static_cast<float>(ri[c]) );
00287           y.push_back( static_cast<float>(rj[c]) );
00288           intensity.push_back( (*img)( ri[c], rj[c] ) );
00289         }
00290         vdgl_digital_region r( x.size(), &x[0], &y[0], &intensity[0]  );
00291         return new vtol_intensity_face( &face_chains, r );
00292       }
00293       else {
00294         // create a face without a digital geometry
00295         vcl_clog << "Creating region with NO pixels"  << vcl_endl;
00296         return new vtol_intensity_face( face_chains );
00297       }
00298     }
00299 
00300 
00301     void
00302     print( vcl_ostream& ostr, unsigned indent ) const
00303     {
00304       for ( unsigned i = 0; i < indent; ++i ) {
00305         ostr << ' ';
00306       }
00307       ostr << '['<<children.size()<<']';
00308       if ( ! children.empty() ) {
00309         ostr << "___\n";
00310         for ( unsigned i = 0; i < indent; ++i ) {
00311           ostr << ' ';
00312         }
00313         ostr << "      \\\n";
00314         for ( unsigned i = 0; i < children.size(); ++i ) {
00315           children[i]->print( ostr, indent+7 );
00316         }
00317       }
00318       else {
00319         ostr << '\n';
00320       }
00321     }
00322   };
00323 
00324 
00325  public: // public methods
00326 
00327   //: Prepare to extract the topology from \a image.
00328   vtol_extract_topology( label_image_type const& image,
00329                          vtol_extract_topology_params const& params = vtol_extract_topology_params() );
00330 
00331   //: List of vertices in the segmentation
00332   //
00333   vcl_vector< vtol_vertex_2d_sptr >
00334   vertices() const;
00335 
00336   //: List of all the faces in the segmentation
00337   //
00338   // These will be intensity faces without a digital region. This
00339   // function should probably return vtol_face_2d objects, not
00340   // vtol_intensity_face objects.
00341   //
00342   vcl_vector< vtol_intensity_face_sptr >
00343   faces() const;
00344 
00345   //: List of all the faces in the segmentation
00346   //
00347   // The faces will have digital regions formed using \a data_img.
00348   // \a data_img must have the same size as the label image.
00349   //
00350   vcl_vector< vtol_intensity_face_sptr >
00351   faces( data_image_type const& data_img ) const;
00352 
00353   // Adds the faces contained in the tree rooted at node. Essentially,
00354   // it will create a face from each node at an even depth. (The root
00355   // node, the universe, is at an odd depth.) At an even depth, the
00356   // chain for the node represents the outer boundary while the chains
00357   // of the children represent inner boundaries. (Grandchildren
00358   // represent the outer boundaries of smaller faces.)
00359   //
00360 
00361   void
00362   add_faces( vcl_vector<vtol_intensity_face_sptr>& faces,
00363              finder_type* find,
00364              data_image_type const* img,
00365              chain_tree_node* node,
00366              bool even_level = false ) const ;
00367 
00368 
00369   // Returns true if the region bounded by a contains the region bounded
00370   // by b.  Assumes that (1) the chains are cycles and thus bound a
00371   // region, (2) that a containment relationship holds (i.e. no partial
00372   // overlap). A special case allows for a "universe": if a is null,
00373   // then the function will return true. Currently, b cannot be null.
00374   //
00375   static
00376   bool
00377   contains( region_type_sptr a, region_type_sptr b );
00378 
00379   // Computes the number of times that a ray in the positive x direction
00380   // originating from (x,y) intersects the edgel chain.
00381   //
00382   // The implementation assumes that (1) the neighbouring edgels form
00383   // vertical or horizontal lines only, and (2) the ray does not go
00384   // through a vertex (i.e. \a y is not equal to the y-coordinate of any
00385   // edgel).
00386   //
00387   static
00388   unsigned
00389   num_crosses_x_pos_ray( double x, double y, vdgl_edgel_chain const& chain );
00390 
00391   // Smooths an edgel chain by fitting a line to the local
00392   // neighbourhood and projecting onto that line
00393   //
00394   vdgl_edgel_chain_sptr
00395   smooth_chain( vdgl_edgel_chain_sptr chain,
00396                 unsigned int num_pts ) const;
00397 
00398  private:   // internal classes and constants
00399 
00400   //: Queries into label_img_ return either (label, true) or (0, false)
00401   // ...this avoids using one of the possible labels as an off-image flag
00402 
00403   struct LabelPoint
00404   {
00405     LABEL_TYPE label;
00406     bool valid;
00407     LabelPoint(): label(0), valid(false) {}
00408     LabelPoint(LABEL_TYPE const& lt, bool v): label(lt), valid(v) {}
00409     bool operator==( LabelPoint const& lp ) {
00410       return (lp.valid == this->valid) && ( (lp.valid) ? lp.label == this->label : true );
00411     }
00412     bool operator!=( LabelPoint const& lp ) {
00413       return !(*this == lp);
00414     }
00415   };
00416 
00417   typedef vtol_extract_topology_edgel_chain edgel_chain;
00418   typedef vbl_smart_ptr< edgel_chain > edgel_chain_sptr;
00419 
00420   //: Image of indices into the vertex node list
00421   typedef vil_image_view< unsigned > index_image_type;
00422 
00423   // For VC6, to give access to the constants
00424   friend struct vtol_extract_topology_vertex_node;
00425 
00426   // Allow the test harness to call on the "internal" member function is_edge()
00427   // for thorough testing.
00428   //
00429   friend class test_vtol_extract_topology;
00430 
00431   //: Determine the max and min labels in the label image
00432   void
00433   compute_label_range();
00434 
00435   //: The label at pixel position (i,j).
00436   //
00437   // If (i,j) falls inside the input image, return the value of pixel (i,j) of the
00438   // input image. Otherwise, it will return min_label_ - 1.
00439   //
00440   LabelPoint
00441   label( unsigned i, unsigned j ) const;
00442 
00443   //: Is this a vertex bordering at least three regions?
00444   bool
00445   is_junction_vertex( unsigned i, unsigned j ) const;
00446 
00447   //: Is this a non-interior vertex?
00448   bool
00449   is_boundary_vertex( unsigned i, unsigned j ) const;
00450 
00451   //: True iff there is an edge from vertex (i,j) in direction dir.
00452   //
00453   // The directions are
00454   // \verbatim
00455   //             3
00456   //
00457   //             ^
00458   //             |
00459   //             |
00460   //    2 <--- (i,j) ---> 0           +----->
00461   //             |                    |       i
00462   //             |                    |
00463   //             v                    v
00464   //                                    j
00465   //             1
00466   // \endverbatim
00467   //
00468   // The `crack' between two pixels forms an edge iff the pixels have
00469   // different labels.
00470   //
00471   bool
00472   is_edge( unsigned i, unsigned j, unsigned dir ) const;
00473 
00474   //: The labels of the pixels on either side of the edge.
00475   //
00476   // Left and right correspond to the displayed image. That is, left
00477   // and right are defined on a left-handed coordinate system.
00478   //
00479   void
00480   edge_labels( unsigned i, unsigned j, unsigned dir,
00481                LabelPoint& left, LabelPoint& right ) const;
00482 
00483   //: The node index of the vertex at coordinate (i,j)
00484   unsigned
00485   vertex_index( unsigned i, unsigned j ) const;
00486 
00487   //: Marks vertex (i,j) as having the given index
00488   void
00489   set_vertex_index( unsigned i, unsigned j, unsigned index );
00490 
00491   //: The vertex node structure given by \a index
00492   vtol_extract_topology_vertex_node&
00493   node( unsigned index );
00494 
00495   //: The vertex node structure given by \a index
00496   vtol_extract_topology_vertex_node const&
00497   node( unsigned index ) const;
00498 
00499   //: Move ( \a i, \a j ) in direction \a dir
00500   void
00501   move( unsigned dir, unsigned& i, unsigned& j );
00502 
00503   //: Mark direction \a dir as travelled
00504   //
00505   // \a marker is updated to reflect the new movement.
00506   //
00507   void
00508   set_mark( unsigned& marker, unsigned dir ) const;
00509 
00510   //: Check if a direction has been travelled
00511   //
00512   // The directions that have been travelled is extracted from \a marker.
00513   //
00514   bool
00515   is_marked( unsigned marker, unsigned dir ) const;
00516 
00517   //: Add an edge chain into the graph structure.
00518   //
00519   // This will trace the edgel chain starting at the vertex at (i,j),
00520   // starting in direction \a dir. It will update the graph nodes to
00521   // reflect the new chain.
00522   //
00523   void
00524   trace_edge_chain( unsigned i, unsigned j, unsigned dir );
00525 
00526   //: This will create the full graph structure of the vertices.
00527   //
00528   // It will find all the vertices and connecting edges necessary to
00529   // describe the topology of the segmentation. It will try to create
00530   // as few vertices as possible to do so.
00531   //
00532   void
00533   construct_topology();
00534 
00535   //: Trace the boundary of a region starting at vertex \a index going \a dir.
00536   //
00537   // It will output, in \a chain, the one chain of edges corresponding
00538   // to the closed contour bounding a region. It will also return the
00539   // label of the enclosed region.
00540   //
00541   // It will only trace regions interior to the image. That is, it
00542   // will not trace a "face" containing the region outside the image
00543   // boundaries. The return value will indicate this. A return value
00544   // of "true" indicates that the a region was successfully extracted,
00545   // and that \a chain_list and \a region_label are valid. A return
00546   // value of "false" indicates that a region was not traced.
00547   //
00548   bool
00549   trace_face_boundary( vcl_vector<unsigned>& markers,
00550                        unsigned index,
00551                        unsigned dir,
00552                        region_type& chain,
00553                        LabelPoint& region_label ) const;
00554 
00555   typedef vcl_vector< vcl_vector< region_type_sptr > > region_collection;
00556 
00557   //: Trace the boundary curves and collect up a set of regions.
00558   //
00559   // The output in \a out_region_list is a set of regions indexed by
00560   // the label of the region. So, out_region_list[x] will be a set of
00561   // closed boundaries bounding regions with label x.
00562   //
00563   void
00564   collect_regions( region_collection& out_region_list ) const;
00565 
00566   //: Create a set of faces given a set of boundary chains.
00567   //
00568   // The boundary chains must all bound regions with the same
00569   // label. This routine will determine which chains should be
00570   // interior boundaries and which should be exterior. It will add a
00571   // set of faces to \a faces based on this determination.
00572   //
00573   // Each face so added will form a single connected component.
00574   //
00575   // If data_img is not null, each face will have a digital region
00576   // (vdgl_digital_region).
00577   //
00578   void
00579   compute_faces( vcl_vector< region_type_sptr > const& chains,
00580                  vcl_vector< vtol_intensity_face_sptr >& faces,
00581                  data_image_type const* data_img ) const;
00582 
00583  private: // internal data
00584 
00585   //: The input label image
00586   label_image_type const& label_img_;
00587 
00588   //: Parameters
00589   vtol_extract_topology_params params_;
00590 
00591   //: The label ranges in the image
00592   LABEL_TYPE min_label_, max_label_;
00593 
00594   //: List of vertices (which form the nodes of the graph)
00595   vcl_vector< vtol_extract_topology_vertex_node > node_list_;
00596 
00597   //: Quick conversion from vertex coordinates to vertex node indices
00598   index_image_type index_img_;
00599 };
00600 
00601 #endif // vtol_extract_topology_h_