https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
Index: ChangeLog
from Ugo Jardonnet <jardonnet(a)lrde.epita.fr>
Sandbox: ICP: icp_ref + cleanup.
icp_ref uses no rounding.
* jardonnet/test/icp_ref.cc: New.
* jardonnet/registration/icp_ref.hh: New.
* jardonnet/test/icp.cc: clean up.
* jardonnet/test/Makefile: Update.
* jardonnet/registration/final_qk.hh: Put final_qk function in a
dedicated file.
* jardonnet/registration/tools.hh: Add buffer and shuffle.
* jardonnet/registration/icp.hh: Minor fix.
registration/cloud.hh | 2
registration/final_qk.hh | 107 +++++++++++++++++++
registration/icp.hh | 9 -
registration/icp_ref.hh | 254
+++++++++++++++++++++++++++++++++++++++++++++++
registration/quat7.hh | 2
registration/tools.hh | 116 +++++----------------
test/Makefile | 4
test/icp.cc | 12 +-
test/icp_ref.cc | 87 ++++++++++++++++
9 files changed, 495 insertions(+), 98 deletions(-)
Index: jardonnet/test/icp_ref.cc
--- jardonnet/test/icp_ref.cc (revision 0)
+++ jardonnet/test/icp_ref.cc (revision 0)
@@ -0,0 +1,87 @@
+#include <mln/core/image3d.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/pbm/save.hh>
+#include <mln/io/ppm/save.hh>
+#include <mln/norm/l2.hh>
+
+#include <sandbox/jardonnet/registration/icp_ref.hh>
+#include <sandbox/jardonnet/registration/tools.hh>
+
+void usage(char *argv[])
+{
+ std::cout << "usage : " << argv[0]
+ << " cloud.pbm surface.pbm q e" << std::endl
+ << " q >= 1 and e >= 1" << std::endl;
+ exit(1);
+}
+
+
+int main(int argc, char* argv[])
+{
+ if (argc != 5)
+ usage(argv);
+
+ float q = std::atof(argv[3]);
+ int e = std::atoi(argv[4]);
+
+ if (q < 1 or e < 1)
+ usage(argv);
+
+ using namespace mln;
+ typedef image3d< bool > I3d;
+
+ image2d< bool > img1;
+ image2d< bool > img2;
+
+ //load images
+ io::pbm::load(img1, argv[1]);
+ io::pbm::load(img2, argv[2]);
+
+ //convert to image3d
+ I3d cloud = convert::to_image3d(img1);
+ I3d surface = convert::to_image3d(img2);
+
+ //build p_arrays.
+ p_array<mln_point_(I3d)> c = convert::to_p_array(cloud);
+ p_array<mln_point_(I3d)> x = convert::to_p_array(surface);
+
+ // FIXME : TODO : map : vec<3,float> -> point
+
+ quat7<3> qk = registration::icp(c, map, q, e, x);
+
+#ifndef NDEBUG
+ std::cout << "closest_point(Ck[i]) = " << fun.i <<
std::endl;
+ std::cout << "Pts processed = " << registration::pts
<< std::endl;
+#endif
+
+ qk.apply_on(c, c, c.npoints());
+
+ //init output image
+ //FIXME: Should be like
+ //mln_concrete(I) output(res.bbox()) = convert::to_image<I>(res) ?
+
+ const box_<point2d> box2d(400,700);
+ image2d<value::rgb8> output(box2d, 1);
+
+ //to 2d : projection (FIXME:if 3d)
+ for (size_t i = 0; i < c.npoints(); i++)
+ {
+ //Ck points
+ point2d p(c[i][0], c[i][1]);
+ if (output.has(p))
+ output(p) = literal::white;
+ //Xk points
+ }
+
+ for (size_t i = 0; i < x.npoints(); i++)
+ {
+ point2d x(map(c[i])[0], map(c[i])[1]);
+ if (output.has(x))
+ output(x) = literal::green;
+ }
+
+ io::ppm::save(output, "registred.ppm");
+
+}
+
Index: jardonnet/test/icp.cc
--- jardonnet/test/icp.cc (revision 1945)
+++ jardonnet/test/icp.cc (working copy)
@@ -7,6 +7,7 @@
#include <sandbox/jardonnet/registration/icp.hh>
#include <sandbox/jardonnet/registration/tools.hh>
+#include <sandbox/jardonnet/registration/final_qk.hh>
void usage(char *argv[])
{
@@ -57,18 +58,16 @@
std::cout << "Pts processed = " << registration::pts
<<
std::endl;
#endif
- //FIXME: register img1
+ qk.apply_on(c, c, c.npoints());
//init output image
//FIXME: Should be like
//mln_concrete(I) output(res.bbox()) = convert::to_image<I>(res) ?
- qk.apply_on(c, c, c.npoints());
const box_<point2d> box2d(400,700);
image2d<value::rgb8> output(box2d, 1);
-
float stddev, mean;
registration::mean_stddev(c, map, mean, stddev);
@@ -103,10 +102,15 @@
else
output(p) = literal::white;
}
+ }
+
+ // print surface
+ for (size_t i = 0; i < c.npoints(); i++)
+ {
//Xk points
point2d x(map(c[i])[0], map(c[i])[1]);
if (output.has(x))
- output(x) = value::rgb8(0,255,0);
+ output(x) = literal::green;
}
io::ppm::save(output, "registred.ppm");
Index: jardonnet/test/Makefile
--- jardonnet/test/Makefile (revision 1945)
+++ jardonnet/test/Makefile (working copy)
@@ -19,6 +19,9 @@
icp: icp.cc +depend icp.o
g++ icp.o -I../../.. -O3 -DNDEBUG -o 'icp'
+icp_ref:
+ g++ icp_ref.cc -I../../.. -g -o 'icp_ref'
+
icp.o:
g++ -c icp.cc -I../../.. -O3 -DNDEBUG
@@ -30,6 +33,7 @@
rm -f sub gsub gau icpD icp
rm -f log.dat registred.pbm
rm -f i_*
+ rm -f tmp.ppm registred.ppm semantic.cache
.PHONY: check bench icpD
Index: jardonnet/registration/final_qk.hh
--- jardonnet/registration/final_qk.hh (revision 0)
+++ jardonnet/registration/final_qk.hh (revision 0)
@@ -0,0 +1,107 @@
+#ifndef MLN_REGISTRATION_FINAL_QK_HH
+# define MLN_REGISTRATION_FINAL_QK_HH
+
+namespace mln
+{
+
+ namespace registration
+ {
+
+ template <typename P, typename M>
+ void mean_stddev(const p_array<P>& c,
+ const M& map,
+ float& mean,
+ float& stddev)
+ {
+ //mean + length
+ std::vector<float> length(c.npoints());
+ mean = 0;
+
+ for (size_t i = 0; i < c.npoints(); i++)
+ {
+ float f = norm::l2(algebra::vec<3,int> (c[i] - map(c[i])));
+ length[i] = f;
+ mean += f;
+ }
+ mean /= c.npoints();
+
+ //standar variation
+ stddev = 0;
+ for (size_t i = 0; i < c.npoints(); i++)
+ stddev += (length[i] - mean) * (length[i] - mean);
+ stddev /= c.npoints();
+ stddev = math::sqrt(stddev);
+ }
+
+
+ //final qk : only use point less than nstddev (2*stddev);
+ template <typename P, typename M>
+ quat7<P::dim>
+ final_qk(const p_array<P>& c,
+ const M& map,
+ float nstddev)
+ {
+ p_array<P> newc;
+ algebra::vec<3,float> mu_newc(literal::zero);
+
+ for (size_t i = 0; i < c.npoints(); ++i)
+ {
+ algebra::vec<3,float> xki = map(c[i]);
+ algebra::vec<3,float> ci = c[i];
+
+ if (norm::l2(ci - xki) > nstddev)
+ {
+ newc.append(c[i]);
+ mu_newc += ci;
+ }
+ }
+ mu_newc /= newc.npoints();
+
+ quat7<P::dim> qk = match(newc, mu_newc, newc, map, newc.npoints());
+
+ return qk;
+ }
+
+ //final > nstddev for translation; < nstddev for rotation
+ template <typename P, typename M>
+ quat7<P::dim>
+ final_qk2(const p_array<P>& c,
+ const M& map,
+ float nstddev)
+ {
+ //mu_Xk = center map(Ck)
+ algebra::vec<3,float> mu_Xk(literal::zero);
+ algebra::vec<3,float> mu_C(literal::zero);
+
+ float nb_point = 0;
+ for (size_t i = 0; i < c.npoints(); ++i)
+ {
+ algebra::vec<3,float> xki = map(c[i]);
+ algebra::vec<3,float> ci = c[i];
+
+ if (norm::l2(ci - xki) > nstddev)
+ {
+ mu_C += ci;
+ mu_Xk += xki;
+ nb_point++;
+ }
+ }
+ mu_C /= nb_point;
+ mu_Xk /= nb_point;
+
+ // qT
+ const algebra::vec<3,float> qT = mu_C - mu_Xk;
+
+ // qR
+ quat7<P::dim> qk = final_qk(c, map, nstddev);
+ qk._qT = qT;
+
+ return qk;
+ }
+
+
+ } // end of namespace mln::registration
+
+}
+
+#endif // MLN_REGISTRATION_FINAL_QK_HH
Index: jardonnet/registration/tools.hh
--- jardonnet/registration/tools.hh (revision 1945)
+++ jardonnet/registration/tools.hh (working copy)
@@ -12,104 +12,46 @@
namespace mln
{
- namespace registration
- {
-
- template <typename P, typename M>
- void mean_stddev(const p_array<P>& c,
- const M& map,
- float& mean,
- float& stddev)
+ template <typename P>
+ void shuffle(p_array<P>& a)
{
- //mean + length
- std::vector<float> length(c.npoints());
- mean = 0;
-
- for (size_t i = 0; i < c.npoints(); i++)
+ for (unsigned int i = 0; i < a.npoints(); i++)
{
- float f = norm::l2(algebra::vec<3,int> (c[i] - map(c[i])));
- length[i] = f;
- mean += f;
+ unsigned int r = rand() % a.npoints();
+ P tmp;
+ tmp = a[i];
+ a.hook_()[i] = a[r];
+ a.hook_()[r] = tmp;
}
- mean /= c.npoints();
-
- //standar variation
- stddev = 0;
- for (size_t i = 0; i < c.npoints(); i++)
- stddev += (length[i] - mean) * (length[i] - mean);
- stddev /= c.npoints();
- stddev = math::sqrt(stddev);
}
-
- //final qk : only use point less than nstddev (2*stddev);
- template <typename P, typename M>
- quat7<P::dim>
- final_qk(const p_array<P>& c,
- const M& map,
- float nstddev)
+ template <unsigned int n, typename T>
+ struct buffer
{
- p_array<P> newc;
- algebra::vec<3,float> mu_newc(literal::zero);
-
- for (size_t i = 0; i < c.npoints(); ++i)
- {
- algebra::vec<3,float> xki = map(c[i]);
- algebra::vec<3,float> ci = c[i];
-
- if (norm::l2(ci - xki) > nstddev)
+ buffer()
+ : setted(0)
{
- newc.append(c[i]);
- mu_newc += ci;
}
- }
- mu_newc /= newc.npoints();
-
- quat7<P::dim> qk = match(newc, mu_newc, newc, map, newc.npoints());
- return qk;
- }
-
- //final > nstddev for translation; < nstddev for rotation
- template <typename P, typename M>
- quat7<P::dim>
- final_qk2(const p_array<P>& c,
- const M& map,
- float nstddev)
- {
- //mu_Xk = center map(Ck)
- algebra::vec<3,float> mu_Xk(literal::zero);
- algebra::vec<3,float> mu_C(literal::zero);
-
- float nb_point = 0;
- for (size_t i = 0; i < c.npoints(); ++i)
+ void add(T e)
{
- algebra::vec<3,float> xki = map(c[i]);
- algebra::vec<3,float> ci = c[i];
+ for (int i = 0; i < n-1; i++)
+ buf[i+1] = buf[i];
+ buf[0] = e;
- if (norm::l2(ci - xki) > nstddev)
- {
- mu_C += ci;
- mu_Xk += xki;
- nb_point++;
- }
+ setted++;
}
- mu_C /= nb_point;
- mu_Xk /= nb_point;
-
- // qT
- const algebra::vec<3,float> qT = mu_C - mu_Xk;
- // qR
- quat7<P::dim> qk = final_qk(c, map, nstddev);
- qk._qT = qT;
-
- return qk;
+ T& operator[](unsigned int i)
+ {
+ assert(i < n && i < setted);
+ return buf[i];
}
-
- } // end of namespace mln::registration
-
+ private:
+ T buf[n];
+ unsigned int setted;
+ };
//FIXME: groe length
template <typename P>
@@ -127,7 +69,7 @@
result
//inline
- operator () (const P& Ck) const
+ operator () (const input& Ck) const
{
#ifndef NDEBUG
@@ -170,13 +112,13 @@
struct lazy_image
{
// Fun is potentially an image.
- lazy_image(F& fun)
+ lazy_image(const F& fun)
: value(fun.domain()), is_known(fun.domain()), fun(fun)
{
}
// FIXME: hack, remove this constructor
- lazy_image(F& fun, int nrows, int ncols, int nslis)
+ lazy_image(const F& fun, int nrows, int ncols, int nslis)
: value(nrows, ncols,1), is_known(nrows,ncols,1), fun(fun)
{ }
@@ -197,7 +139,7 @@
//FIXME: 3d -> //mln_dim(ml_input(input))
mutable image3d<mln_result(F)> value;
mutable image3d<bool> is_known;
- F& fun;
+ const F& fun;
};
Index: jardonnet/registration/quat7.hh
--- jardonnet/registration/quat7.hh (revision 1945)
+++ jardonnet/registration/quat7.hh (working copy)
@@ -126,7 +126,7 @@
quat7<P::dim> match(const p_array<P>& C,
const algebra::vec<P::dim,float>& mu_C,
const p_array<P>& Ck,
- M& map,
+ const M& map,
size_t c_length)
{
//mu_Xk = center map(Ck)
Index: jardonnet/registration/cloud.hh
--- jardonnet/registration/cloud.hh (revision 1945)
+++ jardonnet/registration/cloud.hh (working copy)
@@ -46,7 +46,7 @@
template <typename P, typename M>
float rms(const p_array<P>& a1,
quat7<P::dim>& qk,
- M& map,
+ const M& map,
const size_t length)
{
assert(length <= a1.npoints());
Index: jardonnet/registration/icp_ref.hh
--- jardonnet/registration/icp_ref.hh (revision 0)
+++ jardonnet/registration/icp_ref.hh (revision 0)
@@ -0,0 +1,254 @@
+// Copyright (C) 2008 EPITA Research and Development Laboratory
+//
+// 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
+ *
+ * \brief image registration
+ */
+
+# include <iostream>
+# include <string>
+# include <cmath>
+
+# include <mln/algebra/quat.hh>
+# include <mln/algebra/vec.hh>
+# include <mln/make/w_window.hh>
+# include <mln/make/w_window3d.hh>
+
+# include <mln/value/rgb8.hh>
+# include <mln/literal/colors.hh>
+# include <mln/literal/black.hh>
+# include <mln/level/fill.hh>
+# include <mln/io/ppm/save.hh>
+
+
+# include "tools.hh"
+
+# include "cloud.hh"
+# include "quat7.hh"
+# include "update_qk.hh"
+# include "chamfer.hh"
+
+# include "save.hh"
+
+namespace mln
+{
+
+ namespace registration
+ {
+
+#ifndef NDEBUG
+ static unsigned pts = 0;
+#endif
+
+ /*! Registration FIXME : doxy
+ *
+ *
+ */
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ icp(const Image<I>& cloud,
+ const Image<J>& surface);
+
+# ifndef MLN_INCLUDE_ONLY
+
+ namespace impl
+ {
+
+ template <typename P, typename M>
+ inline
+ void
+ icp_(const p_array<P>& C,
+ const M& map,
+ quat7<P::dim>& qk,
+ const size_t c_length,
+ const float epsilon = 1e-3)
+ {
+ trace::entering("registration::impl::icp_");
+
+#ifndef NDEBUG
+ //display registred points
+ std::cout << "Register "
+ << c_length << " points" << std::endl;
+ std::cout << "k\t\te_k >=\td_k\tdqk" << std::endl;
+#endif
+
+ quat7<P::dim> buf_qk[4];
+ float buf_dk[3];
+
+ float err = 100;
+ //float err_bis;
+ p_array<P> Ck(C);
+
+ algebra::vec<P::dim,float> mu_C = center(C, c_length), mu_Xk;
+
+ buf_qk[0] = qk;
+
+ qk.apply_on(C, Ck, c_length);
+
+ unsigned int k = 0;
+
+ do {
+ //update buff dk //FIXME: rewrite it
+ buf_dk[2] = buf_dk[1];
+ buf_dk[1] = buf_dk[0];
+ buf_dk[0] = err;
+
+ //compute qk
+ qk = match(C, mu_C, Ck, map, c_length);
+
+ //update buff qk //FIXME: rewrite it
+ buf_qk[3] = buf_qk[2];
+ buf_qk[2] = buf_qk[1];
+ buf_qk[1] = buf_qk[0];
+ buf_qk[0] = qk;
+
+ //update qk
+ /*
+ if (k > 3)
+ qk = update_qk(buf_qk, buf_dk);
+ qk._qR.set_unit();
+ buf_qk[0] = qk;
+ */
+
+ //Ck+1 = qk(C)
+ qk.apply_on(C, Ck, c_length);
+
+ // e_k = d(Yk, Pk)
+ // = d(closest(Pk), Pk)
+ // = d(closest(qk-1(P)), qk-1(P))
+ float e_k = rms(C, map, c_length, buf_qk[1], buf_qk[1]);
+
+ // d_k = d(Yk, Pk+1)
+ // = d(closest(qk-1(P)), qk(P))
+ float d_k = rms(C, map, c_length, buf_qk[1], qk);
+
+
+ //err = d(Ck+1,Xk) = d_k
+ err = rms(C, qk, map, c_length);
+
+ //err = d(Ck,Xk) = e_k
+ float err_bis = rms(C, buf_qk[1], map, c_length);
+
+#ifndef NDEBUG
+ //plot file
+ std::cout << k << '\t' << (e_k >= d_k ? '
' : '-') << '\t' <<
e_k << '\t' << d_k << '\t'
+ << ((qk - buf_qk[1]).sqr_norm() / qk.sqr_norm()) <<
'\t'
+ << std::endl;
+ //count the number of points processed
+ pts += c_length;
+#endif
+ k++;
+ } while ((qk - buf_qk[1]).sqr_norm() / qk.sqr_norm() > epsilon);
+
+ trace::exiting("registration::impl::icp_");
+ }
+
+ } // end of namespace mln::registration::impl
+
+
+ // Only for 3d images
+ template <typename P, typename M>
+ inline
+ quat7<P::dim>
+ icp(p_array<P> cloud, // FIXME : is almost const (shuffled)
+ const M& map,
+ const float q,
+ const unsigned nb_it,
+ const p_array<P>& x)
+ {
+ trace::entering("registration::icp");
+
+ mln_precondition(P::dim == 3);
+ mln_precondition(cloud.npoints() != 0);
+
+ shuffle(cloud);
+
+ //init rigid transform qk
+ quat7<P::dim> qk;
+
+#ifndef NDEBUG // FIXME: theo
+ image2d<value::rgb8> tmp(500,800);
+ level::fill(tmp, literal::black);
+ //write X
+ mln_piter(p_array<P>) p(x);
+ for_all(p)
+ {
+ point2d qp = make::point2d(p[0], p[1]);
+ if (tmp.has(qp))
+ tmp(qp) = literal::white;
+ }
+#endif
+
+
+ //run icp
+ for (int e = nb_it-1; e >= 0; e--)
+ {
+
+ size_t l = cloud.npoints() / std::pow(q, e);
+ l = (l<1) ? 1 : l;
+ impl::icp_(cloud, map, qk, l, 1e-3);
+
+#ifndef NDEBUG
+ {
+ value::rgb8 c;
+ switch (e) {
+ case 2: c = literal::green; break;
+ case 1: c = literal::blue; break;
+ case 0: c = literal::red; break;
+ }
+ mln_piter(p_array<P>) p(cloud);
+ for_all(p)
+ {
+ algebra::vec<3,float> p_ = point3d(p), qp_ = qk(p_);
+ point2d qp = make::point2d(int(qp_[0]), int(qp_[1]));
+ if (tmp.has(qp))
+ tmp(qp) = c;
+ }
+ if (e == 0)
+ io::ppm::save(tmp, "tmp.ppm");
+ }
+#endif
+ }
+
+ trace::exiting("registration::icp");
+
+ return qk;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace mln::registration
+
+} // end of namespace mln
+
+
+#endif // ! MLN_REGISTRATION_ICP_HH
Index: jardonnet/registration/icp.hh
--- jardonnet/registration/icp.hh (revision 1945)
+++ jardonnet/registration/icp.hh (working copy)
@@ -85,9 +85,9 @@
template <typename P, typename M>
inline
- p_array<P>
+ void
icp_(const p_array<P>& C,
- M& map,
+ const M& map,
quat7<P::dim>& qk,
const size_t c_length,
const float epsilon = 1e-3)
@@ -171,7 +171,6 @@
} while (/*k < 3 ||*/ (qk - buf_qk[1]).sqr_norm() /
qk.sqr_norm() > epsilon);
trace::exiting("registration::impl::icp_");
- return Ck;
}
} // end of namespace mln::registration::impl
@@ -181,8 +180,8 @@
template <typename P, typename M>
inline
quat7<P::dim>
- icp(p_array<P> cloud,
- M& map,
+ icp(p_array<P> cloud, //here reference implies low efficiency
(FIXME:check again)
+ const M& map,
const float q,
const unsigned nb_it,
const p_array<P>& x)