https://svn.lrde.epita.fr/svn/oln/branches/cleanup-2008/milena/sandbox
Index: ChangeLog
from Nicolas Ballas <ballas(a)lrde.epita.fr>
Inim: Add the code related to edge detection in the sandbox.
* ballas/color: New.
* ballas/color/min_tree_volume_filter.cc: Min tree using volume filter.
* ballas/color/reference.cc: Method using a gradient.
* ballas/color/min_tree_area_filter.cc: Min tree using area filter.
* ballas/color/min_tree_color.cc: Min tree using filter based on color.
* ballas/color/reference2.cc: Method using the laplacien.
* ballas/color/src: New.
* ballas/color/src/graph.hh,
* ballas/color/src/io.hh,
* ballas/color/src/distance.hh,
* ballas/color/src/convert.hh: New, factorize some code.
* ballas/color/laplacien.cc: Laplacien tests.
laplacien.cc | 129 ++++++++++
min_tree_area_filter.cc | 505 +++++++++++++++++++++++++++++++++++++++++
min_tree_color.cc | 525 +++++++++++++++++++++++++++++++++++++++++++
min_tree_volume_filter.cc | 514 ++++++++++++++++++++++++++++++++++++++++++
reference.cc | 556 ++++++++++++++++++++++++++++++++++++++++++++++
reference2.cc | 406 +++++++++++++++++++++++++++++++++
src/convert.hh | 37 +++
src/distance.hh | 52 ++++
src/graph.hh | 61 +++++
src/io.hh | 57 ++++
10 files changed, 2842 insertions(+)
Index: ballas/color/min_tree_volume_filter.cc
--- ballas/color/min_tree_volume_filter.cc (revision 0)
+++ ballas/color/min_tree_volume_filter.cc (revision 0)
@@ -0,0 +1,514 @@
+# include <mln/core/var.hh>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/core/image/image_if.hh>
+# include <mln/core/image/extended.hh>
+# include <mln/core/routine/extend.hh>
+
+# include <mln/core/alias/window2d.hh>
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/make/double_neighb2d.hh>
+# include <mln/core/site_set/p_centered.hh>
+
+# include <mln/literal/origin.hh>
+# include <mln/literal/black.hh>
+# include <mln/literal/white.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/value/int_u16.hh>
+# include <mln/io/pgm/load.hh>
+# include <mln/io/pgm/save.hh>
+
+# include <mln/value/rgb8.hh>
+# include <mln/io/ppm/load.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <mln/morpho/closing_area.hh>
+
+# include <mln/level/paste.hh>
+# include <mln/level/fill.hh>
+# include <mln/level/transform.hh>
+# include <mln/extension/fill.hh>
+
+# include <mln/debug/println.hh>
+
+# include "src/distance.hh"
+
+namespace mln
+{
+ template <typename I, typename N, typename Ic, typename Nc>
+ struct min_tree_
+ {
+ typedef mln_site(I) point;
+ typedef p_array<point> S;
+
+ // in:
+ const I& f;
+ const N& nbh;
+ const Ic& ref;
+ const Nc& nbhc;
+
+ // aux:
+ S s;
+ mln_ch_value(I, bool) deja_vu;
+ mln_ch_value(I, point) parent;
+ mln_ch_value(I, bool) resp;
+ mln_ch_value(I, point) zpar;
+
+ // attached data:
+ int lambda;
+ mln_ch_value(I, int) volume;
+ //mln_ch_value(Ic, value::rgb8) values;
+ //initialize(values, ref);
+ //mln_ch_value(I, int) comp;
+
+ min_tree_(const I& f, const N& nbh, const Ic& ref, const Nc& nbhc,
+ int lambda)
+ : f(f),
+ nbh(nbh),
+ ref(ref),
+ nbhc(nbhc),
+ lambda(lambda)
+ {
+ run();
+ }
+
+ void run()
+ {
+ // init
+ {
+ initialize(deja_vu, f);
+ initialize(parent, f);
+ initialize(resp, f);
+ initialize(zpar, f);
+ initialize(volume, f);
+ //initialize(comp, f);
+
+ mln::level::fill(deja_vu, false);
+ //mln::level::fill(resp, false);
+ mln::level::fill(volume, 0);
+
+ s = level::sort_psites_increasing(f);
+ }
+
+ // first pass
+ {
+ mln_fwd_piter(S) p(s);
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ make_set(p);
+ for_all(n)
+ if (f.has(n) && deja_vu(n))
+ do_union(n, p);
+ deja_vu(p) = true;
+ }
+ }
+
+ // second pass: canonization
+ {
+ mln_bkd_piter(S) p(s);
+ for_all(p)
+ {
+ point q = parent(p);
+ if (f(parent(q)) == f(q))
+ {
+ parent(p) = parent(q);
+ resp(q) = false;
+ }
+ }
+ }
+
+ // third pass: Merging region with volume < lambda
+ {
+ mln_fwd_piter(S) p(s);
+ for_all(p)
+ {
+ if (resp(p) && (volume(p) < lambda))
+ {
+ resp(p) = false;
+ update_data(parent(p), volume(p));
+ }
+ }
+ }
+
+ } // end of run()
+
+ void make_set(const point& p)
+ {
+ parent(p) = p;
+ zpar(p) = p;
+ init_data(p);
+ }
+
+ void set_parent(const point& r, const point& p)
+ {
+ parent(r) = p;
+ merge_data(r, p);
+ }
+
+ bool is_root(const point& p) const
+ {
+ return parent(p) == p;
+ }
+
+ bool is_node(const point& p) const
+ {
+ //return is_root(p) || f(parent(p)) != f(p);
+ return (is_root(p) || resp(p));
+ }
+
+ point find_root(const point& x)
+ {
+ if (zpar(x) == x)
+ return x;
+ else
+ return zpar(x) = find_root(zpar(x));
+ }
+
+ point find_representative(const point& x)
+ {
+ if (parent(x) == x || resp(x))
+ return x;
+ else
+ return find_representative(parent(x));
+ }
+
+ void do_union(const point& n, const point& p)
+ {
+ point r = find_root(n);
+ if (r != p)
+ {
+ set_parent(r, p);
+ zpar(r) = p;
+ }
+ }
+
+ void init_data(const point& p)
+ {
+ int red =0, green = 0, blue = 0;
+
+ mln_niter(Nc) n(nbhc, p);
+ for_all(n)
+ {
+ red += ref(n).red();
+ green += ref(n).green();
+ blue += ref(n).blue();
+ }
+
+ red /= 2;
+ green /= 2;
+ blue /= 2;
+
+ volume(p) = distance(value::rgb8(red, green, blue),
+ value::rgb8(0, 0, 0));
+ resp(p) = true;
+ }
+
+ void merge_data(const point& r, const point& p)
+ {
+ if (f(p) == f(r))
+ {
+ resp(p) = false;
+ volume(r) += volume(p);
+ }
+ }
+
+ void update_data(const point& p, int val)
+ {
+ volume(p) += val;
+ if (parent(p) != p && !resp(p))
+ update_data(parent(p), val);
+ }
+
+ };
+}
+
+namespace mln
+{
+ image2d<value::int_u16> convert_to_grey(const image2d<value::rgb8>&
data)
+ {
+ image2d<value::int_u16> output(data.domain());
+ mln_piter_(image2d<value::int_u16>) p(output.domain());
+ for_all(p)
+ output(p) = (int) (data(p).red() * 0.3 + data(p).green() * 0.58 + data(p).blue()) *
0.12;
+ return output;
+ }
+} // end of mln
+
+namespace mln
+{
+
+ struct colorize : Function_v2v< colorize >
+ {
+ typedef value::rgb8 result;
+ colorize(unsigned max)
+ : lut(max + 1)
+ {
+ lut[0] = literal::black;
+ for (unsigned i = 1; i <= max; ++i)
+ lut[i] = result(100 + std::rand() % 150,
+ 100 + std::rand() % 150,
+ 100 + std::rand() % 150);
+ }
+ result operator()(unsigned i) const
+ {
+ return lut[i];
+ }
+ std::vector<result> lut;
+ };
+
+ template <typename I>
+ I display_edge(const I& ima, mln_value(I) bg, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+ level::fill(output, bg);
+
+ mln_VAR(edge, ima | is_edge);
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+
+ template <typename I>
+ I display_edge(const I& ima, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+
+ mln_VAR( cell, ima | is_cell );
+ mln_piter(cell_t) q(cell.domain());
+ for_all(q)
+ {
+ unsigned row = (q.row() / 2) * (zoom + 1);
+ unsigned col = (q.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ for (unsigned j = 0; j < zoom; ++j)
+ output.at(row + i, col + j) = ima(q);
+ }
+
+ mln_VAR( edge, ima | is_edge );
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+
+ namespace morpho
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ dilation(const I& input, const N& nbh)
+ {
+ typedef mln_value(I) V;
+
+ mln_concrete(I) output;
+ initialize(output, input);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ for_all(n)
+ if (input.has(n) && input(n) != value::rgb8(0,0,0))
+ output(p) = input(n);
+ }
+ return output;
+ }
+ } // mln::morpho
+
+} // mln
+
+
+
+template <typename T>
+mln::image2d<T>
+image2cells(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output(2 * input.nrows() - 1,
+ 2 * input.ncols() - 1);
+ for (unsigned row = 0; row < input.nrows(); ++row)
+ for (unsigned col = 0; col < input.ncols(); ++col)
+ output.at(2 * row, 2 * col) = input.at(row, col);
+ return output;
+}
+
+
+template <typename T>
+mln::image2d<T>
+cells2image(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output((input.nrows() + 1) / 2,
+ (input.ncols() + 1) / 2);
+ for (unsigned row = 0; row < input.nrows(); row += 2)
+ for (unsigned col = 0; col < input.ncols(); col += 2)
+ output.at(row / 2, col / 2) = input.at(row, col);
+ return output;
+}
+
+
+template <typename I, typename N, typename Ic, typename Nc>
+unsigned min_tree(const I& f, const N& nbh, const Ic& ref, const Nc&
nbhc,
+ int lambda)
+{
+ using namespace mln;
+
+ min_tree_<I,N,Ic,Nc> run(f, nbh, ref, nbhc, lambda);
+
+ mln_piter(I) p(f.domain());
+ unsigned nnodes = 0;
+ for_all(p)
+ {
+ if (run.is_node(p))
+ {
+ ++nnodes;
+ }
+ }
+
+ colorize colors(nnodes);
+ image2d<value::rgb8> tmp(ref.domain());
+ level::fill(tmp, ref);
+
+ mln_piter(I) q(f.domain());
+ unsigned int i = 0;
+ for_all(q)
+ {
+ if (run.is_node(q))
+ {
+ tmp(q) = colors(i);
+ i++;
+ }
+ }
+ mln_piter(I) r(f.domain());
+ for_all(r)
+ {
+ if (!run.is_node(r))
+ {
+ tmp(r) = tmp(run.find_representative(r));
+ }
+ }
+
+ image2d<value::rgb8> to_display(tmp.domain());
+
+ level::fill(to_display, value::rgb8(255, 255, 255));
+ level::paste((tmp | is_edge), to_display);
+ level::paste(morpho::dilation(to_display, c4()), to_display);
+
+ io::ppm::save(display_edge(tmp, literal::black, 3),
+ "edge.ppm");
+ io::ppm::save(tmp, "full.ppm");
+ io::ppm::save(cells2image(to_display), "colorize.ppm");
+
+
+ return nnodes;
+}
+
+
+template <typename I>
+I
+do_it(I& input, int lambda, unsigned& nbasins)
+{
+ using namespace mln;
+
+ /// Graph creation
+ I graph;
+ create_graph(input, graph, value::rgb8(0, 0, 0));
+
+ // Initialization
+ image2d<value::int_u16> ima = convert_to_grey(graph);
+
+ // Neigbhorhood
+ // e2c
+ bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+
+ mln_VAR(e2c, make::double_neighb2d(is_row_odd, e2c_h, e2c_v));
+
+ bool e2e_h[] = { 0, 0, 1, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 1, 0, 0 };
+
+ bool e2e_v[] = { 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 1,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0 };
+ mln_VAR(e2e, make::double_neighb2d(is_row_odd, e2e_h, e2e_v));
+
+ // Algorithm
+ distance(extend((graph | is_edge).rw(), pw::value(graph)), e2c, ima);
+
+ io::pgm::save(ima, "edge.pgm");
+
+ nbasins = min_tree((ima | is_edge), e2e, graph, e2c, lambda);
+
+ return graph;
+}
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm
lambda" << std::endl;
+ std::cerr << " lambda >= 0" << std::endl;
+ abort();
+}
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ if (argc != 3)
+ usage(argv);
+
+ int lambda = atoi(argv[2]);
+ if (lambda < 0)
+ usage(argv);
+
+ image2d<value::rgb8> ima;
+ io::ppm::load(ima, argv[1]);
+
+ unsigned nbasins;
+ image2d<value::rgb8> output = do_it(ima, lambda, nbasins);
+
+ //io::ppm::save(output, argv[3]);
+}
Index: ballas/color/reference.cc
--- ballas/color/reference.cc (revision 0)
+++ ballas/color/reference.cc (revision 0)
@@ -0,0 +1,556 @@
+# include <mln/core/var.hh>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/core/image/image_if.hh>
+# include <mln/core/image/extended.hh>
+# include <mln/core/routine/extend.hh>
+
+# include <mln/core/alias/window2d.hh>
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/make/double_neighb2d.hh>
+# include <mln/core/site_set/p_centered.hh>
+
+# include <mln/literal/origin.hh>
+# include <mln/literal/black.hh>
+# include <mln/literal/white.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/io/pgm/load.hh>
+# include <mln/io/pgm/save.hh>
+
+# include <mln/value/rgb8.hh>
+# include <mln/io/ppm/load.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <mln/accu/min_max.hh>
+# include <mln/accu/mean.hh>
+
+# include <mln/fun/i2v/array.hh>
+# include <mln/fun/p2v/iota.hh>
+
+# include <mln/level/paste.hh>
+# include <mln/level/fill.hh>
+# include <mln/level/transform.hh>
+# include <mln/extension/fill.hh>
+# include <mln/convert/to.hh>
+
+# include <mln/linear/gaussian.hh>
+
+# include <mln/morpho/meyer_wst.hh>
+# include <mln/morpho/closing_volume.hh>
+
+# include <mln/make/w_window2d.hh>
+
+# include <mln/debug/println.hh>
+
+// Laplacian method
+namespace mln
+{
+ namespace linear
+ {
+ // required to deal with a input image that differ from the output since I
+ //don't succeed in using a float image in entry.
+ template <class I, class O>
+ inline
+ void
+ gaussian_2nd_derivative(const Image<I>& input, float sigma,
Image<O>& output)
+ {
+ mln_precondition(exact(input).has_data());
+
+ impl::recursivefilter_coef_
+ coef(-1.331f, 3.661f,
+ 1.24f, 1.314f,
+ 0.3225f, -1.738f,
+ 0.748f, 2.166f,
+ sigma, impl::gaussian_2nd_deriv_coef_norm_);
+ impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
+ input, coef, sigma, output);
+ }
+
+ template <class I, class O>
+ inline
+ void
+ gaussian_1st_derivative(const Image<I>& input, float sigma,
Image<O>& output)
+ {
+ mln_precondition(exact(input).has_data());
+
+ impl::recursivefilter_coef_
+ coef(-0.6472f, -4.531f,
+ 1.527f, 1.516f,
+ 0.6494f, 0.9557f,
+ 0.6719f, 2.072f,
+ sigma, impl::gaussian_1st_deriv_coef_norm_);
+ impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
+ input, coef, sigma, output);
+ }
+ }
+}
+
+// Gradient + watershed method
+namespace mln
+{
+ namespace morpho
+ {
+ template <typename I, typename N>
+ mln_concrete(I)
+ closing_volume(const I& input, const Neighborhood<N>& nbh, std::size_t
lambda)
+ {
+ mln_concrete(I) output;
+ initialize(output, input);
+ closing_volume(input, nbh, lambda, output);
+ return output;
+ }
+ }
+} // !mln
+
+namespace mln
+{
+ image2d<value::int_u8> convert_to_grey(const image2d<value::rgb8>&
data)
+ {
+ image2d<value::int_u8> output(data.domain());
+ mln_piter_(image2d<value::int_u8>) p(output.domain());
+ for_all(p)
+ output(p) = (int) (data(p).red() * 0.3 + data(p).green() * 0.58 + data(p).blue()) *
0.12;
+ return output;
+ }
+} // !mln
+
+// Functions
+
+inline
+bool is_row_odd(const mln::point2d& p)
+{
+ return p.row() % 2;
+}
+
+inline
+bool is_cell(const mln::point2d& p)
+{
+ return p.row() % 2 == 0 && p.col() % 2 == 0;
+}
+
+inline
+bool is_edge(const mln::point2d& p)
+{
+ return p.row() % 2 + p.col() % 2 == 1;
+}
+
+inline
+bool is_point(const mln::point2d& p)
+{
+ return p.row() % 2 && p.col() % 2;
+}
+
+inline
+bool is_not_edge(const mln::point2d& p)
+{
+ return ! is_edge(p);
+}
+
+
+
+namespace mln
+{
+
+ namespace border
+ {
+
+ template <typename I>
+ void
+ fill(I& ima, const mln_value(I)& v)
+ {
+ const int nrows = ima.nrows();
+ const int ncols = ima.ncols();
+ for (int r = -1; r <= nrows; ++r)
+ {
+ ima.at(r, -1) = v;
+ ima.at(r, ncols) = v;
+ }
+ for (int c = -1; c <= ncols; ++c)
+ {
+ ima.at(-1, c) = v;
+ ima.at(nrows, c) = v;
+ }
+ }
+
+ } // mln::border
+
+ namespace accu
+ {
+
+ template <typename I, typename L, typename A, typename V>
+ inline
+ void
+ compute(const Image<I>& input_,
+ const Image<L>& label_,
+ const Accumulator<A>&,
+ V& v)
+ {
+ trace::entering("accu::compute");
+
+ const I& input = exact(input_);
+ const L& label = exact(label_);
+
+ const unsigned n = v.size();
+ std::vector<A> a(n);
+
+ mln_piter(I) p(input.domain());
+ for_all(p)
+ a[label(p)].take(input(p));
+
+ for (unsigned l = 1; l < n; ++l)
+ v(l) = a[l].to_result();
+
+ trace::exiting("accu::compute");
+ }
+
+ } // mln::accu
+
+ namespace morpho
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ gradient(const I& input, const N& nbh)
+ {
+ mln_concrete(I) output;
+ initialize(output, input);
+ accu::min_max<mln_value(I)> mm;
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ mm.init();
+ for_all(n) if (input.has(n))
+ mm.take(input(n));
+ output(p) = mm.second() - mm.first();
+ }
+ return output;
+ }
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ dilation(const I& input, const N& nbh)
+ {
+ typedef mln_value(I) V;
+
+ mln_concrete(I) output;
+ initialize(output, input);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ for_all(n)
+ if (input.has(n) && input(n) != value::rgb8(0,0,0))
+ output(p) = input(n);
+ }
+ return output;
+ }
+ } // mln::morpho
+
+
+ struct colorize : Function_v2v< colorize >
+ {
+ typedef value::rgb8 result;
+ colorize(unsigned max)
+ : lut(max + 1)
+ {
+ lut[0] = literal::black;
+ for (unsigned i = 1; i <= max; ++i)
+ lut[i] = result(100 + std::rand() % 150,
+ 100 + std::rand() % 150,
+ 100 + std::rand() % 150);
+ }
+ result operator()(unsigned i) const
+ {
+ return lut[i];
+ }
+ std::vector<result> lut;
+ };
+
+
+ template <typename I>
+ I display_edge_with_bg(const I& ima, unsigned zoom, mln_value(I) bg)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+ level::fill(output, bg);
+ mln_VAR( edge, ima | is_edge );
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+ template <typename I>
+ I display_edge(const I& ima, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+
+ mln_VAR( cell, ima | is_cell );
+ mln_piter(cell_t) q(cell.domain());
+ for_all(q)
+ {
+ unsigned row = (q.row() / 2) * (zoom + 1);
+ unsigned col = (q.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ for (unsigned j = 0; j < zoom; ++j)
+ output.at(row + i, col + j) = ima(q);
+ }
+
+ mln_VAR( edge, ima | is_edge );
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+} // mln
+
+
+
+template <typename T>
+mln::image2d<T>
+image2cells(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output(2 * input.nrows() - 1,
+ 2 * input.ncols() - 1);
+ for (int row = 0; row < input.nrows(); ++row)
+ for (int col = 0; col < input.ncols(); ++col)
+ output.at(2 * row, 2 * col) = input.at(row, col);
+ return output;
+}
+
+namespace mln {
+
+ template <typename I, typename N, typename M>
+ mln_concrete(I)
+ mean(const I& input, const N& nbh, const M& nbh2)
+ {
+ mln_concrete(I) output;
+ initialize(output, input);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ mln_niter(M) m(nbh2, p);
+ for_all(p)
+ {
+ if (is_edge(p))
+ {
+ int nb = 0;
+ int r = 0, g = 0, b = 0;
+ for_all(n)
+ {
+ if (input.has(n))
+ {
+ r += input(n).red();
+ g += input(n).green();
+ b += input(n).blue();
+ ++nb;
+ }
+ }
+ output(p) = value::rgb8(r / nb, g / nb, b / nb);
+ }
+ if (is_point(p))
+ {
+ int nb = 0;
+ int r = 0, g = 0, b = 0;
+ for_all(m)
+ {
+ if (input.has(m))
+ {
+ r += input(m).red();
+ g += input(m).green();
+ b += input(m).blue();
+ ++nb;
+ }
+ }
+ output(p) = value::rgb8(r / nb, g / nb, b / nb);
+ }
+ if (is_cell(p))
+ output(p) = input(p);
+ }
+ return output;
+ }
+
+}
+
+template <typename T>
+mln::image2d<T>
+cells2image(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output((input.nrows() + 1) / 2,
+ (input.ncols() + 1) / 2);
+ for (int row = 0; row < input.nrows(); row += 2)
+ for (int col = 0; col < input.ncols(); col += 2)
+ output.at(row / 2, col / 2) = input.at(row, col);
+ return output;
+}
+
+
+
+
+template <typename I>
+mln_concrete(I)
+do_it(I& input, float lambda, unsigned& nbasins)
+{
+ using namespace mln;
+
+ /**************************/
+ /* Neighborhood defintion */
+ /**************************/
+
+ // e2c
+ bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+ mln_VAR( e2c, make::double_neighb2d(is_row_odd, e2c_h, e2c_v) );
+
+ // e2e
+ bool e2e_h[] = { 0, 0, 1, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 1, 0, 0 };
+ bool e2e_v[] = { 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 1,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0 };
+ mln_VAR( e2e, make::double_neighb2d(is_row_odd, e2e_h, e2e_v) );
+
+ // e2p
+ bool e2p_h[] = { 1, 0, 1,
+ 0, 0, 0,
+ 1, 0, 1 };
+ bool e2p_v[] = { 1, 0, 1,
+ 0, 0, 0,
+ 1, 0, 1 };
+ mln_VAR( e2p, make::double_neighb2d(is_row_odd, e2p_h, e2p_v) );
+
+ /******************/
+ /* Initialisation */
+ /******************/
+
+ I output = mean(image2cells(input), e2c, e2p);
+ io::ppm::save(output, "tmp_input.ppm");
+ //image2d<value::int_u8> ima = convert_to_grey(output);
+ image2d<value::int_u8> imau = convert_to_grey(output);
+ io::pgm::save(imau, "tmp_grey_input.pgm");
+
+ image2d<float> ima(exact(imau).domain());
+
+ // cell
+ mln_VAR(cell, imau | is_cell);
+
+ // edge
+ mln_VAR(edge, extend((imau | is_edge).rw(), pw::value(imau)));
+
+ // FIXME until laplacian is working use gradient / closing_area / wst
+
+ linear::gaussian_2nd_derivative(imau, lambda, ima);
+
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ {
+ if (is_row_odd(p))
+ {
+ mln_value(image2d<float>) t = ima.at(p.row() - 1, p.col());
+ mln_value(image2d<float>) b = ima.at(p.row() + 1, p.col());
+ if ((t > 0 && b < 0) || (t < 0 && b > 0))
+ output(p) = value::rgb8(255,0,0);
+ }
+ else
+ {
+ mln_value(image2d<float>) r = ima.at(p.row(), p.col() - 1);
+ mln_value(image2d<float>) d = ima.at(p.row(), p.col() + 1);
+ if ((r > 0 && d < 0) || (r < 0 && d > 0))
+ output(p) = value::rgb8(255,0,0);
+ }
+ }
+
+#if 0
+ level::paste(morpho::gradient(edge, e2c), edge);
+ level::paste(morpho::closing_volume(edge, e2e, lambda), edge);
+ level::fill(edge, morpho::meyer_wst(edge, e2e, nbasins));
+
+ // Fill regions (with colorize) (won't work with laplacian...)
+
+ colorize colors(nbasins);
+
+ image2d<value::rgb8> cells(ima.domain());
+ level::fill(cells, literal::white);
+ level::paste(level::transform(edge, colors), cells);
+ io::ppm::save(display_edge_with_bg(cells, 3, literal::white),
"tmp_edge.ppm");
+
+ // Move the color of an edge which is non black in the cell
+ level::paste(morpho::dilation(cells, c4()), cells);
+#endif
+
+ //cells = convert_to_rgb8(ima);
+
+ return output;
+}
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm
lambda output.ppm" << std::endl;
+ std::cerr << " lambda >= 0" << std::endl;
+ abort();
+}
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ if (argc != 4)
+ usage(argv);
+
+ float lambda = atof(argv[2]);
+ if (lambda < 0)
+ usage(argv);
+
+ image2d<value::rgb8> ima;
+ io::ppm::load(ima, argv[1]);
+
+ unsigned nbasins;
+ image2d<value::rgb8> output = do_it(ima, lambda, nbasins);
+
+ io::ppm::save(display_edge(output, 3), argv[3]);
+}
Index: ballas/color/min_tree_area_filter.cc
--- ballas/color/min_tree_area_filter.cc (revision 0)
+++ ballas/color/min_tree_area_filter.cc (revision 0)
@@ -0,0 +1,505 @@
+# include <mln/core/var.hh>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/core/image/image_if.hh>
+# include <mln/core/image/extended.hh>
+# include <mln/core/routine/extend.hh>
+
+# include <mln/core/alias/window2d.hh>
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/make/double_neighb2d.hh>
+# include <mln/core/site_set/p_centered.hh>
+
+# include <mln/literal/origin.hh>
+# include <mln/literal/black.hh>
+# include <mln/literal/white.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/value/int_u16.hh>
+# include <mln/io/pgm/load.hh>
+# include <mln/io/pgm/save.hh>
+
+# include <mln/value/rgb8.hh>
+# include <mln/io/ppm/load.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <mln/accu/min_max.hh>
+
+# include <mln/fun/i2v/array.hh>
+# include <mln/fun/p2v/iota.hh>
+
+# include <mln/level/paste.hh>
+# include <mln/level/fill.hh>
+# include <mln/level/transform.hh>
+# include <mln/extension/fill.hh>
+
+# include <mln/morpho/meyer_wst.hh>
+# include <mln/morpho/closing_area.hh>
+
+# include <mln/debug/println.hh>
+
+# include "src/distance.hh"
+
+namespace mln
+{
+ template <typename I, typename N, typename Ic, typename Nc>
+ struct min_tree_
+ {
+ typedef mln_site(I) point;
+ typedef p_array<point> S;
+
+ // in:
+ const I& f;
+ const N& nbh;
+ const Ic& ref;
+ const Nc& nbhc;
+
+ // aux:
+ S s;
+ mln_ch_value(I, bool) deja_vu;
+ mln_ch_value(I, point) parent;
+ mln_ch_value(I, bool) resp;
+ mln_ch_value(I, point) zpar;
+
+ // attached data:
+ int lambda;
+ mln_ch_value(I, int) area;
+ //mln_ch_value(Ic, value::rgb8) values;
+ //initialize(values, ref);
+ //mln_ch_value(I, int) comp;
+
+ min_tree_(const I& f, const N& nbh, const Ic& ref, const Nc& nbhc,
+ int lambda)
+ : f(f),
+ nbh(nbh),
+ ref(ref),
+ nbhc(nbhc),
+ lambda(lambda)
+ {
+ run();
+ }
+
+ void run()
+ {
+ // init
+ {
+ initialize(deja_vu, f);
+ initialize(parent, f);
+ initialize(resp, f);
+ initialize(zpar, f);
+ initialize(area, f);
+ //initialize(comp, f);
+
+ mln::level::fill(deja_vu, false);
+ //mln::level::fill(resp, false);
+ mln::level::fill(area, 0);
+
+ s = level::sort_psites_increasing(f);
+ }
+
+ // first pass
+ {
+ mln_fwd_piter(S) p(s);
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ make_set(p);
+ for_all(n)
+ if (f.has(n) && deja_vu(n))
+ do_union(n, p);
+ deja_vu(p) = true;
+ }
+ }
+
+ // second pass: canonization
+ {
+ mln_bkd_piter(S) p(s);
+ for_all(p)
+ {
+ point q = parent(p);
+ if (f(parent(q)) == f(q))
+ {
+ parent(p) = parent(q);
+ resp(q) = false;
+ }
+ }
+ }
+
+ // third pass: Merging region with area < lambda
+ {
+ mln_fwd_piter(S) p(s);
+ for_all(p)
+ {
+ if (resp(p) && area(p) < lambda)
+ {
+ resp(p) = false;
+ update_data(parent(p), area(p));
+ }
+ }
+ }
+ } // end of run()
+
+ void make_set(const point& p)
+ {
+ parent(p) = p;
+ zpar(p) = p;
+ init_data(p);
+ }
+
+ void set_parent(const point& r, const point& p)
+ {
+ parent(r) = p;
+ merge_data(r, p);
+ }
+
+ bool is_root(const point& p) const
+ {
+ return parent(p) == p;
+ }
+
+ bool is_node(const point& p) const
+ {
+ //return is_root(p) || f(parent(p)) != f(p);
+ return (is_root(p) || resp(p));
+ }
+
+ point find_root(const point& x)
+ {
+ if (zpar(x) == x)
+ return x;
+ else
+ return zpar(x) = find_root(zpar(x));
+ }
+
+ point find_representative(const point& x)
+ {
+ if (parent(x) == x || resp(x))
+ return x;
+ else
+ return find_representative(parent(x));
+ }
+
+ void do_union(const point& n, const point& p)
+ {
+ point r = find_root(n);
+ if (r != p)
+ {
+ set_parent(r, p);
+ zpar(r) = p;
+ }
+ }
+
+ void init_data(const point& p)
+ {
+ area(p) = 1;
+ resp(p) = true;
+ }
+
+ void merge_data(const point& r, const point& p)
+ {
+ if (f(p) == f(r))
+ {
+ resp(p) = false;
+ area(p) += area(r);
+ }
+ }
+
+ void update_data(const point& p, int val)
+ {
+ area(p) += val;
+ if (parent(p) != p && !resp(p))
+ update_data(parent(p), val);
+ }
+
+ };
+}
+
+namespace mln
+{
+ image2d<value::int_u16> convert_to_grey(const image2d<value::rgb8>&
data)
+ {
+ image2d<value::int_u16> output(data.domain());
+ mln_piter_(image2d<value::int_u16>) p(output.domain());
+ for_all(p)
+ output(p) = (int) (data(p).red() * 0.3 + data(p).green() * 0.58 + data(p).blue()) *
0.12;
+ return output;
+ }
+} // end of mln
+
+namespace mln
+{
+
+ struct colorize : Function_v2v< colorize >
+ {
+ typedef value::rgb8 result;
+ colorize(unsigned max)
+ : lut(max + 1)
+ {
+ lut[0] = literal::black;
+ for (unsigned i = 1; i <= max; ++i)
+ lut[i] = result(100 + std::rand() % 150,
+ 100 + std::rand() % 150,
+ 100 + std::rand() % 150);
+ }
+ result operator()(unsigned i) const
+ {
+ return lut[i];
+ }
+ std::vector<result> lut;
+ };
+
+ template <typename I>
+ I display_edge(const I& ima, mln_value(I) bg, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+ level::fill(output, bg);
+
+ mln_VAR(edge, ima | is_edge);
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+ template <typename I>
+ I display_edge(const I& ima, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+
+ mln_VAR( cell, ima | is_cell );
+ mln_piter(cell_t) q(cell.domain());
+ for_all(q)
+ {
+ unsigned row = (q.row() / 2) * (zoom + 1);
+ unsigned col = (q.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ for (unsigned j = 0; j < zoom; ++j)
+ output.at(row + i, col + j) = ima(q);
+ }
+
+ mln_VAR( edge, ima | is_edge );
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+
+ namespace morpho
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ dilation(const I& input, const N& nbh)
+ {
+ typedef mln_value(I) V;
+
+ mln_concrete(I) output;
+ initialize(output, input);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ for_all(n)
+ if (input.has(n) && input(n) != value::rgb8(0,0,0))
+ output(p) = input(n);
+ }
+ return output;
+ }
+ } // mln::morpho
+
+
+} // mln
+
+
+
+template <typename T>
+mln::image2d<T>
+image2cells(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output(2 * input.nrows() - 1,
+ 2 * input.ncols() - 1);
+ for (unsigned row = 0; row < input.nrows(); ++row)
+ for (unsigned col = 0; col < input.ncols(); ++col)
+ output.at(2 * row, 2 * col) = input.at(row, col);
+ return output;
+}
+
+
+template <typename T>
+mln::image2d<T>
+cells2image(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output((input.nrows() + 1) / 2,
+ (input.ncols() + 1) / 2);
+ for (unsigned row = 0; row < input.nrows(); row += 2)
+ for (unsigned col = 0; col < input.ncols(); col += 2)
+ output.at(row / 2, col / 2) = input.at(row, col);
+ return output;
+}
+
+
+template <typename I, typename N, typename Ic, typename Nc>
+unsigned min_tree(const I& f, const N& nbh, const Ic& ref, const Nc&
nbhc,
+ int lambda)
+{
+ using namespace mln;
+
+ min_tree_<I,N,Ic,Nc> run(f, nbh, ref, nbhc, lambda);
+
+
+ mln_piter(I) p(f.domain());
+ unsigned nnodes = 0;
+ for_all(p)
+ {
+ if (run.is_node(p))
+ {
+ std::cout << "nodes: " << p << std::endl;
+ ++nnodes;
+ }
+ }
+
+ colorize colors(nnodes);
+ image2d<value::rgb8> tmp(ref.domain());
+ level::fill(tmp, ref);
+
+ mln_piter(I) q(f.domain());
+ unsigned int i = 0;
+ for_all(q)
+ {
+ if (run.is_node(q))
+ {
+ tmp(q) = colors(i);
+ i++;
+ }
+ }
+ mln_piter(I) r(f.domain());
+ for_all(r)
+ {
+ if (!run.is_node(r))
+ {
+ tmp(r) = tmp(run.find_representative(r));
+ }
+ }
+
+
+ image2d<value::rgb8> to_display(tmp.domain());
+
+ level::fill(to_display, value::rgb8(255, 255, 255));
+ level::paste((tmp | is_edge), to_display);
+ level::paste(morpho::dilation(to_display, c4()), to_display);
+
+ io::ppm::save(display_edge(tmp, literal::white, 3),
+ "edge.ppm");
+ io::ppm::save(tmp, "full.ppm");
+ io::ppm::save(cells2image(to_display), "colorize.ppm");
+
+ return nnodes;
+}
+
+
+template <typename I>
+I
+do_it(I& input, int lambda, unsigned& nbasins)
+{
+ using namespace mln;
+
+ /// Graph creation
+ I graph;
+ create_graph(input, graph, value::rgb8(0, 0, 0));
+
+ // Initialization
+ image2d<value::int_u16> ima = convert_to_grey(graph);
+
+ // Neigbhorhood
+ // e2c
+ bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+
+ mln_VAR(e2c, make::double_neighb2d(is_row_odd, e2c_h, e2c_v));
+
+ bool e2e_h[] = { 0, 0, 1, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 1, 0, 0 };
+
+ bool e2e_v[] = { 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 1,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0 };
+ mln_VAR(e2e, make::double_neighb2d(is_row_odd, e2e_h, e2e_v));
+
+ // Algorithm
+ distance(extend((graph | is_edge).rw(), pw::value(graph)), e2c, ima);
+ io::pgm::save(ima, "edge.pgm");
+
+ nbasins = min_tree((ima | is_edge), e2e, graph, e2c, lambda);
+
+ return graph;
+}
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm
lambda" << std::endl;
+ std::cerr << " lambda >= 0" << std::endl;
+ abort();
+}
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ if (argc != 3)
+ usage(argv);
+
+ int lambda = atoi(argv[2]);
+ if (lambda < 0)
+ usage(argv);
+
+ image2d<value::rgb8> ima;
+ io::ppm::load(ima, argv[1]);
+
+ unsigned nbasins;
+ image2d<value::rgb8> output = do_it(ima, lambda, nbasins);
+
+ //io::ppm::save(output, argv[3]);
+}
Index: ballas/color/min_tree_color.cc
--- ballas/color/min_tree_color.cc (revision 0)
+++ ballas/color/min_tree_color.cc (revision 0)
@@ -0,0 +1,525 @@
+# include <mln/core/var.hh>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/core/image/image_if.hh>
+# include <mln/core/image/extended.hh>
+# include <mln/core/routine/extend.hh>
+
+# include <mln/core/alias/window2d.hh>
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/make/double_neighb2d.hh>
+# include <mln/core/site_set/p_centered.hh>
+
+# include <mln/literal/origin.hh>
+# include <mln/literal/black.hh>
+# include <mln/literal/white.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/value/int_u16.hh>
+# include <mln/io/pgm/load.hh>
+# include <mln/io/pgm/save.hh>
+
+# include <mln/value/rgb8.hh>
+# include <mln/io/ppm/load.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <mln/accu/min_max.hh>
+
+# include <mln/fun/i2v/array.hh>
+# include <mln/fun/p2v/iota.hh>
+
+# include <mln/level/paste.hh>
+# include <mln/level/fill.hh>
+# include <mln/level/transform.hh>
+# include <mln/extension/fill.hh>
+
+# include <mln/morpho/closing_area.hh>
+
+
+# include <mln/debug/println.hh>
+
+# include "src/distance.hh"
+
+namespace mln
+{
+ template <typename I, typename N, typename Ic, typename Nc>
+ struct min_tree_
+ {
+ typedef mln_site(I) point;
+ typedef p_array<point> S;
+
+ // in:
+ const I& f;
+ const N& nbh;
+ const Ic& ref;
+ const Nc& nbhc;
+
+ // aux:
+ S s;
+ mln_ch_value(I, bool) deja_vu;
+ mln_ch_value(I, point) parent;
+ mln_ch_value(I, bool) resp;
+ mln_ch_value(I, point) zpar;
+
+ // attached data:
+ unsigned lambda;
+ mln_ch_value(I, value::rgb8) color;
+ //mln_ch_value(Ic, value::rgb8) values;
+ //initialize(values, ref);
+ //mln_ch_value(I, int) comp;
+
+ min_tree_(const I& f, const N& nbh, const Ic& ref, const Nc& nbhc,
+ int lambda)
+ : f(f),
+ nbh(nbh),
+ ref(ref),
+ nbhc(nbhc),
+ lambda(lambda)
+ {
+ run();
+ }
+
+ void run()
+ {
+ // init
+ {
+ initialize(deja_vu, f);
+ initialize(parent, f);
+ initialize(resp, f);
+ initialize(zpar, f);
+ initialize(color, f);
+ //initialize(comp, f);
+
+ mln::level::fill(deja_vu, false);
+ //mln::level::fill(resp, false);
+ mln::level::fill(color, value::rgb8(0, 0, 0));
+
+ s = level::sort_psites_increasing(f);
+ }
+
+ // first pass
+ {
+ mln_fwd_piter(S) p(s);
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ make_set(p);
+ for_all(n)
+ if (f.has(n) && deja_vu(n))
+ do_union(n, p);
+ deja_vu(p) = true;
+ }
+ }
+
+ // second pass: canonization
+ {
+ mln_bkd_piter(S) p(s);
+ for_all(p)
+ {
+ point q = parent(p);
+ if (f(parent(q)) == f(q))
+ {
+ parent(p) = parent(q);
+ resp(q) = false;
+ }
+ }
+ }
+
+ // third pass: Merging region with distance(color) < lambda
+ {
+ mln_fwd_piter(S) p(s);
+ for_all(p)
+ {
+ point q = parent(p);
+ if (resp(p) && distance(color(p), color(q)) < lambda)
+ {
+ resp(p) = false;
+ update_data(q, color(p));
+ }
+ }
+ }
+
+ } // end of run()
+
+ void make_set(const point& p)
+ {
+ parent(p) = p;
+ zpar(p) = p;
+ init_data(p);
+ }
+
+ void set_parent(const point& r, const point& p)
+ {
+ parent(r) = p;
+ merge_data(r, p);
+ }
+
+ bool is_root(const point& p) const
+ {
+ return parent(p) == p;
+ }
+
+ bool is_node(const point& p) const
+ {
+ //return is_root(p) || f(parent(p)) != f(p);
+ return (is_root(p) || resp(p));
+ }
+
+ point find_root(const point& x)
+ {
+ if (zpar(x) == x)
+ return x;
+ else
+ return zpar(x) = find_root(zpar(x));
+ }
+
+ point find_representative(const point& x)
+ {
+ if (parent(x) == x || resp(x))
+ return x;
+ else
+ return find_representative(parent(x));
+ }
+
+ void do_union(const point& n, const point& p)
+ {
+ point r = find_root(n);
+ if (r != p)
+ {
+ set_parent(r, p);
+ zpar(r) = p;
+ }
+ }
+
+ void init_data(const point& p)
+ {
+ int red =0, green = 0, blue = 0;
+
+ mln_niter(Nc) n(nbhc, p);
+ for_all(n)
+ {
+ red += ref(n).red();
+ green += ref(n).green();
+ blue += ref(n).blue();
+ }
+
+ red /= 2;
+ green /= 2;
+ blue /= 2;
+
+ color(p).red() = red;
+ color(p).green() = green;
+ color(p).blue() = blue;
+
+ resp(p) = true;
+ }
+
+ void merge_data(const point& r, const point& p)
+ {
+ if (f(p) == f(r))
+ {
+ resp(p) = false;
+ color(r) = (color(r) + color(p)) / 2;
+ }
+ }
+
+ void update_data(const point& p, value::rgb8 val)
+ {
+ color(p) = (color(p) + val) / 2;
+ if (parent(p) != p && !resp(p))
+ update_data(parent(p), color(p));
+ }
+
+ };
+}
+
+namespace mln
+{
+ image2d<value::int_u16> convert_to_grey(const image2d<value::rgb8>&
data)
+ {
+ image2d<value::int_u16> output(data.domain());
+ mln_piter_(image2d<value::int_u16>) p(output.domain());
+ for_all(p)
+ output(p) = (int) (data(p).red() * 0.3 + data(p).green() * 0.58 + data(p).blue()) *
0.12;
+ return output;
+ }
+} // end of mln
+
+namespace mln
+{
+
+ struct colorize : Function_v2v< colorize >
+ {
+ typedef value::rgb8 result;
+ colorize(unsigned max)
+ : lut(max + 1)
+ {
+ lut[0] = literal::black;
+ for (unsigned i = 1; i <= max; ++i)
+ lut[i] = result(100 + std::rand() % 150,
+ 100 + std::rand() % 150,
+ 100 + std::rand() % 150);
+ }
+ result operator()(unsigned i) const
+ {
+ return lut[i];
+ }
+ std::vector<result> lut;
+ };
+
+ template <typename I>
+ I display_edge(const I& ima, mln_value(I) bg, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+ level::fill(output, bg);
+
+ mln_VAR(edge, ima | is_edge);
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+ template <typename I>
+ I display_edge(const I& ima, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+
+ mln_VAR( cell, ima | is_cell );
+ mln_piter(cell_t) q(cell.domain());
+ for_all(q)
+ {
+ unsigned row = (q.row() / 2) * (zoom + 1);
+ unsigned col = (q.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ for (unsigned j = 0; j < zoom; ++j)
+ output.at(row + i, col + j) = ima(q);
+ }
+
+ mln_VAR( edge, ima | is_edge );
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+
+ namespace morpho
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ dilation(const I& input, const N& nbh)
+ {
+ typedef mln_value(I) V;
+
+ mln_concrete(I) output;
+ initialize(output, input);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ for_all(n)
+ if (input.has(n) && input(n) != value::rgb8(0,0,0))
+ output(p) = input(n);
+ }
+ return output;
+ }
+ } // mln::morpho
+
+} // mln
+
+
+
+template <typename T>
+mln::image2d<T>
+image2cells(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output(2 * input.nrows() - 1,
+ 2 * input.ncols() - 1);
+ for (unsigned row = 0; row < input.nrows(); ++row)
+ for (unsigned col = 0; col < input.ncols(); ++col)
+ output.at(2 * row, 2 * col) = input.at(row, col);
+ return output;
+}
+
+
+template <typename T>
+mln::image2d<T>
+cells2image(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output((input.nrows() + 1) / 2,
+ (input.ncols() + 1) / 2);
+ for (unsigned row = 0; row < input.nrows(); row += 2)
+ for (unsigned col = 0; col < input.ncols(); col += 2)
+ output.at(row / 2, col / 2) = input.at(row, col);
+ return output;
+}
+
+
+template <typename I, typename N, typename Ic, typename Nc>
+unsigned min_tree(const I& f, const N& nbh, const Ic& ref, const Nc&
nbhc,
+ int lambda)
+{
+ using namespace mln;
+
+ min_tree_<I,N,Ic,Nc> run(f, nbh, ref, nbhc, lambda);
+
+
+ mln_piter(I) p(f.domain());
+ unsigned nnodes = 0;
+ for_all(p)
+ {
+ if (run.is_node(p))
+ ++nnodes;
+ }
+
+#if 1
+ colorize colors(nnodes);
+ image2d<value::rgb8> tmp(ref.domain());
+ level::fill(tmp, ref);
+
+
+ mln_piter(I) q(f.domain());
+ unsigned int i = 0;
+ for_all(q)
+ {
+ if (run.is_node(q))
+ {
+ tmp(q) = colors(i);
+ i++;
+ }
+ }
+ mln_piter(I) r(f.domain());
+ for_all(r)
+ {
+ if (!run.is_node(r))
+ tmp(r) = tmp(run.find_representative(r));
+ }
+
+ image2d<value::rgb8> to_display(tmp.domain());
+
+ level::fill(to_display, value::rgb8(255, 255, 255));
+ level::paste((tmp | is_edge), to_display);
+ level::paste(morpho::dilation(to_display, c4()), to_display);
+
+ io::ppm::save(display_edge(tmp, literal::white, 3),
+ "edge.ppm");
+ io::ppm::save(tmp, "full.ppm");
+ io::ppm::save(cells2image(to_display), "colorize.ppm");
+#endif
+
+#if 1
+ // io::ppm::save(display_edge(run.values, literal::white, 3),
"tmp_tree_colored.pgm");
+#endif
+
+ return nnodes;
+}
+
+
+template <typename I>
+I
+do_it(I& input, int lambda, unsigned& nbasins)
+{
+ using namespace mln;
+
+ /// Graph creation
+ I graph;
+ create_graph(input, graph, value::rgb8(0, 0, 0));
+
+ // Initialization
+ image2d<value::int_u16> ima = convert_to_grey(graph);
+
+ // Neigbhorhood
+ // e2c
+ bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+
+ mln_VAR(e2c, make::double_neighb2d(is_row_odd, e2c_h, e2c_v));
+
+ bool e2e_h[] = { 0, 0, 1, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 1, 0, 0 };
+
+ bool e2e_v[] = { 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 1,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0 };
+ mln_VAR(e2e, make::double_neighb2d(is_row_odd, e2e_h, e2e_v));
+
+ // Algorithm
+ distance(extend((graph | is_edge).rw(), pw::value(graph)), e2c, ima);
+
+ io::pgm::save(ima, "edge.pgm");
+
+ nbasins = min_tree((ima | is_edge), e2e, graph, e2c, lambda);
+
+ return graph;
+}
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm
lambda" << std::endl;
+ std::cerr << " lambda >= 0" << std::endl;
+ abort();
+}
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ if (argc != 3)
+ usage(argv);
+
+ int lambda = atoi(argv[2]);
+ if (lambda < 0)
+ usage(argv);
+
+ image2d<value::rgb8> ima;
+ io::ppm::load(ima, argv[1]);
+
+ unsigned nbasins;
+ image2d<value::rgb8> output = do_it(ima, lambda, nbasins);
+
+ //io::ppm::save(output, argv[3]);
+}
Index: ballas/color/reference2.cc
--- ballas/color/reference2.cc (revision 0)
+++ ballas/color/reference2.cc (revision 0)
@@ -0,0 +1,406 @@
+# include <mln/core/var.hh>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/core/image/image_if.hh>
+# include <mln/core/image/extended.hh>
+# include <mln/core/routine/extend.hh>
+
+# include <mln/core/alias/window2d.hh>
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/make/double_neighb2d.hh>
+# include <mln/core/site_set/p_centered.hh>
+
+# include <mln/literal/origin.hh>
+# include <mln/literal/black.hh>
+# include <mln/literal/white.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/io/pgm/load.hh>
+# include <mln/io/pgm/save.hh>
+
+# include <mln/value/rgb8.hh>
+# include <mln/io/ppm/load.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <mln/accu/min_max.hh>
+# include <mln/accu/mean.hh>
+
+# include <mln/fun/i2v/array.hh>
+# include <mln/fun/p2v/iota.hh>
+
+# include <mln/level/paste.hh>
+# include <mln/level/fill.hh>
+# include <mln/level/transform.hh>
+# include <mln/extension/fill.hh>
+
+# include <mln/morpho/meyer_wst.hh>
+# include <mln/morpho/closing_volume.hh>
+
+# include <mln/linear/convolve.hh>
+# include <mln/make/w_window2d.hh>
+
+# include <mln/debug/println.hh>
+
+namespace mln
+{
+ namespace morpho
+ {
+ template <typename I, typename N>
+ mln_concrete(I)
+ closing_volume(const I& input, const Neighborhood<N>& nbh, std::size_t
lambda)
+ {
+ mln_concrete(I) output;
+ initialize(output, input);
+ closing_volume(input, nbh, lambda, output);
+ return output;
+ }
+ }
+}
+
+namespace mln
+{
+ template <typename I, typename O>
+ void
+ LoG_17x17(const I& input, const O& output)
+ {
+ mln_precondition(exact(output).domain() == exact(input).domain());
+ int ws[] = { +0, 0, 0, 0, 0, 0,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 0,
+ +0, 0, 0, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 0, 0, 0,
+ +0, 0,-1,-1,-1,-2,-3,-3,-3,-3,-3,-2,-1,-1,-1, 0, 0,
+ +0, 0,-1,-1,-2,-3,-3,-3,-3,-3,-3,-3,-2,-1,-1, 0, 0,
+ +0,-1,-1,-2,-3,-3,-3,-2,-3,-2,-3,-3,-3,-2,-1,-1, 0,
+ +0,-1,-2,-3,-3,-3, 0, 2, 4, 2, 0,-3,-3,-3,-2,-1, 0,
+ -1,-1,-3,-3,-3, 0, 4,10,12,10, 4, 0,-3,-3,-3,-1,-1,
+ -1,-1,-3,-3,-2, 2,10,18,21,18,10, 2,-2,-3,-3,-1,-1,
+ -1,-1,-3,-3,-3, 4,12,21,24,21,12, 4,-3,-3,-3,-1,-1,
+ -1,-1,-3,-3,-2, 2,10,18,21,18,10, 2,-2,-3,-3,-1,-1,
+ -1,-1,-3,-3,-3, 0, 4,10,12,10, 4, 0,-3,-3,-3,-1,-1,
+ +0,-1,-2,-3,-3,-3, 0, 2, 4, 2, 0,-3,-3,-3,-2,-1, 0,
+ +0,-1,-1,-2,-3,-3,-3,-2,-3,-2,-3,-3,-3,-2,-1,-1, 0,
+ +0, 0,-1,-1,-2,-3,-3,-3,-3,-3,-3,-3,-2,-1,-1, 0, 0,
+ +0, 0,-1,-1,-1,-2,-3,-3,-3,-3,-3,-2,-1,-1,-1, 0, 0,
+ +0, 0, 0, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 0, 0, 0,
+ +0, 0, 0, 0, 0, 0,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 0 };
+ linear::convolve(input, make::w_window2d(ws), output);
+ }
+} // !mln
+
+namespace mln
+{
+ image2d<value::int_u8> convert_to_grey(const image2d<value::rgb8>&
data)
+ {
+ image2d<value::int_u8> output(data.domain());
+ mln_piter_(image2d<value::int_u8>) p(output.domain());
+ for_all(p)
+ output(p) = (int) (data(p).red() * 0.3 + data(p).green() * 0.58 + data(p).blue()) *
0.12;
+ return output;
+ }
+} // !mln
+
+// Functions
+
+inline
+bool is_row_odd(const mln::point2d& p)
+{
+ return p.row() % 2;
+}
+
+inline
+bool is_cell(const mln::point2d& p)
+{
+ return p.row() % 2 == 0 && p.col() % 2 == 0;
+}
+
+inline
+bool is_edge(const mln::point2d& p)
+{
+ return p.row() % 2 + p.col() % 2 == 1;
+}
+
+inline
+bool is_point(const mln::point2d& p)
+{
+ return p.row() % 2 && p.col() % 2;
+}
+
+inline
+bool is_not_edge(const mln::point2d& p)
+{
+ return ! is_edge(p);
+}
+
+
+
+namespace mln
+{
+
+ namespace border
+ {
+
+ template <typename I>
+ void
+ fill(I& ima, const mln_value(I)& v)
+ {
+ const int nrows = ima.nrows();
+ const int ncols = ima.ncols();
+ for (int r = -1; r <= nrows; ++r)
+ {
+ ima.at(r, -1) = v;
+ ima.at(r, ncols) = v;
+ }
+ for (int c = -1; c <= ncols; ++c)
+ {
+ ima.at(-1, c) = v;
+ ima.at(nrows, c) = v;
+ }
+ }
+
+ } // mln::border
+
+ namespace accu
+ {
+
+ template <typename I, typename L, typename A, typename V>
+ inline
+ void
+ compute(const Image<I>& input_,
+ const Image<L>& label_,
+ const Accumulator<A>&,
+ V& v)
+ {
+ trace::entering("accu::compute");
+
+ const I& input = exact(input_);
+ const L& label = exact(label_);
+
+ const unsigned n = v.size();
+ std::vector<A> a(n);
+
+ mln_piter(I) p(input.domain());
+ for_all(p)
+ a[label(p)].take(input(p));
+
+ for (unsigned l = 1; l < n; ++l)
+ v(l) = a[l].to_result();
+
+ trace::exiting("accu::compute");
+ }
+
+ } // mln::accu
+
+ namespace morpho
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ gradient(const I& input, const N& nbh)
+ {
+ mln_concrete(I) output;
+ initialize(output, input);
+ accu::min_max<mln_value(I)> mm;
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ mm.init();
+ for_all(n) if (input.has(n))
+ mm.take(input(n));
+ output(p) = mm.second() - mm.first();
+ }
+ return output;
+ }
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ dilation(const I& input, const N& nbh)
+ {
+ typedef mln_value(I) V;
+
+ mln_concrete(I) output;
+ initialize(output, input);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ for_all(n)
+ if (input.has(n) && input(n) != value::rgb8(0,0,0))
+ output(p) = input(n);
+ }
+ return output;
+ }
+ } // mln::morpho
+
+
+ struct colorize : Function_v2v< colorize >
+ {
+ typedef value::rgb8 result;
+ colorize(unsigned max)
+ : lut(max + 1)
+ {
+ lut[0] = literal::black;
+ for (unsigned i = 1; i <= max; ++i)
+ lut[i] = result(100 + std::rand() % 150,
+ 100 + std::rand() % 150,
+ 100 + std::rand() % 150);
+ }
+ result operator()(unsigned i) const
+ {
+ return lut[i];
+ }
+ std::vector<result> lut;
+ };
+
+
+ template <typename I>
+ I display_edge(const I& ima, mln_value(I) bg, unsigned zoom)
+ {
+ unsigned nrows = ima.nrows() / 2 + 1;
+ unsigned ncols = ima.ncols() / 2 + 1;
+ I output(nrows * (zoom + 1) - 1,
+ ncols * (zoom + 1) - 1);
+ level::fill(output, bg);
+ mln_VAR( edge, ima | is_edge );
+ mln_piter(edge_t) p(edge.domain());
+ for_all(p)
+ if (p.row() % 2) // horizontal edge
+ {
+ unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1;
+ unsigned col = (p.col() / 2) * (zoom + 1);
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row, col + i) = ima(p);
+ }
+ else // vertical edge
+ {
+ unsigned row = (p.row() / 2) * (zoom + 1);
+ unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1;
+ for (unsigned i = 0; i < zoom; ++i)
+ output.at(row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+} // mln
+
+
+
+template <typename T>
+mln::image2d<T>
+image2cells(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output(2 * input.nrows() - 1,
+ 2 * input.ncols() - 1);
+ for (int row = 0; row < input.nrows(); ++row)
+ for (int col = 0; col < input.ncols(); ++col)
+ output.at(2 * row, 2 * col) = input.at(row, col);
+ return output;
+}
+
+
+template <typename T>
+mln::image2d<T>
+cells2image(const mln::image2d<T>& input)
+{
+ mln::image2d<T> output((input.nrows() + 1) / 2,
+ (input.ncols() + 1) / 2);
+ for (int row = 0; row < input.nrows(); row += 2)
+ for (int col = 0; col < input.ncols(); col += 2)
+ output.at(row / 2, col / 2) = input.at(row, col);
+ return output;
+}
+
+
+
+
+template <typename I>
+mln_concrete(I)
+do_it(I& input, int lambda, unsigned& nbasins)
+{
+ using namespace mln;
+
+ /******************/
+ /* Initialisation */
+ /******************/
+
+ image2d<value::int_u8> ima = image2cells(convert_to_grey(input));
+
+ /**************************/
+ /* Neighborhood defintion */
+ /**************************/
+
+ // e2c
+ bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+ mln_VAR( e2c, make::double_neighb2d(is_row_odd, e2c_h, e2c_v) );
+
+ // e2e
+ bool e2e_h[] = { 0, 0, 1, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 0, 0, 1, 0, 0 };
+ bool e2e_v[] = { 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 1,
+ 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0 };
+ mln_VAR( e2e, make::double_neighb2d(is_row_odd, e2e_h, e2e_v) );
+
+ // cell
+ mln_VAR(cell, ima | is_cell);
+
+ // edge
+ mln_VAR(edge, extend((ima | is_edge).rw(), pw::value(ima)));
+
+ // FIXME until laplacian is working use gradient / closing_area / wst
+
+ level::paste(morpho::gradient(edge, e2c), edge);
+ level::paste(morpho::closing_volume(edge, e2e, lambda), edge);
+ level::fill(edge, morpho::meyer_wst(edge, e2e, nbasins));
+
+ // Fill regions (with colorize) (won't work with laplacian...)
+
+ colorize colors(nbasins);
+
+ image2d<value::rgb8> cells(ima.domain());
+ level::fill(cells, literal::white);
+ level::paste(level::transform(edge, colors), cells);
+ io::ppm::save(display_edge(cells, literal::white, 3), "tmp_edge.ppm");
+
+ // Move the color of an edge which is non black in the cell
+ level::paste(morpho::dilation(cells, c4()), cells);
+
+ return cells2image(cells);
+}
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm
lambda output.ppm" << std::endl;
+ std::cerr << " lambda >= 0" << std::endl;
+ abort();
+}
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ if (argc != 4)
+ usage(argv);
+
+ int lambda = atoi(argv[2]);
+ if (lambda < 0)
+ usage(argv);
+
+ image2d<value::rgb8> ima;
+ io::ppm::load(ima, argv[1]);
+
+ unsigned nbasins;
+ image2d<value::rgb8> output = do_it(ima, lambda, nbasins);
+
+ io::ppm::save(output, argv[3]);
+}
Index: ballas/color/src/graph.hh
--- ballas/color/src/graph.hh (revision 0)
+++ ballas/color/src/graph.hh (revision 0)
@@ -0,0 +1,61 @@
+/*! \file src/graph.hh
+ *
+ */
+
+#ifndef SRC_GRAPH_HH
+# define SRC_GRAPH_HH
+
+# include <mln/value/int_u8.hh>
+# include <mln/value/int_u16.hh>
+# include <mln/value/rgb8.hh>
+
+# include <mln/level/fill.hh>
+
+# include <mln/core/image/image2d.hh>
+
+
+// Neighborhood functions
+inline
+bool is_row_odd(const mln::point2d& p)
+{
+ return p.row() % 2;
+}
+inline
+bool is_cell(const mln::point2d& p)
+{
+ return p.row() % 2 == 0 && p.col() % 2 == 0;
+}
+inline
+bool is_edge(const mln::point2d& p)
+{
+ return (p.row() % 2 + p.col() % 2) == 1;
+}
+inline
+bool is_point(const mln::point2d& p)
+{
+ return p.row() % 2 && p.col() % 2;
+}
+inline
+bool is_not_edge(const mln::point2d& p)
+{
+ return ! is_edge(p);
+}
+
+// Graph image creation function
+// FIXME: add exact conversion....
+// FIXME: check that the input image is in 2 dimension
+template <typename I>
+void
+create_graph(const I& ima, I& graph, mln_value(I) val)
+{
+ graph = I(ima.nrows() * 2 -1, ima.ncols() * 2 - 1);
+
+ mln::level::fill(graph, val);
+
+ mln_piter(I) p(ima.domain());
+ for_all(p)
+ graph.at(p.row() * 2, p.col() * 2) = ima(p);
+}
+
+
+#endif // !SRC_GRAPH_HH
Index: ballas/color/src/io.hh
--- ballas/color/src/io.hh (revision 0)
+++ ballas/color/src/io.hh (revision 0)
@@ -0,0 +1,57 @@
+/*! \file src/io.hh
+ *
+ * Contains various method to load/save an image
+ */
+
+#ifndef SRC_IO_HH
+# define SRC_IO_HH
+
+#include <mln/core/image/image2d.hh>
+
+#include <mln/value/int_u16.hh>
+#include <mln/value/int_s16.hh>
+
+# include <mln/io/pgm/load.hh>
+# include <mln/io/pgm/save.hh>
+# include <mln/io/ppm/load.hh>
+# include <mln/io/ppm/save.hh>
+
+namespace IO
+{
+
+ template <typename I>
+ void load(I& ima, const std::string& file)
+ {
+ mln::io::ppm::load(ima, file);
+ }
+
+ template <>
+ void load(mln::image2d<mln::value::int_u16>& ima,
+ const std::string& file)
+ {
+ mln::io::pgm::load(ima, file);
+ }
+
+ template <typename I>
+ void save(I& ima, const std::string& file)
+ {
+ mln::io::ppm::save(ima, file);
+ }
+
+ template <>
+ void save(mln::image2d<mln::value::int_u16>& ima,
+ const std::string& file)
+ {
+ mln::io::pgm::save(ima, file);
+ }
+
+ template <>
+ void save(mln::image2d<mln::value::int_s16>& ima,
+ const std::string& file)
+ {
+ mln::io::pgm::save(ima, file);
+ }
+
+} // !IO
+
+#endif // !SRC_IO_HH
Index: ballas/color/src/distance.hh
--- ballas/color/src/distance.hh (revision 0)
+++ ballas/color/src/distance.hh (revision 0)
@@ -0,0 +1,52 @@
+/*! \file src/distance.hh
+ *
+ */
+
+#ifndef SRC_DISTANCE_HH
+# define SRC_DISTANCE_HH
+
+# include "graph.hh"
+
+# include <cmath>
+
+
+/// Manhatan distance
+inline
+unsigned distance(const mln::value::rgb8& lhs,
+ const mln::value::rgb8& rhs)
+{
+ return abs(lhs.red() - rhs.red()) +
+ abs(lhs.green() - rhs.green()) +
+ abs(lhs.blue() - rhs.blue());
+}
+
+
+/// Store the distance between two points on edge
+/// FIXME documentation
+template <typename I, typename N, typename O>
+void distance(const I& rgb_graph,
+ const N& nbh,
+ O& gl_graph)
+{
+ mln_piter(I) p(rgb_graph.domain());
+ mln_niter(N) n(nbh, p);
+
+ for_all(p)
+ {
+ mln::value::rgb8 v1;
+ mln::value::rgb8 v2;
+
+ n.start();
+ assert(n.is_valid() && rgb_graph.has(n));
+ v1 = rgb_graph(n);
+ n.next();
+ assert(n.is_valid() && rgb_graph.has(n));
+ v2 = rgb_graph(n);
+
+ gl_graph(p) = distance(v1, v2);
+ }
+}
+
+
+
+#endif // !SRC_DISTANCE_HH
Index: ballas/color/src/convert.hh
--- ballas/color/src/convert.hh (revision 0)
+++ ballas/color/src/convert.hh (revision 0)
@@ -0,0 +1,37 @@
+/*! \file src/convert.hh
+ *
+ * Method that convert an rgb image 2d into gray level
+ */
+
+#ifndef SRC_CONVERT_HH
+# define SRC_CONVERT_HH
+
+# include <mln/value/int_u8.hh>
+# include <mln/value/int_u16.hh>
+# include <mln/value/int_s16.hh>
+# include <mln/value/rgb8.hh>
+
+# include <mln/core/image/image2d.hh>
+
+// Convert function
+void convert_to_gl(mln::image2d<mln::value::int_u16>& ima,
+ const mln::image2d<mln::value::rgb8>& data)
+{
+ mln_piter_(mln::image2d<mln::value::int_u16>) p(ima.domain());
+ for_all(p)
+ ima(p) = (int) (data(p).red() * 0.3 +
+ data(p).green() * 0.58 +
+ data(p).blue() * 0.12);
+}
+
+void convert_to_gl(mln::image2d<mln::value::int_s16>& ima,
+ const mln::image2d<mln::value::rgb8>& data)
+{
+ mln_piter_(mln::image2d<mln::value::int_s16>) p(ima.domain());
+ for_all(p)
+ ima(p) = (int) (data(p).red() * 0.3 +
+ data(p).green() * 0.58 +
+ data(p).blue() * 0.12);
+}
+
+#endif // !SRC_CONVERT_HH
Index: ballas/color/laplacien.cc
--- ballas/color/laplacien.cc (revision 0)
+++ ballas/color/laplacien.cc (revision 0)
@@ -0,0 +1,129 @@
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/image_if.hh>
+
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/make/double_neighb2d.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/int_u16.hh>
+#include <mln/value/int_s16.hh>
+
+#include <mln/linear/gaussian.hh>
+#include <mln/morpho/erosion.hh>
+
+#include <mln/core/var.hh>
+#include <mln/debug/println.hh>
+
+#include "src/io.hh"
+#include "src/graph.hh"
+#include "src/convert.hh"
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.ppm
output.pgm" << std::endl;
+ abort();
+}
+
+/// FIXME Put these elsewhere
+typedef mln::image2d<mln::value::rgb8> Icolor;
+typedef mln::image2d<mln::value::int_u16> Igray;
+
+void process(Icolor& graph, const Icolor& input)
+{
+ using namespace mln;
+
+ // Neighborhood definition
+
+// bool e2c_h[] = { 0, 1, 0,
+// 0, 0, 0,
+// 0, 1, 0 };
+// bool e2c_v[] = { 0, 0, 0,
+// 1, 0, 1,
+// 0, 0, 0 };
+
+// bool e2e_h[] = { 0, 0, 1, 0, 0,
+// 0, 1, 0, 1, 0,
+// 0, 0, 0, 0, 0,
+// 0, 1, 0, 1, 0,
+// 0, 0, 1, 0, 0 };
+// bool e2e_v[] = { 0, 0, 0, 0, 0,
+// 0, 1, 0, 1, 0,
+// 1, 0, 0, 0, 1,
+// 0, 1, 0, 1, 0,
+// 0, 0, 0, 0, 0 };
+
+// bool e2p_h[] = { 0, 0, 1,
+// 0, 0, 0,
+// 1, 0, 0 };
+// bool e2p_v[] = { 1, 0, 0,
+// 0, 0, 0,
+// 0, 0, 1 };
+ //mln_VAR(e2c, make::double_neighb2d(is_row_odd, e2c_h, e2c_v));
+
+
+ // convert the image into grey level
+ Igray gray_graph(graph.bbox());
+ convert_to_gl(gray_graph, graph);
+
+ Igray gray_input(input.bbox());
+ convert_to_gl(gray_input, input);
+
+
+ //level::paste(morpho::dilation((gray_graph | is_cell), e2c.win()), gray_graph);
+// Igray save(gray_graph.bbox());
+// level::paste(gray_graph | is_cell, save);
+// IO::save(save, "tmp3.ppm");
+
+ // Create the laplacian image
+ //Igray laplacian(gray_input.bbox());
+ image2d<int> laplacian(gray_input.bbox());
+ linear::gaussian(gray_input, 2.0f, laplacian);
+ //IO::save(laplacian, "tmp1.ppm");
+ linear::laplacian(gray_input, 2.0f, laplacian);
+ debug::println(laplacian);
+ //IO::save(laplacian, "tmp2.ppm");
+
+ // Display edge on the output images
+// debug::println(laplacian);
+
+// mln_piter_(Igray) p(laplacian.domain());
+// for_all(p)
+// if (laplacian(p) == 0u)
+// {
+// std::cout << "I'm here" << std::endl;
+// graph.at(p.row() * 2, p.col() * 2) = value::rgb<8>(255, 0, 0);
+// }
+
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ // Initialisation
+ if (argc != 3)
+ usage(argv);
+
+ std::string input_file(argv[1]);
+ std::string output_file(argv[2]);
+
+
+ Icolor input;
+
+ // Load the image
+ IO::load(input, input_file);
+
+ // create a graph image from the input
+ Icolor graph;
+ create_graph(input, graph);
+
+ // Process
+ process(graph, input);
+
+
+ // Save
+ IO::save(graph, output_file);
+}