Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/tesseract/src/textord/colpartition.h @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /////////////////////////////////////////////////////////////////////// | |
| 2 // File: colpartition.h | |
| 3 // Description: Class to hold partitions of the page that correspond | |
| 4 // roughly to text lines. | |
| 5 // Author: Ray Smith | |
| 6 // | |
| 7 // (C) Copyright 2008, Google Inc. | |
| 8 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 9 // you may not use this file except in compliance with the License. | |
| 10 // You may obtain a copy of the License at | |
| 11 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 12 // Unless required by applicable law or agreed to in writing, software | |
| 13 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 15 // See the License for the specific language governing permissions and | |
| 16 // limitations under the License. | |
| 17 // | |
| 18 /////////////////////////////////////////////////////////////////////// | |
| 19 | |
| 20 #ifndef TESSERACT_TEXTORD_COLPARTITION_H_ | |
| 21 #define TESSERACT_TEXTORD_COLPARTITION_H_ | |
| 22 | |
| 23 #include "bbgrid.h" | |
| 24 #include "blobbox.h" // For BlobRegionType. | |
| 25 #include "ocrblock.h" | |
| 26 #include "rect.h" // For TBOX. | |
| 27 #include "scrollview.h" | |
| 28 #include "tabfind.h" // For WidthCallback. | |
| 29 #include "tabvector.h" // For BLOBNBOX_CLIST. | |
| 30 | |
| 31 #include <algorithm> | |
| 32 | |
| 33 namespace tesseract { | |
| 34 | |
| 35 // Number of colors in the color1, color2 arrays. | |
| 36 const int kRGBRMSColors = 4; | |
| 37 | |
| 38 class ColPartition; | |
| 39 class ColPartitionSet; | |
| 40 class ColPartitionGrid; | |
| 41 class WorkingPartSet; | |
| 42 class WorkingPartSet_LIST; | |
| 43 | |
| 44 // An enum to indicate how a partition sits on the columns. | |
| 45 // The order of flowing/heading/pullout must be kept consistent with | |
| 46 // PolyBlockType. | |
| 47 enum ColumnSpanningType { | |
| 48 CST_NOISE, // Strictly between columns. | |
| 49 CST_FLOWING, // Strictly within a single column. | |
| 50 CST_HEADING, // Spans multiple columns. | |
| 51 CST_PULLOUT, // Touches multiple columns, but doesn't span them. | |
| 52 CST_COUNT // Number of entries. | |
| 53 }; | |
| 54 | |
| 55 ELIST2IZEH(ColPartition) | |
| 56 CLISTIZEH(ColPartition) | |
| 57 | |
| 58 /** | |
| 59 * ColPartition is a partition of a horizontal slice of the page. | |
| 60 * It starts out as a collection of blobs at a particular y-coord in the grid, | |
| 61 * but ends up (after merging and uniquing) as an approximate text line. | |
| 62 * ColPartitions are also used to hold a partitioning of the page into | |
| 63 * columns, each representing one column. Although a ColPartition applies | |
| 64 * to a given y-coordinate range, eventually, a ColPartitionSet of ColPartitions | |
| 65 * emerges, which represents the columns over a wide y-coordinate range. | |
| 66 */ | |
| 67 class TESS_API ColPartition : public ELIST2_LINK { | |
| 68 public: | |
| 69 // This empty constructor is here only so that the class can be ELISTIZED. | |
| 70 // TODO(rays) change deep_copy in elst.h line 955 to take a callback copier | |
| 71 // and eliminate CLASSNAME##_copier. | |
| 72 ColPartition() = default; | |
| 73 | |
| 74 /** | |
| 75 * @param blob_type is the blob_region_type_ of the blobs in this partition. | |
| 76 * @param vertical is the direction of logical vertical on the possibly skewed | |
| 77 * image. | |
| 78 */ | |
| 79 ColPartition(BlobRegionType blob_type, const ICOORD &vertical); | |
| 80 /** | |
| 81 * Constructs a fake ColPartition with no BLOBNBOXes to represent a | |
| 82 * horizontal or vertical line, given a type and a bounding box. | |
| 83 */ | |
| 84 static ColPartition *MakeLinePartition(BlobRegionType blob_type, | |
| 85 const ICOORD &vertical, int left, | |
| 86 int bottom, int right, int top); | |
| 87 | |
| 88 // Constructs and returns a fake ColPartition with a single fake BLOBNBOX, | |
| 89 // all made from a single TBOX. | |
| 90 // WARNING: Despite being on C_LISTs, the BLOBNBOX owns the C_BLOB and | |
| 91 // the ColPartition owns the BLOBNBOX!!! | |
| 92 // Call DeleteBoxes before deleting the ColPartition. | |
| 93 static ColPartition *FakePartition(const TBOX &box, PolyBlockType block_type, | |
| 94 BlobRegionType blob_type, | |
| 95 BlobTextFlowType flow); | |
| 96 | |
| 97 // Constructs and returns a ColPartition with the given real BLOBNBOX, | |
| 98 // and sets it up to be a "big" partition (single-blob partition bigger | |
| 99 // than the surrounding text that may be a dropcap, two or more vertically | |
| 100 // touching characters, or some graphic element. | |
| 101 // If the given list is not nullptr, the partition is also added to the list. | |
| 102 static ColPartition *MakeBigPartition(BLOBNBOX *box, | |
| 103 ColPartition_LIST *big_part_list); | |
| 104 | |
| 105 ~ColPartition(); | |
| 106 | |
| 107 // Simple accessors. | |
| 108 const TBOX &bounding_box() const { | |
| 109 return bounding_box_; | |
| 110 } | |
| 111 int left_margin() const { | |
| 112 return left_margin_; | |
| 113 } | |
| 114 void set_left_margin(int margin) { | |
| 115 left_margin_ = margin; | |
| 116 } | |
| 117 int right_margin() const { | |
| 118 return right_margin_; | |
| 119 } | |
| 120 void set_right_margin(int margin) { | |
| 121 right_margin_ = margin; | |
| 122 } | |
| 123 int median_top() const { | |
| 124 return median_top_; | |
| 125 } | |
| 126 int median_bottom() const { | |
| 127 return median_bottom_; | |
| 128 } | |
| 129 int median_left() const { | |
| 130 return median_left_; | |
| 131 } | |
| 132 int median_right() const { | |
| 133 return median_right_; | |
| 134 } | |
| 135 int median_height() const { | |
| 136 return median_height_; | |
| 137 } | |
| 138 void set_median_height(int height) { | |
| 139 median_height_ = height; | |
| 140 } | |
| 141 int median_width() const { | |
| 142 return median_width_; | |
| 143 } | |
| 144 void set_median_width(int width) { | |
| 145 median_width_ = width; | |
| 146 } | |
| 147 BlobRegionType blob_type() const { | |
| 148 return blob_type_; | |
| 149 } | |
| 150 void set_blob_type(BlobRegionType t) { | |
| 151 blob_type_ = t; | |
| 152 } | |
| 153 BlobTextFlowType flow() const { | |
| 154 return flow_; | |
| 155 } | |
| 156 void set_flow(BlobTextFlowType f) { | |
| 157 flow_ = f; | |
| 158 } | |
| 159 int good_blob_score() const { | |
| 160 return good_blob_score_; | |
| 161 } | |
| 162 bool good_width() const { | |
| 163 return good_width_; | |
| 164 } | |
| 165 bool good_column() const { | |
| 166 return good_column_; | |
| 167 } | |
| 168 bool left_key_tab() const { | |
| 169 return left_key_tab_; | |
| 170 } | |
| 171 int left_key() const { | |
| 172 return left_key_; | |
| 173 } | |
| 174 bool right_key_tab() const { | |
| 175 return right_key_tab_; | |
| 176 } | |
| 177 int right_key() const { | |
| 178 return right_key_; | |
| 179 } | |
| 180 PolyBlockType type() const { | |
| 181 return type_; | |
| 182 } | |
| 183 void set_type(PolyBlockType t) { | |
| 184 type_ = t; | |
| 185 } | |
| 186 BLOBNBOX_CLIST *boxes() { | |
| 187 return &boxes_; | |
| 188 } | |
| 189 int boxes_count() const { | |
| 190 return boxes_.length(); | |
| 191 } | |
| 192 void set_vertical(const ICOORD &v) { | |
| 193 vertical_ = v; | |
| 194 } | |
| 195 ColPartition_CLIST *upper_partners() { | |
| 196 return &upper_partners_; | |
| 197 } | |
| 198 ColPartition_CLIST *lower_partners() { | |
| 199 return &lower_partners_; | |
| 200 } | |
| 201 void set_working_set(WorkingPartSet *working_set) { | |
| 202 working_set_ = working_set; | |
| 203 } | |
| 204 bool block_owned() const { | |
| 205 return block_owned_; | |
| 206 } | |
| 207 void set_block_owned(bool owned) { | |
| 208 block_owned_ = owned; | |
| 209 } | |
| 210 bool desperately_merged() const { | |
| 211 return desperately_merged_; | |
| 212 } | |
| 213 ColPartitionSet *column_set() const { | |
| 214 return column_set_; | |
| 215 } | |
| 216 void set_side_step(int step) { | |
| 217 side_step_ = step; | |
| 218 } | |
| 219 int bottom_spacing() const { | |
| 220 return bottom_spacing_; | |
| 221 } | |
| 222 void set_bottom_spacing(int spacing) { | |
| 223 bottom_spacing_ = spacing; | |
| 224 } | |
| 225 int top_spacing() const { | |
| 226 return top_spacing_; | |
| 227 } | |
| 228 void set_top_spacing(int spacing) { | |
| 229 top_spacing_ = spacing; | |
| 230 } | |
| 231 | |
| 232 void set_table_type() { | |
| 233 if (type_ != PT_TABLE) { | |
| 234 type_before_table_ = type_; | |
| 235 type_ = PT_TABLE; | |
| 236 } | |
| 237 } | |
| 238 void clear_table_type() { | |
| 239 if (type_ == PT_TABLE) { | |
| 240 type_ = type_before_table_; | |
| 241 } | |
| 242 } | |
| 243 bool inside_table_column() { | |
| 244 return inside_table_column_; | |
| 245 } | |
| 246 void set_inside_table_column(bool val) { | |
| 247 inside_table_column_ = val; | |
| 248 } | |
| 249 ColPartition *nearest_neighbor_above() const { | |
| 250 return nearest_neighbor_above_; | |
| 251 } | |
| 252 void set_nearest_neighbor_above(ColPartition *part) { | |
| 253 nearest_neighbor_above_ = part; | |
| 254 } | |
| 255 ColPartition *nearest_neighbor_below() const { | |
| 256 return nearest_neighbor_below_; | |
| 257 } | |
| 258 void set_nearest_neighbor_below(ColPartition *part) { | |
| 259 nearest_neighbor_below_ = part; | |
| 260 } | |
| 261 int space_above() const { | |
| 262 return space_above_; | |
| 263 } | |
| 264 void set_space_above(int space) { | |
| 265 space_above_ = space; | |
| 266 } | |
| 267 int space_below() const { | |
| 268 return space_below_; | |
| 269 } | |
| 270 void set_space_below(int space) { | |
| 271 space_below_ = space; | |
| 272 } | |
| 273 int space_to_left() const { | |
| 274 return space_to_left_; | |
| 275 } | |
| 276 void set_space_to_left(int space) { | |
| 277 space_to_left_ = space; | |
| 278 } | |
| 279 int space_to_right() const { | |
| 280 return space_to_right_; | |
| 281 } | |
| 282 void set_space_to_right(int space) { | |
| 283 space_to_right_ = space; | |
| 284 } | |
| 285 uint8_t *color1() { | |
| 286 return color1_; | |
| 287 } | |
| 288 uint8_t *color2() { | |
| 289 return color2_; | |
| 290 } | |
| 291 bool owns_blobs() const { | |
| 292 return owns_blobs_; | |
| 293 } | |
| 294 void set_owns_blobs(bool owns_blobs) { | |
| 295 // Do NOT change ownership flag when there are blobs in the list. | |
| 296 // Immediately set the ownership flag when creating copies. | |
| 297 ASSERT_HOST(boxes_.empty()); | |
| 298 owns_blobs_ = owns_blobs; | |
| 299 } | |
| 300 | |
| 301 // Inline quasi-accessors that require some computation. | |
| 302 | |
| 303 // Returns the middle y-coord of the bounding box. | |
| 304 int MidY() const { | |
| 305 return (bounding_box_.top() + bounding_box_.bottom()) / 2; | |
| 306 } | |
| 307 // Returns the middle y-coord of the median top and bottom. | |
| 308 int MedianY() const { | |
| 309 return (median_top_ + median_bottom_) / 2; | |
| 310 } | |
| 311 // Returns the middle x-coord of the bounding box. | |
| 312 int MidX() const { | |
| 313 return (bounding_box_.left() + bounding_box_.right()) / 2; | |
| 314 } | |
| 315 // Returns the sort key at any given x,y. | |
| 316 int SortKey(int x, int y) const { | |
| 317 return TabVector::SortKey(vertical_, x, y); | |
| 318 } | |
| 319 // Returns the x corresponding to the sortkey, y pair. | |
| 320 int XAtY(int sort_key, int y) const { | |
| 321 return TabVector::XAtY(vertical_, sort_key, y); | |
| 322 } | |
| 323 // Returns the x difference between the two sort keys. | |
| 324 int KeyWidth(int left_key, int right_key) const { | |
| 325 return (right_key - left_key) / vertical_.y(); | |
| 326 } | |
| 327 // Returns the column width between the left and right keys. | |
| 328 int ColumnWidth() const { | |
| 329 return KeyWidth(left_key_, right_key_); | |
| 330 } | |
| 331 // Returns the sort key of the box left edge. | |
| 332 int BoxLeftKey() const { | |
| 333 return SortKey(bounding_box_.left(), MidY()); | |
| 334 } | |
| 335 // Returns the sort key of the box right edge. | |
| 336 int BoxRightKey() const { | |
| 337 return SortKey(bounding_box_.right(), MidY()); | |
| 338 } | |
| 339 // Returns the left edge at the given y, using the sort key. | |
| 340 int LeftAtY(int y) const { | |
| 341 return XAtY(left_key_, y); | |
| 342 } | |
| 343 // Returns the right edge at the given y, using the sort key. | |
| 344 int RightAtY(int y) const { | |
| 345 return XAtY(right_key_, y); | |
| 346 } | |
| 347 // Returns true if the right edge of this is to the left of the right | |
| 348 // edge of other. | |
| 349 bool IsLeftOf(const ColPartition &other) const { | |
| 350 return bounding_box_.right() < other.bounding_box_.right(); | |
| 351 } | |
| 352 // Returns true if the partition contains the given x coordinate at the y. | |
| 353 bool ColumnContains(int x, int y) const { | |
| 354 return LeftAtY(y) - 1 <= x && x <= RightAtY(y) + 1; | |
| 355 } | |
| 356 // Returns true if there are no blobs in the list. | |
| 357 bool IsEmpty() const { | |
| 358 return boxes_.empty(); | |
| 359 } | |
| 360 // Returns true if there is a single blob in the list. | |
| 361 bool IsSingleton() const { | |
| 362 return boxes_.singleton(); | |
| 363 } | |
| 364 // Returns true if this and other overlap horizontally by bounding box. | |
| 365 bool HOverlaps(const ColPartition &other) const { | |
| 366 return bounding_box_.x_overlap(other.bounding_box_); | |
| 367 } | |
| 368 // Returns true if this and other's bounding boxes overlap vertically. | |
| 369 // TODO(rays) Make HOverlaps and VOverlaps truly symmetric. | |
| 370 bool VOverlaps(const ColPartition &other) const { | |
| 371 return bounding_box_.y_gap(other.bounding_box_) < 0; | |
| 372 } | |
| 373 // Returns the vertical overlap (by median) of this and other. | |
| 374 // WARNING! Only makes sense on horizontal partitions! | |
| 375 int VCoreOverlap(const ColPartition &other) const { | |
| 376 if (median_bottom_ == INT32_MAX || other.median_bottom_ == INT32_MAX) { | |
| 377 return 0; | |
| 378 } | |
| 379 return std::min(median_top_, other.median_top_) - | |
| 380 std::max(median_bottom_, other.median_bottom_); | |
| 381 } | |
| 382 // Returns the horizontal overlap (by median) of this and other. | |
| 383 // WARNING! Only makes sense on vertical partitions! | |
| 384 int HCoreOverlap(const ColPartition &other) const { | |
| 385 return std::min(median_right_, other.median_right_) - | |
| 386 std::max(median_left_, other.median_left_); | |
| 387 } | |
| 388 // Returns true if this and other overlap significantly vertically. | |
| 389 // WARNING! Only makes sense on horizontal partitions! | |
| 390 bool VSignificantCoreOverlap(const ColPartition &other) const { | |
| 391 if (median_bottom_ == INT32_MAX || other.median_bottom_ == INT32_MAX) { | |
| 392 return false; | |
| 393 } | |
| 394 int overlap = VCoreOverlap(other); | |
| 395 int height = std::min(median_top_ - median_bottom_, | |
| 396 other.median_top_ - other.median_bottom_); | |
| 397 return overlap * 3 > height; | |
| 398 } | |
| 399 // Returns true if this and other can be combined without putting a | |
| 400 // horizontal step in either left or right edge of the resulting block. | |
| 401 bool WithinSameMargins(const ColPartition &other) const { | |
| 402 return left_margin_ <= other.bounding_box_.left() && | |
| 403 bounding_box_.left() >= other.left_margin_ && | |
| 404 bounding_box_.right() <= other.right_margin_ && | |
| 405 right_margin_ >= other.bounding_box_.right(); | |
| 406 } | |
| 407 // Returns true if the region types (aligned_text_) match. | |
| 408 // Lines never match anything, as they should never be merged or chained. | |
| 409 bool TypesMatch(const ColPartition &other) const { | |
| 410 return TypesMatch(blob_type_, other.blob_type_); | |
| 411 } | |
| 412 static bool TypesMatch(BlobRegionType type1, BlobRegionType type2) { | |
| 413 return (type1 == type2 || type1 == BRT_UNKNOWN || type2 == BRT_UNKNOWN) && | |
| 414 !BLOBNBOX::IsLineType(type1) && !BLOBNBOX::IsLineType(type2); | |
| 415 } | |
| 416 | |
| 417 // Returns true if the types are similar to each other. | |
| 418 static bool TypesSimilar(PolyBlockType type1, PolyBlockType type2) { | |
| 419 return (type1 == type2 || | |
| 420 (type1 == PT_FLOWING_TEXT && type2 == PT_INLINE_EQUATION) || | |
| 421 (type2 == PT_FLOWING_TEXT && type1 == PT_INLINE_EQUATION)); | |
| 422 } | |
| 423 | |
| 424 // Returns true if partitions is of horizontal line type | |
| 425 bool IsLineType() const { | |
| 426 return PTIsLineType(type_); | |
| 427 } | |
| 428 // Returns true if partitions is of image type | |
| 429 bool IsImageType() const { | |
| 430 return PTIsImageType(type_); | |
| 431 } | |
| 432 // Returns true if partitions is of text type | |
| 433 bool IsTextType() const { | |
| 434 return PTIsTextType(type_); | |
| 435 } | |
| 436 // Returns true if partitions is of pullout(inter-column) type | |
| 437 bool IsPulloutType() const { | |
| 438 return PTIsPulloutType(type_); | |
| 439 } | |
| 440 // Returns true if the partition is of an exclusively vertical type. | |
| 441 bool IsVerticalType() const { | |
| 442 return blob_type_ == BRT_VERT_TEXT || blob_type_ == BRT_VLINE; | |
| 443 } | |
| 444 // Returns true if the partition is of a definite horizontal type. | |
| 445 bool IsHorizontalType() const { | |
| 446 return blob_type_ == BRT_TEXT || blob_type_ == BRT_HLINE; | |
| 447 } | |
| 448 // Returns true is the partition is of a type that cannot be merged. | |
| 449 bool IsUnMergeableType() const { | |
| 450 return BLOBNBOX::UnMergeableType(blob_type_) || type_ == PT_NOISE; | |
| 451 } | |
| 452 // Returns true if this partition is a vertical line | |
| 453 // TODO(nbeato): Use PartitionType enum when Ray's code is submitted. | |
| 454 bool IsVerticalLine() const { | |
| 455 return IsVerticalType() && IsLineType(); | |
| 456 } | |
| 457 // Returns true if this partition is a horizontal line | |
| 458 // TODO(nbeato): Use PartitionType enum when Ray's code is submitted. | |
| 459 bool IsHorizontalLine() const { | |
| 460 return IsHorizontalType() && IsLineType(); | |
| 461 } | |
| 462 | |
| 463 // Adds the given box to the partition, updating the partition bounds. | |
| 464 // The list of boxes in the partition is updated, ensuring that no box is | |
| 465 // recorded twice, and the boxes are kept in increasing left position. | |
| 466 void AddBox(BLOBNBOX *box); | |
| 467 | |
| 468 // Removes the given box from the partition, updating the bounds. | |
| 469 void RemoveBox(BLOBNBOX *box); | |
| 470 | |
| 471 // Returns the tallest box in the partition, as measured perpendicular to the | |
| 472 // presumed flow of text. | |
| 473 BLOBNBOX *BiggestBox(); | |
| 474 | |
| 475 // Returns the bounding box excluding the given box. | |
| 476 TBOX BoundsWithoutBox(BLOBNBOX *box); | |
| 477 | |
| 478 // Claims the boxes in the boxes_list by marking them with a this owner | |
| 479 // pointer. | |
| 480 void ClaimBoxes(); | |
| 481 | |
| 482 // nullptr the owner of the blobs in this partition, so they can be deleted | |
| 483 // independently of the ColPartition. | |
| 484 void DisownBoxes(); | |
| 485 // nullptr the owner of the blobs in this partition that are owned by this | |
| 486 // partition, so they can be deleted independently of the ColPartition. | |
| 487 // Any blobs that are not owned by this partition get to keep their owner | |
| 488 // without an assert failure. | |
| 489 void DisownBoxesNoAssert(); | |
| 490 // Nulls the owner of the blobs in this partition that are owned by this | |
| 491 // partition and not leader blobs, removing them from the boxes_ list, thus | |
| 492 // turning this partition back to a leader partition if it contains a leader, | |
| 493 // or otherwise leaving it empty. Returns true if any boxes remain. | |
| 494 bool ReleaseNonLeaderBoxes(); | |
| 495 | |
| 496 // Delete the boxes that this partition owns. | |
| 497 void DeleteBoxes(); | |
| 498 | |
| 499 // Reflects the partition in the y-axis, assuming that its blobs have | |
| 500 // already been done. Corrects only a limited part of the members, since | |
| 501 // this function is assumed to be used shortly after initial creation, which | |
| 502 // is before a lot of the members are used. | |
| 503 void ReflectInYAxis(); | |
| 504 | |
| 505 // Returns true if this is a legal partition - meaning that the conditions | |
| 506 // left_margin <= bounding_box left | |
| 507 // left_key <= bounding box left key | |
| 508 // bounding box left <= bounding box right | |
| 509 // and likewise for right margin and key | |
| 510 // are all met. | |
| 511 bool IsLegal(); | |
| 512 | |
| 513 // Returns true if the left and right edges are approximately equal. | |
| 514 bool MatchingColumns(const ColPartition &other) const; | |
| 515 | |
| 516 // Returns true if the colors match for two text partitions. | |
| 517 bool MatchingTextColor(const ColPartition &other) const; | |
| 518 | |
| 519 // Returns true if the sizes match for two text partitions, | |
| 520 // taking orientation into account | |
| 521 bool MatchingSizes(const ColPartition &other) const; | |
| 522 | |
| 523 // Returns true if there is no tabstop violation in merging this and other. | |
| 524 bool ConfirmNoTabViolation(const ColPartition &other) const; | |
| 525 | |
| 526 // Returns true if other has a similar stroke width to this. | |
| 527 bool MatchingStrokeWidth(const ColPartition &other, | |
| 528 double fractional_tolerance, | |
| 529 double constant_tolerance) const; | |
| 530 // Returns true if candidate is an acceptable diacritic base char merge | |
| 531 // with this as the diacritic. | |
| 532 bool OKDiacriticMerge(const ColPartition &candidate, bool debug) const; | |
| 533 | |
| 534 // Sets the sort key using either the tab vector, or the bounding box if | |
| 535 // the tab vector is nullptr. If the tab_vector lies inside the bounding_box, | |
| 536 // use the edge of the box as a key any way. | |
| 537 void SetLeftTab(const TabVector *tab_vector); | |
| 538 void SetRightTab(const TabVector *tab_vector); | |
| 539 | |
| 540 // Copies the left/right tab from the src partition, but if take_box is | |
| 541 // true, copies the box instead and uses that as a key. | |
| 542 void CopyLeftTab(const ColPartition &src, bool take_box); | |
| 543 void CopyRightTab(const ColPartition &src, bool take_box); | |
| 544 | |
| 545 // Returns the left rule line x coord of the leftmost blob. | |
| 546 int LeftBlobRule() const; | |
| 547 // Returns the right rule line x coord of the rightmost blob. | |
| 548 int RightBlobRule() const; | |
| 549 | |
| 550 // Returns the density value for a particular BlobSpecialTextType. | |
| 551 float SpecialBlobsDensity(const BlobSpecialTextType type) const; | |
| 552 // Returns the number of blobs for a particular BlobSpecialTextType. | |
| 553 int SpecialBlobsCount(const BlobSpecialTextType type); | |
| 554 // Set the density value for a particular BlobSpecialTextType, should ONLY be | |
| 555 // used for debugging or testing. In production code, use | |
| 556 // ComputeSpecialBlobsDensity instead. | |
| 557 void SetSpecialBlobsDensity(const BlobSpecialTextType type, | |
| 558 const float density); | |
| 559 // Compute the SpecialTextType density of blobs, where we assume | |
| 560 // that the SpecialTextType in the boxes_ has been set. | |
| 561 void ComputeSpecialBlobsDensity(); | |
| 562 | |
| 563 // Add a partner above if upper, otherwise below. | |
| 564 // Add them uniquely and keep the list sorted by box left. | |
| 565 // Partnerships are added symmetrically to partner and this. | |
| 566 void AddPartner(bool upper, ColPartition *partner); | |
| 567 // Removes the partner from this, but does not remove this from partner. | |
| 568 // This asymmetric removal is so as not to mess up the iterator that is | |
| 569 // working on partner's partner list. | |
| 570 void RemovePartner(bool upper, ColPartition *partner); | |
| 571 // Returns the partner if the given partner is a singleton, otherwise nullptr. | |
| 572 ColPartition *SingletonPartner(bool upper); | |
| 573 | |
| 574 // Merge with the other partition and delete it. | |
| 575 void Absorb(ColPartition *other, const WidthCallback &cb); | |
| 576 | |
| 577 // Returns true if the overlap between this and the merged pair of | |
| 578 // merge candidates is sufficiently trivial to be allowed. | |
| 579 // The merged box can graze the edge of this by the ok_box_overlap | |
| 580 // if that exceeds the margin to the median top and bottom. | |
| 581 bool OKMergeOverlap(const ColPartition &merge1, const ColPartition &merge2, | |
| 582 int ok_box_overlap, bool debug); | |
| 583 | |
| 584 // Find the blob at which to split this to minimize the overlap with the | |
| 585 // given box. Returns the first blob to go in the second partition. | |
| 586 BLOBNBOX *OverlapSplitBlob(const TBOX &box); | |
| 587 | |
| 588 // Split this partition keeping the first half in this and returning | |
| 589 // the second half. | |
| 590 // Splits by putting the split_blob and the blobs that follow | |
| 591 // in the second half, and the rest in the first half. | |
| 592 ColPartition *SplitAtBlob(BLOBNBOX *split_blob); | |
| 593 | |
| 594 // Splits this partition at the given x coordinate, returning the right | |
| 595 // half and keeping the left half in this. | |
| 596 ColPartition *SplitAt(int split_x); | |
| 597 | |
| 598 // Recalculates all the coordinate limits of the partition. | |
| 599 void ComputeLimits(); | |
| 600 | |
| 601 // Returns the number of boxes that overlap the given box. | |
| 602 int CountOverlappingBoxes(const TBOX &box); | |
| 603 | |
| 604 // Computes and sets the type_, first_column_, last_column_ and column_set_. | |
| 605 // resolution refers to the ppi resolution of the image. | |
| 606 void SetPartitionType(int resolution, ColPartitionSet *columns); | |
| 607 | |
| 608 // Returns the PartitionType from the current BlobRegionType and a column | |
| 609 // flow spanning type ColumnSpanningType, generated by | |
| 610 // ColPartitionSet::SpanningType, that indicates how the partition sits | |
| 611 // in the columns. | |
| 612 PolyBlockType PartitionType(ColumnSpanningType flow) const; | |
| 613 | |
| 614 // Returns the first and last column touched by this partition. | |
| 615 // resolution refers to the ppi resolution of the image. | |
| 616 void ColumnRange(int resolution, ColPartitionSet *columns, int *first_col, | |
| 617 int *last_col); | |
| 618 | |
| 619 // Sets the internal flags good_width_ and good_column_. | |
| 620 void SetColumnGoodness(const WidthCallback &cb); | |
| 621 | |
| 622 // Determines whether the blobs in this partition mostly represent | |
| 623 // a leader (fixed pitch sequence) and sets the member blobs accordingly. | |
| 624 // Note that height is assumed to have been tested elsewhere, and that this | |
| 625 // function will find most fixed-pitch text as leader without a height filter. | |
| 626 // Leader detection is limited to sequences of identical width objects, | |
| 627 // such as .... or ----, so patterns, such as .-.-.-.-. will not be found. | |
| 628 bool MarkAsLeaderIfMonospaced(); | |
| 629 // Given the result of TextlineProjection::EvaluateColPartition, (positive for | |
| 630 // horizontal text, negative for vertical text, and near zero for non-text), | |
| 631 // sets the blob_type_ and flow_ for this partition to indicate whether it | |
| 632 // is strongly or weakly vertical or horizontal text, or non-text. | |
| 633 void SetRegionAndFlowTypesFromProjectionValue(int value); | |
| 634 | |
| 635 // Sets all blobs with the partition blob type and flow, but never overwrite | |
| 636 // leader blobs, as we need to be able to identify them later. | |
| 637 void SetBlobTypes(); | |
| 638 | |
| 639 // Returns true if a decent baseline can be fitted through the blobs. | |
| 640 // Works for both horizontal and vertical text. | |
| 641 bool HasGoodBaseline(); | |
| 642 | |
| 643 // Adds this ColPartition to a matching WorkingPartSet if one can be found, | |
| 644 // otherwise starts a new one in the appropriate column, ending the previous. | |
| 645 void AddToWorkingSet(const ICOORD &bleft, const ICOORD &tright, | |
| 646 int resolution, ColPartition_LIST *used_parts, | |
| 647 WorkingPartSet_LIST *working_set); | |
| 648 | |
| 649 // From the given block_parts list, builds one or more BLOCKs and | |
| 650 // corresponding TO_BLOCKs, such that the line spacing is uniform in each. | |
| 651 // Created blocks are appended to the end of completed_blocks and to_blocks. | |
| 652 // The used partitions are put onto used_parts, as they may still be referred | |
| 653 // to in the partition grid. bleft, tright and resolution are the bounds | |
| 654 // and resolution of the original image. | |
| 655 static void LineSpacingBlocks(const ICOORD &bleft, const ICOORD &tright, | |
| 656 int resolution, ColPartition_LIST *block_parts, | |
| 657 ColPartition_LIST *used_parts, | |
| 658 BLOCK_LIST *completed_blocks, | |
| 659 TO_BLOCK_LIST *to_blocks); | |
| 660 // Constructs a block from the given list of partitions. | |
| 661 // Arguments are as LineSpacingBlocks above. | |
| 662 static TO_BLOCK *MakeBlock(const ICOORD &bleft, const ICOORD &tright, | |
| 663 ColPartition_LIST *block_parts, | |
| 664 ColPartition_LIST *used_parts); | |
| 665 | |
| 666 // Constructs a block from the given list of vertical text partitions. | |
| 667 // Currently only creates rectangular blocks. | |
| 668 static TO_BLOCK *MakeVerticalTextBlock(const ICOORD &bleft, | |
| 669 const ICOORD &tright, | |
| 670 ColPartition_LIST *block_parts, | |
| 671 ColPartition_LIST *used_parts); | |
| 672 | |
| 673 // Makes a TO_ROW matching this and moves all the blobs to it, transferring | |
| 674 // ownership to returned TO_ROW. | |
| 675 TO_ROW *MakeToRow(); | |
| 676 | |
| 677 // Returns a copy of everything except the list of boxes. The resulting | |
| 678 // ColPartition is only suitable for keeping in a column candidate list. | |
| 679 ColPartition *ShallowCopy() const; | |
| 680 // Returns a copy of everything with a shallow copy of the blobs. | |
| 681 // The blobs are still owned by their original parent, so they are | |
| 682 // treated as read-only. | |
| 683 ColPartition *CopyButDontOwnBlobs(); | |
| 684 | |
| 685 #ifndef GRAPHICS_DISABLED | |
| 686 // Provides a color for BBGrid to draw the rectangle. | |
| 687 ScrollView::Color BoxColor() const; | |
| 688 #endif // !GRAPHICS_DISABLED | |
| 689 | |
| 690 // Prints debug information on this. | |
| 691 void Print() const; | |
| 692 // Prints debug information on the colors. | |
| 693 void PrintColors(); | |
| 694 | |
| 695 // Sets the types of all partitions in the run to be the max of the types. | |
| 696 void SmoothPartnerRun(int working_set_count); | |
| 697 | |
| 698 // Cleans up the partners of the given type so that there is at most | |
| 699 // one partner. This makes block creation simpler. | |
| 700 // If get_desperate is true, goes to more desperate merge methods | |
| 701 // to merge flowing text before breaking partnerships. | |
| 702 void RefinePartners(PolyBlockType type, bool get_desperate, | |
| 703 ColPartitionGrid *grid); | |
| 704 | |
| 705 // Returns true if this column partition is in the same column as | |
| 706 // part. This function will only work after the SetPartitionType function | |
| 707 // has been called on both column partitions. This is useful for | |
| 708 // doing a SideSearch when you want things in the same page column. | |
| 709 bool IsInSameColumnAs(const ColPartition &part) const; | |
| 710 | |
| 711 // Sort function to sort by bounding box. | |
| 712 static int SortByBBox(const void *p1, const void *p2) { | |
| 713 const ColPartition *part1 = *static_cast<const ColPartition *const *>(p1); | |
| 714 const ColPartition *part2 = *static_cast<const ColPartition *const *>(p2); | |
| 715 int mid_y1 = part1->bounding_box_.y_middle(); | |
| 716 int mid_y2 = part2->bounding_box_.y_middle(); | |
| 717 if ((part2->bounding_box_.bottom() <= mid_y1 && | |
| 718 mid_y1 <= part2->bounding_box_.top()) || | |
| 719 (part1->bounding_box_.bottom() <= mid_y2 && | |
| 720 mid_y2 <= part1->bounding_box_.top())) { | |
| 721 // Sort by increasing x. | |
| 722 return part1->bounding_box_.x_middle() - part2->bounding_box_.x_middle(); | |
| 723 } | |
| 724 // Sort by decreasing y. | |
| 725 return mid_y2 - mid_y1; | |
| 726 } | |
| 727 | |
| 728 // Sets the column bounds. Primarily used in testing. | |
| 729 void set_first_column(int column) { | |
| 730 first_column_ = column; | |
| 731 } | |
| 732 void set_last_column(int column) { | |
| 733 last_column_ = column; | |
| 734 } | |
| 735 | |
| 736 private: | |
| 737 // Cleans up the partners above if upper is true, else below. | |
| 738 // If get_desperate is true, goes to more desperate merge methods | |
| 739 // to merge flowing text before breaking partnerships. | |
| 740 void RefinePartnersInternal(bool upper, bool get_desperate, | |
| 741 ColPartitionGrid *grid); | |
| 742 // Restricts the partners to only desirable types. For text and BRT_HLINE this | |
| 743 // means the same type_ , and for image types it means any image type. | |
| 744 void RefinePartnersByType(bool upper, ColPartition_CLIST *partners); | |
| 745 // Remove transitive partnerships: this<->a, and a<->b and this<->b. | |
| 746 // Gets rid of this<->b, leaving a clean chain. | |
| 747 // Also if we have this<->a and a<->this, then gets rid of this<->a, as | |
| 748 // this has multiple partners. | |
| 749 void RefinePartnerShortcuts(bool upper, ColPartition_CLIST *partners); | |
| 750 // If multiple text partners can be merged, then do so. | |
| 751 // If desperate is true, then an increase in overlap with the merge is | |
| 752 // allowed. If the overlap increases, then the desperately_merged_ flag | |
| 753 // is set, indicating that the textlines probably need to be regenerated | |
| 754 // by aggressive line fitting/splitting, as there are probably vertically | |
| 755 // joined blobs that cross textlines. | |
| 756 void RefineTextPartnersByMerge(bool upper, bool desperate, | |
| 757 ColPartition_CLIST *partners, | |
| 758 ColPartitionGrid *grid); | |
| 759 // Keep the partner with the biggest overlap. | |
| 760 void RefinePartnersByOverlap(bool upper, ColPartition_CLIST *partners); | |
| 761 | |
| 762 // Return true if bbox belongs better in this than other. | |
| 763 bool ThisPartitionBetter(BLOBNBOX *bbox, const ColPartition &other); | |
| 764 | |
| 765 // Smoothes the spacings in the list into groups of equal linespacing. | |
| 766 // resolution is the resolution of the original image, used as a basis | |
| 767 // for thresholds in change of spacing. page_height is in pixels. | |
| 768 static void SmoothSpacings(int resolution, int page_height, | |
| 769 ColPartition_LIST *parts); | |
| 770 | |
| 771 // Returns true if the parts array of pointers to partitions matches the | |
| 772 // condition for a spacing blip. See SmoothSpacings for what this means | |
| 773 // and how it is used. | |
| 774 static bool OKSpacingBlip(int resolution, int median_spacing, | |
| 775 ColPartition **parts, int offset); | |
| 776 | |
| 777 // Returns true if both the top and bottom spacings of this match the given | |
| 778 // spacing to within suitable margins dictated by the image resolution. | |
| 779 bool SpacingEqual(int spacing, int resolution) const; | |
| 780 | |
| 781 // Returns true if both the top and bottom spacings of this and other | |
| 782 // match to within suitable margins dictated by the image resolution. | |
| 783 bool SpacingsEqual(const ColPartition &other, int resolution) const; | |
| 784 | |
| 785 // Returns true if the sum spacing of this and other match the given | |
| 786 // spacing (or twice the given spacing) to within a suitable margin dictated | |
| 787 // by the image resolution. | |
| 788 bool SummedSpacingOK(const ColPartition &other, int spacing, | |
| 789 int resolution) const; | |
| 790 | |
| 791 // Returns a suitable spacing margin that can be applied to bottoms of | |
| 792 // text lines, based on the resolution and the stored side_step_. | |
| 793 int BottomSpacingMargin(int resolution) const; | |
| 794 | |
| 795 // Returns a suitable spacing margin that can be applied to tops of | |
| 796 // text lines, based on the resolution and the stored side_step_. | |
| 797 int TopSpacingMargin(int resolution) const; | |
| 798 | |
| 799 // Returns true if the median text sizes of this and other agree to within | |
| 800 // a reasonable multiplicative factor. | |
| 801 bool SizesSimilar(const ColPartition &other) const; | |
| 802 | |
| 803 // Computes and returns in start, end a line segment formed from a | |
| 804 // forwards-iterated group of left edges of partitions that satisfy the | |
| 805 // condition that the rightmost left margin is to the left of the | |
| 806 // leftmost left bounding box edge. | |
| 807 // TODO(rays) Not good enough. Needs improving to tightly wrap text in both | |
| 808 // directions, and to loosely wrap images. | |
| 809 static void LeftEdgeRun(ColPartition_IT *part_it, ICOORD *start, ICOORD *end); | |
| 810 // Computes and returns in start, end a line segment formed from a | |
| 811 // backwards-iterated group of right edges of partitions that satisfy the | |
| 812 // condition that the leftmost right margin is to the right of the | |
| 813 // rightmost right bounding box edge. | |
| 814 // TODO(rays) Not good enough. Needs improving to tightly wrap text in both | |
| 815 // directions, and to loosely wrap images. | |
| 816 static void RightEdgeRun(ColPartition_IT *part_it, ICOORD *start, | |
| 817 ICOORD *end); | |
| 818 | |
| 819 // The margins are determined by the position of the nearest vertically | |
| 820 // overlapping neighbour to the side. They indicate the maximum extent | |
| 821 // that the block/column may be extended without touching something else. | |
| 822 // Leftmost coordinate that the region may occupy over the y limits. | |
| 823 int left_margin_ = 0; | |
| 824 // Rightmost coordinate that the region may occupy over the y limits. | |
| 825 int right_margin_ = 0; | |
| 826 // Bounding box of all blobs in the partition. | |
| 827 TBOX bounding_box_; | |
| 828 // Median top and bottom of blobs in this partition. | |
| 829 int median_bottom_ = 0; | |
| 830 int median_top_ = 0; | |
| 831 // Median height of blobs in this partition. | |
| 832 int median_height_ = 0; | |
| 833 // Median left and right of blobs in this partition. | |
| 834 int median_left_ = 0; | |
| 835 int median_right_ = 0; | |
| 836 // Median width of blobs in this partition. | |
| 837 int median_width_ = 0; | |
| 838 // blob_region_type_ for the blobs in this partition. | |
| 839 BlobRegionType blob_type_ = BRT_UNKNOWN; | |
| 840 BlobTextFlowType flow_ = BTFT_NONE; // Quality of text flow. | |
| 841 // Total of GoodTextBlob results for all blobs in the partition. | |
| 842 int good_blob_score_ = 0; | |
| 843 // True if this partition has a common width. | |
| 844 bool good_width_ = false; | |
| 845 // True if this is a good column candidate. | |
| 846 bool good_column_ = false; | |
| 847 // True if the left_key_ is from a tab vector. | |
| 848 bool left_key_tab_ = false; | |
| 849 // True if the right_key_ is from a tab vector. | |
| 850 bool right_key_tab_ = false; | |
| 851 // Left and right sort keys for the edges of the partition. | |
| 852 // If the respective *_key_tab_ is true then this key came from a tab vector. | |
| 853 // If not, then the class promises to keep the key equal to the sort key | |
| 854 // for the respective edge of the bounding box at the MidY, so that | |
| 855 // LeftAtY and RightAtY always returns an x coordinate on the line parallel | |
| 856 // to vertical_ through the bounding box edge at MidY. | |
| 857 int left_key_ = 0; | |
| 858 int right_key_ = 0; | |
| 859 // Type of this partition after looking at its relation to the columns. | |
| 860 PolyBlockType type_ = PT_UNKNOWN; | |
| 861 // The global vertical skew direction. | |
| 862 ICOORD vertical_; | |
| 863 // All boxes in the partition stored in increasing left edge coordinate. | |
| 864 BLOBNBOX_CLIST boxes_; | |
| 865 // The partitions above that matched this. | |
| 866 ColPartition_CLIST upper_partners_; | |
| 867 // The partitions below that matched this. | |
| 868 ColPartition_CLIST lower_partners_; | |
| 869 // The WorkingPartSet it lives in while blocks are being made. | |
| 870 WorkingPartSet *working_set_ = nullptr; | |
| 871 // Column_set_ is the column layout applicable to this ColPartition. | |
| 872 ColPartitionSet *column_set_ = nullptr; | |
| 873 // Flag is true when AddBox is sorting vertically, false otherwise. | |
| 874 bool last_add_was_vertical_ = false; | |
| 875 // True when the partition's ownership has been taken from the grid and | |
| 876 // placed in a working set, or, after that, in the good_parts_ list. | |
| 877 bool block_owned_ = false; | |
| 878 // Flag to indicate that this partition was subjected to a desperate merge, | |
| 879 // and therefore the textlines need rebuilding. | |
| 880 bool desperately_merged_ = false; | |
| 881 bool owns_blobs_ = true; // Does the partition own its blobs? | |
| 882 // The first and last column that this partition applies to. | |
| 883 // Flowing partitions (see type_) will have an equal first and last value | |
| 884 // of the form 2n + 1, where n is the zero-based index into the partitions | |
| 885 // in column_set_. (See ColPartitionSet::GetColumnByIndex). | |
| 886 // Heading partitions will have unequal values of the same form. | |
| 887 // Pullout partitions will have equal values, but may have even values, | |
| 888 // indicating placement between columns. | |
| 889 int first_column_ = -1; | |
| 890 int last_column_ = -1; | |
| 891 // Linespacing data. | |
| 892 int side_step_ = 0; // Median y-shift to next blob on same line. | |
| 893 int top_spacing_ = 0; // Line spacing from median_top_. | |
| 894 int bottom_spacing_ = 0; // Line spacing from median_bottom_. | |
| 895 | |
| 896 // Nearest neighbor above with major x-overlap | |
| 897 ColPartition *nearest_neighbor_above_ = nullptr; | |
| 898 // Nearest neighbor below with major x-overlap | |
| 899 ColPartition *nearest_neighbor_below_ = nullptr; | |
| 900 int space_above_ = 0; // Distance from nearest_neighbor_above | |
| 901 int space_below_ = 0; // Distance from nearest_neighbor_below | |
| 902 int space_to_left_ = 0; // Distance from the left edge of the column | |
| 903 int space_to_right_ = 0; // Distance from the right edge of the column | |
| 904 // Color foreground/background data. | |
| 905 uint8_t color1_[kRGBRMSColors]; | |
| 906 uint8_t color2_[kRGBRMSColors]; | |
| 907 // The density of special blobs. | |
| 908 float special_blobs_densities_[BSTT_COUNT]; | |
| 909 // Type of this partition before considering it as a table cell. This is | |
| 910 // used to revert the type if a partition is first marked as a table cell but | |
| 911 // later filtering steps decide it does not belong to a table | |
| 912 PolyBlockType type_before_table_ = PT_UNKNOWN; | |
| 913 // Check whether the current partition has been assigned to a table column. | |
| 914 bool inside_table_column_ = false; | |
| 915 }; | |
| 916 | |
| 917 // Typedef it now in case it becomes a class later. | |
| 918 using ColPartitionGridSearch = | |
| 919 GridSearch<ColPartition, ColPartition_CLIST, ColPartition_C_IT>; | |
| 920 | |
| 921 } // namespace tesseract. | |
| 922 | |
| 923 #endif // TESSERACT_TEXTORD_COLPARTITION_H_ |
