mirror of
https://github.com/tesseract-ocr/tesseract.git
synced 2024-11-23 18:49:08 +08:00
Merge pull request #3330 from Sintun/master
Enable api access to table detector results, resolves #1714
This commit is contained in:
commit
122daf1d64
@ -271,6 +271,7 @@ noinst_HEADERS += src/ccstruct/seam.h
|
||||
noinst_HEADERS += src/ccstruct/split.h
|
||||
noinst_HEADERS += src/ccstruct/statistc.h
|
||||
noinst_HEADERS += src/ccstruct/stepblob.h
|
||||
noinst_HEADERS += src/ccstruct/tabletransfer.h
|
||||
noinst_HEADERS += src/ccstruct/werd.h
|
||||
if !DISABLED_LEGACY_ENGINE
|
||||
noinst_HEADERS += src/ccstruct/fontinfo.h
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector> // for std::vector
|
||||
#include <tuple> // for std::tuple
|
||||
|
||||
struct Pix;
|
||||
struct Pixa;
|
||||
@ -531,6 +532,28 @@ public:
|
||||
* as UTF8 and must be freed with the delete [] operator.
|
||||
*/
|
||||
char *GetUTF8Text();
|
||||
|
||||
size_t GetNumberOfTables();
|
||||
|
||||
/// Return the i-th table bounding box coordinates
|
||||
///
|
||||
///Gives the (top_left.x, top_left.y, bottom_right.x, bottom_right.y)
|
||||
/// coordinates of the i-th table.
|
||||
std::tuple<int,int,int,int> GetTableBoundingBox(
|
||||
unsigned i///< Index of the table, for upper limit \see GetNumberOfTables()
|
||||
);
|
||||
|
||||
/// Get bounding boxes of the rows of a table
|
||||
/// return values are (top_left.x, top_left.y, bottom_right.x, bottom_right.y)
|
||||
std::vector<std::tuple<int,int,int,int> > GetTableRows(
|
||||
unsigned i///< Index of the table, for upper limit \see GetNumberOfTables()
|
||||
);
|
||||
|
||||
/// Get bounding boxes of the cols of a table
|
||||
/// return values are (top_left.x, top_left.y, bottom_right.x, bottom_right.y)
|
||||
std::vector<std::tuple<int,int,int,int> > GetTableCols(
|
||||
unsigned i///< Index of the table, for upper limit \see GetNumberOfTables()
|
||||
);
|
||||
|
||||
/**
|
||||
* Make a HTML-formatted string with hOCR markup from the internal
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "tesseractclass.h" // for Tesseract
|
||||
#include "tprintf.h" // for tprintf
|
||||
#include "werd.h" // for WERD, WERD_IT, W_FUZZY_NON, W_FUZZY_SP
|
||||
#include "tabletransfer.h" // for detected tables from tablefind.h
|
||||
|
||||
#include <tesseract/baseapi.h>
|
||||
#include <tesseract/ocrclass.h> // for ETEXT_DESC
|
||||
@ -1294,6 +1295,62 @@ char *TessBaseAPI::GetUTF8Text() {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t TessBaseAPI::GetNumberOfTables()
|
||||
{
|
||||
return constUniqueInstance<std::vector<TessTable>>().size();
|
||||
}
|
||||
|
||||
std::tuple<int,int,int,int> TessBaseAPI::GetTableBoundingBox(unsigned i)
|
||||
{
|
||||
const std::vector<TessTable>& t = constUniqueInstance<std::vector<TessTable>>();
|
||||
|
||||
if(i >= t.size())
|
||||
return std::tuple<int,int,int,int>(0, 0, 0, 0);
|
||||
|
||||
const int height = tesseract_->ImageHeight();
|
||||
|
||||
return std::make_tuple<int,int,int,int>(
|
||||
t[i].box.left(), height - t[i].box.top(),
|
||||
t[i].box.right(), height - t[i].box.bottom());
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::tuple<int,int,int,int>> TessBaseAPI::GetTableRows(unsigned i)
|
||||
{
|
||||
const std::vector<TessTable>& t = constUniqueInstance<std::vector<TessTable>>();
|
||||
|
||||
if(i >= t.size())
|
||||
return std::vector<std::tuple<int,int,int,int>>();
|
||||
|
||||
std::vector<std::tuple<int,int,int,int>> rows(t[i].rows.size());
|
||||
const int height = tesseract_->ImageHeight();
|
||||
|
||||
for(unsigned j = 0; j < t[i].rows.size(); ++j)
|
||||
rows[j] = std::make_tuple<int,int,int,int>(
|
||||
t[i].rows[j].left(), height - t[i].rows[j].top(),
|
||||
t[i].rows[j].right(), height - t[i].rows[j].bottom());
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<int,int,int,int> > TessBaseAPI::GetTableCols(unsigned i)
|
||||
{
|
||||
const std::vector<TessTable>& t = constUniqueInstance<std::vector<TessTable>>();
|
||||
|
||||
if(i >= t.size())
|
||||
return std::vector<std::tuple<int,int,int,int>>();
|
||||
|
||||
std::vector<std::tuple<int,int,int,int>> cols(t[i].cols.size());
|
||||
const int height = tesseract_->ImageHeight();
|
||||
|
||||
for(unsigned j = 0; j < t[i].cols.size(); ++j)
|
||||
cols[j] = std::make_tuple<int,int,int,int>(
|
||||
t[i].cols[j].left(), height - t[i].cols[j].top(),
|
||||
t[i].cols[j].right(), height - t[i].cols[j].bottom());
|
||||
|
||||
return cols;
|
||||
}
|
||||
|
||||
static void AddBoxToTSV(const PageIterator *it, PageIteratorLevel level, std::string &text) {
|
||||
int left, top, right, bottom;
|
||||
it->BoundingBox(level, &left, &top, &right, &bottom);
|
||||
@ -2062,6 +2119,8 @@ void TessBaseAPI::ClearResults() {
|
||||
delete paragraph_models_;
|
||||
paragraph_models_ = nullptr;
|
||||
}
|
||||
|
||||
uniqueInstance<std::vector<TessTable>>().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
66
src/ccstruct/tabletransfer.h
Normal file
66
src/ccstruct/tabletransfer.h
Normal file
@ -0,0 +1,66 @@
|
||||
/******************************************************************************
|
||||
* File: tabletransfer.h
|
||||
* Description: Infrastructure for the transfer of table detection results
|
||||
* Author: Stefan Brechtken
|
||||
*
|
||||
* (C) Copyright 2021, Stefan Brechtken
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESSERACT_CCSTRUCT_TABLETRANSFER_H_
|
||||
#define TESSERACT_CCSTRUCT_TABLETRANSFER_H_
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "rect.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
/// Structure for data transfer from table detector
|
||||
struct TessTable {
|
||||
tesseract::TBOX box;
|
||||
std::vector<tesseract::TBOX> rows;
|
||||
std::vector<tesseract::TBOX> cols;
|
||||
};
|
||||
|
||||
/** \brief You can use this small template function to ensure that one and
|
||||
* only one object of type T exists. It implements the Singleton Pattern.
|
||||
*
|
||||
* T must be default-constructable.
|
||||
* Usage examples:
|
||||
* A& a = uniqueInstance<A>();
|
||||
* a.xyz();
|
||||
* uniqueInstance<A>(make_unique<A>(42)); // replace instance
|
||||
* a.foo();
|
||||
* or
|
||||
* uniqueInstance<A>().xyz();
|
||||
*/
|
||||
template<typename T>
|
||||
T& uniqueInstance(std::unique_ptr<T> new_instance = nullptr)
|
||||
{
|
||||
static std::unique_ptr<T> _instance = std::make_unique<T>();
|
||||
|
||||
if(new_instance)
|
||||
_instance = std::move(new_instance);
|
||||
|
||||
return *_instance.get();
|
||||
}
|
||||
|
||||
/// return const version of \see uniqueInstance
|
||||
template<typename T>
|
||||
const T& constUniqueInstance(std::unique_ptr<T> new_instance = nullptr)
|
||||
{
|
||||
return uniqueInstance<T>(std::move(new_instance));
|
||||
}
|
||||
|
||||
} // namespace tesseract
|
||||
|
||||
#endif // TESSERACT_CCSTRUCT_TABLETRANSFER_H_
|
@ -38,6 +38,7 @@
|
||||
#include "strokewidth.h"
|
||||
#include "tablefind.h"
|
||||
#include "workingpartset.h"
|
||||
#include "tabletransfer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -1530,6 +1531,10 @@ void ColumnFinder::RotateAndReskewBlocks(bool input_is_rtl, TO_BLOCK_LIST *block
|
||||
if (textord_debug_tabfind >= 2)
|
||||
tprintf("Block median size = (%d, %d)\n", block->median_size().x(), block->median_size().y());
|
||||
}
|
||||
|
||||
std::vector<TessTable>& tables = uniqueInstance<std::vector<TessTable>>();
|
||||
for(TessTable& mt: tables)
|
||||
mt.box.rotate_large(reskew_);
|
||||
}
|
||||
|
||||
// Computes the rotations for the block (to make textlines horizontal) and
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
#include "colpartitionset.h"
|
||||
#include "tablerecog.h"
|
||||
#include "tabletransfer.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
@ -263,12 +264,13 @@ void TableFinder::LocateTables(ColPartitionGrid *grid, ColPartitionSet **all_col
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
ScrollView *table_win = MakeWindow(0, 300, "Column Partitions & Neighbors");
|
||||
ScrollView *table_win = MakeWindow(0, 300,
|
||||
"Step 1: Column Partitions & Neighbors");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE);
|
||||
DisplayColPartitions(table_win, &leader_and_ruling_grid_, ScrollView::AQUAMARINE);
|
||||
DisplayColPartitionConnections(table_win, &clean_part_grid_, ScrollView::ORANGE);
|
||||
|
||||
table_win = MakeWindow(100, 300, "Fragmented Text");
|
||||
table_win = MakeWindow(100, 300, "Step 2: Fragmented Text");
|
||||
DisplayColPartitions(table_win, &fragmented_text_grid_, ScrollView::BLUE);
|
||||
}
|
||||
#endif // !GRAPHICS_DISABLED
|
||||
@ -303,7 +305,8 @@ void TableFinder::LocateTables(ColPartitionGrid *grid, ColPartitionSet **all_col
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_tablefind_show_mark) {
|
||||
ScrollView *table_win = MakeWindow(1200, 300, "Table Columns and Regions");
|
||||
ScrollView *table_win = MakeWindow(1200, 300,
|
||||
"Step 7: Table Columns and Regions");
|
||||
DisplayColSegments(table_win, &table_columns, ScrollView::DARK_TURQUOISE);
|
||||
DisplayColSegments(table_win, &table_regions, ScrollView::YELLOW);
|
||||
}
|
||||
@ -325,7 +328,8 @@ void TableFinder::LocateTables(ColPartitionGrid *grid, ColPartitionSet **all_col
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
ScrollView *table_win = MakeWindow(1200, 300, "Detected Table Locations");
|
||||
ScrollView *table_win = MakeWindow(1200, 300,
|
||||
"Step 8: Detected Table Locations");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE);
|
||||
DisplayColSegments(table_win, &table_columns, ScrollView::KHAKI);
|
||||
table_grid_.DisplayBoxes(table_win);
|
||||
@ -339,8 +343,10 @@ void TableFinder::LocateTables(ColPartitionGrid *grid, ColPartitionSet **all_col
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
ScrollView *table_win = MakeWindow(1400, 600, "Recognized Tables");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE, ScrollView::BLUE);
|
||||
ScrollView *table_win = MakeWindow(1400, 600,
|
||||
"Step 10: Recognized Tables");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_,
|
||||
ScrollView::BLUE, ScrollView::BLUE);
|
||||
table_grid_.DisplayBoxes(table_win);
|
||||
}
|
||||
#endif // !GRAPHICS_DISABLED
|
||||
@ -353,8 +359,9 @@ void TableFinder::LocateTables(ColPartitionGrid *grid, ColPartitionSet **all_col
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
ScrollView *table_win = MakeWindow(1500, 300, "Detected Tables");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE, ScrollView::BLUE);
|
||||
ScrollView *table_win = MakeWindow(1500, 300, "Step 11: Detected Tables");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_,
|
||||
ScrollView::BLUE, ScrollView::BLUE);
|
||||
table_grid_.DisplayBoxes(table_win);
|
||||
}
|
||||
#endif // !GRAPHICS_DISABLED
|
||||
@ -773,7 +780,8 @@ void TableFinder::MarkTablePartitions() {
|
||||
MarkPartitionsUsingLocalInformation();
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_tablefind_show_mark) {
|
||||
ScrollView *table_win = MakeWindow(300, 300, "Initial Table Partitions");
|
||||
ScrollView *table_win = MakeWindow(300, 300,
|
||||
"Step 3: Initial Table Partitions");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE);
|
||||
DisplayColPartitions(table_win, &leader_and_ruling_grid_, ScrollView::AQUAMARINE);
|
||||
}
|
||||
@ -781,7 +789,8 @@ void TableFinder::MarkTablePartitions() {
|
||||
FilterFalseAlarms();
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_tablefind_show_mark) {
|
||||
ScrollView *table_win = MakeWindow(600, 300, "Filtered Table Partitions");
|
||||
ScrollView *table_win = MakeWindow(600, 300,
|
||||
"Step 4: Filtered Table Partitions");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE);
|
||||
DisplayColPartitions(table_win, &leader_and_ruling_grid_, ScrollView::AQUAMARINE);
|
||||
}
|
||||
@ -789,7 +798,8 @@ void TableFinder::MarkTablePartitions() {
|
||||
SmoothTablePartitionRuns();
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_tablefind_show_mark) {
|
||||
ScrollView *table_win = MakeWindow(900, 300, "Smoothed Table Partitions");
|
||||
ScrollView *table_win = MakeWindow(900, 300,
|
||||
"Step 5: Smoothed Table Partitions");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE);
|
||||
DisplayColPartitions(table_win, &leader_and_ruling_grid_, ScrollView::AQUAMARINE);
|
||||
}
|
||||
@ -797,7 +807,8 @@ void TableFinder::MarkTablePartitions() {
|
||||
FilterFalseAlarms();
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_tablefind_show_mark || textord_show_tables) {
|
||||
ScrollView *table_win = MakeWindow(900, 300, "Final Table Partitions");
|
||||
ScrollView *table_win = MakeWindow(900, 300,
|
||||
"Step 6: Final Table Partitions");
|
||||
DisplayColPartitions(table_win, &clean_part_grid_, ScrollView::BLUE);
|
||||
DisplayColPartitions(table_win, &leader_and_ruling_grid_, ScrollView::AQUAMARINE);
|
||||
}
|
||||
@ -1774,7 +1785,7 @@ void TableFinder::RecognizeTables() {
|
||||
ScrollView *table_win = nullptr;
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
table_win = MakeWindow(0, 0, "Table Structure");
|
||||
table_win = MakeWindow(0, 0, "Step 9: Table Structure");
|
||||
DisplayColPartitions(table_win, &fragmented_text_grid_, ScrollView::BLUE,
|
||||
ScrollView::LIGHT_BLUE);
|
||||
// table_grid_.DisplayBoxes(table_win);
|
||||
@ -1925,6 +1936,25 @@ void TableFinder::DisplayColPartitionConnections(ScrollView *win, ColPartitionGr
|
||||
// assigned to any table to their original types.
|
||||
void TableFinder::MakeTableBlocks(ColPartitionGrid *grid, ColPartitionSet **all_columns,
|
||||
WidthCallback width_cb) {
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
ScrollView* table_win = nullptr;
|
||||
if (textord_show_tables) {
|
||||
table_win = MakeWindow(0, 0, "Step 12: Final tables");
|
||||
DisplayColPartitions(table_win, &fragmented_text_grid_,
|
||||
ScrollView::BLUE, ScrollView::LIGHT_BLUE);
|
||||
}
|
||||
#endif // GRAPHICS_DISABLED
|
||||
|
||||
// initializing recognizer in order to extract table row and columnd info
|
||||
TableRecognizer recognizer;
|
||||
{
|
||||
recognizer.Init();
|
||||
recognizer.set_line_grid(&leader_and_ruling_grid_);
|
||||
recognizer.set_text_grid(&fragmented_text_grid_);
|
||||
recognizer.set_max_text_height(global_median_xheight_ * 2.0);
|
||||
recognizer.set_min_height(1.5 * gridheight());
|
||||
}
|
||||
|
||||
// Since we have table blocks already, remove table tags from all
|
||||
// colpartitions
|
||||
GridSearch<ColPartition, ColPartition_CLIST, ColPartition_C_IT> gsearch(grid);
|
||||
@ -1978,8 +2008,30 @@ void TableFinder::MakeTableBlocks(ColPartitionGrid *grid, ColPartitionSet **all_
|
||||
table_partition->set_flow(BTFT_CHAIN);
|
||||
table_partition->SetBlobTypes();
|
||||
grid->InsertBBox(true, true, table_partition);
|
||||
|
||||
// Insert table columns and rows into an api accessible object
|
||||
StructuredTable* table_structure = recognizer.RecognizeTable(table_box);
|
||||
if (table_structure != nullptr) {
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
table_structure->Display(table_win, ScrollView::LIME_GREEN);
|
||||
}
|
||||
#endif // GRAPHICS_DISABLED
|
||||
|
||||
std::vector<TessTable>& tables = uniqueInstance<std::vector<TessTable>>();
|
||||
tables.push_back(TessTable{table_box, table_structure->getRows(),
|
||||
table_structure->getCols()});
|
||||
|
||||
delete table_structure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (textord_show_tables) {
|
||||
table_grid_.DisplayBoxes(table_win);
|
||||
}
|
||||
#endif // GRAPHICS_DISABLED
|
||||
}
|
||||
|
||||
//////// ColSegment code
|
||||
|
@ -301,6 +301,37 @@ void StructuredTable::Display(ScrollView *window, ScrollView::Color color) {
|
||||
|
||||
#endif
|
||||
|
||||
std::vector<TBOX> StructuredTable::getRows()
|
||||
{
|
||||
if(cell_y_.size() < 2)
|
||||
return std::vector<TBOX>();
|
||||
|
||||
std::vector<TBOX> rows(cell_y_.size() - 1);
|
||||
unsigned ct = cell_y_.size() - 2;
|
||||
for(unsigned i = 0; i + 1 < cell_y_.size(); i++) {
|
||||
const ICOORD left(bounding_box_.left(), cell_y_[i]);
|
||||
const ICOORD right(bounding_box_.right(), cell_y_[i + 1]);
|
||||
rows[ct - i] = TBOX(left, right);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
std::vector<TBOX> StructuredTable::getCols()
|
||||
{
|
||||
if(cell_x_.size() < 2)
|
||||
return std::vector<TBOX>();
|
||||
|
||||
std::vector<TBOX> cols(cell_x_.size() - 1);
|
||||
for(unsigned i = 0; i + 1 < cell_x_.size(); i++) {
|
||||
const ICOORD top(cell_x_[i], bounding_box_.top());
|
||||
const ICOORD bot(cell_x_[i+1], bounding_box_.bottom());
|
||||
cols[i] = TBOX(top, bot);
|
||||
}
|
||||
|
||||
return cols;
|
||||
}
|
||||
|
||||
// Clear structure information.
|
||||
void StructuredTable::ClearStructure() {
|
||||
cell_x_.clear();
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define TABLERECOG_H_
|
||||
|
||||
#include "colpartitiongrid.h"
|
||||
#include <vector>
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
@ -134,6 +135,11 @@ public:
|
||||
// Debug display, draws the table in the given color. If the table is not
|
||||
// valid, the table and "best" grid lines are still drawn in the given color.
|
||||
void Display(ScrollView *window, ScrollView::Color color);
|
||||
|
||||
/// Calculate bounding boxes of the rows and return them.
|
||||
std::vector<TBOX> getRows();
|
||||
/// Calculate bounding boxes of the columns and return them.
|
||||
std::vector<TBOX> getCols();
|
||||
|
||||
protected:
|
||||
// Clear the structure information.
|
||||
|
Loading…
Reference in New Issue
Block a user