* mln/topo/is_simple_cell.hh: Make this functor more generic.
* mln/topo/detach_cell.hh (detach): Turn this function into a
more generic functor.
* mln/make/attachment.hh (mln::make::attachment)
* mln/make/detachment.hh (mln::make::detachment):
Make these routines more generic.
* mln/topo/skeleton/breadth_first_thinning.hh
(mln::topo::skeleton::breadth_first_thinning):
Adjust.
* headers.mk: Regen.
---
milena/ChangeLog | 15 +++
milena/mln/make/attachment.hh | 48 +++++----
milena/mln/make/detachment.hh | 41 +++++---
milena/mln/topo/detach_cell.hh | 107 ++++++++++++++++---
milena/mln/topo/is_simple_cell.hh | 81 ++++++++-------
milena/mln/topo/skeleton/breadth_first_thinning.hh | 18 +++-
6 files changed, 213 insertions(+), 97 deletions(-)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index c0a7088..bff25d3 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,20 @@
2010-09-09 Roland Levillain <roland(a)lrde.epita.fr>
+ More generic breadth-first-thinning-based skeleton transformations.
+
+ * mln/topo/is_simple_cell.hh: Make this functor more generic.
+ * mln/topo/detach_cell.hh (detach): Turn this function into a
+ more generic functor.
+ * mln/make/attachment.hh (mln::make::attachment)
+ * mln/make/detachment.hh (mln::make::detachment):
+ Make these routines more generic.
+ * mln/topo/skeleton/breadth_first_thinning.hh
+ (mln::topo::skeleton::breadth_first_thinning):
+ Adjust.
+ * headers.mk: Regen.
+
+2010-09-09 Roland Levillain <roland(a)lrde.epita.fr>
+
Introduce a helper to build an mln::mutable_extension_ima.
* mln/core/routine/mutable_extend.hh: New.
diff --git a/milena/mln/make/attachment.hh b/milena/mln/make/attachment.hh
index 088f9df..3af5332 100644
--- a/milena/mln/make/attachment.hh
+++ b/milena/mln/make/attachment.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -43,52 +43,58 @@ namespace mln
/** \brief Compute the attachment of the cell corresponding to the
facet \a f to the image \a ima.
+ \param ima The input image to which the facet is attached.
+ \param f The psite corresponding to the attached facet.
+ \param nbh An adjacency relationship between faces
+ (should return the set of (n-1)- and (n+1)-faces
+ adjacent to an n-face).
+
+ \return A set of faces containing the attachment.
+
+ \pre ima is an image of Boolean values.
\pre \a f is a facet (it does not belong to any face of higher
dimension).
- \pre ima is an image of Boolean values.
-
- \return a set of faces containing the attachment.
We do not use the fomal definition of the attachment here (see
couprie.08.pami). We use the following (equivalent) definition:
an N-face F in CELL is in the attachment of CELL to IMA if it is
adjacent to at least an (N-1)-face or an (N+1)-face that does not
belong to CELL. */
- template <unsigned D, typename G, typename V>
- p_set< complex_psite<D, G> >
- attachment(const complex_psite<D, G>& f,
- const complex_image<D, G, V>& ima);
+ template <typename I, typename N>
+ p_set<mln_psite(I)>
+ attachment(const Image<I>& ima, const mln_psite(I)& f,
+ const Neighborhood<N>& nbh);
# ifndef MLN_INCLUDE_ONLY
- template <unsigned D, typename G, typename V>
+ template <typename I, typename N>
inline
- p_set< complex_psite<D, G> >
- attachment(const complex_psite<D, G>& f,
- const complex_image<D, G, V>& ima)
+ p_set<mln_psite(I)>
+ attachment(const Image<I>& ima_, const mln_psite(I)& f,
+ const Neighborhood<N>& nbh_)
{
mln_precondition(topo::is_facet(f));
- mlc_equal(V, bool)::check();
+ mlc_equal(mln_value(I), bool)::check();
+
+ I ima = exact(ima_);
+ N nbh = exact(nbh_);
- typedef complex_psite<D, G> psite;
- typedef p_set<psite> faces_t;
+ typedef p_set<mln_psite(I)> faces_t;
faces_t f_hat = make::cell(f);
- faces_t att_f;
+ faces_t attach_f;
- typedef complex_lower_higher_neighborhood<D, G> adj_nbh_t;
- adj_nbh_t adj_nbh;
mln_piter(faces_t) g(f_hat);
- mln_niter(adj_nbh_t) n(adj_nbh, g);
+ mln_niter(N) n(nbh, g);
for_all(g)
for_all(n)
if (ima(n) && !f_hat.has(n))
{
- att_f.insert(g);
+ attach_f.insert(g);
break;
}
- return att_f;
+ return attach_f;
}
# endif // MLN_INCLUDE_ONLY
diff --git a/milena/mln/make/detachment.hh b/milena/mln/make/detachment.hh
index 16a8c92..6cd01ba 100644
--- a/milena/mln/make/detachment.hh
+++ b/milena/mln/make/detachment.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -43,12 +43,19 @@ namespace mln
/** \brief Compute the detachment of the cell corresponding to the
facet \a f from the image \a ima.
+ \param ima The input image from which the facet is to be
+ detached.
+ \param f The psite corresponding to the facet to detach.
+ \param nbh An adjacency relationship between faces
+ (should return the set of (n-1)- and (n+1)-faces
+ adjacent to an n-face).
+
+ \return A set of faces containing the detachment.
+
\pre \a f is a facet (it does not belong to any face of higher
dimension).
\pre \a ima is an image of Boolean values.
- \return a set of faces containing the detachment.
-
We do not use the fomal definition of the detachment here (see
couprie.08.pami). We use the following (equivalent) definition:
an N-face F in CELL is not in the detachment of CELL from IMA if
@@ -60,34 +67,34 @@ namespace mln
the part that is removed, i.e., the detached part CELL -
ATTACHMENT. It would be wise to rename this routine to
something else. */
- template <unsigned D, typename G, typename V>
- p_set< complex_psite<D, G> >
- detachment(const complex_psite<D, G>& f,
- const complex_image<D, G, V>& ima);
+ template <typename I, typename N>
+ p_set<mln_psite(I)>
+ detachment(const Image<I>& ima, const mln_psite(I)& f,
+ const Neighborhood<N>& nbh);
# ifndef MLN_INCLUDE_ONLY
- template <unsigned D, typename G, typename V>
+ template <typename I, typename N>
inline
- p_set< complex_psite<D, G> >
- detachment(const complex_psite<D, G>& f,
- const complex_image<D, G, V>& ima)
+ p_set<mln_psite(I)>
+ detachment(const Image<I>& ima_, const mln_psite(I)& f,
+ const Neighborhood<N>& nbh_)
{
mln_precondition(topo::is_facet(f));
- mlc_equal(V, bool)::check();
+ mlc_equal(mln_value(I), bool)::check();
+
+ I ima = exact(ima_);
+ N nbh = exact(nbh_);
- typedef complex_psite<D, G> psite;
- typedef p_set<psite> faces_t;
+ typedef p_set<mln_psite(I)> faces_t;
faces_t f_hat = make::cell(f);
// Initialize DETACH_F to F_HAT.
faces_t detach_f = f_hat;
- typedef complex_lower_higher_neighborhood<D, G> adj_nbh_t;
- adj_nbh_t adj_nbh;
mln_piter(faces_t) g(f_hat);
- mln_niter(adj_nbh_t) n(adj_nbh, g);
+ mln_niter(N) n(nbh, g);
for_all(g)
for_all(n)
if (ima(n) && !f_hat.has(n))
diff --git a/milena/mln/topo/detach_cell.hh b/milena/mln/topo/detach_cell.hh
index dfc4fb5..9e2a489 100644
--- a/milena/mln/topo/detach_cell.hh
+++ b/milena/mln/topo/detach_cell.hh
@@ -27,7 +27,7 @@
# define MLN_TOPO_DETACH_CELL_HH
/// \file
-/// \brief Detaching a cell from a binary complex-based image.
+/// \brief Detaching a cell from a binary (probably complex-based) image.
# include <mln/core/site_set/p_set.hh>
# include <mln/core/image/complex_image.hh>
@@ -40,38 +40,111 @@ namespace mln
namespace topo
{
- /** Detach the cell corresponding to \a f from \a ima.
+ /** A functor detaching a cell from a binary (probably
+ complex-based) image.
- \pre \a f is a facet (it does not belong to any face of higher
- dimension).
- \pre \a ima is an image of Boolean values. */
- template <unsigned D, typename G>
- void
- detach_cell(const complex_psite<D, G>& f, complex_image<D, G, bool>& ima);
+ \tparam I The type of the image.
+ \tparam N An neighborhood type corresponding to (directly)
+ adjacent faces (should return the set of (n-1)- and
+ (n+1)-faces adjacent to an n-face). */
+ template <typename I, typename N>
+ class detach_cell
+ {
+ public:
+ /// Constructors.
+ /// \{
+ /** Construct an mln::topo::detach_cell from a neighborhood.
+
+ \param nbh An adjacency relationship between faces
+ (should return the set of (n-1)- and (n+1)-faces
+ adjacent to an n-face). */
+ detach_cell(const Neighborhood<N>& nbh);
+
+ /** Construct an mln::topo::detach_cell from an image and a
+ neighborhood.
+
+ \pre \a ima is an image of Boolean values.
+
+ \param ima The input image from which the facet is to be
+ detached.
+ \param nbh An adjacency relationship between faces
+ (should return the set of (n-1)- and (n+1)-faces
+ adjacent to an n-face). */
+ detach_cell(mln::Image<I>& ima, const Neighborhood<N>& nbh);
+ /// \}
+
+ /* FIXME: Rename as init() or something like this? See how other
+ functors are written. */
+ /** Set the underlying image.
+
+ \pre \a ima is an image of Boolean values. */
+ void set_image(mln::Image<I>& ima);
+
+ /** Detach the cell corresponding to \a f from \a ima.
+
+ \pre \a f is a facet (it does not belong to any face of
+ higher dimension).
+
+ \param f The psite corresponding to the facet to detach.
+ \param nbh An adjacency relationship between faces
+ (should return the set of (n-1)- and (n+1)-faces
+ adjacent to an n-face). */
+ void
+ operator()(const mln_psite(I)& f);
+
+ private:
+ I* ima_;
+ const N& nbh_;
+ };
# ifndef MLN_INCLUDE_ONLY
- template <unsigned D, typename G>
+ template <typename I, typename N>
+ inline
+ detach_cell<I, N>::detach_cell(const Neighborhood<N>& nbh)
+ : ima_(0), nbh_(exact(nbh))
+ {
+ }
+
+ template <typename I, typename N>
+ inline
+ detach_cell<I, N>::detach_cell(mln::Image<I>& ima,
+ const Neighborhood<N>& nbh)
+ : ima_(exact(&ima)), nbh_(exact(nbh))
+ {
+ mlc_equal(mln_value(I), bool)::check();
+ }
+
+ template <typename I, typename N>
+ inline
+ void
+ detach_cell<I, N>::set_image(mln::Image<I>& ima)
+ {
+ mlc_equal(mln_value(I), bool)::check();
+ ima_ = exact(&ima);
+ }
+
+ template <typename I, typename N>
inline
void
- detach_cell(const complex_psite<D, G>& f, complex_image<D, G, bool>& ima)
+ detach_cell<I, N>::operator()(const mln_psite(I)& f)
{
+ mln_precondition(ima_);
mln_precondition(topo::is_facet(f));
- typedef complex_psite<D, G> psite;
- typedef p_set<psite> faces_t;
+ typedef p_set<mln_psite(I)> faces_t;
- // Compute the detachment of P from IMA.
- faces_t detach = make::detachment(f, ima);
- // Detach all its faces from IMA.
+ // Compute the detachment of P from *IMA_.
+ faces_t detach = make::detachment(*ima_, f, nbh_);
+ // Detach all its faces from *IMA_.
# if 0
// FIXME: Does not work yet? Check again.
- data::fill(ima | detach, false);
+ data::fill(*ima_ | detach, false);
# endif
mln_piter(faces_t) p(detach);
for_all(p)
- ima(p) = false;
+ (*ima_)(p) = false;
}
# endif // MLN_INCLUDE_ONLY
diff --git a/milena/mln/topo/is_simple_cell.hh b/milena/mln/topo/is_simple_cell.hh
index a86a8fa..bf60c3d 100644
--- a/milena/mln/topo/is_simple_cell.hh
+++ b/milena/mln/topo/is_simple_cell.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -27,7 +27,6 @@
# define MLN_TOPO_IS_SIMPLE_CELL_HH
/// \file
-///
/// \brief Testing whether a facet is a simple cell.
# include <mln/core/concept/function.hh>
@@ -48,22 +47,29 @@ namespace mln
namespace topo
{
+ /* FIXME: Maybe we could add traits to deduce N from NL and NH (or
+ the converse). Anyway, the part of the code where neighborhood
+ are used should be moved into another functor
+ (`is_collapsible', see below), thus pushing away this
+ issue. */
+
/** \brief A predicate for the simplicity of a point based on the
collapse property of the attachment.
The functor does not actually take a cell as input, but a face
- that is expected to be a D-facet. */
- template <typename I>
- class is_simple_cell : public mln::Function_v2b< is_simple_cell<I> >
+ that is expected to be a D-facet.
+
+ \tparam I The type of the image.
+ \tparam N The neighborhood type returning the set of
+ (n-1)- and (n+1)-faces adjacent to a an n-face.
+ \tparam NL The neighborhood type returning the set of
+ (n-1)-faces adjacent to a an n-face.
+ \tparam NH The neighborhood type returning the set of
+ (n+1)-faces adjacent to a an n-face. */
+ template <typename I, typename N, typename NL, typename NH>
+ class is_simple_cell : public mln::Function_v2b< is_simple_cell<I, N, NL, NH> >
{
public:
- /// Dimension of the image (and therefore of the complex).
- static const unsigned D = I::dim;
- /// Geometry of the image.
- typedef mln_geom(I) G;
- /// Psite type.
- typedef mln::complex_psite<D, G> psite;
-
/// Result type of the functor.
typedef bool result;
@@ -76,8 +82,11 @@ namespace mln
void set_image(const mln::Image<I>& ima);
/// Based on the algorithm A2 from couprie.08.pami.
- bool operator()(const mln::complex_psite<I::dim,mln_geom(I)>& p) const;
- // Tech note: The argument type above is explicit to help g++-3.3.
+ /* FIXME: We probably broke the compatiblity with g++ 3.3, as it
+ seems this compiler does not like an indirect type like the
+ one of the following operator's argument. Check and possibly
+ improve this. */
+ bool operator()(const mln_psite(I)& p) const;
private:
const I* ima_;
@@ -87,44 +96,44 @@ namespace mln
# ifndef MLN_INCLUDE_ONLY
- template <typename I>
+ template <typename I, typename N, typename NL, typename NH>
inline
- is_simple_cell<I>::is_simple_cell()
+ is_simple_cell<I, N, NL, NH>::is_simple_cell()
: ima_(0)
{
}
- template <typename I>
+ template <typename I, typename N, typename NL, typename NH>
inline
- is_simple_cell<I>::is_simple_cell(const mln::Image<I>& ima)
- : ima_(mln::exact(&ima))
+ is_simple_cell<I, N, NL, NH>::is_simple_cell(const mln::Image<I>& ima)
+ : ima_(exact(&ima))
{
}
- template <typename I>
+ template <typename I, typename N, typename NL, typename NH>
inline
void
- is_simple_cell<I>::set_image(const mln::Image<I>& ima)
+ is_simple_cell<I, N, NL, NH>::set_image(const mln::Image<I>& ima)
{
- ima_ = mln::exact(&ima);
+ ima_ = exact(&ima);
}
- template <typename I>
+ template <typename I, typename N, typename NL, typename NH>
inline
bool
- is_simple_cell<I>::operator()(const mln::complex_psite<I::dim,mln_geom(I)>& p) const
- // Tech note: The argument type above is explicit to help g++-3.3.
+ is_simple_cell<I, N, NL, NH>::operator()(const mln_psite(I)& p) const
{
mln_precondition(ima_);
- typedef p_set<psite> faces_t;
+ typedef p_set<mln_psite(I)> faces_t;
- // Compute the attachment of the cell corresponding to P to he
+ // Compute the attachment of the cell corresponding to P to the
// domain of *IMA_.
- faces_t att = make::attachment(p, *ima_);
+ N adj_nbh;
+ faces_t att = make::attachment(*ima_, p, adj_nbh);
// A cell with an empty attachment is not simple.
- /* FIXME: Why does p_set not provide an empty() predicate? */
+ /* FIXME: Why p_set does not provide an empty() predicate? */
if (att.nsites() == 0)
return false;
@@ -134,10 +143,8 @@ namespace mln
routine. */
// Try to collapse the attachment (to a single point).
{
- typedef complex_lower_neighborhood<D, G> lower_adj_nbh_t;
- typedef complex_higher_neighborhood<D, G> higher_adj_nbh_t;
- lower_adj_nbh_t lower_adj_nbh;
- higher_adj_nbh_t higher_adj_nbh;
+ NL lower_adj_nbh;
+ NH higher_adj_nbh;
while (att.nsites() > 1)
{
@@ -149,11 +156,11 @@ namespace mln
for_all(g)
/* G cannot have dimension 0, since we later look for an
adjacent face H of lower dimension. */
- if (static_cast<psite>(g).n() > 0)
+ if (static_cast<mln_psite(I)>(g).n() > 0)
{
// Check whether G is a facet within ATT.
bool g_is_facet = true;
- mln_niter(higher_adj_nbh_t) f(higher_adj_nbh, g);
+ mln_niter(NH) f(higher_adj_nbh, g);
for_all(f)
if (att.has(f))
{
@@ -165,13 +172,13 @@ namespace mln
// Look for a face H stricly included in G.
bool gh_is_simple_pair = false;
- mln_niter(lower_adj_nbh_t) h(lower_adj_nbh, g);
+ mln_niter(NL) h(lower_adj_nbh, g);
for_all(h)
{
bool h_strictly_in_g = true;
if (att.has(h))
{
- mln_niter(higher_adj_nbh_t) i(higher_adj_nbh, h);
+ mln_niter(NH) i(higher_adj_nbh, h);
for_all(i)
if (i != g && att.has(i))
{
diff --git a/milena/mln/topo/skeleton/breadth_first_thinning.hh b/milena/mln/topo/skeleton/breadth_first_thinning.hh
index 7401bae..121c159 100644
--- a/milena/mln/topo/skeleton/breadth_first_thinning.hh
+++ b/milena/mln/topo/skeleton/breadth_first_thinning.hh
@@ -61,9 +61,13 @@ namespace mln
(sites). This functor must provide a method
<tt>void set_image(const Image<I>&)</tt>.
\param detach A function used to detach a cell from \a input.
+ This functor must provide a method
+ <tt>void set_image(const Image<I>&)</tt>.
\param constraint A constraint on point (site); if it
returns \c false for a point, this point
- will not be removed. */
+ will not be removed.
+
+ Keywords: skeletons, simple points. */
template <typename I, typename N, typename F, typename G, typename H>
mln_concrete(I)
breadth_first_thinning(const Image<I>& input,
@@ -83,8 +87,11 @@ namespace mln
\param is_simple The predicate on the simplicity of points
(sites). This functor must provide a method
<tt>void set_image(const Image<I>&)</tt>.
- \param detach A function used to detach a cell from
- \a input. */
+ \param detach A function used to detach a cell from \a input.
+ This functor must provide a method
+ <tt>void set_image(const Image<I>&)</tt>.
+
+ Keywords: skeletons, simple points. */
template <typename I, typename N, typename F, typename G>
mln_concrete(I)
breadth_first_thinning(const Image<I>& input,
@@ -112,8 +119,9 @@ namespace mln
const H& constraint = exact(constraint_);
mln_concrete(I) output = duplicate(input);
- // Attach the work image to IS_SIMPLE.
+ // Attach the work image to IS_SIMPLE and DETACH.
is_simple.set_image(output);
+ detach.set_image(output);
typedef mln_psite(I) psite;
typedef p_queue_fast<psite> queue_t;
@@ -131,7 +139,7 @@ namespace mln
psite p = queue.pop_front();
if (output(p) && constraint(p) && is_simple(p))
{
- detach(p, output);
+ detach(p);
mln_niter(N) n(nbh, p);
for_all(n)
{
--
1.5.6.5
* apps/mesh-segm-skel/mesh-complex-skel.cc,
* apps/mesh-segm-skel/mesh-complex-max-curv-skel.cc,
* apps/mesh-segm-skel/mesh-complex-pinv-curv-skel.cc:
Catch up with the new interface of breadth-first thinning and
auxiliary routines.
---
milena/ChangeLog | 10 +++
.../mesh-segm-skel/mesh-complex-max-curv-skel.cc | 82 ++++++++++++++++----
.../mesh-segm-skel/mesh-complex-pinv-curv-skel.cc | 82 ++++++++++++++++----
milena/apps/mesh-segm-skel/mesh-complex-skel.cc | 82 ++++++++++++++++----
4 files changed, 205 insertions(+), 51 deletions(-)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index bff25d3..40e876c 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,15 @@
2010-09-09 Roland Levillain <roland(a)lrde.epita.fr>
+ Update mesh skeleton applications.
+
+ * apps/mesh-segm-skel/mesh-complex-skel.cc,
+ * apps/mesh-segm-skel/mesh-complex-max-curv-skel.cc,
+ * apps/mesh-segm-skel/mesh-complex-pinv-curv-skel.cc:
+ Catch up with the new interface of breadth-first thinning and
+ auxiliary routines.
+
+2010-09-09 Roland Levillain <roland(a)lrde.epita.fr>
+
More generic breadth-first-thinning-based skeleton transformations.
* mln/topo/is_simple_cell.hh: Make this functor more generic.
diff --git a/milena/apps/mesh-segm-skel/mesh-complex-max-curv-skel.cc b/milena/apps/mesh-segm-skel/mesh-complex-max-curv-skel.cc
index edb943e..641b0b4 100644
--- a/milena/apps/mesh-segm-skel/mesh-complex-max-curv-skel.cc
+++ b/milena/apps/mesh-segm-skel/mesh-complex-max-curv-skel.cc
@@ -39,7 +39,9 @@
#include <mln/core/image/complex_image.hh>
#include <mln/core/image/complex_neighborhoods.hh>
-#include <mln/core/site_set/p_set.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+#include <mln/core/image/dmorph/mutable_extension_ima.hh>
+#include <mln/core/routine/mutable_extend.hh>
#include <mln/value/label_16.hh>
@@ -252,31 +254,77 @@ main(int argc, char* argv[])
| Skeleton. |
`-----------*/
- mln::topo::is_simple_cell<bin_ima_t> is_simple_p;
- /* FIXME: Cheat! We'd like to iterate on cells of highest
- dimension (2-cells) only, but we cannot constrain the domain of
- the input using image_if (yet) like this
+ // ---------------- //
+ // Skeleton image. //
+ // ---------------- //
+
+ // Predicate type: is a face a triangle (2-face)?
+ typedef mln::topo::is_n_face<mln_psite_(bin_ima_t), D> is_a_triangle_t;
+ is_a_triangle_t is_a_triangle;
+ // Surface image type, of which domain is restricted to triangles.
+ typedef mln::image_if<bin_ima_t, is_a_triangle_t> bin_triangle_only_ima_t;
+ // Surface image type, of which iteration (not domain) is restricted
+ // to triangles.
+ typedef mln::mutable_extension_ima<bin_triangle_only_ima_t, bin_ima_t>
+ bin_triangle_ima_t;
+ // FIXME: Find a shorter name (skel_ima ? Careful, there is already a `skel' image below).
+ bin_triangle_ima_t bin_triangle_ima =
+ mln::mutable_extend((surface | is_a_triangle).rw(), surface);
+
+ // ------------------------ //
+ // Simple point predicate. //
+ // ------------------------ //
+
+ // Neighborhood type returning the set of (n-1)- and (n+1)-faces
+ // adjacent to a an n-face.
+ typedef mln::complex_lower_higher_neighborhood<D, G> adj_nbh_t;
+ // Neighborhood type returning the set of (n-1)-faces adjacent to a
+ // an n-face.
+ typedef mln::complex_lower_neighborhood<D, G> lower_adj_nbh_t;
+ // Neighborhood type returning the set of (n+1)-faces adjacent to a
+ // an n-face.
+ typedef mln::complex_higher_neighborhood<D, G> higher_adj_nbh_t;
+ // Predicate type: is a triangle (2-face) simple?
+ typedef mln::topo::is_simple_cell< bin_triangle_ima_t,
+ adj_nbh_t,
+ lower_adj_nbh_t,
+ higher_adj_nbh_t >
+ is_simple_triangle_t;
+ is_simple_triangle_t is_simple_triangle;
+
+ // ------------------------------- //
+ // Simple point detach procedure. //
+ // ------------------------------- //
+
+ // Type of adjacency relationships between faces of immediately
+ // lower and higher dimensions.
+ adj_nbh_t adj_nbh;
+ // Functor detaching a cell.
+ mln::topo::detach_cell<bin_triangle_ima_t, adj_nbh_t> detach(adj_nbh);
+
+ mln_concrete_(bin_triangle_ima_t) skel =
+ mln::topo::skeleton::breadth_first_thinning(bin_triangle_ima,
+ nbh,
+ is_simple_triangle,
+ detach);
- breadth_first_thinning(surface | is_n_face<2>, nbh, is_simple_p);
-
- As a workaround, we use the constraint predicate of the
- skeleton routine to restrict the iteration to 2-cells. */
- mln::topo::is_n_face<mln_psite_(bin_ima_t), bin_ima_t::dim> constraint_p;
- bin_ima_t skel =
- mln::topo::skeleton::breadth_first_thinning(surface, nbh,
- is_simple_p,
- mln::topo::detach_cell<D, G>,
- constraint_p);
/*---------.
| Output. |
`---------*/
/* FIXME: This does not work (yet).
- Use workaround mln::io::off::save_bin_alt instead (bad!) */
+ Use workaround mln::io::off::save_bin_alt instead (bad!)
+
+ Moreover, even if it worked, it would not have the same meaning
+ as mln::io::off::save_bin_alt. Maybe the latter is useful, after
+ all. But we need to factor it with the code of
+ mln::io::off::save, anyway. */
#if 0
mln::io::off::save(skel | mln::pw::value(skel) == mln::pw::cst(true),
output_filename);
#endif
- mln::io::off::save_bin_alt(skel, output_filename);
+ // FIXME: We have to ``unmorph'' (twice!) SKEL first, since save_bin_alt only
+ // handles complex_image's.
+ mln::io::off::save_bin_alt(skel.unmorph_().unmorph_(), output_filename);
}
diff --git a/milena/apps/mesh-segm-skel/mesh-complex-pinv-curv-skel.cc b/milena/apps/mesh-segm-skel/mesh-complex-pinv-curv-skel.cc
index e2bdf45..5096efa 100644
--- a/milena/apps/mesh-segm-skel/mesh-complex-pinv-curv-skel.cc
+++ b/milena/apps/mesh-segm-skel/mesh-complex-pinv-curv-skel.cc
@@ -39,7 +39,9 @@
#include <mln/core/image/complex_image.hh>
#include <mln/core/image/complex_neighborhoods.hh>
-#include <mln/core/site_set/p_set.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+#include <mln/core/image/dmorph/mutable_extension_ima.hh>
+#include <mln/core/routine/mutable_extend.hh>
#include <mln/value/label_16.hh>
@@ -257,31 +259,77 @@ main(int argc, char* argv[])
| Skeleton. |
`-----------*/
- mln::topo::is_simple_cell<bin_ima_t> is_simple_p;
- /* FIXME: Cheat! We'd like to iterate on cells of highest
- dimension (2-cells) only, but we cannot constrain the domain of
- the input using image_if (yet) like this
+ // ---------------- //
+ // Skeleton image. //
+ // ---------------- //
+
+ // Predicate type: is a face a triangle (2-face)?
+ typedef mln::topo::is_n_face<mln_psite_(bin_ima_t), D> is_a_triangle_t;
+ is_a_triangle_t is_a_triangle;
+ // Surface image type, of which domain is restricted to triangles.
+ typedef mln::image_if<bin_ima_t, is_a_triangle_t> bin_triangle_only_ima_t;
+ // Surface image type, of which iteration (not domain) is restricted
+ // to triangles.
+ typedef mln::mutable_extension_ima<bin_triangle_only_ima_t, bin_ima_t>
+ bin_triangle_ima_t;
+ // FIXME: Find a shorter name (skel_ima ? Careful, there is already a `skel' image below).
+ bin_triangle_ima_t bin_triangle_ima =
+ mln::mutable_extend((surface | is_a_triangle).rw(), surface);
+
+ // ------------------------ //
+ // Simple point predicate. //
+ // ------------------------ //
+
+ // Neighborhood type returning the set of (n-1)- and (n+1)-faces
+ // adjacent to a an n-face.
+ typedef mln::complex_lower_higher_neighborhood<D, G> adj_nbh_t;
+ // Neighborhood type returning the set of (n-1)-faces adjacent to a
+ // an n-face.
+ typedef mln::complex_lower_neighborhood<D, G> lower_adj_nbh_t;
+ // Neighborhood type returning the set of (n+1)-faces adjacent to a
+ // an n-face.
+ typedef mln::complex_higher_neighborhood<D, G> higher_adj_nbh_t;
+ // Predicate type: is a triangle (2-face) simple?
+ typedef mln::topo::is_simple_cell< bin_triangle_ima_t,
+ adj_nbh_t,
+ lower_adj_nbh_t,
+ higher_adj_nbh_t >
+ is_simple_triangle_t;
+ is_simple_triangle_t is_simple_triangle;
+
+ // ------------------------------- //
+ // Simple point detach procedure. //
+ // ------------------------------- //
+
+ // Type of adjacency relationships between faces of immediately
+ // lower and higher dimensions.
+ adj_nbh_t adj_nbh;
+ // Functor detaching a cell.
+ mln::topo::detach_cell<bin_triangle_ima_t, adj_nbh_t> detach(adj_nbh);
+
+ mln_concrete_(bin_triangle_ima_t) skel =
+ mln::topo::skeleton::breadth_first_thinning(bin_triangle_ima,
+ nbh,
+ is_simple_triangle,
+ detach);
- breadth_first_thinning(surface | is_n_face<2>, nbh, is_simple_p);
-
- As a workaround, we use the constraint predicate of the
- skeleton routine to restrict the iteration to 2-cells. */
- mln::topo::is_n_face<mln_psite_(bin_ima_t), bin_ima_t::dim> constraint_p;
- bin_ima_t skel =
- mln::topo::skeleton::breadth_first_thinning(surface, nbh,
- is_simple_p,
- mln::topo::detach_cell<D, G>,
- constraint_p);
/*---------.
| Output. |
`---------*/
/* FIXME: This does not work (yet).
- Use workaround mln::io::off::save_bin_alt instead (bad!) */
+ Use workaround mln::io::off::save_bin_alt instead (bad!)
+
+ Moreover, even if it worked, it would not have the same meaning
+ as mln::io::off::save_bin_alt. Maybe the latter is useful, after
+ all. But we need to factor it with the code of
+ mln::io::off::save, anyway. */
#if 0
mln::io::off::save(skel | mln::pw::value(skel) == mln::pw::cst(true),
output_filename);
#endif
- mln::io::off::save_bin_alt(skel, output_filename);
+ // FIXME: We have to ``unmorph'' (twice!) SKEL first, since save_bin_alt only
+ // handles complex_image's.
+ mln::io::off::save_bin_alt(skel.unmorph_().unmorph_(), output_filename);
}
diff --git a/milena/apps/mesh-segm-skel/mesh-complex-skel.cc b/milena/apps/mesh-segm-skel/mesh-complex-skel.cc
index b8b7da8..4f5ca78 100644
--- a/milena/apps/mesh-segm-skel/mesh-complex-skel.cc
+++ b/milena/apps/mesh-segm-skel/mesh-complex-skel.cc
@@ -36,7 +36,9 @@
#include <mln/core/image/complex_image.hh>
#include <mln/core/image/complex_neighborhoods.hh>
-#include <mln/core/site_set/p_set.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+#include <mln/core/image/dmorph/mutable_extension_ima.hh>
+#include <mln/core/routine/mutable_extend.hh>
#include <mln/value/label_16.hh>
@@ -210,31 +212,77 @@ main(int argc, char* argv[])
| Skeleton. |
`-----------*/
- mln::topo::is_simple_cell<bin_ima_t> is_simple_p;
- /* FIXME: Cheat! We'd like to iterate on cells of highest
- dimension (2-cells) only, but we cannot constrain the domain of
- the input using image_if (yet) like this
+ // ---------------- //
+ // Skeleton image. //
+ // ---------------- //
+
+ // Predicate type: is a face a triangle (2-face)?
+ typedef mln::topo::is_n_face<mln_psite_(bin_ima_t), D> is_a_triangle_t;
+ is_a_triangle_t is_a_triangle;
+ // Surface image type, of which domain is restricted to triangles.
+ typedef mln::image_if<bin_ima_t, is_a_triangle_t> bin_triangle_only_ima_t;
+ // Surface image type, of which iteration (not domain) is restricted
+ // to triangles.
+ typedef mln::mutable_extension_ima<bin_triangle_only_ima_t, bin_ima_t>
+ bin_triangle_ima_t;
+ // FIXME: Find a shorter name (skel_ima ? Careful, there is already a `skel' image below).
+ bin_triangle_ima_t bin_triangle_ima =
+ mln::mutable_extend((surface | is_a_triangle).rw(), surface);
+
+ // ------------------------ //
+ // Simple point predicate. //
+ // ------------------------ //
+
+ // Neighborhood type returning the set of (n-1)- and (n+1)-faces
+ // adjacent to a an n-face.
+ typedef mln::complex_lower_higher_neighborhood<D, G> adj_nbh_t;
+ // Neighborhood type returning the set of (n-1)-faces adjacent to a
+ // an n-face.
+ typedef mln::complex_lower_neighborhood<D, G> lower_adj_nbh_t;
+ // Neighborhood type returning the set of (n+1)-faces adjacent to a
+ // an n-face.
+ typedef mln::complex_higher_neighborhood<D, G> higher_adj_nbh_t;
+ // Predicate type: is a triangle (2-face) simple?
+ typedef mln::topo::is_simple_cell< bin_triangle_ima_t,
+ adj_nbh_t,
+ lower_adj_nbh_t,
+ higher_adj_nbh_t >
+ is_simple_triangle_t;
+ is_simple_triangle_t is_simple_triangle;
+
+ // ------------------------------- //
+ // Simple point detach procedure. //
+ // ------------------------------- //
+
+ // Type of adjacency relationships between faces of immediately
+ // lower and higher dimensions.
+ adj_nbh_t adj_nbh;
+ // Functor detaching a cell.
+ mln::topo::detach_cell<bin_triangle_ima_t, adj_nbh_t> detach(adj_nbh);
+
+ mln_concrete_(bin_triangle_ima_t) skel =
+ mln::topo::skeleton::breadth_first_thinning(bin_triangle_ima,
+ nbh,
+ is_simple_triangle,
+ detach);
- breadth_first_thinning(surface | is_n_face<2>, nbh, is_simple_p);
-
- As a workaround, we use the constraint predicate of the
- skeleton routine to restrict the iteration to 2-cells. */
- mln::topo::is_n_face<mln_psite_(bin_ima_t), bin_ima_t::dim> constraint_p;
- bin_ima_t skel =
- mln::topo::skeleton::breadth_first_thinning(surface, nbh,
- is_simple_p,
- mln::topo::detach_cell<D, G>,
- constraint_p);
/*---------.
| Output. |
`---------*/
/* FIXME: This does not work (yet).
- Use workaround mln::io::off::save_bin_alt instead (bad!) */
+ Use workaround mln::io::off::save_bin_alt instead (bad!)
+
+ Moreover, even if it worked, it would not have the same meaning
+ as mln::io::off::save_bin_alt. Maybe the latter is useful, after
+ all. But we need to factor it with the code of
+ mln::io::off::save, anyway. */
#if 0
mln::io::off::save(skel | mln::pw::value(skel) == mln::pw::cst(true),
output_filename);
#endif
- mln::io::off::save_bin_alt(skel, output_filename);
+ // FIXME: We have to ``unmorph'' (twice!) SKEL first, since save_bin_alt only
+ // handles complex_image's.
+ mln::io::off::save_bin_alt(skel.unmorph_().unmorph_(), output_filename);
}
--
1.5.6.5
* mln/geom/complex_geometry.hh
(mln::geom::complex_geometry<D, P>::reserve): New.
Use it...
* mln/io/off/load.hh: ...here.
Aesthetic changes.
---
milena/ChangeLog | 10 ++++++++++
milena/mln/geom/complex_geometry.hh | 17 +++++++++++++++--
milena/mln/io/off/load.hh | 30 +++++++++++++++---------------
3 files changed, 40 insertions(+), 17 deletions(-)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index 4697665..b1288ef 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,3 +1,13 @@
+2011-03-01 Roland Levillain <roland(a)lrde.epita.fr>
+
+ Add a pre-allocation routine to mln::geom::complex_geometry.
+
+ * mln/geom/complex_geometry.hh
+ (mln::geom::complex_geometry<D, P>::reserve): New.
+ Use it...
+ * mln/io/off/load.hh: ...here.
+ Aesthetic changes.
+
2010-07-28 Roland Levillain <roland(a)lrde.epita.fr>
Regen Milena's Makefile helpers.
diff --git a/milena/mln/geom/complex_geometry.hh b/milena/mln/geom/complex_geometry.hh
index f1c604c..7ca99f8 100644
--- a/milena/mln/geom/complex_geometry.hh
+++ b/milena/mln/geom/complex_geometry.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2008, 2009, 2011 EPITA Research and Development
+// Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -43,7 +44,7 @@
/* FIXME: Also provide functors where the locations are computed using
- a function (useful for a complex on a regular grid/support. */
+ a function (useful for a complex on a regular grid/support). */
/* FIXME: This class could probably be turned into something more
generic, usable for other other purpose, e.g. attaching sites to
@@ -106,6 +107,9 @@ namespace mln
/// Retrieve the site associated to \a f.
site operator()(const mln::topo::face<D>& f) const;
+ /// Pre-allocate memory.
+ void reserve(size_t n);
+
private:
mln::util::tracked_ptr< internal::complex_geometry_data<P> > data_;
};
@@ -170,6 +174,15 @@ namespace mln
return s;
}
+ template <unsigned D, typename P>
+ inline
+ void
+ complex_geometry<D, P>::reserve(size_t n)
+ {
+ mln_precondition(data_);
+ data_->zero_faces_geom.reserve(n);
+ }
+
# endif // ! MLN_INCLUDE_ONLY
} // end of mln::geom
diff --git a/milena/mln/io/off/load.hh b/milena/mln/io/off/load.hh
index c9c4af7..e66ae28 100644
--- a/milena/mln/io/off/load.hh
+++ b/milena/mln/io/off/load.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2008, 2009, 2011 EPITA Research and Development
+// Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -296,17 +297,17 @@ namespace mln
/* ``The vertices are listed with x, y, z coordinates, written
one per line.'' */
- /* FIXME: We should have a faster way to create a bunch of
- 0-faces (vertices). */
- for (unsigned v = 0; v < nvertices; ++v)
- c.add_face();
-
typedef point3df P;
typedef mln_coord_(P) C;
typedef geom::complex_geometry<D, P> G;
G geom;
+ geom.reserve(nvertices);
for (unsigned v = 0; v < nvertices; ++v)
{
+ // Create a 0-face.
+ c.add_face();
+
+ // Record the geometry (point) associated to this vertex.
C x, y, z;
istr >> &self::eat_comment >> x
>> &self::eat_comment >> y
@@ -346,10 +347,10 @@ namespace mln
topo::n_faces_set<1, D> face_edges_set;
face_edges_set.reserve(nface_vertices);
- // The first vertex id of the face.
+ // Remember the first vertex id of the face.
unsigned first_vertex_id;
istr >> &self::eat_comment >> first_vertex_id;
- // The current vertex id initialized with the first id.
+ // The current vertex id, initialized with the first id.
unsigned vertex_id = first_vertex_id;
if (first_vertex_id >= nvertices)
{
@@ -366,7 +367,7 @@ namespace mln
mesh/complex. */
unsigned next_vertex_id;
/* When V is the id of the last vertex of the face F,
- set next_vertex_id to first_vertex_id; otherwise,
+ set NEXT_VERTEX_ID to FIRST_VERTEX_ID; otherwise,
read it from the input. */
if (v == nface_vertices - 1)
next_vertex_id = first_vertex_id;
@@ -376,7 +377,7 @@ namespace mln
if (next_vertex_id >= nvertices)
{
std::cerr << me << ": `" << filename
- << "': invalid vertex id "
+ << "': invalid vertex id: "
<< next_vertex_id << std::endl;
std::exit(1);
}
@@ -386,15 +387,14 @@ namespace mln
topo::n_face<0, D> next_vertex(c, next_vertex_id);
// The current edge.
topo::algebraic_n_face<1, D> edge;
- // If the edge has been constructed yet, create it;
- // otherwise, retrieve its id from the complex.
+ // If the edge has not been constructed yet, create
+ // it; otherwise, retrieve its id from the complex.
if (!complex_edges[vertex_id][next_vertex_id])
{
complex_edges[vertex_id][next_vertex_id] = true;
complex_edges[next_vertex_id][vertex_id] = true;
edge =
- make_algebraic_n_face(c.add_face(vertex -
- next_vertex),
+ make_algebraic_n_face(c.add_face(vertex - next_vertex),
true);
}
else
@@ -422,7 +422,7 @@ namespace mln
// Site set.
domain s(c, geom);
- // Values
+ // Values.
values vs;
exact(this)->assign(vs, s);
--
1.5.6.5
* mln/io/vtk/load.hh: New file.
Include it...
* mln/io/vtk/all.hh: ...here.
* tests/io/vtk/load_bin.cc,
* tests/io/vtk/load_save_bin.cc:
New.
* tests/io/vtk/Makefile.am (check_PROGRAMS): Add load_bin and
load_save_bin.
(load_bin_SOURCES, load_save_bin_SOURCES): New.
(MOSTLYCLEANFILES): Add load_save_bin-out.vtk.
---
milena/ChangeLog | 15 +
milena/mln/io/vtk/all.hh | 3 +-
milena/mln/io/vtk/load.hh | 615 +++++++++++++++++++++++++
milena/tests/io/vtk/Makefile.am | 7 +-
milena/tests/io/{off => vtk}/load_bin.cc | 12 +-
milena/tests/io/{off => vtk}/load_save_bin.cc | 15 +-
6 files changed, 654 insertions(+), 13 deletions(-)
create mode 100644 milena/mln/io/vtk/load.hh
copy milena/tests/io/{off => vtk}/load_bin.cc (87%)
copy milena/tests/io/{off => vtk}/load_save_bin.cc (80%)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index 8f604a9..690a83b 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,20 @@
2011-03-01 Roland Levillain <roland(a)lrde.epita.fr>
+ Add preliminary VTK input for binary images.
+
+ * mln/io/vtk/load.hh: New file.
+ Include it...
+ * mln/io/vtk/all.hh: ...here.
+ * tests/io/vtk/load_bin.cc,
+ * tests/io/vtk/load_save_bin.cc:
+ New.
+ * tests/io/vtk/Makefile.am (check_PROGRAMS): Add load_bin and
+ load_save_bin.
+ (load_bin_SOURCES, load_save_bin_SOURCES): New.
+ (MOSTLYCLEANFILES): Add load_save_bin-out.vtk.
+
+2011-03-01 Roland Levillain <roland(a)lrde.epita.fr>
+
Add VTK-format meshes.
* mesh/tetrahedron.vtk,
diff --git a/milena/mln/io/vtk/all.hh b/milena/mln/io/vtk/all.hh
index 6ff98ce..74d37c8 100644
--- a/milena/mln/io/vtk/all.hh
+++ b/milena/mln/io/vtk/all.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2010, 2011 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -41,6 +41,7 @@ namespace mln
}
+# include <mln/io/vtk/load.hh>
# include <mln/io/vtk/save.hh>
#endif // ! MLN_IO_VTK_ALL_HH
diff --git a/milena/mln/io/vtk/load.hh b/milena/mln/io/vtk/load.hh
new file mode 100644
index 0000000..ec078cb
--- /dev/null
+++ b/milena/mln/io/vtk/load.hh
@@ -0,0 +1,615 @@
+// Copyright (C) 2011 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.
+
+#ifndef MLN_IO_VTK_LOAD_HH
+# define MLN_IO_VTK_LOAD_HH
+
+/// \file
+/// Input saving function for VTK files.
+///
+/// \see http://www.vtk.org/VTK/img/file-formats.pdf
+/// \see http://dunne.uni-hd.de/VisuSimple/documents/vtkfileformat.html
+///
+/// The routines of this file only supported a subset of the file
+/// format(s) defined in the VTK documentation.
+
+# include <cstdlib>
+# include <iostream>
+# include <fstream>
+# include <sstream>
+# include <string>
+
+# include <mln/core/alias/complex_image.hh>
+
+
+namespace mln
+{
+
+ namespace io
+ {
+
+ namespace vtk
+ {
+
+ /** \brief Load a (binary) VTK image into a complex image.
+
+ \param[in] ima A reference to the image to construct.
+ \param[in] filename The name of the file to load.
+ */
+ void load(bin_2complex_image3df& ima, const std::string& filename);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ // FIXME: To be put elsewehre (factored), or encapsulated in a
+ // class containing the `load' routine as well.
+ namespace internal
+ {
+
+ inline
+ void
+ error(const std::string& caller, const std::string& filename,
+ const std::string& message)
+ {
+ std::cerr << caller << ": `" << filename << "': "
+ << message << std::endl;
+ std::exit(1);
+ }
+
+ inline
+ void
+ accept(const std::string& token, const std::string& expected,
+ const std::string& caller, const std::string& filename,
+ const std::string& message)
+ {
+ if (token != expected)
+ error(caller, filename, message);
+ }
+
+ // Likewise, with default message.
+ inline
+ void
+ accept(const std::string& token, const std::string& expected,
+ const std::string& caller, const std::string& filename)
+ {
+ accept(token, expected, caller, filename,
+ std::string("parse error (`") + expected + "' expected, "
+ + "got `" + token + "').");
+ }
+
+ // FIXME: To be moved elsewhere.
+ /// Create a string from X using operator<<.
+ template <typename T>
+ std::string
+ str(const T& x)
+ {
+ std::stringstream s;
+ s << x;
+ return s.str();
+ }
+
+ } // end of namespace mln::io::vtk::internal
+
+
+ inline
+ void
+ load(bin_2complex_image3df& ima, const std::string& filename)
+ {
+ typedef bin_2complex_image3df I;
+
+ const std::string me = "mln::io::off::load";
+
+ std::ifstream istr(filename.c_str());
+ if (!istr)
+ {
+ std::cerr << me << ": `" << filename << "' not found."
+ << std::endl;
+ /* FIXME: Too violent. We should allow the use of
+ exceptions, at least to have Milena's code behave
+ correctly in interpreted environments (std::exit() or
+ std::abort() causes the termination of a Python
+ interpreter, for instance!). */
+ std::exit(1);
+ }
+
+ /*---------.
+ | Header. |
+ `---------*/
+
+ /* ``The legacy VTK file formats consist of five basic
+ parts.'' */
+
+ /* ``1. The first part is the file version and
+ identifier. This part contains the single line:
+
+ # vtk DataFile Version x.x.
+
+ This line must be exactly as shown with the
+ exception of the version number x.x, which will vary
+ with different releases of VTK. (Note: the current
+ version number is 3.0. Version 1.0 and 2.0 files are
+ compatible with version 3.0 files.)'' */
+ std::string version;
+ std::getline(istr, version);
+ // FIXME: Check the format of VERSION.
+
+ /* ``2. The second part is the header. The header consists
+ of a character string terminated by end-of-line
+ character `\n'. The header is 256 characters
+ maximum. The header can be used to describe the data
+ and include any other pertinent information.'' */
+ std::string header;
+ std::getline(istr, header);
+
+ /* ``3. The next part is the file format. The file format
+ describes the type of file, either ASCII or
+ binary. On this line the single word ASCII or BINARY
+ must appear.'' */
+ std::string format;
+ istr >> format;
+ if (format == "BINARY")
+ internal::error(me, filename,
+ "`BINARY' VTK format is not supported "
+ "(only `ASCII' is supported yet).");
+ else if (format != "ASCII")
+ internal::error(me, filename,
+ std::string("invalid file format: `")
+ + format + "'.");
+
+ /*-------.
+ | Data. |
+ `-------*/
+
+ /* ``4. The fourth part is the dataset structure. The
+ geometry part describes the geometry and topology of
+ the dataset. This part begins with a line containing
+ the keyword DATASET followed by a keyword describing
+ the type of dataset. Then, depending upon the type
+ of dataset, other keyword/data combinations define
+ the actual data.''
+
+ [...]
+
+ Dataset Format. The Visualization Toolkit supports
+ five different dataset formats: structured points,
+ structured grid, rectilinear grid, unstructured
+ grid, and polygonal data.'' */
+
+ std::string dataset_keyword, dataset_type;
+ istr >> dataset_keyword >> dataset_type;
+ internal::accept(dataset_keyword, "DATASET", me, filename);
+ internal::accept(dataset_type, "POLYDATA", me, filename,
+ "unsupported dataset structure "
+ "(only `POLYDATA' is supported yet).");
+
+ // --------- //
+ // Complex. //
+ // --------- //
+
+ const unsigned D = 2;
+ topo::complex<D> c;
+
+ /* ``* Polygonal Data
+ The polygonal dataset consists of arbitrary
+ combinations of surface graphics primitives
+ vertices (and polyvertices), lines (and
+ polylines), polygons (of various types), and
+ triangle strips. Polygonal data is defined by
+ the POINTS, VERTICES, LINES, POLYGONS, or
+ TRIANGLE_STRIPS sections. The POINTS definition
+ is the same as we saw for structured grid
+ datasets.'' */
+
+ // ----------------------------------------- //
+ // Vertices and geometry (point locations). //
+ // ----------------------------------------- //
+
+ std::string points_keyword;
+ unsigned npoints;
+ std::string points_datatype;
+ istr >> points_keyword >> npoints >> points_datatype;
+ internal::accept(points_keyword, "POINTS", me, filename);
+ internal::accept(points_datatype, "float", me, filename,
+ "unsupported points data type "
+ "(only `float' is supported yet).");
+
+ typedef point3df P;
+ typedef mln_coord_(P) C;
+ typedef mln_geom_(I) G;
+ G geom;
+ geom.reserve(npoints);
+ for (unsigned i = 0; i < npoints; ++i)
+ {
+ // Create a 0-face.
+ c.add_face();
+
+ // Record the geometry (point) associated to this vertex.
+ C x, y, z;
+ istr >> x >> y >> z;
+ geom.add_location(P(x, y, z));
+ }
+
+ /* ``The VERTICES, LINES, POLYGONS, or
+ TRIANGLE_STRIPS keywords define the polygonal
+ dataset topology. Each of these keywords
+ requires two parameters: the number of cells `n'
+ and the size of the cell list `size'. The cell
+ list size is the total number of integer values
+ required to represent the list (i.e., sum of
+ `numPoints' and connectivity indices over each
+ cell). None of the keywords VERTICES, LINES,
+ POLYGONS, or TRIANGLE_STRIPS is required.'' */
+
+ // ---------- //
+ // Vertices. //
+ // ---------- //
+
+ /* FIXME: Add another constraint: a bijection between
+ VERTICES and POINTS shall exist. Update the following
+ comment accordingly. */
+
+ /* The VTK file format contains both a POINTS and a VERTICES
+ section. Despite its name, the former is used to create
+ the set of vertices of the complex (because POINTS are
+ later used to form line (edges) and polygons. The latter
+ (VERTICES) is used to attach values (data) to 0-faces,
+ and act as an indirection toward POINTS. The fact is,
+ most VTK file we use have a VERTICES section where the
+ N-th line (starting at 0) contains
+
+ 1 N
+
+ which makes the VERTICES section kind of useless.
+ However, we have to parse it and take it into account for
+ the sake of the file format. */
+
+ std::string vertices_keyword;
+ unsigned nvertices, vertices_size;
+ istr >> vertices_keyword >> nvertices >> vertices_size;
+ internal::accept(vertices_keyword, "VERTICES", me, filename);
+
+ /* Each vertex requires two numbers: the cardinal of
+ its ends (which is always 1) and the indices of the
+ point among the POINTS section. Hence the total
+ number of values in the VERTEX section is
+ nvertices * 2. */
+ // FIXME: This test is not really robust: we should involve
+ // the VERTICES_SIZE value in the parsing process.
+ if (vertices_size != nvertices * 2)
+ internal::error(me, filename, "ill-formed `VERTICES' section.");
+
+ // Vertices built on points.
+ std::vector<unsigned> vertices;
+ vertices.reserve(npoints);
+ for (unsigned i = 0; i < nvertices; ++i)
+ {
+ unsigned numpoints, p;
+ istr >> numpoints >> p;
+ if (numpoints != 1)
+ internal::error(me, filename, "ill-formed vertex item.");
+ if (p > npoints)
+ internal::error(me, filename,
+ "point id out of bounds in vertex item.");
+ vertices.push_back(p);
+ }
+
+ // ------- //
+ // Edges. //
+ // ------- //
+
+ std::string lines_keyword;
+ unsigned nedges, edges_size;
+ istr >> lines_keyword >> nedges >> edges_size;
+ internal::accept(lines_keyword, "LINES", me, filename);
+
+ /* Each edge requires three numbers: the cardinal of
+ its ends (which is always 2) and the indices of
+ these ends among the POINTS section. Hence the
+ total number of values in the LINES section is
+ nedges * 3. */
+ // FIXME: This test is not really robust: we should involve
+ // the EDGES_SIZE value in the parsing process.
+ if (edges_size != nedges * 3)
+ internal::error(me, filename, "ill-formed `LINES' section.");
+
+ // An adjacenty matrix recording the edges seen so far.
+ typedef std::vector< std::vector<bool> > complex_edges_t;
+ complex_edges_t complex_edges (npoints,
+ std::vector<bool>(npoints, false));
+
+ // Populate the complex and the adjacency matrix
+ for (unsigned i = 0; i < nedges; ++i)
+ {
+ // Create a 1-face.
+ unsigned numpoints, p1, p2;
+ istr >> numpoints >> p1 >> p2;
+ if (numpoints != 2)
+ internal::error(me, filename, "ill-formed line item.");
+ if (p1 > npoints || p2 > npoints)
+ internal::error(me, filename,
+ "point id out of bounds in line item.");
+ topo::n_face<0, D> v1(c, p1);
+ topo::n_face<0, D> v2(c, p2);
+ c.add_face(v1 - v2);
+ // Tag this edged (and its opposite) in the adjacency matrix.
+ complex_edges[p1][p2] = true;
+ complex_edges[p2][p1] = true;
+ }
+
+ // ---------- //
+ // Polygons. //
+ // ---------- //
+
+ std::string polygons_keyword;
+ unsigned npolygons, polygons_size;
+ istr >> polygons_keyword >> npolygons >> polygons_size;
+ internal::accept(polygons_keyword, "POLYGONS", me, filename);
+
+ // Read polygon values.
+ unsigned npolygons_values = 0;
+
+ for (unsigned i = 0; i < npolygons; ++i)
+ {
+ unsigned numpoints;
+ istr >> numpoints;
+ ++npolygons_values;
+ if (numpoints <= 2)
+ {
+ internal::error(me, filename,
+ std::string("ill-formed face (having ")
+ + internal::str(numpoints)
+ + (numpoints < 2 ? "vertex" : "vertices")
+ + ").");
+ }
+
+ /* FIXME: This part (computing the set of edges on which
+ a polygon is built) is also shared by
+ mln::io::off::load; we can probably factor it. */
+
+ // The edges of the face.
+ topo::n_faces_set<1, D> face_edges_set;
+ face_edges_set.reserve(numpoints);
+
+ // Remember the first point id of the face.
+ unsigned first_point_id;
+ istr >> first_point_id;
+ ++npolygons_values;
+ // The current point id, initialized with the first id.
+ unsigned point_id = first_point_id;
+ if (point_id >= npoints)
+ internal::error(me, filename,
+ std::string("invalid point id: `")
+ + internal::str(point_id) + "'.");
+ for (unsigned p = 0; p < numpoints; ++p)
+ {
+ /* The next point id. The pair (point_id,
+ next_point_id) is an edge of the
+ mesh/complex. */
+ unsigned next_point_id;
+ /* When P is the id of the last point of the face,
+ set NEXT_POINT_ID to FIRST_VERTEX_ID; otherwise,
+ read it from the input. */
+ if (p == numpoints - 1)
+ next_point_id = first_point_id;
+ else
+ {
+ istr >> next_point_id;
+ ++npolygons_values;
+ if (next_point_id >= npoints)
+ internal::error(me, filename,
+ std::string("invalid point id: `")
+ // In-line ``itoa'' of NEXT_POINT_ID.
+ + internal::str(next_point_id) + "'.");
+ }
+ // The ends of the current edge.
+ topo::n_face<0, D> vertex(c, point_id);
+ topo::n_face<0, D> next_vertex(c, next_point_id);
+ // The current edge.
+ topo::algebraic_n_face<1, D> edge;
+ /* The edge (POINT_ID, NEXT_POINT_ID) (or its
+ opposite (NEXT_POINT_ID, POINT_ID) must have been
+ inserted previously. In other words, the sides
+ of POLYGON items must belong to the list of
+ LINES. */
+ if (!complex_edges[point_id][next_point_id])
+ internal::error(me, filename,
+ "ill-formed polygon, having a side "
+ "(edge) not part of the list of LINES.");
+ edge = topo::edge(vertex, next_vertex);
+ mln_assertion(edge.is_valid());
+ // Add this edge a side of the polygon.
+ face_edges_set += edge;
+ // Next vertex.
+ point_id = next_point_id;
+ }
+
+ // Add face.
+ c.add_face(face_edges_set);
+ }
+
+ // FIXME: This test is not really robust: we should involve
+ // the POLYGONS_SIZE value in the parsing process.
+ if (polygons_size != npolygons_values)
+ internal::error(me, filename, "ill-formed `LINES' section.");
+
+ // -------------------- //
+ // Dataset attributes. //
+ // -------------------- //
+
+ /* ``5. The final part describes the dataset attributes.
+ This part begins with the keywords POINT_DATA or
+ CELL_DATA, followed by an integer number specifying
+ the number of points or cells, respectively. (It
+ doesn't matter whether POINT_DATA or CELL_DATA comes
+ first.) Other keyword/data combinations then define
+ the actual dataset attribute values (i.e., scalars,
+ vectors, tensors, normals, texture coordinates, or
+ field data).'' */
+ std::string dataset_kind;
+ istr >> dataset_kind;
+ if (dataset_kind == "POINT_DATA")
+ internal::error(me, filename,
+ "`POINT_DATA' datasets are not supported "
+ "(only `CELL_DATA' datasets are supported yet).");
+ else if (dataset_kind != "CELL_DATA")
+ internal::error(me, filename,
+ std::string("invalid dataset kind: `")
+ + dataset_kind + "'.");
+ unsigned nfaces = nvertices + nedges + npolygons;
+ unsigned ncell_data;
+ istr >> ncell_data;
+ if (ncell_data != nfaces)
+ internal::error(me, filename,
+ std::string("wrong number of dataset attributes ")
+ + "(expected " + internal::str(nfaces)
+ + ", got " + internal::str(ncell_data) + ").");
+
+ /* ``Dataset Attribute Format. The Visualization Toolkit
+ supports the following dataset attributes: scalars
+ (one to four components), vectors, normals,
+ texture coordinates (1D, 2D, and 3D), 3 x 3
+ tensors, and field data. In addition, a lookup
+ table using the RGBA color specification,
+ associated with the scalar data, can be defined as
+ well. Dataset attributes are supported for both
+ points and cells.
+ Each type of attribute data has a `dataName'
+ associated with it. This is a character string
+ (without embedded whitespace) used to identify a
+ particular data. The `dataName' is used by the VTK
+ readers to extract data. As a result, more than
+ one attribute data of the same type can be
+ included in a file. For example, two different
+ scalar fields defined on the dataset points,
+ pressure and temperature, can be contained in the
+ same file. (If the appropriate dataName is not
+ specified in the VTK reader, then the first data
+ of that type is extracted from the file.)
+
+ * Scalars
+ Scalar definition includes specification of a
+ lookup table. The definition of a lookup table
+ is optional.
+
+ [...]
+
+ SCALARS dataName dataType numComp
+ LOOKUP_TABLE tableName
+ s0
+ s1
+ ...
+ sn-1''
+
+ Note: values accepted by Paraview 3.8 for `dataType' are:
+ "bit", "char", "unsigned_char", "short", "unsigned_short",
+ "vtkidtype", "int", "unsigned_int", "long",
+ "unsigned_long", "vtktypeint64", "vtktypeuint64", "float",
+ "double", "string", "utf8_string" and "variant". */
+
+ std::string scalars_keyword, data_name, data_type;
+ istr >> scalars_keyword >> data_name >> data_type;
+ internal::accept(scalars_keyword, "SCALARS", me, filename);
+ // FIXME: As in the rest of this routine's code, we only
+ // handle the case of binary values (for the moment).
+ if (data_type != "bit")
+ internal::error(me, filename,
+ std::string("unsupported data (value) type : `")
+ + data_type + "' (only `bit' is supported yet).");
+
+ std::string lookup_table_keyword, lookup_table_name;
+ istr >> lookup_table_keyword >> lookup_table_name;
+ internal::accept(lookup_table_keyword, "LOOKUP_TABLE", me, filename);
+
+ // Values.
+ typedef mln_value_(I) V;
+ typedef metal::vec<D + 1, std::vector<V> > values;
+ values vs;
+
+ // Populate values associated to 0-faces.
+ /* FIXME: The default value used (here 0) depends on the
+ images's value type. */
+ vs[0].resize(npoints, 0);
+ // Iterate on VERTICES (not ``points''). We cannot copy
+ // VERTICES into VS[0] as-is, since VS[0] corresponds to
+ // points.
+ for (std::vector<unsigned>::const_iterator v = vertices.begin();
+ v != vertices.end(); ++ v)
+ {
+ V value;
+ istr >> value;
+ // The point id on which the vertex V is built.
+ unsigned p = *v;
+ vs[0][p] = value;
+ }
+
+ // Populate values associated to 1-faces.
+ for(unsigned e = 0; e < nedges; ++e)
+ {
+ V value;
+ istr >> value;
+ vs[1].push_back(value);
+ }
+
+ // Populate values associated to 2-faces.
+ for(unsigned f = 0; f < npolygons; ++f)
+ {
+ V value;
+ istr >> value;
+ vs[2].push_back(value);
+ }
+
+ /*--------.
+ | Image. |
+ `--------*/
+
+ // Site set.
+ typedef mln_domain_(I) domain;
+ domain s(c, geom);
+
+ // Image.
+ ima.init_(s, vs);
+
+ /*--------------.
+ | End of file. |
+ `--------------*/
+
+ // FIXME: Eat comments and whitespace, and check the end of
+ // the file has been reached.
+ // ...
+ istr.close();
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+ } // end of namespace mln::io::vtk
+
+ } // end of namespace mln::io
+
+} // end of namespace mln
+
+
+#endif // ! MLN_IO_VTK_LOAD_HH
diff --git a/milena/tests/io/vtk/Makefile.am b/milena/tests/io/vtk/Makefile.am
index 548f1b5..9554a4b 100644
--- a/milena/tests/io/vtk/Makefile.am
+++ b/milena/tests/io/vtk/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE).
+# Copyright (C) 2010, 2011 EPITA Research and Development Laboratory (LRDE).
#
# This file is part of Olena.
#
@@ -16,13 +16,16 @@
include $(top_srcdir)/milena/tests/tests.mk
-check_PROGRAMS = save
+check_PROGRAMS = load_bin load_save_bin save
+load_bin_SOURCES = load_bin.cc
+load_save_bin_SOURCES = load_save_bin.cc
save_SOURCES = save.cc
TESTS = $(check_PROGRAMS)
MOSTLYCLEANFILES = \
+ load_save_bin-out.vtk \
save-tetrahedron-bool.vtk \
save-tetrahedron-int_u8.vtk \
save-tetrahedron-unsigned.vtk \
diff --git a/milena/tests/io/off/load_bin.cc b/milena/tests/io/vtk/load_bin.cc
similarity index 87%
copy from milena/tests/io/off/load_bin.cc
copy to milena/tests/io/vtk/load_bin.cc
index 3d35a44..a4ddf10 100644
--- a/milena/tests/io/off/load_bin.cc
+++ b/milena/tests/io/vtk/load_bin.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -23,22 +23,26 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
+/// \file
+/// \brief Exercise mln::io::vtk::load on binary mesh images.
+
#include <algorithm>
#include <iterator>
#include <iostream>
-#include <mln/io/off/load.hh>
+#include <mln/io/vtk/load.hh>
#include "tests/data.hh"
-int main()
+int
+main()
{
using namespace mln;
typedef bin_2complex_image3df ima_t;
ima_t ima;
- io::off::load(ima, MLN_MESH_DIR "/tetrahedron.off");
+ io::vtk::load(ima, MLN_MESH_DIR "/tetrahedron.vtk");
std::cout << ima.domain().cplx() << std::endl;
diff --git a/milena/tests/io/off/load_save_bin.cc b/milena/tests/io/vtk/load_save_bin.cc
similarity index 80%
copy from milena/tests/io/off/load_save_bin.cc
copy to milena/tests/io/vtk/load_save_bin.cc
index 3287910..a4fc777 100644
--- a/milena/tests/io/off/load_save_bin.cc
+++ b/milena/tests/io/vtk/load_save_bin.cc
@@ -1,5 +1,4 @@
-// Copyright (C) 2008, 2009, 2010 EPITA Research and Development
-// Laboratory (LRDE)
+// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -24,12 +23,16 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
+/// \file
+/// \brief Exercise mln::io::vtk::load and mln::io::vtk::save on
+/// binary mesh images.
+
#include <algorithm>
#include <iterator>
#include <iostream>
-#include <mln/io/off/load.hh>
-#include <mln/io/off/save.hh>
+#include <mln/io/vtk/load.hh>
+#include <mln/io/vtk/save.hh>
#include "tests/data.hh"
@@ -40,6 +43,6 @@ int main()
typedef bin_2complex_image3df ima_t;
ima_t ima;
- io::off::load(ima, MLN_MESH_DIR "/tetrahedron.off");
- io::off::save(ima, "load_save_bin-out.off");
+ io::vtk::load(ima, MLN_MESH_DIR "/tetrahedron.vtk");
+ io::vtk::save(ima, "load_save_bin-out.vtk");
}
--
1.5.6.5