4487: preprocessing/split_bg_fg.hh: Add a new algorithm to split background and foreground.

--- scribo/ChangeLog | 5 + scribo/preprocessing/split_bg_fg.hh | 337 +++++++++++++++++++++++++++++++++++ 2 files changed, 342 insertions(+), 0 deletions(-) create mode 100644 scribo/preprocessing/split_bg_fg.hh diff --git a/scribo/ChangeLog b/scribo/ChangeLog index 39c0b92..51f38a0 100644 --- a/scribo/ChangeLog +++ b/scribo/ChangeLog @@ -1,5 +1,10 @@ 2009-09-15 Guillaume Lazzara <lazzara@lrde.epita.fr> + * preprocessing/split_bg_fg.hh: Add a new algorithm to split + background and foreground. + +2009-09-15 Guillaume Lazzara <lazzara@lrde.epita.fr> + Improve Scribo's demo. * demo/src/mainwindow.cc, diff --git a/scribo/preprocessing/split_bg_fg.hh b/scribo/preprocessing/split_bg_fg.hh new file mode 100644 index 0000000..7e05d0d --- /dev/null +++ b/scribo/preprocessing/split_bg_fg.hh @@ -0,0 +1,337 @@ +// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + + +#ifndef SCRIBO_PREPROCESSING_SPLIT_BG_FG_HH +# define SCRIBO_PREPROCESSING_SPLIT_BG_FG_HH + +/// \file +/// +/// Split the background and the foreground. + +# include <mln/core/image/image2d.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/core/routine/duplicate.hh> + +# include <mln/core/image/dmorph/image_if.hh> +# include <mln/pw/all.hh> + +# include <mln/data/fill.hh> +# include <mln/data/transform.hh> + +# include <mln/value/int_u8.hh> +# include <mln/value/rgb8.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/math/diff_abs.hh> +# include <mln/math/min.hh> + +# include <mln/morpho/closing/area.hh> +# include <mln/morpho/opening/area.hh> +# include <mln/morpho/elementary/dilation.hh> + +# include <mln/labeling/blobs.hh> +# include <mln/labeling/colorize.hh> +# include <mln/labeling/compute.hh> +# include <mln/accu/stat/mean.hh> + + + + +namespace scribo +{ + + namespace preprocessing + { + + using namespace mln; + + + /*! \brief Split the background and the foreground. + + \param[in] input A color image. + \param[in] lambda Lambda used for morphological closing/opening. + \param[in] delta Max distance between values in closing and + opening image. + + \return A couple of color images. The first is the background + and the second is the foreground. + */ + template <typename I> + util::couple<mln_concrete(I), mln_concrete(I)> + split_bg_fg(const Image<I>& input_, unsigned lambda, unsigned delta); + + + +# ifndef MLN_INCLUDE_ONLY + + + + // Internal routines. + + namespace internal + { + + using value::rgb8; + + + template <unsigned n> + void + split(const image2d< value::rgb<n> >& input, + // out: + image2d< value::int_u<n> >& r, + image2d< value::int_u<n> >& g, + image2d< value::int_u<n> >& b) + { + trace::entering("scribo::preprocessing::internal::split"); + initialize(r, input); + initialize(g, input); + initialize(b, input); + + // Generic version: + + // mln_piter(box2d) p(input.domain()); + // for_all(p) + // { + // r(p) = input(p).red(); + // g(p) = input(p).green(); + // b(p) = input(p).blue(); + // } + + typedef image2d< value::rgb<n> > I; + typedef image2d< value::int_u<n> > O; + + mln_pixter(const I) p_i(input); + mln_pixter(O) p_r(r); + mln_pixter(O) p_g(g); + mln_pixter(O) p_b(b); + for (p_i.start(), p_r.start(), p_g.start(), p_b.start(); + p_i.is_valid(); + p_i.next(), p_r.next(), p_g.next(), p_b.next()) + { + const value::rgb<n>& c = p_i.val(); + p_r.val() = c.red(); + p_g.val() = c.green(); + p_b.val() = c.blue(); + } + + trace::exiting("scribo::preprocessing::internal::split"); + } + + + template <unsigned n> + image2d< value::rgb<n> > + merge(const image2d< value::int_u<n> >& r, + const image2d< value::int_u<n> >& g, + const image2d< value::int_u<n> >& b) + { + trace::entering("scribo::preprocessing::internal::merge"); + image2d< value::rgb<n> > output(r.domain()); + + // mln_piter(box2d) p(output.domain()); + // for_all(p) + // { + // value::rgb<n>& c = output(p); + // c.red() = r(p); + // c.green() = g(p); + // c.blue() = b(p); + // } + + typedef image2d< value::int_u<n> > I; + mln_pixter(const I) p_r(r); + mln_pixter(const I) p_g(g); + mln_pixter(const I) p_b(b); + typedef image2d< value::rgb<n> > O; + mln_pixter(O) p_o(output); + + for (p_o.start(), p_r.start(), p_g.start(), p_b.start(); + p_o.is_valid(); + p_o.next(), p_r.next(), p_g.next(), p_b.next()) + { + value::rgb<n>& c = p_o.val(); + c.red() = p_r.val(); + c.green() = p_g.val(); + c.blue() = p_b.val(); + } + + trace::exiting("scribo::preprocessing::internal::merge"); + return output; + } + + + + image2d< value::rgb8 > + diff_abs(const image2d< value::rgb8 >& input1, + const image2d< value::rgb8 >& input2) + { + image2d< value::rgb8 > output(input1.domain()); + mln_piter_(box2d) p(input1.domain()); + for_all(p) + { + value::rgb8& c = output(p); + c.red() = math::diff_abs(input1(p).red(), input2(p).red()); + c.green() = math::diff_abs(input1(p).green(), input2(p).green()); + c.blue() = math::diff_abs(input1(p).blue(), input2(p).blue()); + } + return output; + } + + + image2d< value::rgb8 > + inverted_diff_abs(const image2d< value::rgb8 >& input1, + const image2d< value::rgb8 >& input2) + { + image2d< value::rgb8 > output(input1.domain()); + mln_piter_(box2d) p(input1.domain()); + for_all(p) + { + value::rgb8& c = output(p); +// c.red() = 255 - math::diff_abs(input1(p).red(), input2(p).red()); +// c.green() = 255 - math::diff_abs(input1(p).green(), input2(p).green()); +// c.blue() = 255 - math::diff_abs(input1(p).blue(), input2(p).blue()); + + c.red() = 255 - math::min(2 * math::diff_abs(input1(p).red(), input2(p).red()), 255); + c.green() = 255 - math::min(2 * math::diff_abs(input1(p).green(), input2(p).green()), 255); + c.blue() = 255 - math::min(2 * math::diff_abs(input1(p).blue(), input2(p).blue()), 255); + } + return output; + } + + + + unsigned dist(const rgb8& c1, const rgb8& c2) + { + unsigned d = 0; + d += std::abs(c1.red() - c2.red()); + d += std::abs(c1.green() - c2.green()); + d += std::abs(c1.blue() - c2.blue()); + return d; + } + + + image2d<rgb8> + background_analyze(const image2d<rgb8>& input, + unsigned lambda, unsigned delta) + { + trace::entering("scribo::preprocessing::internal::background_analyze"); + + image2d<value::int_u8> r, g, b; + split(input, r, g, b); + image2d<rgb8> closed, opened; + closed = merge(morpho::closing::area(r, c4(), lambda), + morpho::closing::area(g, c4(), lambda), + morpho::closing::area(b, c4(), lambda)); + opened = merge(morpho::opening::area(r, c4(), lambda), + morpho::opening::area(g, c4(), lambda), + morpho::opening::area(b, c4(), lambda)); + + image2d<bool> mask(input.domain()); + mln_piter_(box2d) p(input.domain()); + for_all(p) + mask(p) = (dist(closed(p), opened(p)) >= delta); + + image2d<rgb8> output = duplicate(input); + data::fill((output | pw::value(mask)).rw(), rgb8(255,255,0)); + +// { +// io::ppm::save(output, "temp_output.ppm"); +// io::pbm::save(mask, "temp_mask.pbm"); +// } + + unsigned nblobs; + image2d<unsigned> lab, con; + lab = labeling::blobs(mask, c4(), nblobs); + +// { +// io::ppm::save(labeling::colorize(rgb8(), +// lab, +// nblobs), +// "temp_blobs.ppm"); +// } + + con = morpho::elementary::dilation(lab, c8()); + data::fill((con | (pw::value(lab) != pw::cst(0u))).rw(), + 0u); + +// { +// io::ppm::save(labeling::colorize(rgb8(), +// con, +// nblobs), +// "temp_con.ppm"); +// } + + typedef accu::stat::mean< rgb8, algebra::vec<3,float>, rgb8 > A; + util::array<rgb8> m = labeling::compute(A(), input, con, nblobs); + + data::fill((output | pw::value(mask)).rw(), + data::transform(lab, m)); + + trace::exiting("scribo::preprocessing::internal::toggle"); + return output; + } + + + } // end of namespace scribo::internal + + + + + // Facade + + template <typename I> + util::couple<mln_concrete(I), mln_concrete(I)> + split_bg_fg(const Image<I>& input_, unsigned lambda, unsigned delta) + { + trace::entering("scribo::preprocessing::split_bg_fg"); + + using namespace mln; + + const I& input = exact(input_); + + mln_precondition(input.is_valid()); + mln_precondition(lambda >= 1); + mln_precondition(delta >= 1); + + image2d<value::rgb8> + bg = internal::background_analyze(input, lambda, delta); + image2d<value::rgb8> fg = internal::inverted_diff_abs(input, bg); + + trace::exiting("scribo::preprocessing::split_bg_fg"); + return make::couple(bg, fg); + } + + +# endif // ! MLN_INCLUDE_ONLY + + + } // end of namespace scribo::preprocessing + +} // end of namespace scribo + +#endif // ! SCRIBO_PREPROCESSING_SPLIT_BG_FG_HH -- 1.5.6.5
participants (1)
-
Guillaume Lazzara