---
scribo/sandbox/icdar_13_table/Makefile | 9 +-
scribo/sandbox/icdar_13_table/README_ROLAND | 18 +++
scribo/sandbox/icdar_13_table/TODO | 2 -
scribo/sandbox/icdar_13_table/src/new.cc | 208 +++++++++++++++++++++++---
4 files changed, 209 insertions(+), 28 deletions(-)
create mode 100644 scribo/sandbox/icdar_13_table/README_ROLAND
diff --git a/scribo/sandbox/icdar_13_table/Makefile
b/scribo/sandbox/icdar_13_table/Makefile
index b9600a4..19743e1 100644
--- a/scribo/sandbox/icdar_13_table/Makefile
+++ b/scribo/sandbox/icdar_13_table/Makefile
@@ -1,21 +1,26 @@
CCACHE=
CC=g++
-CFLAGS=-Wall -Werror -O3 -DHAVE_TESSERACT_3 -DNDEBUG
+CFLAGS=-Wall -Werror -O3 -DHAVE_TESSERACT_3 -DNDEBUG -g
CLIBS=-I../../../milena/ -I../../ -I/usr/include/poppler
CLEAN=*.o output/* log final.xml
SRC=src/new.cc
+SRC_OLD=src/main.cc
OUTPUT=table
+OUTPUT_OLD=old
all: table
table:
$(CCACHE) $(CC) $(CFLAGS) $(CLIBS) $(SRC) -ltesseract -lpoppler-cpp -o $(OUTPUT)
+old:
+ $(CCACHE) $(CC) $(CFLAGS) $(CLIBS) $(SRC_OLD) -ltesseract -lpoppler-cpp -o
$(OUTPUT_OLD)
+
clean:
rm -rf $(CLEAN)
mrproper: clean
- rm -f $(OUTPUT)
+ rm -f $(OUTPUT) $(OUTPUT_OLD)
.PHONY: table clean mrproper
diff --git a/scribo/sandbox/icdar_13_table/README_ROLAND
b/scribo/sandbox/icdar_13_table/README_ROLAND
new file mode 100644
index 0000000..89b70dc
--- /dev/null
+++ b/scribo/sandbox/icdar_13_table/README_ROLAND
@@ -0,0 +1,18 @@
+#------------------------------------------------------------------------------#
+ README - ICDAR 2013 - Table competition
+#------------------------------------------------------------------------------#
+
+FIRST OF ALL :
+ * mkdir output
+
+Compilation and cleaning :
+ * make (generates the main program)
+ * make old (generates the old program (without scribo toolchain))
+ * make clean (remove all files expect the binary)
+ * make mrproper (remove all files)
+
+Usage :
+ * ./table [your-pdf-file]
+ Generate debug images in the output/ directory and the final.xml
+ * ./old [your-pdf-file]
+ Same thing but old processing (without scribo toolchain)
diff --git a/scribo/sandbox/icdar_13_table/TODO b/scribo/sandbox/icdar_13_table/TODO
index b34e7a2..a4aa631 100644
--- a/scribo/sandbox/icdar_13_table/TODO
+++ b/scribo/sandbox/icdar_13_table/TODO
@@ -3,10 +3,8 @@
#------------------------------------------------------------------------------#
Table location sub-competition :
- * Load PDF files (instead of PNM)
* Find links betwwen pages for mutlipages tables
* Get text from reversed-video zones
- * Expand the process to borderless tables
Table structure recognition sub-competition :
* All
diff --git a/scribo/sandbox/icdar_13_table/src/new.cc
b/scribo/sandbox/icdar_13_table/src/new.cc
index 963aa7d..714d0c2 100644
--- a/scribo/sandbox/icdar_13_table/src/new.cc
+++ b/scribo/sandbox/icdar_13_table/src/new.cc
@@ -1,9 +1,10 @@
+// INCLUDES OLENA
#include <mln/binarization/all.hh>
#include <mln/core/image/image2d.hh>
#include <mln/data/all.hh>
-#include <mln/draw/line.hh>
+#include <mln/draw/all.hh>
#include <mln/fun/v2v/rgb_to_luma.hh>
@@ -14,25 +15,45 @@
#include <mln/labeling/all.hh>
#include <mln/literal/all.hh>
#include <mln/logical/and.hh>
+#include <mln/logical/not.hh>
#include <mln/value/all.hh>
+// INCLUDE TESSERACT
#include <tesseract/baseapi.h>
+// INCLUDES SCRIBO
#include <scribo/binarization/sauvola.hh>
+
#include <scribo/core/component_set.hh>
+#include <scribo/core/line_set.hh>
+#include <scribo/core/paragraph_set.hh>
+
+#include <scribo/debug/links_image.hh>
+#include <scribo/draw/groups_bboxes.hh>
+#include <scribo/draw/line_components.hh>
+
+#include <scribo/filter/object_links_bbox_h_ratio.hh>
+
#include <scribo/preprocessing/denoise_fg.hh>
-#include <scribo/primitive/extract/vertical_separators.hh>
-#include <scribo/primitive/remove/separators.hh>
+#include <scribo/primitive/extract/lines_h_discontinued.hh>
+#include <scribo/primitive/extract/lines_v_discontinued.hh>
#include <scribo/primitive/extract/separators_nonvisible.hh>
-
+#include <scribo/primitive/extract/vertical_separators.hh>
+#include <scribo/primitive/group/from_single_link.hh>
+#include <scribo/primitive/link/internal/compute_anchor.hh>
#include <scribo/primitive/link/internal/dmax_width_and_height.hh>
-#include <scribo/primitive/link/with_single_right_link_dmax_ratio.hh>
-#include <scribo/primitive/link/with_single_left_link_dmax_ratio.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/remove/separators.hh>
+
+#include <scribo/text/extract_paragraphs.hh>
+#include <scribo/text/merging.hh>
using namespace mln;
+// Open and initialize XML
void start_xml(std::ofstream& xml, const char* name, const char* pdf)
{
xml.open(name);
@@ -40,12 +61,14 @@ void start_xml(std::ofstream& xml, const char* name, const char*
pdf)
<< "<document filename='" << pdf <<
"'>" << std::endl;
}
+// Finalize an close XML
void end_xml(std::ofstream& xml)
{
xml << "</document>" << std::endl;
xml.close();
}
+// Write a new (simple) table in XML file
void write_table(std::ofstream& xml, const point2d& start, const point2d&
end)
{
static unsigned table = 0;
@@ -62,6 +85,10 @@ void write_table(std::ofstream& xml, const point2d& start,
const point2d& end)
++table;
}
+ /********/
+ /* MAIN */
+ /********/
+
int main(int argc, char** argv)
{
typedef value::label_16 V;
@@ -69,10 +96,9 @@ int main(int argc, char** argv)
std::ofstream xml;
std::ostringstream path;
- image2d<value::rgb8> original;
+ image2d<value::rgb8> original, ima_links, ima_groups, ima_valid;
image2d<value::int_u8> filtered;
- image2d<bool> bin, separators, bin_without_separators, whitespaces,
- denoised, comp, links;
+ image2d<bool> bin, separators, bin_without_separators, whitespaces, comp,
denoised;
scribo::component_set< image2d<unsigned> > components;
unsigned dpi = 72;
@@ -89,9 +115,18 @@ int main(int argc, char** argv)
bin = scribo::binarization::sauvola(filtered, 81, 0.44);
// Find separators
- separators = scribo::primitive::extract::vertical_separators(bin, 81);
- bin_without_separators = scribo::primitive::remove::separators(bin, separators);
- whitespaces = scribo::primitive::extract::separators_nonvisible(bin);
+ bin_without_separators = duplicate(bin);
+ separators = separators;
+ V nhlines, nvlines;
+ unsigned min_width = 31;
+ unsigned min_height = 71;
+ scribo::component_set<L> hlines =
scribo::primitive::extract::lines_h_discontinued(bin_without_separators, c4(), nhlines,
min_width, 2);
+ scribo::component_set<L> vlines =
scribo::primitive::extract::lines_v_discontinued(bin_without_separators, c4(), nvlines,
min_height, 2);
+ for (unsigned i = 1; i <= hlines.nelements(); ++i)
+ data::fill((bin_without_separators | hlines(i).bbox()).rw(), false);
+
+ for (unsigned i = 1; i <= vlines.nelements(); ++i)
+ data::fill((bin_without_separators | vlines(i).bbox()).rw(), false);
// Denoise
denoised = scribo::preprocessing::denoise_fg(bin_without_separators, c8(), 4);
@@ -103,8 +138,13 @@ int main(int argc, char** argv)
initialize(comp, denoised);
data::fill(comp, false);
for (unsigned i = 1; i <= components.nelements(); ++i)
- data::fill((comp | components(i).bbox()).rw(), true);
+ {
+ const box2d& b = components(i).bbox();
+ if (b.width() > 2 && b.height() > 2)
+ data::fill((comp | b).rw(), true);
+ }
+ // Find links
scribo::object_links< image2d<unsigned> > right_link =
scribo::primitive::link::with_single_right_link_dmax_ratio(components,
scribo::primitive::link::internal::dmax_width_and_height(1),
scribo::anchor::MassCenter);
@@ -115,10 +155,127 @@ int main(int argc, char** argv)
scribo::object_links< image2d<unsigned> > merged_links =
scribo::primitive::link::merge_double_link(left_link, right_link);
- initialize(links, denoised);
- data::fill(links, false);
- for (unsigned i = 1; i <= merged_links.components().nelements(); ++i)
- data::fill((links | merged_links.components()(i).bbox()).rw(), true);
+ // Filter links
+ scribo::object_links< image2d<unsigned> > hratio_filtered_links =
scribo::filter::object_links_bbox_h_ratio(merged_links, 2.5f);
+
+ ima_links = data::convert(value::rgb8(), denoised);
+ ima_groups = data::convert(value::rgb8(), denoised);
+ ima_valid = data::convert(value::rgb8(), denoised);
+
+ // Write links
+ for (unsigned l = 1; l < merged_links.nelements(); ++l)
+ {
+ point2d p1 =
scribo::primitive::link::internal::compute_anchor(merged_links.components(), l,
scribo::anchor::MassCenter);
+ point2d p2 =
scribo::primitive::link::internal::compute_anchor(merged_links.components(),
merged_links(l), scribo::anchor::MassCenter);
+
+ draw::line(ima_links, p1, p2, literal::red);
+ }
+
+ for (unsigned l = 1; l < hratio_filtered_links.nelements(); ++l)
+ {
+ point2d p1 =
scribo::primitive::link::internal::compute_anchor(hratio_filtered_links.components(), l,
scribo::anchor::MassCenter);
+ point2d p2 =
scribo::primitive::link::internal::compute_anchor(hratio_filtered_links.components(),
hratio_filtered_links(l), scribo::anchor::MassCenter);
+
+ draw::line(ima_links, p1, p2, literal::blue);
+ }
+
+ // Group components
+ scribo::object_groups< image2d<unsigned> > groups =
scribo::primitive::group::from_single_link(hratio_filtered_links);
+ scribo::draw::groups_bboxes(ima_groups, groups, literal::blue);
+
+ // Compute averages
+ unsigned average_height = 0;
+ unsigned average_width = 0;
+
+ for (unsigned i = 1; i < groups.nelements(); ++i)
+ {
+ average_height += groups(i).bbox().height();
+ average_width += groups(i).bbox().width();
+ }
+ average_height /= groups.nelements();
+ average_width /= groups.nelements();
+
+ std::vector<short> balance(groups.nelements(), 0);
+
+ // Draw vertical links (red)
+ for (unsigned i = 1; i < groups.nelements(); ++i)
+ {
+ for (unsigned j = 1; j < groups.nelements(); ++j)
+ {
+ if (i != j)
+ {
+ const box2d& b1 = groups(i).bbox();
+ const box2d& b2 = groups(j).bbox();
+ const point2d& p1 = b1.pcenter();
+ const point2d& p2 = b2.pcenter();
+
+ unsigned max_height = std::max(b1.height(), b2.height());
+ unsigned min_height = std::min(b1.height(), b2.height());
+
+ if (p1[0] < p2[0] // Avoid redundancy
+ && max_height * 2 < denoised.ncols()
+ && min_height + 3 >= max_height // Same heights
+ && b1.width() < 2 * average_width && b2.width() < 2 *
average_width // Regular width
+ && (b1.pmin()[1] == b2.pmin()[1]
+ || (b1.pmin()[1] < b2.pmin()[1] && b1.pmax()[1] >
b2.pmin()[1])
+ || (b1.pmin()[1] > b2.pmin()[1] && b2.pmax()[1] >
b1.pmin()[1])) // Boxes are aligned
+ && abs(p1[0] - p2[0]) < 3 * max_height // Reduced gap
+ && abs(p1[1] - p2[1]) < 20) // Vertical proximity
+ {
+ draw::line(ima_groups, p1, p2, literal::red);
+ balance[i] += 1;
+ balance[j] += 1;
+ break;
+ }
+ }
+ }
+ }
+
+ // Draw horizontal links (green)
+ for (unsigned i = 1; i < groups.nelements(); ++i)
+ {
+ for (unsigned j = 1; j < groups.nelements(); ++j)
+ {
+ if (i != j)
+ {
+ const box2d& b1 = groups(i).bbox();
+ const box2d& b2 = groups(j).bbox();
+ const point2d& p1 = b1.pcenter();
+ const point2d& p2 = b2.pcenter();
+
+ if (p1[1] < p2[1] // Avoid redundancy
+ && (b1.pmin()[0] == b2.pmin()[0]
+ || (b1.pmin()[0] < b2.pmin()[0] && b1.pmax()[0] >
b2.pmin()[0])
+ || (b1.pmin()[0] > b2.pmin()[0] && b2.pmax()[0] >
b1.pmin()[0])) // Boxes are aligned
+ && abs(p1[0] - p2[0]) < 10) // Reduced gap
+ {
+ draw::line(ima_groups, p1, p2, literal::green);
+ balance[i] += 1;
+ balance[j] += 1;
+ break;
+ }
+ }
+ }
+ }
+
+ // Draw weighted boxes (red < orange < cyan < green)
+ for (unsigned i = 0; i < balance.size(); ++i)
+ {
+ std::cout << balance[i] << " ";
+ if (balance[i] == 1)
+ draw::box(ima_valid, groups(i).bbox(), literal::red);
+
+ if (balance[i] == 2)
+ draw::box(ima_valid, groups(i).bbox(), literal::orange);
+
+ if (balance[i] == 3)
+ draw::box(ima_valid, groups(i).bbox(), literal::cyan);
+
+ if (balance[i] > 3)
+ draw::box(ima_valid, groups(i).bbox(), literal::green);
+ }
+ std::cout << std::endl << std::endl;
+
// Write images and close XML
path.str(""); path << "output/p" << page <<
"_0_bin.pbm";
@@ -127,17 +284,20 @@ int main(int argc, char** argv)
path.str(""); path << "output/p" << page <<
"_1_bin_without_separators.pbm";
io::pbm::save(bin_without_separators, path.str());
- path.str(""); path << "output/p" << page <<
"_2_whitespaces.pbm";
- io::pbm::save(whitespaces, path.str());
-
- path.str(""); path << "output/p" << page <<
"_3_denoised.pbm";
+ path.str(""); path << "output/p" << page <<
"_2_denoised.pbm";
io::pbm::save(denoised, path.str());
- path.str(""); path << "output/p" << page <<
"_4_components.pbm";
+ path.str(""); path << "output/p" << page <<
"_3_components.pbm";
io::pbm::save(comp, path.str());
- path.str(""); path << "output/p" << page <<
"_5_links.pbm";
- io::pbm::save(links, path.str());
+ path.str(""); path << "output/p" << page <<
"_4_links.ppm";
+ io::ppm::save(ima_links, path.str());
+
+ path.str(""); path << "output/p" << page <<
"_5_groups.ppm";
+ io::ppm::save(ima_groups, path.str());
+
+ path.str(""); path << "output/p" << page <<
"_6_valid.ppm";
+ io::ppm::save(ima_valid, path.str());
}
end_xml(xml);
--
1.7.2.5