diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/tesseract/src/ccstruct/blobs.h	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,476 @@
+/******************************************************************************
+ *
+ * File:        blobs.h
+ * Description: Blob definition
+ * Author:      Mark Seaman, OCR Technology
+ *
+ * (c) Copyright 1989, Hewlett-Packard Company.
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ *
+ *****************************************************************************/
+
+#ifndef BLOBS_H
+#define BLOBS_H
+
+#include "clst.h"       // for CLIST_ITERATOR, CLISTIZEH
+#include "normalis.h"   // for DENORM
+#include "points.h"     // for FCOORD, ICOORD
+#include "rect.h"       // for TBOX
+#include "scrollview.h" // for ScrollView, ScrollView::Color
+
+#include <tesseract/publictypes.h> // for OcrEngineMode
+
+#include "tesstypes.h" // for TDimension
+
+struct Pix;
+
+namespace tesseract {
+
+class BLOCK;
+class C_BLOB;
+class C_OUTLINE;
+class LLSQ;
+class ROW;
+class WERD;
+
+/*----------------------------------------------------------------------
+              T y p e s
+----------------------------------------------------------------------*/
+
+struct TPOINT {
+  TPOINT() = default;
+  TPOINT(TDimension vx, TDimension vy) : x(vx), y(vy) {}
+  TPOINT(const ICOORD &ic) : x(ic.x()), y(ic.y()) {}
+
+  void operator+=(const TPOINT &other) {
+    x += other.x;
+    y += other.y;
+  }
+  void operator/=(int divisor) {
+    x /= divisor;
+    y /= divisor;
+  }
+  bool operator==(const TPOINT &other) const {
+    return x == other.x && y == other.y;
+  }
+  // Returns true when the two line segments cross each other.
+  // (Moved from outlines.cpp).
+  static bool IsCrossed(const TPOINT &a0, const TPOINT &a1, const TPOINT &b0, const TPOINT &b1);
+
+  // Assign the difference from point p1 to point p2.
+  void diff(const TPOINT &p1, const TPOINT &p2) {
+    x = p1.x - p2.x;
+    y = p1.y - p2.y;
+  }
+
+  // Return cross product.
+  int cross(const TPOINT &other) const {
+    return x * other.y - y * other.x;
+  }
+
+  // Return scalar or dot product.
+  int dot(const TPOINT &other) const {
+    return x * other.x + y * other.y;
+  }
+
+  // Calculate square of vector length.
+  int length2() const {
+    return x * x + y * y;
+  }
+
+  TDimension x = 0; // absolute x coord.
+  TDimension y = 0; // absolute y coord.
+};
+
+using VECTOR = TPOINT; // structure for coordinates.
+
+struct EDGEPT {
+  EDGEPT() = default;
+  EDGEPT(const EDGEPT &src) : next(nullptr), prev(nullptr) {
+    CopyFrom(src);
+  }
+  EDGEPT &operator=(const EDGEPT &src) {
+    CopyFrom(src);
+    return *this;
+  }
+  // Copies the data elements, but leaves the pointers untouched.
+  void CopyFrom(const EDGEPT &src) {
+    pos = src.pos;
+    vec = src.vec;
+    is_hidden = src.is_hidden;
+    runlength = src.runlength;
+    dir = src.dir;
+    fixed = src.fixed;
+    src_outline = src.src_outline;
+    start_step = src.start_step;
+    step_count = src.step_count;
+  }
+  // Returns the squared distance between the points, with the x-component
+  // weighted by x_factor.
+  int WeightedDistance(const EDGEPT &other, int x_factor) const {
+    int x_dist = pos.x - other.pos.x;
+    int y_dist = pos.y - other.pos.y;
+    return x_dist * x_dist * x_factor + y_dist * y_dist;
+  }
+  // Returns true if the positions are equal.
+  bool EqualPos(const EDGEPT &other) const {
+    return pos == other.pos;
+  }
+  // Returns the bounding box of the outline segment from *this to *end.
+  // Ignores hidden edge flags.
+  TBOX SegmentBox(const EDGEPT *end) const {
+    TBOX box(pos.x, pos.y, pos.x, pos.y);
+    const EDGEPT *pt = this;
+    do {
+      pt = pt->next;
+      if (pt->pos.x < box.left()) {
+        box.set_left(pt->pos.x);
+      }
+      if (pt->pos.x > box.right()) {
+        box.set_right(pt->pos.x);
+      }
+      if (pt->pos.y < box.bottom()) {
+        box.set_bottom(pt->pos.y);
+      }
+      if (pt->pos.y > box.top()) {
+        box.set_top(pt->pos.y);
+      }
+    } while (pt != end && pt != this);
+    return box;
+  }
+  // Returns the area of the outline segment from *this to *end.
+  // Ignores hidden edge flags.
+  int SegmentArea(const EDGEPT *end) const {
+    int area = 0;
+    const EDGEPT *pt = this->next;
+    do {
+      TPOINT origin_vec(pt->pos.x - pos.x, pt->pos.y - pos.y);
+      area += origin_vec.cross(pt->vec);
+      pt = pt->next;
+    } while (pt != end && pt != this);
+    return area;
+  }
+  // Returns true if the number of points in the outline segment from *this to
+  // *end is less that min_points and false if we get back to *this first.
+  // Ignores hidden edge flags.
+  bool ShortNonCircularSegment(int min_points, const EDGEPT *end) const {
+    int count = 0;
+    const EDGEPT *pt = this;
+    do {
+      if (pt == end) {
+        return true;
+      }
+      pt = pt->next;
+      ++count;
+    } while (pt != this && count <= min_points);
+    return false;
+  }
+
+  // Accessors to hide or reveal a cut edge from feature extractors.
+  void Hide() {
+    is_hidden = true;
+  }
+  void Reveal() {
+    is_hidden = false;
+  }
+  bool IsHidden() const {
+    return is_hidden;
+  }
+  void MarkChop() {
+    dir = 1;
+  }
+  bool IsChopPt() const {
+    return dir != 0;
+  }
+
+  TPOINT pos; // position
+  VECTOR vec; // vector to next point
+  bool is_hidden = false;
+  uint8_t runlength = 0;
+  int8_t dir = 0;
+  bool fixed = false;
+  EDGEPT *next = nullptr;           // anticlockwise element
+  EDGEPT *prev = nullptr;           // clockwise element
+  C_OUTLINE *src_outline = nullptr; // Outline it came from.
+  // The following fields are not used if src_outline is nullptr.
+  int start_step = 0; // Location of pos in src_outline.
+  int step_count = 0; // Number of steps used (may wrap around).
+};
+
+// For use in chop and findseam to keep a list of which EDGEPTs were inserted.
+CLISTIZEH(EDGEPT)
+
+struct TESSLINE {
+  TESSLINE() : is_hole(false), loop(nullptr), next(nullptr) {}
+  TESSLINE(const TESSLINE &src) : loop(nullptr), next(nullptr) {
+    CopyFrom(src);
+  }
+  ~TESSLINE() {
+    Clear();
+  }
+  TESSLINE &operator=(const TESSLINE &src) {
+    CopyFrom(src);
+    return *this;
+  }
+  // Consume the circular list of EDGEPTs to make a TESSLINE.
+  static TESSLINE *BuildFromOutlineList(EDGEPT *outline);
+  // Copies the data and the outline, but leaves next untouched.
+  void CopyFrom(const TESSLINE &src);
+  // Deletes owned data.
+  void Clear();
+  // Normalize in-place using the DENORM.
+  void Normalize(const DENORM &denorm);
+  // Rotates by the given rotation in place.
+  void Rotate(const FCOORD rotation);
+  // Moves by the given vec in place.
+  void Move(const ICOORD vec);
+  // Scales by the given factor in place.
+  void Scale(float factor);
+  // Sets up the start and vec members of the loop from the pos members.
+  void SetupFromPos();
+  // Recomputes the bounding box from the points in the loop.
+  void ComputeBoundingBox();
+  // Computes the min and max cross product of the outline points with the
+  // given vec and returns the results in min_xp and max_xp. Geometrically
+  // this is the left and right edge of the outline perpendicular to the
+  // given direction, but to get the distance units correct, you would
+  // have to divide by the modulus of vec.
+  void MinMaxCrossProduct(const TPOINT vec, int *min_xp, int *max_xp) const;
+
+  TBOX bounding_box() const;
+  // Returns true if *this and other have equal bounding boxes.
+  bool SameBox(const TESSLINE &other) const {
+    return topleft == other.topleft && botright == other.botright;
+  }
+  // Returns true if the given line segment crosses any outline of this blob.
+  bool SegmentCrosses(const TPOINT &pt1, const TPOINT &pt2) const {
+    if (Contains(pt1) && Contains(pt2)) {
+      EDGEPT *pt = loop;
+      do {
+        if (TPOINT::IsCrossed(pt1, pt2, pt->pos, pt->next->pos)) {
+          return true;
+        }
+        pt = pt->next;
+      } while (pt != loop);
+    }
+    return false;
+  }
+  // Returns true if the point is contained within the outline box.
+  bool Contains(const TPOINT &pt) const {
+    return topleft.x <= pt.x && pt.x <= botright.x && botright.y <= pt.y && pt.y <= topleft.y;
+  }
+
+#ifndef GRAPHICS_DISABLED
+  void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color);
+#endif // !GRAPHICS_DISABLED
+
+  // Returns the first outline point that has a different src_outline to its
+  // predecessor, or, if all the same, the lowest indexed point.
+  EDGEPT *FindBestStartPt() const;
+
+  int BBArea() const {
+    return (botright.x - topleft.x) * (topleft.y - botright.y);
+  }
+
+  TPOINT topleft;  // Top left of loop.
+  TPOINT botright; // Bottom right of loop.
+  TPOINT start;    // Start of loop.
+  bool is_hole;    // True if this is a hole/child outline.
+  EDGEPT *loop;    // Edgeloop.
+  TESSLINE *next;  // Next outline in blob.
+};                 // Outline structure.
+
+struct TBLOB {
+  TBLOB() : outlines(nullptr) {}
+  TBLOB(const TBLOB &src) : outlines(nullptr) {
+    CopyFrom(src);
+  }
+  ~TBLOB() {
+    Clear();
+  }
+  TBLOB &operator=(const TBLOB &src) {
+    CopyFrom(src);
+    return *this;
+  }
+  // Factory to build a TBLOB from a C_BLOB with polygonal approximation along
+  // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB
+  // contain pointers to the input C_OUTLINEs that enable higher-resolution
+  // feature extraction that does not use the polygonal approximation.
+  static TBLOB *PolygonalCopy(bool allow_detailed_fx, C_BLOB *src);
+  // Factory builds a blob with no outlines, but copies the other member data.
+  static TBLOB *ShallowCopy(const TBLOB &src);
+  // Normalizes the blob for classification only if needed.
+  // (Normally this means a non-zero classify rotation.)
+  // If no Normalization is needed, then nullptr is returned, and the input blob
+  // can be used directly. Otherwise a new TBLOB is returned which must be
+  // deleted after use.
+  TBLOB *ClassifyNormalizeIfNeeded() const;
+
+  // Copies the data and the outlines, but leaves next untouched.
+  void CopyFrom(const TBLOB &src);
+  // Deletes owned data.
+  void Clear();
+  // Sets up the built-in DENORM and normalizes the blob in-place.
+  // For parameters see DENORM::SetupNormalization, plus the inverse flag for
+  // this blob and the Pix for the full image.
+  void Normalize(const BLOCK *block, const FCOORD *rotation, const DENORM *predecessor,
+                 float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift,
+                 float final_yshift, bool inverse, Image pix);
+  // Rotates by the given rotation in place.
+  void Rotate(const FCOORD rotation);
+  // Moves by the given vec in place.
+  void Move(const ICOORD vec);
+  // Scales by the given factor in place.
+  void Scale(float factor);
+  // Recomputes the bounding boxes of the outlines.
+  void ComputeBoundingBoxes();
+
+  // Returns the number of outlines.
+  int NumOutlines() const;
+
+  TBOX bounding_box() const;
+
+  // Returns true if the given line segment crosses any outline of this blob.
+  bool SegmentCrossesOutline(const TPOINT &pt1, const TPOINT &pt2) const {
+    for (const TESSLINE *outline = outlines; outline != nullptr; outline = outline->next) {
+      if (outline->SegmentCrosses(pt1, pt2)) {
+        return true;
+      }
+    }
+    return false;
+  }
+  // Returns true if the point is contained within any of the outline boxes.
+  bool Contains(const TPOINT &pt) const {
+    for (const TESSLINE *outline = outlines; outline != nullptr; outline = outline->next) {
+      if (outline->Contains(pt)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Finds and deletes any duplicate outlines in this blob, without deleting
+  // their EDGEPTs.
+  void EliminateDuplicateOutlines();
+
+  // Swaps the outlines of *this and next if needed to keep the centers in
+  // increasing x.
+  void CorrectBlobOrder(TBLOB *next);
+
+  const DENORM &denorm() const {
+    return denorm_;
+  }
+
+#ifndef GRAPHICS_DISABLED
+  void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color);
+#endif // !GRAPHICS_DISABLED
+
+  int BBArea() const {
+    int total_area = 0;
+    for (TESSLINE *outline = outlines; outline != nullptr; outline = outline->next) {
+      total_area += outline->BBArea();
+    }
+    return total_area;
+  }
+
+  // Computes the center of mass and second moments for the old baseline and
+  // 2nd moment normalizations. Returns the outline length.
+  // The input denorm should be the normalizations that have been applied from
+  // the image to the current state of this TBLOB.
+  int ComputeMoments(FCOORD *center, FCOORD *second_moments) const;
+  // Computes the precise bounding box of the coords that are generated by
+  // GetEdgeCoords. This may be different from the bounding box of the polygon.
+  void GetPreciseBoundingBox(TBOX *precise_box) const;
+  // Adds edges to the given vectors.
+  // For all the edge steps in all the outlines, or polygonal approximation
+  // where there are no edge steps, collects the steps into x_coords/y_coords.
+  // x_coords is a collection of the x-coords of vertical edges for each
+  // y-coord starting at box.bottom().
+  // y_coords is a collection of the y-coords of horizontal edges for each
+  // x-coord starting at box.left().
+  // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom.
+  // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1.
+  void GetEdgeCoords(const TBOX &box, std::vector<std::vector<int>> &x_coords,
+                     std::vector<std::vector<int>> &y_coords) const;
+
+  TESSLINE *outlines; // List of outlines in blob.
+
+private: // TODO(rays) Someday the data members will be private too.
+  // For all the edge steps in all the outlines, or polygonal approximation
+  // where there are no edge steps, collects the steps into the bounding_box,
+  // llsq and/or the x_coords/y_coords. Both are used in different kinds of
+  // normalization.
+  // For a description of x_coords, y_coords, see GetEdgeCoords above.
+  void CollectEdges(const TBOX &box, TBOX *bounding_box, LLSQ *llsq,
+                    std::vector<std::vector<int>> *x_coords,
+                    std::vector<std::vector<int>> *y_coords) const;
+
+private:
+  // DENORM indicating the transformations that this blob has undergone so far.
+  DENORM denorm_;
+}; // Blob structure.
+
+struct TWERD {
+  TWERD() : latin_script(false) {}
+  TWERD(const TWERD &src) {
+    CopyFrom(src);
+  }
+  ~TWERD() {
+    Clear();
+  }
+  TWERD &operator=(const TWERD &src) {
+    CopyFrom(src);
+    return *this;
+  }
+  // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal
+  // approximation along the way.
+  static TWERD *PolygonalCopy(bool allow_detailed_fx, WERD *src);
+  // Baseline normalizes the blobs in-place, recording the normalization in the
+  // DENORMs in the blobs.
+  void BLNormalize(const BLOCK *block, const ROW *row, Image pix, bool inverse, float x_height,
+                   float baseline_shift, bool numeric_mode, tesseract::OcrEngineMode hint,
+                   const TBOX *norm_box, DENORM *word_denorm);
+  // Copies the data and the blobs, but leaves next untouched.
+  void CopyFrom(const TWERD &src);
+  // Deletes owned data.
+  void Clear();
+  // Recomputes the bounding boxes of the blobs.
+  void ComputeBoundingBoxes();
+
+  // Returns the number of blobs in the word.
+  unsigned NumBlobs() const {
+    return blobs.size();
+  }
+  TBOX bounding_box() const;
+
+  // Merges the blobs from start to end, not including end, and deletes
+  // the blobs between start and end.
+  void MergeBlobs(unsigned start, unsigned end);
+
+#ifndef GRAPHICS_DISABLED
+  void plot(ScrollView *window);
+#endif // !GRAPHICS_DISABLED
+
+  std::vector<TBLOB *> blobs; // Blobs in word.
+  bool latin_script;          // This word is in a latin-based script.
+};
+
+/*----------------------------------------------------------------------
+              F u n c t i o n s
+----------------------------------------------------------------------*/
+// TODO(rays) Make divisible_blob and divide_blobs members of TBLOB.
+bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT *location);
+
+void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob, const TPOINT &location);
+
+} // namespace tesseract
+
+#endif