olena: olena-2.0-557-gedf9cfd Add binarization::tmms and binarization::tmms_hysteresis.

* mln/binarization/tmms.hh, * mln/binarization/tmms_hysteresis.hh, * tests/binarization/tmms-hysteresis-out.pgm, * tests/binarization/tmms-out.pgm, * tests/binarization/tmms.cc, * tests/binarization/tmms_hysteresis.cc: New. * tests/binarization/Makefile.am: New target. --- milena/ChangeLog | 13 + milena/mln/binarization/tmms.hh | 163 +++++++++ milena/mln/binarization/tmms_hysteresis.hh | 380 ++++++++++++++++++++ milena/tests/binarization/Makefile.am | 14 +- milena/tests/binarization/tmms-hysteresis-out.pgm | Bin 0 -> 262264 bytes milena/tests/binarization/tmms-out.pgm | Bin 0 -> 262264 bytes .../closing/structural.cc => binarization/tmms.cc} | 31 +- .../tmms_hysteresis.cc} | 30 +- 8 files changed, 597 insertions(+), 34 deletions(-) create mode 100644 milena/mln/binarization/tmms.hh create mode 100644 milena/mln/binarization/tmms_hysteresis.hh create mode 100644 milena/tests/binarization/tmms-hysteresis-out.pgm create mode 100644 milena/tests/binarization/tmms-out.pgm copy milena/tests/{morpho/closing/structural.cc => binarization/tmms.cc} (76%) copy milena/tests/{morpho/closing/structural.cc => binarization/tmms_hysteresis.cc} (73%) diff --git a/milena/ChangeLog b/milena/ChangeLog index 0688513..850a127 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -1,3 +1,16 @@ +2013-05-02 Guillaume Lazzara <z@lrde.epita.fr> + + Add binarization::tmms and binarization::tmms_hysteresis. + + * mln/binarization/tmms.hh, + * mln/binarization/tmms_hysteresis.hh, + * tests/binarization/tmms-hysteresis-out.pgm, + * tests/binarization/tmms-out.pgm, + * tests/binarization/tmms.cc, + * tests/binarization/tmms_hysteresis.cc: New. + + * tests/binarization/Makefile.am: New target. + 2013-04-18 Guillaume Lazzara <z@lrde.epita.fr> Add support for graylevel images in labeling::blobs. diff --git a/milena/mln/binarization/tmms.hh b/milena/mln/binarization/tmms.hh new file mode 100644 index 0000000..5f83547 --- /dev/null +++ b/milena/mln/binarization/tmms.hh @@ -0,0 +1,163 @@ +// Copyright (C) 2013 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 MLN_BINARIZATION_TMMS_HH +# define MLN_BINARIZATION_TMMS_HH + +/// \file +/// +/// \brief Binarization using TMMS algorithm. + +# include <mln/core/concept/image.hh> +# include <mln/core/concept/window.hh> +# include <mln/morpho/dilation.hh> +# include <mln/morpho/erosion.hh> + + +namespace mln +{ + + namespace binarization + { + + /*! + Binarize \p input by the use of TMMS algorithm (see "Text + Segmentation in Natural Scenes Using Toggle-Mapping" from icip09 + for more details \cite fabrizio2009icip). + + \param[in] input the input image. + + \param[in] win the structuring element used to compute erosion + and dilation of input image (During TMMS algorithm, input image + is mapped over two fuctions: an erosion and a dilation of the + input image). + + \param[in] c_min the min contrast allowed to extract feature. + + \return an image with "foreground" marked with the highest + possible color (hc), the "background" marked with the lowest + possible color (lc) and homogeneous regions of the image marked + with the color (hc+lc)/2. (This will fail with two colors + images, but it seems to be a bit stupid to try to binarize two + color images?) + + \author J. Fabrizio + + \ingroup modroutines + */ + template<typename I, typename W> + mln_concrete(I) + tmms(const Image<I>& input, const Window<W>& win, + const mln_value(I)& c_min, const mln_value(I)& min_level, + const mln_value(I)& med_level, const mln_value(I)& max_level, + int pourcent); + + /*! \overload + + \verbatim + min_level = mln_min(V); //background + max_level = mln_max(V); //foreground + med_level = (max_level + min_level) / 2; //homogeneous + \endverbatim + */ + template<typename I, typename W> + mln_concrete(I) + tmms(const Image<I>& input, const Window<W>& win, + const mln_value(I)& c_min); + + +# ifndef MLN_INCLUDE_ONLY + + template<typename I, typename W> + mln_concrete(I) + tmms(const Image<I>& input_, const Window<W>& win_, + const mln_value(I)& c_min, const mln_value(I)& min_level, + const mln_value(I)& med_level, const mln_value(I)& max_level, + int pourcent) + { + mln_trace("binarization::tmms"); + + const I& input = exact(input_); + const W& win = exact(win_); + + mln_precondition(input.is_valid()); + mln_precondition(win.is_valid()); + + mln_concrete(I) dilation = mln::morpho::dilation(input, win); + mln_concrete(I) erosion = mln::morpho::erosion(input, win); + + typedef mln_value(I) V; + //V min_level = mln_min(V); //background + //V max_level = mln_max(V); //foreground + //V med_level = (max_level + min_level) / 2; //homogeneous + + mln_concrete(I) output; + initialize(output, input); + + mln_piter(I) p(input.domain()); + for_all(p) + { + V dil = dilation(p); // by def : dil>=ima>=ero + V ero = erosion(p); + V ima = input(p); + if ((V)(dil - ero) >= c_min) + { + if ((dil - ima) < pourcent * (dil - ero) / 100) + output(p) = max_level; + else + output(p) = min_level; + } + else + output(p) = med_level; + } + + return output; + } + + + template<typename I, typename W> + mln_concrete(I) + tmms(const Image<I>& input, const Window<W>& win, + const mln_value(I)& c_min) + { + typedef mln_value(I) V; + V min_level = mln_min(V); //background + V max_level = mln_max(V); //foreground + V med_level = (max_level + min_level) / 2; //homogeneous + + return tmms(input, win, c_min, + min_level, med_level, max_level, 50); + } + + + +# endif // ! MLN_INCLUDE_ONLY + + } // end of namespace mln::binarization + + +} // end of namespace mln + +#endif // ! MLN_BINARIZATION_TMMS_HH diff --git a/milena/mln/binarization/tmms_hysteresis.hh b/milena/mln/binarization/tmms_hysteresis.hh new file mode 100644 index 0000000..f80caf5 --- /dev/null +++ b/milena/mln/binarization/tmms_hysteresis.hh @@ -0,0 +1,380 @@ +// Copyright (C) 2013 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 MLN_BINARIZATION_TMMS_HYSTERESIS_HH +# define MLN_BINARIZATION_TMMS_HYSTERESIS_HH + +/// \file +/// +/// \brief Binarization using TMMS algorithm combined with hysteresis. + + +# include <mln/core/concept/image.hh> +# include <mln/core/concept/window.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/morpho/dilation.hh> +# include <mln/morpho/erosion.hh> +# include <mln/labeling/blobs.hh> + + +namespace mln +{ + + namespace binarization + { + + /*! + Binarize \p input by the use of TMMS algorithm (see "Text + Segmentation in Natural Scenes Using Toggle-Mapping" from + icip09 for more details \cite fabrizio2009icip). This version + is combined with hysteresis. + + \param[in] input the input image. + + \param[in] win the structuring element used to compute erosion + and dilation of input image (During TMMS algorithm, input image + is mapped over two fuctions: an erosion and a dilation of the + input image). + + \param[in] c_min the min contrast allowed to extract feature. + + \return an image with "foreground" marked with the highest + possible color (hc), the "background" marked with the lowest + possible color (lc) and homogeneous regions of the image marked + with the color (hc+lc)/2. (This will fail with two colors + images, but it seems to be a bit stupid to try to binarize two + color images?) + + \author J. Fabrizio + + \ingroup modroutines + */ + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis(const Image<I>& input_, const Window<W>& win, + const mln_value(I)& c_min_low, + const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent); + + +# ifndef MLN_INCLUDE_ONLY + + // Implementations + + namespace impl + { + + namespace generic + { + + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis(const Image<I>& input_, const Window<W>& win_, + const mln_value(I)& c_min_low, + const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent) + { + mln_trace("mln::binarization::impl::generic::tmms_hysteresis"); + + const I& input = exact(input_); + const W& win = exact(win_); + + mln_precondition(input.is_valid()); + mln_precondition(win.is_valid()); + + typedef mln_value(I) V; + + mln_concrete(I) dilation = mln::morpho::dilation(input, win); + mln_concrete(I) erosion = mln::morpho::erosion(input, win); + + mln_concrete(I) low_image; + initialize(low_image, input); + mln_concrete(I) high_image; + initialize(high_image, input); + + mln_piter(I) p(input.domain()); + for_all(p) + { + V dil = dilation(p); // by def : dil>=ima>=ero + V ero = erosion(p); + V ima = input(p); + + if ((V)(dil - ero) < c_min_low) + { + low_image(p) = med_level; + high_image(p) = med_level; + } + else if ((V)(dil - ero) < c_min_high) + { + low_image(p) = med_level; + if ((V)(dil - ima) < pourcent * ((V) (dil - ero)) / 100) + high_image(p) = max_level; + else + high_image(p) = min_level; + } + else + { + if ((V)(dil - ima) < pourcent * ((V)(dil - ero)) / 100) + { + low_image(p) = max_level; + high_image(p) = max_level; + } + else + { + low_image(p) = min_level; + high_image(p) = min_level; + } + } + + } + + unsigned nlabels; + mln_ch_value(I, unsigned) + labels_high_images = mln::labeling::blobs(high_image, mln::c4(), nlabels); + + std::vector<V> values(nlabels + 1); + + for (unsigned i = 0; i <= nlabels ; i++) + values[i] = med_level; + + for_all(p) + if (low_image(p) != med_level) + values[labels_high_images(p)] = low_image(p); + + for_all(p) + if (low_image(p) == med_level) + low_image(p) = values[labels_high_images(p)]; + + return low_image; + } + + } // end of namespace mln::binarization::impl::generic + + + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis_fastest(const Image<I>& input_, const Window<W>& win_, + const mln_value(I)& c_min_low, const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent) + { + mln_trace("mln::binarization::impl::tmms_hysteresis_fastest"); + + const I& input = exact(input_); + const W& win = exact(win_); + + mln_precondition(input.is_valid()); + mln_precondition(win.is_valid()); + + typedef mln_value(I) V; + + mln_concrete(I) dilation = mln::morpho::dilation(input, win); + mln_concrete(I) erosion = mln::morpho::erosion(input, win); + + // Checking border sizes. + border::resize(dilation, input.border()); + border::resize(erosion, input.border()); + mln_assertion(input.border() == dilation.border()); + mln_assertion(input.border() == erosion.border()); + + mln_concrete(I) low_image; + initialize(low_image, input); + mln_concrete(I) high_image; + initialize(high_image, input); + + unsigned nrows = geom::nrows(input); + unsigned ncols = geom::ncols(input); + for (unsigned row = 0; row < nrows; ++row) + { + const V *p_input = &(input.at_(row, 0)); + const V *p_dilation = &(dilation.at_(row, 0)); + const V *p_erosion = &(erosion.at_(row, 0)); + V *p_low_image = &(low_image.at_(row, 0)); + V *p_high_image = &(high_image.at_(row, 0)); + + for (unsigned col = 0; col < ncols; ++col) + { + V dil = *p_dilation; // by def : dil>=ima>=ero + V ero = *p_erosion; + V ima = *p_input; + + if ((V)(dil - ero) < c_min_low) + { + *p_low_image = med_level; + *p_high_image = med_level; + } + else if ((V)(dil - ero) < c_min_high) + { + *p_low_image = med_level; + + if ((V)(dil - ima) < (V)(pourcent * (dil - ero) / 100)) + *p_high_image = max_level; + else + *p_high_image = min_level; + } + else + { + if ((V)(dil - ima) < (V)(pourcent * (dil - ero) / 100)) + { + *p_low_image = max_level; + *p_high_image = max_level; + } + else + { + *p_low_image = min_level; + *p_high_image = min_level; + } + } + + ++p_input; + ++p_dilation; + ++p_erosion; + ++p_low_image; + ++p_high_image; + } + } + + unsigned nlabels; + mln_ch_value(I, unsigned) + labels_high_images = mln::labeling::blobs(high_image, c4(), nlabels); + + std::vector<V> values(nlabels + 1); + + for(unsigned i = 0; i <= nlabels; ++i) + values[i] = med_level; + + mln_piter(I) p(input.domain()); + for_all(p) + if (low_image(p) != med_level) + values[labels_high_images(p)] = low_image(p); + + for_all(p) + if (low_image(p) == med_level) + low_image(p) = values[labels_high_images(p)]; + + return low_image; + } + + }// end of namespace mln::binarization::impl + + + // Dispatch + + namespace internal + { + + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis_dispatch(const I& ima, const W& win, + const mln_value(I)& c_min_low, + const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent, mln::trait::image::speed::any) + { + return impl::generic::tmms_hysteresis(ima, win, + c_min_low, c_min_high, + min_level, med_level, + max_level, pourcent); + } + + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis_dispatch(const I& ima, const W& win, + const mln_value(I)& c_min_low, + const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent, mln::trait::image::speed::fastest) + { + return impl::tmms_hysteresis_fastest(ima, win, + c_min_low, c_min_high, + min_level, med_level, + max_level, pourcent); + } + + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis_dispatch(const I& ima, const W& win, + const mln_value(I)& c_min_low, + const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent) + { + return tmms_hysteresis_dispatch(ima, win, + c_min_low, c_min_high, + min_level, med_level, + max_level, pourcent, + mln_trait_image_speed(I)()); + } + + } //end of namespace namespace mln::binarization::internal + + + // Facade + + template<typename I, typename W> + mln_concrete(I) + tmms_hysteresis(const Image<I>& input_, const Window<W>& win_, + const mln_value(I)& c_min_low, const mln_value(I)& c_min_high, + const mln_value(I)& min_level, + const mln_value(I)& med_level, + const mln_value(I)& max_level, + const int pourcent) + { + mln_trace("mln::binarization::tmms_hysteresis"); + + const I& input = exact(input_); + const W& win = exact(win_); + + mln_precondition(input.is_valid()); + mln_precondition(win.is_valid()); + + return internal::tmms_hysteresis_dispatch(input, win, + c_min_low, c_min_high, + min_level, med_level, + max_level, pourcent); + } + +# endif // ! MLN_INCLUDE_ONLY + + } // end of namespace mln::binarization + + +} // end of namespace mln + +#endif // ! MLN_BINARIZATION_TMMS_HYSTERESIS_HH diff --git a/milena/tests/binarization/Makefile.am b/milena/tests/binarization/Makefile.am index 27144e4..5d4bcfe 100644 --- a/milena/tests/binarization/Makefile.am +++ b/milena/tests/binarization/Makefile.am @@ -1,4 +1,4 @@ -# Copyright (C) 2008, 2009, 2010 EPITA Research and Development +# Copyright (C) 2008, 2009, 2010, 2013 EPITA Research and Development # Laboratory (LRDE). # # This file is part of Olena. @@ -18,11 +18,17 @@ include $(top_srcdir)/milena/tests/tests.mk check_PROGRAMS = \ - threshold + threshold \ + tmms \ + tmms_hysteresis TESTS = $(check_PROGRAMS) -MOSTLYCLEANFILES = \ - threshold-out1.pgm \ +MOSTLYCLEANFILES = \ + threshold-out1.pgm \ threshold-out2.pgm + +EXTRA_DIST = \ + tmms-out.pgm \ + tmms-hysteresis-out.pgm diff --git a/milena/tests/binarization/tmms-hysteresis-out.pgm b/milena/tests/binarization/tmms-hysteresis-out.pgm new file mode 100644 index 0000000..14f20c1 Binary files /dev/null and b/milena/tests/binarization/tmms-hysteresis-out.pgm differ diff --git a/milena/tests/binarization/tmms-out.pgm b/milena/tests/binarization/tmms-out.pgm new file mode 100644 index 0000000..3e0ec78 Binary files /dev/null and b/milena/tests/binarization/tmms-out.pgm differ diff --git a/milena/tests/morpho/closing/structural.cc b/milena/tests/binarization/tmms.cc similarity index 76% copy from milena/tests/morpho/closing/structural.cc copy to milena/tests/binarization/tmms.cc index ac925c5..e32a1f2 100644 --- a/milena/tests/morpho/closing/structural.cc +++ b/milena/tests/binarization/tmms.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2013 EPITA Research and Development Laboratory (LRDE) // // This file is part of Olena. // @@ -24,30 +24,31 @@ // executable file might be covered by the GNU General Public License. #include <mln/core/image/image2d.hh> -#include <mln/value/int_u8.hh> #include <mln/core/alias/window2d.hh> - +#include <mln/binarization/tmms.hh> +#include <mln/io/pbm/load.hh> #include <mln/io/pgm/load.hh> -#include <mln/io/pgm/save.hh> - -#include <mln/morpho/closing/structural.hh> +#include <mln/data/compare.hh> +#include <mln/value/int_u8.hh> +#include <mln/win/rectangle2d.hh> #include "tests/data.hh" - +#include <mln/io/pgm/save.hh> int main() { using namespace mln; - using value::int_u8; - image2d<int_u8> lena, out; + image2d<value::int_u8> lena; io::pgm::load(lena, MLN_IMG_DIR "/lena.pgm"); - bool w[] = { 0, 1, 1, - 0, 0, 1, - 0, 0, 1 }; + image2d<value::int_u8> + bin = binarization::tmms(lena, + win::rectangle2d(3,3),50); + + image2d<value::int_u8> ref; + io::pgm::load(ref, MLN_TESTS_DIR "/binarization/tmms-out.pgm"); + + mln_assertion(ref == bin); - out = morpho::closing::structural(lena, - convert::to<window2d>(w)); - io::pgm::save(out, "structural-out.pgm"); } diff --git a/milena/tests/morpho/closing/structural.cc b/milena/tests/binarization/tmms_hysteresis.cc similarity index 73% copy from milena/tests/morpho/closing/structural.cc copy to milena/tests/binarization/tmms_hysteresis.cc index ac925c5..87f9509 100644 --- a/milena/tests/morpho/closing/structural.cc +++ b/milena/tests/binarization/tmms_hysteresis.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2013 EPITA Research and Development Laboratory (LRDE) // // This file is part of Olena. // @@ -24,30 +24,30 @@ // executable file might be covered by the GNU General Public License. #include <mln/core/image/image2d.hh> -#include <mln/value/int_u8.hh> #include <mln/core/alias/window2d.hh> - +#include <mln/binarization/tmms_hysteresis.hh> +#include <mln/io/pbm/load.hh> #include <mln/io/pgm/load.hh> -#include <mln/io/pgm/save.hh> - -#include <mln/morpho/closing/structural.hh> +#include <mln/data/compare.hh> +#include <mln/value/int_u8.hh> +#include <mln/win/rectangle2d.hh> #include "tests/data.hh" - int main() { using namespace mln; - using value::int_u8; - image2d<int_u8> lena, out; + image2d<value::int_u8> lena; io::pgm::load(lena, MLN_IMG_DIR "/lena.pgm"); - bool w[] = { 0, 1, 1, - 0, 0, 1, - 0, 0, 1 }; + image2d<value::int_u8> + bin = binarization::tmms_hysteresis(lena, + win::rectangle2d(3,3), + 20, 45, 0, 128, 255, 60); + + image2d<value::int_u8> ref; + io::pgm::load(ref, MLN_TESTS_DIR "/binarization/tmms-hysteresis-out.pgm"); - out = morpho::closing::structural(lena, - convert::to<window2d>(w)); - io::pgm::save(out, "structural-out.pgm"); + mln_assertion(ref == bin); } -- 1.7.2.5
participants (1)
-
Guillaume Lazzara