* levillain/constrained-connectivity.cc: Complete the processing
chain.
Add a copyright header.
Translate the documentation into English.
s/ima/input/g.
s/ima_x2/f/g.
* levillain/soille.pgm: New.
* levillain/Makefile: New.
---
milena/sandbox/ChangeLog | 13 ++
milena/sandbox/levillain/Makefile | 27 +++
.../sandbox/levillain/constrained-connectivity.cc | 175 ++++++++++++++++----
milena/sandbox/levillain/soille.pgm | 11 ++
4 files changed, 197 insertions(+), 29 deletions(-)
create mode 100644 milena/sandbox/levillain/Makefile
create mode 100644 milena/sandbox/levillain/soille.pgm
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index 673d4d8..4dcc85b 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,3 +1,16 @@
+2009-09-04 Roland Levillain <roland(a)lrde.epita.fr>
+
+ Finish the experiment for Laurent.
+
+ * levillain/constrained-connectivity.cc: Complete the processing
+ chain.
+ Add a copyright header.
+ Translate the documentation into English.
+ s/ima/input/g.
+ s/ima_x2/f/g.
+ * levillain/soille.pgm: New.
+ * levillain/Makefile: New.
+
2009-09-03 Roland Levillain <roland(a)lrde.epita.fr>
Start a new experiment for Laurent.
diff --git a/milena/sandbox/levillain/Makefile b/milena/sandbox/levillain/Makefile
new file mode 100644
index 0000000..d95c7a5
--- /dev/null
+++ b/milena/sandbox/levillain/Makefile
@@ -0,0 +1,27 @@
+# FIXME: A part of this should move out of this sandbox and into milena/apps.
+
+milena_dir = ../..
+img_dir = $(milena_dir)/img
+
+CPPFLAGS = -I. -I$(milena_dir)
+CXXFLAGS = -ggdb -Wall -Werror
+
+PROGRAMS = double min-max constrained-connectivity
+
+all: $(PROGRAMS)
+
+check: check-double check-min-max check-constrained-connectivity
+check-double: double
+ ./$<
+check-min-max: min-max
+ ./$<
+check-constrained-connectivity: constrained-connectivity soille.pgm
+ ./$< soille.pgm
+
+CLEANFILES = double min-max constrained-connectivity
+
+clean:
+ rm -f $(CLEANFILES)
+
+.PHONY: all check clean
+.PHONY: check-double check-min-max check-constrained-connectivity
diff --git a/milena/sandbox/levillain/constrained-connectivity.cc
b/milena/sandbox/levillain/constrained-connectivity.cc
index d740956..81c86ca 100644
--- a/milena/sandbox/levillain/constrained-connectivity.cc
+++ b/milena/sandbox/levillain/constrained-connectivity.cc
@@ -1,21 +1,65 @@
-/* The Work:
-
- - Prendre une image 2d
- - En doubler les pixels (un pixel -> un carré de quatre pixels)
- - En calculer un graphe d'arrête (passage en complexe cubique)
- - Valuer les arrêtes avec une magnitude de gradient (|v1 - v2|)
- - Calculer une LPE topo sur ce gradient
- - Seuiller les valeurs de cette LPE doit être équivalent aux
- alpha-CC de Pierre Soille (hypothèse de Laurent)
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+//
+// This file is part of Olena.
+//
+// Olena is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation, version 2 of the License.
+//
+// Olena is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Olena. If not, see <http://www.gnu.org/licenses/>.
+//
+// As a special exception, you may use this file as part of a free
+// software project without restriction. Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to produce
+// an executable, this file does not by itself cause the resulting
+// executable to be covered by the GNU General Public License. This
+// exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+
+/** \file constrained-connectivity.cc
- - Puis, sur l'arbre des coupes (correspondant à la LPE topo
- calculée ci-avant), calculer pour chaque arrête `e' un attribut
- initialisé tel que `a(e) = (min(v1,v2), max(v1,v2))' (couple) où
- `v1' et `v2' sont adjacents à `e', et propager ce min et ce max
- sur tous les noeuds.
- - Il doit normalement être possible d'obtenir les (alpha,
- omega)-CC de Pierre Soille en filtrant cet arbre. */
-
+ \brief A topological watershed-based implementation of Pierre
+ Soille's constrained connectivity segmentation.
+
+ Reference:
+
+ Pierre Soille. Constrained Connectivity for Hierarchical Image
+ Partitioning and Simplification. In: IEEE Transactions on
+ Pattern Analysis and Machine Intelligence, vol. 30, no. 7, July
+ 2008, pages 1132-1145.
+
+
+ The scenario is as follows.
+
+ Part 1.
+ \li Load a 2D image.
+ \li Double its resolution (a pixel becomes a square of four pixels).
+ \li Compute a line graph from this image and
+ \li Assign values of a gradient to the edges (e.g., |v1 - v2|).
+ \li Compute a topological watershed transform on this gradient.
+ \li Thresholding the values of this watershed gives
+ alpha-connected component (alpha-cc) described by Pierre Soille.
+
+ Part 2.
+ \li For each edge of the watershed (line graph-based) image,
+ compute the min and max value of the adjacent vertices (pixels).
+ \li Using the component tree corresponding to the previously
+ computed topological watershed transform, propagate and
+ compute the min and max value for each component of the
+ watershed.
+ \li Create an image of ``heights'' by subtracting the image of min
+ values from the image of max values.
+ \li Thresholding the watershed image (using a parameter alpha)
+ <em>and</em> the height image (using a parameter omega) gives
+ the (alpha, omega)-connected component (alpha-omega cc)
+ described by Pierre Soille. */
#include <cstdio>
@@ -27,15 +71,19 @@
#include <mln/pw/all.hh>
-#include <mln/io/pgm/load.hh>
-#include <mln/debug/println.hh>
-
// From Théo's sandbox.
#include <theo/cplx2d.hh>
// From Alexandre Abraham's sandbox.
#include <alexandre/topo_wst.hh>
+#include <mln/morpho/tree/compute_attribute_image.hh>
+#include <mln/accu/stat/min.hh>
+#include <mln/accu/stat/max.hh>
+
+#include <mln/io/pgm/load.hh>
+#include <mln/debug/println.hh>
+
int main(int argc, char* argv[])
{
@@ -49,12 +97,12 @@ int main(int argc, char* argv[])
using mln::value::int_u8;
// Load an image.
- image2d<int_u8> ima;
- io::pgm::load(ima, argv[1]);
+ image2d<int_u8> input;
+ io::pgm::load(input, argv[1]);
// Double its resolution.
- image2d<int_u8> ima_x2 (10, 10);
- mln_piter_(image2d<int_u8>) p(ima_x2.domain());
+ image2d<int_u8> f(input.nrows() * 2, input.ncols() * 2);
+ mln_piter_(image2d<int_u8>) p(f.domain());
for_all(p)
{
/* This conversion from ``piter'' to ``point'' is required, since
@@ -62,16 +110,17 @@ int main(int argc, char* argv[])
point (among which the methods row(), col(), etc.). */
point2d p_ = p;
point2d q(p_.row() / 2, p_.col() / 2);
- ima_x2(p) = ima(q);
+ f(p) = input(q);
}
- debug::println(ima_x2);
+ debug::println(f);
// Compute the associated line graph gradient.
- mln_VAR(g, cplx2d::f_to_g(ima_x2) );
+ mln_VAR(g, cplx2d::f_to_g(f) );
debug::println("g:", g);
// Compute a topological watershed transform on this gradient.
- morpho::topo_wst<g_t, cplx2d::dbl_neighb2d> tree(g, cplx2d::e2e());
+ typedef morpho::topo_wst<g_t, cplx2d::dbl_neighb2d> tree_t;
+ tree_t tree(g, cplx2d::e2e());
tree.go();
mln_VAR(w, morpho::topo_watershed(tree));
debug::println("w:", w);
@@ -84,7 +133,7 @@ int main(int argc, char* argv[])
values.insert (w(p2));
// Thresholding for each value of W.
- for (std::set<int_u8>::iterator alpha = values.begin();
+ for (std::set<int_u8>::const_iterator alpha = values.begin();
alpha != values.end(); ++alpha)
{
mln_VAR(alpha_cc, w | (pw::value(w) > pw::cst(*alpha)));
@@ -95,4 +144,72 @@ int main(int argc, char* argv[])
std::cout << *alpha << "-cc:" << std::endl;
debug::impl::println(w.unmorph_().domain(), alpha_cc);
}
+
+
+ // Compute attributes on the components of the topological watershed (W).
+ typedef p_array<tree_t::site> sites_t;
+ sites_t sites = data::sort_psites_decreasing(w);
+
+ /* FIXME: Of course, we'd like to be able to reuse the component tree
+ within TREE instead of rebuilding a morpho::tree::data... This
+ requires some changes in the topological WST implementation to
+ make its component tree structure compatible with
+ morpho::tree::data. */
+ morpho::tree::data<w_t, sites_t> t(w, sites, cplx2d::e2e());
+
+ // Height (max-min) on the line graph WST, but with min and max
+ // values computed on vertices.
+ mln_ch_value_(w_t, accu::stat::min<int_u8>) init_min_val;
+ initialize (init_min_val, w);
+ mln_ch_value_(w_t, accu::stat::max<int_u8>) init_max_val;
+ initialize (init_max_val, w);
+
+ mln_fwd_piter_(w_t) e(w.domain());
+ mln_niter_(cplx2d::dbl_neighb2d) v(cplx2d::e2p(), e);
+ for_all(e)
+ {
+ // Compute the min and max values on vertices (pixels) adjacent to edge E.
+ for_all(v)
+ {
+ /* Unfortunately, the data structure G does not record any
+ information from the image F (i.e., the values on
+ vertices/pixels). We have to convert the coordinates of V
+ to its equivalent in F's domain to get the values on
+ vertices.
+
+ In addition, note that an explicit `to_site()' conversion
+ is required here, since an iterator does not expose the
+ interface of the underlying point (among which the methods
+ row(), col(), etc.). */
+ point2d v_(v.to_site().row() / 2, v.to_site().col() / 2);
+ init_min_val(e).take(f(v_));
+ init_max_val(e).take(f(v_));
+ }
+ }
+ accu::stat::min<int_u8> min_accu;
+ mln_ch_value_(w_t, int_u8) min_val =
+ morpho::tree::compute_attribute_image_from(min_accu, t, init_min_val);
+ accu::stat::max<int_u8> max_accu;
+ mln_ch_value_(w_t, int_u8) max_val =
+ morpho::tree::compute_attribute_image_from(max_accu, t, init_max_val);
+
+ mln_ch_value_(w_t, int_u8) height;
+ initialize(height, w);
+ for_all(e)
+ height(e) = max_val(e) - min_val(e);
+ debug::println(height);
+
+ // Thresholding for the first integer values with a condition on HEIGHT.
+ for (unsigned alpha = 0; alpha <= 6; ++alpha)
+ {
+ mln_VAR(alpha_alpha_cc,
+ w | (pw::value(w) > pw::cst(alpha)
+ || (pw::value(height) > pw::cst(alpha))));
+ /* FIXME: There should be variants of debug::println allowing
+ the user to pass an optional ``support'' larger than the
+ actual domain of the image. For now, use a low-level routine
+ as a workaround. */
+ std::cout << "(" << alpha << ", " <<
alpha << ")-cc:" << std::endl;
+ debug::impl::println(w.unmorph_().domain(), alpha_alpha_cc);
+ }
}
diff --git a/milena/sandbox/levillain/soille.pgm b/milena/sandbox/levillain/soille.pgm
new file mode 100644
index 0000000..7b1593b
--- /dev/null
+++ b/milena/sandbox/levillain/soille.pgm
@@ -0,0 +1,11 @@
+P2
+7 7
+255
+
+1 3 8 7 8 8 2
+2 1 9 8 8 9 1
+1 0 4 1 1 2 5
+1 1 9 3 4 2 6
+3 2 7 9 9 1 1
+1 0 8 4 9 6 7
+0 2 9 3 8 5 9
--
1.6.4.2