Coverage Report

Created: 2026-09-14 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tesseract/src/textord/colfind.h
Line
Count
Source
1
///////////////////////////////////////////////////////////////////////
2
// File:        colfind.h
3
// Description: Class to find columns in the grid of BLOBNBOXes.
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
#ifndef TESSERACT_TEXTORD_COLFIND_H_
20
#define TESSERACT_TEXTORD_COLFIND_H_
21
22
#include "colpartitiongrid.h"
23
#include "colpartitionset.h"
24
#include "debugpixa.h"
25
#include "imagefind.h"
26
#include "ocrblock.h"
27
#include "tabfind.h"
28
#include "textlineprojection.h"
29
30
class BLOCK_LIST;
31
struct Boxa;
32
struct Pixa;
33
class DENORM;
34
class ScrollView;
35
class STATS;
36
class TO_BLOCK;
37
38
namespace tesseract {
39
40
class ColPartitionSet;
41
class ColPartitionSet_LIST;
42
class ColSegment_LIST;
43
class ColumnGroup_LIST;
44
class LineSpacing;
45
class StrokeWidth;
46
class TempColumn_LIST;
47
class EquationDetectBase;
48
49
// The ColumnFinder class finds columns in the grid.
50
class TESS_API ColumnFinder : public TabFind {
51
public:
52
  // Gridsize is an estimate of the text size in the image. A suitable value
53
  // is in TO_BLOCK::line_size after find_components has been used to make
54
  // the blobs.
55
  // bleft and tright are the bounds of the image (rectangle) being processed.
56
  // vlines is a (possibly empty) list of TabVector and vertical_x and y are
57
  // the sum logical vertical vector produced by LineFinder::FindVerticalLines.
58
  // If cjk_script is true, then broken CJK characters are fixed during
59
  // layout analysis to assist in detecting horizontal vs vertically written
60
  // textlines.
61
  ColumnFinder(int gridsize, const ICOORD &bleft, const ICOORD &tright, int resolution,
62
               bool cjk_script, double aligned_gap_fraction, TabVector_LIST *vlines,
63
               TabVector_LIST *hlines, int vertical_x, int vertical_y);
64
  ~ColumnFinder() override;
65
66
  // Accessors for testing
67
0
  const DENORM *denorm() const {
68
0
    return denorm_;
69
0
  }
70
0
  const TextlineProjection *projection() const {
71
0
    return &projection_;
72
0
  }
73
0
  void set_cjk_script(bool is_cjk) {
74
0
    cjk_script_ = is_cjk;
75
0
  }
76
77
  // ======================================================================
78
  // The main function of ColumnFinder is broken into pieces to facilitate
79
  // optional insertion of orientation and script detection in an efficient
80
  // way. The calling sequence IS MANDATORY however, whether or not
81
  // OSD is being used:
82
  // 1. Construction.
83
  // 2. SetupAndFilterNoise.
84
  // 3. IsVerticallyAlignedText.
85
  // 4. CorrectOrientation.
86
  // 5. FindBlocks.
87
  // 6. Destruction. Use of a single column finder for multiple images does not
88
  //    make sense.
89
  // Throughout these steps, the ColPartitions are owned by part_grid_, which
90
  // means that it must be kept correct. Exception: big_parts_ owns its
91
  // own ColPartitions.
92
  // The BLOBNBOXes are owned by the input TO_BLOCK for the whole time, except
93
  // for a phase in FindBlocks before TransformToBlocks, when they become
94
  // owned by the ColPartitions. The owner() ColPartition of a BLOBNBOX
95
  // indicates more of a betrothal for the majority of layout analysis, ie
96
  // which ColPartition will take ownership when the blobs are release from
97
  // the input TO_BLOCK. Exception: image_bblobs_ owns the fake blobs that
98
  // are part of the image regions, as they are not on any TO_BLOCK list.
99
  // TODO(rays) break up column finder further into smaller classes, as
100
  // there is a lot more to it than column finding now.
101
  // ======================================================================
102
103
  // Performs initial processing on the blobs in the input_block:
104
  // Setup the part_grid, stroke_width_, nontext_map_.
105
  // Obvious noise blobs are filtered out and used to mark the nontext_map_.
106
  // Initial stroke-width analysis is used to get local text alignment
107
  // direction, so the textline projection_ map can be setup.
108
  // On return, IsVerticallyAlignedText may be called (now optionally) to
109
  // determine the gross textline alignment of the page.
110
  void SetupAndFilterNoise(PageSegMode pageseg_mode, Image photo_mask_pix, TO_BLOCK *input_block);
111
112
  // Tests for vertical alignment of text (returning true if so), and generates
113
  // a list of blobs (in osd_blobs) for orientation and script detection.
114
  // block is the single block for the whole page or rectangle to be OCRed.
115
  // Note that the vertical alignment may be due to text whose writing direction
116
  // is vertical, like say Japanese, or due to text whose writing direction is
117
  // horizontal but whose text appears vertically aligned because the image is
118
  // not the right way up.
119
  // find_vertical_text_ratio should be textord_tabfind_vertical_text_ratio.
120
  bool IsVerticallyAlignedText(double find_vertical_text_ratio, TO_BLOCK *block,
121
                               BLOBNBOX_CLIST *osd_blobs);
122
123
  // Rotates the blobs and the TabVectors so that the gross writing direction
124
  // (text lines) are horizontal and lines are read down the page.
125
  // Applied rotation stored in rotation_.
126
  // A second rotation is calculated for application during recognition to
127
  // make the rotated blobs upright for recognition.
128
  // Subsequent rotation stored in text_rotation_.
129
  //
130
  // Arguments:
131
  //   vertical_text_lines is true if the text lines are vertical.
132
  //   recognition_rotation [0..3] is the number of anti-clockwise 90 degree
133
  //   rotations from osd required for the text to be upright and readable.
134
  void CorrectOrientation(TO_BLOCK *block, bool vertical_text_lines, int recognition_rotation);
135
136
  // Finds blocks of text, image, rule line, table etc, returning them in the
137
  // blocks and to_blocks
138
  // (Each TO_BLOCK points to the basic BLOCK and adds more information.)
139
  // Image blocks are generated by a combination of photo_mask_pix (which may
140
  // NOT be nullptr) and the rejected text found during preliminary textline
141
  // finding.
142
  // The input_block is the result of a call to find_components, and contains
143
  // the blobs found in the image or rectangle to be OCRed. These blobs will be
144
  // removed and placed in the output blocks, while unused ones will be deleted.
145
  // If single_column is true, the input is treated as single column, but
146
  // it is still divided into blocks of equal line spacing/text size.
147
  // grey_pix is optional, but if present must match the photo_mask_pix in size,
148
  // and must be a *real* grey image instead of binary_pix * 255.
149
  // thresholds_pix is expected to be present iff grey_pix is present and
150
  // can be an integer factor reduction of the grey_pix. It represents the
151
  // thresholds that were used to create the binary_pix from the grey_pix.
152
  // Small blobs that confuse the segmentation into lines are placed into
153
  // diacritic_blobs, with the intention that they be put into the most
154
  // appropriate word after the rest of layout analysis.
155
  // Returns -1 if the user hits the 'd' key in the blocks window while running
156
  // in debug mode, which requests a retry with more debug info.
157
  int FindBlocks(PageSegMode pageseg_mode, TO_BLOCK *block,
158
                 Image photo_mask_pix, Image thresholds_pix, Image grey_pix, DebugPixa *pixa_debug,
159
                 BLOCK_LIST *blocks, BLOBNBOX_LIST *diacritic_blobs, TO_BLOCK_LIST *to_blocks);
160
161
  // Get the rotation required to deskew, and its inverse rotation.
162
  void GetDeskewVectors(FCOORD *deskew, FCOORD *reskew);
163
164
  // Set the equation detection pointer.
165
  void SetEquationDetect(EquationDetectBase *detect);
166
167
private:
168
  // Displays the blob and block bounding boxes in a window called Blocks.
169
  void DisplayBlocks(BLOCK_LIST *blocks);
170
  // Displays the column edges at each grid y coordinate defined by
171
  // best_columns_.
172
  void DisplayColumnBounds();
173
174
  ////// Functions involved in determining the columns used on the page. /////
175
176
  // Sets up column_sets_ (the determined column layout at each horizontal
177
  // slice). Returns false if the page is empty.
178
  bool MakeColumns(bool single_column);
179
  // Attempt to improve the column_candidates by expanding the columns
180
  // and adding new partitions from the partition sets in src_sets.
181
  // Src_sets may be equal to column_candidates, in which case it will
182
  // use them as a source to improve themselves.
183
  void ImproveColumnCandidates(PartSetVector *src_sets, PartSetVector *column_sets);
184
  // Prints debug information on the column candidates.
185
  void PrintColumnCandidates(const char *title);
186
  // Finds the optimal set of columns that cover the entire image with as
187
  // few changes in column partition as possible.
188
  // Returns true if any part of the page is multi-column.
189
  bool AssignColumns(const PartSetVector &part_sets);
190
  // Finds the biggest range in part_sets_ that has no assigned column, but
191
  // column assignment is possible.
192
  bool BiggestUnassignedRange(int set_count, const bool *any_columns_possible, int *start,
193
                              int *end);
194
  // Finds the modal compatible column_set_ index within the given range.
195
  int RangeModalColumnSet(int **column_set_costs, const int *assigned_costs, int start, int end);
196
  // Given that there are many column_set_id compatible columns in the range,
197
  // shrinks the range to the longest contiguous run of compatibility, allowing
198
  // gaps where no columns are possible, but not where competing columns are
199
  // possible.
200
  void ShrinkRangeToLongestRun(int **column_set_costs, const int *assigned_costs,
201
                               const bool *any_columns_possible, int column_set_id, int *best_start,
202
                               int *best_end);
203
  // Moves start in the direction of step, up to, but not including end while
204
  // the only incompatible regions are no more than kMaxIncompatibleColumnCount
205
  // in size, and the compatible regions beyond are bigger.
206
  void ExtendRangePastSmallGaps(int **column_set_costs, const int *assigned_costs,
207
                                const bool *any_columns_possible, int column_set_id, int step,
208
                                int end, int *start);
209
  // Assigns the given column_set_id to the part_sets_ in the given range.
210
  void AssignColumnToRange(int column_set_id, int start, int end, int **column_set_costs,
211
                           int *assigned_costs);
212
213
  // Computes the mean_column_gap_.
214
  void ComputeMeanColumnGap(bool any_multi_column);
215
216
  //////// Functions that manipulate ColPartitions in the part_grid_ /////
217
  //////// to split, merge, find margins, and find types.  //////////////
218
219
  // Hoovers up all un-owned blobs and deletes them.
220
  // The rest get released from the block so the ColPartitions can pass
221
  // ownership to the output blocks.
222
  void ReleaseBlobsAndCleanupUnused(TO_BLOCK *block);
223
  // Splits partitions that cross columns where they have nothing in the gap.
224
  void GridSplitPartitions();
225
  // Merges partitions where there is vertical overlap, within a single column,
226
  // and the horizontal gap is small enough.
227
  void GridMergePartitions();
228
  // Inserts remaining noise blobs into the most applicable partition if any.
229
  // If there is no applicable partition, then the blobs are deleted.
230
  void InsertRemainingNoise(TO_BLOCK *block);
231
  // Remove partitions that come from horizontal lines that look like
232
  // underlines, but are not part of a table.
233
  void GridRemoveUnderlinePartitions();
234
  // Add horizontal line separators as partitions.
235
  void GridInsertHLinePartitions();
236
  // Add vertical line separators as partitions.
237
  void GridInsertVLinePartitions();
238
  // For every ColPartition in the grid, sets its type based on position
239
  // in the columns.
240
  void SetPartitionTypes();
241
  // Only images remain with multiple types in a run of partners.
242
  // Sets the type of all in the group to the maximum of the group.
243
  void SmoothPartnerRuns();
244
245
  //////// Functions that make the final output blocks             ///////
246
247
  // Helper functions for TransformToBlocks.
248
  // Add the part to the temp list in the correct order.
249
  void AddToTempPartList(ColPartition *part, ColPartition_CLIST *temp_list);
250
  // Add everything from the temp list to the work_set assuming correct order.
251
  void EmptyTempPartList(ColPartition_CLIST *temp_list, WorkingPartSet_LIST *work_set);
252
253
  // Transform the grid of partitions to the output blocks.
254
  void TransformToBlocks(BLOCK_LIST *blocks, TO_BLOCK_LIST *to_blocks);
255
256
  // Reflect the blob boxes (but not the outlines) in the y-axis so that
257
  // the blocks get created in the correct RTL order. Rotates the blobs
258
  // in the input_block and the bblobs list.
259
  // The reflection is undone in RotateAndReskewBlocks by
260
  // reflecting the blocks themselves, and then recomputing the blob bounding
261
  //  boxes.
262
  void ReflectForRtl(TO_BLOCK *input_block, BLOBNBOX_LIST *bblobs);
263
264
  // Undo the deskew that was done in FindTabVectors, as recognition is done
265
  // without correcting blobs or blob outlines for skew.
266
  // Reskew the completed blocks to put them back to the original rotated coords
267
  // that were created by CorrectOrientation.
268
  // If the input_is_rtl, then reflect the blocks in the y-axis to undo the
269
  // reflection that was done before FindTabVectors.
270
  // Blocks that were identified as vertical text (relative to the rotated
271
  // coordinates) are further rotated so the text lines are horizontal.
272
  // blob polygonal outlines are rotated to match the position of the blocks
273
  // that they are in, and their bounding boxes are recalculated to be accurate.
274
  // Record appropriate inverse transformations and required
275
  // classifier transformation in the blocks.
276
  void RotateAndReskewBlocks(bool input_is_rtl, TO_BLOCK_LIST *to_blocks);
277
278
  // Computes the rotations for the block (to make textlines horizontal) and
279
  // for the blobs (for classification) and sets the appropriate members
280
  // of the given block.
281
  // Returns the rotation that needs to be applied to the blobs to make
282
  // them sit in the rotated block.
283
  FCOORD ComputeBlockAndClassifyRotation(BLOCK *block);
284
285
  // If true then the page language is cjk, so it is safe to perform
286
  // FixBrokenCJK.
287
  bool cjk_script_;
288
  // The minimum gutter width to apply for finding columns.
289
  // Modified when vertical text is detected to prevent detection of
290
  // vertical text lines as columns.
291
  int min_gutter_width_;
292
  // The mean gap between columns over the page.
293
  int mean_column_gap_;
294
  // Config param saved at construction time. Modifies min_gutter_width_ with
295
  // vertical text to prevent detection of vertical text as columns.
296
  double tabfind_aligned_gap_fraction_;
297
  // The rotation vector needed to convert original coords to deskewed.
298
  FCOORD deskew_;
299
  // The rotation vector needed to convert deskewed back to original coords.
300
  FCOORD reskew_;
301
  // The rotation vector used to rotate vertically oriented pages.
302
  FCOORD rotation_;
303
  // The rotation vector needed to convert the rotated back to original coords.
304
  FCOORD rerotate_;
305
  // The additional rotation vector needed to rotate text for recognition.
306
  FCOORD text_rotation_;
307
  // The column_sets_ contain the ordered candidate ColPartitionSets that
308
  // define the possible divisions of the page into columns.
309
  PartSetVector column_sets_;
310
  // A simple array of pointers to the best assigned column division at
311
  // each grid y coordinate.
312
  ColPartitionSet **best_columns_;
313
  // The grid used for creating initial partitions with strokewidth.
314
  StrokeWidth *stroke_width_;
315
  // The grid used to hold ColPartitions after the columns have been determined.
316
  ColPartitionGrid part_grid_;
317
  // List of ColPartitions that are no longer needed after they have been
318
  // turned into regions, but are kept around because they are referenced
319
  // by the part_grid_.
320
  ColPartition_LIST good_parts_;
321
  // List of ColPartitions that are big and might be dropcap or vertically
322
  // joined.
323
  ColPartition_LIST big_parts_;
324
  // List of ColPartitions that have been declared noise.
325
  ColPartition_LIST noise_parts_;
326
  // The fake blobs that are made from the images.
327
  BLOBNBOX_LIST image_bblobs_;
328
  // Horizontal line separators.
329
  TabVector_LIST horizontal_lines_;
330
  // Image map of photo/noise areas on the page.
331
  Image nontext_map_;
332
  // Textline projection map.
333
  TextlineProjection projection_;
334
  // Sequence of DENORMS that indicate how to get back to the original image
335
  // coordinate space. The destructor must delete all the DENORMs in the chain.
336
  DENORM *denorm_;
337
338
  // The equation region detector pointer. Note: This pointer is passed in by
339
  // member function SetEquationDetect, and releasing it is NOT owned by this
340
  // class.
341
  EquationDetectBase *equation_detect_;
342
343
#ifndef GRAPHICS_DISABLED
344
  // Various debug windows that automatically go away on completion.
345
  ScrollView *input_blobs_win_ = nullptr;
346
347
  // Allow a subsequent instance to reuse the blocks window.
348
  // Not thread-safe, but multiple threads shouldn't be using windows anyway.
349
  static ScrollView *blocks_win_;
350
#endif
351
};
352
353
} // namespace tesseract.
354
355
#endif // TESSERACT_TEXTORD_COLFIND_H_