otary 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. otary-0.1.0/PKG-INFO +19 -0
  2. otary-0.1.0/README.md +0 -0
  3. otary-0.1.0/otary/__init__.py +0 -0
  4. otary-0.1.0/otary/geometry/__init__.py +32 -0
  5. otary-0.1.0/otary/geometry/continuous/entity.py +172 -0
  6. otary-0.1.0/otary/geometry/continuous/shape/circle.py +237 -0
  7. otary-0.1.0/otary/geometry/continuous/shape/ellipse.py +333 -0
  8. otary-0.1.0/otary/geometry/discrete/entity.py +511 -0
  9. otary-0.1.0/otary/geometry/discrete/linear/__init__.py +0 -0
  10. otary-0.1.0/otary/geometry/discrete/linear/directed/__init__.py +0 -0
  11. otary-0.1.0/otary/geometry/discrete/linear/directed/entity.py +91 -0
  12. otary-0.1.0/otary/geometry/discrete/linear/directed/vector.py +83 -0
  13. otary-0.1.0/otary/geometry/discrete/linear/directed/vectorized_linear_spline.py +44 -0
  14. otary-0.1.0/otary/geometry/discrete/linear/entity.py +107 -0
  15. otary-0.1.0/otary/geometry/discrete/linear/segment.py +261 -0
  16. otary-0.1.0/otary/geometry/discrete/linear/spline.py +111 -0
  17. otary-0.1.0/otary/geometry/discrete/point.py +124 -0
  18. otary-0.1.0/otary/geometry/discrete/shape/__init__.py +0 -0
  19. otary-0.1.0/otary/geometry/discrete/shape/polygon.py +894 -0
  20. otary-0.1.0/otary/geometry/discrete/shape/rectangle.py +418 -0
  21. otary-0.1.0/otary/geometry/discrete/shape/triangle.py +16 -0
  22. otary-0.1.0/otary/geometry/entity.py +227 -0
  23. otary-0.1.0/otary/geometry/utils/constants.py +7 -0
  24. otary-0.1.0/otary/geometry/utils/tools.py +110 -0
  25. otary-0.1.0/otary/image/__init__.py +25 -0
  26. otary-0.1.0/otary/image/base.py +340 -0
  27. otary-0.1.0/otary/image/components/__init__.py +10 -0
  28. otary-0.1.0/otary/image/components/drawer/__init__.py +22 -0
  29. otary-0.1.0/otary/image/components/drawer/drawer.py +242 -0
  30. otary-0.1.0/otary/image/components/drawer/utils/render.py +94 -0
  31. otary-0.1.0/otary/image/components/drawer/utils/tools.py +103 -0
  32. otary-0.1.0/otary/image/components/io/reader.py +154 -0
  33. otary-0.1.0/otary/image/components/io/utils/readfile.py +101 -0
  34. otary-0.1.0/otary/image/components/io/writer.py +63 -0
  35. otary-0.1.0/otary/image/components/transformer/__init__.py +0 -0
  36. otary-0.1.0/otary/image/components/transformer/components/__init__.py +23 -0
  37. otary-0.1.0/otary/image/components/transformer/components/binarizer/binarizer.py +152 -0
  38. otary-0.1.0/otary/image/components/transformer/components/binarizer/utils/thresholding.py +89 -0
  39. otary-0.1.0/otary/image/components/transformer/components/cropper/cropper.py +351 -0
  40. otary-0.1.0/otary/image/components/transformer/components/geometrizer/geometrizer.py +217 -0
  41. otary-0.1.0/otary/image/components/transformer/components/morphologyzer/morphologyzer.py +234 -0
  42. otary-0.1.0/otary/image/components/transformer/transformer.py +30 -0
  43. otary-0.1.0/otary/image/image.py +1738 -0
  44. otary-0.1.0/otary/image/utils/__init__.py +0 -0
  45. otary-0.1.0/otary/image/utils/colors.py +50 -0
  46. otary-0.1.0/otary/utils/__init__.py +0 -0
  47. otary-0.1.0/otary/utils/cv/ocrsingleoutput.py +15 -0
  48. otary-0.1.0/otary/utils/perf_eval.py +27 -0
  49. otary-0.1.0/otary/utils/tools.py +25 -0
  50. otary-0.1.0/pyproject.toml +77 -0
