* 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(a)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(a)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