Olena-patches
Threads by month
- ----- 2025 -----
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- 9625 discussions
* core/anchors.hh: New. Add anchor types.
* filter/objects_thin.hh: Use filter::internal::compute.
* primitive/link/internal/find_several_links.hh,
* primitive/link/internal/find_link.hh,
* primitive/link/compute.hh: Update functor interface in order to
specify anchors.
* primitive/link/compute_several.hh: Fix an invalid namespace.
* primitive/link/internal/anchors_3.hh: Move...
* primitive/link/internal/compute_anchor.hh: ... here.
* primitive/link/internal/link_functor_base.hh: Use anchor type.
* primitive/link/internal/link_several_dmax_base.hh: New.
* primitive/link/internal/link_center_dmax_base.hh,
* primitive/link/internal/link_center_dmax_ratio_base.hh: Removed.
* primitive/link/with_several_right_links_overlap.hh: Fix includes.
* primitive/link/with_single_right_link_bottom.hh,
* primitive/link/with_single_right_link_top.hh,
* primitive/link/with_single_left_link.hh,
* primitive/link/with_single_right_link.hh: Use
link_single_dmax_base functor.
* src/debug/show_links_several_right_overlap.cc,
* src/debug/show_links_single_left.cc,
* src/debug/show_links_single_left_dmax_ratio.cc,
* src/debug/show_links_single_right.cc,
* src/debug/show_links_single_right_dmax_ratio.cc: Update functor
interface.
---
scribo/ChangeLog | 41 +++
scribo/{util/all.hh => core/anchors.hh} | 26 +-
scribo/filter/objects_thin.hh | 7 +-
scribo/primitive/link/compute.hh | 39 ++-
scribo/primitive/link/compute_several.hh | 2 +-
.../internal/{anchors_3.hh => compute_anchor.hh} | 56 +++--
scribo/primitive/link/internal/find_link.hh | 14 +-
.../primitive/link/internal/find_several_links.hh | 102 +++++----
.../primitive/link/internal/link_functor_base.hh | 56 +++--
.../link/internal/link_several_dmax_base.hh | 265 ++++++++++++++++++++
...enter_dmax_base.hh => link_single_dmax_base.hh} | 57 ++---
...atio_base.hh => link_single_dmax_ratio_base.hh} | 43 ++--
.../link/with_several_right_links_overlap.hh | 10 +-
scribo/primitive/link/with_single_left_link.hh | 6 +-
scribo/primitive/link/with_single_right_link.hh | 9 +-
.../link/with_single_right_link_bottom.hh | 13 +-
.../primitive/link/with_single_right_link_top.hh | 17 +-
.../src/debug/show_links_several_right_overlap.cc | 51 ++--
scribo/src/debug/show_links_single_left.cc | 4 +-
.../src/debug/show_links_single_left_dmax_ratio.cc | 4 +-
scribo/src/debug/show_links_single_right.cc | 4 +-
.../debug/show_links_single_right_dmax_ratio.cc | 4 +-
22 files changed, 586 insertions(+), 244 deletions(-)
copy scribo/{util/all.hh => core/anchors.hh} (82%)
rename scribo/primitive/link/internal/{anchors_3.hh => compute_anchor.hh} (75%)
create mode 100644 scribo/primitive/link/internal/link_several_dmax_base.hh
rename scribo/primitive/link/internal/{link_center_dmax_base.hh => link_single_dmax_base.hh} (73%)
rename scribo/primitive/link/internal/{link_center_dmax_ratio_base.hh => link_single_dmax_ratio_base.hh} (79%)
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 0fcd35f..2897a58 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,44 @@
+2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Improve linking routines.
+
+ * core/anchors.hh: New. Add anchor types.
+
+ * filter/objects_thin.hh: Use filter::internal::compute.
+
+ * primitive/link/internal/find_several_links.hh,
+ * primitive/link/internal/find_link.hh,
+ * primitive/link/compute.hh: Update functor interface in order to
+ specify anchors.
+
+ * primitive/link/compute_several.hh: Fix an invalid namespace.
+
+ * primitive/link/internal/anchors_3.hh: Move...
+
+ * primitive/link/internal/compute_anchor.hh: ... here.
+
+ * primitive/link/internal/link_functor_base.hh: Use anchor type.
+
+ * primitive/link/internal/link_several_dmax_base.hh: New.
+
+ * primitive/link/internal/link_center_dmax_base.hh,
+ * primitive/link/internal/link_center_dmax_ratio_base.hh: Removed.
+
+ * primitive/link/with_several_right_links_overlap.hh: Fix includes.
+
+ * primitive/link/with_single_right_link_bottom.hh,
+ * primitive/link/with_single_right_link_top.hh,
+ * primitive/link/with_single_left_link.hh,
+ * primitive/link/with_single_right_link.hh: Use
+ link_single_dmax_base functor.
+
+ * src/debug/show_links_several_right_overlap.cc,
+ * src/debug/show_links_single_left.cc,
+ * src/debug/show_links_single_left_dmax_ratio.cc,
+ * src/debug/show_links_single_right.cc,
+ * src/debug/show_links_single_right_dmax_ratio.cc: Update functor
+ interface.
+
2009-12-11 Guillaume Lazzara <z(a)lrde.epita.fr>
Improve Sauvola Multi-scale.
diff --git a/scribo/util/all.hh b/scribo/core/anchors.hh
similarity index 82%
copy from scribo/util/all.hh
copy to scribo/core/anchors.hh
index 3eef9ff..a2994b1 100644
--- a/scribo/util/all.hh
+++ b/scribo/core/anchors.hh
@@ -23,24 +23,26 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef SCRIBO_UTIL_ALL_HH
-# define SCRIBO_UTIL_ALL_HH
-
-/// \file
-///
-/// Include all headers located in scribo/util.
+#ifndef SCRIBO_CORE_ANCHORS_HH
+# define SCRIBO_CORE_ANCHORS_HH
namespace scribo
{
- /// Namespace of useful classes and routines.
- namespace util
+ namespace anchor
{
- } // end of namespace scribo::util
+ enum Type
+ {
+ MassCenter = 0,
+ Top,
+ Bottom,
+ Center,
+ Invalid
+ };
-} // end of namespace scribo
+ }
-# include <scribo/util/text.hh>
+} // end of namespace scribo
-#endif // ! SCRIBO_UTIL_ALL_HH
+#endif // ! SCRIBO_CORE_ANCHORS_HH
diff --git a/scribo/filter/objects_thin.hh b/scribo/filter/objects_thin.hh
index e9c869b..ea8a5af 100644
--- a/scribo/filter/objects_thin.hh
+++ b/scribo/filter/objects_thin.hh
@@ -39,6 +39,7 @@
# include <scribo/core/object_image.hh>
# include <scribo/primitive/extract/objects.hh>
+# include <scribo/filter/internal/compute.hh>
namespace scribo
{
@@ -91,8 +92,6 @@ namespace scribo
struct objects_thin_filter
: Function_v2b< objects_thin_filter<L> >
{
- typedef accu::shape::bbox<mln_psite(L)> box_accu_t;
-
/// Constructor
///
/// \param[in] objects object bounding boxes.
@@ -175,9 +174,7 @@ namespace scribo
typedef internal::objects_thin_filter<L> func_t;
func_t is_not_too_thin(objects, min_thickness);
- object_image(L) output;
- output.init_from_(objects);
- output.relabel(is_not_too_thin);
+ object_image(L) output = internal::compute(objects, is_not_too_thin);
trace::exiting("scribo::filter::objects_thin");
return output;
diff --git a/scribo/primitive/link/compute.hh b/scribo/primitive/link/compute.hh
index bf94787..349721c 100644
--- a/scribo/primitive/link/compute.hh
+++ b/scribo/primitive/link/compute.hh
@@ -55,39 +55,44 @@ namespace scribo
Functors must implement the following interface :
- bool is_potential_link(unsigned current_object,
+ bool is_potential_link_(unsigned current_object,
const P& start_point, const P& p) const
- bool valid_link(unsigned current_object,
+ bool valid_link_(unsigned current_object,
const P& start_point, const P& p)
- bool verify_link_criterion(unsigned current_object,
+ bool verify_link_criterion_(unsigned current_object,
const P& start_point, const P& p)
- void validate_link(unsigned current_object,
- const P& start_point, const P& p)
+ void validate_link_(unsigned current_object,
+ const P& start_point, const P& p, unsigned anchor)
- void invalidate_link(unsigned current_object,
- const P& start_point, const P& p)
+ void invalidate_link_(unsigned current_object,
+ const P& start_point, const P& p, unsigned anchor)
- void compute_next_site(P& p)
+ void compute_next_site_(P& p)
- const mln_site(L)& start_point(unsigned current_object)
+ const mln_site(L)& start_point_(unsigned current_object, unsigned anchor)
- void start_processing_object(unsigned current_object)
+ void start_processing_object_(unsigned current_object)
*/
template <typename F>
object_links<scribo_support(F)>
- compute(Link_Functor<F>& functor);
+ compute(Link_Functor<F>& functor, anchor::Type anchor);
+ /// \overload
+ /// The default anchor is set to 0, the mass center.
+ template <typename F>
+ object_links<scribo_support(F)>
+ compute(Link_Functor<F>& functor);
# ifndef MLN_INCLUDE_ONLY
template <typename F>
object_links<scribo_support(F)>
- compute(Link_Functor<F>& functor_)
+ compute(Link_Functor<F>& functor_, anchor::Type anchor)
{
trace::entering("scribo::primitive::link::compute");
@@ -96,7 +101,7 @@ namespace scribo
for_all_ncomponents(current_object, functor.objects().nlabels())
{
functor.start_processing_object(current_object); //<-- start_processing_object
- primitive::internal::find_link(functor, current_object);
+ primitive::internal::find_link(functor, current_object, anchor);
}
trace::exiting("scribo::primitive::link::compute");
@@ -104,6 +109,14 @@ namespace scribo
}
+ template <typename F>
+ object_links<scribo_support(F)>
+ compute(Link_Functor<F>& functor)
+ {
+ return compute(functor, anchor::MassCenter);
+ }
+
+
# endif // ! MLN_INCLUDE_ONLY
} // end of namespace scribo::primitive::link
diff --git a/scribo/primitive/link/compute_several.hh b/scribo/primitive/link/compute_several.hh
index 0597d99..929a7cb 100644
--- a/scribo/primitive/link/compute_several.hh
+++ b/scribo/primitive/link/compute_several.hh
@@ -98,7 +98,7 @@ namespace scribo
for_all_ncomponents(current_object, functor.objects().nlabels())
{
functor.start_processing_object(current_object); //<-- start_processing_object
- primitive::internal::find_several_links(functor, current_object);
+ internal::find_several_links(functor, current_object);
}
trace::exiting("scribo::primitive::link::compute_several");
diff --git a/scribo/primitive/link/internal/anchors_3.hh b/scribo/primitive/link/internal/compute_anchor.hh
similarity index 75%
rename from scribo/primitive/link/internal/anchors_3.hh
rename to scribo/primitive/link/internal/compute_anchor.hh
index 85fa80b..c7bae11 100644
--- a/scribo/primitive/link/internal/anchors_3.hh
+++ b/scribo/primitive/link/internal/compute_anchor.hh
@@ -23,14 +23,15 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef SCRIBO_PRIMITIVE_LINK_INTERNAL_ANCHORS_3_HH
-# define SCRIBO_PRIMITIVE_LINK_INTERNAL_ANCHORS_3_HH
+#ifndef SCRIBO_PRIMITIVE_LINK_INTERNAL_COMPUTE_ANCHOR_HH
+# define SCRIBO_PRIMITIVE_LINK_INTERNAL_COMPUTE_ANCHOR_HH
/// \file
///
-/// Routine providing 3 anchors for neighbor seeking.
+/// Routine providing anchors for neighbor seeking.
# include <mln/math/min.hh>
+# include <mln/util/array.hh>
# include <scribo/core/object_image.hh>
@@ -46,20 +47,20 @@ namespace scribo
namespace internal
{
+ using namespace mln;
/*! \brief Return the proper anchor used to find a neighbor.
- This routine provides up to 3 different anchors.
-
\param[in] objects An object image.
\param[in] mass_centers Object mass centers.
\param[in] current_object An object id.
\param[in] anchor The expected anchor.
Anchor can take one of the following values:
- - 0, top anchor.
- - 1, center anchor. It is the mass center.
- - 2, bottom anchor.
+ - anchor::MassCenter, mass center anchor.
+ - anchor::Top, top anchor.
+ - anchor::Bottom, bottom anchor.
+ - anchor::Center, center anchor.
Top and bottom anchors are respectively computed from the
@@ -77,53 +78,56 @@ namespace scribo
*/
template <typename L, typename P>
mln_site(L)
- anchors_3(const object_image(L)& objects,
- const mln::util::array<P>& mass_centers,
- unsigned current_object, unsigned anchor);
+ compute_anchor(const object_image(L)& objects,
+ const mln::util::array<P>& mass_centers,
+ unsigned current_object, anchor::Type anchor);
# ifndef MLN_INCLUDE_ONLY
template <typename L, typename P>
mln_site(L)
- anchors_3(const object_image(L)& objects,
- const mln::util::array<P>& mass_centers,
- unsigned current_object, unsigned anchor)
+ compute_anchor(const object_image(L)& objects,
+ const mln::util::array<P>& mass_centers,
+ unsigned current_object, anchor::Type anchor)
{
unsigned h = objects.bbox(current_object).pmax().row()
- objects.bbox(current_object).pmin().row();
mln_site(L) sp = objects.bbox(current_object).center();
- def::coord r;
+ def::coord r = 0;
switch (anchor)
{
+ // Masss Center
+ case anchor::MassCenter:
+ return mass_centers(current_object);
+
+
// Top
- case 0:
+ case anchor::Top:
if (h < 30)
r = objects.bbox(current_object).pmin().row()
+ math::min(2u, (h + 1) / 2 - 1);
else
r = objects.bbox(current_object).pmin().row()
- - math::min(10u, h /10);
+ + math::min(10u, h /10);
break;
- // Center
- case 1:
- return mass_centers(current_object);
-
-
- // Bottom
- case 2:
+ // Bottom
+ case anchor::Bottom:
if (h < 30)
r = objects.bbox(current_object).pmax().row()
- + math::min(2u, (h + 1) / 2 - 1);
+ - math::min(2u, (h + 1) / 2 - 1);
else
r = objects.bbox(current_object).pmax().row()
- math::min(10u, h /10);
break;
+ case anchor::Center:
+ return objects.bbox(current_object).center();
+
default:
trace::warning("Non handled anchor");
mln_assertion(anchor > 2);
@@ -143,4 +147,4 @@ namespace scribo
} // end of namespace scribo
-#endif // ! SCRIBO_PRIMITIVE_LINK_INTERNAL_ANCHORS_3_HH
+#endif // ! SCRIBO_PRIMITIVE_LINK_INTERNAL_COMPUTE_ANCHOR_HH
diff --git a/scribo/primitive/link/internal/find_link.hh b/scribo/primitive/link/internal/find_link.hh
index 8a2544e..b950280 100644
--- a/scribo/primitive/link/internal/find_link.hh
+++ b/scribo/primitive/link/internal/find_link.hh
@@ -38,6 +38,7 @@
# include <mln/util/couple.hh>
# include <scribo/core/concept/link_functor.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/primitive/internal/update_link_array.hh>
@@ -59,27 +60,30 @@ namespace scribo
\param[in,out] functor Functor used to compute the
links. Stores the results.
\param[in] current_object Current object id.
+ \param[in] anchor The lookup anchor.
\return A couple. The first argument tells whether a valid
link has been found, the second one is link anchor if exists.
*/
template <typename F>
mln::util::couple<bool, mln_site(scribo_support_(F))>
- find_link(Link_Functor<F>& functor, unsigned current_object);
+ find_link(Link_Functor<F>& functor, unsigned current_object,
+ anchor::Type anchor);
# ifndef MLN_INCLUDE_ONLY
template <typename F>
mln::util::couple<bool, mln_site(scribo_support_(F))>
- find_link(Link_Functor<F>& functor_, unsigned current_object)
+ find_link(Link_Functor<F>& functor_, unsigned current_object,
+ anchor::Type anchor)
{
F& functor = exact(functor_);
functor.initialize_link(current_object); // <-- initialize_link
mln_site(scribo_support_(F))
- start_point = functor.start_point(current_object),
+ start_point = functor.start_point(current_object, anchor), // <-- start_point
p = start_point;
mln_postcondition(p == start_point);
@@ -93,9 +97,9 @@ namespace scribo
functor.compute_next_site(p); // <-- compute_next_site
if (functor.valid_link(current_object, start_point, p)) // <-- valid_link
- functor.validate_link(current_object, start_point, p); // <-- validate_link
+ functor.validate_link(current_object, start_point, p, anchor); // <-- validate_link
else
- functor.invalidate_link(current_object, start_point, p); // <-- invalidate_link
+ functor.invalidate_link(current_object, start_point, p, anchor); // <-- invalidate_link
functor.finalize_link(current_object); // <-- finalize_link
diff --git a/scribo/primitive/link/internal/find_several_links.hh b/scribo/primitive/link/internal/find_several_links.hh
index a07ee70..a690f10 100644
--- a/scribo/primitive/link/internal/find_several_links.hh
+++ b/scribo/primitive/link/internal/find_several_links.hh
@@ -29,6 +29,9 @@
/// \file
///
/// Find the neighbor of a line of text if exists.
+///
+/// \fixme do not iterate over the number of anchor types but use a
+/// set of anchors specified in the functor.
# include <mln/core/concept/image.hh>
@@ -38,6 +41,7 @@
# include <mln/util/couple.hh>
# include <scribo/core/concept/link_functor.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/primitive/internal/update_link_array.hh>
@@ -51,70 +55,82 @@ namespace scribo
namespace primitive
{
- namespace internal
+ namespace link
{
- /*! Find the neighbor of a line of text if exists.
+ namespace internal
+ {
+
+ /*! Find the neighbor of a line of text if exists.
- \param[in,out] functor Functor used to compute the
- links. Stores the results.
- \param current_object Current object id.
+ \param[in,out] functor Functor used to compute the
+ links. Stores the results.
+ \param current_object Current object id.
- \return A couple. The first argument tells whether a valid
- link has been found, the second one is link anchor if exists.
- */
- template <typename F>
- mln::util::couple<bool, mln_site(scribo_support_(F))>
- find_several_links(Link_Functor<F>& functor,
- unsigned current_object);
+ \return A couple. The first argument tells whether a valid
+ link has been found, the second one is link anchor if exists.
+ */
+ template <typename F>
+ mln::util::couple<bool,
+ mln::util::couple<anchor::Type,
+ mln_site(scribo_support_(F))> >
+ find_several_links(Link_Functor<F>& functor,
+ unsigned current_object);
# ifndef MLN_INCLUDE_ONLY
- template <typename F>
- mln::util::couple<bool, mln_site(scribo_support_(F))>
- find_several_links(Link_Functor<F>& functor_,
- unsigned current_object)
- {
- F& functor = exact(functor_);
+ template <typename F>
+ mln::util::couple<bool,
+ mln::util::couple<anchor::Type,
+ mln_site(scribo_support_(F))> >
+ find_several_links(Link_Functor<F>& functor_,
+ unsigned current_object)
+ {
+ F& functor = exact(functor_);
- typedef mln_site(scribo_support_(F)) P;
- typedef mln::util::couple<unsigned, P> link_t;
+ typedef mln_site(scribo_support_(F)) P;
+ typedef mln::util::couple<anchor::Type, P> link_t;
- P lp = functor.initialize_link(current_object); // <-- initialize_link
+ functor.initialize_link(current_object); // <-- initialize_link
- for (unsigned anchor = 0; anchor < functor.nanchors(); ++anchor) // <-- nanchor
- {
- mln_site(scribo_support_(F))
- start_point = functor.start_point(current_object, anchor), // <-- start_point
- p = start_point;
+ for (unsigned anchor_ = 0; anchor_ < functor.nanchors(); ++anchor_) // <-- nanchor
+ {
+ // FIXME : See fixme at the beginning of this file.
+ anchor::Type anchor = static_cast<anchor::Type>(anchor_);
- mln_postcondition(p == start_point);
+ mln_site(scribo_support_(F))
+ start_point = functor.start_point(current_object, anchor), // <-- start_point
+ p = start_point;
- while (functor.objects().domain().has(p)
- && ! functor.is_potential_link(current_object,
- start_point, p) // <-- is_potential_link
- && functor.verify_link_criterion(current_object,
- start_point, p)) // <-- verify_link_criterion
- functor.compute_next_site(p); // <-- compute_next_site
+ mln_postcondition(p == start_point);
- if (functor.valid_link(current_object, start_point, p)) // <-- valid_link
- functor.validate_link(current_object, start_point, p, anchor); // <-- validate_link
- else
- functor.invalidate_link(current_object, start_point, p, anchor); // <-- invalidate_link
+ while (functor.objects().domain().has(p)
+ && ! functor.is_potential_link(current_object,
+ start_point, p) // <-- is_potential_link
+ && functor.verify_link_criterion(current_object,
+ start_point, p)) // <-- verify_link_criterion
+ functor.compute_next_site(p); // <-- compute_next_site
- }
+ if (functor.valid_link(current_object, start_point, p)) // <-- valid_link
+ functor.validate_link(current_object, start_point, p, anchor); // <-- validate_link
+ else
+ functor.invalidate_link(current_object, start_point, p, anchor); // <-- invalidate_link
- P lp = functor.finalize_link(current_object);
+ }
- bool b = (functor.link(current_object) != current_object); // <-- link
- return mln::make::couple(b, lp);
- }
+ link_t l = functor.finalize_link(current_object);
+
+ bool b = (functor.link(current_object) != current_object); // <-- link
+ return mln::make::couple(b, l);
+ }
# endif // MLN_INCLUDE_ONLY
- } // end of namespace scribo::primitive::internal
+ } // end of namespace scribo::primitive::link::internal
+
+ } // end of namespace scribo::primitive::link
} // end of namespace scribo::primitive
diff --git a/scribo/primitive/link/internal/link_functor_base.hh b/scribo/primitive/link/internal/link_functor_base.hh
index e35f52d..dcc43d4 100644
--- a/scribo/primitive/link/internal/link_functor_base.hh
+++ b/scribo/primitive/link/internal/link_functor_base.hh
@@ -37,14 +37,18 @@
# include <mln/util/array.hh>
# include <mln/util/couple.hh>
+# include <mln/labeling/compute.hh>
+# include <mln/accu/center.hh>
+
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/core/concept/link_functor.hh>
+# include <scribo/primitive/link/internal/compute_anchor.hh>
# include <scribo/primitive/internal/update_link_array.hh>
# include <scribo/primitive/internal/init_link_array.hh>
# include <scribo/primitive/internal/is_invalid_link.hh>
-
# define scribo_support(T) typename T::support
# define scribo_support_(T) T::support
@@ -68,6 +72,7 @@ namespace scribo
typedef L support;
typedef mln_site(L) P;
+ typedef mln::util::couple<anchor::Type, P> couple_t;
link_functor_base(const object_image(L)& objects, unsigned nanchors);
@@ -85,7 +90,7 @@ namespace scribo
void initialize_link(unsigned current_object);
- mln_site(L) finalize_link(unsigned current_object);
+ couple_t finalize_link(unsigned current_object);
bool verify_link_criterion(unsigned current_object,
@@ -106,7 +111,7 @@ namespace scribo
void validate_link(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor);
+ anchor::Type anchor);
/// \overload
/// \p anchor is set to 0.
@@ -115,7 +120,7 @@ namespace scribo
void invalidate_link(unsigned current_object,
const P& start_point, const P& p,
- unsigned anchor);
+ anchor::Type anchor);
/// \overload
/// \p anchor is set to 0
@@ -130,7 +135,7 @@ namespace scribo
void compute_next_site(P& p);
- mln_site(L) start_point(unsigned current_object, unsigned anchor);
+ mln_site(L) start_point(unsigned current_object, anchor::Type anchor);
/// \overload
/// \p anchor is set to 0.
@@ -146,7 +151,7 @@ namespace scribo
// methods.
void initialize_link_(unsigned current_object);
- mln_site(L) finalize_link_(unsigned current_object);
+ couple_t finalize_link_(unsigned current_object);
bool is_potential_link_(unsigned current_object,
const P& start_point, const P& p) const;
@@ -159,21 +164,24 @@ namespace scribo
void validate_link_(unsigned current_object,
const P& start_point, const P& p,
- unsigned anchor);
+ anchor::Type anchor);
void invalidate_link_(unsigned current_object,
const P& start_point, const P& p,
- unsigned anchor);
+ anchor::Type anchor);
void compute_next_site_(P& p);
void start_processing_object_(unsigned current_object);
+ mln_site(L) start_point_(unsigned current_object,
+ anchor::Type anchor);
protected:
object_links<L> links_;
const object_image(L) objects_;
unsigned nanchors_;
+ util::array<mln_result(accu::center<mln_site(L)>)> mass_centers_;
};
@@ -191,6 +199,9 @@ namespace scribo
nanchors_(nanchors)
{
primitive::internal::init_link_array(links_);
+
+ mass_centers_ = labeling::compute(accu::meta::center(),
+ objects, objects.nlabels());
}
@@ -203,6 +214,9 @@ namespace scribo
nanchors_(1)
{
primitive::internal::init_link_array(links_);
+
+ mass_centers_ = labeling::compute(accu::meta::center(),
+ objects, objects.nlabels());
}
@@ -266,7 +280,7 @@ namespace scribo
template <typename L, typename E>
inline
- mln_site(L)
+ mln::util::couple<anchor::Type, mln_site(L)>
link_functor_base<L,E>::finalize_link(unsigned current_object)
{
return exact(this)->finalize_link_(current_object);
@@ -303,7 +317,7 @@ namespace scribo
link_functor_base<L,E>::validate_link(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
exact(this)->validate_link_(current_object, start_point, p, anchor);
}
@@ -327,7 +341,7 @@ namespace scribo
link_functor_base<L,E>::invalidate_link(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
exact(this)->invalidate_link_(current_object, start_point, p, anchor);
}
@@ -361,7 +375,7 @@ namespace scribo
inline
mln_site(L)
link_functor_base<L,E>::start_point(unsigned current_object,
- unsigned anchor)
+ anchor::Type anchor)
{
return exact(this)->start_point_(current_object, anchor);
}
@@ -404,12 +418,12 @@ namespace scribo
template <typename L, typename E>
inline
- mln_site(L)
+ mln::util::couple<anchor::Type, mln_site(L)>
link_functor_base<L,E>::finalize_link_(unsigned current_object)
{
(void) current_object;
// No-Op
- return P();
+ return mln::make::couple(anchor::Invalid, P());
}
@@ -460,7 +474,7 @@ namespace scribo
link_functor_base<L,E>::validate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
(void) start_point;
(void) anchor;
@@ -474,7 +488,7 @@ namespace scribo
link_functor_base<L,E>::invalidate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
(void) current_object;
(void) start_point;
@@ -486,11 +500,13 @@ namespace scribo
template <typename L, typename E>
inline
- void
- link_functor_base<L,E>::compute_next_site_(P& p)
+ mln_site(L)
+ link_functor_base<L,E>::start_point_(unsigned current_object,
+ anchor::Type anchor)
{
- (void) p;
- // No-Op
+ (void) anchor;
+ return internal::compute_anchor(this->objects_, mass_centers_,
+ current_object, anchor);
}
template <typename L, typename E>
diff --git a/scribo/primitive/link/internal/link_several_dmax_base.hh b/scribo/primitive/link/internal/link_several_dmax_base.hh
new file mode 100644
index 0000000..3d4fbae
--- /dev/null
+++ b/scribo/primitive/link/internal/link_several_dmax_base.hh
@@ -0,0 +1,265 @@
+// 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_PRIMITIVE_LINK_INTERNAL_LINK_SEVERAL_DMAX_HH_
+# define SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SEVERAL_DMAX_HH_
+
+/// \file
+///
+/// Base class for link functors using mass centers and a given max
+/// distance.
+
+
+# include <mln/accu/center.hh>
+# include <mln/labeling/compute.hh>
+# include <mln/math/abs.hh>
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+# include <scribo/core/anchors.hh>
+# include <scribo/core/object_image.hh>
+# include <scribo/core/object_links.hh>
+
+# include <scribo/primitive/link/internal/find_link.hh>
+# include <scribo/primitive/link/internal/link_functor_base.hh>
+
+# include <scribo/primitive/link/compute.hh>
+
+
+namespace scribo
+{
+
+ namespace primitive
+ {
+
+ namespace link
+ {
+
+ namespace internal
+ {
+
+ /// \brief Base class for link functors using mass centers and
+ /// a given max distance.
+ //
+ template <typename L, typename E>
+ class link_several_dmax_base
+ : public link_functor_base<L, E>
+ {
+ typedef mln_site(L) P;
+ typedef link_functor_base<L,E> super_;
+ typedef mln_result(accu::center<mln_psite(L)>) ms_t;
+ typedef mln::util::couple<anchor::Type, P> couple_t;
+
+ public:
+
+ link_several_dmax_base(const object_image(L)& objects,
+ unsigned neighb_max_distance,
+ unsigned nanchors);
+
+
+
+ bool verify_link_criterion_(unsigned current_object,
+ const P& start_point, const P& p) const;
+
+ void start_processing_object_(unsigned current_object);
+
+ void validate_link_(unsigned current_object, const P& start_point,
+ const P& p, anchor::Type anchor);
+
+ void initialize_link_(unsigned current_object);
+ couple_t finalize_link_(unsigned current_object);
+
+
+
+ protected:
+ mln::util::array<ms_t> mass_centers_;
+ mln::util::array<couple_t> potential_links_;
+ float dmax_;
+ float neighb_max_distance_;
+ };
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ template <typename L, typename E>
+ inline
+ link_several_dmax_base<L, E>::link_several_dmax_base(
+ const object_image(L)& objects,
+ unsigned neighb_max_distance,
+ unsigned nanchors)
+
+ : super_(objects, nanchors),
+ dmax_(0),
+ neighb_max_distance_(neighb_max_distance)
+ {
+
+ mass_centers_ = labeling::compute(accu::meta::center(),
+ objects, objects.nlabels());
+ }
+
+ template <typename L, typename E>
+ inline
+ bool
+ link_several_dmax_base<L, E>::verify_link_criterion_(
+ unsigned current_object,
+ const P& start_point,
+ const P& p) const
+ {
+ (void) current_object;
+
+ float dist = math::abs(p.col() - start_point.col());
+ return dist <= dmax_; // Not too far
+ }
+
+
+ template <typename L, typename E>
+ inline
+ void
+ link_several_dmax_base<L, E>::start_processing_object_(
+ unsigned current_object)
+ {
+ float
+ midcol = (this->objects_.bbox(current_object).pmax().col()
+ - this->objects_.bbox(current_object).pmin().col()) / 2;
+ dmax_ = midcol + neighb_max_distance_;
+ }
+
+
+ template <typename L, typename E>
+ inline
+ void
+ link_several_dmax_base<L, E>::validate_link_(unsigned current_object,
+ const P& start_point,
+ const P& p,
+ anchor::Type anchor)
+ {
+ (void) current_object;
+ (void) start_point;
+ this->potential_links_.append(mln::make::couple(anchor, p));
+ }
+
+
+ template <typename L, typename E>
+ inline
+ void
+ link_several_dmax_base<L, E>::initialize_link_(unsigned current_object)
+ {
+ (void) current_object;
+ this->links_.clear();
+ this->potential_links_.clear();
+ }
+
+
+ template <typename L, typename E>
+ inline
+ mln::util::couple<anchor::Type, mln_site(L)>
+ link_several_dmax_base<L, E>::finalize_link_(unsigned current_object)
+ {
+ //TODO
+ mln::util::array<unsigned> dist;
+ unsigned
+ min = mln_max(unsigned),
+ tmp;
+
+ if (this->potential_links_.nelements() > 0)
+ {
+ for(unsigned i = 0; i < this->potential_links_.nelements(); ++i)
+ {
+ tmp = math::abs(this->objects_.bbox(current_object).pmax().col()
+ - this->potential_links_(i).second().col());
+ dist.append(tmp);
+ if (tmp < min)
+ min = tmp;
+ }
+
+ mln_assertion(min != mln_max(unsigned));
+
+ // Keep closest links and compute vertical overlap.
+ unsigned
+ nratio = 0,
+ id_max_ratio = 0;
+ mln_value(L) nbh_id;
+ mln::util::array<float> overlap;
+ for(unsigned i = 0; i < this->potential_links_.nelements(); ++i)
+ if (dist[i] < (1.2 * min))
+ {
+ unsigned
+ other_object = this->objects_(potential_links_(i).second());
+ nbh_id = other_object;
+
+ float
+ dr
+ = math::min(this->objects_.bbox(current_object).pmax().row(),
+ this->objects_.bbox(other_object).pmax().row())
+ - math::min(this->objects_.bbox(current_object).pmin().row(),
+ this->objects_.bbox(other_object).pmin().row()),
+ dh = this->objects_.bbox(other_object).pmax().row()
+ - this->objects_.bbox(other_object).pmin().row(),
+ ratio = dr / dh;
+
+ overlap.append(ratio);
+ if (ratio > overlap(id_max_ratio) || nratio == 0)
+ {
+ id_max_ratio = i;
+ nratio = 1;
+ nbh_id = other_object;
+ }
+ else if (ratio == overlap(id_max_ratio)
+ && other_object != nbh_id)
+ ++nratio;
+ }
+ else
+ overlap.append(0);
+
+ if (nratio == 1)
+ {
+ this->links_(current_object)
+ = this->objects_(potential_links_(id_max_ratio).second());
+
+ return potential_links_(id_max_ratio);
+ }
+
+ }
+
+ this->links_(current_object) = current_object;
+ return mln::make::couple(anchor::Invalid, P());
+ }
+
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+ } // end of namespace scribo::primitive::link::internal
+
+ } // end of namespace scribo::primitive::link
+
+ } // end of namespace scribo::primitive
+
+} // end of namespace scribo
+
+
+#endif // SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SEVERAL_DMAX_HH_
diff --git a/scribo/primitive/link/internal/link_center_dmax_base.hh b/scribo/primitive/link/internal/link_single_dmax_base.hh
similarity index 73%
rename from scribo/primitive/link/internal/link_center_dmax_base.hh
rename to scribo/primitive/link/internal/link_single_dmax_base.hh
index 1a7a48d..4f571af 100644
--- a/scribo/primitive/link/internal/link_center_dmax_base.hh
+++ b/scribo/primitive/link/internal/link_single_dmax_base.hh
@@ -23,8 +23,8 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_CENTER_DMAX_HH_
-# define SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_CENTER_DMAX_HH_
+#ifndef SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SINGLE_DMAX_BASE_HH_
+# define SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SINGLE_DMAX_BASE_HH_
/// \file
///
@@ -38,10 +38,12 @@
# include <mln/util/array.hh>
# include <scribo/core/macros.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/primitive/link/internal/find_link.hh>
+# include <scribo/primitive/link/internal/compute_anchor.hh>
# include <scribo/primitive/link/internal/link_functor_base.hh>
# include <scribo/primitive/link/compute.hh>
@@ -63,41 +65,32 @@ namespace scribo
/// a given max distance.
//
template <typename L, typename E>
- class link_center_dmax_base
+ class link_single_dmax_base
: public link_functor_base<L, E>
{
typedef link_functor_base<L,E> super_;
+ typedef mln_result(accu::center<mln_psite(L)>) ms_t;
public:
typedef mln_site(L) P;
-
- /*!
-
- center_type can have the following values:
- 0 - top bounding box center
- 1 - bounding box center
- 2 - bottom bounding box center
-
- */
- link_center_dmax_base(const object_image(L)& objects,
- unsigned neighb_max_distance,
- unsigned center_type);
+ link_single_dmax_base(const object_image(L)& objects,
+ unsigned neighb_max_distance);
bool verify_link_criterion_(unsigned current_object,
const P& start_point, const P& p) const;
mln_site(L) start_point_(unsigned current_object,
- unsigned anchor);
+ anchor::Type anchor);
void start_processing_object_(unsigned current_object);
private:
float dmax_;
float neighb_max_distance_;
- unsigned center_type_;
+// mln::util::array<ms_t> mass_centers_;
};
@@ -106,23 +99,23 @@ namespace scribo
template <typename L, typename E>
inline
- link_center_dmax_base<L, E>::link_center_dmax_base(
+ link_single_dmax_base<L, E>::link_single_dmax_base(
const object_image(L)& objects,
- unsigned neighb_max_distance,
- unsigned center_type)
+ unsigned neighb_max_distance)
: super_(objects),
dmax_(0),
- neighb_max_distance_(neighb_max_distance),
- center_type_(center_type)
+ neighb_max_distance_(neighb_max_distance)
{
+// mass_centers_ = labeling::compute(accu::meta::center(),
+// objects, objects.nlabels());
}
template <typename L, typename E>
inline
bool
- link_center_dmax_base<L, E>::verify_link_criterion_(
+ link_single_dmax_base<L, E>::verify_link_criterion_(
unsigned current_object,
const P& start_point,
const P& p) const
@@ -137,24 +130,18 @@ namespace scribo
template <typename L, typename E>
inline
mln_site(L)
- link_center_dmax_base<L, E>::start_point_(unsigned current_object,
- unsigned anchor)
+ link_single_dmax_base<L, E>::start_point_(unsigned current_object,
+ anchor::Type anchor)
{
- (void) anchor;
- mln_site(L) sp = this->objects_.bbox(current_object).center();
- if (center_type_ == 0)
- sp.row() = this->objects_.bbox(current_object).pmin().row();
- else if (center_type_ == 2)
- sp.row() = this->objects_.bbox(current_object).pmax().row();
-
- return sp;
+ return internal::compute_anchor(this->objects_, this->objects_.mass_centers(),//mass_centers_,
+ current_object, anchor);
}
template <typename L, typename E>
inline
void
- link_center_dmax_base<L, E>::start_processing_object_(
+ link_single_dmax_base<L, E>::start_processing_object_(
unsigned current_object)
{
float
@@ -176,4 +163,4 @@ namespace scribo
} // end of namespace scribo
-#endif // SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_CENTER_DMAX_HH_
+#endif // SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SINGLE_DMAX_BASE_HH_
diff --git a/scribo/primitive/link/internal/link_center_dmax_ratio_base.hh b/scribo/primitive/link/internal/link_single_dmax_ratio_base.hh
similarity index 79%
rename from scribo/primitive/link/internal/link_center_dmax_ratio_base.hh
rename to scribo/primitive/link/internal/link_single_dmax_ratio_base.hh
index 0740ec9..9eadba7 100644
--- a/scribo/primitive/link/internal/link_center_dmax_ratio_base.hh
+++ b/scribo/primitive/link/internal/link_single_dmax_ratio_base.hh
@@ -23,8 +23,8 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_CENTER_DMAX_RATIO_BASE_HH_
-# define SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_CENTER_DMAX_RATIO_BASE_HH_
+#ifndef SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SINGLE_DMAX_RATIO_BASE_HH_
+# define SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SINGLE_DMAX_RATIO_BASE_HH_
/// \file
///
@@ -39,9 +39,11 @@
# include <mln/util/array.hh>
# include <scribo/core/macros.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
+# include <scribo/primitive/link/internal/compute_anchor.hh>
# include <scribo/primitive/link/internal/find_link.hh>
# include <scribo/primitive/link/internal/link_functor_base.hh>
@@ -64,7 +66,7 @@ namespace scribo
/// center and a proportional max distance.
//
template <typename L, typename E>
- class link_center_dmax_ratio_base
+ class link_single_dmax_ratio_base
: public link_functor_base<L, E>
{
typedef link_functor_base<L,E> super_;
@@ -75,7 +77,7 @@ namespace scribo
typedef mln_site(L) P;
- link_center_dmax_ratio_base(const object_image(L)& objects,
+ link_single_dmax_ratio_base(const object_image(L)& objects,
float dmax_ratio,
unsigned center_type_);
@@ -85,14 +87,14 @@ namespace scribo
const P& start_point, const P& p) const;
mln_site(L) start_point_(unsigned current_object,
- unsigned anchor);
+ anchor::Type anchor);
void start_processing_object_(unsigned current_object);
private:
float dmax_ratio_;
float dmax_;
- unsigned center_type_;
+ mln::util::array<ms_t> mass_centers_;
};
@@ -101,22 +103,22 @@ namespace scribo
template <typename L, typename E>
inline
- link_center_dmax_ratio_base<L, E>::link_center_dmax_ratio_base(
+ link_single_dmax_ratio_base<L, E>::link_single_dmax_ratio_base(
const object_image(L)& objects,
- float dmax_ratio,
- unsigned center_type)
+ float dmax_ratio)
: super_(objects),
dmax_ratio_(dmax_ratio),
- dmax_(0),
- center_type_(center_type)
+ dmax_(0)
{
+ mass_centers_ = labeling::compute(accu::meta::center(),
+ objects, objects.nlabels());
}
template <typename L, typename E>
inline
bool
- link_center_dmax_ratio_base<L, E>::verify_link_criterion_(
+ link_single_dmax_ratio_base<L, E>::verify_link_criterion_(
unsigned current_object,
const P& start_point,
const P& p) const
@@ -131,24 +133,19 @@ namespace scribo
template <typename L, typename E>
inline
mln_site(L)
- link_center_dmax_ratio_base<L, E>::start_point_(unsigned current_object,
- unsigned anchor)
+ link_single_dmax_ratio_base<L, E>::start_point_(unsigned current_object,
+ anchor::Type anchor)
{
(void) anchor;
- mln_site(L) sp = this->objects_.bbox(current_object).center();
- if (center_type_ == 0)
- sp.row() = this->objects_.bbox(current_object).pmin().row();
- else if (center_type_ == 2)
- sp.row() = this->objects_.bbox(current_object).pmax().row();
-
- return sp;
+ return internal::compute_anchors(this->objects_, mass_centers_,
+ current_object, anchor);
}
template <typename L, typename E>
inline
void
- link_center_dmax_ratio_base<L, E>::start_processing_object_(
+ link_single_dmax_ratio_base<L, E>::start_processing_object_(
unsigned current_object)
{
float
@@ -172,4 +169,4 @@ namespace scribo
} // end of namespace scribo
-#endif // SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_CENTER_DMAX_RATIO_BASE_HH_
+#endif // SCRIBO_PRIMITIVE_LINK_INTERNAL_LINK_SINGLE_DMAX_RATIO_BASE_HH_
diff --git a/scribo/primitive/link/with_several_right_links_overlap.hh b/scribo/primitive/link/with_several_right_links_overlap.hh
index 27d9d49..f9fd5a4 100644
--- a/scribo/primitive/link/with_several_right_links_overlap.hh
+++ b/scribo/primitive/link/with_several_right_links_overlap.hh
@@ -39,7 +39,8 @@
# include <scribo/core/object_image.hh>
# include <scribo/core/macros.hh>
# include <scribo/primitive/internal/init_link_array.hh>
-# include <scribo/primitive/internal/find_right_link.hh>
+# include <scribo/primitive/link/internal/find_several_links.hh>
+# include <scribo/primitive/link/internal/link_several_dmax_base.hh>
# include <scribo/util/text.hh>
@@ -94,8 +95,6 @@ namespace scribo
{
}
- template <typename L, typename E>
- inline
mln_site(L)
start_point_(unsigned current_object, unsigned anchor)
{
@@ -109,6 +108,8 @@ namespace scribo
++p.col();
}
+ };
+
} // end of namespace scribo::primitive::link::internal
@@ -122,7 +123,8 @@ namespace scribo
mln_precondition(objects.is_valid());
- several_right_overlap_functor<L> functor(objects, neighb_max_distance);
+ internal::several_right_overlap_functor<L>
+ functor(objects, neighb_max_distance);
for_all_ncomponents(current_object, objects.nlabels())
internal::find_several_links(functor, current_object);
diff --git a/scribo/primitive/link/with_single_left_link.hh b/scribo/primitive/link/with_single_left_link.hh
index bef9ea3..2cf0c5a 100644
--- a/scribo/primitive/link/with_single_left_link.hh
+++ b/scribo/primitive/link/with_single_left_link.hh
@@ -47,7 +47,7 @@
# include <scribo/core/object_links.hh>
# include <scribo/primitive/link/internal/find_link.hh>
-# include <scribo/primitive/link/internal/link_ms_dmax_base.hh>
+# include <scribo/primitive/link/internal/link_single_dmax_base.hh>
# include <scribo/primitive/link/compute.hh>
@@ -93,10 +93,10 @@ namespace scribo
template <typename L>
class single_left_functor
- : public internal::link_ms_dmax_base<L, single_left_functor<L> >
+ : public internal::link_single_dmax_base<L, single_left_functor<L> >
{
typedef
- internal::link_ms_dmax_base<L, single_left_functor<L> > super_;
+ internal::link_single_dmax_base<L, single_left_functor<L> > super_;
public:
typedef mln_site(L) P;
diff --git a/scribo/primitive/link/with_single_right_link.hh b/scribo/primitive/link/with_single_right_link.hh
index 236884b..f7272ee 100644
--- a/scribo/primitive/link/with_single_right_link.hh
+++ b/scribo/primitive/link/with_single_right_link.hh
@@ -40,11 +40,12 @@
# include <mln/util/array.hh>
# include <scribo/core/macros.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/primitive/link/internal/find_link.hh>
-# include <scribo/primitive/link/internal/link_ms_dmax_base.hh>
+# include <scribo/primitive/link/internal/link_single_dmax_base.hh>
# include <scribo/primitive/link/compute.hh>
@@ -92,10 +93,10 @@ namespace scribo
template <typename L>
class single_right_functor
- : public internal::link_ms_dmax_base<L, single_right_functor<L> >
+ : public internal::link_single_dmax_base<L, single_right_functor<L> >
{
typedef
- internal::link_ms_dmax_base<L, single_right_functor<L> > super_;
+ internal::link_single_dmax_base<L, single_right_functor<L> > super_;
public:
typedef mln_site(L) P;
@@ -131,7 +132,7 @@ namespace scribo
internal::single_right_functor<L>
functor(objects, neighb_max_distance);
- object_links<L> output = compute(functor);
+ object_links<L> output = compute(functor, anchor::MassCenter);
trace::exiting("scribo::primitive::link::with_single_right_link");
return output;
diff --git a/scribo/primitive/link/with_single_right_link_bottom.hh b/scribo/primitive/link/with_single_right_link_bottom.hh
index 5976f22..3b1f6e7 100644
--- a/scribo/primitive/link/with_single_right_link_bottom.hh
+++ b/scribo/primitive/link/with_single_right_link_bottom.hh
@@ -40,11 +40,12 @@
# include <mln/util/array.hh>
# include <scribo/core/macros.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/primitive/link/internal/find_link.hh>
-# include <scribo/primitive/link/internal/link_center_dmax_base.hh>
+# include <scribo/primitive/link/internal/link_single_dmax_base.hh>
# include <scribo/primitive/link/compute.hh>
@@ -70,7 +71,7 @@ namespace scribo
inline
object_links<L>
with_single_right_link_bottom(const object_image(L)& objects,
- unsigned neighb_max_distance);
+ unsigned neighb_max_distance);
/// \overload
@@ -92,11 +93,11 @@ namespace scribo
template <typename L>
class single_right_bottom_functor
- : public link_center_dmax_base<L,
+ : public link_single_dmax_base<L,
single_right_bottom_functor<L> >
{
typedef
- link_center_dmax_base<L, single_right_bottom_functor<L> >
+ link_single_dmax_base<L, single_right_bottom_functor<L> >
super_;
public:
@@ -104,7 +105,7 @@ namespace scribo
single_right_bottom_functor(const object_image(L)& objects,
unsigned dmax)
- : super_(objects, dmax, 2)
+ : super_(objects, dmax)
{
}
@@ -134,7 +135,7 @@ namespace scribo
internal::single_right_bottom_functor<L>
functor(objects, neighb_max_distance);
- object_links<L> output = compute(functor);
+ object_links<L> output = compute(functor, anchor::Bottom);
trace::exiting("scribo::primitive::link::with_single_right_link_bottom");
return output;
diff --git a/scribo/primitive/link/with_single_right_link_top.hh b/scribo/primitive/link/with_single_right_link_top.hh
index 13bb583..1e3e0df 100644
--- a/scribo/primitive/link/with_single_right_link_top.hh
+++ b/scribo/primitive/link/with_single_right_link_top.hh
@@ -40,11 +40,12 @@
# include <mln/util/array.hh>
# include <scribo/core/macros.hh>
+# include <scribo/core/anchors.hh>
# include <scribo/core/object_image.hh>
# include <scribo/core/object_links.hh>
# include <scribo/primitive/link/internal/find_link.hh>
-# include <scribo/primitive/link/internal/link_center_dmax_base.hh>
+# include <scribo/primitive/link/internal/link_single_dmax_base.hh>
# include <scribo/primitive/link/compute.hh>
@@ -70,7 +71,7 @@ namespace scribo
inline
object_links<L>
with_single_right_link_top(const object_image(L)& objects,
- unsigned neighb_max_distance);
+ unsigned neighb_max_distance);
/// \overload
@@ -92,19 +93,19 @@ namespace scribo
template <typename L>
class single_right_top_functor
- : public link_center_dmax_base<L,
+ : public link_single_dmax_base<L,
single_right_top_functor<L> >
{
typedef
- link_center_dmax_base<L, single_right_top_functor<L> >
+ link_single_dmax_base<L, single_right_top_functor<L> >
super_;
public:
typedef mln_site(L) P;
single_right_top_functor(const object_image(L)& objects,
- unsigned dmax)
- : super_(objects, dmax, 0)
+ unsigned dmax)
+ : super_(objects, dmax)
{
}
@@ -125,7 +126,7 @@ namespace scribo
inline
object_links<L>
with_single_right_link_top(const object_image(L)& objects,
- unsigned neighb_max_distance)
+ unsigned neighb_max_distance)
{
trace::entering("scribo::primitive::link::with_single_right_link_top");
@@ -134,7 +135,7 @@ namespace scribo
internal::single_right_top_functor<L>
functor(objects, neighb_max_distance);
- object_links<L> output = compute(functor);
+ object_links<L> output = compute(functor, anchor::Top);
trace::exiting("scribo::primitive::link::with_single_right_link_top");
return output;
diff --git a/scribo/src/debug/show_links_several_right_overlap.cc b/scribo/src/debug/show_links_several_right_overlap.cc
index 6b2a66a..f2824f3 100644
--- a/scribo/src/debug/show_links_several_right_overlap.cc
+++ b/scribo/src/debug/show_links_several_right_overlap.cc
@@ -30,6 +30,8 @@
#include <mln/data/convert.hh>
+#include <mln/util/couple.hh>
+
#include <mln/value/rgb8.hh>
#include <mln/value/label_16.hh>
#include <mln/literal/colors.hh>
@@ -41,7 +43,7 @@
#include <scribo/primitive/extract/objects.hh>
#include <scribo/primitive/link/internal/link_several_dmax_base.hh>
-#include <scribo/primitive/link/internal/anchors_3.hh>
+#include <scribo/primitive/link/internal/compute_anchor.hh>
#include <scribo/primitive/link/compute_several.hh>
#include <scribo/draw/bounding_boxes.hh>
@@ -61,11 +63,7 @@ namespace scribo
typedef
primitive::link::internal::link_several_dmax_base<L, self_t> super_;
- typedef
- mln::util::array<mln::util::couple<unsigned, mln_site(L)>
- potential_links_t;
-
- public:
+ public:
typedef mln_site(L) P;
several_right_overlap_debug_functor(const I& input,
@@ -79,35 +77,32 @@ namespace scribo
}
- void validate_link_(unsigned current_object,
- const P& start_point,
- const P& p,
- unsigned anchor)
+ mln::util::couple<anchor::Type, mln_site(L)>
+ finalize_link_(unsigned current_object)
{
- mln::draw::line(output_, start_point, p, literal::green);
-
- super_::validate_link_(current_object, start_point, p, anchor);
- }
+ mln::util::couple<anchor::Type, mln_site(L)>
+ c = super_::finalize_link_(current_object);
-
-
- void invalidate_link_(unsigned current_object,
- const P& start_point,
- const P& p,
- unsigned anchor)
- {
- if (output_.domain().has(p))
- mln::draw::line(output_, start_point, p, literal::red);
- else
+ if (c.first() != anchor::Invalid)
{
- P tmp = p;
- ++tmp.col();
- mln::draw::line(output_, start_point, tmp, literal::red);
+ mln_site(L)
+ p = primitive::link::internal::compute_anchor(this->objects_,
+ this->mass_centers_,
+ current_object,
+ c.first());
+ mln::draw::line(output_, p, c.second(), literal::green);
}
- super_::invalidate_link_(current_object, start_point, p, anchor);
+ return c;
}
+ mln_site(L)
+ start_point_(unsigned current_object, anchor::Type anchor)
+ {
+ return primitive::link::internal::compute_anchor(this->objects_,
+ this->mass_centers_,
+ current_object, anchor);
+ }
void compute_next_site_(P& p)
diff --git a/scribo/src/debug/show_links_single_left.cc b/scribo/src/debug/show_links_single_left.cc
index 094ca85..26f78e2 100644
--- a/scribo/src/debug/show_links_single_left.cc
+++ b/scribo/src/debug/show_links_single_left.cc
@@ -78,7 +78,7 @@ namespace scribo
void validate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
mln::draw::line(output_, start_point, p, literal::green);
@@ -90,7 +90,7 @@ namespace scribo
void invalidate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
if (output_.domain().has(p))
mln::draw::line(output_, start_point, p, literal::red);
diff --git a/scribo/src/debug/show_links_single_left_dmax_ratio.cc b/scribo/src/debug/show_links_single_left_dmax_ratio.cc
index 2ba0ace..e133507 100644
--- a/scribo/src/debug/show_links_single_left_dmax_ratio.cc
+++ b/scribo/src/debug/show_links_single_left_dmax_ratio.cc
@@ -78,7 +78,7 @@ namespace scribo
void validate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
mln::draw::line(output_, start_point, p, literal::green);
@@ -90,7 +90,7 @@ namespace scribo
void invalidate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
if (output_.domain().has(p))
mln::draw::line(output_, start_point, p, literal::red);
diff --git a/scribo/src/debug/show_links_single_right.cc b/scribo/src/debug/show_links_single_right.cc
index c121cce..7a25306 100644
--- a/scribo/src/debug/show_links_single_right.cc
+++ b/scribo/src/debug/show_links_single_right.cc
@@ -78,7 +78,7 @@ namespace scribo
void validate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
mln::draw::line(output_, start_point, p, literal::green);
@@ -90,7 +90,7 @@ namespace scribo
void invalidate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
if (output_.domain().has(p))
mln::draw::line(output_, start_point, p, literal::red);
diff --git a/scribo/src/debug/show_links_single_right_dmax_ratio.cc b/scribo/src/debug/show_links_single_right_dmax_ratio.cc
index 81415bc..806114c 100644
--- a/scribo/src/debug/show_links_single_right_dmax_ratio.cc
+++ b/scribo/src/debug/show_links_single_right_dmax_ratio.cc
@@ -78,7 +78,7 @@ namespace scribo
void validate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
mln::draw::line(output_, start_point, p, literal::green);
@@ -90,7 +90,7 @@ namespace scribo
void invalidate_link_(unsigned current_object,
const P& start_point,
const P& p,
- unsigned anchor)
+ anchor::Type anchor)
{
if (output_.domain().has(p))
mln::draw::line(output_, start_point, p, literal::red);
--
1.5.6.5
1
0
last-svn-commit-25-g8d114a2 core/object_image.hh: Make use of mln::labeled_image_base.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
---
scribo/ChangeLog | 4 +
scribo/core/object_image.hh | 347 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 347 insertions(+), 4 deletions(-)
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 2897a58..a5c7fea 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,9 @@
2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+ * core/object_image.hh: Make use of mln::labeled_image_base.
+
+2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Improve linking routines.
* core/anchors.hh: New. Add anchor types.
diff --git a/scribo/core/object_image.hh b/scribo/core/object_image.hh
index a920680..5eaae67 100644
--- a/scribo/core/object_image.hh
+++ b/scribo/core/object_image.hh
@@ -28,11 +28,350 @@
/// \file
///
-/// FIXME.
+/// \brief Definition of a morpher on a labeled image.
-# include <mln/core/image/imorph/labeled_image.hh>
+# include <mln/core/internal/labeled_image_base.hh>
-# define object_image(L) \
- labeled_image<L>
+# include <mln/core/routine/duplicate.hh>
+
+# include <mln/util/array.hh>
+
+# include <mln/data/compute.hh>
+
+# include <mln/accu/center.hh>
+# include <mln/accu/shape/bbox.hh>
+# include <mln/accu/stat/max.hh>
+
+# include <mln/labeling/compute.hh>
+
+namespace mln
+{
+
+ // Forward declarations.
+ template <typename I> struct object_image;
+
+ namespace internal
+ {
+ using namespace mln;
+
+ /// Data structure for \c mln::object_image<I>.
+ template <typename I>
+ struct data< object_image<I> >
+ : data< labeled_image_base<I, object_image<I> > >
+ {
+ typedef data< labeled_image_base<I, object_image<I> > > super_;
+ typedef mln_psite(I) ms_t;
+ typedef mln_result(accu::center<mln_site(I)>) accu_res_t;
+
+ data(const I& ima, const mln_value(I)& nlabels);
+ data(const I& ima, const mln_value(I)& nlabels,
+ const util::array<mln_box(I)>& bboxes,
+ const util::array<ms_t>& mass_centers);
+ data(const I& ima, const mln_value(I)& nlabels,
+ const util::array<mln_box(I)>& bboxes,
+ const util::array<accu_res_t>& mass_centers);
+
+ mutable util::array<ms_t> mass_centers_;
+
+ util::array<accu::center<mln_psite(I)> > tmp_accus_;
+ };
+
+ } // end of namespace mln::internal
+
+
+ namespace trait
+ {
+
+ template <typename I>
+ struct image_< object_image<I> >
+ : image_< labeled_image_base< I, object_image<I> > >
+ {
+ };
+
+ } // end of namespace mln::trait
+
+
+
+ /// Morpher providing an improved interface for labeled image.
+ ///
+ /// \tparam I The label image type.
+ ///
+ /// This image type allows to access every site set at a given
+ /// label.
+ ///
+ /// This image type guaranties that labels are contiguous (from 1 to
+ /// n).
+ ///
+ /// \ingroup modimageidmorpher
+ //
+ template <typename I>
+ class object_image
+ : public mln::labeled_image_base<I, object_image<I> >
+ {
+ typedef mln::labeled_image_base<I, object_image<I> > super_;
+
+ public:
+
+ /// Skeleton.
+ typedef object_image< tag::image_<I> > skeleton;
+
+ /// Mass center type.
+ typedef typename internal::data< object_image<I> >::ms_t ms_t;
+ typedef mln_result(accu::center<mln_site(I)>) accu_res_t;
+
+ /// Constructors
+ /// @{
+ /// Constructor without argument.
+ object_image();
+
+ /// Constructor from an image \p ima and the number of labels \p nlabels.
+ object_image(const I& ima, const mln_value(I)& nlabels);
+
+ /// Constructor from an image \p ima, the number of labels \p
+ /// nlabels and the object bounding boxes.
+ template <typename P>
+ object_image(const I& ima, const mln_value(I)& nlabels,
+ const util::array<mln_box(I)>& bboxes,
+ const util::array<P>& mass_centers);
+ /// @}
+
+ /// Deferred initialization from a labeled image \p ima and the number
+ /// of labels \p nlabels.
+ void init_(const I& ima, const mln_value(I)& nlabels);
+
+ /// Duplicate the underlying image and create a new object_image.
+ void init_from_(const object_image<I>& model);
+
+ /// Return an array of mass centers.
+ const util::array<ms_t>& mass_centers() const;
+
+ /// Return the mass center of an object with label \p label.
+ const mln_psite(I)& mass_center(const mln_value(I)& label) const;
+
+ /// Return the underlying labeled image
+ const I& labeled_image_() const;
+
+ /// Update mass centers when objects are merged.
+ void init_update_data_();
+ void prepare_update_data_(const mln_value(I)& lbl,
+ const mln_value(I)& new_lbl);
+ void update_data_(const fun::i2v::array<mln_value(I)>& relabel_fun);
+ };
+
+
+ // init_
+
+ //FIXME: not enough generic? We would like 'J' instead of
+ // 'object_image<I>'.
+ template <typename I, typename J>
+ void init_(tag::image_t, object_image<I>& target,
+ const object_image<J>& model);
+
+
+
+ namespace make
+ {
+
+ template <typename I>
+ mln::object_image<I>
+ object_image(const Image<I>& ima, const mln_value(I)& nlabels);
+
+ } // end of namespace mln::make
+
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ // internal::data< object_image<I> >
+
+ namespace internal
+ {
+
+
+ // data< object_image<I> >
+
+ template <typename I>
+ inline
+ data< object_image<I> >::data(const I& ima, const mln_value(I)& nlabels)
+ : super_(ima, nlabels)
+ {
+ }
+
+ template <typename I>
+ inline
+ data< object_image<I> >::data(const I& ima, const mln_value(I)& nlabels,
+ const util::array<mln_box(I)>& bboxes,
+ const util::array<ms_t>& mass_centers)
+ : super_(ima, nlabels, bboxes), mass_centers_(mass_centers)
+ {
+ }
+
+
+ template <typename I>
+ inline
+ data< object_image<I> >::data(const I& ima, const mln_value(I)& nlabels,
+ const util::array<mln_box(I)>& bboxes,
+ const util::array<accu_res_t>& mass_centers)
+ : super_(ima, nlabels, bboxes)
+ {
+ convert::from_to(mass_centers, mass_centers_);
+ }
+
+
+ } // end of namespace mln::internal
+
+
+ template <typename I>
+ inline
+ object_image<I>::object_image()
+ {
+ }
+
+ template <typename I>
+ inline
+ object_image<I>::object_image(const I& ima, const mln_value(I)& nlabels)
+ {
+ init_(ima, nlabels);
+ }
+
+ template <typename I>
+ template <typename P>
+ inline
+ object_image<I>::object_image(const I& ima, const mln_value(I)& nlabels,
+ const util::array<mln_box(I)>& bboxes,
+ const util::array<P>& mass_centers)
+ {
+ mln_precondition(data::compute(accu::meta::stat::max(), ima) == nlabels);
+ this->data_ = new internal::data< object_image<I> >(ima, nlabels,
+ bboxes, mass_centers);
+ }
+
+
+ template <typename I>
+ inline
+ void
+ object_image<I>::init_(const I& ima, const mln_value(I)& nlabels)
+ {
+ mln_precondition(data::compute(accu::meta::stat::max(), ima) == nlabels);
+ this->data_ = new internal::data< object_image<I> >(ima, nlabels);
+
+ // FIXME: could be improved!
+ this->data_->bboxes_ = labeling::compute(accu::meta::shape::bbox(),
+ this->data_->ima_,
+ this->data_->nlabels_);
+ convert::from_to(labeling::compute(accu::meta::center(),
+ this->data_->ima_,
+ this->data_->nlabels_),
+ this->data_->mass_centers_);
+ }
+
+
+ template <typename I>
+ inline
+ void
+ object_image<I>::init_from_(const object_image<I>& model)
+ {
+ typedef internal::data< object_image<I> > data_t;
+ this->data_
+ = new data_t(duplicate(model.hook_data_()->ima_),
+ model.nlabels(),
+ model.hook_data_()->bboxes_,
+ model.hook_data_()->mass_centers_);
+ }
+
+
+ template <typename I>
+ inline
+ const util::array<typename object_image<I>::ms_t>&
+ object_image<I>::mass_centers() const
+ {
+ return this->data_->mass_centers_;
+ }
+
+ template <typename I>
+ inline
+ const mln_psite(I)&
+ object_image<I>::mass_center(const mln_value(I)& label) const
+ {
+ return this->data_->mass_centers_(label);
+ }
+
+
+ template <typename I>
+ inline
+ const I&
+ object_image<I>::labeled_image_() const
+ {
+ return this->data_->ima_;
+ }
+
+ template <typename I>
+ inline
+ void
+ object_image<I>::init_update_data_()
+ {
+ // this->nlabels_ is supposed to be updated when this method is
+ // called.
+ this->data_->tmp_accus_.resize(static_cast<unsigned>(this->data_->nlabels_) + 1);
+ }
+
+
+ template <typename I>
+ inline
+ void
+ object_image<I>::prepare_update_data_(const mln_value(I)& lbl,
+ const mln_value(I)& new_lbl)
+ {
+ this->data_->tmp_accus_[new_lbl].take(this->data_->mass_centers_[lbl]);
+ }
+
+
+ template <typename I>
+ inline
+ void
+ object_image<I>::update_data_(
+ const fun::i2v::array<mln_value(I)>& relabel_fun)
+ {
+ (void) relabel_fun;
+ convert::from_to(this->data_->tmp_accus_, this->data_->mass_centers_);
+ }
+
+
+ // init_
+
+ template <typename I, typename J>
+ void init_(tag::image_t, object_image<I>& target,
+ const object_image<J>& model)
+ {
+ I ima;
+ init_(tag::image, ima, model);
+ target.init_(ima, model.nlabels());
+ }
+
+
+ // Make routines.
+
+ namespace make
+ {
+
+ template <typename I>
+ mln::object_image<I>
+ object_image(const Image<I>& ima, const mln_value(I)& nlabels)
+ {
+ mln_precondition(exact(ima).is_valid());
+ mln::object_image<I> tmp(exact(ima), nlabels);
+ return tmp;
+ }
+
+ } // end of namespace mln::make
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+} // end of namespace mln
+
+# define object_image(L) mln::object_image<L>
#endif // ! SCRIBO_CORE_OBJECT_IMAGE_HH
--
1.5.6.5
1
0
11 Mar '10
* binarization/sauvola_threshold.hh,
* binarization/sauvola.hh: Use fun::v2v::rgb_to_int_u8.
* binarization/sauvola_ms.hh: New. Sauvola multi-scale routine.
* src/binarization/sauvola.cc: Make the window size a program
argument.
* src/binarization/sauvola_ms.cc: Use Sauvola multi-scale routine.
---
scribo/ChangeLog | 14 +
scribo/binarization/sauvola.hh | 4 +-
scribo/binarization/sauvola_ms.hh | 822 +++++++++++++++
scribo/binarization/sauvola_threshold.hh | 14 +-
scribo/src/binarization/sauvola.cc | 7 +-
scribo/src/binarization/sauvola_ms.cc | 1669 +-----------------------------
6 files changed, 876 insertions(+), 1654 deletions(-)
create mode 100644 scribo/binarization/sauvola_ms.hh
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index a5c7fea..8488ae3 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,19 @@
2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Cleanup Sauvola's binarization code.
+
+ * binarization/sauvola_threshold.hh,
+ * binarization/sauvola.hh: Use fun::v2v::rgb_to_int_u8.
+
+ * binarization/sauvola_ms.hh: New. Sauvola multi-scale routine.
+
+ * src/binarization/sauvola.cc: Make the window size a program
+ argument.
+
+ * src/binarization/sauvola_ms.cc: Use Sauvola multi-scale routine.
+
+2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
* core/object_image.hh: Make use of mln::labeled_image_base.
2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
diff --git a/scribo/binarization/sauvola.hh b/scribo/binarization/sauvola.hh
index 5323847..1339c66 100644
--- a/scribo/binarization/sauvola.hh
+++ b/scribo/binarization/sauvola.hh
@@ -35,6 +35,8 @@
# include <mln/value/int_u8.hh>
# include <mln/value/rgb8.hh>
+# include <mln/fun/v2v/rgb_to_int_u.hh>
+
# include <scribo/binarization/sauvola_threshold.hh>
# include <scribo/binarization/binarize.hh>
@@ -106,7 +108,7 @@ namespace scribo
mln_precondition(exact(input).is_valid());
mln_ch_value(I, value::int_u8) gima;
- gima = data::transform(input, internal::rgb8_to_int_u8());
+ gima = data::transform(input, mln::fun::v2v::rgb_to_int_u<8>());
mln_ch_value(I, bool)
output = binarize(gima,
diff --git a/scribo/binarization/sauvola_ms.hh b/scribo/binarization/sauvola_ms.hh
new file mode 100644
index 0000000..bc52620
--- /dev/null
+++ b/scribo/binarization/sauvola_ms.hh
@@ -0,0 +1,822 @@
+// 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_BINARIZATION_SAUVOLA_MS_HH
+# define SCRIBO_BINARIZATION_SAUVOLA_MS_HH
+
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/data/fill.hh>
+
+# include <mln/subsampling/antialiased.hh>
+
+# include <mln/transform/influence_zone_geodesic.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/border/mirror.hh>
+# include <mln/border/adjust.hh>
+
+# include <mln/core/box_runend_piter.hh>
+
+# include <scribo/subsampling/integral_single_image.hh>
+
+# include <scribo/core/macros.hh>
+# include <scribo/core/object_image.hh>
+
+# include <scribo/binarization/sauvola_threshold.hh>
+# include <scribo/binarization/internal/first_pass_functor.hh>
+
+# include <scribo/canvas/integral_browsing.hh>
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ using namespace mln;
+ using value::int_u8;
+
+
+ unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
+ {
+ if (parent.element(x) == x)
+ return x;
+ return parent.element(x) = my_find_root(parent,
+ parent.element(x));
+ }
+
+
+ image2d<int_u8>
+ compute_t_n_and_e_2(const image2d<int_u8>& sub, image2d<int_u8>& e_2,
+ unsigned lambda_min, unsigned lambda_max,
+ unsigned s,
+ unsigned q, unsigned i, unsigned w,
+ const image2d<util::couple<double,double> >& integral_sum_sum_2)
+ {
+ typedef image2d<int_u8> I;
+ typedef point2d P;
+
+ unsigned ratio = std::pow(q, i - 2); // Ratio in comparison to e_2
+
+ unsigned
+ w_local = w * ratio,
+ w_local_h = w_local,
+ w_local_w = w_local;
+
+ if (! (w_local % 2))
+ {
+ --w_local_w;
+ ++w_local_h;
+ }
+
+ // 1st pass
+ scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
+ f(sub);
+ scribo::canvas::integral_browsing(integral_sum_sum_2,
+ ratio,
+ w_local_w, w_local_h,
+ s,
+ f);
+
+ // 2nd pass
+ {
+ util::array<mln_value_(I) *> ptr(ratio);
+ unsigned nrows = geom::nrows(e_2);
+
+ mln_box_runend_piter_(I) sp(sub.domain()); // Backward.
+ unsigned ncols = sp.run_length();
+ for_all(sp)
+ {
+ unsigned p = &sub(sp) - sub.buffer(); // Offset
+ P site = sp;
+
+ {
+ P tmp = site * ratio;
+
+ // FIXME: to be removed!
+ if (tmp.row() + ratio >= nrows)
+ ptr.resize(nrows - tmp.row());
+
+ ptr(0) = &e_2(tmp);
+ // FIXME: pointers could just be updated with an offset.
+ for (unsigned j = 1; j < ptr.size(); ++j)
+ {
+ tmp[0] += 1;
+ ptr(j) = & e_2(tmp);
+ }
+ }
+
+ for (unsigned j = 0; j < ncols; ++j)
+ {
+ if (f.msk.element(p))
+ {
+
+ mln_site_(I) sq = site * ratio;
+
+ if (f.parent.element(p) == p)
+ {
+ // test over the component cardinality
+ f.msk.element(p) = f.card.element(p) > lambda_min
+ && f.card.element(p) < lambda_max;
+
+ if (f.msk.element(p) && e_2(sq) == 0u)
+ {
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
+ }
+
+ }
+ else
+ {
+ // Propagation
+ f.msk.element(p) = f.msk.element(f.parent.element(p));
+
+ if (f.msk.element(p) && e_2(sq) == 0u)
+ {
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
+ }
+
+ }
+ }
+
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ ptr(l) -= ratio;
+
+ --site[1];
+ --p;
+ }
+
+ }
+ } // end of 2nd pass
+
+ return f.t_sub;
+ }
+
+
+
+ template <typename I, typename J, typename K>
+ mln_ch_value(I, bool)
+ multi_scale_binarization(const I& in, const J& e2,
+ const util::array<K>& t_ima,
+ unsigned s)
+ {
+ mln_ch_value(I,bool) out;
+ initialize(out, in);
+
+ typedef const mln_value(K)* ptr_type;
+
+ ptr_type ptr_t[5];
+ ptr_t[2] = & t_ima[2].at_(0, 0);
+ ptr_t[3] = & t_ima[3].at_(0, 0);
+ ptr_t[4] = & t_ima[4].at_(0, 0);
+
+
+ const mln_value(J)* ptr_e2 = & e2.at_(0, 0);
+ const mln_value(I)* ptr__in = & in.at_(0, 0);
+ bool* ptr__out = & out.at_(0, 0);
+
+
+ // Since we iterate from a smaller image in the largest ones and
+ // image at scale 1 does not always have a size which can be
+ // divided by (4*s), some sites in the border may not be processed
+ // and we must skip them.
+ int more_offset = - ((4 * s) - in.ncols() % (4 * s));
+
+ if (more_offset == - (static_cast<int>(4*s)))
+ more_offset = 0; // No offset needed.
+
+ const int
+ nrows4 = t_ima[4].nrows(), ncols4 = t_ima[4].ncols(),
+
+
+ delta1 = in.delta_index(dpoint2d(+1, -(s - 1))),
+ delta1b = in.delta_index(dpoint2d(+1, -(s + s - 1))),
+ delta1c = in.delta_index(dpoint2d(-(s + s - 1), +1)),
+ delta1d = in.delta_index(dpoint2d(+1, -(s * 4 - 1))),
+ delta1e = in.delta_index(dpoint2d(-(s * 4 - 1), +1)),
+ delta1f = in.delta_index(dpoint2d(-(s - 1), +1)),
+
+ delta2 = t_ima[2].delta_index(dpoint2d(+1, -1)),
+ delta2b = t_ima[2].delta_index(dpoint2d(+1, -3)),
+ delta2c = t_ima[2].delta_index(dpoint2d(-3, +1)),
+
+ delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
+
+ eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols())) + more_offset,
+ eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
+ eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
+ eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
+
+ mln_value(J) threshold;
+ for (int row4 = 0; row4 < nrows4; ++row4)
+ {
+ for (int col4 = 0; col4 < ncols4; ++col4)
+ {
+ // top left 1
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1c; ptr__in += delta1c;
+ }
+
+ ptr_t[2] -= delta2; ptr_e2 -= delta2;
+ }
+
+ // top right 1
+ ptr_t[3] += 1;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1d; ptr__in += delta1d;
+ }
+
+ ptr_t[2] += delta2b; ptr_e2 += delta2b;
+ }
+
+ // bot left 1
+ ptr_t[3] += delta3;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1c; ptr__in += delta1c;
+ }
+
+ ptr_t[2] -= delta2; ptr_e2 -= delta2;
+ }
+
+ // bot right 1
+ ptr_t[3] += 1;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1e; ptr__in += delta1e;
+ }
+ }
+
+ // bot right -> next top left
+ ptr_t[2] += delta2c; ptr_e2 += delta2c;
+ ptr_t[3] = ptr_t[3] - delta3;
+ ptr_t[4] += 1;
+ }
+
+ // eof -> next bof
+ ptr__out += eor1; ptr__in += eor1;
+ ptr_t[2] += eor2; ptr_e2 += eor2;
+ ptr_t[3] += eor3;
+ ptr_t[4] += eor4;
+ }
+
+ return out;
+ }
+
+
+
+ unsigned sub(unsigned nbr, unsigned down_scaling)
+ {
+ return (nbr + down_scaling - 1) / down_scaling;
+ }
+
+ // Compute domains of subsampled images and make sure they can be
+ // divided by 2.
+ template <typename I>
+ util::array<util::couple<mln_domain(I), unsigned> >
+ compute_sub_domains(const I& ima, unsigned n_scales, unsigned s)
+ {
+ util::array<util::couple<unsigned, unsigned> > n(n_scales + 2);
+
+ n(1) = make::couple(ima.nrows(), ima.ncols());
+ n(2) = make::couple(sub(n(1).first(), s),
+ sub(n(1).second(), s));
+ for (unsigned i = 3; i <= n_scales + 1; ++i)
+ n(i) = make::couple(sub(n(i - 1).first(), 2),
+ sub(n(i - 1).second(), 2));
+
+
+ util::array<util::couple<mln_domain(I), unsigned> > out(n.size());
+ out(0) = make::couple(make::box2d(1,1), 1u);
+ out(1) = make::couple(make::box2d(ima.nrows(), ima.ncols()), 2u);
+ out(n_scales + 1) = make::couple(make::box2d(n(n_scales + 1).first(),
+ n(n_scales + 1).second()),
+ 1u);
+
+ for (unsigned i = n_scales; i > 1; --i)
+ out(i) = make::couple(make::box2d(2 * out(i + 1).first().nrows(),
+ 2 * out(i + 1).first().ncols()),
+ 2 * out(i + 1).second());
+
+ out(1).second() = std::max(out(2).first().ncols() * s - ima.ncols(),
+ out(2).first().nrows() * s - ima.nrows());
+
+ return out;
+ }
+
+ template <typename I>
+ mln_ch_value(I,bool)
+ sauvola_ms(const Image<I>& input_1_, unsigned w_1,
+ unsigned s, unsigned lambda_min_1)
+ {
+ trace::entering("scribo::binarization::sauvola_ms");
+
+ const I& input_1 = exact(input_1_);
+
+ mlc_is_a(mln_value(I), value::Scalar)::check();
+ mln_precondition(input_1.is_valid());
+
+ dpoint2d none(0, 0);
+
+ // Number of subscales.
+ unsigned nb_subscale = 3;
+
+ // Window size.
+ unsigned w_work = w_1 / s; // Scale 2
+
+
+ // Subscale step.
+ unsigned q = 2;
+
+ unsigned lambda_min_2 = lambda_min_1 / s;
+ unsigned lambda_max_2 = lambda_min_2 * q;
+
+
+ util::array<I> t_ima;
+
+ // Make sure t_ima indexes start from 2.
+ {
+ I dummy(1,1);
+ for (unsigned i = 0; i < nb_subscale + 2; ++i)
+ t_ima.append(dummy);
+ }
+
+ util::array<I> sub_ima;
+
+ // Make sure sub_ima indexes start from 2.
+ {
+ I dummy(1,1);
+ sub_ima.append(dummy);
+ sub_ima.append(dummy);
+ }
+
+ util::array<util::couple<box2d, unsigned> >
+ sub_domains = compute_sub_domains(input_1, nb_subscale, s);
+
+ border::adjust(input_1, sub_domains(1).second());
+ border::mirror(input_1);
+
+
+ // Resize input and compute integral images.
+ typedef image2d<util::couple<double,double> > integral_t;
+ integral_t integral_sum_sum_2;
+
+ // Subsampling from scale 1 to 2.
+ sub_ima.append(scribo::subsampling::integral(input_1, s,
+ integral_sum_sum_2,
+ sub_domains[2].first(),
+ sub_domains[2].second()));
+
+
+ // Subsampling to scale 3 and 4.
+ for (unsigned i = 3; i <= nb_subscale + 1; ++i)
+ sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], q, none,
+ sub_domains[i].first(),
+ sub_domains[i].second()));
+
+
+ // Compute threshold images.
+ image2d<int_u8> e_2;
+ initialize(e_2, sub_ima[2]);
+ data::fill(e_2, 0u);
+
+ // Highest scale -> no maximum component size.
+ {
+ int i = sub_ima.size() - 1;
+ unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
+ t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
+ lambda_min_2 / ratio,
+ mln_max(unsigned),
+ s,
+ q, i, w_work, integral_sum_sum_2);
+ }
+
+ // Other scales -> maximum and minimum component size.
+ {
+ for (int i = sub_ima.size() - 2; i > 2; --i)
+ {
+ unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
+ t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
+ lambda_min_2 / ratio,
+ lambda_max_2 / ratio,
+ s,
+ q, i, w_work, integral_sum_sum_2);
+ }
+ }
+
+ // Lowest scale -> no minimum component size.
+ {
+ t_ima[2] = compute_t_n_and_e_2(sub_ima[2], e_2, 0, lambda_max_2,
+ s, 1, 2, w_work, integral_sum_sum_2);
+ }
+
+
+ // Propagate scale values.
+ e_2 = transform::influence_zone_geodesic(e_2, c8());
+
+
+ // Binarize
+ image2d<bool> output = multi_scale_binarization(input_1, e_2, t_ima, s);
+
+ trace::exiting("scribo::binarization::sauvola_ms");
+ return output;
+ }
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+
+#endif // SCRIBO_BINARIZATION_SAUVOLA_MS_HH
diff --git a/scribo/binarization/sauvola_threshold.hh b/scribo/binarization/sauvola_threshold.hh
index b1f9a81..20abc50 100644
--- a/scribo/binarization/sauvola_threshold.hh
+++ b/scribo/binarization/sauvola_threshold.hh
@@ -44,6 +44,8 @@
# include <mln/pw/all.hh>
# include <mln/core/routine/duplicate.hh>
+# include <mln/fun/v2v/rgb_to_int_u.hh>
+
# include <scribo/core/init_integral_image.hh>
@@ -96,16 +98,6 @@ namespace scribo
namespace internal
{
- struct rgb8_to_int_u8 : Function_v2v< rgb8_to_int_u8 >
- {
- typedef value::int_u8 result;
- result operator()(const value::rgb8& c) const
- {
- return (c.red() + c.green() + c.blue()) / 3;
- }
- };
-
-
/*! \brief compute Sauvola's threshold applying directly the formula.
\param[in] m_x_y Mean value.
@@ -383,7 +375,7 @@ namespace scribo
mln_ch_value(I, value::int_u8) gima;
gima = data::transform(input,
- internal::rgb8_to_int_u8());
+ mln::fun::v2v::rgb_to_int_u<8>());
mln_ch_value(I, value::int_u8)
output = impl::generic::sauvola_threshold(gima, window_size,
diff --git a/scribo/src/binarization/sauvola.cc b/scribo/src/binarization/sauvola.cc
index 28d3ae7..efe4787 100644
--- a/scribo/src/binarization/sauvola.cc
+++ b/scribo/src/binarization/sauvola.cc
@@ -33,6 +33,7 @@
const char *args_desc[][2] =
{
{ "input.ppm", "A color image." },
+ { "wsize", "Window size (Common value: 51)." },
{0, 0}
};
@@ -42,10 +43,10 @@ int main(int argc, char *argv[])
using namespace mln;
using value::rgb8;
- if (argc != 3)
+ if (argc != 4)
return scribo::debug::usage(argv,
"Binarization of a color image based on Sauvola's algorithm.",
- "input.ppm output.pbm",
+ "input.ppm wsize output.pbm",
args_desc, "A binary image.");
trace::entering("main");
@@ -53,7 +54,7 @@ int main(int argc, char *argv[])
image2d<rgb8> input;
io::ppm::load(input, argv[1]);
- io::pbm::save(scribo::binarization::sauvola(input), argv[2]);
+ io::pbm::save(scribo::binarization::sauvola(input, atoi(argv[2])), argv[3]);
trace::exiting("main");
diff --git a/scribo/src/binarization/sauvola_ms.cc b/scribo/src/binarization/sauvola_ms.cc
index ff5a99d..b3b4791 100644
--- a/scribo/src/binarization/sauvola_ms.cc
+++ b/scribo/src/binarization/sauvola_ms.cc
@@ -23,1300 +23,39 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#include <mln/core/alias/neighb2d.hh>
-#include <mln/data/stretch.hh>
-#include <mln/data/paste.hh>
-#include <mln/debug/iota.hh>
-#include <mln/debug/quiet.hh>
-#include <mln/debug/println.hh>
-#include <mln/debug/println_with_border.hh>
-#include <mln/debug/filename.hh>
-#include <mln/fun/i2v/array.hh>
-#include <mln/io/pbm/all.hh>
-#include <mln/io/pgm/all.hh>
-#include <mln/io/ppm/all.hh>
-#include <mln/literal/colors.hh>
-#include <mln/math/sqr.hh>
-#include <mln/math/abs.hh>
-
-#include <mln/subsampling/antialiased.hh>
-
-#include <mln/transform/influence_zone_geodesic.hh>
-#include <mln/util/timer.hh>
-#include <mln/value/int_u16.hh>
+#include <mln/core/image/image2d.hh>
#include <mln/value/int_u8.hh>
-#include <mln/value/label_16.hh>
-#include <mln/value/rgb8.hh>
-#include <mln/border/equalize.hh>
-#include <mln/border/mirror.hh>
-#include <mln/border/adjust.hh>
-#include <mln/debug/filename.hh>
-
-#include <mln/core/box_runend_piter.hh>
-#include <mln/core/box_runstart_piter.hh>
-
-#include <scribo/subsampling/integral_single_image.hh>
-//#include <scribo/subsampling/integral.hh>
-
-#include <scribo/core/macros.hh>
-#include <scribo/core/object_image.hh>
-
-#include <scribo/filter/objects_small.hh>
-#include <scribo/filter/objects_large.hh>
-
-#include <scribo/filter/objects_thin.hh>
-#include <scribo/filter/objects_thick.hh>
-
-#include <scribo/primitive/extract/objects.hh>
-
-#include <scribo/binarization/sauvola_threshold.hh>
-#include <scribo/binarization/internal/first_pass_functor.hh>
-
-#include <scribo/canvas/integral_browsing.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pbm/save.hh>
+#include <scribo/binarization/sauvola_ms.hh>
#include <scribo/debug/usage.hh>
-#include <scribo/debug/save_object_diff.hh>
-namespace mln
+bool check_args(int argc, char * argv[])
{
+ if (argc < 5 || argc > 6)
+ return false;
- using value::int_u8;
-
-
- unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
- {
- if (parent.element(x) == x)
- return x;
- return parent.element(x) = my_find_root(parent,
- parent.element(x));
- }
-
-
- image2d<int_u8>
- compute_t_n_and_e_2(const image2d<int_u8>& sub, image2d<int_u8>& e_2,
- unsigned lambda_min, unsigned lambda_max,
- unsigned s,
- unsigned q, unsigned i, unsigned w,
- const image2d<util::couple<double,double> >& integral_sum_sum_2)
- // lambdas: limits of component cardinality at this scale
- {
- typedef image2d<int_u8> I;
- typedef point2d P;
-
- util::timer tt;
- float t_;
-
- unsigned ratio = std::pow(q, i - 2); // Ratio in comparison to e_2
-
-
- tt.restart();
-
- unsigned
- w_local = w * ratio,
- w_local_h = w_local,
- w_local_w = w_local;
-
- if (! (w_local % 2))
- {
- --w_local_w;
- ++w_local_h;
- }
-
-// std::cout << "Echelle " << i
-// << " - w_local_h = " << w_local_h
-// << " - w_local_w = " << w_local_w
-// << " - w_1 = " << (w_local - 1) * s + 1
-// << std::endl;
-
-// std::cout << "Ratio = " << ratio << std::endl;
-// std::cout << " -----------" << std::endl;
-
- // 1st pass
- scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
- f(sub);
- scribo::canvas::integral_browsing(integral_sum_sum_2,
- ratio,
- w_local_w, w_local_h,
- s,
- f);
-
-
- t_ = tt;
- if (! mln::debug::quiet)
- std::cout << "1st pass - " << t_ << std::endl;
-
- tt.restart();
-
- {
- util::array<mln_value_(I) *> ptr(ratio);
- unsigned nrows = geom::nrows(e_2);
-
- mln_box_runend_piter_(I) sp(sub.domain()); // Backward.
- unsigned ncols = sp.run_length();
- for_all(sp)
- {
- unsigned p = &sub(sp) - sub.buffer(); // Offset
- P site = sp;
-
- {
- P tmp = site * ratio;
-
- // FIXME: to be removed!
- if (tmp.row() + ratio >= nrows)
- ptr.resize(nrows - tmp.row());
-
- ptr(0) = &e_2(tmp);
- // FIXME: pointers could just be updated with an offset.
- for (unsigned j = 1; j < ptr.size(); ++j)
- {
- tmp[0] += 1;
- ptr(j) = & e_2(tmp);
- }
- }
-
- for (unsigned j = 0; j < ncols; ++j)
- {
- if (f.msk.element(p))
- {
-
- mln_site_(I) sq = site * ratio;
-
- if (f.parent.element(p) == p)
- {
- // test over the component cardinality
- f.msk.element(p) = f.card.element(p) > lambda_min
- && f.card.element(p) < lambda_max;
-
- if (f.msk.element(p) && e_2(sq) == 0u)
- {
- for (unsigned l = 0; l < ptr.size(); ++l)
- std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
- }
-
- }
- else
- {
- // Propagation
- f.msk.element(p) = f.msk.element(f.parent.element(p));
-
- if (f.msk.element(p) && e_2(sq) == 0u)
- {
- for (unsigned l = 0; l < ptr.size(); ++l)
- std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
- }
-
- }
- }
-
- for (unsigned l = 0; l < ptr.size(); ++l)
- ptr(l) -= ratio;
-
- --site[1];
- --p;
- }
-
- }
-
- t_ = tt;
- if (! mln::debug::quiet)
- std::cout << "2nd pass - " << t_ << std::endl;
-
-// io::pgm::save(e_2, mln::debug::filename("e.pgm", i));
-// io::pbm::save(f.msk, mln::debug::filename("mask.pbm", i));
-// io::pgm::save(data::stretch(int_u8(), card), mln::debug::filename("card.pgm"));
- } // end of 2nd pass
-
-// io::pgm::save(f.t_sub, mln::debug::filename("t.pgm", i));
- return f.t_sub;
- }
-
-
-
- template <typename I, typename J, typename K>
- mln_ch_value(I, bool)
- binarize_generic(const I& in, const J& e2, const util::array<K>& t_ima,
- unsigned s)
- {
- mln_ch_value(I,bool) out;
- initialize(out, in);
-
- typedef const mln_value(K)* ptr_type;
-
- ptr_type ptr_t[5];
- ptr_t[2] = & t_ima[2].at_(0, 0);
- ptr_t[3] = & t_ima[3].at_(0, 0);
- ptr_t[4] = & t_ima[4].at_(0, 0);
-
-
- const mln_value(J)* ptr_e2 = & e2.at_(0, 0);
- const mln_value(I)* ptr__in = & in.at_(0, 0);
- bool* ptr__out = & out.at_(0, 0);
-
-
- // Since we iterate from a smaller image in the largest ones and
- // image at scale 1 does not always have a size which can be
- // divided by (4*s), some sites in the border may not be processed
- // and we must skip them.
- int more_offset = - ((4 * s) - in.ncols() % (4 * s));
-
- if (more_offset == - (static_cast<int>(4*s)))
- more_offset = 0; // No offset needed.
-
- const int
- nrows4 = t_ima[4].nrows(), ncols4 = t_ima[4].ncols(),
-
-
- delta1 = in.delta_index(dpoint2d(+1, -(s - 1))),
- delta1b = in.delta_index(dpoint2d(+1, -(s + s - 1))),
- delta1c = in.delta_index(dpoint2d(-(s + s - 1), +1)),
- delta1d = in.delta_index(dpoint2d(+1, -(s * 4 - 1))),
- delta1e = in.delta_index(dpoint2d(-(s * 4 - 1), +1)),
- delta1f = in.delta_index(dpoint2d(-(s - 1), +1)),
-
- delta2 = t_ima[2].delta_index(dpoint2d(+1, -1)),
- delta2b = t_ima[2].delta_index(dpoint2d(+1, -3)),
- delta2c = t_ima[2].delta_index(dpoint2d(-3, +1)),
-
- delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
-
- eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols())) + more_offset,
- eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
- eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
- eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
-
- mln_value(J) threshold;
- for (int row4 = 0; row4 < nrows4; ++row4)
- {
- for (int col4 = 0; col4 < ncols4; ++col4)
- {
- // top left 1
- {
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1b; ptr__in += delta1b;
- }
-
- ptr_t[2] += delta2; ptr_e2 += delta2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
-
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1c; ptr__in += delta1c;
- }
-
- ptr_t[2] -= delta2; ptr_e2 -= delta2;
- }
-
- // top right 1
- ptr_t[3] += 1;
- {
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1b; ptr__in += delta1b;
- }
-
- ptr_t[2] += delta2; ptr_e2 += delta2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1d; ptr__in += delta1d;
- }
-
- ptr_t[2] += delta2b; ptr_e2 += delta2b;
- }
-
- // bot left 1
- ptr_t[3] += delta3;
- {
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1b; ptr__in += delta1b;
- }
-
- ptr_t[2] += delta2; ptr_e2 += delta2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1c; ptr__in += delta1c;
- }
-
- ptr_t[2] -= delta2; ptr_e2 -= delta2;
- }
-
- // bot right 1
- ptr_t[3] += 1;
- {
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1b; ptr__in += delta1b;
- }
-
- ptr_t[2] += delta2; ptr_e2 += delta2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1f; ptr__in += delta1f;
- }
-
- ++ptr_t[2]; ++ptr_e2;
- threshold = *ptr_t[*ptr_e2];
- {
- for (unsigned i = 1; i < s; ++i)
- {
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
-
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1; ptr__in += delta1;
- }
-
- for (unsigned j = 1; j < s; ++j)
- {
- *ptr__out = *ptr__in < threshold;
- ++ptr__out; ++ptr__in;
- }
- *ptr__out = *ptr__in < threshold;
- ptr__out += delta1e; ptr__in += delta1e;
- }
- }
-
- // bot right -> next top left
- ptr_t[2] += delta2c; ptr_e2 += delta2c;
- ptr_t[3] = ptr_t[3] - delta3;
- ptr_t[4] += 1;
- }
-
- // eof -> next bof
- ptr__out += eor1; ptr__in += eor1;
- ptr_t[2] += eor2; ptr_e2 += eor2;
- ptr_t[3] += eor3;
- ptr_t[4] += eor4;
- }
-
-
-// mln::debug::println(out);
-
- return out;
- }
-
-
-
-// template <typename I, typename J, typename K>
-// mln_ch_value(I, unsigned)
-// binarize_generic_debug(const I& in, const J& e2, const util::array<K>& t_ima,
-// unsigned s)
-// {
-// mln_ch_value(I,unsigned) out;
-// initialize(out, in);
-// data::fill(out, 0);
-
-// typedef const mln_value(K)* ptr_type;
-
-// ptr_type ptr_t[5];
-// ptr_t[2] = & t_ima[2].at_(0, 0);
-// ptr_t[3] = & t_ima[3].at_(0, 0);
-// ptr_t[4] = & t_ima[4].at_(0, 0);
-
-
-// const mln_value(J)* ptr_e2 = & e2.at_(0, 0);
-// const mln_value(I)* ptr__in = & in.at_(0, 0);
-// unsigned* ptr__out = & out.at_(0, 0);
-
-
-// // Since we iterate from a smaller image in the largest ones and
-// // image at scale 1 does not always have a size which can be
-// // divided by (4*s), some sites in the border may not be processed
-// // and we must skip them.
-
-// std::cout << in.ncols() << std::endl;
-// std::cout << in.ncols() % (4 * s) << std::endl;
-// int more_offset = - ((4 * s) - in.ncols() % (4 * s));
-
-// if (more_offset == - (4*s))
-// more_offset = 0; // No offset needed.
-
-// std::cout << "more_offset == " << more_offset << std::endl;
-// std::cout << "- b1 = " << in.border()
-// << "- b2 = " << t_ima[2].border()
-// << "- b3 = " << t_ima[3].border()
-// << "- b4 = " << t_ima[4].border()
-// << std::endl;
-
-// const int
-// nrows4 = t_ima[4].nrows(), ncols4 = t_ima[4].ncols(),
-
-
-// delta1 = in.delta_index(dpoint2d(+1, -(s - 1))),
-// delta1b = in.delta_index(dpoint2d(+1, -(s + s - 1))),
-// delta1c = in.delta_index(dpoint2d(-(s + s - 1), +1)),
-// delta1d = in.delta_index(dpoint2d(+1, -(s * 4 - 1))),
-// delta1e = in.delta_index(dpoint2d(-(s * 4 - 1), +1)),
-// delta1f = in.delta_index(dpoint2d(-(s - 1), +1)),
-
-// delta2 = t_ima[2].delta_index(dpoint2d(+1, -1)),
-// delta2b = t_ima[2].delta_index(dpoint2d(+1, -3)),
-// delta2c = t_ima[2].delta_index(dpoint2d(-3, +1)),
-
-// delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
-
-// eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols())) + more_offset,
-// eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
-// eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
-// eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
-
-// unsigned pid = 0;
-
-// mln_value(J) threshold;
-// for (int row4 = 0; row4 < nrows4; ++row4)
-// {
-// for (int col4 = 0; col4 < ncols4; ++col4)
-// {
-// // top left 1
-// {
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1b; ptr__in += delta1b;
-// }
-
-// ptr_t[2] += delta2; ptr_e2 += delta2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1c; ptr__in += delta1c;
-// }
-
-// ptr_t[2] -= delta2; ptr_e2 -= delta2;
-// }
-
-// // top right 1
-// ptr_t[3] += 1;
-// {
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1b; ptr__in += delta1b;
-// }
-
-// ptr_t[2] += delta2; ptr_e2 += delta2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1d; ptr__in += delta1d;
-// }
+ int s = atoi(argv[3]);
-// ptr_t[2] += delta2b; ptr_e2 += delta2b;
-// }
-
-// // bot left 1
-// ptr_t[3] += delta3;
-// {
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1b; ptr__in += delta1b;
-// }
-
-// ptr_t[2] += delta2; ptr_e2 += delta2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1c; ptr__in += delta1c;
-// }
-
-// ptr_t[2] -= delta2; ptr_e2 -= delta2;
-// }
-
-// // bot right 1
-// ptr_t[3] += 1;
-// {
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1b; ptr__in += delta1b;
-// }
-
-// ptr_t[2] += delta2; ptr_e2 += delta2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1f; ptr__in += delta1f;
-// }
-
-// ++ptr_t[2]; ++ptr_e2;
-// threshold = *ptr_t[*ptr_e2];
-// {
-// for (unsigned i = 1; i < s; ++i)
-// {
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-
-// *ptr__out = pid++;
-// ptr__out += delta1; ptr__in += delta1;
-// }
-
-// for (unsigned j = 1; j < s; ++j)
-// {
-// *ptr__out = pid++;
-// ++ptr__out; ++ptr__in;
-// }
-// *ptr__out = pid++;
-// ptr__out += delta1e; ptr__in += delta1e;
-// }
-// }
-
-// // bot right -> next top left
-// ptr_t[2] += delta2c; ptr_e2 += delta2c;
-// ptr_t[3] = ptr_t[3] - delta3;
-// ptr_t[4] += 1;
-// }
-
-// // eof -> next bof
-// ptr__out += eor1; ptr__in += eor1;
-// ptr_t[2] += eor2; ptr_e2 += eor2;
-// ptr_t[3] += eor3;
-// ptr_t[4] += eor4;
-// }
-
-
-// // mln::debug::println(out);
-
-// return out;
-// }
-
-
-
-
- unsigned sub(unsigned nbr, unsigned down_scaling)
- {
- return (nbr + down_scaling - 1) / down_scaling;
- }
-
- template <typename I>
- util::array<util::couple<mln_domain(I), unsigned> >
- compute_sub_domains(const I& ima, unsigned n_scales, unsigned s)
+ if (s < 1 || s > 3)
{
- util::array<util::couple<unsigned, unsigned> > n(n_scales + 2);
-
- n(1) = make::couple(ima.nrows(), ima.ncols());
- n(2) = make::couple(sub(n(1).first(), s),
- sub(n(1).second(), s));
- for (unsigned i = 3; i <= n_scales + 1; ++i)
- n(i) = make::couple(sub(n(i - 1).first(), 2),
- sub(n(i - 1).second(), 2));
-
-
- util::array<util::couple<mln_domain(I), unsigned> > out(n.size());
- out(0) = make::couple(make::box2d(1,1), 1u);
- out(1) = make::couple(make::box2d(ima.nrows(), ima.ncols()), 2u);
- out(n_scales + 1) = make::couple(make::box2d(n(n_scales + 1).first(),
- n(n_scales + 1).second()),
- 1u);
-
- for (unsigned i = n_scales; i > 1; --i)
- out(i) = make::couple(make::box2d(2 * out(i + 1).first().nrows(),
- 2 * out(i + 1).first().ncols()),
- 2 * out(i + 1).second());
-
- out(1).second() = std::max(out(2).first().ncols() * s - ima.ncols(),
- out(2).first().nrows() * s - ima.nrows());
-
-// out(1).second() = std::max(ima.ncols() % (4 * s),
-// ima.nrows() % (4 * s));
-
-// out(2).first().ncols() * s - ima.ncols(),
-// out(2).first().nrows() * s - ima.nrows() );
-
- return out;
- }
-
-
-
- bool
- check_args(int argc, char * argv[])
- {
- if (argc < 5 || argc > 7)
- return false;
-
-// int nb_scale = atoi(argv[3]);
- int s = atoi(argv[3]);
-// int q = atoi(argv[5]);
-
-// if (q < 2)
-// {
-// std::cout << "q must be greater than 2." << std::endl;
-// return false;
-// }
- if (s < 1 || s > 3)// || s < q)
- {
- std::cout << "s must be set to 2 or 3."
- << std::endl;
- return false;
- }
-
-
-// if (nb_scale < 1)
-// {
-// std::cout << "Not enough scales." << std::endl;
-// return false;
-// }
-
-// if ((argc - 7) != (nb_scale - 1))
-// {
-// std::cout << "Not enough area threshold."
-// << "There must be nb_scale - 1 thresholds."
-// << std::endl;
-// return false;
-// }
-
- return true;
- }
-
-
- void data_rand(image2d<unsigned>& e2)
- {
- unsigned v = 2;
- mln_piter_(box2d) p(e2.domain());
- for_all(p)
- {
- e2(p) = v++;
- if (v == 5) v = 2;
- }
+ std::cout << "s must be set to 2 or 3."
+ << std::endl;
+ return false;
}
-
-} // end of namespace mln;
-
-
+ return true;
+}
const char *args_desc[][2] =
{
{ "input.pgm", "A graylevel image." },
{ "w", "Window size at scale 1. (Common value: 101)" },
- { "s", "First subsampling ratio (Common value: 2)." },
- { "min_area", "Minimum object area (at scale 2) (Common value: 200)" },
- { "debug", "Display debug/bench data if set to 1" },
+ { "s", "First subsampling ratio (Common value: 3)." },
+ { "min_area", "Minimum object area at scale 1 (Common value: 67)" },
{0, 0}
};
@@ -1327,71 +66,36 @@ int main(int argc, char *argv[])
{
using namespace mln;
using namespace scribo;
- using value::rgb8;
- using value::int_u8;
- using value::int_u16;
- using value::label_16;
-
- typedef image2d<label_16> L;
if (!check_args(argc, argv))
return scribo::debug::usage(argv,
"Multi-Scale Binarization of a color image based on Sauvola's algorithm.",
- "input.pgm w s area_thresholds output.pbm [debug]",
+ "input.pgm w s area_threshold output.pbm",
args_desc, "A binary image.");
trace::entering("main");
- mln::debug::internal::filename_prefix = argv[1];
+ // Window size
+ unsigned w_1 = atoi(argv[2]); // Scale 1
// First subsampling scale.
unsigned s = atoi(argv[3]);
- // Window size.
- unsigned
- w_1 = atoi(argv[2]), // Scale 1
- w_work = w_1 / s; // Scale 2
-
- // Number of subscales.
- unsigned nb_subscale = 3;//atoi(argv[3]);
+ // Lambda value
+ unsigned lambda_min_1 = atoi(argv[4]);
- // Subscale step.
- unsigned q = 2;//atoi(argv[5]);
-
- mln::debug::quiet = true;
-
- if (argc == 7)
- mln::debug::quiet = ! atoi(argv[6]);
-
-
- if (! mln::debug::quiet)
- std::cout << "Running Sauvola_ms with w_1 = " << w_1
- << ", s = " << s
- << ", nb_subscale = " << nb_subscale
- << ", q = " << q
- << std::endl;
- typedef image2d<value::int_u8> I;
- dpoint2d none(0, 0);
-
- mln::util::timer
- timer_,
- sauvola_timer_;;
-
- // Tmp variable used for timer results;
- float t_;
-
- I input_full;
- io::pgm::load(input_full, argv[1]);
+ image2d<value::int_u8> input_1;
+ io::pgm::load(input_1, argv[1]);
{
- unsigned max_dim = math::max(input_full.ncols() / s,
- input_full.nrows() / s);
- if (w_work > max_dim)
+ unsigned max_dim = math::max(input_1.ncols(),
+ input_1.nrows());
+ if (w_1 > max_dim)
{
std::cout << "------------------" << std::endl;
std::cout << "The window is too large! Image size is only "
- << input_full.nrows() / s << "x" << input_full.ncols() / s
+ << input_1.nrows() << "x" << input_1.ncols()
<< std::endl
<< "Window size must not exceed " << max_dim
<< std::endl;
@@ -1399,324 +103,11 @@ int main(int argc, char *argv[])
}
}
-// I input_full(9,9);
-// mln::debug::iota(input_full);
-
- sauvola_timer_.start();
-
- unsigned lambda_min = atoi(argv[4]);
- unsigned lambda_max = lambda_min * q; // * atoi(argv[7])
-
-
- util::array<I> t_ima;
-
- // Make sure t_ima indexes start from 2.
- {
- I dummy(1,1);
- for (unsigned i = 0; i < nb_subscale + 2; ++i)
- t_ima.append(dummy);
- }
+ image2d<bool>
+ output = scribo::binarization::sauvola_ms(input_1, w_1, s, lambda_min_1);
-
- image2d<int_u8> e_2;
- util::array<I> sub_ima;
-
- // Make sure sub_ima indexes start from 2.
- {
- I dummy(1,1);
- sub_ima.append(dummy);
- sub_ima.append(dummy);
- }
-
-
- timer_.restart();
-
- util::array<util::couple<box2d, unsigned> >
- sub_domains = compute_sub_domains(input_full, nb_subscale, s);
-
- if (! mln::debug::quiet)
- std::cout << "adjusting input border to " << sub_domains(1).second()
- << std::endl;
-
- border::adjust(input_full, sub_domains(1).second());
- border::mirror(input_full);
-
-// mln::debug::println_with_border(input_full);
-
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "sub domains computed and adjust input border size - "
- << t_ << std::endl;
-
- // Resize input and compute integral images.
- timer_.restart();
- typedef image2d<util::couple<double,double> > integral_t;
- integral_t integral_sum_sum_2;
-
- if (! mln::debug::quiet)
- std::cout << "sub_domain(2).domain() == " << sub_domains(2).first() << std::endl;
-
- sub_ima.append(scribo::subsampling::integral(input_full, s,
- integral_sum_sum_2,
- sub_domains[2].first(),
- sub_domains[2].second()));
-
-// {
-// io::pgm::save(sub_ima[2], "in_50p.pgm");
-// mln_piter_(integral_t) p(integral_sum_sum_2.domain());
-// for_all(p)
-// {
-// std::cout << integral_sum_sum_2(p).first() << ", ";
-// }
-// std::cout << std::endl << " ------- " << std::endl;
-
-// for_all(p)
-// {
-// std::cout << integral_sum_sum_2(p).second() << ", ";
-// }
-// std::cout << std::endl << " ------- " << std::endl;
-// }
-
-// mln::debug::println(integral_sum_sum_2);
-
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "subsampling 1 -> 2 And integral images - " << t_
- << " - nsites = "
- << input_full.domain().nsites() << " -> "
- << sub_ima[2].domain().nsites() << " - "
- << input_full.domain() << " -> "
- << sub_ima[2].domain() << std::endl;
-
-
- for (unsigned i = 3; i <= nb_subscale + 1; ++i)
- {
- timer_.restart();
- sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], q, none,
- sub_domains[i].first(),
- sub_domains[i].second()));
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "subsampling " << (i - 1) << " -> " << i
- << " - " << t_
- << " - nsites = "
- << sub_ima[i].domain().nsites() << " - "
- << sub_ima[i].domain()
- << std::endl;
- }
-
-
- initialize(e_2, sub_ima[2]);
- data::fill(e_2, 0u);
-
- // Compute threshold image.
-
- // Highest scale -> no maximum component size.
- {
- timer_.restart();
- int i = sub_ima.size() - 1;
- unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
- t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
- lambda_min / ratio,
- mln_max(unsigned),
- s,
- q, i, w_work, integral_sum_sum_2);
-
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "Scale " << i
- << " - 1/" << s * ratio
- << " compute t_n and update e - " << t_ << std::endl;
- }
-
- // Other scales -> maximum and minimum component size.
- {
- for (int i = sub_ima.size() - 2; i > 2; --i)
- {
- timer_.restart();
- unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
- t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
- lambda_min / ratio,
- lambda_max / ratio,
- s,
- q, i, w_work, integral_sum_sum_2);
-
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "Scale " << i
- << " - 1/" << s * ratio
- << " compute t_n and update e - " << t_ << std::endl;
- }
- }
-
- // Lowest scale -> no minimum component size.
- {
- timer_.restart();
- t_ima[2] = compute_t_n_and_e_2(sub_ima[2], e_2, 0, lambda_max,
- s, 1, 2, w_work, integral_sum_sum_2);
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "Scale " << 2
- << " - 1/" << s
- << " compute t_n and update e - " << t_ << std::endl;
-
- }
-
- if (! mln::debug::quiet)
- std::cout << "--------" << std::endl;
-// io::pgm::save(e_2, mln::debug::filename("e.pgm"));
-
-
-
- timer_.restart();
- e_2 = transform::influence_zone_geodesic(e_2, c8());
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "influence zone - " << t_ << std::endl;
-
-
-
-// Saving influence zone scale image.
-// io::pgm::save(e_2, mln::debug::filename("e_ext.pgm"));
-// io::pbm::save(bin, argv[8]);
-// io::pgm::save(t, mln::debug::filename("t.pgm"));
-
-// for (unsigned i = 2; i < t_ima.size(); ++i)
-// io::pgm::save(t_ima[i], mln::debug::filename("t.pgm", i));
-
-// {
-// image2d<bool> out_2;
-// initialize(out_2, e_2);
-// mln_piter_(image2d<int_u8>) p(e_2.domain());
-// for_all(p)
-// {
-// out_2(p) = sub_ima[2](p) < t_ima[2](p);
-// }
-// io::pbm::save(out_2, argv[5]);
-// }
-
-
- timer_.restart();
- image2d<bool> out_new = binarize_generic(input_full, e_2, t_ima, s);
-// image2d<bool> out_new = binarize(input_full, e_2, t_ima);
- t_ = timer_;
- if (! mln::debug::quiet)
- std::cout << "Compute bin - " << t_ << std::endl;
-
- t_ = sauvola_timer_;
- if (! mln::debug::quiet)
- std::cout << "Sauvola : " << t_ << std::endl;
-
- io::pbm::save(out_new, argv[5]);
-// abort();
+ io::pbm::save(output, argv[5]);
}
-
-
-// int main(int argc, char *argv[])
-// {
-// using namespace mln;
-// using namespace scribo;
-// using value::rgb8;
-// using value::int_u8;
-// using value::int_u16;
-// using value::label_16;
-
-// typedef image2d<label_16> L;
-
-// unsigned s = atoi(argv[3]);
-
-
-// typedef image2d<value::int_u8> I;
-// dpoint2d none(0, 0);
-
-// I input_full(atoi(argv[1]),atoi(argv[2]));
-// // I input_full(30,20); // Cas pourri
-// // I input_full(30,24); // Cas 'row <<'
-// // I input_full(36,24); // Cas ideal
-// // I input_full(36,20); // Cas 'col <<'
-// mln::debug::iota(input_full);
-
-// mln::debug::println(input_full);
-
-
-// util::array<I> sub_ima;
-// util::array<I> t_ima;
-
-
-// // Make sure t_ima indexes start from 2.
-// {
-// I dummy(1,1);
-// for (unsigned i = 0; i < 3 + 2; ++i)
-// t_ima.append(dummy);
-// }
-
-// // Make sure sub_ima indexes start from 2.
-// {
-// I dummy(1,1);
-// sub_ima.append(dummy);
-// sub_ima.append(dummy);
-// }
-
-
-
-// util::array<util::couple<box2d, unsigned> >
-// sub_domains = compute_sub_domains(input_full, 3, s);
-
-// border::adjust(input_full, sub_domains(1).second());
-// border::mirror(input_full);
-
-// // Resize input and compute integral images.
-// typedef image2d<util::couple<double,double> > integral_t;
-// integral_t integral_sum_sum_2;
-
-// sub_ima.append(scribo::subsampling::integral(input_full, s,
-// integral_sum_sum_2,
-// sub_domains[2].first(),
-// sub_domains[2].second()));
-
-// std::cout << "input border = " << input_full.border() << std::endl;
-// std::cout << "subsampling 1 -> 2 And integral images - "
-// << " - nsites = "
-// << input_full.domain().nsites() << " -> "
-// << sub_ima[2].domain().nsites() << " - "
-// << input_full.domain() << " -> "
-// << sub_ima[2].domain() << std::endl;
-
-
-// for (unsigned i = 3; i <= 3 + 1; ++i)
-// {
-// sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], 2, none,
-// sub_domains[i].first(),
-// sub_domains[i].second()));
-// std::cout << "subsampling " << (i - 1) << " -> " << i
-// << " - "
-// << " - nsites = "
-// << sub_ima[i].domain().nsites() << " - "
-// << sub_ima[i].domain()
-// << std::endl;
-// t_ima[i] = I(sub_ima[i].domain());
-// }
-
-
-
-
-
-// image2d<int_u8> e_2;
-// initialize(e_2, sub_ima[2]);
-// data::fill(e_2, 2);
-
-// data::fill(t_ima[2], 30);
-
-// image2d<unsigned> out_new = binarize_generic_debug(input_full, e_2, t_ima, s);
-
-// mln::debug::println_with_border(out_new);
-// // io::pgm::save(out_new, "out.pgm");
-
-// std::cout << "------------" << std::endl;
-
-// // out_new = binarize_generic_debug(input_full, e_2, t_ima, 2);
-// // mln::debug::println(out_new);
-// }
--
1.5.6.5
1
0
11 Mar '10
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch exp/milena-z has been updated
via ce6a4248b4d98c34ba5ec5022ae68cc240553dd4 (commit)
via 706148bd7bd2a1138c2aee91f0e1463540deaa52 (commit)
from 78149d2acd630f6988e0cbca5682f91213540416 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
No new revisions were added by this update.
-----------------------------------------------------------------------
Summary of changes:
milena/ChangeLog | 13 ++++
milena/mln/draw/{line.hh => dashed_line.hh} | 60 +++++++++++-----
milena/mln/fun/all.hh | 2 +
milena/mln/fun/{stat => n2v}/all.hh | 17 ++---
.../fun/{v2w2v/cos.hh => n2v/white_gaussian.hh} | 75 +++++++++++--------
5 files changed, 106 insertions(+), 61 deletions(-)
copy milena/mln/draw/{line.hh => dashed_line.hh} (61%)
copy milena/mln/fun/{stat => n2v}/all.hh (79%)
copy milena/mln/fun/{v2w2v/cos.hh => n2v/white_gaussian.hh} (61%)
hooks/post-receive
--
Olena, a generic and efficient image processing platform
1
0
11 Mar '10
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch exp/scribo-z has been created
at fd14f7491b6b1e1fd681245831975fb2b0cbf444 (commit)
- Log -----------------------------------------------------------------
fd14f74 Add a new example for finding text in pictures.
3395e22 First draft of the dematerialization wizard.
04e5e93 Update examples.
765dcd0 Improve text line merging algorithm.
f334432 Add a reconstruction algorithm.
908bb22 Small fixes.
c79f409 Add new preprocessing routines.
a8aeee1 Update code according to last changes in Scribo core classes.
2773488 Improve debug routines in Scribo.
3ddc051 Improve core classes in Scribo.
83e3ae6 Share internal data in groups and links structures.
5fe933b text/merging.hh (scribo): Update code with lastest Theo's changes.
56ab608 Add a small tool to recognize text in a binary image.
d957e47 mln/labeling/colorize.hh: Add support for value::qt::rgb32.
466ad51 Add a subsampling algorithm.
9d7ad31 Add a first draft of a text line merging algorithm.
65c1cd1 Add and update examples.
d7f2adc Small fixes.
ad5f900 Keep updating code according to last changed in core classes.
7c750d4 Improve core classes.
27b5f71 configure.ac: Configure scribo/demo/review.
800a27b Move Scribo demo.
b41ec08 Small fixes in Milena.
41b6112 io/xml/save_text_lines.hh: New. Add partial support for PageContent XML format.
b3822e8 Introduce new Scribo core classes and start using them.
c24feb9 Add anchor support in debug routines.
33d6df3 Add new link filters.
17faab3 Improve object linking backend.
55f2abf scribo/filter/objects_with_holes.hh: New component filter.
db3dc85 scribo/draw/bounding_boxes.hh: Do not draw box centers anymore.
7681799 Add dedicated routines for AFP's use case.
22cac49 Add new tools in Scribo.
e7ca877 scribo/src/text_in_photo_ppm.cc: Improve output.
750fe21 Cleanup and avoid warnings in Sauvola related files.
c24686e configure.ac: Configure scribo/tests/core.
4026193 Add a new test for object_image.
80b330e primitive/extract/objects.hh: Compute and store mass centers.
0686d02 Fix object links filters.
6db6d75 Cleanup Sauvola's binarization code.
8d114a2 core/object_image.hh: Make use of mln::labeled_image_base.
c7f2301 Improve linking routines.
3395979 Add rbg_to_int_u function.
567ca25 Add an antialiased subsampling.
495dd2d Add hooks for subclasses in labeled_image_base.
27ae9e6 Improve stats computation in Sauvola Multi-scale.
c6851fd Optimize Sauvola's multiscale binarization.
ce6a424 mln/draw/dashed_line.hh: New draw line routine.
706148b Add white gaussian noise function.
-----------------------------------------------------------------------
hooks/post-receive
--
Olena, a generic and efficient image processing platform
1
0
* src/text_in_photo_ppm_fast.cc,
* src/debug/show_links_bottom_aligned.cc,
* src/debug/show_links_top_aligned.cc: Update calls to routines.
* src/preprocessing/trash.cc,
* src/text_in_photo_ppm_fast_2.cc,
* src/preprocessing/split_bg_fg_ms.cc,
* src/preprocessing/denoising.cc: Removed.
* src/preprocessing/denoise.cc,
* src/preprocessing/homogeneous_contrast.ccm,
* src/preprocessing/subsample.cc: New.
* src/preprocessing/Makefile.am: Add new files as target.
* src/preprocessing/split_bg_fg.cc: Fix usage.
* src/text_in_article.cc: Update processing chain.
---
scribo/ChangeLog | 23 +
scribo/src/debug/show_links_bottom_aligned.cc | 24 +-
scribo/src/debug/show_links_top_aligned.cc | 22 +-
scribo/src/preprocessing/Makefile.am | 13 +-
.../denoise.cc} | 31 +-
scribo/src/preprocessing/denoising.cc | 115 -----
scribo/src/preprocessing/homogeneous_contrast.cc | 44 ++
scribo/src/preprocessing/split_bg_fg.cc | 7 +-
scribo/src/preprocessing/split_bg_fg_ms.cc | 109 ----
.../subsample.cc} | 34 +-
scribo/src/text_in_article.cc | 289 +++--------
scribo/src/text_in_photo_ppm_fast.cc | 55 +-
scribo/src/text_in_photo_ppm_fast_2.cc | 541 --------------------
13 files changed, 242 insertions(+), 1065 deletions(-)
copy scribo/src/{filter/objects_with_holes.cc => preprocessing/denoise.cc} (70%)
delete mode 100644 scribo/src/preprocessing/denoising.cc
create mode 100644 scribo/src/preprocessing/homogeneous_contrast.cc
delete mode 100644 scribo/src/preprocessing/split_bg_fg_ms.cc
copy scribo/src/{text/pbm_recognition.cc => preprocessing/subsample.cc} (74%)
delete mode 100644 scribo/src/text_in_photo_ppm_fast_2.cc
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index f5deefc..87985ff 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,28 @@
2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Update examples.
+
+ * src/text_in_photo_ppm_fast.cc,
+ * src/debug/show_links_bottom_aligned.cc,
+ * src/debug/show_links_top_aligned.cc: Update calls to routines.
+
+ * src/preprocessing/trash.cc,
+ * src/text_in_photo_ppm_fast_2.cc,
+ * src/preprocessing/split_bg_fg_ms.cc,
+ * src/preprocessing/denoising.cc: Removed.
+
+ * src/preprocessing/denoise.cc,
+ * src/preprocessing/homogeneous_contrast.ccm,
+ * src/preprocessing/subsample.cc: New.
+
+ * src/preprocessing/Makefile.am: Add new files as target.
+
+ * src/preprocessing/split_bg_fg.cc: Fix usage.
+
+ * src/text_in_article.cc: Update processing chain.
+
+2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Improve text line merging algorithm.
* text/merging.hh: Change merge rules and improve integration with
diff --git a/scribo/src/debug/show_links_bottom_aligned.cc b/scribo/src/debug/show_links_bottom_aligned.cc
index 4751308..337032a 100644
--- a/scribo/src/debug/show_links_bottom_aligned.cc
+++ b/scribo/src/debug/show_links_bottom_aligned.cc
@@ -37,7 +37,9 @@
#include <mln/io/pbm/load.hh>
#include <mln/io/ppm/save.hh>
-#include <scribo/primitive/extract/objects.hh>
+#include <scribo/core/component_set.hh>
+
+#include <scribo/primitive/extract/components.hh>
#include <scribo/primitive/link/with_single_right_link_bottom.hh>
#include <scribo/filter/object_links_bottom_aligned.hh>
@@ -49,6 +51,7 @@
const char *args_desc[][2] =
{
{ "input.pbm", "A binary image. True for objects and False for the background." },
+ { "max_dist", "Maximum distance lookup (common value 45)" },
{ "max_alpha", "Max angle between two object bottoms. (common value : 5)" },
{0, 0}
};
@@ -60,37 +63,38 @@ int main(int argc, char* argv[])
using namespace scribo::primitive::internal;
using namespace mln;
- if (argc != 4)
+ if (argc != 5)
return scribo::debug::usage(argv,
"Show valid or invalid links according the horizontal alignment (based on bottom line).",
- "input.pbm max_alpha output.ppm",
+ "input.pbm max_dist max_alpha output.ppm",
args_desc,
"A color image. Valid links are drawn in green, invalid ones in red.");
image2d<bool> input;
io::pbm::load(input, argv[1]);
- // Finding objects.
+ // Finding components.
value::label_16 nbboxes;
typedef image2d<value::label_16> L;
- object_image(L) objects
- = scribo::primitive::extract::objects(input, c8(), nbboxes);
+ component_set<L> components
+ = scribo::primitive::extract::components(input, c8(), nbboxes);
// Finding right links.
object_links<L> right_links
- = primitive::link::with_single_right_link_bottom(objects);
+ = primitive::link::with_single_right_link_bottom(components, atoi(argv[2]));
// Filtering.
object_links<L> filtered_links
- = filter::object_links_bottom_aligned(objects, right_links, atof(argv[2]));
+ = filter::object_links_bottom_aligned(right_links, atof(argv[3]));
// Debug image.
image2d<value::rgb8> decision_image
= scribo::debug::alignment_decision_image(input,
right_links,
filtered_links,
- scribo::debug::bottom);
- io::ppm::save(decision_image, argv[3]);
+ scribo::debug::bottom,
+ atoi(argv[2]));
+ io::ppm::save(decision_image, argv[4]);
}
diff --git a/scribo/src/debug/show_links_top_aligned.cc b/scribo/src/debug/show_links_top_aligned.cc
index 97b4a3b..ff01221 100644
--- a/scribo/src/debug/show_links_top_aligned.cc
+++ b/scribo/src/debug/show_links_top_aligned.cc
@@ -37,7 +37,7 @@
#include <mln/io/pbm/load.hh>
#include <mln/io/ppm/save.hh>
-#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/extract/components.hh>
#include <scribo/primitive/link/with_single_right_link_top.hh>
#include <scribo/filter/object_links_top_aligned.hh>
@@ -49,6 +49,7 @@
const char *args_desc[][2] =
{
{ "input.pbm", "A binary image. True for objects and False for the background." },
+ { "max_dist", "Maximum distance lookup (common value 45)" },
{ "max_alpha", "Max angle between two object tops. (common value : 5)" },
{0, 0}
};
@@ -60,30 +61,30 @@ int main(int argc, char* argv[])
using namespace scribo::primitive::internal;
using namespace mln;
- if (argc != 4)
+ if (argc != 5)
return scribo::debug::usage(argv,
"Show valid or invalid links according the horizontal alignment (based on top line).",
- "input.pbm max_alpha output.ppm",
+ "input.pbm max_dist max_alpha output.ppm",
args_desc,
"A color image. Valid links are drawn in green, invalid ones in red.");
image2d<bool> input;
io::pbm::load(input, argv[1]);
- // Finding objects.
+ // Finding components.
value::label_16 nbboxes;
typedef image2d<value::label_16> L;
- object_image(L) objects
- = scribo::primitive::extract::objects(input, c8(), nbboxes);
+ component_set<L> components
+ = scribo::primitive::extract::components(input, c8(), nbboxes);
// Finding right links.
object_links<L> right_links
- = primitive::link::with_single_right_link_top(objects);
+ = primitive::link::with_single_right_link_top(components, atoi(argv[2]));
// Filtering.
object_links<L> filtered_links
- = filter::object_links_top_aligned(objects, right_links, atof(argv[2]));
+ = filter::object_links_top_aligned(right_links, atof(argv[3]));
// Debug image.
@@ -91,7 +92,8 @@ int main(int argc, char* argv[])
= scribo::debug::alignment_decision_image(input,
right_links,
filtered_links,
- scribo::debug::top);
- io::ppm::save(decision_image, argv[3]);
+ scribo::debug::top,
+ atoi(argv[2]));
+ io::ppm::save(decision_image, argv[4]);
}
diff --git a/scribo/src/preprocessing/Makefile.am b/scribo/src/preprocessing/Makefile.am
index 2422d87..501233a 100644
--- a/scribo/src/preprocessing/Makefile.am
+++ b/scribo/src/preprocessing/Makefile.am
@@ -1,4 +1,5 @@
-# Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE).
+# Copyright (C) 2009, 2010 EPITA Research and Development Laboratory
+# (LRDE).
#
# This file is part of Olena.
#
@@ -20,14 +21,16 @@
include $(top_srcdir)/scribo/scribo.mk
bin_PROGRAMS = \
- denoising \
+ denoise \
+ homogeneous_contrast \
preprocess \
split_bg_fg \
- split_bg_fg_ms \
+ subsample \
unskew
-denoising_SOURCES = denoising.cc
+denoise_SOURCES = denoise.cc
+homogeneous_contrast_SOURCES = homogeneous_contrast.cc
preprocess_SOURCES = preprocess.cc
split_bg_fg_SOURCES = split_bg_fg.cc
-split_bg_fg_ms_SOURCES = split_bg_fg_ms.cc
+subsample_SOURCES = subsample.cc
unskew_SOURCES = unskew.cc
diff --git a/scribo/src/filter/objects_with_holes.cc b/scribo/src/preprocessing/denoise.cc
similarity index 70%
copy from scribo/src/filter/objects_with_holes.cc
copy to scribo/src/preprocessing/denoise.cc
index 0ebf89f..0eb567b 100644
--- a/scribo/src/filter/objects_with_holes.cc
+++ b/scribo/src/preprocessing/denoise.cc
@@ -1,4 +1,5 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of Olena.
//
@@ -24,30 +25,31 @@
// executable file might be covered by the GNU General Public License.
#include <mln/core/image/image2d.hh>
-#include <mln/core/alias/neighb2d.hh>
-#include <mln/data/convert.hh>
#include <mln/io/pbm/all.hh>
-#include <mln/value/label_16.hh>
-#include <scribo/filter/objects_with_holes.hh>
#include <scribo/debug/usage.hh>
+#include <scribo/preprocessing/denoise.hh>
+
+
const char *args_desc[][2] =
{
{ "input.pbm", "A binary image. 'True' for objects, 'False'\
for the background." },
- { "min_holes_count", "The minimum holes per objects." },
+ { "fg_min_card", "The minimum neighbor count to be set to true." },
+ { "bg_min_card", "The minimum neighbor count to be set to false." },
{0, 0}
};
+
int main(int argc, char *argv[])
{
using namespace mln;
- if (argc != 4)
+ if (argc != 5)
return scribo::debug::usage(argv,
- "Filter objects with holes",
- "input.pbm min_holes_count output.pbm",
+ "Remove noise.",
+ "input.pbm fg_min_card bg_min_card output.pbm",
args_desc,
"A binary image.");
@@ -57,14 +59,11 @@ int main(int argc, char *argv[])
I input;
io::pbm::load(input, argv[1]);
- value::label_16 nobjects;
- typedef object_image(mln_ch_value_(I,value::label_16)) obj_ima_t;
- obj_ima_t objects
- = scribo::primitive::extract::objects(input, c8(), nobjects);
+ unsigned fg_min_card = atoi(argv[2]);
+ unsigned bg_min_card = atoi(argv[3]);
- obj_ima_t filtered = scribo::filter::objects_with_holes(objects, atoi(argv[2]));
- io::pbm::save(data::convert(bool(), filtered), argv[3]);
+ io::pbm::save(scribo::preprocessing::denoise(input, fg_min_card, bg_min_card),
+ argv[4]);
trace::exiting("main");
-
}
diff --git a/scribo/src/preprocessing/denoising.cc b/scribo/src/preprocessing/denoising.cc
deleted file mode 100644
index 769f75b..0000000
--- a/scribo/src/preprocessing/denoising.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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.
-
-#include <mln/core/image/image2d.hh>
-#include <mln/core/alias/neighb2d.hh>
-#include <mln/data/convert.hh>
-#include <mln/io/pbm/all.hh>
-#include <mln/value/label.hh>
-#include <mln/accu/math/count.hh>
-#include <mln/labeling/relabel.hh>
-#include <mln/labeling/compute.hh>
-#include <mln/labeling/foreground.hh>
-
-#include <scribo/debug/usage.hh>
-
-const char *args_desc[][2] =
-{
- { "input.pbm", "A binary image. 'True' for objects, 'False'\
-for the background." },
- { "nbh", "Select the neighborhood used for checking neighbors. '4' will use C4, '8' will use C8." },
- { "n_nbh", "The minimum neighbor count to be set to true." },
- {0, 0}
-};
-
-int main(int argc, char *argv[])
-{
- using namespace mln;
-
- if (argc != 5)
- return scribo::debug::usage(argv,
- "Remove noise.",
- "input.pbm nbh min_nbh output.pbm",
- args_desc,
- "A binary image.");
-
- trace::entering("main");
-
- typedef image2d<bool> I;
- I input;
- io::pbm::load(input, argv[1]);
-
- typedef value::label<30> V;
-
- neighb2d nbh;
- if (atoi(argv[2]) == 4)
- nbh = c4();
- else if (atoi(argv[2]) == 8)
- nbh = c8();
- else
- {
- std::cout << "Error: nbh must be set to '4' or '8'!" << std::endl;
- return 1;
- }
-
- unsigned min_nbh = atoi(argv[3]);
-
-
- V nlabels;
- image2d<V> lbl = labeling::foreground(input, nbh, nlabels);
- std::cout << nlabels << std::endl;
-
-
- util::array<unsigned> result = labeling::compute(accu::meta::math::count(), lbl, nlabels);
-
- fun::i2v::array<bool> f(static_cast<unsigned>(nlabels) + 1, true);
- for (unsigned i = 1; i <= nlabels; ++i)
- if (result(i) < min_nbh)
- f(i) = false;
-
- labeling::relabel_inplace(lbl, nlabels, f);
- std::cout << nlabels << std::endl;
-
-
-// mln_piter_(I) p(input.domain());
-// mln_niter_(neighb2d) n(nbh, p);
-// unsigned n_nbh;
-// for_all(p)
-// if (input(p))
-// {
-// n_nbh = 0;
-// for_all(n)
-// if (input(n))
-// ++n_nbh;
-
-// if (n_nbh < min_nbh)
-// input(p) = false;
-// }
-
- io::pbm::save(data::convert(bool(), lbl), argv[4]);
-
- trace::exiting("main");
-
-}
diff --git a/scribo/src/preprocessing/homogeneous_contrast.cc b/scribo/src/preprocessing/homogeneous_contrast.cc
new file mode 100644
index 0000000..4930923
--- /dev/null
+++ b/scribo/src/preprocessing/homogeneous_contrast.cc
@@ -0,0 +1,44 @@
+#include <mln/value/int_u8.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <scribo/preprocessing/homogeneous_contrast.hh>
+#include <scribo/debug/usage.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.pgm", "A gray-level image." },
+ { "h", "The height attribute value for the leveling closing." },
+ {0, 0}
+};
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm h output.pgm" << std::endl;
+ abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Remove noise.",
+ "input.pgm h output.pgm",
+ args_desc);
+
+
+ image2d<int_u8> input;
+ io::pgm::load(input, argv[1]);
+
+ int h = std::atoi(argv[2]);
+
+ io::pgm::save(scribo::preprocessing::homogeneous_contrast(input, h),
+ argv[3]);
+}
diff --git a/scribo/src/preprocessing/split_bg_fg.cc b/scribo/src/preprocessing/split_bg_fg.cc
index 0d7ba8a..de7bd02 100644
--- a/scribo/src/preprocessing/split_bg_fg.cc
+++ b/scribo/src/preprocessing/split_bg_fg.cc
@@ -35,6 +35,7 @@ const char *args_desc[][2] =
{ "input.pbm", "A color image." },
{ "lambda", "Lambda value. (FIX Description)" },
{ "delta", "Delta value. (FIX Description)" },
+ { "fg.ppm", "The foreground image (1st output)." },
{ "bg.ppm", "The background image (2nd output)." },
{0, 0}
};
@@ -49,7 +50,7 @@ int main(int argc, char *argv[])
if (argc != 6)
return scribo::debug::usage(argv,
"Split background and foreground.",
- "input.pbm lambda delta bg.ppm fg.ppm",
+ "input.pbm lambda delta fg.ppm bg.ppm",
args_desc, "The foreground image.");
typedef image2d<value::rgb8> I;
@@ -60,8 +61,8 @@ int main(int argc, char *argv[])
bg_fg = scribo::preprocessing::split_bg_fg(input,
atoi(argv[2]),
atoi(argv[3]));
- io::ppm::save(bg_fg.first(), argv[4]);
- io::ppm::save(bg_fg.second(), argv[5]);
+ io::ppm::save(bg_fg.first(), argv[5]);
+ io::ppm::save(bg_fg.second(), argv[4]);
mln::trace::exiting("main");
}
diff --git a/scribo/src/preprocessing/split_bg_fg_ms.cc b/scribo/src/preprocessing/split_bg_fg_ms.cc
deleted file mode 100644
index 4c0180a..0000000
--- a/scribo/src/preprocessing/split_bg_fg_ms.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// 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.
-
-#include <mln/core/image/image2d.hh>
-#include <mln/io/ppm/all.hh>
-
-#include <mln/debug/filename.hh>
-
-#include <mln/subsampling/subsampling.hh>
-
-#include <scribo/preprocessing/split_bg_fg.hh>
-#include <scribo/debug/usage.hh>
-
-
-const char *args_desc[][2] =
-{
- { "input.pbm", "A color image." },
- { "lambda", "Lambda value. (FIX Description)" },
- { "delta", "Delta value. (FIX Description)" },
- { "lambda_sub2", "Lambda value. (FIX Description)" },
- { "delta_sub2", "Delta value. (FIX Description)" },
- { "lambda_sub4", "Lambda value. (FIX Description)" },
- { "delta_sub4", "Delta value. (FIX Description)" },
- {0, 0}
-};
-
-
-
-int main(int argc, char *argv[])
-{
- mln::trace::entering("main");
- using namespace mln;
- dpoint2d none(0, 0);
-
- if (argc != 9)
- return scribo::debug::usage(argv,
- "Split background and foreground.",
- "input.pbm lambda delta lambda_sub2 delta_sub2 lambda_sub3 delta_sub3 output_prefix",
- args_desc, "The background and foreground images.");
-
- mln::debug::internal::filename_prefix = argv[8];
-
- typedef image2d<value::rgb8> I;
- I input;
- io::ppm::load(input, argv[1]);
-
- util::couple<I,I>
- bg_fg = scribo::preprocessing::split_bg_fg(input,
- atoi(argv[2]),
- atoi(argv[3]));
-
- io::ppm::save(bg_fg.first(), mln::debug::filename("out_bg.ppm"));
- io::ppm::save(bg_fg.second(), mln::debug::filename("out_fg.ppm"));
-
-
- {
- // 1/2
- std::cout << "1/2" << std::endl;
- I input_sub2x = mln::subsampling::subsampling(input, none, 2);
-
- util::couple<I,I>
- bg_fg = scribo::preprocessing::split_bg_fg(input_sub2x,
- atoi(argv[4]),
- atoi(argv[5]));
-
-
- io::ppm::save(bg_fg.first(), mln::debug::filename("out_bg_sub2x.ppm"));
- io::ppm::save(bg_fg.second(), mln::debug::filename("out_fg_sub2x.ppm"));
- }
-
- {
- // 1/4
- std::cout << "1/4" << std::endl;
- I input_sub4x = mln::subsampling::subsampling(input, none, 4);
-
- util::couple<I,I>
- bg_fg = scribo::preprocessing::split_bg_fg(input_sub4x,
- atoi(argv[6]),
- atoi(argv[7]));
-
-
- io::ppm::save(bg_fg.first(), mln::debug::filename("out_bg_sub4x.ppm"));
- io::ppm::save(bg_fg.second(), mln::debug::filename("out_fg_sub4x.ppm"));
- }
-
- mln::trace::exiting("main");
-}
diff --git a/scribo/src/text/pbm_recognition.cc b/scribo/src/preprocessing/subsample.cc
similarity index 74%
copy from scribo/src/text/pbm_recognition.cc
copy to scribo/src/preprocessing/subsample.cc
index 70ceca1..388d4d9 100644
--- a/scribo/src/text/pbm_recognition.cc
+++ b/scribo/src/preprocessing/subsample.cc
@@ -23,43 +23,43 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#include <iostream>
-
#include <mln/core/image/image2d.hh>
-#include <mln/io/pbm/load.hh>
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/io/pgm/all.hh>
+
+#include <mln/subsampling/antialiased.hh>
-#include <scribo/text/recognition.hh>
+#include <mln/value/int_u8.hh>
#include <scribo/debug/usage.hh>
const char *args_desc[][2] =
{
- { "input.pbm", "A binary image. 'True' for objects, 'False'\
-for the background." },
+ { "input.pgm", "A gray-scale image." },
+ { "ratio", "Scale ratio." },
{0, 0}
};
-
-int main(int argc, char* argv[])
+int main(int argc, char *argv[])
{
- using namespace scribo;
using namespace mln;
- if (argc != 2)
+ if (argc != 4)
return scribo::debug::usage(argv,
- "Text recognition",
- "input.pbm",
- args_desc,
- "The text is printed on the standard output");
+ "Subsample.",
+ "input.pgm ratio output.pgm",
+ args_desc);
trace::entering("main");
- image2d<bool> input;
- io::pbm::load(input, argv[1]);
+ typedef image2d<value::int_u8> I;
+ I input;
+ io::pgm::load(input, argv[1]);
- scribo::text::recognition(input, "fra");
+ unsigned ratio = atoi(argv[2]);
+ io::pgm::save(mln::subsampling::antialiased(input, ratio), argv[3]);
trace::exiting("main");
}
diff --git a/scribo/src/text_in_article.cc b/scribo/src/text_in_article.cc
index 876eba5..2e6b1a6 100644
--- a/scribo/src/text_in_article.cc
+++ b/scribo/src/text_in_article.cc
@@ -36,64 +36,45 @@
#include <mln/io/ppm/save.hh>
#include <mln/io/dump/save.hh>
-#include <mln/math/min.hh>
-
#include <mln/literal/colors.hh>
#include <mln/value/rgb8.hh>
#include <mln/value/label_16.hh>
-#include <mln/value/int_u16.hh>
-
-#include <mln/draw/box.hh>
-#include <mln/draw/line.hh>
-
-#include <mln/data/convert.hh>
-
-#include <mln/extension/adjust.hh>
-
-#include <mln/accu/stat/median_h.hh>
-
-#include <mln/labeling/colorize.hh>
-#include <mln/labeling/relabel.hh>
-//#include <scribo/draw/bounding_boxes.hh>
-#include <scribo/draw/bounding_box_links.hh>
+#include <scribo/core/line_set.hh>
#include <scribo/primitive/extract/components.hh>
+#include <scribo/primitive/extract/vertical_separators.hh>
+
+#include <scribo/primitive/remove/separators.hh>
#include <scribo/primitive/link/merge_double_link.hh>
#include <scribo/primitive/link/with_single_left_link_dmax_ratio.hh>
#include <scribo/primitive/link/with_single_right_link_dmax_ratio.hh>
-#include <scribo/primitive/group/apply.hh>
-// #include <scribo/primitive/group/from_double_link.hh>
#include <scribo/primitive/group/from_single_link.hh>
#include <scribo/filter/object_links_bbox_h_ratio.hh>
-#include <scribo/filter/objects_small.hh>
-// #include <scribo/filter/objects_thin.hh>
-// #include <scribo/filter/objects_thick.hh>
-
-// #include <scribo/filter/object_groups_small.hh>
-
-// #include <scribo/debug/decision_image.hh>
-// #include <scribo/debug/save_bboxes_image.hh>
-// #include <scribo/debug/save_bboxes_image.hh>
-// #include <scribo/debug/save_linked_bboxes_image.hh>
-
-#include <scribo/core/line_set.hh>
-#include <scribo/io/xml/save_text_lines.hh>
-
#include <scribo/debug/usage.hh>
+#include <scribo/debug/save_bboxes_image.hh>
+#include <scribo/debug/bboxes_enlarged_image.hh>
+#include <scribo/debug/mean_and_base_lines_image.hh>
+#include <scribo/debug/looks_like_a_text_line_image.hh>
#include <scribo/make/debug_filename.hh>
-//#include <scribo/text/recognition.hh>
+#include <scribo/text/recognition.hh>
#include <scribo/text/merging.hh>
+#include <scribo/preprocessing/denoise_fg.hh>
+
+
+// #include <mln/morpho/closing/structural.hh>
+// #include <mln/win/rectangle2d.hh>
+
const char *args_desc[][2] =
{
@@ -128,82 +109,60 @@ int main(int argc, char* argv[])
typedef value::label_16 V;
typedef image2d<V> L;
+
+ // Add whitespace separators.
+// win::rectangle2d win = win::rectangle2d(151, 41);
+// image2d<bool> whitespaces = morpho::closing::structural(input, win);
+// logical::not_inplace(whitespaces);
+
+ // Remove separators
+ std::cout << "Find vertical separators..." << std::endl;
+ image2d<bool> separators = primitive::extract::vertical_separators(input, 81);
+
+ std::cout << "Remove separators..." << std::endl;
+ image2d<bool> input_cleaned = primitive::remove::separators(input,
+ separators);
+
+// whitespaces += separators;
+
+ mln::io::pbm::save(separators, "vseparators.pbm");
+// mln::io::pbm::save(whitespaces, "separators.pbm");
+
+// mln::io::pbm::save(input_cleaned, "input_no_separators.pbm");
+
+ // Denoise
+ std::cout << "Denoise..." << std::endl;
+ input_cleaned = preprocessing::denoise_fg(input_cleaned, c8(), 3);
+
+// mln::io::pbm::save(input_cleaned, "input_denoised.pbm");
+
/// Finding components.
std::cout << "Finding components..." << std::endl;
V ncomponents;
component_set<L>
- components = scribo::primitive::extract::components(input,
- c8(),
+ components = scribo::primitive::extract::components(input_cleaned, c8(),
ncomponents);
- /// First filtering.
- std::cout << "Filtering components..." << std::endl;
- component_set<L> filtered_components
- = scribo::filter::components_small(components, 6);
-
- {
- unsigned none = 0, ignored = 0;
- for_all_comps(i, filtered_components)
- {
- if (filtered_components.info(i).tag() == component::Ignored)
- ++ignored;
- else
- ++none;
- }
- std::cout << "stats - none = " << none << " - ignored = " << ignored << std::endl;
+ /// Set separator components.
+ components.add_separators(separators);
+// components.add_separators(whitespaces);
- }
/// Linking potential objects
std::cout << "Linking objects..." << std::endl;
object_links<L> left_link
- = primitive::link::with_single_left_link_dmax_ratio(filtered_components, 2);
+ = primitive::link::with_single_left_link_dmax_ratio(components, 2);
object_links<L> right_link
- = primitive::link::with_single_right_link_dmax_ratio(filtered_components, 2);
+ = primitive::link::with_single_right_link_dmax_ratio(components, 2);
// Validating left and right links.
object_links<L>
- merged_links = primitive::link::merge_double_link(filtered_components,
- left_link,
- right_link);
-
-// #ifndef NOUT
-// if (argc == 4)
-// {
-// image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-// scribo::draw::bounding_box_links(output,
-// merged_links,
-// literal::green);
-
-// mln::util::array<bool> drawn(static_cast<unsigned>(filtered_components.nelements()) + 1, 0);
-// for_all_comps(i, filtered_components)
-// if (filtered_components(i).tag() != component::Ignored)
-// {
-// if (merged_links[i] == i && ! drawn(i))
-// {
-// mln::draw::box(output, filtered_components(i).bbox(), literal::orange);
-// drawn[i] = true;
-// }
-// else
-// {
-// mln::draw::box(output, filtered_components(i).bbox(), literal::blue);
-// mln::draw::box(output, filtered_components(merged_links[i]).bbox(), literal::blue);
-// drawn[i] = true;
-// drawn[merged_links[i]] = true;
-// }
-// }
+ merged_links = primitive::link::merge_double_link(left_link, right_link);
-// mln::io::ppm::save(output, scribo::make::debug_filename("links.ppm"));
-// }
-// #endif
// Remove links if bboxes have too different sizes.
object_links<L> hratio_filtered_links
- = filter::object_links_bbox_h_ratio(filtered_components,
- merged_links,
- 2.5f);
-
-
+ = filter::object_links_bbox_h_ratio(merged_links, 2.5f);
// #ifndef NOUT
@@ -224,8 +183,7 @@ int main(int argc, char* argv[])
// //
// //######
object_groups<L>
- groups = primitive::group::from_single_link(filtered_components,
- hratio_filtered_links);
+ groups = primitive::group::from_single_link(hratio_filtered_links);
// value::label_16 n_groups;
// mln::fun::i2v::array<value::label_16>
// groups_packed = mln::make::relabelfun(groups,
@@ -235,64 +193,25 @@ int main(int argc, char* argv[])
-// mln::util::array<line_stats_extra> line_stats;
+ // Construct a line set.
line_set<L>
- lines = scribo::make::line_set(hratio_filtered_links, groups);
+ lines = scribo::make::line_set(groups);
+ //===== DEBUG =====
// Bboxes image.
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Ignored)
- mln::draw::box(output, lines(l).bbox(), literal::red);
-
- mln::io::ppm::save(output,
- scribo::make::debug_filename("step1_bboxes.ppm"));
- }
+ scribo::debug::save_bboxes_image(input, lines,
+ scribo::make::debug_filename("step1_bboxes.ppm"));
// Bboxes enlarged
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Ignored)
- {
- if (text::internal::looks_like_a_text_line(lines(l)))
- {
- box2d
- b = text::internal::enlarge(lines(l).bbox(),
- text::internal::delta_of_line(lines(l)));
- b.crop_wrt(input.domain());
- mln::draw::box(output, b, literal::green);
- }
- else
- mln::draw::box(output, lines(l).bbox(), literal::red);
- }
-
- mln::io::ppm::save(output,
- scribo::make::debug_filename("step1_bboxes_enlarged.ppm"));
- }
+ mln::io::ppm::save(scribo::debug::bboxes_enlarged_image(input, lines),
+ scribo::make::debug_filename("step1_bboxes_enlarged.ppm"));
// Looks like a text line
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Ignored)
- {
- if (text::internal::looks_like_a_text_line(lines(l)))
- mln::draw::box(output, lines(l).bbox(), literal::green);
- else
- mln::draw::box(output, lines(l).bbox(), literal::red);
- }
-
- mln::io::ppm::save(output,
- scribo::make::debug_filename("step1_looks_like_a_text_line.ppm"));
- }
+ mln::io::ppm::save(scribo::debug::looks_like_a_text_line_image(input, lines),
+ scribo::make::debug_filename("step1_looks_like_a_text_line.ppm"));
// Bboxes + line infos
@@ -312,7 +231,7 @@ int main(int argc, char* argv[])
<< lines(l).card() << " "
<< lines(l).baseline() << " "
<< lines(l).x_height() << " "
- << lines(l).median() << " "
+ << lines(l).meanline() << " "
<< lines(l).d_height() << " "
<< lines(l).a_height() << " "
<< lines(l).char_space() << " "
@@ -329,88 +248,38 @@ int main(int argc, char* argv[])
}
- std::cout << "Merging lines..." << std::endl;
- lines = scribo::text::merging(input.domain(), lines);
+ // mean and base lines.
+ mln::io::ppm::save(scribo::debug::mean_and_base_lines_image(input, lines),
+ scribo::make::debug_filename("step1_x_height.ppm"));
- // Median and base lines.
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+ //===== END OF DEBUG =====
- for_all_lines(l, lines)
- {
- if (lines(l).tag() == line::Pathological)
- mln::draw::box(output, lines(l).bbox(), literal::orange);
- else if (lines(l).tag() != line::Merged)
- {
- if (lines(l).card() > 1)
- {
- point2d
- b_top(lines(l).baseline() - lines(l).median(), lines(l).bbox().pmin().col()),
- e_top(lines(l).baseline() - lines(l).median(), lines(l).bbox().pmax().col()),
- b_bot(lines(l).baseline(), lines(l).bbox().pmin().col()),
- e_bot(lines(l).baseline(), lines(l).bbox().pmax().col());
-
- mln::draw::line(output, b_top, e_top, literal::blue);
- mln::draw::line(output, b_bot, e_bot, literal::cyan);
-
-// std::cout << lines(l) << std::endl;
- }
- mln::draw::box(output, lines(l).bbox(), literal::purple);
- }
- }
- mln::io::ppm::save(output, scribo::make::debug_filename("step2_x_height.ppm"));
- }
-
-
- // Bboxes image.
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Merged
- && lines(l).tag() != line::Ignored
- && lines(l).tag() != line::Pathological)
- {
- mln::draw::box(output, lines(l).bbox(), literal::red);
- }
-
- mln::io::ppm::save(output, argv[2]);
- }
-// // line label image.
-// {
-// util::array<V>
-// f(static_cast<unsigned>(filtered_components.nelements()) + 1, 0);
-// for_all_lines(l, lines)
-// if (lines(l).tag() != line::Merged
-// && lines(l).tag() != line::Ignored
-// && lines(l).tag() != line::Pathological)
-// {
-// for_all_elements(c, lines(l).components())
-// f(lines(l).components()[c]) = l;
-// }
-// L line_label = duplicate(filtered_components.labeled_image());
-// V nlbl = filtered_components.nelements();
-// mln::labeling::relabel_inplace(line_label, nlbl, f);
-
-// mln::io::ppm::save(labeling::colorize(value::rgb8(), line_label),
-// scribo::make::debug_filename("labeled_lines.ppm"));
-// mln::io::dump::save(line_label, scribo::make::debug_filename("labeled_lines.dump"));
-// }
+ std::cout << "Merging lines..." << std::endl;
+ lines = scribo::text::merging(lines);
-// scribo::io::xml::save_text_lines(argv[1], lines, "out.xml");
+ //===== DEBUG =====
+ // mean and base lines.
+ mln::io::ppm::save(scribo::debug::mean_and_base_lines_image(input, lines),
+ scribo::make::debug_filename("step2_x_height.ppm"));
+ // Looks like a text line
+ mln::io::ppm::save(scribo::debug::looks_like_a_text_line_image(input, lines),
+ scribo::make::debug_filename("step2_looks_like_a_text_line.ppm"));
+ // Bboxes image.
+ scribo::debug::save_bboxes_image(input, lines, argv[2]);
+ //===== END OF DEBUG =====
-// scribo::text::recognition(lines, "fra", "out.txt");
+ scribo::text::recognition(lines, "fra", "out.txt");
// // Display median character space.
@@ -458,5 +327,5 @@ int main(int argc, char* argv[])
// }
-// trace::exiting("main");
+ trace::exiting("main");
}
diff --git a/scribo/src/text_in_photo_ppm_fast.cc b/scribo/src/text_in_photo_ppm_fast.cc
index e4ddd48..0e35487 100644
--- a/scribo/src/text_in_photo_ppm_fast.cc
+++ b/scribo/src/text_in_photo_ppm_fast.cc
@@ -1,4 +1,5 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of Olena.
//
@@ -153,7 +154,7 @@ namespace mln
mask_non_text f(mask);
mln_concrete(I) output = data::transform(input_rgb, f);
- for_all_components(i, components)
+ for_all_comps(i, components)
if (components(i).is_valid())
mln::draw::box(output, components(i).bbox(), literal::red);
@@ -255,9 +256,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
timer_.start();
std::cout << "** Using split_bg_fg" << std::endl;
image2d<value::rgb8>
- fg = preprocessing::split_bg_fg(input_rgb,
- lambda,
- 32).second();
+ fg = preprocessing::split_bg_fg(input_rgb, lambda, 32).second();
intensity_ima = data::transform(fg, mln::fun::v2v::rgb_to_int_u<8>());
t_ = timer_;
std::cout << "Foreground extracted. " << t_ << std::endl;
@@ -379,9 +378,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
// Validating left and right links.
timer_.restart();
object_links<L>
- merged_links = primitive::link::merge_double_link(filtered_components,
- left_link,
- right_link);
+ merged_links = primitive::link::merge_double_link(left_link, right_link);
t_ = timer_;
std::cout << "Right/Left Validation. " << t_ << std::endl;
@@ -389,8 +386,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
// Remove links if bboxes have too different sizes.
timer_.restart();
object_links<L>
- hratio_filtered_links = filter::object_links_bbox_h_ratio(filtered_components,
- merged_links,
+ hratio_filtered_links = filter::object_links_bbox_h_ratio(merged_links,
1.50f);
@@ -413,9 +409,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
//Remove links if bboxes overlap too much.
object_links<L> overlap_filtered_links
- = filter::object_links_bbox_overlap(filtered_components,
- hratio_filtered_links,
- 0.80f);
+ = filter::object_links_bbox_overlap(hratio_filtered_links, 0.80f);
@@ -438,16 +432,14 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
timer_.restart();
object_groups<L>
- groups = primitive::group::from_single_link(filtered_components,
- overlap_filtered_links);
+ groups = primitive::group::from_single_link(overlap_filtered_links);
// Apply grouping in a temporary image (for debug purpose).
#ifndef NOUT
component_set<L>
- raw_group_image = primitive::group::apply(filtered_components,
- groups);
+ raw_group_image = primitive::group::apply(groups);
#endif // !NOUT
t_ = timer_;
@@ -505,10 +497,9 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
{
decision_image = data::convert(value::rgb8(), input);
component_set<L>
- grouped_objects = primitive::group::apply(filtered_components,
- filtered_small_groups);
+ grouped_objects = primitive::group::apply(filtered_small_groups);
- for_all_components(i, filtered_components)
+ for_all_comps(i, filtered_components)
if (filtered_components(i).is_valid()
&& filtered_small_groups(i) != 0)
mln::draw::box(decision_image, filtered_components(i).bbox(), literal::green);
@@ -544,10 +535,9 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
decision_image = data::convert(value::rgb8(), input);
component_set<L>
- grouped_components = primitive::group::apply(filtered_components,
- filtered_thin_groups);
+ grouped_components = primitive::group::apply(filtered_thin_groups);
- for_all_components(i, filtered_components)
+ for_all_comps(i, filtered_components)
if (filtered_components(i).is_valid()
&& filtered_thin_groups(i) != 0)
mln::draw::box(decision_image, filtered_components(i).bbox(), literal::green);
@@ -571,8 +561,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
// 30);
component_set<L>
- grouped_components = primitive::group::apply(filtered_components,
- filtered_thin_groups);
+ grouped_components = primitive::group::apply(filtered_thin_groups);
t_ = g_timer;
std::cout << "Group applied to object image " << t_ << std::endl;
@@ -588,10 +577,10 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
/// Grouping groups.
- groups = primitive::group::from_single_link(grouped_components, left_link);
+ groups = primitive::group::from_single_link(left_link);
-// component_set<L>
- grouped_components = primitive::group::apply(grouped_components, groups);
+// component_set<L>
+ grouped_components = primitive::group::apply(groups);
t_ = g_timer;
std::cout << "Link and group again " << t_ << std::endl;
@@ -663,9 +652,17 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
std::cout << "# objects = " << grouped_components.nelements() << std::endl;
+ std::cout << filtered_components << std::endl;
+
+ for_all_comps(c, filtered_components)
+ if (filtered_components(c).is_valid())
+ std::cout << filtered_components(c) << ", ";
+ std::cout << std::endl;
+
+ std::cout << filtered_thin_groups << std::endl;
scribo::line_set<L>
- lines = scribo::line_set<L>(filtered_components, filtered_thin_groups);
+ lines = scribo::make::line_set(filtered_thin_groups);
text::recognition(lines, "fra", "out.txt");
trace::exiting("main");
diff --git a/scribo/src/text_in_photo_ppm_fast_2.cc b/scribo/src/text_in_photo_ppm_fast_2.cc
deleted file mode 100644
index bb24a11..0000000
--- a/scribo/src/text_in_photo_ppm_fast_2.cc
+++ /dev/null
@@ -1,541 +0,0 @@
-// 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.
-
-#include <libgen.h>
-#include <iostream>
-
-#include <mln/core/image/image2d.hh>
-#include <mln/core/image/imorph/tr_image.hh>
-#include <mln/core/alias/neighb2d.hh>
-
-#include <mln/labeling/colorize.hh>
-
-#include <mln/data/stretch.hh>
-
-#include <mln/io/pbm/all.hh>
-#include <mln/io/ppm/save.hh>
-
-#include <mln/math/min.hh>
-
-#include <mln/logical/not.hh>
-
-#include <mln/literal/colors.hh>
-#include <mln/value/rgb8.hh>
-#include <mln/value/label_16.hh>
-
-#include <mln/fun/v2v/rgb_to_int_u.hh>
-
-#include <mln/data/wrap.hh>
-
-#include <mln/draw/box.hh>
-
-#include <mln/geom/translate.hh>
-
-#include <scribo/draw/bounding_boxes.hh>
-
-#include <scribo/binarization/sauvola_ms.hh>
-#include <scribo/binarization/sauvola.hh>
-
-#include <scribo/primitive/extract/objects.hh>
-
-#include <scribo/primitive/link/merge_double_link.hh>
-#include <scribo/primitive/link/with_single_left_link.hh>
-#include <scribo/primitive/link/with_single_right_link.hh>
-
-#include <scribo/primitive/group/apply.hh>
-#include <scribo/primitive/group/from_double_link.hh>
-#include <scribo/primitive/group/from_single_link.hh>
-
-#include <scribo/filter/objects_with_holes.hh>
-#include <scribo/filter/object_links_bbox_h_ratio.hh>
-#include <scribo/filter/object_links_bbox_overlap.hh>
-
-#include <scribo/filter/common/objects_photo.hh>
-
-#include <scribo/filter/object_groups_small.hh>
-#include <scribo/filter/object_groups_v_thickness.hh>
-
-#include <scribo/debug/decision_image.hh>
-#include <scribo/debug/save_bboxes_image.hh>
-#include <scribo/debug/save_linked_bboxes_image.hh>
-
-#include <scribo/debug/usage.hh>
-
-#include <scribo/preprocessing/split_bg_fg.hh>
-
-#include <scribo/make/debug_filename.hh>
-
-#include <mln/util/timer.hh>
-#include <mln/core/var.hh>
-
-
-#include <scribo/src/afp/components.hh>
-#include <scribo/src/afp/link.hh>
-#include <scribo/src/afp/regroup.hh>
-
-const char *args_desc[][2] =
-{
- { "input.ppm", "A color image." },
- { "debug_output_dir", "Directory were debug images will be saved" },
- { "lambda", "Lambda value used for foreground extraction" },
- {0, 0}
-};
-
-
-namespace mln
-{
-
- struct mask_non_text : Function_v2v<mask_non_text>
- {
- typedef value::rgb8 result;
- typedef image2d<bool> I;
-
- mask_non_text(const image2d<bool>& mask)
- : mask_(mask), p_(mask_)
- {
- p_.start();
- }
-
- result operator()(const result& v) const
- {
- bool b = p_.val();
- p_.next();
- if (!b)
- return v / 2;
- else
- return v;
-
- }
-
- I mask_;
- mutable mln_pixter_(I) p_;
- };
-
-
- template <typename I, typename L>
- mln_concrete(I)
- compute_highlight_image(const I& input_rgb,
- const object_image<L>& objects)
- {
- mln_ch_value(I, bool) mask;
- initialize(mask, input_rgb);
- data::fill(mask, false);
-
- for_all_components(i, objects.bboxes())
- data::fill((mask | objects.bbox(i)).rw(), true);
-
- mask_non_text f(mask);
- mln_concrete(I) output = data::transform(input_rgb, f);
-
- for_all_components(i, objects.bboxes())
- mln::draw::box(output, objects.bbox(i), literal::red);
-
- return output;
- }
-
- template <typename I, typename L>
- mln_concrete(I)
- compute_text_image(const I& input_rgb,
- const object_image<L>& grouped_objects)
- {
- const util::array<mln_domain(L)>& bboxes = grouped_objects.bboxes();
-
- unsigned shift = 5;
- float height = 1, width = 0;
- for_all_components(i, bboxes)
- {
- height += bboxes(i).nrows() + shift;
- width = math::max(static_cast<float>(bboxes(i).ncols()), width);
- }
- if (width == 0)
- width = 1;
-
- I output(height, width);
- data::fill(output, literal::black);
-
- algebra::vec<2, float> dv;
- dv[0] = 0;
- dv[1] = 0;
- for_all_ncomponents(i, grouped_objects.nlabels())
- {
- mln_VAR(tmp, duplicate(input_rgb | grouped_objects.bbox(i)));
-
- typedef fun::x2x::translation<mln_site_(I)::dim, float> trans_t;
- trans_t trans(dv - grouped_objects.bbox(i).pmin().to_vec());
-
- mln_domain(I) tr_box(grouped_objects.bbox(i).pmin().to_vec() + trans.t(),
- grouped_objects.bbox(i).pmax().to_vec() + trans.t());
-
- tr_image<mln_domain(I), tmp_t, trans_t> tr_ima(tr_box, tmp, trans);
-
- data::paste(tr_ima, output);
- dv[0] += grouped_objects.bbox(i).nrows() + shift;
- }
-
- return output;
- }
-
-} // end of namespace mln
-
-
-int main(int argc, char* argv[])
-{
- using namespace scribo;
- using namespace mln;
-
- if (argc < 3 || argc > 10)
- return scribo::debug::usage(argv,
- "Find text in a photo.",
- "input.ppm output.ppm [bg/fg enabled] [sauvola_ms enabled] [Bg comp filter enabled] [small group filter enabled] [thin group filter enabled] [debug_output_dir] [lambda]",
- args_desc,
- "A color image where the text is highlighted.");
-
- bool debug = false;
- if (argc > 8)
- {
- scribo::make::internal::debug_filename_prefix = argv[8];
- debug = true;
- }
-
- trace::entering("main");
-
- image2d<value::rgb8> input_rgb;
- io::ppm::load(input_rgb, argv[1]);
-
-
- unsigned lambda;
- if (argc == 6)
- lambda = atoi(argv[9]);
- else
- lambda = 1.2 * (input_rgb.nrows() + input_rgb.ncols());
-
-
- image2d<value::int_u8> intensity_ima;
- util::timer timer_, global_t_;
- float t_;
-
- global_t_.start();
-
- if (argc > 3 && atoi(argv[3]) != 0)
- {
- // Extract foreground
- timer_.start();
- std::cout << "** Using split_bg_fg" << std::endl;
- image2d<value::rgb8>
- fg = preprocessing::split_bg_fg(input_rgb,
- lambda,
- 32).second();
- intensity_ima = data::transform(fg, mln::fun::v2v::rgb_to_int_u<8>());
- t_ = timer_;
- std::cout << "Foreground extracted. " << t_ << std::endl;
- }
- else
- {
- timer_.start();
- std::cout << "** Using data::transform(intensity)" << std::endl;
- intensity_ima = data::transform(input_rgb,
- mln::fun::v2v::rgb_to_int_u<8>());
- t_ = timer_;
- std::cout << "Intensity image " << t_ << std::endl;
- }
-
-
- // Binarize foreground to use it in the processing chain.
- timer_.restart();
- image2d<bool> input;
- unsigned w = std::min(intensity_ima.nrows() / 3, intensity_ima.ncols() / 3);
- if (! w % 2)
- ++w;
- w = std::min(w, 101u);
- if (argc > 4 && atoi(argv[4]) != 0)
- {
- std::cout << "** Using sauvola_ms with w_1 = " << w << std::endl;
- input = binarization::sauvola_ms(intensity_ima, w, 3, 67);
- }
- else
- {
- std::cout << "** Using sauvola with w_1 = " << w << std::endl;
- input = binarization::sauvola(intensity_ima, w);
- }
-
-
-// if (debug)
-// io::pbm::save(input, "input.pbm");
- t_ = timer_;
- std::cout << "Foreground binarized. " << t_ << std::endl;
-
-
-
- typedef image2d<value::label_16> L;
-
- /// Finding objects.
- timer_.restart();
-
- typedef object_image(L) Obj;
- Obj filtered_objects;
-
- {
- util::array<box2d> bboxes;
- util::array<point2d> mass_centers;
-
- value::label_16 nobjects;
- L components = extract_components(input, nobjects, bboxes, mass_centers);
-
- filtered_objects = Obj(components, nobjects, bboxes, mass_centers);
- }
-
- t_ = timer_;
- std::cout << "Object extracted " << t_ << " - " << filtered_objects.nlabels() << " objects" << std::endl;
-
-// if (debug)
-// io::pgm::save(data::stretch(value::int_u8(), filtered_objects), "ref_objects.pgm");
-
-
- /// linking potential objects
- timer_.restart();
- util::couple<object_links<L>, object_links<L> >
- links = primitive::link::left_right(filtered_objects);
-
- object_links<L>& left_link = links.first();
- object_links<L>& right_link = links.second();
-
-// object_links<L> left_link
-// = primitive::link::with_single_left_link(filtered_objects, 30);
-// t_ = timer_;
-// std::cout << "Left Link done " << t_ << std::endl;
-
-// timer_.restart();
-// object_links<L> right_link
-// = primitive::link::with_single_right_link(filtered_objects, 30);
- t_ = timer_;
- std::cout << "Link done " << t_ << std::endl;
-
-
-
-// #ifndef NOUT
-// if (argc > 4)
-// {
-// std::cerr << "BEFORE - nobjects = " << filtered_objects.nlabels() << std::endl;
-// scribo::debug::save_linked_bboxes_image(input,
-// filtered_objects,
-// left_link, right_link,
-// literal::red, literal::cyan,
-// literal::yellow,
-// literal::green,
-// scribo::make::debug_filename("links.ppm"));
-// }
-// #endif
-
-
-
- // Validating left and right links.
- timer_.restart();
- object_links<L>
- merged_links = primitive::link::merge_double_link(filtered_objects,
- left_link,
- right_link);
- t_ = timer_;
- std::cout << "Right/Left Validation. " << t_ << std::endl;
-
-
- // Remove links if bboxes have too different sizes.
- timer_.restart();
- object_links<L>
- hratio_filtered_links = filter::object_links_bbox_h_ratio(filtered_objects,
- merged_links,
- 1.50f);
-
-
-
-
-// #ifndef NOUT
-// if (argc > 4)
-// {
-// image2d<value::rgb8>
-// hratio_decision_image = scribo::debug::decision_image(input,
-// merged_links,
-// hratio_filtered_links);
-// io::ppm::save(hratio_decision_image,
-// scribo::make::debug_filename("hratio_links_decision_image.ppm"));
-// }
-// #endif
-
-
-
-
- //Remove links if bboxes overlap too much.
- object_links<L> overlap_filtered_links
- = filter::object_links_bbox_overlap(filtered_objects,
- hratio_filtered_links,
- 0.80f);
-
-
-
-
-// #ifndef NOUT
-// if (argc > 4)
-// {
-// image2d<value::rgb8> overlap_decision_image
-// = scribo::debug::decision_image(input,
-// hratio_filtered_links,
-// overlap_filtered_links);
-// io::ppm::save(overlap_decision_image,
-// scribo::make::debug_filename("overlap_links_decision_image.ppm"));
-// }
-// #endif
-
-
- t_ = timer_;
- std::cout << "Objects links filtered. " << t_ << std::endl;
-
- timer_.restart();
- object_groups<L>
- groups = primitive::group::from_single_link(filtered_objects,
- overlap_filtered_links);
-
-
-
- /// Apply grouping in a temporary image (for debug purpose).
-// #ifndef NOUT
-// object_image(L)
-// raw_group_image = primitive::group::apply(filtered_objects,
-// filter::object_groups_small(groups, 2));
-// #endif // !NOUT
-
- t_ = timer_;
- std::cout << "Objects grouped. " << t_ << std::endl;
-
-
- util::timer g_timer;
-
- object_image(L)
- grouped_objects = primitive::group::apply(filtered_objects,
- groups);
-
- t_ = g_timer;
- std::cout << "Group applied to object image " << t_ << std::endl;
-
- /// Objects have been grouped. We try to link groups together.
- /// This time a single link is enough since non-wanted objects have
- /// been removed.
- g_timer.restart();
-
- left_link = primitive::link::left(grouped_objects, 30);
-// left_link
-// = primitive::link::with_single_left_link(grouped_objects, 30);
-
-
- /// Grouping groups.
- groups = primitive::group::from_single_link(grouped_objects, left_link);
-
-// object_image(L)
- grouped_objects = primitive::group::apply(grouped_objects, groups);
-
-
-
- std::cout << "Filtering groups..." << std::endl;
-
- timer_.restart();
- // Remove objects part of groups with strictly less than 3 objects.
-
- g_timer.start();
- object_groups<L> filtered_small_groups;
- if (argc > 6 && atoi(argv[6]) != 0)
- {
- std::cout << "** Using group too small" << std::endl;
- filtered_small_groups = filter::object_groups_small(groups, 3);
- }
- else
- filtered_small_groups = groups;
- t_ = g_timer;
- std::cout << "Small groups removed " << t_ << std::endl;
-
-
-
- // Remove objects part of groups having a mean thickness lower than 8.
- g_timer.restart();
- object_groups<L> filtered_thin_groups;
- if (argc > 7 && atoi(argv[7]) != 0)
- {
- std::cout << "** Using group too thin" << std::endl;
- filtered_thin_groups
- = filter::object_groups_v_thickness(filtered_small_groups, 8);
- }
- else
- filtered_thin_groups = filtered_small_groups;
- t_ = g_timer;
- std::cout << "Groups too thin " << t_ << std::endl;
-
-
-
- t_ = g_timer;
- std::cout << "Link and group again " << t_ << std::endl;
-
- timer_.stop();
-
-
- timer_.resume();
-
- g_timer.restart();
- /// Filter grouped objects not having enough background components.
- if (argc > 5 && atoi(argv[5]) != 0)
- {
- std::cout << "** Using objects_with_two_holes" << std::endl;
- grouped_objects = scribo::filter::objects_with_two_holes(grouped_objects, 5);
- t_ = g_timer;
- std::cout << "Objects_with_holes " << t_ << std::endl;
- }
-
- t_ = timer_;
- std::cout << "Objects groups filtered. " << t_ << std::endl;
-
-// object_image(L)
- grouped_objects = primitive::group::apply(grouped_objects, groups);
-
-
- t_ = global_t_;
- std::cout << "*** Result computed " << t_ << std::endl;
-
- timer_.restart();
- std::cout << "Saving result..." << std::endl;
- io::ppm::save(mln::labeling::colorize(value::rgb8(),
- grouped_objects,
- grouped_objects.nlabels()),
- argv[2]);
-
-
- io::ppm::save(compute_highlight_image(input_rgb, grouped_objects),
- scribo::make::debug_filename("orig_with_bboxes.ppm"));
- io::ppm::save(compute_text_image(input_rgb, grouped_objects),
- scribo::make::debug_filename("out_text.ppm"));
-
- t_ = timer_;
- std::cout << "Output saved " << t_ << std::endl;
-
- std::cout << "# objects = " << grouped_objects.nlabels() << std::endl;
-
- trace::exiting("main");
- return grouped_objects.nlabels() != 0;
-}
--
1.5.6.5
1
0
last-svn-commit-64-gfd14f74 Add a new example for finding text in pictures.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
* src/Makefile.am: Add new target.
* src/text_in_photo_pbm_fast.cc: New.
---
scribo/ChangeLog | 8 +
scribo/src/Makefile.am | 5 +-
scribo/src/text_in_photo_pbm_fast.cc | 441 ++++++++++++++++++++++++++++++++++
3 files changed, 452 insertions(+), 2 deletions(-)
create mode 100644 scribo/src/text_in_photo_pbm_fast.cc
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 49df10a..6fce0bc 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,13 @@
2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add a new example for finding text in pictures.
+
+ * src/Makefile.am: Add new target.
+
+ * src/text_in_photo_pbm_fast.cc: New.
+
+2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
First draft of the dematerialization wizard.
* demo/shared/icons/Symbols-Delete-icon.png,
diff --git a/scribo/src/Makefile.am b/scribo/src/Makefile.am
index a206e04..60c60f7 100644
--- a/scribo/src/Makefile.am
+++ b/scribo/src/Makefile.am
@@ -35,7 +35,7 @@ bin_PROGRAMS = \
text_in_photo \
text_in_photo_ppm \
text_in_photo_ppm_fast \
- text_in_photo_ppm_fast_2 \
+ text_in_photo_pbm_fast \
text_in_photo_invert
@@ -43,7 +43,7 @@ pbm_lines_in_doc_SOURCES = pbm_lines_in_doc.cc
text_in_photo_SOURCES = text_in_photo.cc
text_in_photo_ppm_SOURCES = text_in_photo_ppm.cc
text_in_photo_ppm_fast_SOURCES = text_in_photo_ppm_fast.cc
-text_in_photo_ppm_fast_2_SOURCES = text_in_photo_ppm_fast_2.cc
+text_in_photo_pbm_fast_SOURCES = text_in_photo_pbm_fast.cc
text_in_photo_invert_SOURCES = text_in_photo_invert.cc
@@ -71,6 +71,7 @@ if HAVE_TIFF
$(TIFF_LDFLAGS) \
-lpthread -lhpdf
+
bin_PROGRAMS += text_in_article
text_in_article_CPPFLAGS = $(AM_CPPFLAGS) \
$(TESSERACT_CPPFLAGS) \
diff --git a/scribo/src/text_in_photo_pbm_fast.cc b/scribo/src/text_in_photo_pbm_fast.cc
new file mode 100644
index 0000000..6b27bbc
--- /dev/null
+++ b/scribo/src/text_in_photo_pbm_fast.cc
@@ -0,0 +1,441 @@
+// 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.
+
+#include <libgen.h>
+#include <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/imorph/tr_image.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/labeling/colorize.hh>
+
+#include <mln/data/stretch.hh>
+
+#include <mln/io/pbm/all.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/min.hh>
+
+#include <mln/logical/not.hh>
+
+#include <mln/literal/colors.hh>
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/fun/v2v/rgb_to_int_u.hh>
+
+#include <mln/data/wrap.hh>
+
+#include <mln/draw/box.hh>
+
+#include <mln/geom/translate.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/binarization/sauvola_ms.hh>
+#include <scribo/binarization/sauvola.hh>
+
+#include <scribo/primitive/extract/components.hh>
+
+#include <scribo/primitive/link/merge_double_link.hh>
+#include <scribo/primitive/link/with_single_left_link.hh>
+#include <scribo/primitive/link/with_single_right_link.hh>
+
+#include <scribo/primitive/group/apply.hh>
+#include <scribo/primitive/group/from_double_link.hh>
+#include <scribo/primitive/group/from_single_link.hh>
+
+#include <scribo/filter/objects_with_holes.hh>
+#include <scribo/filter/object_links_bbox_h_ratio.hh>
+#include <scribo/filter/object_links_bbox_overlap.hh>
+
+#include <scribo/filter/common/objects_photo.hh>
+
+#include <scribo/filter/object_groups_small.hh>
+#include <scribo/filter/object_groups_v_thickness.hh>
+
+#include <scribo/debug/decision_image.hh>
+#include <scribo/debug/save_bboxes_image.hh>
+#include <scribo/debug/save_linked_bboxes_image.hh>
+
+#include <scribo/debug/usage.hh>
+
+#include <scribo/preprocessing/split_bg_fg.hh>
+
+#include <scribo/make/debug_filename.hh>
+
+#include <mln/util/timer.hh>
+#include <mln/core/var.hh>
+
+
+#include <scribo/src/afp/components.hh>
+#include <scribo/src/afp/link.hh>
+#include <scribo/src/afp/regroup.hh>
+
+#include <scribo/core/line_set.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.ppm", "A color image." },
+ { "debug_output_dir", "Directory were debug images will be saved" },
+ { "lambda", "Lambda value used for foreground extraction" },
+ {0, 0}
+};
+
+
+namespace mln
+{
+
+ struct mask_non_text : Function_v2v<mask_non_text>
+ {
+ typedef value::rgb8 result;
+ typedef image2d<bool> I;
+
+ mask_non_text(const image2d<bool>& mask)
+ : mask_(mask), p_(mask_)
+ {
+ p_.start();
+ }
+
+ result operator()(const result& v) const
+ {
+ bool b = p_.val();
+ p_.next();
+ if (!b)
+ return v / 2;
+ else
+ return v;
+
+ }
+
+ I mask_;
+ mutable mln_pixter_(I) p_;
+ };
+
+
+ template <typename I, typename L>
+ mln_concrete(I)
+ compute_highlight_image(const I& input_rgb,
+ const scribo::component_set<L>& components)
+ {
+ mln_ch_value(I, bool) mask;
+ initialize(mask, input_rgb);
+ data::fill(mask, false);
+
+ for_all_comps(i, components)
+ if (components(i).is_valid())
+ data::fill((mask | components(i).bbox()).rw(), true);
+
+ mask_non_text f(mask);
+ mln_concrete(I) output = data::transform(input_rgb, f);
+
+ for_all_comps(i, components)
+ if (components(i).is_valid())
+ mln::draw::box(output, components(i).bbox(), literal::red);
+
+ return output;
+ }
+
+ template <typename I, typename L>
+ mln_concrete(I)
+ compute_text_image(const I& input_rgb,
+ const scribo::component_set<L>& grouped_objects)
+ {
+ unsigned shift = 5;
+ float height = 1, width = 0;
+ for_all_comps(i, grouped_objects)
+ if (grouped_objects(i).is_valid())
+ {
+ height += grouped_objects(i).bbox().nrows() + shift;
+ width = math::max(static_cast<float>(grouped_objects(i).bbox().ncols()),
+ width);
+ }
+ if (width == 0)
+ width = 1;
+
+ I output(height, width);
+ data::fill(output, literal::black);
+
+ algebra::vec<2, float> dv;
+ dv[0] = 0;
+ dv[1] = 0;
+ for_all_comps(i, grouped_objects)
+ if (grouped_objects(i).is_valid())
+ {
+ mln_VAR(tmp, duplicate(input_rgb | grouped_objects(i).bbox()));
+
+ typedef fun::x2x::translation<mln_site_(I)::dim, float> trans_t;
+ trans_t trans(dv - grouped_objects(i).bbox().pmin().to_vec());
+
+ mln_domain(I)
+ tr_box(grouped_objects(i).bbox().pmin().to_vec() + trans.t(),
+ grouped_objects(i).bbox().pmax().to_vec() + trans.t());
+
+ tr_image<mln_domain(I), tmp_t, trans_t> tr_ima(tr_box, tmp, trans);
+
+ data::paste(tr_ima, output);
+ dv[0] += grouped_objects(i).bbox().nrows() + shift;
+ }
+
+ return output;
+ }
+
+} // end of namespace mln
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc < 3 || argc > 8)
+ return scribo::debug::usage(argv,
+ "Find text in a photo.\n\n\
+Common usage: ./pbm_text_in_photo input.pbm output.ppm 1 1 1",
+ "input.pbm output.ppm <Bg comp filter enabled> <small group filter enabled> <thin group filter enabled> [debug_output_dir] [lambda]",
+ args_desc,
+ "A color image where the text is highlighted.");
+
+ std::string out_base_dir;
+ bool debug = false;
+ if (argc > 6)
+ {
+ scribo::make::internal::debug_filename_prefix = argv[6];
+ debug = true;
+ out_base_dir = argv[6];
+ }
+
+ trace::entering("main");
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+
+ mln::util::timer timer_, global_t_;
+ float t_;
+
+
+
+ typedef image2d<value::label_16> L;
+
+ /// Finding components.
+ timer_.restart();
+
+ typedef component_set<L> Obj;
+ Obj filtered_components;
+
+ {
+ mln::util::array<std::pair<box2d, std::pair<point2d, unsigned> > > attribs;
+
+ value::label_16 ncomponents;
+ L components = extract_components(input, ncomponents, attribs);
+
+ filtered_components = Obj(components, ncomponents, attribs);
+ }
+
+ t_ = timer_;
+ std::cout << "Object extracted " << t_ << " - " << filtered_components.nelements() << " components" << std::endl;
+
+ /// linking potential components
+ timer_.restart();
+ mln::util::couple<object_links<L>, object_links<L> >
+ links = primitive::link::left_right(filtered_components);
+
+ object_links<L>& left_link = links.first();
+ object_links<L>& right_link = links.second();
+
+ t_ = timer_;
+ std::cout << "Link done " << t_ << std::endl;
+
+
+
+ // Validating left and right links.
+ timer_.restart();
+ object_links<L>
+ merged_links = primitive::link::merge_double_link(left_link, right_link);
+ t_ = timer_;
+ std::cout << "Right/Left Validation. " << t_ << std::endl;
+
+
+ // Remove links if bboxes have too different sizes.
+ timer_.restart();
+ object_links<L>
+ hratio_filtered_links = filter::object_links_bbox_h_ratio(merged_links,
+ 1.50f);
+
+ //Remove links if bboxes overlap too much.
+ object_links<L> overlap_filtered_links
+ = filter::object_links_bbox_overlap(hratio_filtered_links, 0.80f);
+
+ t_ = timer_;
+ std::cout << "Components links filtered. " << t_ << std::endl;
+
+ timer_.restart();
+ object_groups<L>
+ groups = primitive::group::from_single_link(overlap_filtered_links);
+
+
+
+// Apply grouping in a temporary image (for debug purpose).
+
+ t_ = timer_;
+ std::cout << "Components grouped. " << t_ << std::endl;
+
+ std::cout << "Filtering groups..." << std::endl;
+
+ mln::util::timer g_timer;
+
+ timer_.restart();
+ // Remove components part of groups with strictly less than 3 components.
+
+ g_timer.start();
+ object_groups<L> filtered_small_groups;
+ if (argc > 4 && atoi(argv[4]) != 0)
+ {
+ std::cout << "** Using group too small" << std::endl;
+ filtered_small_groups = filter::object_groups_small(groups, 3);
+ }
+ else
+ filtered_small_groups = groups;
+ t_ = g_timer;
+ std::cout << "Small groups removed " << t_ << std::endl;
+
+
+
+ // Remove components part of groups having a mean thickness lower than 8.
+ g_timer.restart();
+ object_groups<L> filtered_thin_groups;
+ if (argc > 5 && atoi(argv[5]) != 0)
+ {
+ std::cout << "** Using group too thin" << std::endl;
+ filtered_thin_groups
+ = filter::object_groups_v_thickness(filtered_small_groups, 8);
+ }
+ else
+ filtered_thin_groups = filtered_small_groups;
+ t_ = g_timer;
+ std::cout << "Groups too thin " << t_ << std::endl;
+
+
+
+ /// Apply grouping in the object image.
+ g_timer.restart();
+
+// groups = primitive::group::regroup_left(filtered_components,
+// filtered_thin_groups,
+// 30);
+
+ component_set<L>
+ grouped_components = primitive::group::apply(filtered_thin_groups);
+
+ t_ = g_timer;
+ std::cout << "Group applied to object image " << t_ << std::endl;
+
+ /// Components have been grouped. We try to link groups together.
+ /// This time a single link is enough since non-wanted components have
+ /// been removed.
+ g_timer.restart();
+
+ left_link = primitive::link::left(grouped_components, 30);
+
+
+ /// Grouping groups.
+ groups = primitive::group::from_single_link(left_link);
+
+
+ grouped_components = primitive::group::apply(groups);
+
+ t_ = g_timer;
+ std::cout << "Link and group again " << t_ << std::endl;
+
+ timer_.stop();
+
+ timer_.resume();
+
+ g_timer.restart();
+
+
+ /// Filter grouped objects not having enough background components.
+ if (argc > 3 && atoi(argv[3]) != 0)
+ {
+ std::cout << "** Using objects_with_two_holes" << std::endl;
+ grouped_components = scribo::filter::components_with_two_holes(grouped_components, 5);
+ t_ = g_timer;
+ std::cout << "Objects_with_holes " << t_ << std::endl;
+ }
+
+
+ t_ = timer_;
+ std::cout << "Objects groups filtered. " << t_ << std::endl;
+
+
+ t_ = global_t_;
+ std::cout << "*** Result computed " << t_ << std::endl;
+
+ timer_.restart();
+ std::cout << "Saving result..." << std::endl;
+ io::ppm::save(mln::labeling::colorize(value::rgb8(),
+ grouped_components.labeled_image(),
+ grouped_components.nelements()),
+ argv[2]);
+
+
+ scribo::line_set<L>
+ lines = scribo::line_set<L>(filtered_thin_groups);
+
+
+ // Bboxes + line infos
+ {
+ std::ofstream file(scribo::make::debug_filename("text_bboxes.txt").c_str());
+
+ for_all_lines(l, lines)
+ if (lines(l).tag() != line::Merged
+ && lines(l).tag() != line::Ignored
+ && lines(l).tag() != line::Pathological)
+ {
+ file << lines(l).bbox().pmin().row() << " "
+ << lines(l).bbox().pmin().col() << " "
+ << lines(l).bbox().pmax().row() << " "
+ << lines(l).bbox().pmax().col()
+// << " "
+// << lines(l).card() << " "
+// << lines(l).baseline() << " "
+// << lines(l).x_height() << " "
+// << lines(l).median() << " "
+// << lines(l).d_height() << " "
+// << lines(l).a_height() << " "
+// << lines(l).char_space() << " "
+// << lines(l).char_width()
+ << std::endl;
+ }
+
+ file.close();
+ }
+
+
+ trace::exiting("main");
+ return grouped_components.nelements() != 0;
+}
--
1.5.6.5
1
0
last-svn-commit-19-gc6851fd Optimize Sauvola's multiscale binarization.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
* core/init_integral_image.hh: New.
* src/binarization/sauvola_ms.cc: Optimize and make it more
robust.
* canvas/integral_browsing.hh: New canvas to browse and compute
data in an integral image.
* binarization/internal/first_pass_functor.hh: New functor to be
used in the integral browsing.
* binarization/sauvola_threshold.hh: Add new overloads.
* subsampling/integral_single_image.hh: Subsample an image and
compute integral images at the same time.
---
scribo/ChangeLog | 20 +
scribo/binarization/internal/first_pass_functor.hh | 141 +++
scribo/binarization/sauvola_threshold.hh | 318 +++---
scribo/canvas/integral_browsing.hh | 412 ++++++++
scribo/core/init_integral_image.hh | 96 ++
scribo/src/binarization/sauvola_ms.cc | 1072 ++++++++++++++++----
scribo/subsampling/integral_single_image.hh | 403 ++++++++
7 files changed, 2103 insertions(+), 359 deletions(-)
create mode 100644 scribo/binarization/internal/first_pass_functor.hh
create mode 100644 scribo/canvas/integral_browsing.hh
create mode 100644 scribo/core/init_integral_image.hh
create mode 100644 scribo/subsampling/integral_single_image.hh
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 4fed85c..9eac19c 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,23 @@
+2009-12-04 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Optimize Sauvola's multiscale binarization.
+
+ * core/init_integral_image.hh: New.
+
+ * src/binarization/sauvola_ms.cc: Optimize and make it more
+ robust.
+
+ * canvas/integral_browsing.hh: New canvas to browse and compute
+ data in an integral image.
+
+ * binarization/internal/first_pass_functor.hh: New functor to be
+ used in the integral browsing.
+
+ * binarization/sauvola_threshold.hh: Add new overloads.
+
+ * subsampling/integral_single_image.hh: Subsample an image and
+ compute integral images at the same time.
+
2009-11-03 Guillaume Lazzara <z(a)lrde.epita.fr>
Add a new example in Scribo.
diff --git a/scribo/binarization/internal/first_pass_functor.hh b/scribo/binarization/internal/first_pass_functor.hh
new file mode 100644
index 0000000..13fc62a
--- /dev/null
+++ b/scribo/binarization/internal/first_pass_functor.hh
@@ -0,0 +1,141 @@
+// 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_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH
+# define SCRIBO_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH
+
+# include <mln/value/int_u8.hh>
+
+# include <scribo/binarization/sauvola_threshold.hh>
+
+
+// #include <mln/border/adjust.hh>
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ namespace internal
+ {
+
+ using namespace mln;
+
+
+ unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
+ {
+ if (parent.element(x) == x)
+ return x;
+ return parent.element(x) = my_find_root(parent,
+ parent.element(x));
+ }
+
+
+ template <typename I>
+ struct first_pass_functor
+ {
+ const I& input;
+ mln_fwd_pixter(const I) pxl;
+ double res;
+ image2d<unsigned> parent;
+ image2d<unsigned> card;
+ image2d<bool> msk;
+ image2d<value::int_u8> t_sub;
+
+ unsigned n_nbhs;
+ util::array<int> dp;
+
+ static const double one_k = 1 - 0.34;
+ static const double k_R = 0.34 / 128.0;
+
+ first_pass_functor(const I& input)
+ : input(input),
+ pxl(input)
+ {
+ res = 0;
+ pxl.start();
+
+ initialize(t_sub, input);
+ initialize(parent, input);
+ initialize(msk, input);
+ extension::fill(msk, false);
+
+ initialize(card, input);
+ data::fill(card, 1);
+
+ dp = negative_offsets_wrt(input, c4());
+ n_nbhs = dp.nelements();
+ }
+
+ void exec(double mean, double stddev)
+ {
+ mln_precondition(pxl.is_valid());
+
+ unsigned p = pxl.offset();
+
+ // Use an inlined and developed version of sauvola's
+ // threshold formula.
+ value::int_u8 t_p = mean * (one_k + k_R * stddev);
+// value::int_u8 t_p = sauvola_threshold_formula(mean, stddev);
+
+ msk.element(p) = input.element(p) < t_p;
+ t_sub.element(p) = t_p;
+ if (! msk.element(p))
+ {
+ pxl.next();
+ return;
+ }
+ parent.element(p) = p;
+ for (unsigned i = 0; i < n_nbhs; ++i)
+ {
+ unsigned n = p + dp[i];
+ if (! msk.element(n))
+ continue;
+ unsigned r = my_find_root(parent, n);
+ if (r != p)
+ {
+ parent.element(r) = p;
+ card.element(p) += card.element(r);
+ }
+ }
+
+ pxl.next(); // next pixel
+ }
+
+ void finalize()
+ {
+ mln_assertion(! pxl.is_valid());
+ }
+ };
+
+
+ } // end of namespace scribo::binarization::internal
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+#endif // SCRIBO_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH
diff --git a/scribo/binarization/sauvola_threshold.hh b/scribo/binarization/sauvola_threshold.hh
index 9a9e439..857de1f 100644
--- a/scribo/binarization/sauvola_threshold.hh
+++ b/scribo/binarization/sauvola_threshold.hh
@@ -44,10 +44,7 @@
# include <mln/pw/all.hh>
# include <mln/core/routine/duplicate.hh>
-
-// Forward declaration.
-namespace mln { template <typename T> class integral_image; }
-
+# include <scribo/core/init_integral_image.hh>
namespace scribo
{
@@ -60,7 +57,7 @@ namespace scribo
/*! \brief Compute a threshold image with Sauvola's algorithm.
\input[in] input An image.
- \input[in] window_size The window size.x
+ \input[in] window_size The window size.
\input[out] simple The sum of all intensities of \p input.
\input[out] squared The sum of all squared intensities of \p
input.
@@ -68,11 +65,11 @@ namespace scribo
\return An image of local thresholds.
*/
- template <typename I, typename T>
+ template <typename I, typename J>
mln_ch_value(I, value::int_u8)
sauvola_threshold(const Image<I>& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared);
+ Image<J>& simple,
+ Image<J>& squared);
/// \overload
@@ -106,36 +103,63 @@ namespace scribo
};
- unsigned long long square_(const value::int_u8& val)
+ /*! \brief compute Sauvola's threshold applying directly the formula.
+
+ \param[in] m_x_y Mean value.
+ \param[in] s_x_y Standard deviation.
+ \param[in] k Control the threshold value in the local
+ window. The higher, the lower the threshold
+ form the local mean m(x, y).
+ \param[in] R Maximum value of the standard deviation (128
+ for grayscale documents).
+
+ \return A threshold.
+ */
+ inline
+ double
+ sauvola_threshold_formula(const double m_x_y, const double s_x_y,
+ const double k, const double R)
{
- unsigned long long v = static_cast<unsigned long long>(val);
- return v * v;
+ return m_x_y * (1.0 + k * ((s_x_y / R) - 1.0));
}
- unsigned long long identity_(const value::int_u8& val)
+
+ /// \overload
+ //
+ inline
+ double
+ sauvola_threshold_formula(double m_x_y, double s_x_y)
{
- return static_cast<unsigned long long>(val);
+ // Badekas et al. said 0.34 was best.
+ const double k = 0.34;
+
+ // 128 is best for grayscale documents.
+ const double R = 128;
+
+ return sauvola_threshold_formula(m_x_y, s_x_y, k, R);
}
- /// \brief Compute a point wise threshold according Sauvola's
- /// binarization.
- ///
- /// \param[in] p A site.
- /// \param[in] simple An integral image of mean values.
- /// \param[in] squared An integral image of squared mean values.
- /// \param[in] win_width Window width.
- /// \param[in] k Control the threshold value in the local
- /// window. The higher, the lower the threshold
- /// form the local mean m(x, y).
- /// \param[in] R Maximum value of the standard deviation (128
- /// for grayscale documents).
-
- template <typename P, typename T>
+ /*! \brief Compute a point wise threshold according Sauvola's
+ binarization.
+
+ \param[in] p A site.
+ \param[in] simple An integral image of mean values.
+ \param[in] squared An integral image of squared mean values.
+ \param[in] win_width Window width.
+ \param[in] k Control the threshold value in the local
+ window. The higher, the lower the threshold
+ form the local mean m(x, y).
+ \param[in] R Maximum value of the standard deviation (128
+ for grayscale documents).
+
+ \return A threshold.
+ */
+ template <typename P, typename J>
double
compute_sauvola_threshold(const P& p,
- const integral_image<T>& simple,
- const integral_image<T>& squared,
+ const J& simple,
+ const J& squared,
int win_width, double k, double R)
{
mln_precondition(simple.nrows() == squared.nrows());
@@ -146,142 +170,116 @@ namespace scribo
int row_min = std::max(0, p.row() - w_2);
int col_min = std::max(0, p.col() - w_2);
- int row_max = std::min(simple.nrows() - 1, p.row() + w_2);
- int col_max = std::min(simple.ncols() - 1, p.col() + w_2);
+
+ int row_max = std::min(static_cast<int>(simple.nrows()) - 1,
+ p.row() + w_2);
+ int col_max = std::min(static_cast<int>(simple.ncols()) - 1,
+ p.col() + w_2);
+
+
+// std::cout << "sauvola threshold : "
+// << simple.domain() << " - "
+// << row_max << " - "
+// << col_max << std::endl;
double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
// Mean.
- double m_x_y_tmp = (simple(row_max, col_max)
- + simple(row_min, col_min)
- - simple(row_max, col_min)
- - simple(row_min, col_max));
+ double m_x_y_tmp = (simple.at_(row_max, col_max)
+ + simple.at_(row_min, col_min)
+ - simple.at_(row_max, col_min)
+ - simple.at_(row_min, col_max));
double m_x_y = m_x_y_tmp / wh;
// Standard deviation.
- double s_x_y_tmp = (squared(row_max, col_max)
- + squared(row_min, col_min)
- - squared(row_max, col_min)
- - squared(row_min, col_max));
+ double s_x_y_tmp = (squared.at_(row_max, col_max)
+ + squared.at_(row_min, col_min)
+ - squared.at_(row_max, col_min)
+ - squared.at_(row_min, col_max));
double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
// Thresholding.
- double t_x_y = m_x_y * (1.0 + k * ((s_x_y / R) - 1.0));
+ double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, k, R);
return t_x_y;
}
- template <typename P, typename T>
+ template <typename P, typename J>
double
- compute_sauvola_threshold(const P& p,
- const integral_image<T>& simple,
- const integral_image<T>& squared,
- int win_width)
+ compute_sauvola_threshold_single_image(const P& p,
+ const J& integral,
+ int win_width)
{
- // Badekas et al. said 0.34 was best.
- const double k = 0.34;
-
- // 128 is best for grayscale documents.
- const double R = 128;
-
- return compute_sauvola_threshold(p, simple, squared, win_width, k, R);
- }
-
-
- } // end of namespace scribo::binarization::internal
-
- } // end of namespace scribo::binarization
+ // Window half width.
+ int w_2 = win_width >> 1;
-} // end of namespace scribo
+ int row_min = std::max(0, p.row() - w_2);
+ int col_min = std::max(0, p.col() - w_2);
+ //FIXME: offset (-4) should be replaced by the number of
+ //padding pixels.
+ int row_max = std::min(static_cast<int>(integral.nrows()) - 1,
+ p.row() + w_2);
+ int col_max = std::min(static_cast<int>(integral.ncols()) - 1,
+ p.col() + w_2);
-namespace mln
-{
- template <typename T>
- class integral_image
- {
- public:
+// std::cout << "sauvola threshold : "
+// << simple.domain() << " - "
+// << row_max << " - "
+// << col_max << std::endl;
- integral_image()
- : img_(0), nrows_(0), ncols_(0)
- {}
-
- template<class F>
- integral_image(const image2d<T>& i, F func)
- : img_(0),
- nrows_(0),
- ncols_(0)
- {
- init_(i, func);
- }
+ double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
- template<class F>
- void init_(const image2d<T>& i, F func)
- {
- nrows_ = i.nrows();
- ncols_ = i.ncols();
+ // Mean.
+ double m_x_y_tmp = (integral.at_(row_max, col_max).first()
+ + integral.at_(row_min, col_min).first()
+ - integral.at_(row_max, col_min).first()
+ - integral.at_(row_min, col_max).first());
- img_ = (unsigned long long**)malloc(sizeof(unsigned long long*) * nrows_);
- for (int n = 0; n < nrows_; ++n)
- img_[n] = (unsigned long long*)malloc(sizeof(unsigned long long) * ncols_);
+ double m_x_y = m_x_y_tmp / wh;
- img_[0][0] = func(i.at_(0, 0));
+ // Standard deviation.
+ double s_x_y_tmp = (integral.at_(row_max, col_max).second()
+ + integral.at_(row_min, col_min).second()
+ - integral.at_(row_max, col_min).second()
+ - integral.at_(row_min, col_max).second());
- for (int row = 1; row < nrows_; ++row)
- img_[row][0] = (*this)(row - 1, 0) + func(i.at_(row, 0));
+ double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
- for (int col = 1; col < ncols_; ++col)
- img_[0][col] = (*this)(0, col - 1)
- + func(i.at_(0, col));
+ // Thresholding.
+ double t_x_y = m_x_y * (1.0 + 0.14 * ((s_x_y / 128) - 1.0));
- for (int row = 1; row < nrows_; ++row)
- for (int col = 1; col < ncols_; ++col)
- img_[row][col] = (*this)(row - 1, col)
- + (*this)(row, col - 1)
- - (*this)(row - 1, col - 1)
- + func(i.at_(row, col));
- }
+ return t_x_y;
+ }
- ~integral_image()
- {
- for (int n = 0; n < nrows_; ++n)
- free(img_[n]);
- free(img_);
- }
- bool is_valid() const
- {
- return img_ != 0;
- }
+ template <typename P, typename J>
+ double
+ compute_sauvola_threshold(const P& p,
+ const J& simple,
+ const J& squared,
+ int win_width)
+ {
+ // Badekas et al. said 0.34 was best.
+ const double k = 0.34;
- unsigned long long operator()(int row, int col) const
- {
- return img_[row][col];
- }
+ // 128 is best for grayscale documents.
+ const double R = 128;
- int nrows() const
- {
- return nrows_;
- }
+ return compute_sauvola_threshold(p, simple, squared, win_width, k, R);
+ }
- int ncols() const
- {
- return ncols_;
- }
+ } // end of namespace scribo::binarization::internal
- private:
- unsigned long long** img_;
- int nrows_;
- int ncols_;
- };
+ } // end of namespace scribo::binarization
-} // end of namespace mln
+} // end of namespace scribo
@@ -300,23 +298,25 @@ namespace scribo
namespace generic
{
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_concrete(I)
- sauvola_threshold(const I& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ sauvola_threshold(const Image<I>& input_, unsigned window_size,
+ Image<J>& simple_,
+ Image<J>& squared_)
{
trace::entering("scribo::binarization::impl::generic::sauvola_threshold");
- typedef mln_value(I) V;
- typedef mln_site(I) P;
+ const I& input = exact(input_);
+ J& simple = exact(simple_);
+ J& squared = exact(squared_);
- // Compute the sum of all intensities of input
- simple.init_(input, internal::identity_);
+ mln_assertion(input.is_valid());
+ mln_assertion(simple.is_valid());
+ mln_assertion(squared.is_valid());
- // Compute the sum of all squared intensities of input
- squared.init_(input, internal::square_);
+ typedef mln_value(I) V;
+ typedef mln_site(I) P;
// Savaula Algorithm with I.I.
@@ -340,24 +340,24 @@ namespace scribo
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_concrete(I)
sauvola_threshold_gl(const I& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ Image<J>& simple,
+ Image<J>& squared)
{
return impl::generic::sauvola_threshold(input, window_size,
simple, squared);
}
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_ch_value(I, value::int_u8)
sauvola_threshold_rgb8(const I& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ Image<J>& simple,
+ Image<J>& squared)
{
trace::entering("scribo::binarization::impl::sauvola_threshold_rgb8");
@@ -384,36 +384,36 @@ namespace scribo
namespace internal
{
- template <unsigned n, typename I, typename T>
+ template <unsigned n, typename I, typename J>
inline
mln_ch_value(I, value::int_u<n>)
sauvola_threshold_dispatch(const value::int_u<n>&, const I& input,
unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ J& simple,
+ J& squared)
{
return impl::sauvola_threshold_gl(input, window_size, simple, squared);
}
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_ch_value(I, value::int_u8)
sauvola_threshold_dispatch(const value::rgb8&, const I& input,
unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ J& simple,
+ J& squared)
{
return impl::sauvola_threshold_rgb8(input, window_size,
simple, squared);
}
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_ch_value(I, value::int_u8)
sauvola_threshold_dispatch(const mln_value(I)&, const I& input,
unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ J& simple,
+ J& squared)
{
// No dispatch for this kind of value type.
mlc_abort(I)::check();
@@ -427,11 +427,11 @@ namespace scribo
- template <typename I, typename T>
+ template <typename I, typename J>
mln_ch_value(I, value::int_u8)
sauvola_threshold(const Image<I>& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ Image<J>& simple,
+ Image<J>& squared)
{
trace::entering("scribo::binarization::sauvola_threshold");
@@ -441,8 +441,9 @@ namespace scribo
typedef mln_value(I) value_t;
mln_ch_value(I, value::int_u8)
output = internal::sauvola_threshold_dispatch(value_t(), exact(input),
- window_size, simple,
- squared);
+ window_size,
+ exact(simple),
+ exact(squared));
trace::exiting("scribo::text::ppm2pbm");
return output;
@@ -454,7 +455,10 @@ namespace scribo
mln_ch_value(I, value::int_u8)
sauvola_threshold(const Image<I>& input, unsigned window_size)
{
- mln::integral_image<value::int_u8> simple, squared;
+ mln_ch_value(I, double)
+ simple = init_integral_image(input, scribo::internal::identity_),
+ squared = init_integral_image(input, scribo::internal::square_);
+
return sauvola_threshold(input, window_size, simple, squared);
}
diff --git a/scribo/canvas/integral_browsing.hh b/scribo/canvas/integral_browsing.hh
new file mode 100644
index 0000000..814733f
--- /dev/null
+++ b/scribo/canvas/integral_browsing.hh
@@ -0,0 +1,412 @@
+// 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_CANVAS_INTEGRAL_BROWSING_HH
+# define SCRIBO_CANVAS_INTEGRAL_BROWSING_HH
+
+# include <mln/core/image/image2d.hh>
+# include <mln/util/couple.hh>
+
+namespace scribo
+{
+
+ namespace canvas
+ {
+ using namespace mln;
+
+
+ template <typename F>
+ void integral_browsing(const image2d<util::couple<double, double> >& ima,
+ unsigned step,
+ unsigned w, unsigned h,
+ F& functor);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ namespace internal
+ {
+
+ inline
+ void compute_stats(// in
+ double sum, double sum_2, unsigned n,
+ // out
+ double& mean, double& stddev)
+ {
+ mean = sum / n;
+ stddev = std::sqrt(sum_2 / n - mean * mean);
+ // unbias version:
+ // stddev = std::sqrt((sum_2 - n * mean * mean) / (n - 1));
+ }
+
+ } // end of namespace scribo::canvas::internal
+
+
+
+
+ template <typename F>
+ void integral_browsing(const image2d<util::couple<double, double> >& ima,
+ unsigned step,
+ unsigned w, unsigned h,
+ F& functor)
+ {
+ typedef util::couple<double, double> V;
+ typedef const V* Ptr;
+ Ptr a_ima, b_ima, c_ima, d_ima;
+
+ const unsigned
+ nrows = ima.nrows(),
+ ncols = ima.ncols(),
+ row_0 = step / 2,
+ col_0 = step / 2;
+
+ const unsigned
+ offset_down = ima.delta_index(dpoint2d(step, 0)),
+ offset_ante = ima.delta_index(dpoint2d(0, -w)),
+ offset_below = ima.delta_index(dpoint2d(+h, 0));
+
+ const unsigned
+ max_row_top = h/2,
+ max_row_mid = nrows - 1 - h/2,
+ max_col_left = w/2,
+ max_col_mid = ncols - 1 - w/2,
+
+ step_2 = step * step,
+
+ h_top = row_0 + h/2 + 1,
+ w_left = col_0 + w/2 + 1;
+
+ unsigned row, col;
+
+ for (col = col_0; col <= max_col_mid; col += step) ;
+ unsigned w_right = ncols - col + w/2;
+
+ Ptr
+ d_tl_start, d_tr_start,
+ b_ml_start = 0, d_ml_start = 0, b_mr_start = 0, d_mr_start = 0,
+ b_bl_start = 0, d_bl_start = 0, b_br_start = 0, d_br = 0;
+
+ double mean, stddev;
+
+
+ // -------------------------------
+ // T (top)
+ // -------------------------------
+
+ const unsigned
+ delta_start_left = step * w_left,
+ delta_start_right = step * w_right,
+ step_w = step * w;
+ unsigned
+ size_tl_start = h_top * w_left,
+ size_tl,
+ delta_size_tl = h_top * step,
+ size_tc = h_top * w,
+ delta_size_tr = h_top * step,
+ size_tr_start = h_top * w_right,
+ size_tr;
+
+ d_tl_start = & ima.at_(row_0 + h/2, col_0 + w/2);
+ d_tr_start = & ima.at_(row_0 + h/2, ncols - 1);
+
+ for (row = row_0; row <= max_row_top; row += step)
+ {
+
+ // TL (top left)
+
+ d_ima = d_tl_start;
+ size_tl = size_tl_start;
+
+ for (col = col_0; col <= max_col_left; col += step)
+ {
+ // D
+ internal::compute_stats(d_ima->first(),
+ d_ima->second(),
+ size_tl,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ d_ima += step;
+ size_tl += delta_size_tl;
+ }
+
+ delta_size_tl += step_2;
+ size_tl_start += delta_start_left;
+ d_tl_start += offset_down;
+
+ // TC (top center)
+
+ c_ima = d_ima + offset_ante;
+
+ for (; col <= max_col_mid; col += step)
+ {
+ // D - C
+ internal::compute_stats(d_ima->first() - c_ima->first(),
+ d_ima->second() - c_ima->second(),
+ size_tc,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ c_ima += step;
+ d_ima += step;
+ }
+
+ size_tc += step_w;
+
+ // TR (top right)
+
+ d_ima = d_tr_start;
+ double
+ d_sum = d_ima->first(),
+ d_sum_2 = d_ima->second();
+ size_tr = size_tr_start;
+
+ for (; col < ncols; col += step)
+ {
+ // D* - C
+ internal::compute_stats(d_sum - c_ima->first(),
+ d_sum_2 - c_ima->second(),
+ size_tr,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ c_ima += step;
+ size_tr -= delta_size_tr;
+ }
+
+ delta_size_tr += step_2;
+ size_tr_start += delta_start_right;
+ d_tr_start += offset_down;
+
+ }
+
+
+
+ // -------------------------------
+ // (M) middle
+ // -------------------------------
+
+
+ const unsigned
+ size_ml_start = h * w_left,
+ h_step = h * step,
+ size_mc = w * h;
+ unsigned
+ size_ml,
+ size_mr_start = h * w_right,
+ size_mr;
+
+ if (row <= max_row_mid)
+ {
+ b_ml_start = & ima.at_(row - h/2 - 1, col_0 + w/2);
+ d_ml_start = b_ml_start + offset_below;
+ b_mr_start = & ima.at_(row - h/2 - 1, ncols - 1);
+ d_mr_start = b_mr_start + offset_below;
+ }
+
+ for (; row <= max_row_mid; row += step)
+ {
+
+ // ML (middle left)
+
+ size_ml = size_ml_start;
+ b_ima = b_ml_start;
+ d_ima = d_ml_start;
+
+ for (col = col_0; col <= max_col_left; col += step)
+ {
+ // D - B
+ internal::compute_stats(d_ima->first() - b_ima->first(),
+ d_ima->second() - b_ima->second(),
+ size_ml,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ b_ima += step;
+ d_ima += step;
+ size_ml += h_step;
+ }
+
+ b_ml_start += offset_down;
+ d_ml_start += offset_down;
+
+ // MC (middle center)
+
+ a_ima = b_ima + offset_ante;
+ c_ima = d_ima + offset_ante;
+
+ for (; col <= max_col_mid; col += step)
+ {
+ // D + A - B - C
+ internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
+ (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
+ size_mc,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ a_ima += step;
+ b_ima += step;
+ c_ima += step;
+ d_ima += step;
+ }
+
+ // MR (middle right)
+
+ size_mr = size_mr_start;
+ b_ima = b_mr_start;
+ d_ima = d_mr_start;
+ double
+ d_b_sum = d_ima->first() - b_ima->first(),
+ d_b_sum_2 = d_ima->second() - b_ima->second();
+
+ for (; col < ncols; col += step)
+ {
+ // D* + A - B* - C
+ internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
+ d_b_sum_2 + (a_ima->second() - c_ima->second()),
+ size_mr,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ a_ima += step;
+ c_ima += step;
+ size_mr -= h_step;
+ }
+
+ b_mr_start += offset_down;
+ d_mr_start += offset_down;
+ }
+
+
+
+ // -------------------------------
+ // B (bottom)
+ // -------------------------------
+
+ unsigned
+ size_bl_start = (nrows - row + h/2) * w_left,
+ size_bl,
+ delta_size_bl = (nrows - row + h/2) * step,
+ size_bc = (nrows - row + h/2) * w,
+ size_br_start = (nrows - row + h/2) * w_right,
+ delta_size_br = (nrows - row + h/2) * step,
+ size_br;
+
+ if (row < nrows)
+ {
+ b_bl_start = & ima.at_(row - h/2 - 1, col_0 + w/2);
+ d_bl_start = & ima.at_(nrows - 1, col_0 + w/2);
+ b_br_start = & ima.at_(row - h/2 - 1, ncols - 1);
+ d_br = & ima.at_(nrows - 1, ncols - 1);
+ }
+
+ for (; row < nrows; row += step)
+ {
+
+ // BL (bottom left)
+
+ size_bl = size_bl_start;
+ b_ima = b_bl_start;
+ d_ima = d_bl_start;
+
+ for (col = col_0; col <= max_col_left; col += step)
+ {
+ // D* - B
+ internal::compute_stats(d_ima->first() - b_ima->first(),
+ d_ima->second() - b_ima->second(),
+ size_bl,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ b_ima += step;
+ d_ima += step;
+ size_bl += delta_size_bl;
+ }
+
+ delta_size_bl -= step_2;
+ size_bl_start -= delta_start_left;
+ b_bl_start += offset_down;
+
+ // BC (bottom center)
+
+ a_ima = b_ima + offset_ante;
+ c_ima = d_ima + offset_ante;
+
+ for (; col <= max_col_mid; col += step)
+ {
+ // D* + A - B - C*
+ internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
+ (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
+ size_bc,
+ mean, stddev);
+// std::cout << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()) << std::endl;
+
+// std::cout << d_ima->second() << " - " << b_ima->second() << " - "
+// << a_ima->second() << " - " << c_ima->second() << std::endl;
+// std::cout << d_ima->first() << " - " << b_ima->first() << " - "
+// << a_ima->first() << " - " << c_ima->first() << std::endl;
+ functor.exec(mean, stddev);
+ a_ima += step;
+ b_ima += step;
+ c_ima += step;
+ d_ima += step;
+ }
+
+ size_bc -= step_w;
+
+ // BR (bottom right)
+
+ size_br = size_br_start;
+ b_ima = b_br_start;
+ d_ima = d_br;
+ double
+ d_b_sum = d_ima->first() - b_ima->first(),
+ d_b_sum_2 = d_ima->second() - b_ima->second();
+
+ for (; col < ncols; col += step)
+ {
+ // D* + A - B* - C*
+ internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
+ d_b_sum_2 + (a_ima->second() - c_ima->second()),
+ size_br,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ a_ima += step;
+ c_ima += step;
+ size_br -= delta_size_br;
+ }
+
+ delta_size_br -= step_2;
+ size_br_start -= delta_start_right;
+ b_br_start += offset_down;
+ }
+
+ functor.finalize();
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+ } // end of namespace scribo::canvas
+
+} // end of namespace mln
+
+
+#endif // ! SCRIBO_CANVAS_INTEGRAL_BROWSING_HH
diff --git a/scribo/core/init_integral_image.hh b/scribo/core/init_integral_image.hh
new file mode 100644
index 0000000..ec19070
--- /dev/null
+++ b/scribo/core/init_integral_image.hh
@@ -0,0 +1,96 @@
+// 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_CORE_INIT_INTEGRAL_IMAGE_HH
+# define SCRIBO_CORE_INIT_INTEGRAL_IMAGE_HH
+
+/// \file
+///
+///
+
+# include <mln/core/image/image2d.hh>
+
+namespace scribo
+{
+ using namespace mln;
+
+
+ namespace internal
+ {
+
+ double square_(const double& val)
+ {
+ double v = static_cast<double>(val);
+ return v * v;
+ }
+
+ double identity_(const double& val)
+ {
+ return static_cast<double>(val);
+ }
+
+ } // end of namespace scribo::internal
+
+
+ template <typename I, typename F>
+ mln_ch_value(I,double)
+ init_integral_image(const Image<I>& input_, F& func)
+ {
+ trace::entering("scribo::init_integral_image");
+
+ const I& input = exact(input_);
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+
+ mln_ch_value(I,double) output;
+ initialize(output, input);
+
+ unsigned
+ nrows_ = input.nrows(),
+ ncols_ = input.ncols();
+
+ output.at_(0,0) = func(input.at_(0, 0));
+
+ for (unsigned row = 1; row < nrows_; ++row)
+ output.at_(row,0) = output.at_(row - 1, 0) + func(input.at_(row, 0));
+
+ for (unsigned col = 1; col < ncols_; ++col)
+ output.at_(0,col) = output.at_(0, col - 1)
+ + func(input.at_(0, col));
+
+ for (unsigned row = 1; row < nrows_; ++row)
+ for (unsigned col = 1; col < ncols_; ++col)
+ output.at_(row, col) = output.at_(row - 1, col)
+ + output.at_(row, col - 1)
+ - output.at_(row - 1, col - 1)
+ + func(input.at_(row, col));
+
+ trace::exiting("scribo::init_integral_image");
+ return output;
+ }
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_CORE_INIT_INTEGRAL_IMAGE_HH
diff --git a/scribo/src/binarization/sauvola_ms.cc b/scribo/src/binarization/sauvola_ms.cc
index 747bc11..8a07dcc 100644
--- a/scribo/src/binarization/sauvola_ms.cc
+++ b/scribo/src/binarization/sauvola_ms.cc
@@ -25,6 +25,11 @@
#include <mln/core/alias/neighb2d.hh>
#include <mln/data/stretch.hh>
+#include <mln/data/paste.hh>
+// #include <mln/debug/iota.hh>
+// #include <mln/debug/quiet.hh>
+// #include <mln/debug/println.hh>
+// #include <mln/debug/println_with_border.hh>
#include <mln/debug/filename.hh>
#include <mln/fun/i2v/array.hh>
#include <mln/io/pbm/all.hh>
@@ -32,17 +37,30 @@
#include <mln/io/ppm/all.hh>
#include <mln/literal/colors.hh>
#include <mln/math/sqr.hh>
-#include <mln/subsampling/subsampling.hh>
+#include <mln/math/abs.hh>
+
+#include <mln/subsampling/antialiased.hh>
+
#include <mln/transform/influence_zone_geodesic.hh>
#include <mln/util/timer.hh>
#include <mln/value/int_u16.hh>
#include <mln/value/int_u8.hh>
#include <mln/value/label_16.hh>
#include <mln/value/rgb8.hh>
+#include <mln/border/equalize.hh>
+#include <mln/border/mirror.hh>
+#include <mln/border/adjust.hh>
#include <mln/debug/filename.hh>
+#include <mln/core/box_runend_piter.hh>
+#include <mln/core/box_runstart_piter.hh>
+
+#include <scribo/subsampling/integral_single_image.hh>
+//#include <scribo/subsampling/integral.hh>
+
#include <scribo/core/macros.hh>
#include <scribo/core/object_image.hh>
+
#include <scribo/filter/objects_small.hh>
#include <scribo/filter/objects_large.hh>
@@ -50,155 +68,737 @@
#include <scribo/filter/objects_thick.hh>
#include <scribo/primitive/extract/objects.hh>
+
#include <scribo/binarization/sauvola_threshold.hh>
+#include <scribo/binarization/internal/first_pass_functor.hh>
+
+#include <scribo/canvas/integral_browsing.hh>
+
#include <scribo/debug/usage.hh>
#include <scribo/debug/save_object_diff.hh>
+
namespace mln
{
+ using value::int_u8;
-// FIXME: do not use scribo's filters since they use object images.
-// Object images computes object bounding boxes which are not needed here.
-//
-// template <typename L>
-// mln_concrete(L)
-// filter_small_objects(const Image<L>& objects,
-// const mln_value(L)& nobjects, unsigned min_size)
-// {
-// card = labeling::compute(card_t(), objects, objects.nlabels());
-
-// mln_concrete(L) output;
-// initialize(output, objects);
-// fun::i2v::array<bool> f(nobjects.next(), 0);
-// for (unsigned i = 0; i < card.size(); ++i)
+ unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
+ {
+ if (parent.element(x) == x)
+ return x;
+ return parent.element(x) = my_find_root(parent,
+ parent.element(x));
+ }
-// }
- template <typename I, typename T>
- mln_ch_value(I, bool)
- apply_sauvola(const I& intensity, const T& threshold, unsigned s)
+ image2d<int_u8>
+ compute_t_n_and_e_2(const image2d<int_u8>& sub, image2d<int_u8>& e_2,
+ unsigned lambda_min, unsigned lambda_max,
+ unsigned q, unsigned i, unsigned w,
+ const image2d<util::couple<double,double> >& integral_sum_sum_2)
+ // lambdas: limits of component cardinality at this scale
{
- mln_precondition(intensity.domain() == threshold.domain());
+ typedef image2d<int_u8> I;
+ typedef point2d P;
- mln_ch_value(I, bool) output;
- initialize(output, intensity);
+ util::timer tt;
+ float t_;
- mln_piter(I) p(intensity.domain());
- for_all(p)
- output(p) = (intensity(p) <= threshold(p / s));
+ unsigned ratio = std::pow(q, i - 2); // Ratio in comparison to e_2
+
+
+ tt.restart();
+
+ unsigned w_2 = w / 2 * ratio;
+
+ // 1st pass
+ scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
+ f(sub);
+ scribo::canvas::integral_browsing(integral_sum_sum_2,
+ ratio,
+ w_2, w_2,
+ f);
+
+// debug::println("mask", f.msk);
+// debug::println("parent", f.parent);
+// debug::println("card", f.card);
+
+ t_ = tt;
+ if (mln::debug::quiet)
+ std::cout << "1st pass - " << t_ << std::endl;
+
+ tt.restart();
+
+ {
+ util::array<mln_value_(I) *> ptr(ratio);
+ unsigned nrows = geom::nrows(e_2);
+
+ mln_box_runend_piter_(I) sp(sub.domain()); // Backward.
+ unsigned ncols = sp.run_length();
+ for_all(sp)
+ {
+ unsigned p = &sub(sp) - sub.buffer(); // Offset
+ P site = sp;
+
+ {
+ P tmp = site * ratio;
+
+ // FIXME: to be removed!
+ if (tmp.row() + ratio >= nrows)
+ ptr.resize(nrows - tmp.row());
+
+ ptr(0) = &e_2(tmp);
+ // FIXME: pointers could just be updated with an offset.
+ for (unsigned j = 1; j < ptr.size(); ++j)
+ {
+ tmp[0] += 1;
+ ptr(j) = & e_2(tmp);
+ }
+ }
+
+ for (unsigned j = 0; j < ncols; ++j)
+ {
+ if (f.msk.element(p))
+ {
+
+ mln_site_(I) sq = site * ratio;
+
+ if (f.parent.element(p) == p)
+ {
+ // test over the component cardinality
+ f.msk.element(p) = f.card.element(p) > lambda_min
+ && f.card.element(p) < lambda_max;
+
+ if (f.msk.element(p) && e_2(sq) == 0u)
+ {
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
+// debug(sq) = i;
+ }
+
+ }
+ else
+ {
+ // Propagation
+ f.msk.element(p) = f.msk.element(f.parent.element(p));
+
+ if (f.msk.element(p) && e_2(sq) == 0u)
+ {
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
+// debug(sq) = i;
+ }
+
+ }
+ }
+
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ ptr(l) -= ratio;
+
+ --site[1];
+ --p;
+ }
+
+ }
- return output;
+ t_ = tt;
+ if (mln::debug::quiet)
+ std::cout << "2nd pass - " << t_ << std::endl;
+
+// io::pgm::save(e_2, mln::debug::filename("e.pgm", i));
+// io::pgm::save(debug, mln::debug::filename("debug.pgm", i));
+// debug::println(msk);
+// io::pbm::save(f.msk, mln::debug::filename("mask.pbm", i));
+// io::pgm::save(data::stretch(int_u8(), card), mln::debug::filename("card.pgm"));
+ } // end of 2nd pass
+
+// io::pgm::save(f.t_sub, mln::debug::filename("t.pgm", i));
+ return f.t_sub;
}
- template <typename I>
- mln_concrete(I)
- enlarge(const Image<I>& input_, unsigned ratio)
+
+ template <typename I, typename J, typename K>
+ mln_ch_value(I, bool)
+ binarize_generic(const I& in, const J& e2, const util::array<K>& t_ima,
+ unsigned s)
{
- const I& input = exact(input_);
- mln_precondition(input.is_valid());
+ mln_ch_value(I,bool) out;
+ initialize(out, in);
- mln_domain(I) bbox(input.domain().pmin() * ratio,
- input.domain().pmax() * ratio
- + (ratio - 1) * mln::down_right);
+ typedef const mln_value(K)* ptr_type;
- mln_concrete(I) output(bbox);
+ ptr_type ptr_t[5];
+ ptr_t[2] = & t_ima[2].at_(0, 0);
+ ptr_t[3] = & t_ima[3].at_(0, 0);
+ ptr_t[4] = & t_ima[4].at_(0, 0);
- mln_site(I) first_p = input.domain().pmin();
- for (def::coord j = geom::min_col(input);
- j <= geom::max_col(input); ++j)
- for (def::coord i = geom::min_row(input);
- i <= geom::max_row(input); ++i)
- {
- point2d p1(i, j);
- point2d p2(first_p[0] + i * ratio, first_p[1] + j * ratio);
- output(p2) = input(p1);
- output(p2 + mln::right) = input(p1);
- output(p2 + mln::down_right) = input(p1);
- output(p2 + mln::down) = input(p1);
+ const mln_value(J)* ptr_e2 = & e2.at_(0, 0);
+ const mln_value(I)* ptr__in = & in.at_(0, 0);
+ bool* ptr__out = & out.at_(0, 0);
+
+
+ // Since we iterate from a smaller image in the largest ones and
+ // image at scale 1 does not always have a size which can be
+ // divided by (4*s), some sites in the border may not be processed
+ // and we must skip them.
+ unsigned more_offset = in.border() - ((4 * s) - in.ncols() % (4 * s));
+
+ if (more_offset == (4 * s))
+ more_offset = 0; // No offset needed.
+
+ const int
+ nrows4 = t_ima[4].nrows(), ncols4 = t_ima[4].ncols(),
+
+
+ delta1 = in.delta_index(dpoint2d(+1, -(s - 1))),
+ delta1b = in.delta_index(dpoint2d(+1, -(s + s - 1))),
+ delta1c = in.delta_index(dpoint2d(-(s + s - 1), +1)),
+ delta1d = in.delta_index(dpoint2d(+1, -(s * 4 - 1))),
+ delta1e = in.delta_index(dpoint2d(-(s * 4 - 1), +1)),
+ delta1f = in.delta_index(dpoint2d(-(s - 1), +1)),
+
+ delta2 = t_ima[2].delta_index(dpoint2d(+1, -1)),
+ delta2b = t_ima[2].delta_index(dpoint2d(+1, -3)),
+ delta2c = t_ima[2].delta_index(dpoint2d(-3, +1)),
+
+ delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
+
+ eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols() - in.border())) + more_offset,
+ eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
+ eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
+ eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
+
+ mln_value(J) threshold;
+ for (int row4 = 0; row4 < nrows4; ++row4)
+ {
+ for (int col4 = 0; col4 < ncols4; ++col4)
+ {
+ // top left 1
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1c; ptr__in += delta1c;
+ }
+
+ ptr_t[2] -= delta2; ptr_e2 -= delta2;
+ }
+
+ // top right 1
+ ptr_t[3] += 1;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1d; ptr__in += delta1d;
+ }
+
+ ptr_t[2] += delta2b; ptr_e2 += delta2b;
+ }
+
+ // bot left 1
+ ptr_t[3] += delta3;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1c; ptr__in += delta1c;
+ }
+
+ ptr_t[2] -= delta2; ptr_e2 -= delta2;
+ }
+
+ // bot right 1
+ ptr_t[3] += 1;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1e; ptr__in += delta1e;
+ }
+ }
+
+ // bot right -> next top left
+ ptr_t[2] += delta2c; ptr_e2 += delta2c;
+ ptr_t[3] = ptr_t[3] - delta3;
+ ptr_t[4] += 1;
}
- return output;
+ // eof -> next bof
+ ptr__out += eor1; ptr__in += eor1;
+ ptr_t[2] += eor2; ptr_e2 += eor2;
+ ptr_t[3] += eor3;
+ ptr_t[4] += eor4;
+ }
+
+
+// mln::debug::println(out);
+
+ return out;
+ }
+
+
+
+
+ unsigned sub(unsigned nbr, unsigned down_scaling)
+ {
+ return (nbr + down_scaling - 1) / down_scaling;
}
template <typename I>
- object_image(mln_ch_value(I, value::label_16))
- get_objects(const Image<I>& input_,
- unsigned min_size, unsigned max_size,
- unsigned ratio, unsigned w, unsigned scale)
+ util::array<util::couple<mln_domain(I), unsigned> >
+ compute_sub_domains(const I& ima, unsigned n_scales, unsigned s)
{
- const I& input = exact(input_);
+ util::array<util::couple<unsigned, unsigned> > n(n_scales + 2);
- mln_precondition(input.is_valid());
+ n(1) = make::couple(ima.nrows(), ima.ncols());
+ n(2) = make::couple(sub(n(1).first(), s),
+ sub(n(1).second(), s));
+ for (unsigned i = 3; i <= n_scales + 1; ++i)
+ n(i) = make::couple(sub(n(i - 1).first(), 2),
+ sub(n(i - 1).second(), 2));
- typedef mln_ch_value(I, value::label_16) L;
- dpoint2d none(0, 0);
- I input_sub = mln::subsampling::subsampling(input, none, ratio);
+ util::array<util::couple<mln_domain(I), unsigned> > out(n.size());
+ out(0) = make::couple(make::box2d(1,1), 1u);
+ out(1) = make::couple(make::box2d(ima.nrows(), ima.ncols()), 2u);
+ out(n_scales + 1) = make::couple(make::box2d(n(n_scales + 1).first(),
+ n(n_scales + 1).second()),
+ 1u);
- image2d<value::int_u8>
- t = scribo::binarization::sauvola_threshold(input_sub, w);
+ for (unsigned i = n_scales; i > 1; --i)
+ out(i) = make::couple(make::box2d(2 * out(i + 1).first().nrows(),
+ 2 * out(i + 1).first().ncols()),
+ 2 * out(i + 1).second());
- image2d<bool> b = apply_sauvola(input_sub, t, 1);
+ out(1).second() = std::max(out(2).first().ncols() * s - ima.ncols(),
+ out(2).first().nrows() * s - ima.nrows());
- value::label_16 nb;
- object_image(L) lbl = scribo::primitive::extract::objects(b, c8(), nb);
- object_image(L) lbl_raw(lbl);
+// out(1).second() = std::max(ima.ncols() % (4 * s),
+// ima.nrows() % (4 * s));
- if (min_size > 0)
- lbl = scribo::filter::objects_small(lbl, min_size);
- if (max_size > 0)
- lbl = scribo::filter::objects_large(lbl, max_size);
- scribo::debug::save_object_diff(lbl_raw, lbl,
- mln::debug::filename("filter_diff.ppm", scale + 2));
+// out(2).first().ncols() * s - ima.ncols(),
+// out(2).first().nrows() * s - ima.nrows() );
- return lbl;
+ return out;
}
+
bool
check_args(int argc, char * argv[])
{
- if (argc < 7)
+ if (argc < 5 || argc > 7)
return false;
- int nb_scale = atoi(argv[3]);
- int s = atoi(argv[4]);
- int q = atoi(argv[5]);
+// int nb_scale = atoi(argv[3]);
+ int s = atoi(argv[3]);
+// int q = atoi(argv[5]);
- if (q < 2)
- {
- std::cout << "q must be greater than 2." << std::endl;
- return false;
- }
- if (s < 1 || s < q)
+// if (q < 2)
+// {
+// std::cout << "q must be greater than 2." << std::endl;
+// return false;
+// }
+ if (s < 1 || s > 3)// || s < q)
{
- std::cout << "s must be greater or equal to 1 and greater than q."
+ std::cout << "s must be set to 2 or 3."
<< std::endl;
return false;
}
- if (nb_scale < 1)
- {
- std::cout << "Not enough scales." << std::endl;
- return false;
- }
- if ((argc - 7) != (nb_scale - 1))
- {
- std::cout << "Not enough area threshold."
- << "There must be nb_scale - 1 thresholds."
- << std::endl;
- return false;
- }
+// if (nb_scale < 1)
+// {
+// std::cout << "Not enough scales." << std::endl;
+// return false;
+// }
+
+// if ((argc - 7) != (nb_scale - 1))
+// {
+// std::cout << "Not enough area threshold."
+// << "There must be nb_scale - 1 thresholds."
+// << std::endl;
+// return false;
+// }
return true;
}
+ void data_rand(image2d<unsigned>& e2)
+ {
+ unsigned v = 2;
+ mln_piter_(box2d) p(e2.domain());
+ for_all(p)
+ {
+ e2(p) = v++;
+ if (v == 5) v = 2;
+ }
+ }
+
+
} // end of namespace mln;
@@ -207,11 +807,10 @@ namespace mln
const char *args_desc[][2] =
{
{ "input.pgm", "A graylevel image." },
- { "w", "Window size." },
- { "nb_scale", "Number of scales (Common value: 3)." },
+ { "w", "Window size. (Common value: 51)" },
{ "s", "First subsampling ratio (Common value: 2)." },
- { "q", "Next subsampling ratio (Common value: 2)." },
- { "<area threshold> (Common values: 200 800)", "Area threshold" },
+ { "min_area", "Minimum object area (relative to scale 2) (Common value: 200)" },
+ { "debug", "Display debug/bench data if set to 1" },
{0, 0}
};
@@ -229,11 +828,10 @@ int main(int argc, char *argv[])
typedef image2d<label_16> L;
-
if (!check_args(argc, argv))
return scribo::debug::usage(argv,
- "Binarization of a color image based on Sauvola's algorithm.",
- "input.pgm w nb_scale s q <area thresholds>",
+ "Multi-Scale Binarization of a color image based on Sauvola's algorithm.",
+ "input.pgm w s area_thresholds output.pbm [debug]",
args_desc, "A binary image.");
trace::entering("main");
@@ -244,163 +842,233 @@ int main(int argc, char *argv[])
unsigned w = atoi(argv[2]);
// First subsampling scale.
- unsigned s = atoi(argv[4]);
+ unsigned s = atoi(argv[3]);
// Number of subscales.
- unsigned nb_subscale = atoi(argv[3]);
+ unsigned nb_subscale = 3;//atoi(argv[3]);
// Subscale step.
- unsigned q = atoi(argv[5]);
+ unsigned q = 2;//atoi(argv[5]);
+
+ mln::debug::quiet = true;
- std::cout << "Running Sauvola_ms with w = " << w
- << ", s = " << s
- << ", nb_subscale = " << nb_subscale
- << ", q = " << q
- << std::endl;
+ if (argc == 7)
+ mln::debug::quiet = ! atoi(argv[6]);
+
+
+ if (mln::debug::quiet)
+ std::cout << "Running Sauvola_ms with w = " << w
+ << ", s = " << s
+ << ", nb_subscale = " << nb_subscale
+ << ", q = " << q
+ << std::endl;
typedef image2d<value::int_u8> I;
dpoint2d none(0, 0);
- mln::util::timer timer_;
+ mln::util::timer
+ timer_,
+ sauvola_timer_;;
+
+ // Tmp variable used for timer results;
+ float t_;
- timer_.start();
I input_full;
io::pgm::load(input_full, argv[1]);
- std::cout << "Image loaded - " << timer_ << std::endl;
- timer_.restart();
+ {
+ unsigned max_dim = math::max(input_full.ncols() / 2,
+ input_full.nrows() / 2);
+ if (w > max_dim)
+ {
+ std::cout << "------------------" << std::endl;
+ std::cout << "The window is too large! Image size is only "
+ << input_full.nrows() << "x" << input_full.ncols()
+ << std::endl
+ << "Window size must not exceed " << max_dim
+ << std::endl;
+ return 1;
+ }
+ }
- util::array<object_image(L)> lbls;
+// I input_full(9,9);
+// mln::debug::iota(input_full);
+ sauvola_timer_.start();
+ unsigned lambda_min = atoi(argv[4]);
+ unsigned lambda_max = lambda_min * q; // * atoi(argv[7])
- // First subsampling 1/s
-// std::cout << "1/" << s << std::endl;
- timer_.start();
- I input = mln::subsampling::subsampling(input_full, none, s);
+ util::array<I> t_ima;
+
+ // Make sure t_ima indexes start from 2.
+ {
+ I dummy(1,1);
+ for (unsigned i = 0; i < nb_subscale + 2; ++i)
+ t_ima.append(dummy);
+ }
- integral_image<int_u8> simple, squared;
- image2d<int_u8>
- t_1 = scribo::binarization::sauvola_threshold(input, w, simple, squared);
- label_16 nb1;
- image2d<bool> b_1 = apply_sauvola(input, t_1, 1);
+ image2d<int_u8> e_2;
+ util::array<I> sub_ima;
+
+ // Make sure sub_ima indexes start from 2.
{
- lbls.append(primitive::extract::objects(b_1, c8(), nb1));
- object_image(L) lbl_raw(lbls[0]);
- lbls[0] = filter::objects_large(lbls[0], atoi(argv[6]));
- scribo::debug::save_object_diff(lbl_raw, lbls[0],
- mln::debug::filename("filter_diff.ppm", 2));
+ I dummy(1,1);
+ sub_ima.append(dummy);
+ sub_ima.append(dummy);
}
- std::cout << "Scale 2 - 1/" << s << " Done - " << timer_ << std::endl;
+ timer_.restart();
+ util::array<util::couple<box2d, unsigned> >
+ sub_domains = compute_sub_domains(input_full, nb_subscale, s);
+ if (mln::debug::quiet)
+ std::cout << "adjusting input border to " << sub_domains(1).second()
+ << std::endl;
- // Additional subscales.
- for (unsigned i = 1; i < nb_subscale; ++i)
- {
- unsigned ratio = std::pow(q, i);
-// std::cout << "Scale " << 2 + i << " - 1/" << s * ratio << std::endl;
- timer_.restart();
- unsigned
- min_size = atoi(argv[5 + i]) / ratio,
- max_size;
- if ((3 + i) == static_cast<unsigned>(argc))
- max_size = 0; // Last subscale, so not max size limit.
- else
- max_size = atoi(argv[6 + i]) / ratio;
-
- lbls.append(get_objects(input, min_size, max_size, ratio, w, i));
- std::cout << "Scale " << 2 + i
- << " - 1/" << s * ratio
- << " Done - " << timer_ << std::endl;
- }
+ border::adjust(input_full, sub_domains(1).second());
+ border::mirror(input_full);
- std::cout << "--------" << std::endl;
+// mln::debug::println_with_border(input_full);
- // Constructing "scale image".
- image2d<int_u8> e;
- initialize(e, input);
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "sub domains computed and adjust input border size - "
+ << t_ << std::endl;
- data::fill(e, 0);
+ // Resize input and compute integral images.
+ timer_.restart();
+ image2d<util::couple<double,double> > integral_sum_sum_2;
- typedef object_image(L) obj_t;
+ if (mln::debug::quiet)
+ std::cout << "sub_domain(2).domain() == " << sub_domains(2).first() << std::endl;
- for (int i = nb_subscale - 1; i >= 0; --i)
- {
- unsigned ratio = std::pow(q, i);
+ sub_ima.append(scribo::subsampling::integral(input_full, s,
+ integral_sum_sum_2,
+ sub_domains[2].first(),
+ sub_domains[2].second()));
- std::cout << "Scale " << 2 + i << " - 1/" << s * ratio << " merged" << std::endl;
+// mln::debug::println(integral_sum_sum_2);
- mln_piter_(obj_t) p(lbls[i].domain());
- for_all(p)
- if (lbls[i](p) != 0 && e(p * ratio) == 0)
- {
- box2d b(p * ratio, p * ratio + mln::down_right * (ratio - 1));
- data::fill((e | b).rw(), i + 1);
- }
- }
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "subsampling 1 -> 2 And integral images - " << t_
+ << " - nsites = "
+ << input_full.domain().nsites() << " -> "
+ << sub_ima[2].domain().nsites() << " - "
+ << input_full.domain() << " -> "
+ << sub_ima[2].domain() << std::endl;
- std::cout << "--------" << std::endl;
- /// Saving "scale image".
+ for (unsigned i = 3; i <= nb_subscale + 1; ++i)
{
- mln::fun::i2v::array<value::int_u8> f(nb_subscale + 1, 0);
- for (unsigned i = 1; i <= nb_subscale; ++i)
- f(i) = 255 / nb_subscale * i;
-
- io::pgm::save(e, "e_raw.pgm");
- io::pgm::save(data::transform(e, f), "e.pgm");
+ timer_.restart();
+ sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], q, none,
+ sub_domains[i].first(),
+ sub_domains[i].second()));
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "subsampling " << (i - 1) << " -> " << i
+ << " - " << t_
+ << " - nsites = "
+ << sub_ima[i].domain().nsites() << " - "
+ << sub_ima[i].domain()
+ << std::endl;
}
- /// Saving influence zone scale image.
- image2d<int_u8>
- e_ext = transform::influence_zone_geodesic(e, c8(), mln_max(unsigned));
- io::pgm::save(e_ext, "e_ext.pgm");
+ initialize(e_2, sub_ima[2]);
+ data::fill(e_2, 0u);
+ // Compute threshold image.
- /// Creating window size image
- std::cout << "Creating window size image..." << std::endl;
- mln::fun::i2v::array<value::int_u16> f(nb_subscale, 0);
- for (unsigned i = 0; i < nb_subscale; ++i)
+ // Highest scale -> no maximum component size.
{
- unsigned ratio = std::pow(q, i);
- f(i) = ratio * w;
+ timer_.restart();
+ int i = sub_ima.size() - 1;
+ unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
+ t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
+ lambda_min / ratio,
+ mln_max(unsigned),
+ q, i, w, integral_sum_sum_2);
+
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Scale " << i
+ << " - 1/" << s * ratio
+ << " compute t_n and update e - " << t_ << std::endl;
}
- image2d<int_u16> wsize = data::transform(e_ext, f);
- io::pgm::save(data::stretch(int_u8(), wsize), "wsize.pgm");
+ // Other scales -> maximum and minimum component size.
+ {
+ for (int i = sub_ima.size() - 2; i > 2; --i)
+ {
+ timer_.restart();
+ unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
+ t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
+ lambda_min / ratio,
+ lambda_max / ratio,
+ q, i, w, integral_sum_sum_2);
+
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Scale " << i
+ << " - 1/" << s * ratio
+ << " compute t_n and update e - " << t_ << std::endl;
+ }
+ }
+ // Lowest scale -> no minimum component size.
+ {
+ timer_.restart();
+ t_ima[2] = compute_t_n_and_e_2(sub_ima[2], e_2, 0, lambda_max,
+ 1, 2, w, integral_sum_sum_2);
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Scale " << 2
+ << " - 1/" << s
+ << " compute t_n and update e - " << t_ << std::endl;
+
+ }
+
+ if (mln::debug::quiet)
+ std::cout << "--------" << std::endl;
+// io::pgm::save(e_2, mln::debug::filename("e.pgm"));
- /// Constructing threshold image
- image2d<int_u8> t;
- initialize(t, input);
- std::cout << "Computing threshold image..." << std::endl;
timer_.restart();
- mln_piter_(L) p(input.domain());
- for_all(p)
- t(p) = binarization::internal::compute_sauvola_threshold(p,
- simple,
- squared,
- wsize(p));
- std::cout << "Compute t Done - " << timer_ << std::endl;
+ e_2 = transform::influence_zone_geodesic(e_2, c8());
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "influence zone - " << t_ << std::endl;
- io::pgm::save(t, "t.pgm");
- timer_.restart();
+// Saving influence zone scale image.
+// io::pgm::save(e_2, mln::debug::filename("e_ext.pgm"));
+// io::pbm::save(bin, argv[8]);
+// io::pgm::save(t, mln::debug::filename("t.pgm"));
- /// Applying threshold image and save.
- io::pbm::save(apply_sauvola(input_full, t, s), argv[argc - 1]);
- std::cout << "sauvola applied and saved Done - " << timer_ << std::endl;
+// for (unsigned i = 2; i < t_ima.size(); ++i)
+// io::pgm::save(t_ima[i], mln::debug::filename("t.pgm", i));
- trace::exiting("main");
+ timer_.restart();
+ image2d<bool> out_new = binarize_generic(input_full, e_2, t_ima, s);
+// image2d<bool> out_new = binarize(input_full, e_2, t_ima);
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Compute bin - " << t_ << std::endl;
+
+ t_ = sauvola_timer_;
+ if (mln::debug::quiet)
+ std::cout << "Sauvola : " << t_ << std::endl;
+ io::pbm::save(out_new, argv[5]);
}
+
diff --git a/scribo/subsampling/integral_single_image.hh b/scribo/subsampling/integral_single_image.hh
new file mode 100644
index 0000000..07b391d
--- /dev/null
+++ b/scribo/subsampling/integral_single_image.hh
@@ -0,0 +1,403 @@
+// 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_SUBSAMPLING_INTEGRAL_HH
+# define SCRIBO_SUBSAMPLING_INTEGRAL_HH
+
+/// \file
+///
+/// Both subsampling and integral image computation.
+
+#include <mln/core/concept/image.hh>
+#include <mln/metal/equal.hh>
+#include <mln/extension/fill.hh>
+#include <mln/debug/println.hh>
+#include <mln/debug/println_with_border.hh>
+
+
+
+namespace scribo
+{
+
+ namespace subsampling
+ {
+ using namespace mln;
+
+
+ /*! \brief Subsample an image and compute tow integral images.
+
+ \param[in] input An image of Scalar.
+ \param[in] scale The scale factor.
+ \param[in] integral_sum Integral image of mean values.
+ \param[in] integral_sum_2 Integral image of squared mean values.
+ \param[in] output_domain The domain of the subscaled image.
+ \param[in] border_thickness Border of the integral and subscaled images.
+
+ \p integral_sum, \p integral_sum_2 and output image have the same domain.
+ The output domain is set with \p output_domain.
+
+ */
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input, unsigned scale,
+ Image<J>& integral_sum, Image<J>& integral_sum_2,
+ const mln_domain(I)& output_domain, unsigned border_thickness);
+
+ /*! \overload
+
+ The output domain is defined as follows :
+
+ for_each(dimension)
+ input.domain()[dimension] = (N + scale - 1) / scale
+
+ where N is the number of elements in one run in a given dimension.
+
+ The border thickness is set to mln::border::thickness.
+ */
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input, unsigned scale,
+ Image<J>& integral_sum, Image<J>& integral_sum_2);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ // Implementation.
+
+ namespace impl
+ {
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral_3(const Image<I>& input_,
+ Image<J>& integral_sum_sum_2_,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::impl::integral_3");
+
+ const unsigned scale = 3;
+
+ const I& input = exact(input_);
+ J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
+ const unsigned area = scale * scale;
+
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ typedef mln_value(I) V;
+ typedef mln_sum(V) S;
+ typedef mln_value(J) V2;
+ typedef mln_site(I) P;
+
+ mln_concrete(I) sub(output_domain, border_thickness);
+ V* p_sub = sub.buffer();
+
+ integral_sum_sum_2.init_(output_domain, border_thickness);
+ V2* p_integ = integral_sum_sum_2.buffer();
+
+ const unsigned up = sub.delta_index(dpoint2d(-1, 0));
+
+ const unsigned nrows = 3 * output_domain.nrows();
+ const unsigned ncols = 3 * output_domain.ncols();
+
+ unsigned row = 0;
+
+ unsigned b_offset = sub.delta_index(dpoint2d(border_thickness,
+ border_thickness));
+ p_sub += b_offset;
+ p_integ += b_offset;
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ const V* ptr3 = & input.at_(row + 2, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
+ sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
+ sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ ptr1 += 3;
+ ptr2 += 3;
+ ptr3 += 3;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ // exception
+ p_integ->first() = h_sum;
+ p_integ->second() = h_sum_2;
+
+ p_integ += 1;
+ }
+ }
+
+ unsigned b_next = 2 * border_thickness;
+
+ p_sub += b_next;
+ p_integ += b_next;
+
+ for (row += scale; row < nrows; row += scale)
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ const V* ptr3 = & input.at_(row + 2, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
+ sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
+ sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ ptr1 += 3;
+ ptr2 += 3;
+ ptr3 += 3;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ p_integ->first() = h_sum + (p_integ + up)->first();
+ p_integ->second() = h_sum_2 + (p_integ + up)->second();
+
+ p_integ += 1;
+ }
+
+ p_sub += b_next;
+ p_integ += b_next;
+ }
+
+ trace::exiting("subsampling::impl::integral_3");
+ return sub;
+ }
+
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral_2(const Image<I>& input_,
+ Image<J>& integral_sum_sum_2_,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::impl::integral_2");
+
+ const unsigned scale = 2;
+
+ const I& input = exact(input_);
+ J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
+ const unsigned area = scale * scale;
+
+ typedef mln_value(I) V;
+ typedef mln_sum(V) S;
+ typedef mln_site(I) P;
+ typedef mln_value(J) V2;
+
+ mlc_bool(P::dim == 2)::check();
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ mln_concrete(I) sub(output_domain, border_thickness);
+ V* p_sub = sub.buffer();
+
+ integral_sum_sum_2.init_(output_domain, border_thickness);
+ V2* p_integ = integral_sum_sum_2.buffer();
+
+ const unsigned up = sub.delta_index(dpoint2d(-1, 0));
+
+ const unsigned nrows = 2 * output_domain.nrows();
+ const unsigned ncols = 2 * output_domain.ncols();
+
+ extension::fill(sub, 0);
+
+ unsigned b_offset = sub.delta_index(dpoint2d(border_thickness,
+ border_thickness));
+ p_sub += b_offset;
+ p_integ += b_offset;
+
+ unsigned row = 0;
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1);
+ sum += *ptr2 + *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ // exception
+ p_integ->first() = h_sum;
+ p_integ->second() = h_sum_2;
+
+ p_integ += 1;
+ }
+ }
+
+ unsigned b_next = 2 * border_thickness;
+
+ p_sub += b_next;
+ p_integ += b_next;
+
+ for (row += scale; row < nrows; row += scale)
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1);
+ sum += *ptr2 + *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ p_integ->first() = h_sum + (p_integ + up)->first();
+ p_integ->second() = h_sum_2 + (p_integ + up)->second();
+
+ p_integ += 1;
+ }
+
+ p_sub += b_next;
+ p_integ += b_next;
+ }
+
+ trace::exiting("subsampling::impl::integral_2");
+ return sub;
+ }
+
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input, unsigned scale,
+ Image<J>& integral_sum_sum_2,
+ const mln_domain(I)& output_domain, unsigned border_thickness)
+ {
+ // mln_precondition(input.nrows() % scale == 0);
+ // mln_precondition(input.ncols() % scale == 0);
+ if (scale == 3)
+ return integral_3(input, integral_sum_sum_2,
+ output_domain, border_thickness);
+ else if (scale == 2)
+ return integral_2(input, integral_sum_sum_2,
+ output_domain, border_thickness);
+ else
+ std::cerr << "NYI!" << std::endl;
+
+ typedef mln_concrete(I) output_t;
+ return output_t();
+ }
+
+
+ } // end of namespace mln::subsampling::impl
+
+
+
+
+ // Facades.
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input_, unsigned scale,
+ Image<J>& integral_sum_sum_2,
+ const mln_domain(I)& output_domain, unsigned border_thickness)
+ {
+ trace::entering("subsampling::integral");
+
+ const I& input = exact(input_);
+
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ mln_concrete(I)
+ output = impl::integral(input, scale, integral_sum_sum_2,
+ output_domain, border_thickness);
+
+ trace::exiting("subsampling::integral");
+ return output;
+ }
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input_, unsigned scale,
+ Image<J>& integral_sum_sum_2)
+ {
+ trace::entering("subsampling::integral");
+
+ const I& input = exact(input_);
+
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ box<mln_site(I)> b = make::box2d((input.nrows() + scale - 1) / scale,
+ (input.ncols() + scale - 1) / scale);
+ mln_concrete(I) output;
+ output = integral(input_, scale, integral_sum_sum_2,
+ b, mln::border::thickness);
+
+ trace::exiting("subsampling::integral");
+ return output;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::subsampling
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_SUBSAMPLING_INTEGRAL_HH
--
1.5.6.5
1
0
last-svn-commit-18-gce6a424 mln/draw/dashed_line.hh: New draw line routine.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
---
milena/ChangeLog | 4 ++
milena/mln/draw/{line.hh => dashed_line.hh} | 60 ++++++++++++++++++---------
2 files changed, 44 insertions(+), 20 deletions(-)
copy milena/mln/draw/{line.hh => dashed_line.hh} (61%)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index fae42e1..bcaf644 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ * mln/draw/dashed_line.hh: New draw line routine.
+
2010-02-24 Guillaume Lazzara <z(a)lrde.epita.fr>
Add white gaussian noise function.
diff --git a/milena/mln/draw/line.hh b/milena/mln/draw/dashed_line.hh
similarity index 61%
copy from milena/mln/draw/line.hh
copy to milena/mln/draw/dashed_line.hh
index 528dce3..6b0d244 100644
--- a/milena/mln/draw/line.hh
+++ b/milena/mln/draw/dashed_line.hh
@@ -1,5 +1,4 @@
-// Copyright (C) 2007, 2008, 2009 EPITA Research and Development
-// Laboratory (LRDE)
+// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -17,21 +16,22 @@
//
// 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
+// instantiate templates or use macros or indashed_line 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_DRAW_LINE_HH
-# define MLN_DRAW_LINE_HH
+#ifndef MLN_DRAW_DASHED_LINE_HH
+# define MLN_DRAW_DASHED_LINE_HH
/// \file
///
-/// Draw a line in an image.
-/// \fixme Add specializations for horizontal lines (use pointers/memset).
+/// Draw a dashed_line in an image.
+/// \fixme Add specializations for horizontal dashed_lines (use pointers/memset).
+# include <mln/core/concept/function.hh>
# include <mln/core/concept/image.hh>
# include <mln/core/site_set/p_line2d.hh>
# include <mln/core/image/imorph/safe.hh>
@@ -46,12 +46,12 @@ namespace mln
namespace draw
{
- /*! Draw a line at level \p v in image \p ima between the points
- * \p beg and \p end.
+ /*! Draw a dashed line at level \p v in image \p ima between the
+ * points \p beg and \p end.
*
* \param[in,out] ima The image to be drawn.
- * \param[in] beg The start point to drawn line.
- * \param[in] end The end point to drawn line.
+ * \param[in] beg The start point to drawn dashed_line.
+ * \param[in] end The end point to drawn dashed_line.
* \param[in] v The value to assign to all drawn pixels.
*
* \pre \p ima has to be initialized.
@@ -60,27 +60,47 @@ namespace mln
*
*/
template <typename I>
- void line(Image<I>& ima,
- const mln_psite(I)& beg, const mln_psite(I)& end,
- const mln_value(I)& v);
+ void dashed_line(Image<I>& ima,
+ const mln_psite(I)& beg, const mln_psite(I)& end,
+ const mln_value(I)& v);
# ifndef MLN_INCLUDE_ONLY
+ namespace internal
+ {
+
+ template <typename I, unsigned dim>
+ struct dashed_line_f : Function_v2b<dashed_line_f<I, dim> >
+ {
+ typedef bool result;
+
+ bool operator()(const mln_site(I)& p) const
+ {
+ return p[dim] % 2;
+ }
+
+ };
+
+ } // end of namespace mln::draw::internal
+
+
+
template <typename I>
inline
- void line(Image<I>& ima_,
- const mln_psite(I)& beg, const mln_psite(I)& end,
- const mln_value(I)& v)
+ void dashed_line(Image<I>& ima_,
+ const mln_psite(I)& beg, const mln_psite(I)& end,
+ const mln_value(I)& v)
{
I& ima = exact(ima_);
mln_precondition(ima.is_valid());
+
if (! ima.has(beg))
trace::warning("Begin site is not part of the given image.");
if (! ima.has(end))
trace::warning("End site is not part of the given image.");
- data::paste(pw::cst(v) | p_line2d(beg, end),
- safe(ima).rw());
+
+ data::fill(((ima | p_line2d(beg, end)).rw() | internal::dashed_line_f<I, 1>()).rw(), v);
}
# endif // ! MLN_INCLUDE_ONLY
@@ -90,4 +110,4 @@ namespace mln
} // end of namespace mln
-#endif // ! MLN_DRAW_LINE_HH
+#endif // ! MLN_DRAW_DASHED_LINE_HH
--
1.5.6.5
1
0
11 Mar '10
* preprocessing/denoise.hh,
* preprocessing/denoise_bg.hh,
* preprocessing/denoise_fg.hh,
* preprocessing/homogeneous_contrast.hh,
* preprocessing/rotate_90.hh: New routines.
* fun/v2b/label_to_bool.hh: New function.
* tests/preprocessing/Makefile.am,
* tests/preprocessing/rotate_90.cc: New test.
---
scribo/ChangeLog | 15 ++
.../n2v/all.hh => scribo/fun/v2b/label_to_bool.hh | 43 ++++--
scribo/preprocessing/denoise.hh | 106 +++++++++++++
scribo/preprocessing/denoise_bg.hh | 156 +++++++++++++++++++
scribo/preprocessing/denoise_fg.hh | 131 ++++++++++++++++
scribo/preprocessing/homogeneous_contrast.hh | 119 +++++++++++++++
scribo/preprocessing/rotate_90.hh | 159 ++++++++++++++++++++
scribo/tests/preprocessing/Makefile.am | 4 +-
.../preprocessing/{unskew.cc => rotate_90.cc} | 29 +++-
9 files changed, 740 insertions(+), 22 deletions(-)
copy milena/mln/fun/n2v/all.hh => scribo/fun/v2b/label_to_bool.hh (66%)
create mode 100644 scribo/preprocessing/denoise.hh
create mode 100644 scribo/preprocessing/denoise_bg.hh
create mode 100644 scribo/preprocessing/denoise_fg.hh
create mode 100644 scribo/preprocessing/homogeneous_contrast.hh
create mode 100644 scribo/preprocessing/rotate_90.hh
copy scribo/tests/preprocessing/{unskew.cc => rotate_90.cc} (72%)
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 74cef68..8b4c4d7 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,20 @@
2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add new preprocessing routines.
+
+ * preprocessing/denoise.hh,
+ * preprocessing/denoise_bg.hh,
+ * preprocessing/denoise_fg.hh,
+ * preprocessing/homogeneous_contrast.hh,
+ * preprocessing/rotate_90.hh: New routines.
+
+ * fun/v2b/label_to_bool.hh: New function.
+
+ * tests/preprocessing/Makefile.am,
+ * tests/preprocessing/rotate_90.cc: New test.
+
+2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Update code according to last changes in Scribo core classes.
* draw/bounding_box_links.hh,
diff --git a/milena/mln/fun/n2v/all.hh b/scribo/fun/v2b/label_to_bool.hh
similarity index 66%
copy from milena/mln/fun/n2v/all.hh
copy to scribo/fun/v2b/label_to_bool.hh
index 0e0e55c..3d65401 100644
--- a/milena/mln/fun/n2v/all.hh
+++ b/scribo/fun/v2b/label_to_bool.hh
@@ -23,29 +23,44 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef MLN_FUN_N2V_ALL_HH
-# define MLN_FUN_N2V_ALL_HH
+#ifndef SCRIBO_FUN_V2B_LABEL_TO_BOOL_HH
+# define SCRIBO_FUN_V2B_LABEL_TO_BOOL_HH
-/// \file
-///
-/// File that includes all functions from nil to value.
+# include <mln/core/concept/function.hh>
+# include <mln/fun/i2v/array.hh>
-namespace mln
+namespace scribo
{
namespace fun
{
- /// \brief Namespace of functions from nil to value.
- ///
- /// \ingroup modfun
- namespace n2v {}
+ namespace v2b
+ {
- }
-}
+ template <typename L>
+ struct label_to_bool : mln::Function_v2b<label_to_bool<L> >
+ {
+ typedef bool result;
+ label_to_bool(const mln::fun::i2v::array<bool>& f)
+ : f_(f)
+ {}
-# include <mln/fun/n2v/white_gaussian.hh>
+ bool operator()(const L& v) const
+ {
+ return f_(v);
+ }
-#endif // ! MLN_FUN_N2V_ALL_HH
+ mln::fun::i2v::array<bool> f_;
+ };
+
+ } // end of namespace scribo::fun::v2b
+
+ } // end of namespace scribo::fun
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_FUN_V2B_LABEL_TO_BOOL_HH
diff --git a/scribo/preprocessing/denoise.hh b/scribo/preprocessing/denoise.hh
new file mode 100644
index 0000000..1877c06
--- /dev/null
+++ b/scribo/preprocessing/denoise.hh
@@ -0,0 +1,106 @@
+// Copyright (C) 2010 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_DENOISE_HH
+# define SCRIBO_PREPROCESSING_DENOISE_HH
+
+/// \file
+///
+/// Denoise an image.
+
+# include <scribo/preprocessing/denoise_fg.hh>
+# include <scribo/preprocessing/denoise_bg.hh>
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ using namespace mln;
+
+ /// \brief Denoise an image.
+ /// Denoising is performed both on the foreground and the background.
+ ///
+ /// \param[in] input A binary image. True for objects, False for
+ /// background.
+ /// \param[in] nbh Neighborhood to use for denoising.
+ /// \param[in] min_card Minimum component cardinality to not be
+ /// considered as noise.
+ ///
+ /// \output A binary image with the same domain as \p input. All
+ /// small components have been removed.
+ //
+ template <typename I, typename N>
+ mln_concrete(I)
+ denoise(const Image<I>& input, const Neighborhood<N>& nbh,
+ unsigned fg_min_card, unsigned bg_min_card);
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ denoise(const Image<I>& input_, const Neighborhood<N>& nbh_,
+ unsigned fg_min_card, unsigned bg_min_card)
+ {
+ trace::entering("scribo::preprocessing::denoise");
+
+ const I& input = exact(input_);
+ const N& nbh = exact(nbh_);
+ mlc_equal(mln_value(I), bool)::check();
+ mln_precondition(input.is_valid());
+
+ mln_concrete(I) output;
+
+ output = denoise_fg(input, nbh, fg_min_card);
+ output = denoise_bg(output, nbh, bg_min_card);
+
+ trace::exiting("scribo::preprocessing::denoise");
+ return output;
+ }
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+# endif // SCRIBO_PREPROCESSING_DENOISE_HH
diff --git a/scribo/preprocessing/denoise_bg.hh b/scribo/preprocessing/denoise_bg.hh
new file mode 100644
index 0000000..9242f36
--- /dev/null
+++ b/scribo/preprocessing/denoise_bg.hh
@@ -0,0 +1,156 @@
+// Copyright (C) 2010 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_DENOISE_BG_HH
+# define SCRIBO_PREPROCESSING_DENOISE_BG_HH
+
+/// \file
+///
+/// Denoise image background.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+# include <mln/core/concept/function.hh>
+# include <mln/data/transform.hh>
+
+# include <mln/accu/math/count.hh>
+
+# include <mln/core/alias/neighb2d.hh>
+
+# include <mln/util/array.hh>
+# include <mln/fun/i2v/array.hh>
+
+# include <mln/labeling/compute.hh>
+# include <mln/labeling/background.hh>
+
+# include <scribo/fun/v2b/label_to_bool.hh>
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ using namespace mln;
+
+ /// Denoise image background.
+ ///
+ /// \param[in] input A binary image. True for objects, False for
+ /// background.
+ /// \param[in] nbh Neighborhood to use for denoising.
+ /// \param[in] min_card Minimum component cardinality to not be
+ /// considered as noise.
+ ///
+ /// \output A binary image with the same domain as \p input. All
+ /// small components have been removed and merged with the
+ /// background.
+ //
+ template <typename I, typename N>
+ mln_concrete(I)
+ denoise_bg(const Image<I>& input, const Neighborhood<N>& nbh,
+ unsigned min_card);
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+namespace mln
+{
+
+
+ template <typename L>
+ struct label_to_bool : Function_v2b<label_to_bool<L> >
+ {
+ typedef bool result;
+
+ label_to_bool(const fun::i2v::array<bool>& f)
+ : f_(f)
+ {}
+
+ bool operator()(const L& v) const
+ {
+ return f_(v);
+ }
+
+ fun::i2v::array<bool> f_;
+ };
+
+
+} // end of namespace mln
+
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ denoise_bg(const Image<I>& input_, const Neighborhood<N>& nbh_,
+ unsigned min_card)
+ {
+ trace::entering("scribo::preprocessing::denoise_bg");
+
+ const I& input = exact(input_);
+ const N& nbh = exact(nbh_);
+ mlc_equal(mln_value(I), bool)::check();
+ mln_precondition(input.is_valid());
+
+ unsigned nlabels;
+ image2d<unsigned> lbl = labeling::background(input, nbh, nlabels);
+
+ util::array<unsigned>
+ result = labeling::compute(accu::meta::math::count(), lbl, nlabels);
+
+ mln::fun::i2v::array<bool> f(nlabels + 1, true);
+ f(0) = false;
+ for (unsigned i = 1; i <= nlabels; ++i)
+ if (result(i) < min_card)
+ f(i) = false;
+
+ scribo::fun::v2b::label_to_bool<unsigned> relabel_f(f);
+ mln_concrete(I)
+ output = data::transform(lbl, relabel_f);
+
+ trace::exiting("scribo::preprocessing::denoise_bg");
+ return output;
+ }
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+# endif // SCRIBO_PREPROCESSING_DENOISE_BG_HH
diff --git a/scribo/preprocessing/denoise_fg.hh b/scribo/preprocessing/denoise_fg.hh
new file mode 100644
index 0000000..5909164
--- /dev/null
+++ b/scribo/preprocessing/denoise_fg.hh
@@ -0,0 +1,131 @@
+// Copyright (C) 2010 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_DENOISE_FG_HH
+# define SCRIBO_PREPROCESSING_DENOISE_FG_HH
+
+/// \file
+///
+/// Denoise image foreground.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+# include <mln/core/concept/function.hh>
+# include <mln/data/transform.hh>
+
+# include <mln/accu/math/count.hh>
+
+# include <mln/core/alias/neighb2d.hh>
+
+# include <mln/util/array.hh>
+# include <mln/fun/i2v/array.hh>
+
+# include <mln/labeling/foreground.hh>
+# include <mln/labeling/compute.hh>
+
+# include <scribo/fun/v2b/label_to_bool.hh>
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ using namespace mln;
+
+ /// Denoise image foreground.
+ ///
+ /// \param[in] input A binary image. True for objects, False for
+ /// background.
+ /// \param[in] nbh Neighborhood to use for denoising.
+ /// \param[in] min_card Minimum component cardinality to not be
+ /// considered as noise.
+ ///
+ /// \output A binary image with the same domain as \p input. All
+ /// small components have been removed and merged with the
+ /// background.
+ //
+ template <typename I, typename N>
+ mln_concrete(I)
+ denoise_fg(const Image<I>& input, const Neighborhood<N>& nbh,
+ unsigned min_card);
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ template <typename I, typename N>
+ mln_concrete(I)
+ denoise_fg(const Image<I>& input_, const Neighborhood<N>& nbh_,
+ unsigned min_card)
+ {
+ trace::entering("scribo::preprocessing::denoise_fg");
+
+ const I& input = exact(input_);
+ const N& nbh = exact(nbh_);
+ mlc_equal(mln_value(I), bool)::check();
+ mln_precondition(input.is_valid());
+
+ // FIXME:
+ unsigned nlabels;
+ image2d<unsigned> lbl = labeling::foreground(input, nbh, nlabels);
+
+ mln::util::array<unsigned>
+ result = labeling::compute(accu::meta::math::count(), lbl, nlabels);
+
+ mln::fun::i2v::array<bool> f(nlabels + 1, true);
+ f(0) = false;
+ for (unsigned i = 1; i <= nlabels; ++i)
+ if (result(i) < min_card)
+ f(i) = false;
+
+ scribo::fun::v2b::label_to_bool<unsigned> relabel_f(f);
+ mln_concrete(I)
+ output = data::transform(lbl, relabel_f);
+
+ trace::exiting("scribo::preprocessing::denoise_fg");
+ return output;
+ }
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+# endif // SCRIBO_PREPROCESSING_DENOISE_FG_HH
diff --git a/scribo/preprocessing/homogeneous_contrast.hh b/scribo/preprocessing/homogeneous_contrast.hh
new file mode 100644
index 0000000..82b2ac6
--- /dev/null
+++ b/scribo/preprocessing/homogeneous_contrast.hh
@@ -0,0 +1,119 @@
+// Copyright (C) 2010 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_HOMOGENEOUS_CONTRAST_HH
+# define SCRIBO_PREPROCESSING_HOMOGENEOUS_CONTRAST_HH
+
+/// \file
+///
+/// Improve contrast homogeneity in an image.
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/morpho/attribute/height.hh>
+#include <mln/morpho/closing/leveling.hh>
+
+#include <mln/arith/minus.hh>
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+
+ using namespace mln;
+
+ /// \brief Improve contrast homogeneity in an image.
+ ///
+ /// \param[in] input A gray-level image.
+ /// \param[in] h Height attribute value for leveling closing.
+ ///
+ /// \output A gray-level image with better contrast homogeneity.
+ //
+ template <typename I>
+ mln_concrete(I)
+ homogeneous_contrast(const Image<I>& input, unsigned h);
+
+
+ /// \overload.
+ /// Default height attribute value is set to 75.
+ //
+ template <typename I>
+ mln_concrete(I)
+ homogeneous_contrast(const Image<I>& input);
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+
+ template <typename I>
+ mln_concrete(I)
+ homogeneous_contrast(const Image<I>& input_, unsigned h)
+ {
+ trace::entering("scribo::preprocessing::homogeneous_contrast");
+
+ const I& input = exact(input_);
+ mln_precondition(input.is_valid());
+
+ morpho::attribute::height<I> acc;
+ mln_concrete(I)
+ output = morpho::closing::leveling(input, c4(), acc, h);
+ arith::minus_inplace(output, input);
+
+ trace::exiting("scribo::preprocessing::homogeneous_contrast");
+ return output;
+ }
+
+
+ template <typename I>
+ mln_concrete(I)
+ homogeneous_contrast(const Image<I>& input)
+ {
+ return homogeneous_contrast(input, 75);
+ }
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+# endif // SCRIBO_PREPROCESSING_HOMOGENEOUS_CONTRAST_HH
diff --git a/scribo/preprocessing/rotate_90.hh b/scribo/preprocessing/rotate_90.hh
new file mode 100644
index 0000000..616c341
--- /dev/null
+++ b/scribo/preprocessing/rotate_90.hh
@@ -0,0 +1,159 @@
+// Copyright (C) 2010 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_ROTATE_90_HH
+# define SCRIBO_PREPROCESSING_ROTATE_90_HH
+
+/// \file
+///
+/// Fast +90/-90 degrees rotation.
+
+
+# include <mln/core/concept/image.hh>
+# include <mln/geom/all.hh>
+
+// FIXME: not generic
+# include <mln/core/alias/dpoint2d.hh>
+
+
+namespace scribo
+{
+
+ namespace preprocessing
+ {
+
+ using namespace mln;
+
+ /*! \brief Perform a +90/-90 degrees rotation.
+
+ \param[in] input An image.
+ \param[in] positive If set to true, performs a +90° rotation,
+ -90° otherwise.
+
+ \return A rotated image.
+ */
+ template <typename I>
+ mln_concrete(I)
+ rotate_90(const Image<I>& input, bool positive);
+
+ /// \brief Performs a +90° rotation.
+ /// \overload
+ //
+ template <typename I>
+ mln_concrete(I)
+ rotate_90(const Image<I>& input);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ template <typename I>
+ mln_concrete(I)
+ rotate_90(const Image<I>& input_, bool positive)
+ {
+ trace::entering("scribo::preprocessing::rotate_90");
+
+ const I& input = exact(input_);
+ mln_precondition(input.is_valid());
+
+ // Works only on one block images.
+ mlc_is(mln_trait_image_value_access(I),
+ mln::trait::image::value_access::direct)::check();
+ mlc_is(mln_trait_image_value_storage(I),
+ mln::trait::image::value_storage::one_block)::check();
+
+ // Initialize output domain and make sure the border size is the
+ // same as input's.
+ mln_site(I) pmax(input.domain().pmax().col(),
+ input.domain().pmax().row());
+ mln_concrete(I) output(mln_domain(I)(input.domain().pmin(), pmax),
+ input.border());
+
+ const mln_value(I)* in_ptr = input.buffer();
+
+ unsigned in_ncols = geom::ncols(input) + 2 * input.border();
+ unsigned in_nrows = geom::nrows(input) + 2 * input.border();
+
+ unsigned out_ncols = geom::ncols(output);
+ unsigned out_nrows = geom::nrows(output);
+
+ unsigned
+ out_next_offset,
+ out_next_p_offset;
+
+ mln_value(I)* out_ptr = output.buffer();
+
+ if (positive) // +90 deg
+ {
+ dpoint2d dp(- out_nrows - 2 * output.border(), - 1);
+ out_next_offset = output.delta_index(dp);
+
+ out_next_p_offset = output.delta_index(dpoint2d(+1, 0));
+
+ out_ptr += 2 * output.border() + out_ncols - 1;
+ }
+ else // -90 deg
+ {
+ dpoint2d dp(out_nrows + 2 * output.border(), + 1);
+ out_next_offset = output.delta_index(dp);
+
+ out_next_p_offset = output.delta_index(dpoint2d(-1, 0));
+
+ out_ptr += output.delta_index(dpoint2d(out_nrows + 2 * output.border() - 1, 0));
+ }
+
+ for (unsigned row = 0; row < in_nrows; ++row)
+ {
+ for (unsigned col = 0; col < in_ncols;
+ ++col, ++in_ptr, out_ptr += out_next_p_offset)
+ *out_ptr = *in_ptr;
+
+ out_ptr += out_next_offset;
+ }
+
+ trace::exiting("scribo::preprocessing::rotate_90");
+ return output;
+ }
+
+
+ template <typename I>
+ mln_concrete(I)
+ rotate_90(const Image<I>& input)
+ {
+ return rotate_90(input, true);
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+ } // end of namespace scribo::preprocessing
+
+} // end of namespace mln
+
+
+# endif // SCRIBO_PREPROCESSING_ROTATE_90_HH
+
diff --git a/scribo/tests/preprocessing/Makefile.am b/scribo/tests/preprocessing/Makefile.am
index 4666033..4a6790c 100644
--- a/scribo/tests/preprocessing/Makefile.am
+++ b/scribo/tests/preprocessing/Makefile.am
@@ -20,8 +20,10 @@
include $(top_srcdir)/scribo/tests/tests.mk
check_PROGRAMS = \
- unskew
+ unskew \
+ rotate_90
unskew_SOURCES = unskew.cc
+rotate_90_SOURCES = rotate_90.cc
TESTS = $(check_PROGRAMS)
diff --git a/scribo/tests/preprocessing/unskew.cc b/scribo/tests/preprocessing/rotate_90.cc
similarity index 72%
copy from scribo/tests/preprocessing/unskew.cc
copy to scribo/tests/preprocessing/rotate_90.cc
index 9c84951..0c2286a 100644
--- a/scribo/tests/preprocessing/unskew.cc
+++ b/scribo/tests/preprocessing/rotate_90.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -24,21 +24,36 @@
// executable file might be covered by the GNU General Public License.
#include <mln/core/image/image2d.hh>
+#include <mln/data/compare.hh>
#include <mln/io/pbm/load.hh>
-#include <mln/io/pbm/save.hh>
-
-#include <scribo/preprocessing/unskew.hh>
+#include <mln/make/image.hh>
+#include <scribo/preprocessing/rotate_90.hh>
#include <scribo/tests/data.hh>
+
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
using namespace mln;
- image2d<bool> ima;
- io::pbm::load(ima, SCRIBO_IMG_DIR "/text_to_group.pbm");
- io::pbm::save(scribo::preprocessing::unskew(ima).first(), "unskew.pbm");
+ bool data[][1] = { { 0 },
+ { 1 },
+ { 0 },
+ { 1 },
+ { 0 },
+ { 1 },
+ { 0 },
+ { 1 },
+ { 0 },
+ { 1 } };
+
+ image2d<bool> ima = make::image(data);
+
+ image2d<bool> tmp = scribo::preprocessing::rotate_90(ima, true);
+ tmp = scribo::preprocessing::rotate_90(tmp, false);
+
+ mln_assertion(tmp == ima);
}
--
1.5.6.5
1
0