otary-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.3
2
+ Name: otary
3
+ Version: 0.1.0
4
+ Summary: Image and 2D Geometry library for easy and fast image manipulation
5
+ Author: Alexandre Poupeau
6
+ Author-email: alexandre.poupeau.contact@gmail.com
7
+ Requires-Python: >=3.11.2,<4.0.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Dist: metpy (>=1.6.2,<2.0.0)
12
+ Requires-Dist: opencv-python (>=4.10.0.84,<5.0.0.0)
13
+ Requires-Dist: pymupdf (>=1.24.12,<2.0.0)
14
+ Requires-Dist: setuptools (>=70.0.0,<71.0.0)
15
+ Requires-Dist: shapely (>=2.0.4,<3.0.0)
16
+ Requires-Dist: sympy (>=1.12,<2.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+
otary-0.1.0/README.md ADDED
File without changes
File without changes
@@ -0,0 +1,32 @@
1
+ """
2
+ Module to facilitate imports in geometry
3
+ """
4
+
5
+ __all__ = [
6
+ "DEFAULT_MARGIN_ANGLE_ERROR",
7
+ "Point",
8
+ "Segment",
9
+ "Vector",
10
+ "LinearSpline",
11
+ "VectorizedLinearSpline",
12
+ "Polygon",
13
+ "Triangle",
14
+ "Rectangle",
15
+ "Ellipse",
16
+ "Circle",
17
+ ]
18
+
19
+ # pylint: disable=cyclic-import
20
+ from otary.geometry.utils.constants import DEFAULT_MARGIN_ANGLE_ERROR
21
+ from otary.geometry.discrete.point import Point
22
+ from otary.geometry.discrete.linear.segment import Segment
23
+ from otary.geometry.discrete.linear.directed.vector import Vector
24
+ from otary.geometry.discrete.linear.spline import LinearSpline
25
+ from otary.geometry.discrete.linear.directed.vectorized_linear_spline import (
26
+ VectorizedLinearSpline,
27
+ )
28
+ from otary.geometry.discrete.shape.polygon import Polygon
29
+ from otary.geometry.discrete.shape.triangle import Triangle
30
+ from otary.geometry.discrete.shape.rectangle import Rectangle
31
+ from otary.geometry.continuous.shape.ellipse import Ellipse
32
+ from otary.geometry.continuous.shape.circle import Circle
@@ -0,0 +1,172 @@
1
+ """
2
+ ContinuousGeometryEntity module class
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from abc import ABC, abstractmethod
8
+
9
+ import cv2
10
+ import numpy as np
11
+
12
+ from otary.geometry.entity import GeometryEntity
13
+ from otary.geometry import Polygon, Rectangle
14
+
15
+
16
+ class ContinuousGeometryEntity(GeometryEntity, ABC):
17
+ """
18
+ ContinuousGeometryEntity class which is the abstract base class for
19
+ continuous or smooth geometry objects like circles, ellipse, etc...
20
+ """
21
+
22
+ DEFAULT_N_POINTS_POLYGONAL_APPROX = 1000
23
+
24
+ def __init__(
25
+ self, n_points_polygonal_approx: int = DEFAULT_N_POINTS_POLYGONAL_APPROX
26
+ ):
27
+ """Initialize a ContinuousGeometryEntity object
28
+
29
+ Args:
30
+ n_points_polygonal_approx (int, optional): n points to be used in
31
+ the polygonal approximation.
32
+ Defaults to DEFAULT_N_POINTS_POLYGONAL_APPROX.
33
+ """
34
+ self._n_points_polygonal_approx = n_points_polygonal_approx
35
+
36
+ # --------------------------------- PROPERTIES ------------------------------------
37
+
38
+ @property
39
+ def n_points_polygonal_approx(self) -> int:
40
+ """Get the number of points for the polygonal approximation.
41
+
42
+ Returns:
43
+ int: The number of points used in the polygonal approximation.
44
+ """
45
+ return self._n_points_polygonal_approx
46
+
47
+ @n_points_polygonal_approx.setter
48
+ def n_points_polygonal_approx(self, value):
49
+ """
50
+ Set the number of points for the polygonal approximation.
51
+ This would also update the polygonal approximation of the geometry entity.
52
+
53
+ Args:
54
+ value (int): The number of points to be used in the polygonal approximation.
55
+ """
56
+ self._n_points_polygonal_approx = value
57
+ self.update_polyapprox()
58
+
59
+ @property
60
+ def polyaprox(self) -> Polygon:
61
+ """Generate a polygonal approximation of the continuous geometry entity
62
+ No setter is defined for this property as it is a read-only property.
63
+
64
+ Returns:
65
+ Polygon: polygonal approximation of the continuous geometry entity
66
+ """
67
+ return self._polyapprox
68
+
69
+ @abstractmethod
70
+ def polygonal_approx(self, n_points: int, is_cast_int: bool) -> Polygon:
71
+ """Generate a polygonal approximation of the continuous geometry entity
72
+
73
+ Args:
74
+ n_points (int): number of points that make up the polygonal
75
+ approximation. The bigger the better to obtain more precise
76
+ results in intersection or other similar computations.
77
+
78
+ Returns:
79
+ Polygon: polygonal approximation of the continuous geometry entity
80
+ """
81
+
82
+ @property
83
+ def xmax(self) -> float:
84
+ """Get the maximum X coordinate of the geometry entity
85
+
86
+ Returns:
87
+ np.ndarray: 2D point
88
+ """
89
+ return self.polyaprox.xmax
90
+
91
+ @property
92
+ def xmin(self) -> float:
93
+ """Get the minimum X coordinate of the geometry entity
94
+
95
+ Returns:
96
+ np.ndarray: 2D point
97
+ """
98
+ return self.polyaprox.xmin
99
+
100
+ @property
101
+ def ymax(self) -> float:
102
+ """Get the maximum Y coordinate of the geometry entity
103
+
104
+ Returns:
105
+ np.ndarray: 2D point
106
+ """
107
+ return self.polyaprox.ymax
108
+
109
+ @property
110
+ def ymin(self) -> float:
111
+ """Get the minimum Y coordinate of the geometry entity
112
+
113
+ Returns:
114
+ np.ndarray: 2D point
115
+ """
116
+ return self.polyaprox.ymin
117
+
118
+ # ---------------------------- MODIFICATION METHODS -------------------------------
119
+
120
+ # done in the derived classes
121
+
122
+ # ------------------------------- CLASSIC METHODS ---------------------------------
123
+
124
+ def update_polyapprox(self) -> None:
125
+ """Update the polygonal approximation of the continuous geometry entity"""
126
+ self._polyapprox = self.polygonal_approx(
127
+ n_points=self.n_points_polygonal_approx, is_cast_int=False
128
+ )
129
+
130
+ def enclosing_axis_aligned_bbox(self) -> Rectangle:
131
+ """Compute the smallest area enclosing Axis-Aligned Bounding Box (AABB)
132
+ See: https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html
133
+
134
+ Returns:
135
+ Rectangle: Rectangle object
136
+ """
137
+ topleft_x, topleft_y, width, height = cv2.boundingRect(
138
+ array=self.polyaprox.asarray
139
+ )
140
+ bbox = np.array(
141
+ [
142
+ [topleft_x, topleft_y],
143
+ [topleft_x + width, topleft_y],
144
+ [topleft_x + width, topleft_y + height],
145
+ [topleft_x, topleft_y + height],
146
+ ]
147
+ )
148
+ return Rectangle(bbox)
149
+
150
+ def enclosing_oriented_bbox(self) -> Rectangle:
151
+ """Compute the smallest area enclosing Oriented Bounding Box (OBB)
152
+ See: https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html
153
+
154
+ Returns:
155
+ Rectangle: Rectangle object
156
+ """
157
+ rect = cv2.minAreaRect(self.polyaprox.asarray)
158
+ bbox = cv2.boxPoints(rect)
159
+ return Rectangle(bbox)
160
+
161
+ def enclosing_convex_hull(self) -> "Polygon":
162
+ """Compute the smallest area enclosing Convex Hull
163
+ See: https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html
164
+
165
+ Returns:
166
+ Polygon: Polygon object
167
+ """
168
+ # pylint: disable=import-outside-toplevel
169
+ from otary.geometry import Polygon
170
+
171
+ convexhull = np.squeeze(cv2.convexHull(self.polyaprox.asarray))
172
+ return Polygon(convexhull)
@@ -0,0 +1,237 @@
1
+ """
2
+ Circle Geometric Object
3
+ """
4
+
5
+ from typing import Self, Optional
6
+ import math
7
+
8
+ import numpy as np
9
+ from numpy.typing import NDArray
10
+
11
+ from shapely import Polygon as SPolygon, LinearRing
12
+
13
+ from otary.geometry.utils.tools import rotate_2d_points
14
+ from otary.geometry.continuous.entity import ContinuousGeometryEntity
15
+ from otary.geometry import Ellipse, Polygon
16
+ from otary.utils.tools import assert_transform_shift_vector
17
+
18
+
19
+ class Circle(Ellipse):
20
+ """Circle geometrical object"""
21
+
22
+ def __init__(
23
+ self,
24
+ center: NDArray,
25
+ radius: float,
26
+ n_points_polygonal_approx: int = ContinuousGeometryEntity.DEFAULT_N_POINTS_POLYGONAL_APPROX,
27
+ ):
28
+ """Initialize a Circle geometrical object
29
+
30
+ Args:
31
+ center (NDArray): center 2D point
32
+ radius (float): radius value
33
+ n_points_polygonal_approx (int, optional): number of points to be used in
34
+ the polygonal approximation of the circle. Defaults to
35
+ ContinuousGeometryEntity.DEFAULT_N_POINTS_POLYGONAL_APPROX.
36
+ """
37
+ super().__init__(
38
+ foci1=center,
39
+ foci2=center,
40
+ semi_major_axis=radius,
41
+ n_points_polygonal_approx=n_points_polygonal_approx,
42
+ )
43
+ self.center = center
44
+ self.radius = radius
45
+ self.update_polyapprox()
46
+
47
+ # --------------------------------- PROPERTIES ------------------------------------
48
+
49
+ @property
50
+ def perimeter(self) -> float:
51
+ """Perimeter of the circle
52
+
53
+ Returns:
54
+ float: perimeter value
55
+ """
56
+ return 2 * math.pi * self.radius
57
+
58
+ @property
59
+ def centroid(self) -> NDArray:
60
+ """Center of the circle
61
+
62
+ Returns:
63
+ float: center 2D point
64
+ """
65
+ return self.center
66
+
67
+ @property
68
+ def shapely_surface(self) -> SPolygon:
69
+ """Returns the Shapely.Polygon as an surface representation of the Circle.
70
+ See https://shapely.readthedocs.io/en/stable/reference/shapely.Polygon.html
71
+
72
+ Returns:
73
+ Polygon: shapely.Polygon object
74
+ """
75
+ return SPolygon(self.polyaprox.asarray, holes=None)
76
+
77
+ @property
78
+ def shapely_edges(self) -> LinearRing:
79
+ """Returns the Shapely.LinearRing as a curve representation of the Circle.
80
+ See https://shapely.readthedocs.io/en/stable/reference/shapely.LinearRing.html
81
+
82
+ Returns:
83
+ LinearRing: shapely.LinearRing object
84
+ """
85
+ return LinearRing(coordinates=self.polyaprox.asarray)
86
+
87
+ def polygonal_approx(self, n_points: int, is_cast_int: bool = False) -> Polygon:
88
+ """Generate a Polygon object that is an approximation of the circle
89
+ as a discrete geometrical object made up of only points and segments.
90
+
91
+ Args:
92
+ n_points (int): number of points that make up the circle
93
+ polygonal approximation
94
+ is_cast_int (bool): whether to cast to int the points coordinates or
95
+ not. Defaults to False
96
+
97
+ Returns:
98
+ Polygon: Polygon representing the circle as a succession of n points
99
+
100
+ """
101
+ points = []
102
+ for theta in np.linspace(0, 2 * math.pi, n_points):
103
+ x = self.center[0] + self.radius * math.cos(theta)
104
+ y = self.center[1] + self.radius * math.sin(theta)
105
+ points.append([x, y])
106
+
107
+ poly = Polygon(points=np.asarray(points), is_cast_int=is_cast_int)
108
+ return poly
109
+
110
+ def curvature(self, point: Optional[NDArray] = None) -> float:
111
+ """Curvature of circle is a constant and does not depend on a position of
112
+ a point
113
+
114
+ Returns:
115
+ float: curvature value
116
+ """
117
+ return 1 / self.radius
118
+
119
+ @property
120
+ def xmax(self) -> float:
121
+ """Get the maximum X coordinate of the geometry entity
122
+
123
+ Returns:
124
+ NDArray: 2D point
125
+ """
126
+ return self.center[0] + self.radius
127
+
128
+ @property
129
+ def xmin(self) -> float:
130
+ """Get the minimum X coordinate of the geometry entity
131
+
132
+ Returns:
133
+ NDArray: 2D point
134
+ """
135
+ return self.center[0] - self.radius
136
+
137
+ @property
138
+ def ymax(self) -> float:
139
+ """Get the maximum Y coordinate of the geometry entity
140
+
141
+ Returns:
142
+ NDArray: 2D point
143
+ """
144
+ return self.center[1] + self.radius
145
+
146
+ @property
147
+ def ymin(self) -> float:
148
+ """Get the minimum Y coordinate of the geometry entity
149
+
150
+ Returns:
151
+ NDArray: 2D point
152
+ """
153
+ return self.center[1] - self.radius
154
+
155
+ # ---------------------------- MODIFICATION METHODS -------------------------------
156
+
157
+ def rotate(
158
+ self,
159
+ angle: float,
160
+ is_degree: bool = False,
161
+ is_clockwise: bool = True,
162
+ pivot: Optional[NDArray] = None,
163
+ ) -> Self:
164
+ """Rotate the circle around a pivot point.
165
+
166
+ Args:
167
+ angle (float): angle by which to rotate the circle
168
+ is_degree (bool, optional): whether the angle is in degrees. Defaults to False.
169
+ is_clockwise (bool, optional): whether the rotation is clockwise. Defaults to True.
170
+ pivot (Optional[NDArray], optional): pivot point around which to rotate. Defaults to None.
171
+
172
+ Returns:
173
+ Self: rotated circle object
174
+ """
175
+ if pivot is None:
176
+ # If no pivot is given, the circle is rotated around its center
177
+ # and thus is not modified
178
+ return self
179
+
180
+ self.center = rotate_2d_points(
181
+ points=self.center,
182
+ angle=angle,
183
+ is_degree=is_degree,
184
+ is_clockwise=is_clockwise,
185
+ pivot=pivot,
186
+ )
187
+ self.update_polyapprox()
188
+ return self
189
+
190
+ def shift(self, vector: NDArray) -> Self:
191
+ """Shift the circle by a given vector.
192
+
193
+ Args:
194
+ vector (NDArray): 2D vector by which to shift the circle
195
+
196
+ Returns:
197
+ Self: shifted circle object
198
+ """
199
+ vector = assert_transform_shift_vector(vector=vector)
200
+ self.center += vector
201
+ self.update_polyapprox()
202
+ return self
203
+
204
+ def normalize(self, x: float, y: float) -> Self:
205
+ """Normalize the circle by dividing the points by a norm on the x and y
206
+ coordinates. This does not change the circle radius.
207
+
208
+ Args:
209
+ x (float): x coordinate norm
210
+ y (float): y coordinate norm
211
+
212
+ Returns:
213
+ Self: normalized circle object
214
+ """
215
+ self.center = self.center / np.array([x, y])
216
+ self.update_polyapprox()
217
+ return self
218
+
219
+ # ------------------------------- CLASSIC METHODS ---------------------------------
220
+
221
+ def copy(self) -> Self:
222
+ """Copy the circle object
223
+
224
+ Returns:
225
+ Self: copied circle object
226
+ """
227
+ return type(self)(
228
+ center=self.center,
229
+ radius=self.radius,
230
+ n_points_polygonal_approx=self.n_points_polygonal_approx,
231
+ )
232
+
233
+ def __str__(self) -> str:
234
+ return f"Circle(center={self.center}, radius={self.radius})"
235
+
236
+ def __repr__(self):
237
+ return f"Circle(center={self.center}, radius={self.radius})"