https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
Index: ChangeLog
from Thierry Geraud <thierry.geraud(a)lrde.epita.fr>
Make a common room for ICDAR.
* icdar: New directory.
* icdar/2009: New directory.
* icdar/2009/hsc: New directory.
* theo/icdar/hsc/main.cc (tmp_clo): New debug image.
Copy to...
* icdar/2009/hsc/main.cc: ...this new file.
(output): Update.
* theo/exec/diff_abs.cc: New.
Propagate Edwin's modifications.
* theo/esiee/laurent/presentation/wst_edge.cc: Update.
* theo/color/change_attributes.hh: Update.
* theo/color/segment.hh: Update.
* theo/color/segment_rgb_pixels.cc: Update.
* theo/color/filter_rgb_pixels.cc: New.
* theo/color/filter_blen_rgb_edges.cc: New.
* lazzara/icdar/quasi.cc: New.
* lazzara/icdar/binarization.cc: Augment.
icdar/2009/hsc/main.cc | 25 -
lazzara/icdar/binarization.cc | 664 +++++++++++++++++++++++++---
lazzara/icdar/quasi.cc | 180 +++++++
theo/color/change_attributes.hh | 10
theo/color/filter_blen_rgb_edges.cc | 387 ++++++++++++++++
theo/color/filter_rgb_pixels.cc | 227 +++++++++
theo/color/segment.hh | 78 ++-
theo/color/segment_rgb_pixels.cc | 28 -
theo/esiee/laurent/presentation/wst_edge.cc | 4
theo/exec/diff_abs.cc | 38 +
theo/icdar/hsc/main.cc | 4
11 files changed, 1552 insertions(+), 93 deletions(-)
Index: icdar/2009/hsc/main.cc
--- icdar/2009/hsc/main.cc (revision 0)
+++ icdar/2009/hsc/main.cc (working copy)
@@ -264,8 +264,11 @@
void usage(char* argv[])
{
- std::cerr << "usage: " << argv[0] << " input.pbm
output.pgm [output.ppm]" << std::endl
- << " ICDAR'2009: HSC." << std::endl;
+ std::cerr << "usage: " << argv[0] << " input.pbm
output.pgm" << std::endl
+ << " HSC @ ICDAR'2009" << std::endl
+ << " input.pbm: input 2D binary image (text is black; background is
white)" << std::endl
+ << " output.dump: output image where line components are labeled
(int_u8)" << std::endl
+ << " 0 is the background label." << std::endl;
std::abort();
}
@@ -277,7 +280,7 @@
using value::int_u8;
using value::rgb8;
- if (argc != 3 && argc != 4)
+ if (argc != 3)
usage(argv);
@@ -331,6 +334,10 @@
clo = morpho::closing::structural(fuzzy, win::rectangle2d(height, width));
+# ifdef LOG
+ io::pgm::save(clo, "tmp_clo.pgm");
+# endif
+
int_u8 n_basins;
ws = morpho::watershed::flooding(clo, c4(), n_basins);
@@ -358,26 +365,14 @@
{
image2d<int_u8> output(input.domain());
- image2d<rgb8>
- cool = debug::colorize(rgb8(), spc, n_basins),
- lab(input.domain());
-
mln_piter_(box2d) p(input.domain());
for_all(p)
if (input(p))
- {
output(p) = 0;
- lab(p) = literal::black;
- }
else
- {
output(p) = spc.at_(p.row() / subsampling_factor, p.col() / subsampling_factor);
- lab(p) = cool.at_(p.row() / subsampling_factor, p.col() / subsampling_factor);
- }
io::pgm::save(output, argv[2]);
- if (argc == 4)
- io::ppm::save(lab, argv[3]);
}
Property changes on: icdar/2009/hsc/main.cc
___________________________________________________________________
Added: svn:mergeinfo
Index: theo/icdar/hsc/main.cc
--- theo/icdar/hsc/main.cc (revision 3606)
+++ theo/icdar/hsc/main.cc (working copy)
@@ -331,6 +331,10 @@
clo = morpho::closing::structural(fuzzy, win::rectangle2d(height, width));
+# ifdef LOG
+ io::pgm::save(clo, "tmp_clo.pgm");
+# endif
+
int_u8 n_basins;
ws = morpho::watershed::flooding(clo, c4(), n_basins);
Index: theo/exec/diff_abs.cc
--- theo/exec/diff_abs.cc (revision 0)
+++ theo/exec/diff_abs.cc (revision 0)
@@ -0,0 +1,38 @@
+#include "filetype.hh"
+
+#include <mln/fun/vv2v/diff_abs.hh>
+#include <mln/level/transform.hh>
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input1.pgm
input2.pgm output.pgm" << std::endl
+ << " Point-wise diff abs." << std::endl;
+ std::abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+
+ if (argc != 4)
+ usage(argv);
+
+
+ trace::entering("main");
+
+ image2d<int_u8> input1, input2;
+ io::pgm::load(input1, argv[1]);
+ io::pgm::load(input2, argv[2]);
+
+ io::pgm::save(level::transform(input1,
+ input2,
+ fun::vv2v::diff_abs<int_u8>()),
+ argv[3]);
+
+ trace::exiting("main");
+}
Index: theo/esiee/laurent/presentation/wst_edge.cc
--- theo/esiee/laurent/presentation/wst_edge.cc (revision 3606)
+++ theo/esiee/laurent/presentation/wst_edge.cc (working copy)
@@ -33,7 +33,7 @@
# include <mln/extension/fill.hh>
# include <mln/morpho/meyer_wst.hh>
-# include <mln/morpho/closing_volume.hh>
+# include <mln/morpho/closing/volume.hh>
# include <mln/opt/at.hh>
@@ -318,7 +318,7 @@
}
- data::paste( morpho::closing_volume(edge, e2e, lambda), edge );
+ data::paste( morpho::closing::volume(edge, e2e, lambda), edge );
image2d<unsigned> label(ima.bbox(), 0);
Index: theo/color/segment.hh
--- theo/color/segment.hh (revision 3606)
+++ theo/color/segment.hh (working copy)
@@ -16,7 +16,7 @@
#include <mln/pw/all.hh>
#include <mln/morpho/tree/data.hh>
-#include <mln/morpho/closing_attribute.hh>
+#include <mln/morpho/closing/leveling.hh>
#include "change_attributes.hh"
@@ -120,7 +120,7 @@
// Propagate attribute from a site to its parent.
{
- mln_fwd_piter(T) p(t.domain());
+ mln_up_site_piter(T) p(t); // WAS: mln_fwd_piter(T) p(t.domain());
for_all(p)
if (! t.is_root(p))
acc(t.parent(p)).take(acc(p));
@@ -131,12 +131,11 @@
// Change accumulator into its result.
{
initialize(attr, acc);
- typedef typename T::nodes_t N;
- mln_piter(N) p(t.nodes());
- for_all(p)
+ mln_node_piter(T) n(t);
+ for_all(n)
{
- mln_invariant(t.is_a_node(p));
- attr(p) = acc(p).to_result();
+ mln_invariant(t.is_a_node(n));
+ attr(n) = acc(n).to_result();
}
}
@@ -213,14 +212,22 @@
data::fill(nchildren, 0);
{
- mln_fwd_piter(T) p(t.domain());
- // Propagate attribute from a site to its parent.
- for_all(p)
- if (t.is_a_non_root_node(p))
- {
- mln_invariant(t.is_a_node(t.parent(p)));
- ++nchildren(t.parent(p)); // so parent(p) is a node
- }
+ mln_up_node_piter(T) n(t);
+ for_all(n)
+ if (! t.is_root(n))
+ {
+ mln_invariant(t.is_a_node(t.parent(n)));
+ ++nchildren(t.parent(n)); // so parent(n) is a node
+ }
+
+ // WAS:
+// mln_fwd_piter(T) p(t.domain());
+// for_all(p)
+// if (t.is_a_non_root_node(p))
+// {
+// mln_invariant(t.is_a_node(t.parent(p)));
+// ++nchildren(t.parent(p)); // so parent(p) is a node
+// }
}
return nchildren;
@@ -325,7 +332,10 @@
mln_concrete(I) g;
{
initialize(g, t.f());
- mln_bkd_piter(T) p(t.domain());
+
+// mln_bkd_piter(T) p(t.domain());
+
+ mln_dn_site_piter(T) p(t);
for_all(p)
if (t.is_a_node(p) && a(p) >= lambda)
g(p) = t.f(p);
@@ -357,7 +367,7 @@
unsigned n_objects, unsigned less,
bool echo = false)
{
- mln_concrete(I) g_ref = morpho::closing_attribute<A>(f, nbh, lambda);
+ mln_concrete(I) g_ref = morpho::closing::leveling(f, nbh, A(), lambda);
unsigned n_regmins_g_ref;
mln_ch_value(I, unsigned) regmin_g_ref = labeling::regional_minima(g_ref, nbh,
n_regmins_g_ref);
@@ -402,6 +412,40 @@
+
+
+
+ template <typename A, typename T, typename N>
+ inline
+ mln_concrete(typename T::function)
+ do_filter(A& a, const T& t, const N& nbh,
+ const mln_value(A)& lambda)
+ {
+ typedef typename T::function I;
+
+ const typename T::parent_t& par = t.parent_image();
+
+ // No need for attributes back propagation since we process the
+ // tree downwards: nodes are set *before* their children nodes and
+ // their related (non-nodes) sites.
+
+ mln_concrete(I) g;
+ initialize(g, t.f());
+
+ mln_dn_site_piter(T) p(t);
+ for_all(p)
+ if (t.is_a_node(p) && a(p) >= lambda)
+ g(p) = t.f(p);
+ else
+ g(p) = g(par(p));
+
+ return g;
+ }
+
+
+
+
+
// Filter facade.
template <typename F, typename N, typename A>
Index: theo/color/segment_rgb_pixels.cc
--- theo/color/segment_rgb_pixels.cc (revision 3606)
+++ theo/color/segment_rgb_pixels.cc (working copy)
@@ -14,7 +14,8 @@
#include <mln/io/pbm/save.hh>
#include <mln/level/convert.hh>
-#include <mln/morpho/meyer_wst.hh>
+#include <mln/morpho/watershed/flooding.hh>
+#include <mln/morpho/attribute/card.hh>
#include "mean_rgb_pix.hh"
#include "segment.hh"
@@ -75,8 +76,7 @@
mln_ch_value(A, value::int_u8) d;
initialize(d, a);
- typedef typename T::nodes_t N;
- mln_fwd_piter(N) n(t.nodes());
+ mln_up_node_piter(T) n(t);
for_all(n)
d(n) = dist(a(n), a(t.parent(n)));
@@ -98,6 +98,7 @@
{
std::cerr << "usage: " << argv[0] << " input.ppm n
echo output.ppm" << std::endl;
std::cerr << "color version on pixels (without edges)" <<
std::endl;
+ std::cerr << "attribute is 'card'" << std::endl;
std::cerr << " n >= 2" << std::endl;
std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl;
abort();
@@ -147,11 +148,22 @@
typedef morpho::tree::data<F,S> tree_t;
tree_t t(f, s, c4());
+
+ morpho::attribute::card<F> a_;
+ mln_VAR(a, compute_attribute_on_nodes(a_, t));
+
+ unsigned lambda;
+ unsigned less;
+ image2d<int_u8> g = run_filter(a, t, c4(), n_objects, // input
+ less, lambda, // output
+ echo);
+
+ /*
+
accu::rgb_image_ = input;
accu::mean_rgb_pix< util::pix<F> > a_;
mln_VAR(a, compute_attribute_on_nodes(a_, t));
-
mln_VAR(d, dists_from_means(t, a, echo));
make_attribute_grow(t, d);
if (echo)
@@ -160,19 +172,21 @@
display_tree_attributes(t, d);
}
-
// BAD: extinct_attributes(t, d, echo);
-
int_u8 lambda;
unsigned less;
image2d<int_u8> g = run_filter(d, t, c4(), n_objects, // input
less, lambda, // output
echo);
+ */
+
+
io::pbm::save( (pw::value(g) != pw::value(f)) | f.domain(),
"temp_activity.pbm" );
+
// if (echo)
// debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) |
f.domain());
@@ -181,7 +195,7 @@
typedef value::label_16 L;
L nbasins;
- mln_ch_value_(I, L) w = morpho::meyer_wst(g, c4(), nbasins);
+ mln_ch_value_(I, L) w = morpho::watershed::flooding(g, c4(), nbasins);
std::cout << "n basins = " << nbasins << std::endl;
image2d<rgb8> output = duplicate(input);
Index: theo/color/filter_rgb_pixels.cc
--- theo/color/filter_rgb_pixels.cc (revision 0)
+++ theo/color/filter_rgb_pixels.cc (revision 0)
@@ -0,0 +1,227 @@
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/image_if.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/value/int_u8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/value/rgb8.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/ppm/load.hh>
+#include <mln/io/ppm/save.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/pbm/save.hh>
+#include <mln/level/convert.hh>
+
+#include <mln/morpho/watershed/flooding.hh>
+
+#include "mean_rgb_pix.hh"
+#include "segment.hh"
+
+
+
+namespace mln
+{
+
+
+ // Distance, stored on pixels, of neighboring colors.
+
+ template <typename N>
+ image2d<value::int_u8>
+ dist_on_pixels(const image2d<value::rgb8>& input, const N& nbh)
+ {
+ using value::int_u8;
+ image2d<int_u8> output(input.domain());
+
+ mln_piter(box2d) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ int_u8 d = 0u;
+ for_all(n) if (input.domain().has(n))
+ {
+ int_u8 d_ = dist(input(p), input(n));
+ if (d_ > d)
+ d = d_;
+ }
+ output(p) = d;
+ }
+
+ io::pgm::save(output, "temp_dist.pgm");
+
+ return output;
+ }
+
+
+
+ // Distance between 2 rgb8 colors.
+ // -----------------------------------------------------------
+
+
+ value::int_u8 dist_mean(const value::rgb8& c1, const value::rgb8& c2)
+ {
+ unsigned d = 0;
+ d += (math::diff_abs(c1.red(), c2.red()) + 2) / 3;
+ d += (math::diff_abs(c1.green(), c2.green()) + 2) / 3;
+ d += (math::diff_abs(c1.blue(), c2.blue()) + 2) / 3;
+ if (d > 255)
+ d = 255;
+ return d;
+ }
+
+ value::int_u8 dist_sat(const value::rgb8& c1, const value::rgb8& c2)
+ {
+ unsigned d = 0;
+ d += math::diff_abs(c1.red(), c2.red());
+ d += math::diff_abs(c1.green(), c2.green());
+ d += math::diff_abs(c1.blue(), c2.blue());
+ if (d > 255)
+ d = 255;
+ return d;
+ }
+
+ value::int_u8 dist_max(const value::rgb8& c1, const value::rgb8& c2)
+ {
+ unsigned d = 0, d_;
+ d_ = math::diff_abs(c1.red(), c2.red());
+ if (d_ > d) d = d_;
+ d_ = math::diff_abs(c1.green(), c2.green());
+ if (d_ > d) d = d_;
+ d_ = math::diff_abs(c1.blue(), c2.blue());
+ if (d_ > d) d = d_;
+ return d;
+ }
+
+
+
+ // From 'mean color' attributes to 'mean difference p/par(p)'.
+ // -----------------------------------------------------------
+
+
+ template <typename T, typename A>
+ inline
+ mln_ch_value(A, value::int_u8)
+ dists_from_means(const T& t, const A& a, bool echo = false)
+ {
+ if (echo)
+ {
+ std::cout << "means:" << std::endl;
+ display_tree_attributes(t, a);
+ }
+
+ mln_ch_value(A, value::int_u8) d;
+ initialize(d, a);
+
+ mln_up_node_piter(T) n(t);
+ for_all(n)
+ d(n) = dist_max(a(n), a(t.parent(n)));
+
+ if (echo)
+ {
+ std::cout << "dists:" << std::endl;
+ display_tree_attributes(t, d);
+ }
+
+ return d;
+ }
+
+
+
+
+
+} // mln
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.ppm
lambda echo output.ppm" << std::endl;
+ std::cerr << "color version on pixels (without edges)" <<
std::endl;
+ std::cerr << "attribute is 'distance of color means'" <<
std::endl;
+ std::cerr << " lambda >= 0 and <= 255" << std::endl;
+ std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl;
+ abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+
+ using value::int_u8;
+ using value::rgb8;
+
+
+ if (argc != 5)
+ usage(argv);
+
+
+ // Color debase (on pixels, not edges) version.
+ // --------------------------------------------
+
+
+ typedef image2d<rgb8> I;
+
+ I input;
+ io::ppm::load(input, argv[1]);
+
+ int lambda = atoi(argv[2]);
+ if (lambda < 0 || lambda > 255)
+ usage(argv);
+
+ bool echo = atoi(argv[3]);
+
+
+ // Neighborhood.
+ mln_VAR(nbh, c4());
+
+
+ // Changing input into 'f'.
+
+ typedef image2d<int_u8> F;
+ F f = dist_on_pixels(input, c4());
+
+
+ typedef p_array<mln_psite_(F)> S;
+ S s = level::sort_psites_decreasing(f);
+
+ typedef morpho::tree::data<F,S> tree_t;
+ tree_t t(f, s, c4());
+
+
+ accu::rgb_image_ = input;
+ accu::mean_rgb_pix< util::pix<F> > a_;
+ mln_VAR(a, compute_attribute_on_nodes(a_, t));
+
+ mln_VAR(d, dists_from_means(t, a, echo));
+ make_attribute_grow(t, d);
+ if (echo)
+ {
+ std::cout << "dists growing:" << std::endl;
+ display_tree_attributes(t, d);
+ }
+
+ image2d<int_u8> g = do_filter(d, t, c4(), lambda);
+
+
+ io::pbm::save( (pw::value(g) != pw::value(f)) | f.domain(),
+ "temp_activity.pbm" );
+
+
+// debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) |
f.domain());
+
+
+ // Watershed transform.
+
+ typedef value::label_16 L;
+ L nbasins;
+ mln_ch_value_(I, L) w = morpho::watershed::flooding(g, c4(), nbasins);
+ std::cout << "n basins = " << nbasins << std::endl;
+
+ image2d<rgb8> output = duplicate(input);
+ data::fill((output | (pw::value(w) == 0)).rw(), literal::black);
+
+ io::ppm::save(output, argv[4]);
+}
Index: theo/color/change_attributes.hh
--- theo/color/change_attributes.hh (revision 3606)
+++ theo/color/change_attributes.hh (working copy)
@@ -16,8 +16,11 @@
initialize(deja_vu, a);
data::fill(deja_vu, false);
- typedef typename T::nodes_t nodes_t;
- mln_fwd_piter(nodes_t) p(t.nodes());
+ // WAS:
+ // typedef typename T::nodes_t nodes_t;
+ // mln_fwd_piter(nodes_t) p(t.nodes());
+
+ mln_up_node_piter(T) p(t);
for_all(p)
{
if (deja_vu(p))
@@ -91,7 +94,8 @@
void
back_propagate(const T& t, A& a)
{
- mln_fwd_piter(T) p(t.domain());
+ // WAS: mln_fwd_piter(T) p(t.domain());
+ mln_site_piter(T) p(t);
for_all(p)
if (! t.is_a_node(p))
{
Index: theo/color/filter_blen_rgb_edges.cc
--- theo/color/filter_blen_rgb_edges.cc (revision 0)
+++ theo/color/filter_blen_rgb_edges.cc (revision 0)
@@ -0,0 +1,387 @@
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/box3d.hh>
+
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/make/double_neighb2d.hh>
+
+#include <mln/value/label_8.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/core/image/image_if.hh>
+#include <mln/core/routine/extend.hh>
+#include <mln/core/routine/duplicate.hh>
+#include <mln/data/paste.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>
+
+#include <mln/debug/colorize.hh>
+
+#include <mln/morpho/dilation.hh>
+#include <mln/morpho/erosion.hh>
+#include <mln/morpho/watershed/flooding.hh>
+#include <mln/labeling/compute.hh>
+
+#include <mln/accu/count.hh>
+#include <mln/accu/volume.hh>
+#include <mln/accu/mean.hh>
+
+#include "sum_pix.hh"
+#include "segment.hh"
+
+
+
+namespace mln
+{
+
+ point3d color2point(const value::rgb8& c)
+ {
+ point3d p(c.red(), c.green(), c.blue());
+ return p;
+ }
+
+
+ image2d<value::rgb8> blen_image;
+
+
+ template <typename B>
+ void accu_blen_take(B& b, const point2d& e)
+ {
+ if (e.row() % 2)
+ {
+ b.take(color2point(blen_image(e + up)));
+ b.take(color2point(blen_image(e + down)));
+ }
+ else
+ {
+ b.take(color2point(blen_image(e + left)));
+ b.take(color2point(blen_image(e + right)));
+ }
+ }
+
+}
+
+#include "blen_pix.hh"
+
+
+
+
+namespace mln
+{
+
+ // Functions.
+
+ inline
+ bool is_row_odd(const point2d& p)
+ {
+ return p.row() % 2;
+ }
+
+ inline
+ bool is_square(const point2d& p)
+ {
+ return p.row() % 2 == 0 && p.col() % 2 == 0;
+ }
+
+ inline
+ bool is_edge(const point2d& p)
+ {
+ return p.row() % 2 + p.col() % 2 == 1;
+ }
+
+ inline
+ bool is_point(const point2d& p)
+ {
+ return p.row() % 2 && p.col() % 2;
+ }
+
+
+ // Neighborhoods.
+
+ typedef neighb< win::multiple<window2d, bool(*)(const point2d&)> >
dbl_neighb2d;
+
+ const dbl_neighb2d& e2c()
+ {
+ static bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ static bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+ static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2c_h, e2c_v);
+ return nbh;
+ }
+
+ const dbl_neighb2d& e2e()
+ {
+ static 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 };
+ static 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 };
+ static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2e_h, e2e_v);
+ return nbh;
+ }
+
+
+ // Transforms.
+
+ template <typename T>
+ image2d<T>
+ image2full(const image2d<T>& input)
+ {
+ 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)
+ opt::at(output, 2 * row, 2 * col) = opt::at(input, row, col);
+ return output;
+ }
+
+ template <typename T>
+ image2d<T>
+ full2image(const image2d<T>& input)
+ {
+ 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)
+ opt::at(output, row / 2, col / 2) =
+ opt::at(input, row, col);
+ return output;
+ }
+
+
+ // Display.
+
+ 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);
+ data::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)
+ opt::at(output, 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)
+ opt::at(output, row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+
+
+ // Distance, stored on edges, of a couple of colors.
+
+ template <typename I, typename N>
+ image2d<value::int_u8>
+ dist_on_edges(const I& input, const N& nbh)
+ {
+ image2d<value::int_u8> output;
+ initialize(output, input);
+ data::fill(output, 0);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ n.start();
+ value::rgb8 c1 = input(n);
+ n.next();
+ value::rgb8 c2 = input(n);
+ output(p) = dist(c1, c2);
+ }
+
+ io::pgm::save(output, "temp_dist.pgm");
+
+ return output;
+ }
+
+
+ value::int_u8 L_to_int_u8(unsigned l)
+ {
+ return l == 0 ?
+ 0 : // wshed line
+ 1 + (l - 1) % 255; // basin
+ }
+
+
+} // mln
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.ppm
lambda echo output.ppm" << std::endl;
+ std::cerr << " lambda >= 0" << std::endl;
+ std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl;
+ abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+ using value::rgb8;
+
+ if (argc != 5)
+ usage(argv);
+
+
+ // Color version.
+ // --------------
+
+
+ image2d<rgb8> input;
+ io::ppm::load(input, argv[1]);
+
+
+ int lambda = atoi(argv[2]);
+ if (lambda < 0)
+ usage(argv);
+
+ bool echo = atoi(argv[3]);
+
+
+ // Changing input into 'f on edges'.
+
+ image2d<int_u8> f_;
+ image2d<rgb8> input_ = image2full(input);
+ {
+ f_ = dist_on_edges(extend(input_ | is_edge, pw::value(input_)),
+ e2c());
+ }
+ mln_VAR(f, f_ | is_edge);
+ typedef f_t I;
+
+
+ typedef p_array<mln_psite_(I)> S;
+ S s = level::sort_psites_decreasing(f);
+
+ typedef morpho::tree::data<I,S> tree_t;
+ tree_t t(f, s, e2e());
+
+
+ blen_image = input_;
+ accu::blen_pix<I> a_;
+ mln_VAR(a, compute_attribute_on_nodes(a_, t));
+
+
+ f_t g = do_filter(a, t, e2e(), lambda);
+
+
+ if (echo)
+ debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) |
f.domain());
+
+ // Watershed transform.
+
+ typedef value::label_16 L;
+ L nbasins;
+ mln_ch_value_(f_t, L) w = morpho::watershed::flooding(g, e2e(), nbasins);
+
+ std::cout << "n basins = " << nbasins << std::endl;
+
+ io::pgm::save(display_edge(w.unmorph_(), 0, 3), "temp_w.pgm");
+
+ if (echo)
+ {
+ image2d<int_u8> g_(f_.domain());
+
+ data::fill(g_, 0);
+ data::paste(g | (pw::value(w) != 0), g_);
+ debug::println("g | basins = ", g_ | is_edge);
+
+ data::fill(g_, 0);
+ data::paste(g | (pw::value(w) == 0), g_);
+ debug::println("g | w line = ", g_ | is_edge);
+ }
+
+ image2d<L> w_all = w.unmorph_();
+
+ // edges -> squares
+ mln_VAR(w_squares, w_all | is_square);
+ data::paste(morpho::dilation(extend(w_squares, pw::value(w_all)),
+ c4().win()),
+ w_all);
+ // edges -> points
+ mln_VAR(w_points, w_all | is_point);
+ data::paste(morpho::erosion(extend(w_points, pw::value(w_all)),
+ c4().win()),
+ w_all);
+
+
+ io::ppm::save(debug::colorize(rgb8(),
+ w_all,
+ nbasins),
+ "temp_w_all.ppm");
+
+
+ // Outputing.
+ // ----------
+
+ image2d<L> out_w = full2image(w_all);
+
+// io::ppm::save(debug::colorize(rgb8(),
+// out_w,
+// nbasins),
+// argv[4]);
+
+
+ typedef algebra::vec<3,float> vec3f;
+
+ util::array<vec3f> m_3f = labeling::compute(accu::mean<rgb8>(),
+ input,
+ full2image(w_all),
+ nbasins);
+ m_3f[0] = vec3f::zero;
+
+ util::array<rgb8> m(m_3f.nelements());
+ // convert::from_to(m_3f, m); // FIXME: Do not work!
+ for (unsigned i = 0; i < m_3f.nelements(); ++i)
+ m[i] = rgb8(m_3f[i][0], m_3f[i][1], m_3f[i][2]);
+
+
+ io::ppm::save( level::transform(out_w,
+ convert::to< fun::i2v::array<rgb8> >(m) ),
+ argv[4] );
+
+
+// io::ppm::save( level::transform(w_all,
+// convert::to< fun::i2v::array<rgb8> >(m) ),
+// argv[4] );
+
+
+// debug::println("input_", input_);
+// debug::println("w_squares", w_squares);
+
+// FIXME: The code below does not work!
+
+// util::array<vec3f> m_3f = labeling::compute(accu::mean<rgb8>(),
+// input_,
+// w_squares,
+// nbasins);
+
+}
Index: lazzara/icdar/quasi.cc
--- lazzara/icdar/quasi.cc (revision 0)
+++ lazzara/icdar/quasi.cc (revision 0)
@@ -0,0 +1,180 @@
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/pw/all.hh>
+#include <mln/core/image/image_if.hh>
+
+#include <mln/core/site_set/p_queue.hh>
+#include <mln/core/site_set/p_array.hh>
+
+#include <mln/data/fill.hh>
+
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <mln/debug/colorize.hh>
+#include <mln/fun/i2v/array.hh>
+
+#include <mln/labeling/regional_minima.hh>
+#include <mln/labeling/wrap.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/level/convert.hh>
+
+#include <mln/math/diff_abs.hh>
+
+
+
+
+
+namespace mln
+{
+
+ float
+ sim(unsigned v1, unsigned v2)
+ {
+ if (v1 == 0 && v2 == 0)
+ return 1.f;
+ return v1 < v2 ? float(v1) / float(v2) : float(v2) / float(v1); // min / max
+ }
+
+
+ template <typename I, typename N, typename L>
+ mln_ch_value(I, bool)
+ labeling__quasi_regional_minima(const Image<I>& input_, const
Neighborhood<N>& nbh_,
+ unsigned threshold,
+ L& cur_lab)
+ {
+ trace::entering("labeling__quasi_regional_minima");
+
+ const I& input = exact(input_);
+ const N& nbh = exact(nbh_);
+ mln_precondition(input.is_valid());
+
+ typedef mln_psite(I) P;
+ p_array<P> s = level::sort_psites_increasing(input);
+
+ std::vector<bool> valid;
+ valid.push_back(false); // For valid[0] where 0 is the non-label value.
+
+ mln_ch_value(I, L) lab;
+ initialize(lab, input);
+ data::fill(lab, 0);
+
+ cur_lab = 0;
+ unsigned
+ n_stop_unknown,
+ n_stop_object,
+ n_stop_background;
+
+ p_queue<P> q;
+ P p_;
+ mln_niter(N) n(nbh, p_);
+
+ mln_piter(p_array<P>) p(s);
+ for_all(p)
+ {
+ if (lab(p) != 0)
+ continue;
+
+ mln_invariant(q.is_empty());
+
+ // Initialization.
+ ++cur_lab;
+ n_stop_unknown = 0;
+ n_stop_object = 0;
+ n_stop_background = 0;
+
+ mln_value(I) input_p = input(p);
+ lab(p) = cur_lab;
+ q.push(p);
+
+ while (! q.is_empty())
+ {
+ p_ = q.pop_front();
+ mln_invariant(lab(p_) != 0); // Already seen.
+ for_all(n) if (input.domain().has(n))
+ {
+ if (lab(n) == cur_lab)
+ continue;
+ if (lab(n) == 0)
+ {
+// if (sim(input(n), input(p_)) > 0.7
+// &&
+// sim(input(n), input_p) > 0.8)
+ if (math::diff_abs(input(n), input(p_)) < threshold
+ &&
+ math::diff_abs(input(n), input_p) < 2 * threshold)
+ {
+ lab(n) = cur_lab;
+ q.push(n);
+ }
+ else
+ ++n_stop_unknown; // Stop because of threshold.
+ continue;
+ }
+ mln_invariant(lab(n) != 0 && lab(n) != cur_lab); // Another component.
+
+ if (valid[lab(n)])
+ ++n_stop_object;
+ else
+ ++n_stop_background;
+ }
+ }
+
+ if (n_stop_object + n_stop_background <= n_stop_unknown / 4)
+ {
+ valid.push_back(true);
+ continue;
+ }
+ valid.push_back(false);
+ }
+
+ fun::i2v::array<bool> f_valid;
+ convert::from_to(valid, f_valid);
+
+ mln_ch_value(I, bool) out = level::transform(lab, f_valid);
+
+ io::pgm::save(labeling::wrap(value::int_u8(), lab),
+ "tmp_lab.pgm");
+
+ trace::exiting("labeling__quasi_regional_minima");
+ return out;
+ }
+
+
+} // end of namespace mln
+
+
+
+int main(int argc, char *argv[])
+{
+ using namespace mln;
+ using namespace value;
+
+ // trace::quiet = false;
+
+ if (argc != 4)
+ {
+ std::cout << argv[0] << " input.pgm threshold output.pbm"
<< std::endl;
+ return 1;
+ }
+
+ int threshold = std::atoi(argv[2]);
+
+
+ image2d<int_u8> input;
+ io::pgm::load(input, argv[1]);
+
+ unsigned n_regions;
+ image2d<bool> output = labeling__quasi_regional_minima(input,
+ c4(),
+ threshold,
+ n_regions);
+
+ std::cout << n_regions << std::endl;
+
+ io::pbm::save(output, argv[3]);
+}
Index: lazzara/icdar/binarization.cc
--- lazzara/icdar/binarization.cc (revision 3606)
+++ lazzara/icdar/binarization.cc (working copy)
@@ -1,34 +1,67 @@
+#include <mln/core/var.hh>
+
+#include <mln/core/image/image1d.hh>
+#include <mln/core/alias/neighb1d.hh>
+
#include <mln/core/image/image2d.hh>
-#include <mln/core/site_set/box.hh>
-#include <mln/data/fill.hh>
-#include <mln/core/alias/dpoint2d.hh>
-#include <mln/core/macros.hh>
-#include <mln/core/concept/function.hh>
#include <mln/core/alias/neighb2d.hh>
+#include <mln/make/double_neighb2d.hh>
+
+#include <mln/pw/all.hh>
+#include <mln/core/image/image_if.hh>
+#include <mln/core/site_set/p_queue.hh>
+
+#include <mln/core/routine/extend.hh>
+#include <mln/core/routine/duplicate.hh>
+#include <mln/data/fill.hh>
+
#include <mln/core/site_set/p_edges.hh>
#include <mln/core/site_set/p_vertices.hh>
+#include <mln/core/image/graph_elt_neighborhood.hh>
+
#include <mln/io/essential.hh>
#include <mln/value/int_u8.hh>
#include <mln/value/int_u16.hh>
+#include <mln/value/label_8.hh>
#include <mln/value/label_16.hh>
#include <mln/value/rgb8.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/histo/compute.hh>
+
+#include <mln/morpho/erosion.hh>
+#include <mln/morpho/dilation.hh>
#include <mln/morpho/elementary/gradient_internal.hh>
#include <mln/morpho/closing/volume.hh>
+#include <mln/morpho/closing/height.hh>
#include <mln/morpho/watershed/flooding.hh>
+
#include <mln/make/rag_and_labeled_wsl.hh>
#include <mln/util/graph.hh>
#include <mln/util/couple.hh>
#include <mln/debug/colorize.hh>
#include <mln/fun/i2v/array.hh>
+
#include <mln/labeling/compute.hh>
+#include <mln/labeling/regional_minima.hh>
+#include <mln/labeling/wrap.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/level/convert.hh>
+
+#include <mln/linear/gaussian_1d.hh>
+
#include <mln/accu/center.hh>
#include <mln/accu/median_h.hh>
#include <mln/accu/mean.hh>
-#include <mln/pw/cst.hh>
+
#include <mln/math/abs.hh>
-#include <mln/literal/colors.hh>
#include <mln/debug/draw_graph.hh>
+
+
+
+
namespace mln
{
@@ -202,6 +235,339 @@
} // end of namespace mln::make
+ int
+ find_threshold(const histo::array<value::int_u8>& h)
+ {
+
+ const float sigma = 5; // FIXME: hard-coded!
+
+ util::array<point1d> c;
+ value::label_8 n;
+
+ {
+ image1d<unsigned> h_, hs_;
+ image1d<value::label_8> l;
+
+ convert::from_to(h, h_);
+ hs_ = linear::gaussian_1d(h_, sigma, 0);
+ l = labeling::regional_minima(hs_, c2(), n);
+
+ {
+// debug::println("l", l);
+// debug::histo(h, "tmp_h.txt");
+
+// std::ofstream file("tmp_hs.txt");
+// mln_piter_(box1d) p(h_.domain());
+// for_all(p)
+// file << p.ind() << ' ' << hs_(p) << std::endl;
+// file.close();
+ }
+
+ accu::center<point1d, point1d> a;
+ c = labeling::compute(a, l, n);
+
+ c[0] = point1d(0); // Force a neutral value for the non-label value (0).
+
+ // std::cout << "c = " << c << std::endl;
+ }
+
+
+ int threshold;
+
+ {
+ std::vector<int> v;
+ for (unsigned i = 0; i <= n; ++i)
+ v.push_back(c[i].ind());
+ std::sort(v.begin(), v.end());
+
+ for (unsigned i = 0; i <= n; ++i)
+ if (v[i] != 0)
+ {
+ threshold = v[i];
+ break;
+ }
+
+ std::cout << "threshold = " << threshold << std::endl;
+ }
+
+ return threshold;
+ }
+
+
+ image2d<bool> contours(const image2d<value::int_u8>& f, unsigned&
threshold)
+ {
+ using value::int_u8;
+ typedef int_u8 V;
+ histo::array<V> h;
+
+ image2d<int_u8> g;
+ g = morpho::elementary::gradient_internal(f, c4());
+ g = morpho::closing::height(g, c4(), 3); // FIXME: hard-coded!
+
+ unsigned nbasins;
+ image2d<unsigned> w = morpho::watershed::flooding(g, c4(), nbasins);
+
+ h = histo::compute(g | (pw::value(w) == pw::cst(0)));
+
+ threshold = find_threshold(h);
+
+ return duplicate((pw::value(g) > pw::cst(threshold)) | f.domain());
+ }
+
+
+ // Distances.
+
+ value::int_u8 dist(const value::int_u8& g1, const value::int_u8& g2)
+ {
+ return math::diff_abs(g1, g2);
+ }
+
+ value::int_u8 dist(const value::rgb8& c1, const value::rgb8& c2)
+ {
+ unsigned d = math::diff_abs(c1.red(), c2.red());
+ unsigned d_;
+ d_ = math::diff_abs(c1.green(), c2.green());
+ if (d_ > d)
+ d = d_;
+ d_ = math::diff_abs(c1.blue(), c2.blue());
+ if (d_ > d)
+ d = d_;
+ return d;
+ }
+
+
+ // Functions.
+
+ inline
+ bool is_row_odd(const point2d& p)
+ {
+ return p.row() % 2;
+ }
+
+ inline
+ bool is_square(const point2d& p)
+ {
+ return p.row() % 2 == 0 && p.col() % 2 == 0;
+ }
+
+ inline
+ bool is_edge(const point2d& p)
+ {
+ return p.row() % 2 + p.col() % 2 == 1;
+ }
+
+ inline
+ bool is_point(const point2d& p)
+ {
+ return p.row() % 2 && p.col() % 2;
+ }
+
+
+ // Neighborhoods.
+
+ typedef neighb< win::multiple<window2d, bool(*)(const point2d&)> >
dbl_neighb2d;
+
+ const dbl_neighb2d& e2c()
+ {
+ static bool e2c_h[] = { 0, 1, 0,
+ 0, 0, 0,
+ 0, 1, 0 };
+ static bool e2c_v[] = { 0, 0, 0,
+ 1, 0, 1,
+ 0, 0, 0 };
+ static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2c_h, e2c_v);
+ return nbh;
+ }
+
+ const dbl_neighb2d& e2e()
+ {
+ static 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 };
+ static 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 };
+ static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2e_h, e2e_v);
+ return nbh;
+ }
+
+
+ // Transforms.
+
+ template <typename T>
+ image2d<T>
+ image2full(const image2d<T>& input)
+ {
+ 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)
+ opt::at(output, 2 * row, 2 * col) = opt::at(input, row, col);
+ return output;
+ }
+
+ template <typename T>
+ image2d<T>
+ full2image(const image2d<T>& input)
+ {
+ 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)
+ opt::at(output, row / 2, col / 2) =
+ opt::at(input, row, col);
+ return output;
+ }
+
+
+ // Display.
+
+ 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);
+ data::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)
+ opt::at(output, 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)
+ opt::at(output, row + i, col) = ima(p);
+ }
+ return output;
+ }
+
+
+
+ // Distance, stored on edges, of a couple of colors.
+
+ template <typename I, typename N>
+ image2d<value::int_u8>
+ dist_on_edges(const I& input, const N& nbh)
+ {
+ image2d<value::int_u8> output;
+ initialize(output, input);
+ data::fill(output, 0);
+
+ mln_piter(I) p(input.domain());
+ mln_niter(N) n(nbh, p);
+ for_all(p)
+ {
+ n.start();
+ mln_value(I) c1 = input(n);
+ n.next();
+ mln_value(I) c2 = input(n);
+ output(p) = dist(c1, c2);
+ }
+
+ // io::pgm::save(output, "temp_dist.pgm");
+
+ return output;
+ }
+
+
+ value::int_u8 L_to_int_u8(unsigned l)
+ {
+ return l == 0 ?
+ 0 : // wshed line
+ 1 + (l - 1) % 255; // basin
+ }
+
+
+
+
+ template <typename I, typename N, typename L>
+ mln_ch_value(I, bool)
+ labeling__quasi_regional_minima(const Image<I>& input_, const
Neighborhood<N>& nbh_,
+ unsigned threshold,
+ L& nlabels)
+ {
+ trace::entering("labeling__quasi_regional_minima");
+
+ const I& input = exact(input_);
+ const N& nbh = exact(nbh_);
+ mln_precondition(input.is_valid());
+
+ typedef mln_psite(I) P;
+ p_array<P> s = level::sort_psites_increasing(input);
+
+ std::vector<bool> valid;
+ valid.push_back(false); // For valid[0] where 0 is the non-label value.
+
+ mln_ch_value(I, L) lab;
+ initialize(lab, input);
+ data::fill(lab, 0);
+
+ L cur_lab = 0;
+
+ p_queue<P> q;
+ P p_;
+ mln_niter(N) n(nbh, p_);
+
+ mln_piter(p_array<P>) p(s);
+ for_all(p)
+ {
+ if (lab(p) != 0)
+ continue;
+
+ mln_invariant(q.is_empty());
+
+ // Initialization.
+ ++cur_lab;
+ valid.push_back(true);
+ mln_invariant(valid[cur_lab] == true);
+ lab(p) = cur_lab;
+ q.push(p);
+
+ while (! q.is_empty())
+ {
+ p_ = q.pop_front();
+ mln_invariant(lab(p_) != 0); // Already seen.
+ for_all(n)
+ {
+ if (lab(n) == cur_lab)
+ continue;
+ if (lab(n) == 0)
+ if (math::diff_abs(input(n), input(p_)) < threshold)
+ {
+ lab(n) = cur_lab;
+ q.push(n);
+ continue;
+ }
+ mln_invariant(lab(n) != 0 && lab(n) != cur_lab);
+ if (valid[cur_lab])
+ valid[cur_lab] = false; // Invalidation.
+ }
+ }
+ }
+
+ fun::i2v::array<bool> f_valid;
+ convert::from_to(valid, f_valid);
+
+ mln_ch_value(I, bool) out = level::transform(lab, f_valid);
+
+ trace::exiting("labeling__quasi_regional_minima");
+ return out;
+ }
+
+
} // end of namespace mln
@@ -211,63 +577,263 @@
using namespace mln;
using namespace value;
- if (argc < 2)
+ // trace::quiet = false;
+
+ if (argc != 5)
+ {
+ std::cout << argv[0] << " input.pgm lambda echo output.pbm"
<< std::endl;
+ return 1;
+ }
+
+ int lambda_grad = std::atoi(argv[2]);
+ int echo = std::atoi(argv[3]);
+
+
+ if (echo != 0 && echo != 1)
{
- std::cout << argv[0] << " <input.pgm> " <<
std::endl;
+ std::cout << argv[0] << " input.pgm lambda echo output.pbm"
<< std::endl;
return 1;
}
+
image2d<int_u8> input;
io::pgm::load(input, argv[1]);
- image2d<int_u8> grad = morpho::elementary::gradient_internal(input, c4());
- io::pgm::save(grad, "binarization_grad.pgm");
+ image2d<int_u8> input_ = image2full(input); // Larger to make room to edges.
+
+ image2d<int_u8> grad_;
+ {
+ grad_ = dist_on_edges(extend(input_ | is_edge, pw::value(input_)),
+ e2c());
+ }
+
+ // io::pgm::save(grad_, "grad_.pgm");
+
+ mln_VAR(grad, grad_ | is_edge);
+ typedef grad_t E;
+
+ grad = morpho::closing::volume(grad,
+ e2e(),
+ lambda_grad);
+
+ // io::pgm::save(grad.unmorph_(), "grad.pgm");
+
+ typedef label_16 L; // Label type.
+
+ L nbasins;
+
+ typedef mln_ch_value_(E, L) EL;
+
+ EL wst = morpho::watershed::flooding(grad,
+ e2e(),
+ nbasins);
+
+ std::cout << "n basins = " << nbasins << std::endl;
+
+
+ if (echo)
+ {
+ io::ppm::save(display_edge(debug::colorize(rgb8(),
+ wst,
+ nbasins).unmorph_(),
+ literal::black,
+ 3),
+ "temp_wst_edges.ppm");
+ }
+
+
+ image2d<L> w_all = wst.unmorph_();
+
+ // edges -> squares
+ mln_VAR(w_squares, w_all | is_square);
+ data::paste(morpho::dilation(extend(w_squares, pw::value(w_all)),
+ c4().win()),
+ w_all);
+ // edges -> points
+ mln_VAR(w_points, w_all | is_point);
+ data::paste(morpho::erosion(extend(w_points, pw::value(w_all)),
+ c4().win()),
+ w_all);
+
+ image2d<L> ws = full2image(w_all);
+
+ if (echo)
+ {
+ io::ppm::save(debug::colorize(rgb8(), ws, nbasins),
+ "temp_ws.ppm");
+ }
+
+ util::couple<util::graph, image2d<L> >
+ rag_data = make::rag_and_labeled_wsl(w_all, c8(), nbasins);
+
+ if (echo)
+ io::pgm::save(labeling::wrap(int_u8(),
+ rag_data.second()),
+ "temp_lwsl.pgm");
+
- image2d<int_u8> clo_vol = morpho::closing::volume(grad, c4(), 500);
- io::pgm::save(clo_vol, "binarization_clo.pgm");
+ unsigned threshold;
+ contours(input, threshold);
- label_16 nbasins;
- image2d<label_16> wst = morpho::watershed::flooding(clo_vol, c4(), nbasins);
- io::ppm::save(debug::colorize(rgb8(), wst, nbasins),
"binarization_wst.ppm");
+ typedef accu::median_h<int_u8> A;
+ typedef fun::i2v::array<point2d> F;
- util::couple<util::graph, image2d<label_16> >
- rag_data = make::rag_and_labeled_wsl(wst, c4(), nbasins);
- io::pgm::save(rag_data.second(), "binarization_lwsl.pgm");
- io::ppm::save(debug::colorize(rgb8(), rag_data.second(), nbasins),
"binarization_lwsl.ppm");
+ util::array<int_u8> basin_med = labeling::compute(A(), input, ws, nbasins);
+ basin_med[0] = 0;
+
+
+
+ if (echo)
+ io::ppm::save(debug::colorize(rgb8(), w_all, nbasins),
+ "temp_w_all.ppm");
+
+
+
+ // ----------------------------- R A G --------------------------------
+
+
+ util::graph& gr = rag_data.first();
+
+ fun::i2v::array<int_u8> f_med;
+ convert::from_to(basin_med, f_med);
+
+ if (echo)
+ {
+ io::pgm::save(level::transform(w_all, f_med),
+ "temp_w_all.pgm");
+ io::pgm::save(level::transform(ws, f_med), "temp_basin_med.pgm");
+ }
p_vertices<util::graph, fun::i2v::array<point2d> >
- pv = make::common_pvertices(wst, nbasins, rag_data.first());
+ pv = make::common_pvertices(ws, nbasins, rag_data.first());
+
+ mln_VAR( med, f_med | pv );
+
+
+ typedef graph_elt_neighborhood<util::graph, F> N;
+ N nbh;
+
+ threshold = 25; // FIXME
+
+ L n_objects;
+ mln_VAR( lab, labeling__quasi_regional_minima(med, nbh, threshold, n_objects) );
+
+
+ io::pbm::save(pw::value(level::transform(ws, lab.function()))
+ | input.domain(),
+ argv[4]);
+
+// io::pbm::save((pw::value(level::transform(ws,
+// lab.function())) != pw::cst(0))
+// | input.domain(),
+// argv[4]);
+
+
+ /*
+
+ int_u8
+ object = 255,
+ background = 0,
+ unknown = 128;
+
+ med_t out;
+ initialize(out, med);
+ data::fill(out, unknown);
+
+ // Initialization.
+
+ mln_piter_(med_t) p(med.domain());
+ mln_niter_(N) n(nbh, p);
+ for_all(p)
+ for_all(n)
+ {
+ int d = math::diff_abs(med(p), med(n));
+ // if (2 * d > 3 * threshold)
+ if (d > threshold)
+ {
+ if (med(p) < med(n))
+ {
+ out(p) = object;
+ out(n) = background;
+ }
+ else
+ {
+ out(n) = object;
+ out(p) = background;
+ }
+ break;
+ }
+ }
+
+ if (echo)
+ {
+ io::pgm::save(level::transform(ws, out.function()),
+ "temp_out.pgm");
+ }
+
+
+ // Enqueuing.
+
+ typedef mln_psite_(med_t) P;
+ p_queue<P> q;
+
+ for_all(p)
+ {
+ if (out(p) != unknown)
+ continue;
+ for_all(n)
+ if (out(n) != unknown)
+ {
+ q.insert(p);
+ break;
+ }
+ }
+
+ // Propagation.
+
+ P p_;
+ mln_niter_(N) n_(nbh, p_);
+ while (! q.is_empty())
+ {
+ p_ = q.pop_front();
+
+ if (out(p_) != unknown)
+ continue; // std::cerr << "oops! (1)" << std::endl;
+
+ unsigned d = 256, id;
+ for_all(n_)
+ {
+ if (out(n_) == unknown)
+ {
+ q.insert(n_);
+ continue;
+ }
+ unsigned d_ = math::diff_abs(med(p_), med(n_));
+ if (d_ < d)
+ {
+ d = d_;
+ id = out(n_);
+ }
+ }
+ if (d == 256)
+ std::cerr << "oops! (2)" << std::endl;
+
+ out(p_) = id; // object or background.
+ }
+
+ if (echo)
+ {
+ io::pgm::save(level::transform(ws, out.function()),
+ "temp_out_2.pgm");
+ }
- pw::image<fun::i2v::array<int_u8>,
- p_vertices<util::graph, fun::i2v::array<point2d> > >
- ima_v = make::vertex_image(accu::mean<int_u8>(), pv, input, wst, nbasins);
-
- pw::image<fun::i2v::array<int_u8>,
- p_edges<util::graph,pw::cst_<literal::origin_t> > >
- ima_e = make::edge_image(ima_v,
- make::dummy_pedges(rag_data.first(),literal::origin),
- edge_values());
-
-
- io::pgm::save(make::debug_graph_image(input,ima_v, ima_e, 4, 0),
"binarization_graph.ppm");
-
- // FIXME: we should use the following line instead. We cannot since currently
- // graph edges are numbered from 0 and we must use mln_max() - 1 as
- // background id.
- //
- //label_16 nelts = rag.first().e_nmax();
- label_16 nelts = mln_max(label_16).prev();
-
- util::array<int_u8> median_grad = labeling::compute(accu::meta::median_h(),
- grad,
- rag_data.second(),
- nelts);
- fun::i2v::array<int_u8> f;
- convert::from_to(median_grad, f);
+ io::pbm::save((pw::value(level::transform(ws,
+ out.function())) == pw::cst(255))
+ | input.domain(),
+ argv[4]);
- image2d<int_u8> med = level::transform(rag_data.second(), f);
+ */
- io::pgm::save(med, "binarization_median_grad.pgm");
}