
* apps/papers/levillain.09.ismm/classif.cc (main): Actually use the chain() routine from chain.hh instead of ad hoc code. Move code related to the construction of the 1-complex (graph) image on which the processing chain is run... (make_complex_image): ...here. * apps/papers/levillain.09.ismm/Makefile.am (classwst.pnm): Change the lambda (area) parameter to 4. --- milena/ChangeLog | 12 ++ milena/apps/papers/levillain.09.ismm/Makefile.am | 2 +- milena/apps/papers/levillain.09.ismm/classif.cc | 213 ++++++++-------------- 3 files changed, 89 insertions(+), 138 deletions(-) diff --git a/milena/ChangeLog b/milena/ChangeLog index 723a858..6fb108d 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -1,3 +1,15 @@ +2011-10-05 Roland Levillain <roland@lrde.epita.fr> + + Fix and clean up apps/papers/levillain.09.ismm/classif.cc. + + * apps/papers/levillain.09.ismm/classif.cc (main): Actually use + the chain() routine from chain.hh instead of ad hoc code. + Move code related to the construction of the 1-complex (graph) + image on which the processing chain is run... + (make_complex_image): ...here. + * apps/papers/levillain.09.ismm/Makefile.am (classwst.pnm): Change + the lambda (area) parameter to 4. + 2011-10-04 Roland Levillain <roland@lrde.epita.fr> Import the WST-based classification in apps/papers/levillain.09.ismm. diff --git a/milena/apps/papers/levillain.09.ismm/Makefile.am b/milena/apps/papers/levillain.09.ismm/Makefile.am index 3b9a63c..74324ae 100644 --- a/milena/apps/papers/levillain.09.ismm/Makefile.am +++ b/milena/apps/papers/levillain.09.ismm/Makefile.am @@ -56,4 +56,4 @@ m283-s.off: m283-c.off complex$(EXEEXT) # FIXME: Adjust the lambda parameter. classwst.pnm: classseedsi.pbm classif$(EXEEXT) - ./classif$(EXEEXT) $< 5 $@ + ./classif$(EXEEXT) $< 4 $@ diff --git a/milena/apps/papers/levillain.09.ismm/classif.cc b/milena/apps/papers/levillain.09.ismm/classif.cc index d5bb052..a38950f 100644 --- a/milena/apps/papers/levillain.09.ismm/classif.cc +++ b/milena/apps/papers/levillain.09.ismm/classif.cc @@ -29,9 +29,8 @@ #include <vector> #include <mln/core/image/image2d.hh> -#include <mln/core/routine/duplicate.hh> #include <mln/core/alias/neighb2d.hh> -#include <mln/core/site_set/p_queue_fast.hh> + #include <mln/labeling/blobs.hh> #include <mln/io/pbm/load.hh> #include <mln/io/pgm/save.hh> @@ -40,14 +39,13 @@ #include <mln/draw/line.hh> #include <mln/pw/all.hh> - #include <mln/value/int_u8.hh> #include <mln/core/alias/point2d.hh> #include <mln/core/site_set/p_faces.hh> #include <mln/core/image/complex_image.hh> +#include <mln/core/alias/complex_image.hh> -// FIXME: Include these elsewhere? (In complex_image.hh?) #include <mln/core/image/complex_neighborhoods.hh> #include <mln/core/image/complex_neighborhood_piter.hh> @@ -55,62 +53,27 @@ #include <mln/data/fill.hh> #include <mln/norm/l2.hh> -#include <mln/morpho/closing/area.hh> -#include <mln/morpho/closing/volume.hh> -#include <mln/morpho/closing/height.hh> -#include <mln/morpho/meyer_wst.hh> - #include <mln/labeling/colorize.hh> #include "influence_zones.hh" +#include "chain.hh" -// FIXME: Include and use chain.hh. - -int main(int argc, char* argv[]) +/** Create a 1-complex (graph) image from the connected components of + \a seeds. The values associated to a 1-face (edge) is equal to + the L2-distance between its two adjacent vertices. The values on + a 0-face (vertex) is set to 0. */ +mln::int_u8_1complex_image2d +make_complex_image(const mln::image2d<bool>& seeds) { - if (argc != 4) - { - std::cerr << "usage: " << argv[0] << " input.pbm lambda output.ppm" - << std::endl; - std::exit(1); - } - std::string input_filename = argv[1]; - unsigned lambda = atoi(argv[2]); - std::string output_filename = argv[3]; - using namespace mln; using mln::value::int_u8; - /*----------------------------------------. - | Complex + complex geometry (location). | - `----------------------------------------*/ - - /* A (simplicial) 1-complex and its adjacency graph. - - c 0 1 2 3 - r .------------------------ - | v0 e3 v3 - 0 | o-----------o v0----e3----v3 - | / \ / / \ / - | / \ / / \ / - 1 | e0 / e1 / e4 e0 e1 e4 - | / \ / / \ / - | / \ / / \ / - 2 | o-----------o v1----e2----v2 - | v1 e2 v2 - - v = vertex - e = edge - */ - - - - border::thickness = 0; - image2d<bool> seeds; - io::pbm::load(seeds, input_filename); + /*-----------------------. + | Connected components. | + `-----------------------*/ unsigned nlabels; image2d<unsigned> label = labeling::blobs(seeds, c4(), nlabels); @@ -124,6 +87,10 @@ int main(int argc, char* argv[]) #endif } + /*------------------. + | Influence zones. | + `------------------*/ + image2d<unsigned> iz = influence_zones(label, c4()); { image2d<int_u8> IZ(iz.domain()); @@ -132,10 +99,10 @@ int main(int argc, char* argv[]) io::pgm::save(IZ, "iz.pgm"); #endif } -// debug::println( (pw::value(iz) - pw::cst(1)) | iz.domain() ); - - // mk_graph + /*--------------------------------. + | Construction of the 1-complex. | + `--------------------------------*/ // Adjacency matrix. std::vector< std::vector<bool> > adj(nlabels + 1); @@ -164,14 +131,13 @@ int main(int argc, char* argv[]) } } - // end of mk_graph - const unsigned D = 1; - + // 1-complex data structure. topo::complex<D> c; typedef point2d P; + // Geometry (spatial locations) associated to the complex's faces. typedef geom::complex_geometry<D, P> G; G geom; @@ -180,7 +146,6 @@ int main(int argc, char* argv[]) typedef topo::n_face<1, D> edge; { - // 0-faces (vertices). std::vector<vertex> v; { @@ -192,7 +157,6 @@ int main(int argc, char* argv[]) v.push_back(c.add_face()); } } - std::cout << "v size = " << v.size() << std::endl; // 1-faces (edges). @@ -201,17 +165,12 @@ int main(int argc, char* argv[]) for (unsigned l = 1; l <= nlabels; ++l) for (unsigned ll = l + 1; ll <= nlabels; ++ll) if (adj[l][ll]) + // Create an edge as an oriented (``algebraic'') face. e.push_back( c.add_face(-v[l-1] + v[ll-1]) ); } - std::cout << "e size = " << e.size() << std::endl; - } - image2d<int_u8> canvas(seeds.domain()); - data::fill(canvas, 0); - - /*---------------------. | Complex-based pset. | `---------------------*/ @@ -222,9 +181,9 @@ int main(int argc, char* argv[]) | Complex-based image. | `----------------------*/ - // An image type built on a 1-complex with unsigned values on each + // An image type built on a 1-complex with int_u8 values on each // face (both vertices and edges). - typedef complex_image<D, G, unsigned> dist_ima_t; + typedef complex_image<D, G, int_u8> dist_ima_t; // Create and initialize an image based on PC. dist_ima_t dist_ima(pc); @@ -234,7 +193,11 @@ int main(int argc, char* argv[]) | Complex-based image iterators. | `--------------------------------*/ - accu::stat::max<unsigned> dist_max; + accu::stat::max<int_u8> dist_max; + + // Output image for visualization purpose only. + image2d<int_u8> canvas(seeds.domain()); + data::fill(canvas, 0); // For each edge (1-face), compute the distance between the two // adjacent vertices (0-faces). @@ -251,11 +214,7 @@ int main(int argc, char* argv[]) v.next(); mln_invariant(!v.is_valid()); - /* FIXME: Why is the norm divided by 2? Is is to have it fit on - an int_u8? If so, we shall use a canvas with a larger image - value (type e.g. int_u16) and use data::stretch() to save it as - an 8-bit image. */ - dist_ima(e) = norm::l2_distance(p1.to_vec(), p2.to_vec()) / 2; + dist_ima(e) = norm::l2_distance(p1.to_vec(), p2.to_vec()); dist_max.take(dist_ima(e)); draw::line(canvas, p1, p2, dist_ima(e)); @@ -265,91 +224,71 @@ int main(int argc, char* argv[]) std::cout << "distance max = " << dist_max << std::endl; - #if 0 io::pgm::save(canvas, "canvas.pgm"); #endif - -// // Initialize 0-faces to a dummy value, to prevent the watershed from -// // finding minima on 0-faces. -// p_n_faces_fwd_piter<D, G> v_(dist_ima.domain(), 0); -// for_all(v_) -// { -// // FIXME: canvas(v_.to_site().front()) = 255; -// dist_ima(v_) = mln_max(mln_value_(dist_ima_t)); -// } - - - // FIXME: Use the chain() routine. + return dist_ima; +} -// // For all edges, iterate on adjacent edges (i.e., on edges sharing -// // an adjacent vertex). - typedef complex_lower_dim_connected_n_face_neighborhood<D, G> nbh_t; - nbh_t nbh; -// // Neighbor edge. -// mln_niter_(nbh_t) ne(nbh, e); -// for_all(e) -// { -// std::cout << "dist_ima(" << e << ") = " << dist_ima(e) -// << " -- adjacent edges :" << std::endl; -// for_all(ne) -// std::cout << " " << ne << std::endl; -// } +int +main(int argc, char* argv[]) +{ + if (argc != 4) + { + std::cerr << "usage: " << argv[0] << " input.pbm lambda output.ppm" + << std::endl; + std::exit(1); + } + std::string input_filename = argv[1]; + unsigned lambda = atoi(argv[2]); + std::string output_filename = argv[3]; + using namespace mln; + using mln::value::int_u8; + // Seeds. + image2d<bool> seeds; + io::pbm::load(seeds, input_filename); - /*-----------------. - | Simplification. | - `-----------------*/ + // Shortcut: type of a 1-complex image located in a discrete 2D + // space with int_u8 values for each face. + typedef int_u8_1complex_image2d ima_t; + // Shortcuts: dimension and geometry (set of spatial locations) + // associated to this image type. + const unsigned D = 1; + typedef mln_geom_(ima_t) G; - // Currently, does nothing (lambda = 1). - dist_ima_t closed_dist_ima; // (dist_ima.domain()); - // FIXME: In other programs, an area closing is used. - closed_dist_ima = morpho::closing::height(dist_ima, nbh, lambda); + // Image computed from the graph of influence zones (IZ) of seeds. + ima_t ima = make_complex_image(seeds); - /*------. - | WST. | - `------*/ + // Adjacency relation between two n-faces through a shared adjacent + // (n-1)-face. (An edge is adjacent to its neighboring edges; a + // vertex has no neighbors.) + typedef complex_lower_dim_connected_n_face_neighborhood<D, G> nbh_t; + nbh_t nbh; - // Perform a Watershed Transform. typedef unsigned wst_val_t; wst_val_t nbasins; + // Output image type. typedef complex_image<D, G, wst_val_t> wst_ima_t; - wst_ima_t wshed = morpho::meyer_wst(closed_dist_ima, nbh, nbasins); - /* Note that since the image is based not only on the 1-faces but - also on the 0-faces of the complex, and given the above - neighborhood, the domain seen by the WST is not connected! It is - actually composed of five components : - - - a component containing all the 1-faces (egdes) which are all - connected through - mln::complex_lower_dim_connected_n_face_neighborhood; - - - four (singleton) components corresponding to the 0-faces - (vertices), connected to no other part of the complex according to - mln::complex_lower_dim_connected_n_face_neighborhood. - - Since the component made of the edges contains two local minima, - the number of basins is equal to 6: - - 2 minima for the edges' component - + 4 * 1 minima for the vertices's components - -------------------------------------------- - 6 basins. - - Hence the result. - - - We definitely need a complex_image that can accept a subset of a - complex as domain (or at least a p_face<N, D, P>. */ - std::cout << "nbasins = " << nbasins - c.nfaces_of_dim(0) << std::endl; + // Chain. + wst_ima_t wst_ima = chain(ima, nbh, lambda, nbasins); + // Output image showing the results of the above chain on an image + // similar to the input. image2d<int_u8> canvas_wst(seeds.domain()); data::fill(canvas_wst, 255); + // Iterator on the edges (1-faces) of WST_IMA. + p_n_faces_fwd_piter<D, G> e(wst_ima.domain(), 1); + typedef complex_lower_neighborhood<D, G> v_nbh_t; + v_nbh_t v_nbh; + // Iterator on the vertices adjacent to E. + mln_niter_(v_nbh_t) v(v_nbh, e); + for_all(e) { v.start(); @@ -359,7 +298,7 @@ int main(int argc, char* argv[]) v.next(); mln_invariant(!v.is_valid()); - draw::line(canvas_wst, p1, p2, wshed(e)); + draw::line(canvas_wst, p1, p2, wst_ima(e)); } io::ppm::save(labeling::colorize(value::rgb8(), canvas_wst, nbasins), -- 1.7.2.5