https://svn/svn/oln/prototypes/proto-1.0/olena
Index: ChangeLog
from Nicolas Widynski <nicolas.widynski(a)lrde.epita.fr>
Add a closed snake algorithm.
* oln/appli/snakes: New. Directory.
* oln/appli/snakes/snakes.hh: New. Snake algorithm.
2 forces : gradient and snake perimeter.
snakes.hh | 413 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 413 insertions(+)
Index: oln/appli/snakes/snakes.hh
--- oln/appli/snakes/snakes.hh (revision 0)
+++ oln/appli/snakes/snakes.hh (revision 0)
@@ -0,0 +1,413 @@
+// Copyright (C) 2001-2006 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, 59 Temple Place - Suite 330, 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 OLENA_SNAKES_HH
+# define OLENA_SNAKES_HH
+
+# include <mlc/is_a.hh>
+# include <oln/basics2d.hh>
+# include <ntg/all.hh>
+# include <oln/morpho/gradient_morpho.hh>
+# include <ntg/int.hh>
+# include <vector>
+
+#define sqr(u) ((u)*(u))
+const float PI = 3.14159265;
+
+
+namespace oln {
+
+ namespace snakes {
+
+ namespace impl {
+
+ inline
+ float euclidian_distance(int x1, int x2, int y1, int y2) // sqr
+ {
+ return sqr(x1 - x2) + sqr(y1 - y2);
+ }
+
+ template <typename T>
+ void verify_integrity_(const image2d<T>& grad,
+ int &x1,
+ int &x2,
+ int &y1,
+ int &y2)
+ {
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 < 0)
+ x2 = 0;
+ if (x1 > grad.nrows())
+ x1 = grad.nrows() - 1;
+ if (x2 > grad.nrows())
+ x2 = grad.nrows() - 1;
+
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 < 0)
+ y2 = 0;
+ if (y1 > grad.ncols())
+ y1 = grad.ncols() - 1;
+ if (y2 > grad.ncols())
+ y2 = grad.ncols() - 1;
+ }
+
+
+ std::vector<point2d> draw_line_(const point2d& p,
+ const point2d& q)
+ {
+ std::vector<point2d> res;
+
+ point2d p_cur = p;
+ if (q.col() == p.col()) // cas vertical
+ {
+ int sens = q.row() > p.row() ? 1 : -1;
+ while (p_cur != q)
+ {
+ double pc_p = euclidian_distance(p_cur.row(), p.row(), p_cur.col(), p.col());
+ double pc_q = euclidian_distance(p_cur.row(), q.row(), p_cur.col(), q.col());
+ double p_q = euclidian_distance(p.row(), q.row(), p.col(), q.col());
+
+ res.push_back(p_cur);
+ p_cur.row() = p_cur.row() + sens;
+ }
+ }
+ else
+ {
+ if (q.row() == p.row()) // cas horizontal
+ {
+ int sens = q.col() > p.col() ? 1 : -1;
+ while (p_cur != q)
+ {
+ double pc_p = euclidian_distance(p_cur.row(), p.row(), p_cur.col(), p.col());
+ double pc_q = euclidian_distance(p_cur.row(), q.row(), p_cur.col(), q.col());
+ double p_q = euclidian_distance(p.row(), q.row(), p.col(), q.col());
+
+ res.push_back(p_cur);
+ p_cur.col() = p_cur.col() + sens;
+ }
+ }
+ else // cas diagonal
+ {
+ long decalage = 0;
+
+ long nom = q.row() - p.row();
+ long denom = q.col() - p.col();
+ int signe = (nom * denom) > 0 ? 1 : -1;
+ nom = nom * signe;
+ denom = denom * signe;
+
+ int vsens = q.row() > p.row() ? 1 : -1;
+ int hsens = q.col() > p.col() ? 1 : -1;
+
+ int deboule = 0;
+
+ while (p_cur != q)
+ {
+ double pc_p = euclidian_distance(p_cur.row(), p.row(), p_cur.col(), p.col());
+ double pc_q = euclidian_distance(p_cur.row(), q.row(), p_cur.col(), q.col());
+ double p_q = euclidian_distance(p.row(), q.row(), p.col(), q.col());
+
+ res.push_back(p_cur);
+
+ if (abs(decalage) >= abs(denom))
+ {
+ p_cur.row() = p_cur.row() + vsens;
+ decalage -= vsens * denom;
+ }
+ else
+ {
+ p_cur.col() = p_cur.col() + hsens;
+ decalage += nom * hsens;
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+
+
+
+ template <typename T>
+ std::vector<point2d> compute_normal_points_(const image2d<T>&
grad,
+ const point2d& p1,
+ const point2d& p2,
+ const point2d& p3,
+ unsigned int dmax)
+ {
+ float x_c = p2.row();
+ float y_c = p2.col();
+ float a_p1_p3 = (p1.col() - p3.col()) != 0 ? (float)(((float)(p1.row() - p3.row())) /
(p1.col() - p3.col())) : 0;
+ float a = -a_p1_p3;
+ float b = p2.col() - a * p2.row(); // use of - slope of p1 et p3
+
+ int x1 = 0;
+ int y1 = 0;
+ int x2 = 0;
+ int y2 = 0;
+
+ if (a != 0)
+ {
+ float A = 1 + sqr(a);
+ float B = -2 * (x_c + a * y_c - a * b);
+ float C = sqr(x_c) + sqr((b - y_c)) - sqr(dmax);
+
+ float delta = sqr(B) - (4 * A * C);
+
+ if (delta > 0)
+ {
+ x1 = (int)(roundf((-B + sqrt(delta)) / (2 * A)));
+ x2 = (int)(roundf((-B - sqrt(delta)) / (2 * A)));
+
+ if (x1 == x2)
+ {
+ float tmp1 = roundf((-B + sqrt(delta)) / (2 * A));
+ float tmp2 = roundf((-B - sqrt(delta)) / (2 * A));
+ if (tmp1 > tmp2)
+ x1++;
+ else
+ x2++;
+ }
+
+ y1 = (int)(a * x1 + b);
+ y2 = (int)(a * x2 + b);
+ }
+ }
+ else
+ {
+ x1 = p2.row() - dmax;
+ y1 = p2.col();
+ x2 = p2.row() + dmax;
+ y2 = p2.col();
+ }
+ verify_integrity_(grad, x1, x2, y1, y2);
+
+ return draw_line_(point2d(x1, y1), point2d(x2, y2));
+ }
+
+
+ float angle_force(const point2d& p1, const point2d& p2, const point2d&
p3)
+ { // al kashi
+ float b = euclidian_distance(p1.row(), p2.row(), p1.col(), p2.col());
+ float c = euclidian_distance(p3.row(), p2.row(), p3.col(), p2.col());
+ float a = euclidian_distance(p1.row(), p3.row(), p1.col(), p3.col());
+ float angle = acos((b + c - a) / (2 * sqrt(b) * sqrt(c))); // rad
+ angle = (angle * 180) / PI; //deg
+ return angle;
+ }
+
+
+
+ template <typename T>
+ std::vector<point2d> gen_snakes_(const image2d<T>& grad,
+ const std::vector<point2d>& v,
+ unsigned int fen,
+ float lambda,
+ unsigned int dmax)
+ {
+ std::vector<point2d>::const_iterator it = v.begin();
+ std::vector<point2d> res;
+
+ for (; (it + 2) != v.end(); it++)
+ {
+ point2d p1 = *it;
+ point2d p2 = *(it + 1);
+ point2d p3 = *(it + 2);
+ std::vector<point2d> v_normal = compute_normal_points_(grad, p1, p2, p3,
dmax);
+ std::vector<point2d>::const_iterator it2 = v_normal.begin();
+
+ float max = -1;
+ point2d n_pt(0,0);
+
+ for (; it2 != v_normal.end(); it2++)
+ {
+ float force = grad[*it2];
+ if (max < force)
+ {
+ max = force;
+ n_pt = *it2;
+ }
+ }
+ res.push_back(n_pt);
+ }
+
+ res.push_back(res[0]);
+ res.push_back(res[1]);
+
+
+ if (lambda != 0)
+ {
+ bool conv = false;
+
+ while (conv == false)
+ {
+ conv = true;
+ float max = 0;
+ std::vector<point2d>::iterator it3 = res.begin();
+ std::vector<point2d>::iterator p_s;
+ point2d n_pt(0,0);
+ for (; (it3 + 2) != res.end(); it3++)
+ {
+ point2d p1 = *it3;
+ point2d p2 = *(it3 + 1);
+ point2d p3 = *(it3 + 2);
+
+ std::vector<point2d> v_normal = compute_normal_points_(grad, p1, p2, p3,
dmax);
+ std::vector<point2d>::const_iterator it2 = v_normal.begin();
+
+ float angle_ref = angle_force(p1, p2, p3);
+
+ float force_ref = grad[p2] - (180 - angle_ref) * lambda;
+
+ for (; it2 != v_normal.end(); it2++)
+ {
+ float angle = angle_force(p1, *it2, p3);
+ float force = grad[*it2] - (180 - angle) * lambda;
+
+ if (p2.col() != (*it2).col() and p2.row() != (*it2).row() and force - force_ref >
max)
+ {
+ max = force - force_ref;
+ p_s = it3 + 1;
+ n_pt = *it2;
+ }
+ }
+ }
+
+ if (max > 0)
+ {
+ conv = false;
+ if (p_s == (res.begin() + 1)) // 2nd
+ {
+ (*(res.end() - 1)).col() = n_pt.col();
+ (*(res.end() - 1)).row() = n_pt.row();
+ }
+ else
+ if (p_s == (res.end() - 2)) // last
+ {
+ (*(res.begin())).col() = n_pt.col();
+ (*(res.begin())).row() = n_pt.row();
+ }
+ (*p_s).col() = n_pt.col();
+ (*p_s).row() = n_pt.row();
+ }
+ }
+ }
+
+ return res;
+ }
+
+
+ std::vector<point2d> regen_snaxels(std::vector<point2d>& v)
+ {
+ std::vector<point2d> res;
+ std::vector<point2d> tmp;
+ std::vector<point2d>::iterator it = v.begin();
+ std::vector<point2d>::iterator it2;
+
+ for (it = v.begin(); (it + 1) != v.end(); it++)
+ {
+ std::vector<point2d> tmp2 = draw_line_(*it, *(it+1));
+ for (it2 = tmp2.begin(); it2 != tmp2.end(); it2++)
+ tmp.push_back(*it2);
+ }
+
+ int nb_pts = v.size() * 2;
+ int cpt = 1;
+
+ for (it2 = tmp.begin(); it2 != tmp.end(); it2++, cpt++)
+ if ((cpt % (tmp.size() / nb_pts)) == 0)
+ res.push_back(*it2);
+
+ return res;
+ }
+
+ template <typename T>
+ void clean_ima(image2d<T>& ima)
+ {
+ for (int j = 0; j < ima.size().ncols(); j++)
+ {
+ ima(-1, j) = ima(0, j);
+ ima(ima.size().nrows(), j) = ima(ima.size().nrows() - 1, j);
+ }
+
+ for (int j = 0; j < ima.size().nrows(); j++)
+ {
+ ima(j, -1) = ima(j, 0);
+ ima(j, ima.size().ncols()) = ima(j, ima.size().ncols() - 1);
+ }
+ }
+
+
+ template <typename T>
+ std::vector<point2d>
+ snakes_(const image2d<T>& input,
+ unsigned int fen,
+ unsigned int nb_gen,
+ float lambda,
+ unsigned int dmax)
+ {
+ image2d<T> grad = morpho::gradient_morpho(input, win_c4p());
+ clean_ima(grad);
+
+ std::vector<point2d> res;
+
+ // build init points + 2 (close snake)
+
+ int i = 0;
+ while (i < nb_gen)
+ {
+ res = gen_snakes_(grad, res, fen, lambda, dmax);
+ i++;
+ if (i < nb_gen)
+ res = regen_snaxels(res);
+ }
+
+ return res;
+ }
+
+
+ } // end of namespace oln::impl::snakes
+
+ template <typename T>
+ std::vector<point2d>
+ snakes(const image2d<T>& input,
+ unsigned int fen,
+ unsigned int nb_gen,
+ float lambda,
+ unsigned int dmax)
+ {
+ return impl::snakes_(input, fen, nb_gen, lambda, dmax);
+ }
+
+ } // end of namespace oln::appli
+
+} // end of namespace oln
+
+
+#endif // ! OLENA_SNAKES_HH