* src/preprocessing/Makefile.am,
* src/filter/Makefile.am,
* src/debug/Makefile.am: Add new targets.
* src/debug/show_info_median_inter_characters.cc,
* src/debug/show_info_x_height.cc,
* src/debug/show_links_single_down.cc,
* src/debug/show_links_single_down_left_aligned.cc,
* src/debug/show_links_single_down_right_aligned.cc,
* src/debug/show_links_single_up.cc,
* src/debug/show_links_single_up_left_aligned.cc,
* src/debug/show_links_single_up_right_aligned.cc,
* src/filter/objects_with_holes.cc,
* src/filter/objects_with_holes_pgm.cc,
* src/pbm_lines_in_doc.cc,
* src/preprocessing/split_bg_fg_ms.cc,
* src/text_in_article.cc,
* src/text_in_photo_ppm_fast.cc: New tools.
* src/debug/show_left_right_links_validation.cc: Rename as...
* src/debug/show_links_left_right_links_validation.cc: ...this.
* src/debug/show_links_bbox_h_ratio.cc: Add a missing argument.
---
scribo/ChangeLog | 28 +
scribo/src/debug/Makefile.am | 20 +-
.../src/debug/show_info_median_inter_characters.cc | 256 ++++++++
scribo/src/debug/show_info_x_height.cc | 281 +++++++++
.../src/debug/show_left_right_links_validation.cc | 122 ----
scribo/src/debug/show_links_bbox_h_ratio.cc | 2 +-
.../show_links_left_right_links_validation.cc | 122 ++++
scribo/src/debug/show_links_single_down.cc | 157 +++++
.../debug/show_links_single_down_left_aligned.cc | 99 +++
.../debug/show_links_single_down_right_aligned.cc | 99 +++
scribo/src/debug/show_links_single_up.cc | 157 +++++
.../src/debug/show_links_single_up_left_aligned.cc | 100 +++
.../debug/show_links_single_up_right_aligned.cc | 98 +++
scribo/src/filter/Makefile.am | 6 +-
scribo/src/filter/objects_with_holes.cc | 70 ++
scribo/src/filter/objects_with_holes_pgm.cc | 71 +++
scribo/src/pbm_lines_in_doc.cc | 299 +++++++++
scribo/src/preprocessing/Makefile.am | 2 +
scribo/src/preprocessing/split_bg_fg_ms.cc | 109 ++++
scribo/src/text_in_article.cc | 327 ++++++++++
scribo/src/text_in_photo_ppm_fast.cc | 658 ++++++++++++++++++++
21 files changed, 2957 insertions(+), 126 deletions(-)
create mode 100644 scribo/src/debug/show_info_median_inter_characters.cc
create mode 100644 scribo/src/debug/show_info_x_height.cc
delete mode 100644 scribo/src/debug/show_left_right_links_validation.cc
create mode 100644 scribo/src/debug/show_links_left_right_links_validation.cc
create mode 100644 scribo/src/debug/show_links_single_down.cc
create mode 100644 scribo/src/debug/show_links_single_down_left_aligned.cc
create mode 100644 scribo/src/debug/show_links_single_down_right_aligned.cc
create mode 100644 scribo/src/debug/show_links_single_up.cc
create mode 100644 scribo/src/debug/show_links_single_up_left_aligned.cc
create mode 100644 scribo/src/debug/show_links_single_up_right_aligned.cc
create mode 100644 scribo/src/filter/objects_with_holes.cc
create mode 100644 scribo/src/filter/objects_with_holes_pgm.cc
create mode 100644 scribo/src/pbm_lines_in_doc.cc
create mode 100644 scribo/src/preprocessing/split_bg_fg_ms.cc
create mode 100644 scribo/src/text_in_article.cc
create mode 100644 scribo/src/text_in_photo_ppm_fast.cc
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index ee8e03c..f7a0e58 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,33 @@
2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add new tools in Scribo.
+
+ * src/preprocessing/Makefile.am,
+ * src/filter/Makefile.am,
+ * src/debug/Makefile.am: Add new targets.
+
+ * src/debug/show_info_median_inter_characters.cc,
+ * src/debug/show_info_x_height.cc,
+ * src/debug/show_links_single_down.cc,
+ * src/debug/show_links_single_down_left_aligned.cc,
+ * src/debug/show_links_single_down_right_aligned.cc,
+ * src/debug/show_links_single_up.cc,
+ * src/debug/show_links_single_up_left_aligned.cc,
+ * src/debug/show_links_single_up_right_aligned.cc,
+ * src/filter/objects_with_holes.cc,
+ * src/filter/objects_with_holes_pgm.cc,
+ * src/pbm_lines_in_doc.cc,
+ * src/preprocessing/split_bg_fg_ms.cc,
+ * src/text_in_article.cc,
+ * src/text_in_photo_ppm_fast.cc: New tools.
+
+ * src/debug/show_left_right_links_validation.cc: Rename as...
+ * src/debug/show_links_left_right_links_validation.cc: ...this.
+
+ * src/debug/show_links_bbox_h_ratio.cc: Add a missing argument.
+
+2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
+
* scribo/src/text_in_photo_ppm.cc: Improve output.
2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
diff --git a/scribo/src/debug/Makefile.am b/scribo/src/debug/Makefile.am
index 28839dc..bb55bfb 100644
--- a/scribo/src/debug/Makefile.am
+++ b/scribo/src/debug/Makefile.am
@@ -20,7 +20,9 @@
include $(top_srcdir)/scribo/scribo.mk
bin_PROGRAMS = \
- show_left_right_links_validation \
+ show_info_x_height \
+ show_info_median_inter_characters \
+ show_links_left_right_links_validation \
show_links_bbox_h_ratio \
show_links_bbox_overlap \
show_links_bottom_aligned \
@@ -28,10 +30,16 @@ bin_PROGRAMS = \
show_links_non_h_aligned \
show_links_several_right \
show_links_several_right_overlap \
+ show_links_single_down \
+ show_links_single_down_left_aligned \
+ show_links_single_down_right_aligned \
show_links_single_left \
show_links_single_left_dmax_ratio \
show_links_single_right \
show_links_single_right_dmax_ratio \
+ show_links_single_up \
+ show_links_single_up_left_aligned \
+ show_links_single_up_right_aligned \
show_links_top_aligned \
show_objects_large \
show_objects_large_small \
@@ -40,7 +48,9 @@ bin_PROGRAMS = \
show_objects_thin
-show_left_right_links_validation_SOURCES = show_left_right_links_validation.cc
+show_info_x_height_SOURCES = show_info_x_height.cc
+show_info_median_inter_characters_SOURCES = show_info_median_inter_characters.cc
+show_links_left_right_links_validation_SOURCES = show_links_left_right_links_validation.cc
show_links_bbox_h_ratio_SOURCES = show_links_bbox_h_ratio.cc
show_links_bbox_overlap_SOURCES = show_links_bbox_overlap.cc
show_links_bottom_aligned_SOURCES = show_links_bottom_aligned.cc
@@ -48,10 +58,16 @@ show_links_center_aligned_SOURCES = show_links_center_aligned.cc
show_links_non_h_aligned_SOURCES = show_links_non_h_aligned.cc
show_links_several_right_SOURCES = show_links_several_right.cc
show_links_several_right_overlap_SOURCES = show_links_several_right_overlap.cc
+show_links_single_down_SOURCES = show_links_single_down.cc
+show_links_single_down_left_aligned_SOURCES = show_links_single_down_left_aligned.cc
+show_links_single_down_right_aligned_SOURCES = show_links_single_down_right_aligned.cc
show_links_single_left_SOURCES = show_links_single_left.cc
show_links_single_left_dmax_ratio_SOURCES = show_links_single_left_dmax_ratio.cc
show_links_single_right_SOURCES = show_links_single_right.cc
show_links_single_right_dmax_ratio_SOURCES = show_links_single_right_dmax_ratio.cc
+show_links_single_up_SOURCES = show_links_single_up.cc
+show_links_single_up_left_aligned_SOURCES = show_links_single_up_left_aligned.cc
+show_links_single_up_right_aligned_SOURCES = show_links_single_up_right_aligned.cc
show_links_top_aligned_SOURCES = show_links_top_aligned.cc
show_objects_large_SOURCES = show_objects_large.cc
show_objects_large_small_SOURCES = show_objects_large_small.cc
diff --git a/scribo/src/debug/show_info_median_inter_characters.cc b/scribo/src/debug/show_info_median_inter_characters.cc
new file mode 100644
index 0000000..e28ad96
--- /dev/null
+++ b/scribo/src/debug/show_info_median_inter_characters.cc
@@ -0,0 +1,256 @@
+// 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/alias/neighb2d.hh>
+
+#include <mln/io/pbm/all.hh>
+#include <mln/io/ppm/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/extension/adjust.hh>
+
+#include <mln/accu/stat/median_h.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/primitive/extract/objects.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/debug/usage.hh>
+
+#include <scribo/make/debug_filename.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. 'False' for objects, 'True'\
+for the background." },
+ { "debug_dir", "Output directory for debug image" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc != 3 && argc != 4)
+ return scribo::debug::usage(argv,
+ "Find text lines using left/right validation and display x-height in a binarized article.",
+ "input.pbm out.ppm <debug_dir>",
+ args_desc,
+ "A color image. The following colors are used : dark blue for object bboxes, orange for single object bboxes, purple for group bboxes and light blue for x-height.");
+
+ if (argc == 4)
+ scribo::make::internal::debug_filename_prefix = argv[3];
+
+ trace::entering("main");
+
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+ typedef image2d<value::label_16> L;
+
+ /// Finding objects.
+ std::cout << "Finding objects..." << std::endl;
+ value::label_16 nobjects;
+ object_image(L)
+ objects = scribo::primitive::extract::objects(input,
+ c8(),
+ nobjects);
+
+
+ /// First filtering.
+ std::cout << "Filtering objects..." << std::endl;
+ object_image(L) filtered_objects
+ = scribo::filter::objects_small(objects, 6);
+
+ /// Linking potential objects
+ std::cout << "Linking objects..." << std::endl;
+ object_links<L> left_link
+ = primitive::link::with_single_left_link_dmax_ratio(filtered_objects);
+ object_links<L> right_link
+ = primitive::link::with_single_right_link_dmax_ratio(filtered_objects);
+
+
+ // Validating left and right links.
+ object_links<L>
+ merged_links = primitive::link::merge_double_link(filtered_objects,
+ 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);
+
+ util::array<bool> drawn(objects.nlabels(), 0);
+ for_all_components(i, objects.bboxes())
+ if (merged_links[i] == i && ! drawn(i))
+ {
+ mln::draw::box(output, objects.bbox(i), literal::orange);
+ drawn[i] = true;
+ }
+ else
+ {
+ mln::draw::box(output, objects.bbox(i), literal::blue);
+ mln::draw::box(output, objects.bbox(merged_links[i]), literal::blue);
+ drawn[i] = true;
+ drawn[merged_links[i]] = true;
+ }
+
+ 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_objects,
+ merged_links,
+ 2.0f);
+
+
+
+
+#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
+
+
+
+ // FIXME: from_single_link should return a packed object_groups?
+ //
+ //######
+ object_groups<L>
+ groups = primitive::group::from_single_link(filtered_objects,
+ hratio_filtered_links);
+ value::label_16 n_groups;
+ mln::fun::i2v::array<value::label_16>
+ groups_packed = mln::make::relabelfun(groups,
+ filtered_objects.nlabels(),
+ n_groups);
+ //######
+
+
+ std::cout << "Grouping objects..." << std::endl;
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects, groups);
+
+
+
+ // Display median character space.
+ {
+ image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+ typedef mln::value::int_u<8> median_t;
+ typedef mln::accu::stat::median_h<median_t> accu_t;
+ util::array<accu_t>
+ lspace_med(static_cast<unsigned>(grouped_objects.nlabels()) + 1);
+
+ for_all_components(i, filtered_objects.bboxes())
+ if (groups_packed(i) != 0)
+ {
+ if (hratio_filtered_links(i) != i)
+ {
+ unsigned
+ space = filtered_objects.bbox(i).pmin().col() - filtered_objects.bbox(hratio_filtered_links(i)).pmax().col();
+
+ lspace_med(groups_packed(i)).take(space);
+
+ }
+ }
+
+ for_all_components(i, filtered_objects.bboxes())
+ if (groups_packed(i) != 0 && lspace_med(groups_packed(i)).card() > 1)
+ {
+ unsigned med = lspace_med(groups_packed(i)).to_result();
+
+ mln::draw::box(output, grouped_objects.bbox(groups_packed(i)),
+ literal::purple);
+
+ point2d
+ beg = filtered_objects.bbox(i).pmax(),
+ end = beg;
+ beg.row() = filtered_objects.bbox(i).pmin().row();
+ mln::draw::line(output, beg, end, literal::cyan);
+ beg.col() += med;
+ end.col() += med;
+ mln::draw::line(output, beg, end, literal::cyan);
+
+ }
+
+ io::ppm::save(output, argv[2]);
+
+ }
+
+ trace::exiting("main");
+}
diff --git a/scribo/src/debug/show_info_x_height.cc b/scribo/src/debug/show_info_x_height.cc
new file mode 100644
index 0000000..e0c84a6
--- /dev/null
+++ b/scribo/src/debug/show_info_x_height.cc
@@ -0,0 +1,281 @@
+// 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/alias/neighb2d.hh>
+
+#include <mln/io/pbm/all.hh>
+#include <mln/io/ppm/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/extension/adjust.hh>
+
+#include <mln/accu/stat/median_h.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/primitive/extract/objects.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/debug/usage.hh>
+
+#include <scribo/make/debug_filename.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. 'False' for objects, 'True'\
+for the background." },
+ { "debug_dir", "Output directory for debug image" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc != 3 && argc != 4)
+ return scribo::debug::usage(argv,
+ "Find text lines using left/right validation and display x-height in a binarized article.",
+ "input.pbm out.ppm <debug_dir>",
+ args_desc,
+ "A color image. The following colors are used : dark blue for object bboxes, orange for single object bboxes, purple for group bboxes and light blue for x-height.");
+
+ if (argc == 4)
+ scribo::make::internal::debug_filename_prefix = argv[3];
+
+ trace::entering("main");
+
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+ typedef image2d<value::label_16> L;
+
+ /// Finding objects.
+ std::cout << "Finding objects..." << std::endl;
+ value::label_16 nobjects;
+ object_image(L)
+ objects = scribo::primitive::extract::objects(input,
+ c8(),
+ nobjects);
+
+
+ /// First filtering.
+ std::cout << "Filtering objects..." << std::endl;
+ object_image(L) filtered_objects
+ = scribo::filter::objects_small(objects, 6);
+
+ /// Linking potential objects
+ std::cout << "Linking objects..." << std::endl;
+ object_links<L> left_link
+ = primitive::link::with_single_left_link_dmax_ratio(filtered_objects);
+ object_links<L> right_link
+ = primitive::link::with_single_right_link_dmax_ratio(filtered_objects);
+
+
+ // Validating left and right links.
+ object_links<L>
+ merged_links = primitive::link::merge_double_link(filtered_objects,
+ left_link,
+ right_link);
+
+#ifndef NOUT
+ if (argc == 4)
+ {
+ image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+ scribo::draw::bounding_box_links(output,
+ filtered_objects.bboxes(),
+ merged_links,
+ literal::green);
+
+ util::array<bool> drawn(objects.nlabels(), 0);
+ for_all_components(i, objects.bboxes())
+ if (merged_links[i] == i && ! drawn(i))
+ {
+ mln::draw::box(output, objects.bbox(i), literal::orange);
+ drawn[i] = true;
+ }
+ else
+ {
+ mln::draw::box(output, objects.bbox(i), literal::blue);
+ mln::draw::box(output, objects.bbox(merged_links[i]), literal::blue);
+ drawn[i] = true;
+ drawn[merged_links[i]] = true;
+ }
+
+ 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_objects,
+ merged_links,
+ 2.0f);
+
+
+
+
+#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
+
+
+
+ // FIXME: from_single_link should return a packed object_groups?
+ //
+ //######
+ object_groups<L>
+ groups = primitive::group::from_single_link(filtered_objects,
+ hratio_filtered_links);
+ value::label_16 n_groups;
+ mln::fun::i2v::array<value::label_16>
+ groups_packed = mln::make::relabelfun(groups,
+ filtered_objects.nlabels(),
+ n_groups);
+ //######
+
+
+ std::cout << "Grouping objects..." << std::endl;
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects, groups);
+
+
+
+ // Compute min_row/max_row median
+ std::cout << "Preparing output" << std::endl;
+ {
+ image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+
+// scribo::draw::bounding_box_links(output,
+// filtered_objects.bboxes(),
+// hratio_filtered_links,
+// literal::green);
+
+ typedef mln::value::int_u<10> median_t;
+ typedef mln::accu::stat::median_h<median_t> accu_t;
+ util::array<accu_t>
+ med_min_row(static_cast<unsigned>(grouped_objects.nlabels()) + 1),
+ med_max_row(static_cast<unsigned>(grouped_objects.nlabels()) + 1);
+
+// std::cout << "Find median min/max rows" << std::endl;
+ for_all_components(i, filtered_objects.bboxes())
+ {
+ if (groups_packed(i) != 0)
+ {
+ med_min_row(groups_packed(i)).take(filtered_objects.bbox(i).pmin().row() - grouped_objects.bbox(groups_packed(i)).pmin().row());
+
+ med_max_row(groups_packed(i)).take(grouped_objects.bbox(groups_packed(i)).pmax().row() - filtered_objects.bbox(i).pmax().row());
+ }
+ }
+
+ // Output
+ std::cout << "Drawing bboxes" << std::endl;
+ util::array<bool> drawn(static_cast<unsigned>(filtered_objects.nlabels()) + 1, 0);
+ util::array<bool> single(static_cast<unsigned>(grouped_objects.nlabels()) + 1);
+ for_all_components(i, filtered_objects.bboxes())
+ if (hratio_filtered_links[i] == i && ! drawn(i))
+ {
+ mln::draw::box(output, filtered_objects.bbox(i), literal::orange);
+ drawn[i] = true;
+ single(groups_packed(i)) = true;
+ }
+ else
+ {
+ mln::draw::box(output, filtered_objects.bbox(i), literal::blue);
+ mln::draw::box(output, filtered_objects.bbox(hratio_filtered_links[i]),
+ literal::blue);
+ drawn[i] = true;
+ drawn[hratio_filtered_links[i]] = true;
+ single(groups_packed(i)) = false;
+ }
+
+// std::cout << "Drawing median lines" << std::endl;
+ for_all_components(i, grouped_objects.bboxes())
+ {
+ if (! single(i))
+ {
+ point2d
+ b_top(grouped_objects.bbox(i).pmin().row() + med_min_row(i).to_result(), grouped_objects.bbox(i).pmin().col()),
+ e_top(grouped_objects.bbox(i).pmin().row() + med_min_row(i).to_result(), grouped_objects.bbox(i).pmax().col()),
+ b_bot(grouped_objects.bbox(i).pmax().row() - med_max_row(i).to_result(), grouped_objects.bbox(i).pmin().col()),
+ e_bot(grouped_objects.bbox(i).pmax().row() - med_max_row(i).to_result(), grouped_objects.bbox(i).pmax().col());
+
+ mln::draw::box(output, grouped_objects.bbox(i), literal::purple);
+ mln::draw::line(output, b_top, e_top, literal::cyan);
+ mln::draw::line(output, b_bot, e_bot, literal::cyan);
+ }
+ }
+
+ io::ppm::save(output, argv[2]);
+ }
+
+ trace::exiting("main");
+}
diff --git a/scribo/src/debug/show_left_right_links_validation.cc b/scribo/src/debug/show_left_right_links_validation.cc
deleted file mode 100644
index bb23dd5..0000000
--- a/scribo/src/debug/show_left_right_links_validation.cc
+++ /dev/null
@@ -1,122 +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 <iostream>
-
-#include <mln/core/image/image2d.hh>
-#include <mln/labeling/colorize.hh>
-#include <mln/debug/println.hh>
-#include <mln/data/convert.hh>
-#include <mln/util/array.hh>
-#include <mln/literal/colors.hh>
-#include <mln/io/pbm/load.hh>
-#include <mln/io/ppm/save.hh>
-#include <mln/core/alias/neighb2d.hh>
-#include <mln/value/label_16.hh>
-
-#include <scribo/core/object_links.hh>
-#include <scribo/core/object_image.hh>
-
-#include <scribo/primitive/extract/objects.hh>
-
-#include <scribo/primitive/link/with_single_left_link.hh>
-#include <scribo/primitive/link/with_single_right_link.hh>
-#include <scribo/primitive/link/merge_double_link.hh>
-
-#include <scribo/draw/bounding_boxes.hh>
-#include <scribo/draw/bounding_box_links.hh>
-
-#include <scribo/debug/usage.hh>
-
-
-
-const char *args_desc[][2] =
-{
- { "input.pbm", "A binary image. 'True' for objects, 'False'\
-for the background." },
- { "hlmax", "Maximum distance between two grouped objects while browsing on the left." },
- { "hrmax", "Maximum distance between two grouped objects while browsing on the right." },
- {0, 0}
-};
-
-int main(int argc, char *argv[])
-{
- using namespace scribo;
- using namespace mln;
-
- if (argc != 5)
- return scribo::debug::usage(argv,
- "Display double validated (left and right) links between objects",
- "<input.pbm> <hlmax> <hrmax> <output.ppm>",
- args_desc,
- "A color image. Validated links are drawn in green.");
-
- image2d<bool> input;
- io::pbm::load(input, argv[1]);
-
- // Finding objects.
- value::label_16 nbboxes;
- typedef image2d<value::label_16> L;
- object_image(L) objects = primitive::extract::objects(input, c8(), nbboxes);
-
- // Left links.
- object_links<L> left_link
- = primitive::link::with_single_left_link(objects, atoi(argv[2]));
-
- // Right links.
- object_links<L> right_link
- = primitive::link::with_single_right_link(objects, atoi(argv[3]));
-
- // Validation.
- object_links<L>
- links = primitive::link::merge_double_link(objects, left_link, right_link);
-
-
- // Saving result.
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
-// scribo::draw::bounding_boxes(output, objects, literal::blue);
- scribo::draw::bounding_box_links(output,
- objects.bboxes(),
- links,
- literal::green);
-
- util::array<bool> drawn(objects.nlabels(), 0);
- for_all_components(i, objects.bboxes())
- if (links[i] == i && ! drawn(i))
- {
- mln::draw::box(output, objects.bbox(i), literal::orange);
- drawn[i] = true;
- }
- else
- {
- mln::draw::box(output, objects.bbox(i), literal::blue);
- mln::draw::box(output, objects.bbox(links[i]), literal::blue);
- drawn[i] = true;
- drawn[links[i]] = true;
- }
-
- io::ppm::save(output, argv[4]);
-}
diff --git a/scribo/src/debug/show_links_bbox_h_ratio.cc b/scribo/src/debug/show_links_bbox_h_ratio.cc
index 86841c6..4377b41 100644
--- a/scribo/src/debug/show_links_bbox_h_ratio.cc
+++ b/scribo/src/debug/show_links_bbox_h_ratio.cc
@@ -92,7 +92,7 @@ int main(int argc, char* argv[])
image2d<value::rgb8> hratio_decision_image
= scribo::debug::links_decision_image(input,
right_links,
- hratio_filtered_links);
+ hratio_filtered_links, 200);
io::ppm::save(hratio_decision_image, argv[3]);
}
diff --git a/scribo/src/debug/show_links_left_right_links_validation.cc b/scribo/src/debug/show_links_left_right_links_validation.cc
new file mode 100644
index 0000000..3c5d1bf
--- /dev/null
+++ b/scribo/src/debug/show_links_left_right_links_validation.cc
@@ -0,0 +1,122 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/labeling/colorize.hh>
+#include <mln/debug/println.hh>
+#include <mln/data/convert.hh>
+#include <mln/util/array.hh>
+#include <mln/literal/colors.hh>
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/value/label_16.hh>
+
+#include <scribo/core/object_links.hh>
+#include <scribo/core/object_image.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+
+#include <scribo/primitive/link/with_single_left_link.hh>
+#include <scribo/primitive/link/with_single_right_link.hh>
+#include <scribo/primitive/link/merge_double_link.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+#include <scribo/draw/bounding_box_links.hh>
+
+#include <scribo/debug/usage.hh>
+
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. 'True' for objects, 'False'\
+for the background." },
+ { "hlmax", "Maximum distance between two grouped objects while browsing on the left." },
+ { "hrmax", "Maximum distance between two grouped objects while browsing on the right." },
+ {0, 0}
+};
+
+int main(int argc, char *argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc != 5)
+ return scribo::debug::usage(argv,
+ "Display double validated (left and right) links between objects",
+ "<input.pbm> <hlmax> <hrmax> <output.ppm>",
+ args_desc,
+ "A color image. Validated links are drawn in green.");
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects = primitive::extract::objects(input, c8(), nbboxes);
+
+ // Left links.
+ object_links<L> left_link
+ = primitive::link::with_single_left_link(objects, atoi(argv[2]));
+
+ // Right links.
+ object_links<L> right_link
+ = primitive::link::with_single_right_link(objects, atoi(argv[3]));
+
+ // Validation.
+ object_links<L>
+ links = primitive::link::merge_double_link(objects, left_link, right_link);
+
+
+ // Saving result.
+ image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+
+// scribo::draw::bounding_boxes(output, objects, literal::blue);
+ scribo::draw::bounding_box_links(output,
+ objects.bboxes(),
+ links,
+ literal::green);
+
+ util::array<bool> drawn(static_cast<unsigned>(objects.nlabels()) + 1, 0);
+ for_all_components(i, objects.bboxes())
+ if (links[i] == i && ! drawn(i))
+ {
+ mln::draw::box(output, objects.bbox(i), literal::orange);
+ drawn[i] = true;
+ }
+ else
+ {
+ mln::draw::box(output, objects.bbox(i), literal::blue);
+ mln::draw::box(output, objects.bbox(links[i]), literal::blue);
+ drawn[i] = true;
+ drawn[links[i]] = true;
+ }
+
+ io::ppm::save(output, argv[4]);
+}
diff --git a/scribo/src/debug/show_links_single_down.cc b/scribo/src/debug/show_links_single_down.cc
new file mode 100644
index 0000000..37325d2
--- /dev/null
+++ b/scribo/src/debug/show_links_single_down.cc
@@ -0,0 +1,157 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/data/convert.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/draw/line.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/link/internal/link_ms_dmax_base.hh>
+#include <scribo/primitive/link/compute.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/debug/save_linked_bboxes_image.hh>
+#include <scribo/debug/usage.hh>
+
+
+namespace scribo
+{
+
+ template <typename I, typename L>
+ struct single_down_link_debug_functor
+ : primitive::link::internal::link_ms_dmax_base<L,
+ single_down_link_debug_functor<I, L> >
+ {
+ typedef single_down_link_debug_functor<I, L> self_t;
+ typedef
+ primitive::link::internal::link_ms_dmax_base<L, self_t> super_;
+
+ public:
+ typedef mln_site(L) P;
+
+ single_down_link_debug_functor(const I& input,
+ const object_image(L)& objects,
+ float dmax)
+ : super_(objects, dmax, 0)
+ {
+ output_ = data::convert(value::rgb8(), input);
+ scribo::draw::bounding_boxes(output_, objects, literal::blue);
+ mln_postcondition(output_.is_valid());
+ }
+
+ void validate_link_(unsigned current_object,
+ const P& start_point,
+ const P& p,
+ anchor::Type anchor)
+ {
+ mln::draw::line(output_, start_point, p, literal::green);
+
+ super_::validate_link_(current_object, start_point, p, anchor);
+ }
+
+
+
+ void invalidate_link_(unsigned current_object,
+ const P& start_point,
+ const P& p,
+ anchor::Type anchor)
+ {
+ if (output_.domain().has(p))
+ mln::draw::line(output_, start_point, p, literal::red);
+ else
+ {
+ P tmp = p;
+ --tmp.row();
+ mln::draw::line(output_, start_point, tmp, literal::red);
+ }
+
+ super_::invalidate_link_(current_object, start_point, p, anchor);
+ }
+
+
+
+ void compute_next_site_(P& p)
+ {
+ ++p.row();
+ }
+
+
+ image2d<value::rgb8> output_;
+ };
+
+
+}
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. True for objects and False for the background." },
+ { "max_nbh_dist", "The maximum lookup distance. (common value : 30)" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace scribo::primitive::internal;
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Show sucessful/unsuccessful down links between components.",
+ "input.pbm max_nbh_dist output.ppm",
+ args_desc,
+ "A color image. Valid links are drawn in green, invalid ones in red.");
+
+ typedef image2d<bool> I;
+ I input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects
+ = scribo::primitive::extract::objects(input, c8(), nbboxes);
+
+ // Write debug image.
+ single_down_link_debug_functor<I, L> functor(input, objects, atof(argv[2]));
+ primitive::link::compute(functor);
+
+ io::ppm::save(functor.output_, argv[3]);
+}
diff --git a/scribo/src/debug/show_links_single_down_left_aligned.cc b/scribo/src/debug/show_links_single_down_left_aligned.cc
new file mode 100644
index 0000000..b094d6e
--- /dev/null
+++ b/scribo/src/debug/show_links_single_down_left_aligned.cc
@@ -0,0 +1,99 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/data/convert.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/pi.hh>
+
+#include <mln/draw/line.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/link/with_single_down_link.hh>
+#include <scribo/primitive/link/compute.hh>
+#include <scribo/filter/object_links_left_aligned.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/debug/save_linked_bboxes_image.hh>
+#include <scribo/debug/usage.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. True for objects and False for the background." },
+ { "dmax", "The maximum lookup distance. (common value : 300)" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace scribo::primitive::internal;
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Show sucessful/unsuccessful down links between components.",
+ "input.pbm dmax output.ppm",
+ args_desc,
+ "A color image. Valid links are drawn in green, invalid ones in red.");
+
+ typedef image2d<bool> I;
+ I input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects
+ = scribo::primitive::extract::objects(input, c8(), nbboxes);
+
+
+ object_links<L>
+ down_links = primitive::link::with_single_down_link(objects,
+ atof(argv[2]),
+ anchor::Left);
+
+ // Filtering.
+ down_links = filter::object_links_left_aligned(objects, down_links, 5);
+
+
+ scribo::debug::save_linked_bboxes_image(input, objects, down_links,
+ literal::blue, literal::green,
+ argv[3], anchor::ActualLeft);
+}
diff --git a/scribo/src/debug/show_links_single_down_right_aligned.cc b/scribo/src/debug/show_links_single_down_right_aligned.cc
new file mode 100644
index 0000000..9a12c0d
--- /dev/null
+++ b/scribo/src/debug/show_links_single_down_right_aligned.cc
@@ -0,0 +1,99 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/data/convert.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/pi.hh>
+
+#include <mln/draw/line.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/link/with_single_down_link.hh>
+#include <scribo/primitive/link/compute.hh>
+#include <scribo/filter/object_links_right_aligned.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/debug/save_linked_bboxes_image.hh>
+#include <scribo/debug/usage.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. True for objects and False for the background." },
+ { "dmax", "The maximum lookup distance. (common value : 300)" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace scribo::primitive::internal;
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Show sucessful/unsuccessful down links between components.",
+ "input.pbm dmax output.ppm",
+ args_desc,
+ "A color image. Valid links are drawn in green, invalid ones in red.");
+
+ typedef image2d<bool> I;
+ I input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects
+ = scribo::primitive::extract::objects(input, c8(), nbboxes);
+
+
+ object_links<L>
+ down_links = primitive::link::with_single_down_link(objects,
+ atof(argv[2]),
+ anchor::Right);
+
+ // Filtering.
+ down_links = filter::object_links_right_aligned(objects, down_links, 5);
+
+
+ scribo::debug::save_linked_bboxes_image(input, objects, down_links,
+ literal::blue, literal::green,
+ argv[3], anchor::ActualRight);
+}
diff --git a/scribo/src/debug/show_links_single_up.cc b/scribo/src/debug/show_links_single_up.cc
new file mode 100644
index 0000000..59c8ce5
--- /dev/null
+++ b/scribo/src/debug/show_links_single_up.cc
@@ -0,0 +1,157 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/data/convert.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/draw/line.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/link/internal/link_ms_dmax_base.hh>
+#include <scribo/primitive/link/compute.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/debug/save_linked_bboxes_image.hh>
+#include <scribo/debug/usage.hh>
+
+
+namespace scribo
+{
+
+ template <typename I, typename L>
+ struct single_up_link_debug_functor
+ : primitive::link::internal::link_ms_dmax_base<L,
+ single_up_link_debug_functor<I, L> >
+ {
+ typedef single_up_link_debug_functor<I, L> self_t;
+ typedef
+ primitive::link::internal::link_ms_dmax_base<L, self_t> super_;
+
+ public:
+ typedef mln_site(L) P;
+
+ single_up_link_debug_functor(const I& input,
+ const object_image(L)& objects,
+ float dmax)
+ : super_(objects, dmax)
+ {
+ output_ = data::convert(value::rgb8(), input);
+ scribo::draw::bounding_boxes(output_, objects, literal::blue);
+ mln_postcondition(output_.is_valid());
+ }
+
+ void validate_link_(unsigned current_object,
+ const P& start_point,
+ const P& p,
+ anchor::Type anchor)
+ {
+ mln::draw::line(output_, start_point, p, literal::green);
+
+ super_::validate_link_(current_object, start_point, p, anchor);
+ }
+
+
+
+ void invalidate_link_(unsigned current_object,
+ const P& start_point,
+ const P& p,
+ anchor::Type anchor)
+ {
+ if (output_.domain().has(p))
+ mln::draw::line(output_, start_point, p, literal::red);
+ else
+ {
+ P tmp = p;
+ ++tmp.row();
+ mln::draw::line(output_, start_point, tmp, literal::red);
+ }
+
+ super_::invalidate_link_(current_object, start_point, p, anchor);
+ }
+
+
+
+ void compute_next_site_(P& p)
+ {
+ --p.row();
+ }
+
+
+ image2d<value::rgb8> output_;
+ };
+
+
+}
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. True for objects and False for the background." },
+ { "max_nbh_dist", "The maximum lookup distance. (common value : 30)" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace scribo::primitive::internal;
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Show sucessful/unsuccessful up links between components.",
+ "input.pbm max_nbh_dist output.ppm",
+ args_desc,
+ "A color image. Valid links are drawn in green, invalid ones in red.");
+
+ typedef image2d<bool> I;
+ I input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects
+ = scribo::primitive::extract::objects(input, c8(), nbboxes);
+
+ // Write debug image.
+ single_up_link_debug_functor<I, L> functor(input, objects, atof(argv[2]));
+ primitive::link::compute(functor);
+
+ io::ppm::save(functor.output_, argv[3]);
+}
diff --git a/scribo/src/debug/show_links_single_up_left_aligned.cc b/scribo/src/debug/show_links_single_up_left_aligned.cc
new file mode 100644
index 0000000..5239619
--- /dev/null
+++ b/scribo/src/debug/show_links_single_up_left_aligned.cc
@@ -0,0 +1,100 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/data/convert.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/pi.hh>
+
+#include <mln/draw/line.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/link/with_single_up_link.hh>
+#include <scribo/primitive/link/compute.hh>
+#include <scribo/filter/object_links_left_aligned.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/debug/save_linked_bboxes_image.hh>
+#include <scribo/debug/usage.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. True for objects and False for the background." },
+ { "dmax", "The maximum lookup distance. (common value : 300)" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace scribo::primitive::internal;
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Show sucessful/unsuccessful up links between components.",
+ "input.pbm dmax output.ppm",
+ args_desc,
+ "A color image. Valid links are drawn in green, invalid ones in red.");
+
+ typedef image2d<bool> I;
+ I input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects
+ = scribo::primitive::extract::objects(input, c8(), nbboxes);
+
+
+ object_links<L>
+ up_links = primitive::link::with_single_up_link(objects,
+ atof(argv[2]),
+ anchor::Left);
+
+
+ // Filtering.
+ up_links = filter::object_links_left_aligned(objects, up_links, 5);
+
+
+ scribo::debug::save_linked_bboxes_image(input, objects, up_links,
+ literal::blue, literal::green,
+ argv[3], anchor::ActualLeft);
+}
diff --git a/scribo/src/debug/show_links_single_up_right_aligned.cc b/scribo/src/debug/show_links_single_up_right_aligned.cc
new file mode 100644
index 0000000..8c1841e
--- /dev/null
+++ b/scribo/src/debug/show_links_single_up_right_aligned.cc
@@ -0,0 +1,98 @@
+// 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 <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/data/convert.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pbm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/pi.hh>
+
+#include <mln/draw/line.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/link/with_single_up_link.hh>
+#include <scribo/primitive/link/compute.hh>
+#include <scribo/filter/object_links_right_aligned.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/debug/save_linked_bboxes_image.hh>
+#include <scribo/debug/usage.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. True for objects and False for the background." },
+ { "dmax", "The maximum lookup distance. (common value : 300)" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace scribo::primitive::internal;
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Show sucessful/unsuccessful up links between components.",
+ "input.pbm dmax output.ppm",
+ args_desc,
+ "A color image. Valid links are drawn in green, invalid ones in red.");
+
+ typedef image2d<bool> I;
+ I input;
+ io::pbm::load(input, argv[1]);
+
+ // Finding objects.
+ value::label_16 nbboxes;
+ typedef image2d<value::label_16> L;
+ object_image(L) objects
+ = scribo::primitive::extract::objects(input, c8(), nbboxes);
+
+ object_links<L>
+ up_links = primitive::link::with_single_up_link(objects,
+ atof(argv[2]),
+ anchor::Right);
+
+ // Filtering.
+ up_links = filter::object_links_right_aligned(objects, up_links, 5);
+
+
+ scribo::debug::save_linked_bboxes_image(input, objects, up_links,
+ literal::blue, literal::green,
+ argv[3], anchor::ActualRight);
+}
diff --git a/scribo/src/filter/Makefile.am b/scribo/src/filter/Makefile.am
index e8a2b9c..98f8371 100644
--- a/scribo/src/filter/Makefile.am
+++ b/scribo/src/filter/Makefile.am
@@ -23,9 +23,13 @@ bin_PROGRAMS = \
objects_large \
objects_small \
objects_thick \
- objects_thin
+ objects_thin \
+ objects_with_holes \
+ objects_with_holes_pgm
objects_large_SOURCES = objects_large.cc
objects_small_SOURCES = objects_small.cc
objects_thick_SOURCES = objects_thick.cc
objects_thin_SOURCES = objects_thin.cc
+objects_with_holes_SOURCES = objects_with_holes.cc
+objects_with_holes_pgm_SOURCES = objects_with_holes_pgm.cc
diff --git a/scribo/src/filter/objects_with_holes.cc b/scribo/src/filter/objects_with_holes.cc
new file mode 100644
index 0000000..0ebf89f
--- /dev/null
+++ b/scribo/src/filter/objects_with_holes.cc
@@ -0,0 +1,70 @@
+// 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_16.hh>
+
+#include <scribo/filter/objects_with_holes.hh>
+#include <scribo/debug/usage.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." },
+ {0, 0}
+};
+
+int main(int argc, char *argv[])
+{
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Filter objects with holes",
+ "input.pbm min_holes_count output.pbm",
+ args_desc,
+ "A binary image.");
+
+ trace::entering("main");
+
+ typedef image2d<bool> I;
+ 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);
+
+ obj_ima_t filtered = scribo::filter::objects_with_holes(objects, atoi(argv[2]));
+ io::pbm::save(data::convert(bool(), filtered), argv[3]);
+
+ trace::exiting("main");
+
+}
diff --git a/scribo/src/filter/objects_with_holes_pgm.cc b/scribo/src/filter/objects_with_holes_pgm.cc
new file mode 100644
index 0000000..c834538
--- /dev/null
+++ b/scribo/src/filter/objects_with_holes_pgm.cc
@@ -0,0 +1,71 @@
+// 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/data/compute.hh>
+#include <mln/io/pbm/save.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/value/label_8.hh>
+#include <mln/accu/stat/max.hh>
+
+#include <scribo/filter/objects_with_holes.hh>
+#include <scribo/debug/usage.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.pgm", "A label image. 'True' for objects, 'False'\
+for the background." },
+ { "min_holes_count", "The minimum holes per objects." },
+ {0, 0}
+};
+
+int main(int argc, char *argv[])
+{
+ using namespace mln;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Filter objects with holes",
+ "input.pgm min_holes_count output.pbm",
+ args_desc,
+ "A binary image.");
+
+ trace::entering("main");
+
+ typedef image2d<value::label_8> I;
+ I input;
+ io::pgm::load(input, argv[1]);
+
+ value::label_8 nobjects = data::compute(accu::meta::stat::max(), input);
+ object_image(I) objects(input, nobjects);
+
+ object_image(I) filtered = scribo::filter::objects_with_holes(objects, atoi(argv[2]));
+ io::pbm::save(data::convert(bool(), filtered), argv[3]);
+
+ trace::exiting("main");
+
+}
diff --git a/scribo/src/pbm_lines_in_doc.cc b/scribo/src/pbm_lines_in_doc.cc
new file mode 100644
index 0000000..8363b61
--- /dev/null
+++ b/scribo/src/pbm_lines_in_doc.cc
@@ -0,0 +1,299 @@
+// 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 <fstream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/logical/not.hh>
+
+#include <mln/labeling/colorize.hh>
+
+#include <mln/io/pbm/all.hh>
+#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/draw/box.hh>
+
+#include <mln/extension/adjust.hh>
+
+#include <scribo/table/erase.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/extract/lines_h_discontinued.hh>
+#include <scribo/primitive/extract/lines_v_discontinued.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_bottom_aligned.hh>
+#include <scribo/filter/object_links_bbox_h_ratio.hh>
+#include <scribo/filter/object_links_bbox_overlap.hh>
+
+#include <scribo/filter/objects_large.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/debug/usage.hh>
+
+#include <scribo/make/debug_filename.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. 'False' for objects, 'True'\
+for the background." },
+ { "out.ppm", "Show the text found in the input." },
+ { "object_label.dump", "Object labeled image." },
+ { "line_label.dump", "Text lines labeled image." },
+ { "bbox_100p.txt", "Text file containing pmin and pmax of text line bounding boxes." },
+ { "bbox_50p.txt", "Text file containing pmin and pmax of text line bounding boxes." },
+ { "output_dir", "Output directory for debug image" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc != 7 && argc != 8)
+ return scribo::debug::usage(argv,
+ "Find text lines in a document",
+ "input.pbm out.ppm object_label.dump line_label.dump bbox_100p.txt bbox_50p.txt [output_dir]",
+ args_desc,
+ "Debug outputs. The recognized text is printed in the standard output.");
+
+ if (argc == 8)
+ scribo::make::internal::debug_filename_prefix = argv[7];
+
+ trace::entering("main");
+
+
+ bool enable_debug = (argc == 5);
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+ typedef image2d<value::label_16> L;
+
+ /// Finding objects.
+ std::cout << "Finding objects..." << std::endl;
+ value::label_16 nobjects;
+ object_image(L)
+ objects = scribo::primitive::extract::objects(input,
+ c8(),
+ nobjects);
+
+
+ /// First filtering.
+ std::cout << "Filtering objects..." << std::endl;
+ object_image(L) filtered_objects
+ = scribo::filter::objects_small(objects, 6);
+
+
+ filtered_objects
+ = scribo::filter::objects_large(filtered_objects,
+ math::min(input.ncols(), input.nrows())
+ * math::min(input.ncols(), input.nrows()) / 5);
+
+
+
+ /// Linking potential objects
+ std::cout << "Linking objects..." << std::endl;
+ object_links<L> left_link
+ = primitive::link::with_single_left_link_dmax_ratio(filtered_objects);
+ object_links<L> right_link
+ = primitive::link::with_single_right_link_dmax_ratio(filtered_objects);
+
+
+#ifndef NOUT
+ if (enable_debug)
+ {
+ std::cerr << "BEFORE - nobjects = " << nobjects << 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.
+ object_links<L>
+ merged_links = primitive::link::merge_double_link(filtered_objects,
+ left_link,
+ right_link);
+
+
+
+
+ // Remove links if bboxes have too different sizes.
+ object_links<L> hratio_filtered_links
+ = filter::object_links_bbox_h_ratio(filtered_objects,
+ merged_links,
+ 2.0f);
+
+
+
+
+#ifndef NOUT
+ if (enable_debug)
+ {
+ 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.8f);
+
+
+
+
+#ifndef NOUT
+ if (enable_debug)
+ {
+ 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
+
+
+
+
+ object_groups<L>
+ groups = primitive::group::from_single_link(filtered_objects,
+ overlap_filtered_links);
+
+
+ std::cout << "Grouping objects..." << std::endl;
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects, groups);
+
+
+
+ std::cout << "Saving line bboxes..." << std::endl;
+
+ // 100p bboxes
+ {
+ std::ofstream out;
+ out.open(argv[5], std::ios_base::trunc);
+
+ if (! out)
+ {
+ std::cout << "Can't create output file!" << std::endl;
+ return 1;
+ }
+
+ for_all_ncomponents(i, grouped_objects.nlabels())
+ {
+ out << grouped_objects.bbox(i).pmin().row() << " "
+ << grouped_objects.bbox(i).pmin().col()
+ << " "
+ << grouped_objects.bbox(i).pmax().row() << " "
+ << grouped_objects.bbox(i).pmax().col()
+ << std::endl;
+ }
+ out.close();
+
+ }
+
+ // 50p bboxes
+ {
+ std::ofstream out;
+ out.open(argv[6], std::ios_base::trunc);
+
+ if (! out)
+ {
+ std::cout << "Can't create output file!" << std::endl;
+ return 1;
+ }
+
+ for_all_ncomponents(i, grouped_objects.nlabels())
+ {
+ out << grouped_objects.bbox(i).pmin().row() / 2 << " "
+ << grouped_objects.bbox(i).pmin().col() / 2
+ << " "
+ << grouped_objects.bbox(i).pmax().row() / 2 << " "
+ << grouped_objects.bbox(i).pmax().col() / 2
+ << std::endl;
+ }
+ out.close();
+
+ }
+
+ scribo::debug::save_bboxes_image(input, grouped_objects.bboxes(),
+ literal::red,
+ argv[2]);
+
+ io::dump::save(filtered_objects, argv[3]);
+ io::dump::save(grouped_objects, argv[4]);
+
+ trace::exiting("main");
+}
diff --git a/scribo/src/preprocessing/Makefile.am b/scribo/src/preprocessing/Makefile.am
index 8c1cd80..086a70f 100644
--- a/scribo/src/preprocessing/Makefile.am
+++ b/scribo/src/preprocessing/Makefile.am
@@ -22,8 +22,10 @@ include $(top_srcdir)/scribo/scribo.mk
bin_PROGRAMS = \
preprocess \
split_bg_fg \
+ split_bg_fg_ms \
unskew
preprocess_SOURCES = preprocess.cc
split_bg_fg_SOURCES = split_bg_fg.cc
+split_bg_fg_ms_SOURCES = split_bg_fg_ms.cc
unskew_SOURCES = unskew.cc
diff --git a/scribo/src/preprocessing/split_bg_fg_ms.cc b/scribo/src/preprocessing/split_bg_fg_ms.cc
new file mode 100644
index 0000000..4c0180a
--- /dev/null
+++ b/scribo/src/preprocessing/split_bg_fg_ms.cc
@@ -0,0 +1,109 @@
+// 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_in_article.cc b/scribo/src/text_in_article.cc
new file mode 100644
index 0000000..d140719
--- /dev/null
+++ b/scribo/src/text_in_article.cc
@@ -0,0 +1,327 @@
+// 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/alias/neighb2d.hh>
+
+#include <mln/io/pbm/all.hh>
+#include <mln/io/ppm/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/extension/adjust.hh>
+
+#include <mln/accu/stat/median_h.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/primitive/extract/objects.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/debug/usage.hh>
+
+#include <scribo/make/debug_filename.hh>
+
+
+const char *args_desc[][2] =
+{
+ { "input.pbm", "A binary image. 'False' for objects, 'True'\
+for the background." },
+ { "debug_dir", "Output directory for debug image" },
+ {0, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc != 3 && argc != 4)
+ return scribo::debug::usage(argv,
+ "Find text lines using left/right validation and display x-height in a binarized article.",
+ "input.pbm out.ppm <debug_dir>",
+ args_desc,
+ "A color image. The following colors are used : dark blue for object bboxes, orange for single object bboxes, purple for group bboxes and light blue for x-height.");
+
+ if (argc == 4)
+ scribo::make::internal::debug_filename_prefix = argv[3];
+
+ trace::entering("main");
+
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+ typedef image2d<value::label_16> L;
+
+ /// Finding objects.
+ std::cout << "Finding objects..." << std::endl;
+ value::label_16 nobjects;
+ object_image(L)
+ objects = scribo::primitive::extract::objects(input,
+ c8(),
+ nobjects);
+
+
+ /// First filtering.
+ std::cout << "Filtering objects..." << std::endl;
+ object_image(L) filtered_objects
+ = scribo::filter::objects_small(objects, 6);
+
+ /// Linking potential objects
+ std::cout << "Linking objects..." << std::endl;
+ object_links<L> left_link
+ = primitive::link::with_single_left_link_dmax_ratio(filtered_objects);
+ object_links<L> right_link
+ = primitive::link::with_single_right_link_dmax_ratio(filtered_objects);
+
+
+ // Validating left and right links.
+ object_links<L>
+ merged_links = primitive::link::merge_double_link(filtered_objects,
+ 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);
+
+ util::array<bool> drawn(objects.nlabels(), 0);
+ for_all_components(i, objects.bboxes())
+ if (merged_links[i] == i && ! drawn(i))
+ {
+ mln::draw::box(output, objects.bbox(i), literal::orange);
+ drawn[i] = true;
+ }
+ else
+ {
+ mln::draw::box(output, objects.bbox(i), literal::blue);
+ mln::draw::box(output, objects.bbox(merged_links[i]), literal::blue);
+ drawn[i] = true;
+ drawn[merged_links[i]] = true;
+ }
+
+ 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_objects,
+ merged_links,
+ 2.0f);
+
+
+
+
+#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
+
+
+
+ // FIXME: from_single_link should return a packed object_groups?
+ //
+ //######
+ object_groups<L>
+ groups = primitive::group::from_single_link(filtered_objects,
+ hratio_filtered_links);
+ value::label_16 n_groups;
+ mln::fun::i2v::array<value::label_16>
+ groups_packed = mln::make::relabelfun(groups,
+ filtered_objects.nlabels(),
+ n_groups);
+ //######
+
+
+ std::cout << "Grouping objects..." << std::endl;
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects, groups);
+
+
+
+ // Compute min_row/max_row median
+ std::cout << "Preparing output" << std::endl;
+ {
+ image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+
+// scribo::draw::bounding_box_links(output,
+// filtered_objects.bboxes(),
+// hratio_filtered_links,
+// literal::green);
+
+ typedef mln::value::int_u<10> median_t;
+ typedef mln::accu::stat::median_h<median_t> accu_t;
+ util::array<accu_t>
+ med_min_row(static_cast<unsigned>(grouped_objects.nlabels()) + 1),
+ med_max_row(static_cast<unsigned>(grouped_objects.nlabels()) + 1);
+
+// std::cout << "Find median min/max rows" << std::endl;
+ for_all_components(i, filtered_objects.bboxes())
+ {
+ if (groups_packed(i) != 0)
+ {
+ med_min_row(groups_packed(i)).take(filtered_objects.bbox(i).pmin().row() - grouped_objects.bbox(groups_packed(i)).pmin().row());
+
+ med_max_row(groups_packed(i)).take(grouped_objects.bbox(groups_packed(i)).pmax().row() - filtered_objects.bbox(i).pmax().row());
+ }
+ }
+
+ // Output
+ std::cout << "Drawing bboxes" << std::endl;
+ util::array<bool> drawn(static_cast<unsigned>(filtered_objects.nlabels()) + 1, 0);
+ util::array<bool> single(static_cast<unsigned>(grouped_objects.nlabels()) + 1);
+ for_all_components(i, filtered_objects.bboxes())
+ if (hratio_filtered_links[i] == i && ! drawn(i))
+ {
+ mln::draw::box(output, filtered_objects.bbox(i), literal::orange);
+ drawn[i] = true;
+ single(groups_packed(i)) = true;
+ }
+ else
+ {
+ mln::draw::box(output, filtered_objects.bbox(i), literal::blue);
+ mln::draw::box(output, filtered_objects.bbox(hratio_filtered_links[i]),
+ literal::blue);
+ drawn[i] = true;
+ drawn[hratio_filtered_links[i]] = true;
+ single(groups_packed(i)) = false;
+ }
+
+// std::cout << "Drawing median lines" << std::endl;
+ for_all_components(i, grouped_objects.bboxes())
+ {
+ if (! single(i))
+ {
+ point2d
+ b_top(grouped_objects.bbox(i).pmin().row() + med_min_row(i).to_result(), grouped_objects.bbox(i).pmin().col()),
+ e_top(grouped_objects.bbox(i).pmin().row() + med_min_row(i).to_result(), grouped_objects.bbox(i).pmax().col()),
+ b_bot(grouped_objects.bbox(i).pmax().row() - med_max_row(i).to_result(), grouped_objects.bbox(i).pmin().col()),
+ e_bot(grouped_objects.bbox(i).pmax().row() - med_max_row(i).to_result(), grouped_objects.bbox(i).pmax().col());
+
+ mln::draw::box(output, grouped_objects.bbox(i), literal::purple);
+ mln::draw::line(output, b_top, e_top, literal::cyan);
+ mln::draw::line(output, b_bot, e_bot, literal::cyan);
+ }
+ }
+
+ io::ppm::save(output, argv[2]);
+ }
+
+
+
+ // Display median character space.
+ {
+ image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+ typedef mln::value::int_u<8> median_t;
+ typedef mln::accu::stat::median_h<median_t> accu_t;
+ util::array<accu_t>
+ lspace_med(static_cast<unsigned>(grouped_objects.nlabels()) + 1);
+
+ for_all_components(i, filtered_objects.bboxes())
+ if (groups_packed(i) != 0)
+ {
+ if (hratio_filtered_links(i) != i)
+ {
+ unsigned
+ space = filtered_objects.bbox(i).pmin().col() - filtered_objects.bbox(hratio_filtered_links(i)).pmax().col();
+
+ lspace_med(groups_packed(i)).take(space);
+
+ }
+ }
+
+ std::cout << "Drawing median character space" << std::endl;
+ for_all_components(i, filtered_objects.bboxes())
+ if (groups_packed(i) != 0 && lspace_med(groups_packed(i)).card() > 1)
+ {
+ unsigned med = lspace_med(groups_packed(i)).to_result();
+
+ mln::draw::box(output, grouped_objects.bbox(groups_packed(i)),
+ literal::purple);
+
+ point2d
+ beg = filtered_objects.bbox(i).pmax(),
+ end = beg;
+ beg.row() = filtered_objects.bbox(i).pmin().row();
+ mln::draw::line(output, beg, end, literal::cyan);
+ beg.col() += med;
+ end.col() += med;
+ mln::draw::line(output, beg, end, literal::cyan);
+
+ }
+
+ io::ppm::save(output, "median_wspace.ppm");
+
+ }
+
+ trace::exiting("main");
+}
diff --git a/scribo/src/text_in_photo_ppm_fast.cc b/scribo/src/text_in_photo_ppm_fast.cc
new file mode 100644
index 0000000..4a7562f
--- /dev/null
+++ b/scribo/src/text_in_photo_ppm_fast.cc
@@ -0,0 +1,658 @@
+// 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.\n\n\
+Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
+ "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.");
+
+ std::string out_base_dir;
+ bool debug = false;
+ if (argc > 8)
+ {
+ scribo::make::internal::debug_filename_prefix = argv[8];
+ debug = true;
+ out_base_dir = argv[8];
+ }
+
+ trace::entering("main");
+
+ image2d<value::rgb8> input_rgb;
+ io::ppm::load(input_rgb, argv[1]);
+
+
+ unsigned lambda;
+ if (argc == 10)
+ lambda = atoi(argv[9]);
+ else
+ lambda = 1.2 * (input_rgb.nrows() + input_rgb.ncols());
+
+ std::cout << "Using lambda = " << lambda << std::endl;
+
+ 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;
+
+#ifndef NOUT
+ if (debug)
+ {
+ io::ppm::save(fg, scribo::make::debug_filename("foreground.ppm"));
+ }
+
+#endif // !NOUT
+ }
+ 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);
+ }
+#ifndef NOUT
+ if (debug)
+ {
+ io::pbm::save(input,
+ scribo::make::debug_filename("binarization.pbm"));
+ }
+
+#endif // !NOUT
+
+
+// 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 (debug)
+ {
+ 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 (debug)
+ {
+ 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 (debug)
+ {
+ 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,
+ groups);
+#endif // !NOUT
+
+ t_ = timer_;
+ std::cout << "Objects grouped. " << t_ << std::endl;
+
+#ifndef NOUT
+
+ if (debug)
+ {
+ image2d<value::rgb8> decision_image = data::convert(value::rgb8(), input);
+
+ scribo::draw::bounding_boxes(decision_image, filtered_objects, literal::green);
+ scribo::draw::bounding_boxes(decision_image, raw_group_image, literal::blue);
+
+ io::ppm::save(decision_image,
+ scribo::make::debug_filename("group_and_object_image.ppm"));
+
+ decision_image = data::convert(value::rgb8(), input);
+ scribo::draw::bounding_boxes(decision_image, raw_group_image, literal::blue);
+ io::ppm::save(decision_image,
+ scribo::make::debug_filename("group_image.ppm"));
+ }
+#endif // !NOUT
+
+
+ std::cout << "Filtering groups..." << std::endl;
+
+ util::timer g_timer;
+
+ 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;
+
+
+
+#ifndef NOUT
+ object_image(L) debug_image;
+ image2d<value::rgb8> decision_image;
+ if (debug)
+ {
+ decision_image = data::convert(value::rgb8(), input);
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects,
+ filtered_small_groups);
+
+ for_all_components(i, filtered_objects.bboxes())
+ if (filtered_small_groups(i) != 0)
+ mln::draw::box(decision_image, filtered_objects.bbox(i), literal::green);
+ scribo::draw::bounding_boxes(decision_image, raw_group_image, literal::red);
+ scribo::draw::bounding_boxes(decision_image, grouped_objects, literal::blue);
+
+ io::ppm::save(decision_image,
+ scribo::make::debug_filename("small_groups_filter.ppm"));
+ debug_image = grouped_objects;
+ }
+#endif
+
+
+
+ // 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;
+
+
+#ifndef NOUT
+ if (debug)
+ {
+ decision_image = data::convert(value::rgb8(), input);
+
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects,
+ filtered_thin_groups);
+
+ for_all_components(i, filtered_objects.bboxes())
+ {
+ if (filtered_thin_groups(i) != 0)
+ mln::draw::box(decision_image, filtered_objects.bbox(i), literal::green);
+ }
+
+ scribo::draw::bounding_boxes(decision_image, debug_image, literal::red);
+ scribo::draw::bounding_boxes(decision_image, grouped_objects, literal::blue);
+
+ io::ppm::save(decision_image,
+ scribo::make::debug_filename("thin_groups_filter.ppm"));
+ debug_image = grouped_objects;
+ }
+#endif
+
+
+
+ /// Apply grouping in the object image.
+ g_timer.restart();
+
+// groups = primitive::group::regroup_left(filtered_objects,
+// filtered_thin_groups,
+// 30);
+
+ object_image(L)
+ grouped_objects = primitive::group::apply(filtered_objects,
+ filtered_thin_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);
+
+ t_ = g_timer;
+ std::cout << "Link and group again " << t_ << std::endl;
+
+ timer_.stop();
+
+// if (debug)
+// io::ppm::save(mln::labeling::colorize(value::rgb8(),
+// grouped_objects,
+// grouped_objects.nlabels()),
+// scribo::make::debug_filename("out_raw.ppm"));
+
+
+ timer_.resume();
+
+// io::pgm::save(data::wrap(value::int_u8(), grouped_objects.labeled_image_()), "lbl_to_be_filtered.pgm");
+
+ 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;
+
+
+ t_ = global_t_;
+ std::cout << "*** Result computed " << t_ << std::endl;
+
+#ifndef NOUT
+ if (debug)
+ {
+ decision_image = data::convert(value::rgb8(), input);
+
+ scribo::draw::bounding_boxes(decision_image, debug_image, literal::red);
+ scribo::draw::bounding_boxes(decision_image, grouped_objects, literal::blue);
+ io::ppm::save(decision_image, scribo::make::debug_filename("group_with_holes_filter.ppm"));
+
+ std::cerr << "AFTER - nobjects = " << grouped_objects.nlabels() << std::endl;
+ }
+#endif
+
+ timer_.restart();
+ std::cout << "Saving result..." << std::endl;
+ io::ppm::save(mln::labeling::colorize(value::rgb8(),
+ grouped_objects,
+ grouped_objects.nlabels()),
+ argv[2]);
+
+#ifndef NOUT
+// scribo::debug::save_bboxes_image(input_rgb, grouped_objects.bboxes(),
+// literal::red,
+// scribo::make::debug_filename("orig_with_bboxes.ppm"));
+#endif
+
+ io::ppm::save(compute_highlight_image(input_rgb, grouped_objects),
+ out_base_dir + "_input_with_bboxes.ppm");
+ io::ppm::save(compute_text_image(input_rgb, grouped_objects),
+ out_base_dir + "_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