* scribo/layout/internal/smearing/bbox2d.hh,
* scribo/layout/internal/smearing/classification.hh,
* scribo/layout/internal/smearing/crla.hh,
* scribo/layout/internal/smearing/draw_region.hh,
* scribo/layout/internal/smearing/labeling.hh,
* scribo/layout/smearing.hh,
* tests/layout/smearing-nontext.pbm,
* tests/layout/smearing-text.pbm,
* tests/layout/smearing.cc: New.
* tests/layout/Makefile.am: New target.
---
scribo/ChangeLog | 16 +
scribo/scribo/layout/internal/smearing/bbox2d.hh | 151 +++++++++
.../layout/internal/smearing/classification.hh | 147 +++++++++
scribo/scribo/layout/internal/smearing/crla.hh | 219 +++++++++++++
.../scribo/layout/internal/smearing/draw_region.hh | 121 +++++++
scribo/scribo/layout/internal/smearing/labeling.hh | 340 ++++++++++++++++++++
scribo/scribo/layout/smearing.hh | 223 +++++++++++++
scribo/tests/layout/Makefile.am | 10 +-
scribo/tests/layout/smearing-nontext.pbm | 5 +
.../alignment_3.pbm => layout/smearing-text.pbm} | Bin 12167 -> 11996 bytes
scribo/tests/layout/{xy_cut.cc => smearing.cc} | 19 +-
11 files changed, 1240 insertions(+), 11 deletions(-)
create mode 100644 scribo/scribo/layout/internal/smearing/bbox2d.hh
create mode 100644 scribo/scribo/layout/internal/smearing/classification.hh
create mode 100644 scribo/scribo/layout/internal/smearing/crla.hh
create mode 100644 scribo/scribo/layout/internal/smearing/draw_region.hh
create mode 100644 scribo/scribo/layout/internal/smearing/labeling.hh
create mode 100644 scribo/scribo/layout/smearing.hh
create mode 100644 scribo/tests/layout/smearing-nontext.pbm
copy scribo/tests/{img/alignment_3.pbm => layout/smearing-text.pbm} (95%)
copy scribo/tests/layout/{xy_cut.cc => smearing.cc} (78%)
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 90de65c..b10317a 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,19 @@
+2013-05-13 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Add Smearing layout analysis algorithm.
+
+ * scribo/layout/internal/smearing/bbox2d.hh,
+ * scribo/layout/internal/smearing/classification.hh,
+ * scribo/layout/internal/smearing/crla.hh,
+ * scribo/layout/internal/smearing/draw_region.hh,
+ * scribo/layout/internal/smearing/labeling.hh,
+ * scribo/layout/smearing.hh,
+ * tests/layout/smearing-nontext.pbm,
+ * tests/layout/smearing-text.pbm,
+ * tests/layout/smearing.cc: New.
+
+ * tests/layout/Makefile.am: New target.
+
2013-04-26 Guillaume Lazzara <z(a)lrde.epita.fr>
* scribo/binarization/sauvola_ms_split.hh: Make use of all arguments.
diff --git a/scribo/scribo/layout/internal/smearing/bbox2d.hh
b/scribo/scribo/layout/internal/smearing/bbox2d.hh
new file mode 100644
index 0000000..502e279
--- /dev/null
+++ b/scribo/scribo/layout/internal/smearing/bbox2d.hh
@@ -0,0 +1,151 @@
+// 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.
+
+/// \file
+///
+/// \brief 2D box class used in Smearing algorithm.
+
+/// FIXME: Use box2d ?
+
+#ifndef SCRIBO_LAYOUT_INTERNAL_SMEARING_BBOX2D_HH
+# define SCRIBO_LAYOUT_INTERNAL_SMEARING_BBOX2D_HH
+
+# include <mln/core/alias/point2d.hh>
+
+namespace scribo
+{
+
+ namespace layout
+ {
+
+ namespace internal
+ {
+
+ namespace smearing
+ {
+
+ using namespace mln;
+
+ //---------------------------
+ // Bounding box 2D
+ //---------------------------
+
+ class bbox2d
+ {
+ public:
+ bbox2d();
+
+ bbox2d(const unsigned n,
+ const point2d& pmin,
+ const point2d& pmax);
+
+ point2d& pmin();
+ const point2d& pmin() const;
+
+ point2d& pmax();
+ const point2d& pmax() const;
+
+ point2d pcenter() const;
+
+ unsigned bbox_number() const;
+
+ private:
+ unsigned n_;
+ point2d pmin_;
+ point2d pmax_;
+ };
+
+# ifndef MLN_INCLUDE_ONLY
+
+ inline
+ bbox2d::bbox2d()
+ : n_(0), pmin_(point2d(0, 0)), pmax_(point2d(0, 0))
+ {
+ }
+
+ inline
+ bbox2d::bbox2d(const unsigned n,
+ const point2d& pmin,
+ const point2d& pmax)
+ : n_(n), pmin_(pmin), pmax_(pmax)
+ {
+ }
+
+ inline
+ point2d&
+ bbox2d::pmin()
+ {
+ return pmin_;
+ }
+
+ inline
+ const point2d&
+ bbox2d::pmin() const
+ {
+ return pmin_;
+ }
+
+ inline
+ point2d&
+ bbox2d::pmax()
+ {
+ return pmax_;
+ }
+
+ inline
+ const point2d&
+ bbox2d::pmax() const
+ {
+ return pmax_;
+ }
+
+ inline
+ point2d
+ bbox2d::pcenter() const
+ {
+ point2d p(pmin_.row() + (pmax_.row() - pmin_.row()) / 2,
+ pmin_.col() + (pmax_.col() - pmin_.col()) / 2);
+
+ return p;
+ }
+
+ inline
+ unsigned
+ bbox2d::bbox_number() const
+ {
+ return n_;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::layout::internal::smearing
+
+ } // end of namespace scribo::layout::internal
+
+ } // end of namespace scribo::layout
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_LAYOUT_INTERNAL_SMEARING_BBOX2D_HH
diff --git a/scribo/scribo/layout/internal/smearing/classification.hh
b/scribo/scribo/layout/internal/smearing/classification.hh
new file mode 100644
index 0000000..32c2405
--- /dev/null
+++ b/scribo/scribo/layout/internal/smearing/classification.hh
@@ -0,0 +1,147 @@
+// 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.
+
+/// \file
+///
+/// \brief Routines used in scribo::layout::smearing
+
+#ifndef SCRIBO_LAYOUT_INTERNAL_SMEARING_CLASSIFICATION_HH
+# define SCRIBO_LAYOUT_INTERNAL_SMEARING_CLASSIFICATION_HH
+
+# include <mln/core/image/image2d.hh>
+# include <scribo/layout/internal/smearing/bbox2d.hh>
+
+
+namespace scribo
+{
+
+ namespace layout
+ {
+
+ namespace internal
+ {
+
+ namespace smearing
+ {
+
+ using namespace mln;
+
+
+ template<typename V>
+ void classify_component(const bbox2d& bbox,
+ V label,
+ image2d<V>& labeled_img);
+
+ template<typename V>
+ void compute_region_info(const image2d<bool>& input,
+ const image2d<V>& labeled_img,
+ const bbox2d& bbox,
+ float& brl,
+ float& tc);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ //---------------------------
+ // Classify component
+ //---------------------------
+
+ template<typename V>
+ inline
+ void classify_component(const bbox2d& bbox,
+ V label,
+ image2d<V>& labeled_img)
+ {
+ const unsigned id = bbox.bbox_number();
+ const point2d& pmin = bbox.pmin();
+ const point2d& pmax = bbox.pmax();
+ box2d b(pmin, pmax);
+ mln_piter_(box2d) p(b);
+
+ for_all(p)
+ {
+ V& pixel = labeled_img(p);
+ if (pixel == id)
+ pixel = label;
+ }
+ }
+
+ //---------------------------
+ // Compute region information
+ //---------------------------
+
+ template<typename V>
+ inline
+ void compute_region_info(const image2d<bool>& input,
+ const image2d<V>& labeled_img,
+ const bbox2d& bbox,
+ float& brl,
+ float& tc)
+ {
+ const unsigned id = bbox.bbox_number();
+ const point2d& pmin = bbox.pmin();
+ const point2d& pmax = bbox.pmax();
+
+ for (int i = pmin.row(); i <= pmax.row(); ++i)
+ {
+ for (int j = pmin.col(); j <= pmax.col(); ++j)
+ {
+ const unsigned label_pixel = labeled_img.at_(i, j);
+
+ if (label_pixel == id)
+ {
+ const bool pixel = input.at_(i, j);
+
+ if (pixel)
+ {
+ if (j > 0 && input.at_(i, j - 1) == 0)
+ ++tc;
+
+ int k = j + 1;
+ for (; input.at_(i, k); ++k);
+ brl += (k - j);
+
+ j = k;
+ }
+ }
+ }
+ }
+
+ if (tc == 0.0f)
+ tc = 1.0f;
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::layout::internal::smearing
+
+ } // end of namespace scribo::layout::internal
+
+ } // end of namespace scribo::layout
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_LAYOUT_INTERNAL_SMEARING_CLASSIFICATION_HH
diff --git a/scribo/scribo/layout/internal/smearing/crla.hh
b/scribo/scribo/layout/internal/smearing/crla.hh
new file mode 100644
index 0000000..431ed79
--- /dev/null
+++ b/scribo/scribo/layout/internal/smearing/crla.hh
@@ -0,0 +1,219 @@
+// 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.
+
+/// \file
+///
+/// \brief Routines used in scribo::layout::smearing
+
+#ifndef SCRIBO_LAYOUT_INTERNAL_SMEARING_CRLA_HH
+# define SCRIBO_LAYOUT_INTERNAL_SMEARING_CRLA_HH
+
+#include <mln/core/image/image2d.hh>
+#include <mln/pw/all.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+
+
+namespace scribo
+{
+
+ namespace layout
+ {
+
+ namespace internal
+ {
+
+ namespace smearing
+ {
+
+
+ using namespace mln;
+
+
+ template< typename V >
+ void horizontal_CRLA(const image2d<V>& input,
+ image2d<V>& output,
+ const unsigned threshold,
+ const V bound);
+
+ template< typename V >
+ void vertical_CRLA(const image2d<V>& input,
+ image2d<V>& output,
+ const unsigned threshold,
+ const V bound);
+
+ template< typename V >
+ void CRLA(const image2d<V>& input,
+ image2d<V>& output,
+ const unsigned hthreshold,
+ const unsigned hthreshold2,
+ const unsigned vthreshold,
+ const V bound);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ //---------------------------
+ // Horizontal CRLA
+ //---------------------------
+
+ template< typename V >
+ inline
+ void horizontal_CRLA(const image2d<V>& input,
+ image2d<V>& output,
+ const unsigned threshold,
+ const V bound)
+ {
+ unsigned count = 0;
+ unsigned nrows = input.nrows();
+ unsigned ncols = input.ncols();
+ V last_pixel_value = 0;
+
+ for (unsigned i = 0; i < nrows; ++i)
+ {
+ for (unsigned j = 0; j < ncols; ++j)
+ {
+ V current_pixel = input.at_(i, j);
+
+ if (current_pixel == 0)
+ {
+ if (last_pixel_value)
+ {
+ unsigned k = j + 1;
+ for (; input.at_(i, k) == 0 && (k < ncols); ++k);
+
+ count = k - j;
+ current_pixel = input.at_(i, k);
+
+ if (last_pixel_value == current_pixel &&
+ current_pixel <= bound &&
+ count < threshold)
+ for (unsigned l = j; l <= k; ++l)
+ output.at_(i, l) = last_pixel_value;
+
+ j = k;
+ output.at_(i, j) = current_pixel;
+ last_pixel_value = current_pixel;
+ }
+ }
+ else
+ {
+ output.at_(i, j) = current_pixel;
+ last_pixel_value = current_pixel;
+ }
+ }
+ }
+ }
+
+ //---------------------------
+ // Vertical CRLA
+ //---------------------------
+
+ template< typename V >
+ inline
+ void vertical_CRLA(const image2d<V>& input,
+ image2d<V>& output,
+ const unsigned threshold,
+ const V bound)
+ {
+ unsigned count = 0;
+ unsigned nrows = input.nrows();
+ unsigned ncols = input.ncols();
+ V last_pixel_value = 0;
+
+ for (unsigned j = 0; j < ncols; ++j)
+ {
+ for (unsigned i = 0; i < nrows; ++i)
+ {
+ V current_pixel = input.at_(i, j);
+
+ if (current_pixel == 0)
+ {
+ if (last_pixel_value)
+ {
+ unsigned k = i + 1;
+ for (; !input.at_(k, j) && (k < nrows); ++k);
+
+ count = k - i;
+ current_pixel = input.at_(k, j);
+
+ if (last_pixel_value == current_pixel &&
+ current_pixel <= bound &&
+ count < threshold)
+ for (unsigned l = i; l <= k; ++l)
+ output.at_(l, j) = last_pixel_value;
+
+ i = k;
+ output.at_(i, j) = current_pixel;
+ last_pixel_value = current_pixel;
+ }
+ }
+ else
+ {
+ output.at_(i, j) = current_pixel;
+ last_pixel_value = current_pixel;
+ }
+ }
+ }
+ }
+
+ //---------------------------
+ // CRLA
+ //---------------------------
+
+ template< typename V >
+ inline
+ void CRLA(const image2d<V>& input,
+ image2d<V>& output,
+ const unsigned hthreshold,
+ const unsigned hthreshold2,
+ const unsigned vthreshold,
+ const V bound)
+ {
+ const box2d& domain = input.domain();
+ image2d<V> himg(domain);
+ image2d<V> vimg(domain);
+ data::fill(himg, 0);
+ data::fill(vimg, 0);
+
+ horizontal_CRLA(input, himg, hthreshold, bound);
+ vertical_CRLA(input, vimg, vthreshold, bound);
+
+ data::fill((output |(pw::value(himg) == pw::value(vimg))).rw(), pw::value(himg));
+
+ horizontal_CRLA(output, output, hthreshold2, bound);
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::layout::internal::smearing
+
+ } // end of namespace scribo::layout::internal
+
+ } // end of namespace scribo::layout
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_LAYOUT_INTERNAL_SMEARING_CRLA_HH
+
diff --git a/scribo/scribo/layout/internal/smearing/draw_region.hh
b/scribo/scribo/layout/internal/smearing/draw_region.hh
new file mode 100644
index 0000000..ec1a415
--- /dev/null
+++ b/scribo/scribo/layout/internal/smearing/draw_region.hh
@@ -0,0 +1,121 @@
+// 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.
+
+/// \file
+///
+/// \brief Routines used in scribo::layout::smearing
+
+#ifndef SCRIBO_LAYOUT_INTERNAL_SMEARING_DRAW_REGION_HH
+# define SCRIBO_LAYOUT_INTERNAL_SMEARING_DRAW_REGION_HH
+
+# include <mln/core/image/image2d.hh>
+# include <scribo/layout/internal/smearing/bbox2d.hh>
+
+
+namespace scribo
+{
+
+ namespace layout
+ {
+
+ namespace internal
+ {
+
+ namespace smearing
+ {
+
+ using namespace mln;
+
+
+ template< typename V >
+ void remove_text_region(const bbox2d& bbox,
+ const image2d<bool>& input,
+ image2d<bool>& output,
+ image2d<V>& labeled_img_orig,
+ const image2d<V>& labeled_img);
+
+ template< typename V >
+ void draw_region(const bbox2d& bbox,
+ const image2d<bool>& input,
+ image2d<bool>& output,
+ image2d<V>& labeled_img);
+
+# ifndef MLN_INCLUDE_ONLY
+
+ template< typename V >
+ void remove_text_region(const bbox2d& bbox,
+ const image2d<bool>& input,
+ image2d<bool>& output,
+ image2d<V>& labeled_img_orig,
+ const image2d<V>& labeled_img)
+ {
+ const unsigned id = bbox.bbox_number();
+ box2d b(bbox.pmin(), bbox.pmax());
+ mln_piter_(box2d) p(b);
+
+ for_all(p)
+ {
+ const bool pixel = input(p);
+ const V label_pixel = labeled_img(p);
+
+ if (pixel && label_pixel == id)
+ {
+ output(p) = true;
+ labeled_img_orig(p) = 0;
+ }
+ }
+ }
+
+ template< typename V >
+ void draw_region(const bbox2d& bbox,
+ const image2d<bool>& input,
+ image2d<bool>& output,
+ image2d<V>& labeled_img)
+ {
+ const unsigned id = bbox.bbox_number();
+ box2d b(bbox.pmin(), bbox.pmax());
+ mln_piter_(box2d) p(b);
+
+ for_all(p)
+ {
+ const bool pixel = input(p);
+ const V label_pixel = labeled_img(p);
+
+ if (pixel && label_pixel == id)
+ output(p) = true;
+ }
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::layout::internal::smearing
+
+ } // end of namespace scribo::layout::internal
+
+ } // end of namespace scribo::layout
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_LAYOUT_INTERNAL_SMEARING_DRAW_REGION_HH
diff --git a/scribo/scribo/layout/internal/smearing/labeling.hh
b/scribo/scribo/layout/internal/smearing/labeling.hh
new file mode 100644
index 0000000..bc55741
--- /dev/null
+++ b/scribo/scribo/layout/internal/smearing/labeling.hh
@@ -0,0 +1,340 @@
+// 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.
+
+/// \file
+///
+/// \brief Routines used in scribo::layout::smearing
+
+#ifndef SCRIBO_LAYOUT_INTERNAL_SMEARING_LABELING_HH
+# define SCRIBO_LAYOUT_INTERNAL_SMEARING_LABELING_HH
+
+#include <mln/core/image/image2d.hh>
+#include <scribo/layout/internal/smearing/bbox2d.hh>
+
+
+namespace scribo
+{
+
+ namespace layout
+ {
+
+ namespace internal
+ {
+
+ namespace smearing
+ {
+
+
+ using namespace mln;
+
+ struct labeling_position
+ {
+ labeling_position(const int x_, const int y_);
+ int x;
+ int y;
+ };
+
+
+ template< typename V >
+ void propagate(const labeling_position& position,
+ std::vector< labeling_position >& next_position_list,
+ const image2d<V>& input, image2d<unsigned>& labeled_image,
+ const unsigned nb_labels, bbox2d& bbox, const V value);
+
+
+ template< typename V >
+ void component_labeling(const image2d<V>& input,
+ unsigned& nb_labels,
+ std::vector<bbox2d>& bbox_list,
+ image2d<unsigned>& labeled_image,
+ const V bound);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ //---------------------------
+ // Labeling position
+ //---------------------------
+
+ labeling_position::labeling_position(const int x_, const int y_)
+ : x(x_),
+ y(y_)
+ {
+ }
+
+ //---------------------------
+ // Propagate labeling
+ //---------------------------
+
+ template< typename V >
+ inline
+ void propagate(const labeling_position& position,
+ std::vector< labeling_position >& next_position_list,
+ const image2d<V>& input, image2d<unsigned>& labeled_image,
+ const unsigned nb_labels, bbox2d& bbox, const V value)
+ {
+ int x = position.x;
+ int y = position.y;
+ int nx;
+ int ny;
+ int ncols = input.ncols();
+ int nrows = input.nrows();
+
+ bool increment_xlr = false;
+ bool increment_ylr = false;
+ bool decrement_xul = false;
+ bool decrement_yul = false;
+
+ point2d& pmin = bbox.pmin();
+ point2d& pmax = bbox.pmax();
+ const int xul = pmin.col();
+ const int yul = pmin.row();
+ const int xlr = pmax.col();
+ const int ylr = pmax.row();
+
+ {
+ nx = x + 1;
+
+ if (nx < ncols)
+ {
+ unsigned& pixel = labeled_image.at_(y, nx);
+ if (input.at_(y, nx) == value && !pixel)
+ {
+ if (xlr < nx)
+ increment_xlr = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(nx, y));
+ }
+ }
+ }
+
+ {
+ nx = x + 1;
+ ny = y + 1;
+
+ if (nx < ncols && ny < nrows)
+ {
+ unsigned& pixel = labeled_image.at_(ny, nx);
+ if (input.at_(ny, nx) == value && !pixel)
+ {
+ if (xlr < nx)
+ increment_xlr = true;
+ if (ylr < ny)
+ increment_ylr = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(nx, ny));
+ }
+ }
+ }
+
+ {
+ ny = y + 1;
+
+ if (ny < nrows)
+ {
+ unsigned& pixel = labeled_image.at_(ny, x);
+ if (input.at_(ny, x) == value && !pixel)
+ {
+ if (ylr < ny)
+ increment_ylr = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(x, ny));
+ }
+ }
+ }
+
+ {
+ nx = x - 1;
+ ny = y + 1;
+
+ if (nx >= 0 && ny < nrows)
+ {
+ unsigned& pixel = labeled_image.at_(ny, nx);
+ if (input.at_(ny, nx) == value && !pixel)
+ {
+ if (xul > nx)
+ decrement_xul = true;
+ if (ylr < ny)
+ increment_ylr = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(nx, ny));
+ }
+ }
+ }
+
+ {
+ nx = x - 1;
+
+ if (nx >= 0)
+ {
+ unsigned& pixel = labeled_image.at_(y, nx);
+ if (input.at_(y, nx) == value && !pixel)
+ {
+ if (xul > nx)
+ decrement_xul = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(nx, y));
+ }
+ }
+ }
+
+ {
+ nx = x - 1;
+ ny = y - 1;
+
+ if (nx >= 0 && ny >= 0)
+ {
+ unsigned& pixel = labeled_image.at_(ny, nx);
+ if (input.at_(ny, nx) == value && !pixel)
+ {
+ if (xul > nx)
+ decrement_xul = true;
+ if (yul > ny)
+ decrement_yul = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(nx, ny));
+ }
+ }
+ }
+
+ {
+ ny = y - 1;
+
+ if (ny >= 0)
+ {
+ unsigned& pixel = labeled_image.at_(ny, x);
+ if (input.at_(ny, x) == value && !pixel)
+ {
+ if (yul > ny)
+ decrement_yul = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(x, ny));
+ }
+ }
+ }
+
+ {
+ nx = x + 1;
+ ny = y - 1;
+
+ if (nx < ncols && ny >= 0)
+ {
+ unsigned& pixel = labeled_image.at_(ny, nx);
+ if (input.at_(ny, nx) == value && !pixel)
+ {
+ if (xlr < nx)
+ increment_xlr = true;
+ if (yul > ny)
+ decrement_yul = true;
+ pixel = nb_labels;
+ next_position_list.push_back(labeling_position(nx, ny));
+ }
+ }
+ }
+
+ if (increment_xlr)
+ ++pmax.col();
+ if (increment_ylr)
+ ++pmax.row();
+ if (decrement_xul)
+ --pmin.col();
+ if (decrement_yul)
+ --pmin.row();
+ }
+
+ //---------------------------
+ // Component labeling
+ //---------------------------
+
+ template< typename V >
+ inline
+ void component_labeling(const image2d<V>& input,
+ unsigned& nb_labels,
+ std::vector<bbox2d>& bbox_list,
+ image2d<unsigned>& labeled_image,
+ const V bound)
+ {
+ std::vector<labeling_position> position_list;
+ std::vector<labeling_position> next_position_list;
+ unsigned nrows = input.nrows();
+ unsigned ncols = input.ncols();
+
+ next_position_list.reserve(8);
+
+ for (unsigned i = 0; i < nrows; ++i)
+ {
+ for (unsigned j = 0; j < ncols; ++j)
+ {
+ if (input.at_(i, j) && !labeled_image.at_(i, j))
+ {
+ V value = input.at_(i, j);
+
+ if (value <= bound)
+ {
+ ++nb_labels;
+ position_list.clear();
+ position_list.push_back(labeling_position(j, i));
+
+ labeled_image.at_(i, j) = nb_labels;
+
+ point2d p(i, j);
+ bbox2d bbox(nb_labels, p, p);
+
+ while (!position_list.empty())
+ {
+ unsigned nb_positions = position_list.size();
+ next_position_list.clear();
+
+ for (unsigned k = 0; k < nb_positions; ++k)
+ {
+ const labeling_position& position = position_list[k];
+
+ propagate(position, next_position_list, input,
+ labeled_image, nb_labels, bbox, value);
+ }
+
+ position_list.clear();
+ position_list = next_position_list;
+ }
+
+ bbox_list.push_back(bbox);
+ }
+ }
+ }
+ }
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::layout::internal::smearing
+
+ } // end of namespace scribo::layout::internal
+
+ } // end of namespace scribo::layout
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_LAYOUT_INTERNAL_SMEARING_LABELING_HH
diff --git a/scribo/scribo/layout/smearing.hh b/scribo/scribo/layout/smearing.hh
new file mode 100644
index 0000000..2574876
--- /dev/null
+++ b/scribo/scribo/layout/smearing.hh
@@ -0,0 +1,223 @@
+// 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.
+
+/// \file
+///
+/// \brief Smearing layout analysis algorithm.
+
+#ifndef SCRIBO_LAYOUT_SMEARING_HH
+# define SCRIBO_LAYOUT_SMEARING_HH
+
+# include <mln/core/image/image2d.hh>
+# include <mln/util/couple.hh>
+
+# include <scribo/layout/internal/smearing/crla.hh>
+# include <scribo/layout/internal/smearing/labeling.hh>
+# include <scribo/layout/internal/smearing/classification.hh>
+# include <scribo/layout/internal/smearing/draw_region.hh>
+
+
+namespace scribo
+{
+
+ namespace layout
+ {
+
+ using namespace mln;
+
+ /*! \brief The Smearing layout analysis algorithm. This algorithm
+ is an implementation according to paper \cite ogorman1993pami.
+
+ \param[in] A binary image.
+
+ \return A couple of binary images. The first one contains the
+ text components and the second one the non-text components.
+
+ \ingroup grpalgolayout
+ */
+ template <typename I>
+ util::couple<mln_concrete(I), mln_concrete(I)>
+ smearing(const Image<I>& input);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ template <typename I>
+ util::couple<mln_concrete(I), mln_concrete(I)>
+ smearing(const Image<I>& input_)
+ {
+ mln_trace("scribo::layout::smearing");
+
+ using namespace internal::smearing;
+
+ typedef mln_value(I) V;
+ mlc_is(V,bool)::check();
+
+ const I& input = exact(input_);
+ mln_precondition(input.is_valid());
+
+ //-------------------------
+ // Initialization
+ //-------------------------
+
+ const box2d& domain = input.domain();
+ image2d<bool> output_text(domain);
+ image2d<bool> output_non_text(domain);
+ data::fill(output_text, false);
+ data::fill(output_non_text, false);
+
+
+ //-------------------------
+ // Component labeling
+ //-------------------------
+
+ std::vector<bbox2d> bbox_list;
+ image2d<unsigned> labeled_img(domain);
+ data::fill(labeled_img, 0);
+ unsigned nlabels = 0;
+
+ internal::smearing::component_labeling(input, nlabels,
+ bbox_list, labeled_img, true);
+
+ //-------------------------
+ // Classification
+ //-------------------------
+
+ std::vector<bbox2d>::iterator it_begin = bbox_list.begin();
+ std::vector<bbox2d>::iterator it_end = bbox_list.end();
+
+ for (; it_begin != it_end; ++it_begin)
+ {
+ const bbox2d& bbox = (*it_begin);
+ const unsigned height = bbox.pmax().row() - bbox.pmin().row();
+ unsigned c = 0;
+
+ if (height < 120)
+ c = 1;
+ else if (height >= 120 && height < 360)
+ c = 2;
+ else
+ c = 3;
+
+ internal::smearing::classify_component(bbox, c, labeled_img);
+ }
+
+ //-------------------------
+ // First pass
+ //-------------------------
+
+ {
+ image2d<unsigned> output_CRLA(domain);
+ data::fill(output_CRLA, 0);
+ const unsigned bound = 1;
+
+ internal::smearing::CRLA(labeled_img, output_CRLA, 360, 15, 360, bound);
+
+ image2d<unsigned> f_labeled_img(domain);
+ data::fill(f_labeled_img, 0);
+ nlabels = 0;
+ bbox_list.clear();
+
+ internal::smearing::component_labeling(output_CRLA, nlabels,
+ bbox_list, f_labeled_img,
+ bound);
+
+ it_begin = bbox_list.begin();
+ it_end = bbox_list.end();
+
+ for (; it_begin != it_end; ++it_begin)
+ {
+ float brl = 0.0f;
+ float tc = 0.0f;
+ const bbox2d& bbox = (*it_begin);
+ const unsigned width = bbox.pmax().col() - bbox.pmin().col();
+
+ internal::smearing::compute_region_info(input, f_labeled_img,
+ bbox, brl, tc);
+
+ const float mbrl = brl / tc;
+ const float mtc = tc / width;
+
+ if (mbrl >= 0.4f && mbrl <= 15.0f && mtc >= 1.0f &&
mtc <= 3.8f)
+ internal::smearing::remove_text_region(bbox, input, output_text,
+ labeled_img, f_labeled_img);
+ }
+ }
+
+ //-------------------------
+ // Second pass
+ //-------------------------
+
+ {
+ image2d<unsigned> output_CRLA(domain);
+ data::fill(output_CRLA, 0);
+ const unsigned bound = 2;
+
+ internal::smearing::CRLA(labeled_img, output_CRLA, 360, 60, 360, bound);
+
+ image2d<unsigned> s_labeled_img(domain);
+ data::fill(s_labeled_img, 0);
+ nlabels = 0;
+ bbox_list.clear();
+
+ internal::smearing::component_labeling(output_CRLA, nlabels,
+ bbox_list, s_labeled_img,
+ bound);
+
+ it_begin = bbox_list.begin();
+ it_end = bbox_list.end();
+
+ for (; it_begin != it_end; ++it_begin)
+ {
+ float brl = 0.0f;
+ float tc = 0.0f;
+ const bbox2d& bbox = (*it_begin);
+ const unsigned width = bbox.pmax().col() - bbox.pmin().col();
+
+ internal::smearing::compute_region_info(input, s_labeled_img,
+ bbox, brl, tc);
+
+ const float mbrl = brl / tc;
+ const float mtc = tc / width;
+
+ if (mbrl >= 2.2f && mbrl <= 45.0f && mtc >= 1.2f &&
mtc <= 9.0f)
+ internal::smearing::draw_region(bbox, input, output_text,
+ s_labeled_img);
+ else
+ internal::smearing::draw_region(bbox, input, output_non_text,
+ s_labeled_img);
+ }
+ }
+
+ return mln::make::couple(output_text, output_non_text);
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::layout
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_LAYOUT_SMEARING_HH
diff --git a/scribo/tests/layout/Makefile.am b/scribo/tests/layout/Makefile.am
index 09990b1..0eab5d5 100644
--- a/scribo/tests/layout/Makefile.am
+++ b/scribo/tests/layout/Makefile.am
@@ -1,5 +1,4 @@
-# Copyright (C) 2013 EPITA Research and Development
-# Laboratory (LRDE).
+# Copyright (C) 2013 EPITA Research and Development Laboratory (LRDE).
#
# This file is part of Olena.
#
@@ -17,6 +16,11 @@
include $(top_srcdir)/scribo/tests/tests.mk
-check_PROGRAMS = xy_cut
+check_PROGRAMS = smearing \
+ xy_cut
+
+
+EXTRA_DIST = smearing-nontext.pbm \
+ smearing-text.pbm
TESTS = $(check_PROGRAMS)
diff --git a/scribo/tests/layout/smearing-nontext.pbm
b/scribo/tests/layout/smearing-nontext.pbm
new file mode 100644
index 0000000..0bffbcf
--- /dev/null
+++ b/scribo/tests/layout/smearing-nontext.pbm
@@ -0,0 +1,5 @@
+P4
+# Generated by Milena 1.0
http://olena.lrde.epita.fr
+# EPITA Research and Development Laboratory (LRDE)
+436 216
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð
\ No newline at end of file
diff --git a/scribo/tests/img/alignment_3.pbm b/scribo/tests/layout/smearing-text.pbm
similarity index 95%
copy from scribo/tests/img/alignment_3.pbm
copy to scribo/tests/layout/smearing-text.pbm
index 89ec978..1975c01 100644
Binary files a/scribo/tests/img/alignment_3.pbm and
b/scribo/tests/layout/smearing-text.pbm differ
diff --git a/scribo/tests/layout/xy_cut.cc b/scribo/tests/layout/smearing.cc
similarity index 78%
copy from scribo/tests/layout/xy_cut.cc
copy to scribo/tests/layout/smearing.cc
index 3601d64..d5185db 100644
--- a/scribo/tests/layout/xy_cut.cc
+++ b/scribo/tests/layout/smearing.cc
@@ -28,22 +28,25 @@
#include <iostream>
#include <mln/io/pbm/load.hh>
#include <mln/make/box2d.hh>
-#include <scribo/layout/xy_cut.hh>
+#include <mln/data/compare.hh>
+#include <scribo/layout/smearing.hh>
#include "tests/data.hh"
using namespace mln;
-int main(int argc, char** argv)
+int main()
{
image2d<bool> input;
io::pbm::load(input, SCRIBO_IMG_DIR "/alignment_3.pbm");
- util::array<box2d> bb = scribo::layout::xy_cut(input, 10, 10);
+ util::couple<image2d<bool>, image2d<bool> >
+ res = scribo::layout::smearing(input);
- mln_assertion(bb.nelements() == 4);
- mln_assertion(bb[0] == make::box2d(20,66, 38,140));
- mln_assertion(bb[1] == make::box2d(11,171, 38,221));
- mln_assertion(bb[2] == make::box2d(0,237, 38,384));
- mln_assertion(bb[3] == make::box2d(50,60, 215,402));
+ image2d<bool> ref1, ref2;
+ io::pbm::load(ref1, SCRIBO_TESTS_DIR "/layout/smearing-text.pbm");
+ io::pbm::load(ref2, SCRIBO_TESTS_DIR "/layout/smearing-nontext.pbm");
+
+ mln_assertion(ref1 == res.first());
+ mln_assertion(ref2 == res.second());
}
--
1.7.2.5