Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/tesseract/src/ccstruct/blobs.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 * | |
| 3 * File: blobs.h | |
| 4 * Description: Blob definition | |
| 5 * Author: Mark Seaman, OCR Technology | |
| 6 * | |
| 7 * (c) Copyright 1989, Hewlett-Packard Company. | |
| 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 BLOBS_H | |
| 21 #define BLOBS_H | |
| 22 | |
| 23 #include "clst.h" // for CLIST_ITERATOR, CLISTIZEH | |
| 24 #include "normalis.h" // for DENORM | |
| 25 #include "points.h" // for FCOORD, ICOORD | |
| 26 #include "rect.h" // for TBOX | |
| 27 #include "scrollview.h" // for ScrollView, ScrollView::Color | |
| 28 | |
| 29 #include <tesseract/publictypes.h> // for OcrEngineMode | |
| 30 | |
| 31 #include "tesstypes.h" // for TDimension | |
| 32 | |
| 33 struct Pix; | |
| 34 | |
| 35 namespace tesseract { | |
| 36 | |
| 37 class BLOCK; | |
| 38 class C_BLOB; | |
| 39 class C_OUTLINE; | |
| 40 class LLSQ; | |
| 41 class ROW; | |
| 42 class WERD; | |
| 43 | |
| 44 /*---------------------------------------------------------------------- | |
| 45 T y p e s | |
| 46 ----------------------------------------------------------------------*/ | |
| 47 | |
| 48 struct TPOINT { | |
| 49 TPOINT() = default; | |
| 50 TPOINT(TDimension vx, TDimension vy) : x(vx), y(vy) {} | |
| 51 TPOINT(const ICOORD &ic) : x(ic.x()), y(ic.y()) {} | |
| 52 | |
| 53 void operator+=(const TPOINT &other) { | |
| 54 x += other.x; | |
| 55 y += other.y; | |
| 56 } | |
| 57 void operator/=(int divisor) { | |
| 58 x /= divisor; | |
| 59 y /= divisor; | |
| 60 } | |
| 61 bool operator==(const TPOINT &other) const { | |
| 62 return x == other.x && y == other.y; | |
| 63 } | |
| 64 // Returns true when the two line segments cross each other. | |
| 65 // (Moved from outlines.cpp). | |
| 66 static bool IsCrossed(const TPOINT &a0, const TPOINT &a1, const TPOINT &b0, const TPOINT &b1); | |
| 67 | |
| 68 // Assign the difference from point p1 to point p2. | |
| 69 void diff(const TPOINT &p1, const TPOINT &p2) { | |
| 70 x = p1.x - p2.x; | |
| 71 y = p1.y - p2.y; | |
| 72 } | |
| 73 | |
| 74 // Return cross product. | |
| 75 int cross(const TPOINT &other) const { | |
| 76 return x * other.y - y * other.x; | |
| 77 } | |
| 78 | |
| 79 // Return scalar or dot product. | |
| 80 int dot(const TPOINT &other) const { | |
| 81 return x * other.x + y * other.y; | |
| 82 } | |
| 83 | |
| 84 // Calculate square of vector length. | |
| 85 int length2() const { | |
| 86 return x * x + y * y; | |
| 87 } | |
| 88 | |
| 89 TDimension x = 0; // absolute x coord. | |
| 90 TDimension y = 0; // absolute y coord. | |
| 91 }; | |
| 92 | |
| 93 using VECTOR = TPOINT; // structure for coordinates. | |
| 94 | |
| 95 struct EDGEPT { | |
| 96 EDGEPT() = default; | |
| 97 EDGEPT(const EDGEPT &src) : next(nullptr), prev(nullptr) { | |
| 98 CopyFrom(src); | |
| 99 } | |
| 100 EDGEPT &operator=(const EDGEPT &src) { | |
| 101 CopyFrom(src); | |
| 102 return *this; | |
| 103 } | |
| 104 // Copies the data elements, but leaves the pointers untouched. | |
| 105 void CopyFrom(const EDGEPT &src) { | |
| 106 pos = src.pos; | |
| 107 vec = src.vec; | |
| 108 is_hidden = src.is_hidden; | |
| 109 runlength = src.runlength; | |
| 110 dir = src.dir; | |
| 111 fixed = src.fixed; | |
| 112 src_outline = src.src_outline; | |
| 113 start_step = src.start_step; | |
| 114 step_count = src.step_count; | |
| 115 } | |
| 116 // Returns the squared distance between the points, with the x-component | |
| 117 // weighted by x_factor. | |
| 118 int WeightedDistance(const EDGEPT &other, int x_factor) const { | |
| 119 int x_dist = pos.x - other.pos.x; | |
| 120 int y_dist = pos.y - other.pos.y; | |
| 121 return x_dist * x_dist * x_factor + y_dist * y_dist; | |
| 122 } | |
| 123 // Returns true if the positions are equal. | |
| 124 bool EqualPos(const EDGEPT &other) const { | |
| 125 return pos == other.pos; | |
| 126 } | |
| 127 // Returns the bounding box of the outline segment from *this to *end. | |
| 128 // Ignores hidden edge flags. | |
| 129 TBOX SegmentBox(const EDGEPT *end) const { | |
| 130 TBOX box(pos.x, pos.y, pos.x, pos.y); | |
| 131 const EDGEPT *pt = this; | |
| 132 do { | |
| 133 pt = pt->next; | |
| 134 if (pt->pos.x < box.left()) { | |
| 135 box.set_left(pt->pos.x); | |
| 136 } | |
| 137 if (pt->pos.x > box.right()) { | |
| 138 box.set_right(pt->pos.x); | |
| 139 } | |
| 140 if (pt->pos.y < box.bottom()) { | |
| 141 box.set_bottom(pt->pos.y); | |
| 142 } | |
| 143 if (pt->pos.y > box.top()) { | |
| 144 box.set_top(pt->pos.y); | |
| 145 } | |
| 146 } while (pt != end && pt != this); | |
| 147 return box; | |
| 148 } | |
| 149 // Returns the area of the outline segment from *this to *end. | |
| 150 // Ignores hidden edge flags. | |
| 151 int SegmentArea(const EDGEPT *end) const { | |
| 152 int area = 0; | |
| 153 const EDGEPT *pt = this->next; | |
| 154 do { | |
| 155 TPOINT origin_vec(pt->pos.x - pos.x, pt->pos.y - pos.y); | |
| 156 area += origin_vec.cross(pt->vec); | |
| 157 pt = pt->next; | |
| 158 } while (pt != end && pt != this); | |
| 159 return area; | |
| 160 } | |
| 161 // Returns true if the number of points in the outline segment from *this to | |
| 162 // *end is less that min_points and false if we get back to *this first. | |
| 163 // Ignores hidden edge flags. | |
| 164 bool ShortNonCircularSegment(int min_points, const EDGEPT *end) const { | |
| 165 int count = 0; | |
| 166 const EDGEPT *pt = this; | |
| 167 do { | |
| 168 if (pt == end) { | |
| 169 return true; | |
| 170 } | |
| 171 pt = pt->next; | |
| 172 ++count; | |
| 173 } while (pt != this && count <= min_points); | |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 // Accessors to hide or reveal a cut edge from feature extractors. | |
| 178 void Hide() { | |
| 179 is_hidden = true; | |
| 180 } | |
| 181 void Reveal() { | |
| 182 is_hidden = false; | |
| 183 } | |
| 184 bool IsHidden() const { | |
| 185 return is_hidden; | |
| 186 } | |
| 187 void MarkChop() { | |
| 188 dir = 1; | |
| 189 } | |
| 190 bool IsChopPt() const { | |
| 191 return dir != 0; | |
| 192 } | |
| 193 | |
| 194 TPOINT pos; // position | |
| 195 VECTOR vec; // vector to next point | |
| 196 bool is_hidden = false; | |
| 197 uint8_t runlength = 0; | |
| 198 int8_t dir = 0; | |
| 199 bool fixed = false; | |
| 200 EDGEPT *next = nullptr; // anticlockwise element | |
| 201 EDGEPT *prev = nullptr; // clockwise element | |
| 202 C_OUTLINE *src_outline = nullptr; // Outline it came from. | |
| 203 // The following fields are not used if src_outline is nullptr. | |
| 204 int start_step = 0; // Location of pos in src_outline. | |
| 205 int step_count = 0; // Number of steps used (may wrap around). | |
| 206 }; | |
| 207 | |
| 208 // For use in chop and findseam to keep a list of which EDGEPTs were inserted. | |
| 209 CLISTIZEH(EDGEPT) | |
| 210 | |
| 211 struct TESSLINE { | |
| 212 TESSLINE() : is_hole(false), loop(nullptr), next(nullptr) {} | |
| 213 TESSLINE(const TESSLINE &src) : loop(nullptr), next(nullptr) { | |
| 214 CopyFrom(src); | |
| 215 } | |
| 216 ~TESSLINE() { | |
| 217 Clear(); | |
| 218 } | |
| 219 TESSLINE &operator=(const TESSLINE &src) { | |
| 220 CopyFrom(src); | |
| 221 return *this; | |
| 222 } | |
| 223 // Consume the circular list of EDGEPTs to make a TESSLINE. | |
| 224 static TESSLINE *BuildFromOutlineList(EDGEPT *outline); | |
| 225 // Copies the data and the outline, but leaves next untouched. | |
| 226 void CopyFrom(const TESSLINE &src); | |
| 227 // Deletes owned data. | |
| 228 void Clear(); | |
| 229 // Normalize in-place using the DENORM. | |
| 230 void Normalize(const DENORM &denorm); | |
| 231 // Rotates by the given rotation in place. | |
| 232 void Rotate(const FCOORD rotation); | |
| 233 // Moves by the given vec in place. | |
| 234 void Move(const ICOORD vec); | |
| 235 // Scales by the given factor in place. | |
| 236 void Scale(float factor); | |
| 237 // Sets up the start and vec members of the loop from the pos members. | |
| 238 void SetupFromPos(); | |
| 239 // Recomputes the bounding box from the points in the loop. | |
| 240 void ComputeBoundingBox(); | |
| 241 // Computes the min and max cross product of the outline points with the | |
| 242 // given vec and returns the results in min_xp and max_xp. Geometrically | |
| 243 // this is the left and right edge of the outline perpendicular to the | |
| 244 // given direction, but to get the distance units correct, you would | |
| 245 // have to divide by the modulus of vec. | |
| 246 void MinMaxCrossProduct(const TPOINT vec, int *min_xp, int *max_xp) const; | |
| 247 | |
| 248 TBOX bounding_box() const; | |
| 249 // Returns true if *this and other have equal bounding boxes. | |
| 250 bool SameBox(const TESSLINE &other) const { | |
| 251 return topleft == other.topleft && botright == other.botright; | |
| 252 } | |
| 253 // Returns true if the given line segment crosses any outline of this blob. | |
| 254 bool SegmentCrosses(const TPOINT &pt1, const TPOINT &pt2) const { | |
| 255 if (Contains(pt1) && Contains(pt2)) { | |
| 256 EDGEPT *pt = loop; | |
| 257 do { | |
| 258 if (TPOINT::IsCrossed(pt1, pt2, pt->pos, pt->next->pos)) { | |
| 259 return true; | |
| 260 } | |
| 261 pt = pt->next; | |
| 262 } while (pt != loop); | |
| 263 } | |
| 264 return false; | |
| 265 } | |
| 266 // Returns true if the point is contained within the outline box. | |
| 267 bool Contains(const TPOINT &pt) const { | |
| 268 return topleft.x <= pt.x && pt.x <= botright.x && botright.y <= pt.y && pt.y <= topleft.y; | |
| 269 } | |
| 270 | |
| 271 #ifndef GRAPHICS_DISABLED | |
| 272 void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color); | |
| 273 #endif // !GRAPHICS_DISABLED | |
| 274 | |
| 275 // Returns the first outline point that has a different src_outline to its | |
| 276 // predecessor, or, if all the same, the lowest indexed point. | |
| 277 EDGEPT *FindBestStartPt() const; | |
| 278 | |
| 279 int BBArea() const { | |
| 280 return (botright.x - topleft.x) * (topleft.y - botright.y); | |
| 281 } | |
| 282 | |
| 283 TPOINT topleft; // Top left of loop. | |
| 284 TPOINT botright; // Bottom right of loop. | |
| 285 TPOINT start; // Start of loop. | |
| 286 bool is_hole; // True if this is a hole/child outline. | |
| 287 EDGEPT *loop; // Edgeloop. | |
| 288 TESSLINE *next; // Next outline in blob. | |
| 289 }; // Outline structure. | |
| 290 | |
| 291 struct TBLOB { | |
| 292 TBLOB() : outlines(nullptr) {} | |
| 293 TBLOB(const TBLOB &src) : outlines(nullptr) { | |
| 294 CopyFrom(src); | |
| 295 } | |
| 296 ~TBLOB() { | |
| 297 Clear(); | |
| 298 } | |
| 299 TBLOB &operator=(const TBLOB &src) { | |
| 300 CopyFrom(src); | |
| 301 return *this; | |
| 302 } | |
| 303 // Factory to build a TBLOB from a C_BLOB with polygonal approximation along | |
| 304 // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB | |
| 305 // contain pointers to the input C_OUTLINEs that enable higher-resolution | |
| 306 // feature extraction that does not use the polygonal approximation. | |
| 307 static TBLOB *PolygonalCopy(bool allow_detailed_fx, C_BLOB *src); | |
| 308 // Factory builds a blob with no outlines, but copies the other member data. | |
| 309 static TBLOB *ShallowCopy(const TBLOB &src); | |
| 310 // Normalizes the blob for classification only if needed. | |
| 311 // (Normally this means a non-zero classify rotation.) | |
| 312 // If no Normalization is needed, then nullptr is returned, and the input blob | |
| 313 // can be used directly. Otherwise a new TBLOB is returned which must be | |
| 314 // deleted after use. | |
| 315 TBLOB *ClassifyNormalizeIfNeeded() const; | |
| 316 | |
| 317 // Copies the data and the outlines, but leaves next untouched. | |
| 318 void CopyFrom(const TBLOB &src); | |
| 319 // Deletes owned data. | |
| 320 void Clear(); | |
| 321 // Sets up the built-in DENORM and normalizes the blob in-place. | |
| 322 // For parameters see DENORM::SetupNormalization, plus the inverse flag for | |
| 323 // this blob and the Pix for the full image. | |
| 324 void Normalize(const BLOCK *block, const FCOORD *rotation, const DENORM *predecessor, | |
| 325 float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, | |
| 326 float final_yshift, bool inverse, Image pix); | |
| 327 // Rotates by the given rotation in place. | |
| 328 void Rotate(const FCOORD rotation); | |
| 329 // Moves by the given vec in place. | |
| 330 void Move(const ICOORD vec); | |
| 331 // Scales by the given factor in place. | |
| 332 void Scale(float factor); | |
| 333 // Recomputes the bounding boxes of the outlines. | |
| 334 void ComputeBoundingBoxes(); | |
| 335 | |
| 336 // Returns the number of outlines. | |
| 337 int NumOutlines() const; | |
| 338 | |
| 339 TBOX bounding_box() const; | |
| 340 | |
| 341 // Returns true if the given line segment crosses any outline of this blob. | |
| 342 bool SegmentCrossesOutline(const TPOINT &pt1, const TPOINT &pt2) const { | |
| 343 for (const TESSLINE *outline = outlines; outline != nullptr; outline = outline->next) { | |
| 344 if (outline->SegmentCrosses(pt1, pt2)) { | |
| 345 return true; | |
| 346 } | |
| 347 } | |
| 348 return false; | |
| 349 } | |
| 350 // Returns true if the point is contained within any of the outline boxes. | |
| 351 bool Contains(const TPOINT &pt) const { | |
| 352 for (const TESSLINE *outline = outlines; outline != nullptr; outline = outline->next) { | |
| 353 if (outline->Contains(pt)) { | |
| 354 return true; | |
| 355 } | |
| 356 } | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 360 // Finds and deletes any duplicate outlines in this blob, without deleting | |
| 361 // their EDGEPTs. | |
| 362 void EliminateDuplicateOutlines(); | |
| 363 | |
| 364 // Swaps the outlines of *this and next if needed to keep the centers in | |
| 365 // increasing x. | |
| 366 void CorrectBlobOrder(TBLOB *next); | |
| 367 | |
| 368 const DENORM &denorm() const { | |
| 369 return denorm_; | |
| 370 } | |
| 371 | |
| 372 #ifndef GRAPHICS_DISABLED | |
| 373 void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color); | |
| 374 #endif // !GRAPHICS_DISABLED | |
| 375 | |
| 376 int BBArea() const { | |
| 377 int total_area = 0; | |
| 378 for (TESSLINE *outline = outlines; outline != nullptr; outline = outline->next) { | |
| 379 total_area += outline->BBArea(); | |
| 380 } | |
| 381 return total_area; | |
| 382 } | |
| 383 | |
| 384 // Computes the center of mass and second moments for the old baseline and | |
| 385 // 2nd moment normalizations. Returns the outline length. | |
| 386 // The input denorm should be the normalizations that have been applied from | |
| 387 // the image to the current state of this TBLOB. | |
| 388 int ComputeMoments(FCOORD *center, FCOORD *second_moments) const; | |
| 389 // Computes the precise bounding box of the coords that are generated by | |
| 390 // GetEdgeCoords. This may be different from the bounding box of the polygon. | |
| 391 void GetPreciseBoundingBox(TBOX *precise_box) const; | |
| 392 // Adds edges to the given vectors. | |
| 393 // For all the edge steps in all the outlines, or polygonal approximation | |
| 394 // where there are no edge steps, collects the steps into x_coords/y_coords. | |
| 395 // x_coords is a collection of the x-coords of vertical edges for each | |
| 396 // y-coord starting at box.bottom(). | |
| 397 // y_coords is a collection of the y-coords of horizontal edges for each | |
| 398 // x-coord starting at box.left(). | |
| 399 // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. | |
| 400 // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. | |
| 401 void GetEdgeCoords(const TBOX &box, std::vector<std::vector<int>> &x_coords, | |
| 402 std::vector<std::vector<int>> &y_coords) const; | |
| 403 | |
| 404 TESSLINE *outlines; // List of outlines in blob. | |
| 405 | |
| 406 private: // TODO(rays) Someday the data members will be private too. | |
| 407 // For all the edge steps in all the outlines, or polygonal approximation | |
| 408 // where there are no edge steps, collects the steps into the bounding_box, | |
| 409 // llsq and/or the x_coords/y_coords. Both are used in different kinds of | |
| 410 // normalization. | |
| 411 // For a description of x_coords, y_coords, see GetEdgeCoords above. | |
| 412 void CollectEdges(const TBOX &box, TBOX *bounding_box, LLSQ *llsq, | |
| 413 std::vector<std::vector<int>> *x_coords, | |
| 414 std::vector<std::vector<int>> *y_coords) const; | |
| 415 | |
| 416 private: | |
| 417 // DENORM indicating the transformations that this blob has undergone so far. | |
| 418 DENORM denorm_; | |
| 419 }; // Blob structure. | |
| 420 | |
| 421 struct TWERD { | |
| 422 TWERD() : latin_script(false) {} | |
| 423 TWERD(const TWERD &src) { | |
| 424 CopyFrom(src); | |
| 425 } | |
| 426 ~TWERD() { | |
| 427 Clear(); | |
| 428 } | |
| 429 TWERD &operator=(const TWERD &src) { | |
| 430 CopyFrom(src); | |
| 431 return *this; | |
| 432 } | |
| 433 // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal | |
| 434 // approximation along the way. | |
| 435 static TWERD *PolygonalCopy(bool allow_detailed_fx, WERD *src); | |
| 436 // Baseline normalizes the blobs in-place, recording the normalization in the | |
| 437 // DENORMs in the blobs. | |
| 438 void BLNormalize(const BLOCK *block, const ROW *row, Image pix, bool inverse, float x_height, | |
| 439 float baseline_shift, bool numeric_mode, tesseract::OcrEngineMode hint, | |
| 440 const TBOX *norm_box, DENORM *word_denorm); | |
| 441 // Copies the data and the blobs, but leaves next untouched. | |
| 442 void CopyFrom(const TWERD &src); | |
| 443 // Deletes owned data. | |
| 444 void Clear(); | |
| 445 // Recomputes the bounding boxes of the blobs. | |
| 446 void ComputeBoundingBoxes(); | |
| 447 | |
| 448 // Returns the number of blobs in the word. | |
| 449 unsigned NumBlobs() const { | |
| 450 return blobs.size(); | |
| 451 } | |
| 452 TBOX bounding_box() const; | |
| 453 | |
| 454 // Merges the blobs from start to end, not including end, and deletes | |
| 455 // the blobs between start and end. | |
| 456 void MergeBlobs(unsigned start, unsigned end); | |
| 457 | |
| 458 #ifndef GRAPHICS_DISABLED | |
| 459 void plot(ScrollView *window); | |
| 460 #endif // !GRAPHICS_DISABLED | |
| 461 | |
| 462 std::vector<TBLOB *> blobs; // Blobs in word. | |
| 463 bool latin_script; // This word is in a latin-based script. | |
| 464 }; | |
| 465 | |
| 466 /*---------------------------------------------------------------------- | |
| 467 F u n c t i o n s | |
| 468 ----------------------------------------------------------------------*/ | |
| 469 // TODO(rays) Make divisible_blob and divide_blobs members of TBLOB. | |
| 470 bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT *location); | |
| 471 | |
| 472 void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob, const TPOINT &location); | |
| 473 | |
| 474 } // namespace tesseract | |
| 475 | |
| 476 #endif |
