tesseract  5.0.0-alpha-619-ge9db
pagesegmain.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: pagesegmain.cpp
3  * Description: Top-level page segmenter for Tesseract.
4  * Author: Ray Smith
5  *
6  * (C) Copyright 2008, Google Inc.
7  ** Licensed under the Apache License, Version 2.0 (the "License");
8  ** you may not use this file except in compliance with the License.
9  ** You may obtain a copy of the License at
10  ** http://www.apache.org/licenses/LICENSE-2.0
11  ** Unless required by applicable law or agreed to in writing, software
12  ** distributed under the License is distributed on an "AS IS" BASIS,
13  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  ** See the License for the specific language governing permissions and
15  ** limitations under the License.
16  *
17  **********************************************************************/
18 
19 #ifdef _WIN32
20 #ifndef unlink
21 #include <io.h>
22 #endif
23 #else
24 #include <unistd.h>
25 #endif // _WIN32
26 
27 // Include automatically generated configuration file if running autoconf.
28 #ifdef HAVE_CONFIG_H
29 #include "config_auto.h"
30 #endif
31 
32 #include "allheaders.h"
33 #include "blobbox.h"
34 #include "blread.h"
35 #include "colfind.h"
36 #include "debugpixa.h"
37 #include "equationdetect.h"
38 #include "imagefind.h"
39 #include "linefind.h"
40 #include "makerow.h"
41 #include <tesseract/osdetect.h>
42 #include "tabvector.h"
43 #include "tesseractclass.h"
44 #include "tessvars.h"
45 #include "textord.h"
46 #include "tordmain.h"
47 #include "wordseg.h"
48 
49 namespace tesseract {
50 
51 // Max erosions to perform in removing an enclosing circle.
52 const int kMaxCircleErosions = 8;
53 
54 // Helper to remove an enclosing circle from an image.
55 // If there isn't one, then the image will most likely get badly mangled.
56 // The returned pix must be pixDestroyed after use. nullptr may be returned
57 // if the image doesn't meet the trivial conditions that it uses to determine
58 // success.
59 static Pix* RemoveEnclosingCircle(Pix* pixs) {
60  Pix* pixsi = pixInvert(nullptr, pixs);
61  Pix* pixc = pixCreateTemplate(pixs);
62  pixSetOrClearBorder(pixc, 1, 1, 1, 1, PIX_SET);
63  pixSeedfillBinary(pixc, pixc, pixsi, 4);
64  pixInvert(pixc, pixc);
65  pixDestroy(&pixsi);
66  Pix* pixt = pixAnd(nullptr, pixs, pixc);
67  l_int32 max_count;
68  pixCountConnComp(pixt, 8, &max_count);
69  // The count has to go up before we start looking for the minimum.
70  l_int32 min_count = INT32_MAX;
71  Pix* pixout = nullptr;
72  for (int i = 1; i < kMaxCircleErosions; i++) {
73  pixDestroy(&pixt);
74  pixErodeBrick(pixc, pixc, 3, 3);
75  pixt = pixAnd(nullptr, pixs, pixc);
76  l_int32 count;
77  pixCountConnComp(pixt, 8, &count);
78  if (i == 1 || count > max_count) {
79  max_count = count;
80  min_count = count;
81  } else if (i > 1 && count < min_count) {
82  min_count = count;
83  pixDestroy(&pixout);
84  pixout = pixCopy(nullptr, pixt); // Save the best.
85  } else if (count >= min_count) {
86  break; // We have passed by the best.
87  }
88  }
89  pixDestroy(&pixt);
90  pixDestroy(&pixc);
91  return pixout;
92 }
93 
99 int Tesseract::SegmentPage(const STRING* input_file, BLOCK_LIST* blocks,
100  Tesseract* osd_tess, OSResults* osr) {
101  ASSERT_HOST(pix_binary_ != nullptr);
102  int width = pixGetWidth(pix_binary_);
103  int height = pixGetHeight(pix_binary_);
104  // Get page segmentation mode.
105  auto pageseg_mode = static_cast<PageSegMode>(
106  static_cast<int>(tessedit_pageseg_mode));
107  // If a UNLV zone file can be found, use that instead of segmentation.
108  if (!PSM_COL_FIND_ENABLED(pageseg_mode) &&
109  input_file != nullptr && input_file->length() > 0) {
110  STRING name = *input_file;
111  const char* lastdot = strrchr(name.c_str(), '.');
112  if (lastdot != nullptr)
113  name[lastdot - name.c_str()] = '\0';
114  read_unlv_file(name, width, height, blocks);
115  }
116  if (blocks->empty()) {
117  // No UNLV file present. Work according to the PageSegMode.
118  // First make a single block covering the whole image.
119  BLOCK_IT block_it(blocks);
120  auto* block = new BLOCK("", true, 0, 0, 0, 0, width, height);
121  block->set_right_to_left(right_to_left());
122  block_it.add_to_end(block);
123  } else {
124  // UNLV file present. Use PSM_SINGLE_BLOCK.
125  pageseg_mode = PSM_SINGLE_BLOCK;
126  }
127  // The diacritic_blobs holds noise blobs that may be diacritics. They
128  // are separated out on areas of the image that seem noisy and short-circuit
129  // the layout process, going straight from the initial partition creation
130  // right through to after word segmentation, where they are added to the
131  // rej_cblobs list of the most appropriate word. From there classification
132  // will determine whether they are used.
133  BLOBNBOX_LIST diacritic_blobs;
134  int auto_page_seg_ret_val = 0;
135  TO_BLOCK_LIST to_blocks;
136  if (PSM_OSD_ENABLED(pageseg_mode) || PSM_BLOCK_FIND_ENABLED(pageseg_mode) ||
137  PSM_SPARSE(pageseg_mode)) {
138  auto_page_seg_ret_val = AutoPageSeg(
139  pageseg_mode, blocks, &to_blocks,
140  enable_noise_removal ? &diacritic_blobs : nullptr, osd_tess, osr);
141  if (pageseg_mode == PSM_OSD_ONLY)
142  return auto_page_seg_ret_val;
143  // To create blobs from the image region bounds uncomment this line:
144  // to_blocks.clear(); // Uncomment to go back to the old mode.
145  } else {
146  deskew_ = FCOORD(1.0f, 0.0f);
147  reskew_ = FCOORD(1.0f, 0.0f);
148  if (pageseg_mode == PSM_CIRCLE_WORD) {
149  Pix* pixcleaned = RemoveEnclosingCircle(pix_binary_);
150  if (pixcleaned != nullptr) {
151  pixDestroy(&pix_binary_);
152  pix_binary_ = pixcleaned;
153  }
154  }
155  }
156 
157  if (auto_page_seg_ret_val < 0) {
158  return -1;
159  }
160 
161  if (blocks->empty()) {
163  tprintf("Empty page\n");
164  return 0; // AutoPageSeg found an empty page.
165  }
166  bool splitting =
168  bool cjk_mode = textord_use_cjk_fp_model;
169 
170  textord_.TextordPage(pageseg_mode, reskew_, width, height, pix_binary_,
171  pix_thresholds_, pix_grey_, splitting || cjk_mode,
172  &diacritic_blobs, blocks, &to_blocks);
173  return auto_page_seg_ret_val;
174 }
175 
200 int Tesseract::AutoPageSeg(PageSegMode pageseg_mode, BLOCK_LIST* blocks,
201  TO_BLOCK_LIST* to_blocks,
202  BLOBNBOX_LIST* diacritic_blobs, Tesseract* osd_tess,
203  OSResults* osr) {
204  Pix* photomask_pix = nullptr;
205  Pix* musicmask_pix = nullptr;
206  // The blocks made by the ColumnFinder. Moved to blocks before return.
207  BLOCK_LIST found_blocks;
208  TO_BLOCK_LIST temp_blocks;
209 
210  ColumnFinder* finder = SetupPageSegAndDetectOrientation(
211  pageseg_mode, blocks, osd_tess, osr, &temp_blocks, &photomask_pix,
212  pageseg_apply_music_mask ? &musicmask_pix : nullptr);
213  int result = 0;
214  if (finder != nullptr) {
215  TO_BLOCK_IT to_block_it(&temp_blocks);
216  TO_BLOCK* to_block = to_block_it.data();
217  if (musicmask_pix != nullptr) {
218  // TODO(rays) pass the musicmask_pix into FindBlocks and mark music
219  // blocks separately. For now combine with photomask_pix.
220  pixOr(photomask_pix, photomask_pix, musicmask_pix);
221  }
222  if (equ_detect_) {
223  finder->SetEquationDetect(equ_detect_);
224  }
225  result = finder->FindBlocks(pageseg_mode, scaled_color_, scaled_factor_,
226  to_block, photomask_pix, pix_thresholds_,
227  pix_grey_, &pixa_debug_, &found_blocks,
228  diacritic_blobs, to_blocks);
229  if (result >= 0)
230  finder->GetDeskewVectors(&deskew_, &reskew_);
231  delete finder;
232  }
233  pixDestroy(&photomask_pix);
234  pixDestroy(&musicmask_pix);
235  if (result < 0) return result;
236 
237  blocks->clear();
238  BLOCK_IT block_it(blocks);
239  // Move the found blocks to the input/output blocks.
240  block_it.add_list_after(&found_blocks);
241  return result;
242 }
243 
244 // Helper adds all the scripts from sid_set converted to ids from osd_set to
245 // allowed_ids.
246 static void AddAllScriptsConverted(const UNICHARSET& sid_set,
247  const UNICHARSET& osd_set,
248  GenericVector<int>* allowed_ids) {
249  for (int i = 0; i < sid_set.get_script_table_size(); ++i) {
250  if (i != sid_set.null_sid()) {
251  const char* script = sid_set.get_script_from_script_id(i);
252  allowed_ids->push_back(osd_set.get_script_id_from_name(script));
253  }
254  }
255 }
256 
271  PageSegMode pageseg_mode, BLOCK_LIST* blocks, Tesseract* osd_tess,
272  OSResults* osr, TO_BLOCK_LIST* to_blocks, Pix** photo_mask_pix,
273  Pix** music_mask_pix) {
274  int vertical_x = 0;
275  int vertical_y = 1;
276  TabVector_LIST v_lines;
277  TabVector_LIST h_lines;
278  ICOORD bleft(0, 0);
279 
280  ASSERT_HOST(pix_binary_ != nullptr);
282  pixa_debug_.AddPix(pix_binary_, "PageSegInput");
283  }
284  // Leptonica is used to find the rule/separator lines in the input.
285  LineFinder::FindAndRemoveLines(source_resolution_,
286  textord_tabfind_show_vlines, pix_binary_,
287  &vertical_x, &vertical_y, music_mask_pix,
288  &v_lines, &h_lines);
290  pixa_debug_.AddPix(pix_binary_, "NoLines");
291  }
292  // Leptonica is used to find a mask of the photo regions in the input.
293  *photo_mask_pix = ImageFind::FindImages(pix_binary_, &pixa_debug_);
295  pixa_debug_.AddPix(pix_binary_, "NoImages");
296  }
297  if (!PSM_COL_FIND_ENABLED(pageseg_mode)) v_lines.clear();
298 
299  // The rest of the algorithm uses the usual connected components.
300  textord_.find_components(pix_binary_, blocks, to_blocks);
301 
302  TO_BLOCK_IT to_block_it(to_blocks);
303  // There must be exactly one input block.
304  // TODO(rays) handle new textline finding with a UNLV zone file.
305  ASSERT_HOST(to_blocks->singleton());
306  TO_BLOCK* to_block = to_block_it.data();
307  TBOX blkbox = to_block->block->pdblk.bounding_box();
308  ColumnFinder* finder = nullptr;
309  int estimated_resolution = source_resolution_;
310  if (source_resolution_ == kMinCredibleResolution) {
311  // Try to estimate resolution from typical body text size.
312  int res = IntCastRounded(to_block->line_size * kResolutionEstimationFactor);
313  if (res > estimated_resolution && res < kMaxCredibleResolution) {
314  estimated_resolution = res;
315  tprintf("Estimating resolution as %d\n", estimated_resolution);
316  }
317  }
318 
319  if (to_block->line_size >= 2) {
320  finder = new ColumnFinder(static_cast<int>(to_block->line_size),
321  blkbox.botleft(), blkbox.topright(),
322  estimated_resolution, textord_use_cjk_fp_model,
324  &h_lines, vertical_x, vertical_y);
325 
326  finder->SetupAndFilterNoise(pageseg_mode, *photo_mask_pix, to_block);
327 
328 #ifndef DISABLED_LEGACY_ENGINE
329 
330  if (equ_detect_) {
331  equ_detect_->LabelSpecialText(to_block);
332  }
333 
334  BLOBNBOX_CLIST osd_blobs;
335  // osd_orientation is the number of 90 degree rotations to make the
336  // characters upright. (See tesseract/osdetect.h for precise definition.)
337  // We want the text lines horizontal, (vertical text indicates vertical
338  // textlines) which may conflict (eg vertically written CJK).
339  int osd_orientation = 0;
340  bool vertical_text = textord_tabfind_force_vertical_text ||
341  pageseg_mode == PSM_SINGLE_BLOCK_VERT_TEXT;
342  if (!vertical_text && textord_tabfind_vertical_text &&
343  PSM_ORIENTATION_ENABLED(pageseg_mode)) {
344  vertical_text =
345  finder->IsVerticallyAlignedText(textord_tabfind_vertical_text_ratio,
346  to_block, &osd_blobs);
347  }
348  if (PSM_OSD_ENABLED(pageseg_mode) && osd_tess != nullptr && osr != nullptr) {
349  GenericVector<int> osd_scripts;
350  if (osd_tess != this) {
351  // We are running osd as part of layout analysis, so constrain the
352  // scripts to those allowed by *this.
353  AddAllScriptsConverted(unicharset, osd_tess->unicharset, &osd_scripts);
354  for (int s = 0; s < sub_langs_.size(); ++s) {
355  AddAllScriptsConverted(sub_langs_[s]->unicharset,
356  osd_tess->unicharset, &osd_scripts);
357  }
358  }
359  os_detect_blobs(&osd_scripts, &osd_blobs, osr, osd_tess);
360  if (pageseg_mode == PSM_OSD_ONLY) {
361  delete finder;
362  return nullptr;
363  }
364  osd_orientation = osr->best_result.orientation_id;
365  double osd_score = osr->orientations[osd_orientation];
366  double osd_margin = min_orientation_margin * 2;
367  for (int i = 0; i < 4; ++i) {
368  if (i != osd_orientation &&
369  osd_score - osr->orientations[i] < osd_margin) {
370  osd_margin = osd_score - osr->orientations[i];
371  }
372  }
373  int best_script_id = osr->best_result.script_id;
374  const char* best_script_str =
375  osd_tess->unicharset.get_script_from_script_id(best_script_id);
376  bool cjk = best_script_id == osd_tess->unicharset.han_sid() ||
377  best_script_id == osd_tess->unicharset.hiragana_sid() ||
378  best_script_id == osd_tess->unicharset.katakana_sid() ||
379  strcmp("Japanese", best_script_str) == 0 ||
380  strcmp("Korean", best_script_str) == 0 ||
381  strcmp("Hangul", best_script_str) == 0;
382  if (cjk) {
383  finder->set_cjk_script(true);
384  }
385  if (osd_margin < min_orientation_margin) {
386  // The margin is weak.
387  if (!cjk && !vertical_text && osd_orientation == 2) {
388  // upside down latin text is improbable with such a weak margin.
389  tprintf("OSD: Weak margin (%.2f), horiz textlines, not CJK: "
390  "Don't rotate.\n", osd_margin);
391  osd_orientation = 0;
392  } else {
393  tprintf(
394  "OSD: Weak margin (%.2f) for %d blob text block, "
395  "but using orientation anyway: %d\n",
396  osd_margin, osd_blobs.length(), osd_orientation);
397  }
398  }
399  }
400  osd_blobs.shallow_clear();
401  finder->CorrectOrientation(to_block, vertical_text, osd_orientation);
402 
403 #endif // ndef DISABLED_LEGACY_ENGINE
404  }
405 
406  return finder;
407 }
408 
409 } // namespace tesseract.
tesseract::Tesseract::SetupPageSegAndDetectOrientation
ColumnFinder * SetupPageSegAndDetectOrientation(PageSegMode pageseg_mode, BLOCK_LIST *blocks, Tesseract *osd_tess, OSResults *osr, TO_BLOCK_LIST *to_blocks, Pix **photo_mask_pix, Pix **music_mask_pix)
Definition: pagesegmain.cpp:284
os_detect_blobs
int os_detect_blobs(const GenericVector< int > *allowed_scripts, BLOBNBOX_CLIST *blob_list, OSResults *osr, tesseract::Tesseract *tess)
Definition: osdetect.cpp:278
tesseract::PSM_OSD_ENABLED
bool PSM_OSD_ENABLED(int pageseg_mode)
Definition: publictypes.h:188
OSResults::best_result
OSBestResult best_result
Definition: osdetect.h:81
tesseract::Tesseract::pageseg_devanagari_split_strategy
int pageseg_devanagari_split_strategy
Definition: tesseractclass.h:813
tessvars.h
tesseract::Tesseract::textord_use_cjk_fp_model
bool textord_use_cjk_fp_model
Definition: tesseractclass.h:1059
tesseract::PSM_COL_FIND_ENABLED
bool PSM_COL_FIND_ENABLED(int pageseg_mode)
Definition: publictypes.h:194
UNICHARSET::get_script_id_from_name
int get_script_id_from_name(const char *script_name) const
Definition: unicharset.cpp:1099
tesseractclass.h
ASSERT_HOST
#define ASSERT_HOST(x)
Definition: errcode.h:87
blread.h
linefind.h
tesseract::Textord::TextordPage
void TextordPage(PageSegMode pageseg_mode, const FCOORD &reskew, int width, int height, Pix *binary_pix, Pix *thresholds_pix, Pix *grey_pix, bool use_box_bottoms, BLOBNBOX_LIST *diacritic_blobs, BLOCK_LIST *blocks, TO_BLOCK_LIST *to_blocks)
Definition: textord.cpp:226
OSBestResult::script_id
int script_id
Definition: osdetect.h:45
blobbox.h
ICOORD
integer coordinate
Definition: points.h:30
kMaxCredibleResolution
constexpr int kMaxCredibleResolution
Definition: publictypes.h:39
tesseract::Tesseract::min_orientation_margin
double min_orientation_margin
Definition: tesseractclass.h:1057
OSResults
Definition: osdetect.h:50
STRING
Definition: strngs.h:45
UNICHARSET::get_script_from_script_id
const char * get_script_from_script_id(int id) const
Definition: unicharset.h:844
TO_BLOCK
Definition: blobbox.h:691
tesseract::PSM_SINGLE_BLOCK
Assume a single uniform block of text. (Default.)
Definition: publictypes.h:168
IntCastRounded
int IntCastRounded(double x)
Definition: helpers.h:173
tesseract::Tesseract::tessedit_pageseg_mode
int tessedit_pageseg_mode
Definition: tesseractclass.h:799
tesseract::Tesseract::SegmentPage
int SegmentPage(const STRING *input_file, BLOCK_LIST *blocks, Tesseract *osd_tess, OSResults *osr)
Definition: pagesegmain.cpp:113
FCOORD
Definition: points.h:187
tesseract::Tesseract::textord_tabfind_force_vertical_text
bool textord_tabfind_force_vertical_text
Definition: tesseractclass.h:1068
tesseract::Tesseract::enable_noise_removal
bool enable_noise_removal
Definition: tesseractclass.h:856
tesseract::CCUtil::unicharset
UNICHARSET unicharset
Definition: ccutil.h:57
tesseract::Tesseract::AutoPageSeg
int AutoPageSeg(PageSegMode pageseg_mode, BLOCK_LIST *blocks, TO_BLOCK_LIST *to_blocks, BLOBNBOX_LIST *diacritic_blobs, Tesseract *osd_tess, OSResults *osr)
Definition: pagesegmain.cpp:214
tesseract::kMaxCircleErosions
const int kMaxCircleErosions
Definition: pagesegmain.cpp:66
tesseract::Tesseract::textord_tabfind_vertical_text
bool textord_tabfind_vertical_text
Definition: tesseractclass.h:1066
tesseract::DebugPixa::AddPix
void AddPix(const Pix *pix, const char *caption)
Definition: debugpixa.h:26
tesseract::LineFinder::FindAndRemoveLines
static void FindAndRemoveLines(int resolution, bool debug, Pix *pix, int *vertical_x, int *vertical_y, Pix **pix_music_mask, TabVector_LIST *v_lines, TabVector_LIST *h_lines)
Definition: linefind.cpp:243
OSBestResult::orientation_id
int orientation_id
Definition: osdetect.h:44
tesseract::Textord::find_components
void find_components(Pix *pix, BLOCK_LIST *blocks, TO_BLOCK_LIST *to_blocks)
Definition: tordmain.cpp:215
tesseract::PSM_SPARSE
bool PSM_SPARSE(int pageseg_mode)
Definition: publictypes.h:197
debugpixa.h
GenericVector::push_back
int push_back(T object)
Definition: genericvector.h:799
BLOCK
Definition: ocrblock.h:28
tesseract::PSM_SINGLE_BLOCK_VERT_TEXT
Definition: publictypes.h:166
colfind.h
tesseract::Tesseract::pageseg_apply_music_mask
bool pageseg_apply_music_mask
Definition: tesseractclass.h:1097
makerow.h
STRING::c_str
const char * c_str() const
Definition: strngs.cpp:192
UNICHARSET::null_sid
int null_sid() const
Definition: unicharset.h:874
equationdetect.h
tesseract::PSM_BLOCK_FIND_ENABLED
bool PSM_BLOCK_FIND_ENABLED(int pageseg_mode)
Definition: publictypes.h:200
tesseract::Tesseract::tessedit_dump_pageseg_images
bool tessedit_dump_pageseg_images
Definition: tesseractclass.h:793
kResolutionEstimationFactor
constexpr int kResolutionEstimationFactor
Definition: publictypes.h:44
kMinCredibleResolution
constexpr int kMinCredibleResolution
Definition: publictypes.h:37
UNICHARSET
Definition: unicharset.h:145
textord.h
TBOX::topright
const ICOORD & topright() const
Definition: rect.h:103
tesseract
Definition: baseapi.h:65
OSResults::orientations
float orientations[4]
Definition: osdetect.h:76
TBOX::botleft
const ICOORD & botleft() const
Definition: rect.h:91
tesseract::Tesseract::textord_tabfind_show_vlines
bool textord_tabfind_show_vlines
Definition: tesseractclass.h:1058
read_unlv_file
bool read_unlv_file(STRING name, int32_t xsize, int32_t ysize, BLOCK_LIST *blocks)
Definition: blread.cpp:31
GenericVector< int >
UNICHARSET::get_script_table_size
int get_script_table_size() const
Definition: unicharset.h:839
tesseract::EquationDetect::LabelSpecialText
int LabelSpecialText(TO_BLOCK *to_block) override
Definition: equationdetect.cpp:131
STRING::length
int32_t length() const
Definition: strngs.cpp:187
count
int count(LIST var_list)
Definition: oldlist.cpp:79
wordseg.h
tesseract::PageSegMode
PageSegMode
Definition: publictypes.h:159
tesseract::Tesseract::textord_tabfind_vertical_text_ratio
double textord_tabfind_vertical_text_ratio
Definition: tesseractclass.h:1071
imagefind.h
tesseract::ImageFind::FindImages
static Pix * FindImages(Pix *pix, DebugPixa *pixa_debug)
Definition: imagefind.cpp:62
tesseract::Tesseract::textord_tabfind_aligned_gap_fraction
double textord_tabfind_aligned_gap_fraction
Definition: tesseractclass.h:1073
tesseract::PSM_ORIENTATION_ENABLED
bool PSM_ORIENTATION_ENABLED(int pageseg_mode)
Definition: publictypes.h:191
tesseract::PSM_CIRCLE_WORD
Treat the image as a single word in a circle.
Definition: publictypes.h:171
tprintf
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:34
tesseract::PSM_OSD_ONLY
Orientation and script detection only.
Definition: publictypes.h:160
tordmain.h
osdetect.h
textord_debug_tabfind
int textord_debug_tabfind
Definition: alignedblob.cpp:27
tabvector.h
tesseract::ShiroRekhaSplitter::NO_SPLIT
Definition: devanagari_processing.h:74
tesseract::Tesseract::right_to_left
bool right_to_left() const
Definition: tesseractclass.h:273
TBOX
Definition: rect.h:33