https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox Index: ChangeLog from Alexandre Abraham <abraham@lrde.epita.fr> Add a new Watershed algorithm. * abraham/tests/morpho/test_component_tree.cc: New. * abraham/tests/morpho/images: New (test images). * abraham/tests/morpho/images/+test_watershed.pgm: New. * abraham/tests/morpho/images/result_m_watershed.pgm: New. * abraham/tests/morpho/images/test_component_tree.pgm: New. * abraham/tests/morpho/images/test_2.pgm: New. * abraham/tests/morpho/images/test_3.pgm: New. * abraham/tests/morpho/images/test_4.pgm: New. * abraham/tests/morpho/images/result_topo_watershed.pgm: New. * abraham/tests/morpho/images/test_watershed.pgm: New. * abraham/tests/morpho/images/+irm6.pgm: New. * abraham/tests/morpho/images/test_component_mapping.pgm: New. * abraham/tests/morpho/test_watershed.cc: New. * abraham/tests/morpho/test_watershed_topo.cc: New. * abraham/tests/morpho/ref/src/lib/lwshedtopo.c: uncomment specific pieces of code to show debug. * abraham/tests/morpho/Makefile: Fix. * abraham/tests/io/tikz/Makefile: Fix. * abraham/mln/morpho/basic_najman.hh: Add the new watershed from Pink library. * abraham/mln/morpho/test_component_tree.cc: Remove. * abraham/mln/morpho/test_watershed.cc: Remove. * abraham/mln/morpho/test_watershed_topo.cc: Remove. * abraham/mln/morpho/Makefile: Remove. * abraham/mln/io/tikz/save.hh: Add the ability to tag values on images. * abraham/img/dots.pgm: New. mln/io/tikz/save.hh | 13 - mln/morpho/Makefile | 22 - mln/morpho/basic_najman.hh | 396 +++++++++++++++++++++++++++++----- mln/morpho/test_component_tree.cc | 230 ------------------- mln/morpho/test_watershed.cc | 103 -------- mln/morpho/test_watershed_topo.cc | 60 ----- tests/io/tikz/Makefile | 1 tests/morpho/Makefile | 1 tests/morpho/ref/src/lib/lwshedtopo.c | 140 ++++++------ tests/morpho/test_component_tree.cc | 2 tests/morpho/test_watershed.cc | 2 tests/morpho/test_watershed_topo.cc | 9 12 files changed, 424 insertions(+), 555 deletions(-) Index: abraham/tests/morpho/test_component_tree.cc --- abraham/tests/morpho/test_component_tree.cc (revision 1935) +++ abraham/tests/morpho/test_component_tree.cc (working copy) @@ -8,7 +8,7 @@ #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> -#include "basic_najman.hh" +#include <mln/morpho/basic_najman.hh> #include <string> #include <iostream> Index: abraham/tests/morpho/images/+test_watershed.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/+test_watershed.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/result_m_watershed.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/result_m_watershed.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/test_component_tree.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/test_component_tree.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/test_2.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/test_2.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/test_3.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/test_3.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/test_4.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/test_4.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/result_topo_watershed.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/result_topo_watershed.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/test_watershed.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/test_watershed.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/images/+irm6.pgm Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: abraham/tests/morpho/images/+irm6.pgm ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: abraham/tests/morpho/images/test_component_mapping.pgm Cannot display: file marked as a binary type. svn:mime-type = abraham/tests/morpho/images/+irm6.pgm Property changes on: abraham/tests/morpho/images/test_component_mapping.pgm ___________________________________________________________________ Name: svn:mime-type + abraham/tests/morpho/images/+irm6.pgm Index: abraham/tests/morpho/test_watershed.cc --- abraham/tests/morpho/test_watershed.cc (revision 1935) +++ abraham/tests/morpho/test_watershed.cc (working copy) @@ -7,7 +7,7 @@ #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> -#include "basic_najman.hh" +#include <mln/morpho/basic_najman.hh> #include <string> #include <iostream> Index: abraham/tests/morpho/test_watershed_topo.cc --- abraham/tests/morpho/test_watershed_topo.cc (revision 1935) +++ abraham/tests/morpho/test_watershed_topo.cc (working copy) @@ -9,7 +9,7 @@ #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> -#include "basic_najman.hh" +#include <mln/morpho/basic_najman.hh> #include <string> #include <iostream> @@ -32,7 +32,8 @@ // io::pgm::load(input, "./images/test_watershed.pgm"); // io::pgm::load(input, "./images/little_test.pgm"); - io::pgm::load(input, "./images/test_2.pgm"); + io::pgm::load(input, "./images/test_4.pgm"); + // io::pgm::load(input, "../../img/dots.pgm"); // io::pgm::load(input, "./images/+irm6.pgm"); // io::pgm::load(input, "./images/lena_light.pgm"); Index: abraham/tests/morpho/ref/src/lib/lwshedtopo.c --- abraham/tests/morpho/ref/src/lib/lwshedtopo.c (revision 1966) +++ abraham/tests/morpho/ref/src/lib/lwshedtopo.c (working copy) @@ -266,13 +266,13 @@ } } } -#ifdef _DEBUG_ + //#ifdef _DEBUG_ for (i=0; i<logn; i++) { for (j=0; j<nbRepresent; j++) printf("M[%d][%d] = %d - ", i, j, Minim[i][j]); printf("\n"); } -#endif + //#endif return Minim; } @@ -491,10 +491,10 @@ if (CT->tabnodes[c].nbsons > 1) // noeud { } -#ifdef PARANO + //#ifdef PARANO else printf("%s : ERREUR COMPOSANTE BRANCHE!!!\n", F_NAME); -#endif + //#endif // empile les c-voisins de x non marques MASSIF ni EN_FAH switch (connex) { Index: abraham/tests/morpho/Makefile --- abraham/tests/morpho/Makefile (revision 1966) +++ abraham/tests/morpho/Makefile (working copy) @@ -16,4 +16,6 @@ .PHONY: clean distclean ## Depedencies -tikz.o: ../../../mln/io/tikz/save.hh \ No newline at end of file +test_watershed_topo.cc: ../../mln/morpho/basic_najman.hh +test_watershed.cc: ../../mln/morpho/basic_najman.hh +test_componenet_tree.cc: ../../mln/morpho/basic_najman.hh \ No newline at end of file Index: abraham/tests/io/tikz/Makefile --- abraham/tests/io/tikz/Makefile (revision 1966) +++ abraham/tests/io/tikz/Makefile (working copy) @@ -21,7 +21,6 @@ rm -rf *~ rm -rf *.log rm -rf *.aux - rm -rf distclean: clean rm -rf $(EXEC) Index: abraham/mln/morpho/basic_najman.hh --- abraham/mln/morpho/basic_najman.hh (revision 1966) +++ abraham/mln/morpho/basic_najman.hh (working copy) @@ -7,6 +7,7 @@ #include <mln/math/sqr.hh> #include <queue> +#include <vector> #include <set> namespace mln @@ -158,14 +159,15 @@ - template <typename I, typename N> + template <typename I, typename N, typename B> class my_lesser_psite { public: typedef mln_psite(I) psite; my_lesser_psite(const Image<I>& ima, - const Neighborhood<N>& nbh); + const Neighborhood<N>& nbh, + const Image<B>& enqueued); /// Is \a x lesser than \a y? bool operator()(const psite& x, const psite& y); @@ -173,30 +175,33 @@ private: const I& ima_; const N& nbh_; + const B& enqueued_; }; - template <typename I, typename N> - my_lesser_psite<I, N> + template <typename I, typename N, typename B> + my_lesser_psite<I, N, B> make_my_lesser_psite(const Image<I>& ima, - const Neighborhood<N>& nbh); + const Neighborhood<N>& nbh, + const Image<B>& enqueued); # ifndef MLN_INCLUDE_ONLY - template <typename I, typename N> - my_lesser_psite<I, N>::my_lesser_psite(const Image<I>& ima, - const Neighborhood<N>& nbh) - : ima_ (exact(ima)), nbh_(exact(nbh)) + template <typename I, typename N, typename B> + my_lesser_psite<I, N, B>::my_lesser_psite(const Image<I>& ima, + const Neighborhood<N>& nbh, + const Image<B>& enqueued) + : ima_ (exact(ima)), nbh_(exact(nbh)), enqueued_(exact(enqueued)) { } - template <typename I, typename N> + template <typename I, typename N, typename B> bool - my_lesser_psite<I, N>::operator()(const mln_psite(I)& x, const mln_psite(I)& y) + my_lesser_psite<I, N, B>::operator()(const mln_psite(I)& x, const mln_psite(I)& y) { -#ifdef V1 +#if 0 if (ima_(x) == ima_(y)) { @@ -212,27 +217,47 @@ if (ima_.has(b) and ima_(b) > ima_(y)) ++my; - return mx > my; + return mx < my; } -#else +#endif +#if 0 if (ima_(x) == ima_(y)) { mln_value(I) mx = ima_(x), my = ima_(y); mln_niter(N) a(nbh_, x); for_all(a) - if (ima_.has(a) and ima_(a) > ima_(mx)) - mx = a; + if (ima_.has(a) and ima_(a) > mx) + mx = ima_(a); + + mln_niter(N) b(nbh_, y); + for_all(b) + if (ima_.has(b) and ima_(b) > my) + my = ima_(b); + + return mx < my; + } +#endif + +#if 1 + if (ima_(x) == ima_(y)) + { + int mx = 0, my = 0; + + mln_niter(N) a(nbh_, x); + for_all(a) + if (ima_.has(a) and enqueued_(a)) + ++mx; mln_niter(N) b(nbh_, y); for_all(b) - if (ima_.has(b) and ima_(b) > ima_(my)) - my = b; + if (ima_.has(b) and enqueued_(b)) + ++my; - return ima_(x) < ima(y); + return mx > my; } @@ -243,16 +268,154 @@ } - template <typename I, typename N> - my_lesser_psite<I, N> + template <typename I, typename N, typename B> + my_lesser_psite<I, N, B> make_my_lesser_psite(const Image<I>& ima, - const Neighborhood<N>& nbh) + const Neighborhood<N>& nbh, + const Image<B>& enqueued) { - return my_lesser_psite<I, N>(ima); + return my_lesser_psite<I, N, B>(ima, nbh, enqueued); } # endif // ! MLN_INCLUDE_ONLY + + + + + + + // We need a custom queue in fact... + + // #define NPRIO 65536 + + template <typename P, typename V> + class fah_queue { + + public : + + // Push + void push(P point, + V level) + { + if (!util_ || (level < level_)) + level_ = level; + + ++util_; + if (util_ > max_util_) + max_util_ = util_; + + if (is_empty(level)) + queues[level] = new pqueue(); + + queues[level]->push(point); + + } // void push(P point, V level) + + + // Top + P top () + { + if (!util_) { + std::cerr << "Fah vide!" << std::endl; + exit (1); // FIXME! + } + return top(level_); + } // P top () + + + // Pop + void pop () + { + if (!util_) { + std::cerr << "Fah vide!" << std::endl; + return; + } + pop(level_); + } // void pop () + + + // Is Empty + bool is_empty () { + return !util_; + } // bool is_empty () + + + // Ctor + fah_queue() : + level_(0), + util_(0), + max_util_(0) + { } // fah_queue() + + + // Dtor + ~fah_queue() + { + typename qmap::iterator i; + + for (i = queues.begin(); i != queues.end(); ++i) + delete (i->second); + } + + + // Print + + + // Flush + + + private : + + V level_; /* niveau a partir duquel des listes existent */ + int util_; /* nombre de points courant dans la fah */ + int max_util_; /* nombre de points utilises max (au cours du temps) */ + + typedef std::queue <P> pqueue; + typedef std::map <V, pqueue *> qmap; + qmap queues; + + + // Is Empty + bool is_empty (V level) { + return ((queues.find(level) == queues.end()) || queues[level]->empty()); + } // bool is_empty (V level) + + + // Top + P top (V level) + { + if (is_empty(level)) + { + std::cerr << "erreur Fah vide au niveau " << level << std::endl; + exit (1); // FIXME! + } + return queues[level]->front(); + } + + + // Pop + void pop (V level) + { + if (is_empty(level)) + { + std::cerr << "erreur Fah vide au niveau " << level << std::endl; + return; + } + + util_--; + queues[level]->pop(); + + typename qmap::iterator i = queues.find(level); + for (; i != queues.end() && i->second->empty(); ++i) + ; + + level_ = i->first; + } + + }; // class fah_queue + + } // end of namespace mln::util @@ -713,8 +876,6 @@ mln_niter(N) q(nbh, p); p_set<psite> v; - psite pmax = p; - for_all(q) if (pima.has(q) && pima(q) > pima(p)) v.insert(Par_node(q)); @@ -726,16 +887,17 @@ return v[0]; psite - c = max(v); + c = max(v), + cmax = c; typename p_set<psite>::fwd_piter it(v); for_all(it) { // Can't remove the point from the set - if (it.to_point() == c) + if (it.to_point() == cmax) continue; - psite c1 = lca(c, it.to_point()); + psite c1 = lca_opt(c, it.to_point()); if (c1 != it.to_point()) c = c1; } @@ -919,7 +1081,9 @@ { init(); + std::cout << "Build component tree..." << std::endl; BuildComponentTree(); + std::cout << " done" << std::endl; mln_piter(I) p (pima.domain()); for_all(p) @@ -927,7 +1091,17 @@ revert_tree(Root); + std::cout << "Build euler tour..." << std::endl;; + build_euler_tour(); + std::cout << " done" << std::endl; + + std::cout << "Build minim table..." << std::endl;; + build_minim(); + std::cout << " done" << std::endl; + + std::cout << "Watersheding..." << std::endl;; topo_watershed(); + std::cout << " done" << std::endl; for_all(p) pima(p) = 255 - pima(p); @@ -945,15 +1119,8 @@ // Not used // level::fill(isproc, false); - - typedef - std::priority_queue< psite, std::vector<psite>, util::lesser_psite<I> > - ordered_queue_type; - - ordered_queue_type l(util::make_lesser_psite(pima)); - - - + util::fah_queue < psite, mln_value(I) > l; + mln_value(I) max = mln_max(mln_value(I)); // Flag C-maxima level::fill(cmax, false); @@ -962,16 +1129,6 @@ if (nodes(Par_node(it.to_point())).children.npoints() == 0) cmax(it.to_point()) = true; -#ifdef SLOW - - // Enqueue all - for_all(it) - { - enqueued(it.to_point()) = true; - l.push(it.to_point()); - } -#else // !SLOW - // Optimisation : enqueue minima's neighbours level::fill(enqueued, false); for_all(it) @@ -981,16 +1138,16 @@ if (cmax.has(q) && cmax(q)) { enqueued(it.to_point()) = true; - l.push(it.to_point()); + + l.push(it.to_point(), max - pima(it.to_point())); + break; } } -#endif // SLOW - // Main loop - while(!l.empty()) + while(!l.is_empty()) { psite x = l.top(); l.pop(); @@ -998,11 +1155,6 @@ psite c = w_constructible(x); - if (x == psite(3, 3)) - { - std::cout << "w-c : " << c << std::endl; - io::pgm::save(pima, "int.pgm"); - } if (c != psite(-1, -1)) { @@ -1017,14 +1169,15 @@ { } else - std::cerr << "ERREUR COMPOSANTE BRANCHE" << std::endl; + std::cerr << "ERREUR COMPOSANTE BRANCHE " << nodes(c).children.npoints() << std::endl; mln_niter(N) q(nbh, x); for_all(q) if (pima.has(q) && !cmax(q) && !enqueued(q)) { enqueued(q) = true; - l.push(q); + + l.push(q, max - pima(q)); } } // if (c != psite(-1, -1)) } // while(!l.empty()) @@ -1032,6 +1185,133 @@ for_all(it) pima(it.to_point()) = nodes(Par_node(it.to_point())).level; } + + + // Optimized LCA Algorithm + + int *euler; + int *depth; + int ctree_size; + std::map<psite, int> pos; + psite *repr; + + int *minim; + int **Minim; + + void compute_ctree_size (psite p) + { + ctree_size += 1; + node& n = nodes(p); + + typename p_array<mln_psite(I)>::fwd_piter it(n.children); + for_all(it) + compute_ctree_size(it.to_point()); + } + + void build_euler_tour_rec(psite p, int &position, int d) + { + if (pos.find(p) == pos.end()) + pos[p] = position; + + repr[position] = p; + depth[position] = d; + euler[position] = pos[p]; + position++; + node& n = nodes(p); + + typename p_array<mln_psite(I)>::fwd_piter it(n.children); + for_all(it) + { + build_euler_tour_rec(it.to_point(), position, d+1); + depth[position] = d; // Not optimized + euler[position] = pos[p]; + position++; + } + + } + + void build_euler_tour () + { + ctree_size = 0; + compute_ctree_size(Root); + + int size = 2 * ctree_size - 1; + + // FIXME : free this + euler = new int[size]; + depth = new int[size]; + repr = new psite[size]; + + int position = 0; + build_euler_tour_rec(Root, position, 0); + } + + void build_minim () + { + // compute_tree_size(); // Already done in build_euler_tour + int size = 2 * ctree_size - 1; + int logn = (int)(ceil(log((double)(size))/log(2.0))); + // minim.reserve(size); + minim = new int [logn * size]; // FIXME : Think about freeing this + Minim = new int* [logn]; + + Minim[0] = minim; + + // Init + for (int i = 0; i < size - 1; ++i) + if (depth[euler[i]] < depth[euler[i+1]]) { + Minim[0][i] = i; + } else { + Minim[0][i] = i+1; + } + + Minim[0][size - 1] = size - 1; + + int k; + for (int j = 1; j < logn; j++) { + k = 1 << (j - 1); + Minim[j] = &minim[j * size]; + for (int i = 0; i < size; i++) { + if ((i + (k << 1)) >= size) { + Minim[j][i] = size - 1; + } + else { + if (depth[euler[Minim[j - 1][i]]] <= depth[euler[Minim[j - 1][i + k]]]) + Minim[j][i] = Minim[j - 1][i]; + else + Minim[j][i] = Minim[j - 1][i + k]; + } + } + } + + } // void build_minim () + + psite lca_opt (psite a, psite b) + { + int + m = pos[a], + n = pos[b], + k; + + if (m == n) + return repr[m]; + + if (m > n) + { + k = n; + n = m; + m = k; + } + + k = (int)(log((double)(n - m))/log(2.)); + + if (depth[euler[Minim[k][m]]] < depth[euler[Minim[k][n - (1 << k)]]]) { + return repr[euler[Minim[k][m]]]; + } else { + return repr[euler[Minim[k][n - (1 << k)]]]; + } + } + }; // struct basic_najman }; // namespace morpho Index: abraham/mln/io/tikz/save.hh --- abraham/mln/io/tikz/save.hh (revision 1966) +++ abraham/mln/io/tikz/save.hh (working copy) @@ -165,9 +165,7 @@ { file << "\\path (" << p.col() << "," << max_row - p.row() << ") node[fill="; write_value(file, ima(p)); - file << "] (p_" << p.row() << "_" << p.col() << ") { \\color{"; - file << "black"; - file << "}" << "};" << std::endl; + file << "] (p_" << p.row() << "_" << p.col() << ") { }" << "};" << std::endl; } } @@ -184,6 +182,10 @@ min_col = geom::min_col(ima), max_col = geom::max_col(ima); + typedef mln_value(I) ival; + ival mid = (mln_max(ival) - mln_min(ival)) / 2; + mid += mln_min(ival); + point2d p; for (p.row() = min_row; p.row() <= max_row; ++p.row()) for (p.col() = min_col; p.col() <= max_col; ++p.col()) @@ -191,7 +193,10 @@ file << "\\path (" << p.col() << "," << max_row - p.row() << ") node[fill="; write_value(file, ima(p)); file << "] (p_" << p.row() << "_" << p.col() << ") { \\color{"; + if (ima(p) > mid) file << "black"; + else + file << "white"; file << "}" << val(p) << "};" << std::endl; } } Index: abraham/img/dots.pgm Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: abraham/img/dots.pgm ___________________________________________________________________ Name: svn:mime-type + application/octet-stream