* doc/tutorial/Makefile.am: fix wrong include path.
* mln/canvas/morpho/algebraic_union_find.hh: add missing
MLN_INCLUDE_ONLY guards.
* mln/core/clock_neighb.hh: add missing is_valid().
* mln/labeling/mean_values.hh,
* mln/fun/v2v/rgb_to_hsl.hh: add missing includes.
* mln/fun/x2x/rotation.hh: add more comments.
* mln/io/dump/load.hh: check if the loaded image is valid.
* mln/labeling/level.hh: Fix warnings.
* mln/registration/all.hh: update comments.
* mln/registration/icp2.hh: rename as...
* mln/registration/icp.hh: ... this.
* mln/registration/registration.hh: fix wrong call to
registration_tests.
* mln/util/graph.hh: add NDEBUG guards.
* mln/value/rgb.hh: add missing forward declaration.
---
milena/ChangeLog | 32 +
milena/doc/tutorial/Makefile.am | 22 +-
milena/mln/canvas/morpho/algebraic_union_find.hh | 12 +
milena/mln/core/clock_neighb.hh | 18 +-
milena/mln/fun/v2v/rgb_to_hsl.hh | 3 +-
milena/mln/fun/x2x/rotation.hh | 3 +
milena/mln/io/dump/load.hh | 2 +
milena/mln/labeling/level.hh | 4 +-
milena/mln/labeling/mean_values.hh | 9 +-
milena/mln/registration/all.hh | 10 +-
milena/mln/registration/icp.hh | 723 ++++++++++++++++++----
milena/mln/registration/icp2.hh | 693 ---------------------
milena/mln/registration/registration.hh | 9 +-
milena/mln/util/graph.hh | 2 +
milena/mln/value/rgb.hh | 5 +
15 files changed, 692 insertions(+), 855 deletions(-)
delete mode 100644 milena/mln/registration/icp2.hh
diff --git a/milena/ChangeLog b/milena/ChangeLog
index 1c446cd..6531f76 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,37 @@
2009-02-18 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Various small fixes.
+
+ * doc/tutorial/Makefile.am: fix wrong include path.
+
+ * mln/canvas/morpho/algebraic_union_find.hh: add missing
+ MLN_INCLUDE_ONLY guards.
+
+ * mln/core/clock_neighb.hh: add missing is_valid().
+
+ * mln/labeling/mean_values.hh,
+ * mln/fun/v2v/rgb_to_hsl.hh: add missing includes.
+
+ * mln/fun/x2x/rotation.hh: add more comments.
+
+ * mln/io/dump/load.hh: check if the loaded image is valid.
+
+ * mln/labeling/level.hh: Fix warnings.
+
+ * mln/registration/all.hh: update comments.
+
+ * mln/registration/icp2.hh: rename as...
+ * mln/registration/icp.hh: ... this.
+
+ * mln/registration/registration.hh: fix wrong call to
+ registration_tests.
+
+ * mln/util/graph.hh: add NDEBUG guards.
+
+ * mln/value/rgb.hh: add missing forward declaration.
+
+2009-02-18 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Add support for 3D images in fun::x2v::bilinear.
* mln/fun/x2v/bilinear.hh: Add a new specialization for 3d Images.
diff --git a/milena/doc/tutorial/Makefile.am b/milena/doc/tutorial/Makefile.am
index ceb4534..447dcbe 100644
--- a/milena/doc/tutorial/Makefile.am
+++ b/milena/doc/tutorial/Makefile.am
@@ -41,24 +41,24 @@ tutorial.tex \
tutorial.pdf
.PHONY: regen-dist
-regen-dist: $(srcdir)/headers.stamp
+regen-dist: $(top_srcdir)/headers.stamp
-$(srcdir)/examples/examples.mk: $(srcdir)/headers.stamp
-$(srcdir)/figures/figures.mk: $(srcdir)/headers.stamp
-$(srcdir)/outputs/outputs.mk: $(srcdir)/headers.stamp
-$(srcdir)/samples/samples.mk: $(srcdir)/headers.stamp
+$(top_srcdir)/milena/doc/tutorial/examples/examples.mk: $(top_srcdir)/headers.stamp
+$(top_srcdir)/milena/doc/tutorial/figures/figures.mk: $(top_srcdir)/headers.stamp
+$(top_srcdir)/milena/doc/tutorial/outputs/outputs.mk: $(top_srcdir)/headers.stamp
+$(top_srcdir)/milena/doc/tutorial/samples/samples.mk: $(top_srcdir)/headers.stamp
-EXTRA_DIST += $(srcdir)/headers.stamp
-$(srcdir)/headers.stamp: $(srcdir)/generate_dist_files.sh
+EXTRA_DIST += $(top_srcdir)/headers.stamp
+$(top_srcdir)/headers.stamp: $(top_srcdir)/milena/doc/tutorial/generate_dist_files.sh
@rm -f $@.tmp
@touch $@.tmp
cd $(srcdir) && ./generate_dist_files.sh
@mv -f $@.tmp $@
-include $(srcdir)/examples/examples.mk
-include $(srcdir)/figures/figures.mk
-include $(srcdir)/outputs/outputs.mk
-include $(srcdir)/samples/samples.mk
+include $(top_srcdir)/milena/doc/tutorial/examples/examples.mk
+include $(top_srcdir)/milena/doc/tutorial/figures/figures.mk
+include $(top_srcdir)/milena/doc/tutorial/outputs/outputs.mk
+include $(top_srcdir)/milena/doc/tutorial/samples/samples.mk
EXTRA_DIST += \
tools/sample_utils.hh \
diff --git a/milena/mln/canvas/morpho/algebraic_union_find.hh
b/milena/mln/canvas/morpho/algebraic_union_find.hh
index 34a922c..a19ace8 100644
--- a/milena/mln/canvas/morpho/algebraic_union_find.hh
+++ b/milena/mln/canvas/morpho/algebraic_union_find.hh
@@ -51,6 +51,16 @@ namespace mln
namespace morpho
{
+ template <typename I, typename N, typename F>
+ inline
+ mln_concrete(I)
+ algebraic_union_find(const Image<I>& input,
+ const Neighborhood<N>& nbh,
+ F& f);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
namespace impl
{
@@ -393,6 +403,8 @@ namespace mln
}
+# endif // ! MLN_INCLUDE_ONLY
+
} // end of namespace mln::canvas::morpho
} // end of namespace mln::canvas
diff --git a/milena/mln/core/clock_neighb.hh b/milena/mln/core/clock_neighb.hh
index 2c72506..1474978 100644
--- a/milena/mln/core/clock_neighb.hh
+++ b/milena/mln/core/clock_neighb.hh
@@ -1,5 +1,5 @@
-// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory
-// (LRDE)
+// Copyright (C) 2007, 2008, 2009 EPITA Research and Development
+// Laboratory (LRDE)
//
// This file is part of the Olena Library. This library is free
// software; you can redistribute it and/or modify it under the terms
@@ -105,6 +105,9 @@ namespace mln
/// FIXME: not in constant time!
mln::window<D> win() const;
+ /// Return whether this neighborhood is valid.
+ bool is_valid() const;
+
private:
std::vector<D> vec_;
};
@@ -145,6 +148,17 @@ namespace mln
result.insert(vec_[i]);
return result;
}
+
+ template <typename D>
+ inline
+ bool
+ clock_neighb<D>::is_valid() const
+ {
+ //FIXME: correct?
+ return true;
+ }
+
+
# endif // ! MLN_INCLUDE_ONLY
} // end of namespace mln
diff --git a/milena/mln/fun/v2v/rgb_to_hsl.hh b/milena/mln/fun/v2v/rgb_to_hsl.hh
index 73a68f2..39433f2 100644
--- a/milena/mln/fun/v2v/rgb_to_hsl.hh
+++ b/milena/mln/fun/v2v/rgb_to_hsl.hh
@@ -36,6 +36,8 @@
# include <mln/trait/value_.hh>
+# include <mln/value/rgb8.hh>
+# include <mln/value/rgb16.hh>
namespace mln
{
@@ -43,7 +45,6 @@ namespace mln
// Forward declaration
namespace value
{
- template <unsigned n> struct rgb;
template <typename H, typename S, typename L> class hsl_;
typedef hsl_<float, float, float> hsl_f;
}
diff --git a/milena/mln/fun/x2x/rotation.hh b/milena/mln/fun/x2x/rotation.hh
index 9628c26..9addb50 100644
--- a/milena/mln/fun/x2x/rotation.hh
+++ b/milena/mln/fun/x2x/rotation.hh
@@ -35,6 +35,9 @@
///
/// \todo store the quaternion instead of (axis, alpha)
/// => better precision while composing two rotation matrices.
+///
+/// Conversion from Quaternion to (angle,axis), Source:
+///
http://jeux.developpez.com/faq/matquat/?page=quaternions#Q56
# include <cmath>
# include <mln/core/concept/function.hh>
diff --git a/milena/mln/io/dump/load.hh b/milena/mln/io/dump/load.hh
index 7bbce9e..21d6534 100644
--- a/milena/mln/io/dump/load.hh
+++ b/milena/mln/io/dump/load.hh
@@ -129,6 +129,8 @@ namespace mln
internal::load_header(ima, file);
internal::load_data(ima, file);
+ mln_postcondition(exact(ima).is_valid());
+
trace::exiting("mln::io::dump::load");
}
diff --git a/milena/mln/labeling/level.hh b/milena/mln/labeling/level.hh
index 050b2d0..11b3a06 100644
--- a/milena/mln/labeling/level.hh
+++ b/milena/mln/labeling/level.hh
@@ -121,9 +121,9 @@ namespace mln
bool handles_(unsigned p) const { return input.element(p) == val; }
bool equiv_(unsigned n, unsigned) const { return input.element(n) == val; }
bool labels_(unsigned) const { return true; }
- void do_no_union_(unsigned n, unsigned p) {}
+ void do_no_union_(unsigned, unsigned) {}
void init_attr_(unsigned) {}
- void merge_attr_(unsigned r, unsigned p) {}
+ void merge_attr_(unsigned, unsigned) {}
// end of Requirements.
diff --git a/milena/mln/labeling/mean_values.hh b/milena/mln/labeling/mean_values.hh
index d7c131a..2ea06e4 100644
--- a/milena/mln/labeling/mean_values.hh
+++ b/milena/mln/labeling/mean_values.hh
@@ -29,6 +29,13 @@
# define MLN_LABELING_MEAN_VALUES_HH
# include <mln/core/concept/image.hh>
+# include <mln/core/alias/vec3d.hh>
+
+# include <mln/accu/mean.hh>
+
+# include <mln/labeling/compute.hh>
+
+# include <mln/literal/colors.hh>
/// \file mln/labeling/mean_values.hh
///
@@ -116,7 +123,7 @@ namespace mln
util::array<mln_value(I)> m;
convert::from_to(m_3f, m);
- m[0] = literal::white; //FIXME: handle label 0 correctly.
+ m[0] = literal::yellow; //FIXME: handle label 0 correctly.
mln_concrete(I) output = level::transform(lbl,
convert::to< fun::i2v::array<mln_value(I)> >(m));
diff --git a/milena/mln/registration/all.hh b/milena/mln/registration/all.hh
index dd29f26..bb4e18e 100644
--- a/milena/mln/registration/all.hh
+++ b/milena/mln/registration/all.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of the Olena Library. This library is free
// software; you can redistribute it and/or modify it under the terms
@@ -28,10 +29,9 @@
#ifndef MLN_REGISTRATION_ALL_HH
# define MLN_REGISTRATION_ALL_HH
-/*! \file mln/registration/all.hh
- *
- * \brief File that includes all "point-wise" expression tools.
- */
+/// \file mln/registration/all.hh
+///
+/// File that includes all "point-wise" expression tools.
namespace mln
diff --git a/milena/mln/registration/icp.hh b/milena/mln/registration/icp.hh
index b116b0e..de763ea 100644
--- a/milena/mln/registration/icp.hh
+++ b/milena/mln/registration/icp.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of the Olena Library. This library is free
// software; you can redistribute it and/or modify it under the terms
@@ -28,217 +28,666 @@
#ifndef MLN_REGISTRATION_ICP_HH
# define MLN_REGISTRATION_ICP_HH
-/*! \file mln/registration/icp.hh
- *
- * \brief Register an image over an another using the ICP algorithm.
- */
+/// \file mln/registration/icp.hh
+///
+/// Register an image over an another using the ICP algorithm.
+///
+/// \todo encode distances on 12 bits.
+# include <cmath>
+# include <algorithm>
+
+# include <mln/core/alias/vec3d.hh>
+# include <mln/math/jacobi.hh>
# include <mln/fun/x2x/all.hh>
# include <mln/fun/x2v/all.hh>
-# include <mln/registration/get_rtransf.hh>
-# include <mln/registration/internal/rms.hh>
+# include <mln/convert/to.hh>
+# include <mln/accu/compute.hh>
+# include <mln/accu/center.hh>
+# include <mln/accu/rms.hh>
+# include <mln/trait/image_from_grid.hh>
+# include <mln/set/compute.hh>
+
+//Should be removed when closest_point functors are moved.
+# include <mln/core/alias/neighb3d.hh>
+# include <mln/transform/internal/closest_point_functor.hh>
+# include <mln/canvas/distance_geodesic.hh>
+# include <mln/pw/all.hh>
+
+# include <mln/io/ppm/save.hh>
+# include <mln/io/pbm/save.hh>
+# include <mln/debug/colorize.hh>
+
+# include <mln/literal/black.hh>
+# include <mln/literal/white.hh>
+# include <mln/literal/colors.hh>
+
+# include <mln/core/image/slice_image.hh>
+
+#include <mln/core/image/tr_image.hh>
+#include <mln/core/image/extension_fun.hh>
+
+#include <mln/accu/histo.hh>
+#include <mln/accu/sum.hh>
+#include <mln/debug/histo.hh>
+
+# include <mln/util/timer.hh>
namespace mln
{
- namespace util
+
+ namespace registration
{
- // FIXME: Remove use of this class
- template <unsigned int n, typename T>
- struct buffer
+ //FIXME: used for debug purpose. Should be removed later.
+
+ using namespace fun::x2x;
+
+ /*! Register point in \p c using a function of closest points
+ * \p closest_point.
+ *
+ * \param[in] P_ The cloud of points.
+ * \param[in] X the reference surface.
+ * \param[in] closest_point The function of closest points.
+ * \param[out] qk The rigid transformation obtained.
+ *
+ * \return the rigid transformation which may be use later to create
+ * a registered image.
+ *
+ * WARNING: the function \p closest_point *MUST* take float/double
+ * vector as arguments. Otherwise the resulting transformation may be
+ * wrong due to the truncation of the vector coordinate values.
+ *
+ * \pre \p P_ and \p X must not be empty.
+ *
+ * Reference article: "A Method for Registration of 3-D Shapes", Paul J.
+ * Besl and Neil D. McKay, IEEE, 2, February 1992.
+ *
+ */
+ template <typename P, typename F>
+ std::pair<algebra::quat,mln_vec(P)>
+ icp(const p_array<P>& P_,
+ const p_array<P>& X,
+ const F& closest_point,
+ const algebra::quat& initial_rot,
+ const mln_vec(P)& initial_translation);
+
+
+ template <typename P, typename F>
+ composed< translation<P::dim,float>,rotation<P::dim,float> >
+ icp(const p_array<P>& P_,
+ const p_array<P>& X,
+ const F& closest_point);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ /// Closest point functor based on map distance.
+ template <typename P>
+ class closest_point_with_map
{
- buffer()
- : setted(0)
+ typedef mln_image_from_grid(mln_grid(P), P) I;
+ typedef mln_ch_value(I, P) cp_ima_t;
+ typedef mln_ch_value(I,value::int_u16) dmap_t;
+
+ public:
+
+ closest_point_with_map(const p_array<P>& X)
{
+ box3d box = geom::bbox(X);
+ box.enlarge(1, box.nrows());
+ box.enlarge(2, box.ncols());
+
+ mln_postcondition(box.is_valid());
+
+ std::cout << "Map image defined on " << box << std::endl;
+
+ init(X, box);
}
- void store(T e)
+ closest_point_with_map(const p_array<P>& X, const box<P>& box)
{
- for (unsigned i = 0; i < n-1; i++)
- buf[i+1] = buf[i];
- buf[0] = e;
-
- setted++;
+ init(X, box);
}
- T& operator[](unsigned int i)
+ void init(const p_array<P>& X, const box<P>& box)
{
- assert(i < n && i < setted);
- return buf[i];
+ typedef mln_ch_value(I, bool) model_t;
+ model_t model(box);
+ data::fill(model, false);
+ data::fill((model | X).rw(), true);
+
+ transform::internal::closest_point_functor<model_t> f;
+ util::timer t;
+ t.start();
+ dmap_X_ = canvas::distance_geodesic(model, c6(),
+ mln_max(value::int_u16),
+ f);
+ std::cout << "canvas::distance_geodesic - " << t <<
"s" << std::endl;
+
+ initialize(this->cp_ima_, f.cp_ima);
+ this->cp_ima_ = f.cp_ima;
+
+ mln_postcondition(cp_ima_.is_valid());
+ mln_postcondition(cp_ima_.domain().is_valid());
+ std::cout << "pmin = " << cp_ima_.domain().pmin() <<
std::endl;;
+ std::cout << "pmax = " << cp_ima_.domain().pmax() <<
std::endl;;
+
+#ifndef NDEBUG
+// mln_ch_value(I, bool) debug2(box);
+// data::fill(debug2, false);
+// mln_ch_value(I, value::rgb8) debug(box);
+// mln_piter(p_array<P>) p(X);
+// for_all(p)
+// {
+// debug(p) = debug::internal::random_color(value::rgb8());
+// debug2(p) = true;
+// }
+// io::pbm::save(slice(debug2,0), "debug2-a.ppm");
+//
+// mln_piter(I) pi(cp_ima_.domain());
+// for_all(pi)
+// {
+// debug(pi) = debug(cp_ima_(pi));
+// debug2(pi) = debug2(cp_ima_(pi));
+// }
+//
+// io::pbm::save(slice(debug2,0), "debug2-b.ppm");
+// io::ppm::save(slice(debug,0), "debug.ppm");
+// std::cout << "map saved" << std::endl;
+#endif
}
- const T * get_array()
+ mln_site(I) operator()(const mln_site(I)& p) const
{
- return buf;
+ return cp_ima_(p);
}
+ // Distance map
+ dmap_t dmap_X_;
+ // Closest point image.
+ cp_ima_t cp_ima_;
private:
- T buf[n];
- unsigned int setted;
};
- } // end of namespace mln::util
- namespace registration
- {
+ /// Closest point functor based on map distance.
+ template <typename P>
+ class closest_point_basic
+ {
+ typedef mln_image_from_grid(mln_grid(P), P) I;
+ typedef p_array<P> X_t;
- using namespace fun::x2x;
+ public:
- /*! Register point in \p c using a map of closest points \p map
- *
- * \param[in] c The cloud of points.
- * \param[in] map The map of closest points.
- * \param[in] epsilon ICP stops if sqr_norm(qk - qk-1) /
- * sqr_norm(qk) > epsilon
- * \param[out] qk The rigid transformation obtained.
- *
- * \pre \p ima has to be initialized.
- */
- template <typename P, typename M>
- inline
- composed<rotation<P::dim, float>, translation<P::dim, float> >
- icp(const p_array<P>& c, const M& map,
- const float epsilon = 1e-3);
+ closest_point_basic(const p_array<P>& X)
+ : X_(X)
+ {
+ }
+ mln_site(I) operator()(const vec3d_f& v) const
+ {
+ vec3d_f best_x = convert::to<vec3d_f>(X_[0].to_vec());
+
+ float best_d = norm::l2_distance(v, best_x);
+ mln_piter(X_t) X_i(X_);
+ for_all(X_i)
+ {
+ float d = norm::l2_distance(v, convert::to<vec3d_f>(X_i));
+ if (d < best_d)
+ {
+ best_d = d;
+ best_x = X_i.to_vec();
+ }
+ }
+ return best_x;
+ }
- /*! Register point in \p c using a map of closest points \p map
- *
- * \param[in] c The cloud of points.
- * \param[in] c_length points from cloud to use.
- * \param[in] map The map of closest points.
- * \param[in] qk The initial rigid transformation applied.
- * \param[in] epsilon ICP stops if sqr_norm(qk - qk-1) /
- * sqr_norm(qk) > epsilon
- * \param[out] qk The rigid transformation obtained.
- *
- * \pre \p ima has to be initialized.
- */
- template <typename P, typename M, typename T>
- inline
+ private:
+ const p_array<P>& X_;
+ };
+
+
+ template <typename P>
void
- icp_subset(const p_array<P>& c,
- const unsigned int c_length,
- const M& map,
- T& qk,
- float epsilon = 1e-3);
+ draw_last_run(const box3d& box, const p_array<P>& kept,
+ const p_array<P>& removed, const p_array<P>& X,
+ const algebra::quat& qR, const vec3d_f qT)
+ {
+ typedef image3d<value::rgb8> result_t;
+ result_t result(box);
+ typedef extension_fun<result_t,pw::cst_<mln_value(result_t)> >
ext_result_t;
+ ext_result_t ext_result(result, pw::cst(value::rgb8(0,0,0)));
+ data::fill(ext_result, literal::black);
+ data::fill((ext_result | X).rw(), literal::white);
-# ifndef MLN_INCLUDE_ONLY
+ mln_piter(p_array<P>) p(kept);
+ for_all(p)
+ ext_result(qR.rotate(p.to_vec()) + qT) = literal::green;
+
+ mln_piter(p_array<P>) p2(removed);
+ for_all(p2)
+ ext_result(qR.rotate(p2.to_vec()) + qT) = literal::red;
- namespace impl
+ io::ppm::save(slice(ext_result,0), "registered-2.ppm");
+ }
+
+
+
+ template <typename P, typename F>
+ float compute_standard_deviation(const p_array<P>& P_,
+ const std::pair<algebra::quat,mln_vec(P)>& pair,
+ const F& closest_point)
{
+ accu::rms<vec3d_f,float> e_k_accu;
- // FIXME: Move elsewhere
- template <typename P>
- algebra::vec<P::dim,float>
- center(const p_array<P>& a)
+ // Standard Deviation
+ float sd;
+ mln_piter(p_array<P>) p(P_);
+ for_all(p)
{
- algebra::vec<P::dim,float> c(literal::zero);
- for (unsigned i = 0; i < a.nsites(); ++i)
- c += convert::to< algebra::vec<P::dim,float> > (a[i]);
+ vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
+ vec3d_f Yk_i = closest_point(Pk_i).to_vec();
+ // yk_i - pk_i
+ e_k_accu.take(Yk_i - Pk_i);
+ }
+
+ float d = e_k_accu.to_result();
+ sd = std::sqrt(e_k_accu.hook_value_() / P_.nsites() - d * d);
+ return sd;
+ }
+
- return c / a.nsites();
+ template <typename P, typename F>
+ void
+ remove_too_far_sites_debug(image3d<value::rgb8>& out, const
p_array<P>& P_,
+ const F& closest_point,
+ const std::pair<algebra::quat,mln_vec(P)>& pair,
+ const p_array<P>& X,
+ unsigned r, int d_min, int d_max, unsigned prefix)
+ {
+ unsigned removed = 0;
+ accu::histo<value::int_u8> h;
+ mln_piter(p_array<P>) p(P_);
+ data::fill(out, literal::black);
+ data::fill((out | X).rw(), literal::white);
+
+ for_all(p)
+ {
+ vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
+ vec3d_f Yk_i = closest_point(Pk_i).to_vec();
+
+ int d_i = closest_point.dmap_X_(Pk_i);
+ if (d_i >= d_min && d_i <= d_max)
+ out(Pk_i) = literal::green;
+ else
+ {
+ ++removed;
+ out(Pk_i) = literal::red;
+ }
}
- // FIXME: Make use of level::apply
- template <typename P, typename F>
- void apply(F& f,
- const p_array<P>& a,
- p_array<P>& b)
+#ifndef NDEBUG
+ std::ostringstream ss1;
+ ss1 << "histo_" << prefix << r <<
".dat";
+ std::cout << h << std::endl;
+
+ std::ostringstream ss2;
+ ss2 << "out_" << prefix << r <<
".ppm";
+ io::ppm::save(mln::slice(out,0), ss2.str());
+#endif
+ std::cout << "Points removed with the whole set and current d_min/d_max:
" << removed << std::endl;
+
+ }
+
+
+ template <typename P, typename F>
+ void
+ compute_distance_criteria(const p_array<P>& P_,
+ const F& closest_point,
+ const std::pair<algebra::quat,mln_vec(P)>& pair,
+ unsigned r, int& d_min, int& d_max)
+ {
+ mln_piter(p_array<P>) p(P_);
+ accu::histo<value::int_u8> h;
+
+ float sd;
{
- for (unsigned i = 0; i < a.nsites(); i++)
- b[i] = f(convert::to< algebra::vec<P::dim,float> >(a[i]));
+ accu::sum<float> s, s2;
+ for_all(p)
+ {
+ vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
+ unsigned d_i = closest_point.dmap_X_(Pk_i);
+ h.take(d_i);
+ s.take(d_i);
+ s2.take(d_i * d_i);
+ }
+ float mean = s / P_.nsites();
+ sd = std::sqrt(s2 / P_.nsites() - mean * mean);
+ d_min = int(mean - sd);
+ d_max = int(mean + sd);
}
- template <typename P, typename M, typename T>
- inline
- void
- icp_(const p_array<P>& c_,
- const unsigned c_length,
- const M& map,
- T& qk,
- const float epsilon = 1e-3)
+ std::cout << "Standard deviation = " << sd <<
std::endl;
+ std::ostringstream ss1;
+ ss1 << "histo_" << r << ".dat";
+ std::cout << h << std::endl;
+ std::cout << "d thresholds = " << d_min << ' '
<< d_max << std::endl;
+ }
+
+ template <typename P, typename F>
+ p_array<P>
+ remove_too_far_sites(image3d<value::rgb8>& out, const p_array<P>&
P_,
+ const F& closest_point,
+ const std::pair<algebra::quat,mln_vec(P)>& pair,
+ const p_array<P>& X, p_array<P>& removed_set,
+ unsigned r, int d_min, int d_max,
+ const std::string& method)
+ {
+ p_array<P> tmp;
+ unsigned removed = 0;
+
+# ifndef NDEBUG
+ data::fill(out, literal::black);
+ data::fill((out | X).rw(), literal::white);
+# endif // ! NDEBUG
+
+ mln_piter(p_array<P>) p(P_);
+ for_all(p)
{
- trace::entering("registration::impl::icp_");
+ vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
+ vec3d_f Yk_i = closest_point(Pk_i).to_vec();
+
+ int d_i = closest_point.dmap_X_(Pk_i);
+ if (d_i >= d_min && d_i <= d_max)
+ {
+ tmp.append(p);
+ out(Pk_i) = literal::green;
+ }
+ else
+ {
+ ++removed;
+ removed_set.append(p);
+ out(Pk_i) = literal::red;
+ }
+ }
+
- util::buffer<4,T> buf_qk;
- util::buffer<3,float> buf_dk;
+# ifndef NDEBUG
+ std::ostringstream ss2;
+ ss2 << method << "_" << r <<
"_removed_sites" << ".ppm";
+ io::ppm::save(mln::slice(out,0), ss2.str());
+
+ std::cout << "Points removed: " << removed <<
std::endl;
+# endif // ! NDEBUG
+ // They are used for debug purpose only.
+ // When NDEBUG is set, they are unused.
+ (void) X;
+ (void) r;
+ (void) method;
+
+ return tmp;
+ }
- float d_k = 10000;
+ template <typename P>
+ void
+ display_sites_used_in_icp(image3d<value::rgb8>& out, const
p_array<P>& P_sub,
+ const p_array<P>& P_, const p_array<P>& X,
+ unsigned r, const std::string& prefix,
+ const std::pair<algebra::quat,mln_vec(P)>& pair,
+ const std::string& period, const value::rgb8& c)
+ {
+# ifndef NDEBUG
+ data::fill(out, literal::black);
+ data::fill((out | X).rw(), literal::white);
+# endif // !NDEBUG
- //work on a reduced version of c_
- p_array<P> c = c_;
- //FIXME: Use c_ and specify c_length to function using c_
- c.hook_std_vector_().resize(c_length);
+ mln_piter(p_array<P>) p1(P_);
+ for_all(p1)
+ {
+ vec3d_f Pk_i = pair.first.rotate(p1.to_vec()) + pair.second;
+ out(Pk_i) = literal::red;
+ }
- //FIXME: avoid use of ck
- p_array<P> ck(c);
- algebra::vec<P::dim,float> mu_c = center(c);
+ mln_piter(p_array<P>) p2(P_sub);
+ for_all(p2)
+ {
+ vec3d_f Pk_i = pair.first.rotate(p2.to_vec()) + pair.second;
+ out(Pk_i) = c;
+ }
- buf_qk.store(qk);
+# ifndef NDEBUG
+ std::ostringstream ss;
+ ss << prefix << "_" << r << "_"
<< period << ".ppm";
- apply(qk, c, ck);
+ io::ppm::save(slice(out,0), ss.str());
+# endif // ! NDEBUG
+ }
- do {
- std::cout << "*------" << std::endl;
- buf_dk.store(d_k);
- //compute qk
- qk = get_rtransf(c, mu_c, ck, map);
- buf_qk.store(qk);
+ template <typename P, typename F>
+ inline
+ float
+ compute_d_k(const p_array<P>& P_,
+ const F& closest_point,
+ const algebra::quat& qR,
+ const algebra::quat& qR_old,
+ const vec3d_f& qT,
+ const vec3d_f& qT_old)
+ {
+ accu::rms<vec3d_f, float> accu;
+ mln_piter(p_array<P>) p(P_);
+ for_all(p)
+ {
+ // yk_i - pk+1_i
+ vec3d_f Pk_i = qR_old.rotate(p.to_vec()) + qT_old;
+ vec3d_f Pk_1_i = qR.rotate(p.to_vec()) + qT;
+ accu.take(closest_point(Pk_i).to_vec() - Pk_1_i);
+ }
+ return accu.to_result();
+ }
- //Ck+1 = qk(c)
- apply(qk, c, ck);
- // d_k = d(Yk, Pk+1)
- // = d(closest(qk-1(P)), qk(P))
- d_k = internal::rms(c, map, buf_qk[1], qk);
+ /// FIXME: work only for 3d images.
+ template <typename P, typename F>
+ algebra::quat
+ get_rot(const p_array<P>& P_,
+ const vec3d_f& mu_P,
+ const vec3d_f& mu_Yk,
+ const F& closest_point,
+ const algebra::quat& qR,
+ const vec3d_f& qT)
+ {
+ /// Spx: cross-covariance matrix.
+ algebra::mat<3u,3u,float> Spx;
+ mln_piter(p_array<P>) p(P_);
- //FIXME: Add matrix norm
- //} while ((qk - buf_qk[1]).sqr_norm() / qk.sqr_norm() > epsilon);
+ // FIXME: could we use an accu?
+ for_all(p)
+ {
+ vec3d_f P_i = p.to_vec();
+ vec3d_f Pk_i = qR.rotate(p.to_vec()) + qT;
+ vec3d_f Yk_i = closest_point(Pk_i).to_vec();
+ Spx += make::mat(P_i - mu_P) * trans(make::mat(Yk_i - mu_Yk));
+ }
+ Spx /= P_.nsites();
- std::cout << d_k << std::endl;
+ vec3d_f A;
+ A[0] = Spx(1,2) - Spx(2,1);
+ A[1] = Spx(2,0) - Spx(0,2);
+ A[2] = Spx(0,1) - Spx(1,0);
- } while (d_k > 9.2 + epsilon);
+ algebra::mat<4u,4u,float> Qk;
+ float t = tr(Spx);
- trace::exiting("registration::impl::icp_");
+ Qk(0,0) = t;
+ for (int i = 1; i < 4; ++i)
+ {
+ Qk(i,0) = A[i - 1];
+ Qk(0,i) = A[i - 1];
+ for (int j = 1; j < 4; ++j)
+ if (i == j)
+ Qk(i,j) = 2 * Spx(i - 1,i - 1) - t;
}
- } // end of namespace mln::registration::impl
+ Qk(1,2) = Spx(0,1) + Spx(1,0);
+ Qk(2,1) = Spx(0,1) + Spx(1,0);
+
+ Qk(1,3) = Spx(0,2) + Spx(2,0);
+ Qk(3,1) = Spx(0,2) + Spx(2,0);
+
+ Qk(2,3) = Spx(1,2) + Spx(2,1);
+ Qk(3,2) = Spx(1,2) + Spx(2,1);
+
+ return math::jacobi(Qk);
+ }
+
- template <typename P, typename M>
+ // Compute mu_Yk, mass center of Yk.
+ template <typename P, typename F>
inline
- composed<rotation<P::dim, float>, translation<P::dim, float> >
- icp(const p_array<P>& c, const M& map,
- const float epsilon = 1e-3)
+ vec3d_f
+ get_mu_yk(const p_array<P>& P_,
+ const F& closest_point,
+ const algebra::quat& qR,
+ const vec3d_f& qT,
+ float& e_k)
{
- composed<rotation<P::dim, float>, translation<P::dim, float> >
qk;
+ accu::rms<vec3d_f,float> e_k_accu;
+ accu::center<P,vec3d_f> mu_yk;
- std::cout << " icp " << std::endl;
+ mln_piter(p_array<P>) p(P_);
+ for_all(p)
+ {
+ // yk_i - pk_i
+ vec3d_f Pk_i = qR.rotate(p.to_vec()) + qT;
+ vec3d_f Yk_i = closest_point(Pk_i).to_vec();
+ mu_yk.take(Yk_i);
+ e_k_accu.take(Yk_i - Pk_i);
+ }
- impl::icp_(c, c.nsites(), map, qk, epsilon);
- return qk;
+ e_k = e_k_accu.to_result();
+ return mu_yk.to_result();
}
- template <typename P, typename M, typename T>
+
+
+ /// Base version of the ICP algorithm. It is called in other variants.
+ template <typename P, typename F>
inline
- void
- icp_subset(const p_array<P>& c,
- const unsigned int c_length,
- const M& map,
- T& qk,
- float epsilon = 1e-3)
+ std::pair<algebra::quat,mln_vec(P)>
+ icp(const p_array<P>& P_,
+ const p_array<P>& X,
+ const F& closest_point,
+ const algebra::quat& initial_rot,
+ const mln_vec(P)& initial_translation)
{
- impl::icp_(c, c_length, map, qk, epsilon);
+ trace::entering("registration::icp");
+
+ (void) X;
+ mln_precondition(P::dim == 3);
+ mln_precondition(!P_.is_empty());
+ mln_precondition(!X.is_empty());
+ mln_precondition(!initial_rot.is_null());
+
+ typedef p_array<P> cloud_t;
+
+ vec3d_f mu_P = set::compute(accu::center<P,vec3d_f>(), P_);
+
+ vec3d_f qT_old, qT = initial_translation;
+ algebra::quat qR_old, qR = initial_rot;
+ float e_k, e_k_old = mln_max(float);
+ float d_k, d_k_old = mln_max(float);
+ unsigned k = 0;
+
+# ifndef NDEBUG
+ box3d box = geom::bbox(X);
+ //FIXME: too large?
+ box.enlarge(1, box.nrows() / 2);
+ box.enlarge(2, box.ncols() / 2);
+ image3d<value::rgb8> debug(box);
+ data::fill(debug, literal::black);
+ data::fill((debug | X).rw(), literal::white);
+# endif
+
+ do
+ {
+ qT_old = qT;
+ qR_old = qR;
+
+ /// Compute transformation
+ ///
+ // mu_Yk - Pk's mass center.
+ // + Compute error ek = d(Pk,Yk)
+ vec3d_f mu_Yk = get_mu_yk(P_, closest_point, qR_old, qT_old, e_k);
+
+ // quaternion qR - rotation
+ qR = get_rot(P_, mu_P, mu_Yk, closest_point, qR_old, qT_old);
+ vec3d_f tmp = qR.v();
+
+ // vector qT - translation
+ qT = mu_Yk - qR.rotate(mu_P);
+ ///
+ /// End of "compute transformation"
+
+ // Distance dk = d(Pk+1, Yk)
+ d_k = compute_d_k(P_, closest_point, qR, qR_old, qT, qT_old);
+
+
+#ifndef NDEBUG
+ image3d<value::rgb8> tmp_ = duplicate(debug);
+ mln_piter(p_array<P>) p_dbg(P_);
+ for_all(p_dbg)
+ tmp_(qR_old.rotate(p_dbg.to_vec()) + qT_old) = literal::green;
+ std::ostringstream ss;
+ ss << "tmp_0";
+ if (k < 10)
+ ss << "0";
+ ss << k << ".ppm";
+ io::ppm::save(mln::slice(tmp_,0), ss.str());
+#endif
+
+ std::cout << "e_" << k << "=" << e_k
<< std::endl;
+ std::cout << "d_" << k << "=" << d_k
<< std::endl;
+
+ // Check distance and error according to the related paper.
+ // Disabled because of the following 'if'
+// mln_assertion(0 <= d_k);
+// mln_assertion(d_k <= e_k);
+
+// mln_assertion(e_k <= d_k_old);
+// mln_assertion(d_k_old <= e_k_old);
+
+ // During the first runs, d_k may be higher than e_k.
+ // Hence, there we test k > 3.
+ if (k > 3 && (d_k > e_k || d_k > d_k_old || e_k > e_k_old))
+ {
+ qR = qR_old;
+ qT = qT_old;
+ break;
+ }
+
+ // Backing up results.
+ d_k_old = d_k;
+ e_k_old = e_k;
+
+ ++k;
+
+ } while ((k < 3)
+ || norm::l2((qT - qT_old)) + norm::l2((qR - qR_old).to_vec()) > 1e-3);
+
+ trace::exiting("registration::icp");
+ return std::make_pair(qR, qT);
}
+
# endif // ! MLN_INCLUDE_ONLY
} // end of namespace mln::registration
} // end of namespace mln
-
-
#endif // ! MLN_REGISTRATION_ICP_HH
diff --git a/milena/mln/registration/icp2.hh b/milena/mln/registration/icp2.hh
deleted file mode 100644
index de763ea..0000000
--- a/milena/mln/registration/icp2.hh
+++ /dev/null
@@ -1,693 +0,0 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
-//
-// This file is part of the Olena Library. This library is free
-// software; you can redistribute it and/or modify it under the terms
-// of the GNU General Public License version 2 as published by the
-// Free Software Foundation.
-//
-// This library 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 this library; see the file COPYING. If not, write to
-// the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-// Boston, MA 02111-1307, USA.
-//
-// As a special exception, you may use this file as part of a free
-// software library 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_REGISTRATION_ICP_HH
-# define MLN_REGISTRATION_ICP_HH
-
-/// \file mln/registration/icp.hh
-///
-/// Register an image over an another using the ICP algorithm.
-///
-/// \todo encode distances on 12 bits.
-
-# include <cmath>
-# include <algorithm>
-
-# include <mln/core/alias/vec3d.hh>
-# include <mln/math/jacobi.hh>
-# include <mln/fun/x2x/all.hh>
-# include <mln/fun/x2v/all.hh>
-# include <mln/convert/to.hh>
-# include <mln/accu/compute.hh>
-# include <mln/accu/center.hh>
-# include <mln/accu/rms.hh>
-# include <mln/trait/image_from_grid.hh>
-# include <mln/set/compute.hh>
-
-//Should be removed when closest_point functors are moved.
-# include <mln/core/alias/neighb3d.hh>
-# include <mln/transform/internal/closest_point_functor.hh>
-# include <mln/canvas/distance_geodesic.hh>
-# include <mln/pw/all.hh>
-
-# include <mln/io/ppm/save.hh>
-# include <mln/io/pbm/save.hh>
-# include <mln/debug/colorize.hh>
-
-# include <mln/literal/black.hh>
-# include <mln/literal/white.hh>
-# include <mln/literal/colors.hh>
-
-# include <mln/core/image/slice_image.hh>
-
-#include <mln/core/image/tr_image.hh>
-#include <mln/core/image/extension_fun.hh>
-
-#include <mln/accu/histo.hh>
-#include <mln/accu/sum.hh>
-#include <mln/debug/histo.hh>
-
-# include <mln/util/timer.hh>
-
-namespace mln
-{
-
-
- namespace registration
- {
-
- //FIXME: used for debug purpose. Should be removed later.
-
- using namespace fun::x2x;
-
- /*! Register point in \p c using a function of closest points
- * \p closest_point.
- *
- * \param[in] P_ The cloud of points.
- * \param[in] X the reference surface.
- * \param[in] closest_point The function of closest points.
- * \param[out] qk The rigid transformation obtained.
- *
- * \return the rigid transformation which may be use later to create
- * a registered image.
- *
- * WARNING: the function \p closest_point *MUST* take float/double
- * vector as arguments. Otherwise the resulting transformation may be
- * wrong due to the truncation of the vector coordinate values.
- *
- * \pre \p P_ and \p X must not be empty.
- *
- * Reference article: "A Method for Registration of 3-D Shapes", Paul J.
- * Besl and Neil D. McKay, IEEE, 2, February 1992.
- *
- */
- template <typename P, typename F>
- std::pair<algebra::quat,mln_vec(P)>
- icp(const p_array<P>& P_,
- const p_array<P>& X,
- const F& closest_point,
- const algebra::quat& initial_rot,
- const mln_vec(P)& initial_translation);
-
-
- template <typename P, typename F>
- composed< translation<P::dim,float>,rotation<P::dim,float> >
- icp(const p_array<P>& P_,
- const p_array<P>& X,
- const F& closest_point);
-
-
-# ifndef MLN_INCLUDE_ONLY
-
-
- /// Closest point functor based on map distance.
- template <typename P>
- class closest_point_with_map
- {
- typedef mln_image_from_grid(mln_grid(P), P) I;
- typedef mln_ch_value(I, P) cp_ima_t;
- typedef mln_ch_value(I,value::int_u16) dmap_t;
-
- public:
-
- closest_point_with_map(const p_array<P>& X)
- {
- box3d box = geom::bbox(X);
- box.enlarge(1, box.nrows());
- box.enlarge(2, box.ncols());
-
- mln_postcondition(box.is_valid());
-
- std::cout << "Map image defined on " << box << std::endl;
-
- init(X, box);
- }
-
- closest_point_with_map(const p_array<P>& X, const box<P>& box)
- {
- init(X, box);
- }
-
- void init(const p_array<P>& X, const box<P>& box)
- {
- typedef mln_ch_value(I, bool) model_t;
- model_t model(box);
- data::fill(model, false);
- data::fill((model | X).rw(), true);
-
- transform::internal::closest_point_functor<model_t> f;
- util::timer t;
- t.start();
- dmap_X_ = canvas::distance_geodesic(model, c6(),
- mln_max(value::int_u16),
- f);
- std::cout << "canvas::distance_geodesic - " << t <<
"s" << std::endl;
-
- initialize(this->cp_ima_, f.cp_ima);
- this->cp_ima_ = f.cp_ima;
-
- mln_postcondition(cp_ima_.is_valid());
- mln_postcondition(cp_ima_.domain().is_valid());
- std::cout << "pmin = " << cp_ima_.domain().pmin() <<
std::endl;;
- std::cout << "pmax = " << cp_ima_.domain().pmax() <<
std::endl;;
-
-#ifndef NDEBUG
-// mln_ch_value(I, bool) debug2(box);
-// data::fill(debug2, false);
-// mln_ch_value(I, value::rgb8) debug(box);
-// mln_piter(p_array<P>) p(X);
-// for_all(p)
-// {
-// debug(p) = debug::internal::random_color(value::rgb8());
-// debug2(p) = true;
-// }
-// io::pbm::save(slice(debug2,0), "debug2-a.ppm");
-//
-// mln_piter(I) pi(cp_ima_.domain());
-// for_all(pi)
-// {
-// debug(pi) = debug(cp_ima_(pi));
-// debug2(pi) = debug2(cp_ima_(pi));
-// }
-//
-// io::pbm::save(slice(debug2,0), "debug2-b.ppm");
-// io::ppm::save(slice(debug,0), "debug.ppm");
-// std::cout << "map saved" << std::endl;
-#endif
- }
-
- mln_site(I) operator()(const mln_site(I)& p) const
- {
- return cp_ima_(p);
- }
-
- // Distance map
- dmap_t dmap_X_;
- // Closest point image.
- cp_ima_t cp_ima_;
- private:
- };
-
-
- /// Closest point functor based on map distance.
- template <typename P>
- class closest_point_basic
- {
- typedef mln_image_from_grid(mln_grid(P), P) I;
- typedef p_array<P> X_t;
-
- public:
-
- closest_point_basic(const p_array<P>& X)
- : X_(X)
- {
- }
-
- mln_site(I) operator()(const vec3d_f& v) const
- {
- vec3d_f best_x = convert::to<vec3d_f>(X_[0].to_vec());
-
- float best_d = norm::l2_distance(v, best_x);
- mln_piter(X_t) X_i(X_);
- for_all(X_i)
- {
- float d = norm::l2_distance(v, convert::to<vec3d_f>(X_i));
- if (d < best_d)
- {
- best_d = d;
- best_x = X_i.to_vec();
- }
- }
- return best_x;
- }
-
- private:
- const p_array<P>& X_;
- };
-
-
- template <typename P>
- void
- draw_last_run(const box3d& box, const p_array<P>& kept,
- const p_array<P>& removed, const p_array<P>& X,
- const algebra::quat& qR, const vec3d_f qT)
- {
- typedef image3d<value::rgb8> result_t;
- result_t result(box);
- typedef extension_fun<result_t,pw::cst_<mln_value(result_t)> >
ext_result_t;
- ext_result_t ext_result(result, pw::cst(value::rgb8(0,0,0)));
-
- data::fill(ext_result, literal::black);
- data::fill((ext_result | X).rw(), literal::white);
-
- mln_piter(p_array<P>) p(kept);
- for_all(p)
- ext_result(qR.rotate(p.to_vec()) + qT) = literal::green;
-
- mln_piter(p_array<P>) p2(removed);
- for_all(p2)
- ext_result(qR.rotate(p2.to_vec()) + qT) = literal::red;
-
- io::ppm::save(slice(ext_result,0), "registered-2.ppm");
- }
-
-
-
- template <typename P, typename F>
- float compute_standard_deviation(const p_array<P>& P_,
- const std::pair<algebra::quat,mln_vec(P)>& pair,
- const F& closest_point)
- {
- accu::rms<vec3d_f,float> e_k_accu;
-
- // Standard Deviation
- float sd;
- mln_piter(p_array<P>) p(P_);
- for_all(p)
- {
- vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
- vec3d_f Yk_i = closest_point(Pk_i).to_vec();
- // yk_i - pk_i
- e_k_accu.take(Yk_i - Pk_i);
- }
-
- float d = e_k_accu.to_result();
- sd = std::sqrt(e_k_accu.hook_value_() / P_.nsites() - d * d);
- return sd;
- }
-
-
- template <typename P, typename F>
- void
- remove_too_far_sites_debug(image3d<value::rgb8>& out, const
p_array<P>& P_,
- const F& closest_point,
- const std::pair<algebra::quat,mln_vec(P)>& pair,
- const p_array<P>& X,
- unsigned r, int d_min, int d_max, unsigned prefix)
- {
- unsigned removed = 0;
- accu::histo<value::int_u8> h;
- mln_piter(p_array<P>) p(P_);
- data::fill(out, literal::black);
- data::fill((out | X).rw(), literal::white);
-
- for_all(p)
- {
- vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
- vec3d_f Yk_i = closest_point(Pk_i).to_vec();
-
- int d_i = closest_point.dmap_X_(Pk_i);
- if (d_i >= d_min && d_i <= d_max)
- out(Pk_i) = literal::green;
- else
- {
- ++removed;
- out(Pk_i) = literal::red;
- }
- }
-
-#ifndef NDEBUG
- std::ostringstream ss1;
- ss1 << "histo_" << prefix << r <<
".dat";
- std::cout << h << std::endl;
-
- std::ostringstream ss2;
- ss2 << "out_" << prefix << r <<
".ppm";
- io::ppm::save(mln::slice(out,0), ss2.str());
-#endif
- std::cout << "Points removed with the whole set and current d_min/d_max:
" << removed << std::endl;
-
- }
-
-
- template <typename P, typename F>
- void
- compute_distance_criteria(const p_array<P>& P_,
- const F& closest_point,
- const std::pair<algebra::quat,mln_vec(P)>& pair,
- unsigned r, int& d_min, int& d_max)
- {
- mln_piter(p_array<P>) p(P_);
- accu::histo<value::int_u8> h;
-
- float sd;
- {
- accu::sum<float> s, s2;
- for_all(p)
- {
- vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
- unsigned d_i = closest_point.dmap_X_(Pk_i);
- h.take(d_i);
- s.take(d_i);
- s2.take(d_i * d_i);
- }
- float mean = s / P_.nsites();
- sd = std::sqrt(s2 / P_.nsites() - mean * mean);
- d_min = int(mean - sd);
- d_max = int(mean + sd);
- }
-
- std::cout << "Standard deviation = " << sd <<
std::endl;
- std::ostringstream ss1;
- ss1 << "histo_" << r << ".dat";
- std::cout << h << std::endl;
- std::cout << "d thresholds = " << d_min << ' '
<< d_max << std::endl;
- }
-
- template <typename P, typename F>
- p_array<P>
- remove_too_far_sites(image3d<value::rgb8>& out, const p_array<P>&
P_,
- const F& closest_point,
- const std::pair<algebra::quat,mln_vec(P)>& pair,
- const p_array<P>& X, p_array<P>& removed_set,
- unsigned r, int d_min, int d_max,
- const std::string& method)
- {
- p_array<P> tmp;
- unsigned removed = 0;
-
-# ifndef NDEBUG
- data::fill(out, literal::black);
- data::fill((out | X).rw(), literal::white);
-# endif // ! NDEBUG
-
- mln_piter(p_array<P>) p(P_);
- for_all(p)
- {
- vec3d_f Pk_i = pair.first.rotate(p.to_vec()) + pair.second;
- vec3d_f Yk_i = closest_point(Pk_i).to_vec();
-
- int d_i = closest_point.dmap_X_(Pk_i);
- if (d_i >= d_min && d_i <= d_max)
- {
- tmp.append(p);
- out(Pk_i) = literal::green;
- }
- else
- {
- ++removed;
- removed_set.append(p);
- out(Pk_i) = literal::red;
- }
- }
-
-
-# ifndef NDEBUG
- std::ostringstream ss2;
- ss2 << method << "_" << r <<
"_removed_sites" << ".ppm";
- io::ppm::save(mln::slice(out,0), ss2.str());
-
- std::cout << "Points removed: " << removed <<
std::endl;
-# endif // ! NDEBUG
- // They are used for debug purpose only.
- // When NDEBUG is set, they are unused.
- (void) X;
- (void) r;
- (void) method;
-
- return tmp;
- }
-
- template <typename P>
- void
- display_sites_used_in_icp(image3d<value::rgb8>& out, const
p_array<P>& P_sub,
- const p_array<P>& P_, const p_array<P>& X,
- unsigned r, const std::string& prefix,
- const std::pair<algebra::quat,mln_vec(P)>& pair,
- const std::string& period, const value::rgb8& c)
- {
-# ifndef NDEBUG
- data::fill(out, literal::black);
- data::fill((out | X).rw(), literal::white);
-# endif // !NDEBUG
-
- mln_piter(p_array<P>) p1(P_);
- for_all(p1)
- {
- vec3d_f Pk_i = pair.first.rotate(p1.to_vec()) + pair.second;
- out(Pk_i) = literal::red;
- }
-
- mln_piter(p_array<P>) p2(P_sub);
- for_all(p2)
- {
- vec3d_f Pk_i = pair.first.rotate(p2.to_vec()) + pair.second;
- out(Pk_i) = c;
- }
-
-# ifndef NDEBUG
- std::ostringstream ss;
- ss << prefix << "_" << r << "_"
<< period << ".ppm";
-
- io::ppm::save(slice(out,0), ss.str());
-# endif // ! NDEBUG
- }
-
-
- template <typename P, typename F>
- inline
- float
- compute_d_k(const p_array<P>& P_,
- const F& closest_point,
- const algebra::quat& qR,
- const algebra::quat& qR_old,
- const vec3d_f& qT,
- const vec3d_f& qT_old)
- {
- accu::rms<vec3d_f, float> accu;
- mln_piter(p_array<P>) p(P_);
- for_all(p)
- {
- // yk_i - pk+1_i
- vec3d_f Pk_i = qR_old.rotate(p.to_vec()) + qT_old;
- vec3d_f Pk_1_i = qR.rotate(p.to_vec()) + qT;
- accu.take(closest_point(Pk_i).to_vec() - Pk_1_i);
- }
- return accu.to_result();
- }
-
-
- /// FIXME: work only for 3d images.
- template <typename P, typename F>
- algebra::quat
- get_rot(const p_array<P>& P_,
- const vec3d_f& mu_P,
- const vec3d_f& mu_Yk,
- const F& closest_point,
- const algebra::quat& qR,
- const vec3d_f& qT)
- {
- /// Spx: cross-covariance matrix.
- algebra::mat<3u,3u,float> Spx;
- mln_piter(p_array<P>) p(P_);
-
- // FIXME: could we use an accu?
- for_all(p)
- {
- vec3d_f P_i = p.to_vec();
- vec3d_f Pk_i = qR.rotate(p.to_vec()) + qT;
- vec3d_f Yk_i = closest_point(Pk_i).to_vec();
- Spx += make::mat(P_i - mu_P) * trans(make::mat(Yk_i - mu_Yk));
- }
- Spx /= P_.nsites();
-
- vec3d_f A;
- A[0] = Spx(1,2) - Spx(2,1);
- A[1] = Spx(2,0) - Spx(0,2);
- A[2] = Spx(0,1) - Spx(1,0);
-
- algebra::mat<4u,4u,float> Qk;
- float t = tr(Spx);
-
- Qk(0,0) = t;
- for (int i = 1; i < 4; ++i)
- {
- Qk(i,0) = A[i - 1];
- Qk(0,i) = A[i - 1];
- for (int j = 1; j < 4; ++j)
- if (i == j)
- Qk(i,j) = 2 * Spx(i - 1,i - 1) - t;
- }
-
- Qk(1,2) = Spx(0,1) + Spx(1,0);
- Qk(2,1) = Spx(0,1) + Spx(1,0);
-
- Qk(1,3) = Spx(0,2) + Spx(2,0);
- Qk(3,1) = Spx(0,2) + Spx(2,0);
-
- Qk(2,3) = Spx(1,2) + Spx(2,1);
- Qk(3,2) = Spx(1,2) + Spx(2,1);
-
- return math::jacobi(Qk);
- }
-
-
- // Compute mu_Yk, mass center of Yk.
- template <typename P, typename F>
- inline
- vec3d_f
- get_mu_yk(const p_array<P>& P_,
- const F& closest_point,
- const algebra::quat& qR,
- const vec3d_f& qT,
- float& e_k)
- {
- accu::rms<vec3d_f,float> e_k_accu;
- accu::center<P,vec3d_f> mu_yk;
-
- mln_piter(p_array<P>) p(P_);
- for_all(p)
- {
- // yk_i - pk_i
- vec3d_f Pk_i = qR.rotate(p.to_vec()) + qT;
- vec3d_f Yk_i = closest_point(Pk_i).to_vec();
- mu_yk.take(Yk_i);
- e_k_accu.take(Yk_i - Pk_i);
- }
-
- e_k = e_k_accu.to_result();
- return mu_yk.to_result();
- }
-
-
-
- /// Base version of the ICP algorithm. It is called in other variants.
- template <typename P, typename F>
- inline
- std::pair<algebra::quat,mln_vec(P)>
- icp(const p_array<P>& P_,
- const p_array<P>& X,
- const F& closest_point,
- const algebra::quat& initial_rot,
- const mln_vec(P)& initial_translation)
- {
- trace::entering("registration::icp");
-
- (void) X;
- mln_precondition(P::dim == 3);
- mln_precondition(!P_.is_empty());
- mln_precondition(!X.is_empty());
- mln_precondition(!initial_rot.is_null());
-
- typedef p_array<P> cloud_t;
-
- vec3d_f mu_P = set::compute(accu::center<P,vec3d_f>(), P_);
-
- vec3d_f qT_old, qT = initial_translation;
- algebra::quat qR_old, qR = initial_rot;
- float e_k, e_k_old = mln_max(float);
- float d_k, d_k_old = mln_max(float);
- unsigned k = 0;
-
-# ifndef NDEBUG
- box3d box = geom::bbox(X);
- //FIXME: too large?
- box.enlarge(1, box.nrows() / 2);
- box.enlarge(2, box.ncols() / 2);
- image3d<value::rgb8> debug(box);
- data::fill(debug, literal::black);
- data::fill((debug | X).rw(), literal::white);
-# endif
-
- do
- {
- qT_old = qT;
- qR_old = qR;
-
- /// Compute transformation
- ///
- // mu_Yk - Pk's mass center.
- // + Compute error ek = d(Pk,Yk)
- vec3d_f mu_Yk = get_mu_yk(P_, closest_point, qR_old, qT_old, e_k);
-
- // quaternion qR - rotation
- qR = get_rot(P_, mu_P, mu_Yk, closest_point, qR_old, qT_old);
- vec3d_f tmp = qR.v();
-
- // vector qT - translation
- qT = mu_Yk - qR.rotate(mu_P);
- ///
- /// End of "compute transformation"
-
- // Distance dk = d(Pk+1, Yk)
- d_k = compute_d_k(P_, closest_point, qR, qR_old, qT, qT_old);
-
-
-#ifndef NDEBUG
- image3d<value::rgb8> tmp_ = duplicate(debug);
- mln_piter(p_array<P>) p_dbg(P_);
- for_all(p_dbg)
- tmp_(qR_old.rotate(p_dbg.to_vec()) + qT_old) = literal::green;
- std::ostringstream ss;
- ss << "tmp_0";
- if (k < 10)
- ss << "0";
- ss << k << ".ppm";
- io::ppm::save(mln::slice(tmp_,0), ss.str());
-#endif
-
- std::cout << "e_" << k << "=" << e_k
<< std::endl;
- std::cout << "d_" << k << "=" << d_k
<< std::endl;
-
- // Check distance and error according to the related paper.
- // Disabled because of the following 'if'
-// mln_assertion(0 <= d_k);
-// mln_assertion(d_k <= e_k);
-
-// mln_assertion(e_k <= d_k_old);
-// mln_assertion(d_k_old <= e_k_old);
-
- // During the first runs, d_k may be higher than e_k.
- // Hence, there we test k > 3.
- if (k > 3 && (d_k > e_k || d_k > d_k_old || e_k > e_k_old))
- {
- qR = qR_old;
- qT = qT_old;
- break;
- }
-
- // Backing up results.
- d_k_old = d_k;
- e_k_old = e_k;
-
- ++k;
-
- } while ((k < 3)
- || norm::l2((qT - qT_old)) + norm::l2((qR - qR_old).to_vec()) > 1e-3);
-
- trace::exiting("registration::icp");
- return std::make_pair(qR, qT);
- }
-
-
-# endif // ! MLN_INCLUDE_ONLY
-
- } // end of namespace mln::registration
-
-} // end of namespace mln
-
-#endif // ! MLN_REGISTRATION_ICP_HH
diff --git a/milena/mln/registration/registration.hh
b/milena/mln/registration/registration.hh
index f96787a..21d6ea5 100644
--- a/milena/mln/registration/registration.hh
+++ b/milena/mln/registration/registration.hh
@@ -34,11 +34,14 @@
/// \sa registration::icp
# include <mln/core/image/image3d.hh>
-# include <mln/registration/icp2.hh>
+# include <mln/registration/icp.hh>
# include <mln/fun/x2x/all.hh>
# include <mln/fun/x2p/closest_point.hh>
# include <mln/convert/to_p_array.hh>
+//FIXME: to be removed.
+# include <mln/util/timer.hh>
+
namespace mln
{
@@ -331,7 +334,7 @@ namespace mln
{
trace::entering("registration::registration1");
- registration_tests(cloud, surface);
+ internal::registration_tests(cloud, surface);
composed< translation<P::dim,float>, rotation<P::dim,float> >
qk = impl::registration1(cloud, surface);
@@ -350,7 +353,7 @@ namespace mln
{
trace::entering("registration::registration2");
- registration_tests(cloud, surface);
+ internal::registration_tests(cloud, surface);
composed< translation<P::dim,float>, rotation<P::dim,float> >
qk = impl::registration2(cloud, surface);
diff --git a/milena/mln/util/graph.hh b/milena/mln/util/graph.hh
index f95e1e8..10e900f 100644
--- a/milena/mln/util/graph.hh
+++ b/milena/mln/util/graph.hh
@@ -405,7 +405,9 @@ namespace mln
unsigned id = data_->edges_.size() - 1;
// Update the set of edges.
+# ifndef NDEBUG
data_->edges_set_.insert(edge);
+# endif // ! NDEBUG
data_->vertices_[edge.first()].push_back(id);
data_->vertices_[edge.second()].push_back(id);
diff --git a/milena/mln/value/rgb.hh b/milena/mln/value/rgb.hh
index 9c64936..839d89c 100644
--- a/milena/mln/value/rgb.hh
+++ b/milena/mln/value/rgb.hh
@@ -51,6 +51,11 @@
namespace mln
{
+ // Forward declaration.
+ namespace value { template <unsigned n> struct rgb; }
+
+
+
namespace fun
{
--
1.5.6.5