olena-2.0-385-g28f05e6 [ICDAR_13] Find texts inside cells on cropped tables

--- scribo/sandbox/icdar_13_table/Makefile | 18 +++ scribo/sandbox/icdar_13_table/src/main.cc | 171 +++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 0 deletions(-) create mode 100644 scribo/sandbox/icdar_13_table/Makefile create mode 100644 scribo/sandbox/icdar_13_table/src/main.cc diff --git a/scribo/sandbox/icdar_13_table/Makefile b/scribo/sandbox/icdar_13_table/Makefile new file mode 100644 index 0000000..2420eec --- /dev/null +++ b/scribo/sandbox/icdar_13_table/Makefile @@ -0,0 +1,18 @@ +CCACHE= +CC=g++ +CFLAGS=-Wall -Werror -O3 +CLIBS=-I../../../milena/ -I../../ +CLEAN=*.o $(OUTPUT) output/* log final.xml + +SRC=src/main.cc +OUTPUT=table + +all: table + +table: + $(CCACHE) $(CC) $(CFLAGS) -DHAVE_TESSERACT_3 $(CLIBS) $(SRC) -ltesseract -o $(OUTPUT) + +clean: + rm -rf $(CLEAN) + +.PHONY: table diff --git a/scribo/sandbox/icdar_13_table/src/main.cc b/scribo/sandbox/icdar_13_table/src/main.cc new file mode 100644 index 0000000..2ed125d --- /dev/null +++ b/scribo/sandbox/icdar_13_table/src/main.cc @@ -0,0 +1,171 @@ +#include <mln/binarization/all.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/data/all.hh> + +#include <mln/fun/v2v/rgb_to_luma.hh> + +#include <mln/io/pbm/all.hh> +#include <mln/io/ppm/all.hh> + +#include <mln/labeling/all.hh> +#include <mln/logical/and.hh> + +#include <mln/value/all.hh> + +#include <tesseract/baseapi.h> + +#include <scribo/binarization/sauvola.hh> +#include <scribo/core/component_set.hh> +#include <scribo/preprocessing/denoise_fg.hh> +#include <scribo/primitive/extract/all.hh> + +using namespace mln; + +void start_xml(std::ofstream& xml, const char* name, const char* pdf) +{ + xml.open(name); + xml << "<?xml version\"1.0\" encoding=\"UTF-8\"?>" << std::endl + << "<document filename='" << pdf << "'>" << std::endl; +} + +void end_xml(std::ofstream& xml) +{ + xml << "</document>" << std::endl; + xml.close(); +} + +void write_table(std::ofstream& xml, const point2d& start, const point2d& end) +{ + static unsigned table = 0; + static unsigned region = 0; + static unsigned page = 1; + + xml << "\t<table id='" << table << "'>" << std::endl + << "\t\t<region id='" << region << "' page='" << page << "'>" << std::endl + << "\t\t<bounding-box x1='" << start[1] << "' y1='" << start[0] << "' " + << "x2='" << end[1] << "' y2='" << end[0] << "'/>" << std::endl + << "\t\t</region>" << std::endl; + + ++table; +} + +template<typename L> +void get_horizontal_lines(scribo::component_set<L>& hlines, + image2d<bool>& ima, + image2d<bool>& bin_without, + const unsigned av_height) +{ + for (unsigned i = 1; i <= hlines.nelements(); ++i) + { + const box2d& b = hlines(i).bbox(); + + data::fill((bin_without | b).rw(), false); + + if (b.height() != 0 && b.width() != 0 && b.height() < av_height) + data::fill((ima | b).rw(), true); + } +} + +template<typename L> +void get_vertical_lines(scribo::component_set<L>& vlines, + image2d<bool>& ima, + image2d<bool>& bin_without, + const unsigned av_width, + const int delta_prox_h) +{ + for (unsigned i = 1; i <= vlines.nelements(); ++i) + { + const box2d& b = vlines(i).bbox(); + + data::fill((bin_without | b).rw(), false); + + if (b.height() != 0 && b.width() != 0 + && b.width() < av_width) + if (vlines(i).mass_center()[1] - vlines(i - 1).mass_center()[1] > delta_prox_h + || vlines(i).mass_center()[1] < vlines(i - 1).mass_center()[1]) + data::fill((ima | b).rw(), true); + } +} + +int main(int argc, char** argv) +{ + typedef value::label_16 V; + typedef image2d<V> L; + + image2d<value::rgb8> original; + image2d<value::int_u8> filtered; + image2d<bool> bin, ima_hlines, ima_vlines, ima_tables, ima_texts, + bin_without_lines, bin_without_lines_denoised, + mask; + image2d<unsigned> labeled; + scribo::component_set< image2d<unsigned> > masks; + V nhlines, nvlines; + L hlines_ima, vlines_ima; + scribo::component_set<L> hlines, vlines; + + // PARAMETERS + unsigned av_height = 4; + unsigned av_width = 4; + int delta_prox_h = 5; + unsigned min_height = 71; + unsigned min_width = 31; + + // Loadin and binarization + std::ofstream xml; + start_xml(xml, "final.xml", argv[1]); + + io::ppm::load(original, argv[1]); + filtered = data::transform(original, mln::fun::v2v::rgb_to_luma<value::int_u8>()); + + bin = scribo::binarization::sauvola(filtered, 81, 0.44); + + initialize(mask, bin); + initialize(ima_texts, bin); + initialize(ima_tables, bin); + data::fill(ima_tables, false); + + bin_without_lines = duplicate(bin); + + // Lines extraction + hlines = scribo::primitive::extract::lines_h_discontinued(bin, c4(), nhlines, min_width, 2); + vlines = scribo::primitive::extract::lines_v_discontinued(bin, c4(), nvlines, min_height, 2); + + get_horizontal_lines(hlines, ima_tables, bin_without_lines, av_height); + get_vertical_lines(vlines, ima_tables, bin_without_lines, av_width, delta_prox_h); + + // Denoising + bin_without_lines_denoised = scribo::preprocessing::denoise_fg(bin_without_lines, c8(), 3); + + // Set the tables mask + unsigned n; + labeled = labeling::blobs(ima_tables, c8(), n); + masks = scribo::component_set< image2d<unsigned> >(labeled, n); + data::fill(mask, false); + + for (unsigned i = 1; i <= masks.nelements(); ++i) + data::fill((mask | masks(i).bbox()).rw(), true); + + // Compose table zones with bin_without_lines_lines + ima_texts = logical::and_(bin_without_lines_denoised, mask); + + // Get lines images + hlines_ima = hlines.labeled_image(); + vlines_ima = vlines.labeled_image(); + ima_hlines = data::convert(bool(), hlines_ima); + ima_vlines = data::convert(bool(), vlines_ima); + + // Write images and close XML + io::pbm::save(bin, "output/0_bin.pbm"); + io::pbm::save(bin_without_lines, "output/1_bin_without_lines.pbm"); + io::pbm::save(bin_without_lines_denoised, "output/2_bin_without_lines_denoised.pbm"); + io::pbm::save(ima_hlines, "output/3_hlines.pbm"); + io::pbm::save(ima_vlines, "output/4_vlines.pbm"); + io::pbm::save(ima_tables, "output/5_tables.pbm"); + io::pbm::save(mask, "output/6_mask.pbm"); + io::pbm::save(ima_texts, "output/7_texts.pbm"); + + end_xml(xml); + + return 0; +} -- 1.7.2.5
participants (1)
-
Anthony Seure