nafrec 0.3.0__py3-none-any.whl

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.
nafrec/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ from .main import load_rfdetr_model, load_trocr_model, get_text_predictions, classify_and_recognize, run, make_args
2
+ from .nafrec import NAFRec
3
+ from .seg_inference import predict_polygons
4
+ from .paddle_layout import load_paddle_layout_model, PaddleLayoutDetector
5
+ from .utils import get_default_region, get_line_regions, order_regions_lines, get_iou
6
+
7
+ __all__ = [
8
+ "NAFRec",
9
+ "load_rfdetr_model",
10
+ "load_trocr_model",
11
+ "get_text_predictions",
12
+ "classify_and_recognize",
13
+ "run",
14
+ "make_args",
15
+ "predict_polygons",
16
+ "load_paddle_layout_model",
17
+ "PaddleLayoutDetector",
18
+ "get_default_region",
19
+ "get_line_regions",
20
+ "order_regions_lines",
21
+ "get_iou"
22
+ ]
@@ -0,0 +1,237 @@
1
+ from torchvision.io import read_image, ImageReadMode
2
+ import numpy as np
3
+ from torchvision.transforms import v2 as transforms_v2
4
+ import torch
5
+ import cv2
6
+
7
+
8
+ def load_with_torchvision(img_path):
9
+ """
10
+ Load an image using torchvision and convert to numpy array.
11
+
12
+ Args:
13
+ img_path (str or Path): Path to the image file
14
+
15
+ Returns:
16
+ numpy.ndarray: Image array in RGB format with shape (H, W, C)
17
+ """
18
+ # Read as tensor
19
+ img_tensor = read_image(str(img_path), mode=ImageReadMode.RGB)
20
+ # Convert to numpy: (C, H, W) -> (H, W, C)
21
+ img_np = img_tensor.permute(1, 2, 0).numpy()
22
+ return img_np
23
+
24
+
25
+ def preprocess_resize_smallerof_wh_torch_transform(image, max_size=1024, normalize=True):
26
+ """
27
+ Resize so that the *smaller* of (width, height) is at most max_size.
28
+ The larger dimension is allowed to grow arbitrarily.
29
+
30
+ Args:
31
+ image: torch.Tensor (C, H, W) or PIL Image
32
+ max_size: maximum size for the *smaller* dimension
33
+ normalize: whether to normalize to [0, 1] range
34
+
35
+ Returns:
36
+ torch.Tensor (C, H, W) or PIL Image (same type as input)
37
+ """
38
+ # Convert numpy -> torch
39
+ if isinstance(image, np.ndarray):
40
+ image = torch.from_numpy(image)
41
+ if image.ndim == 3 and image.shape[2] in [1, 3]:
42
+ image = image.permute(2, 0, 1)
43
+
44
+ # Extract H, W
45
+ if isinstance(image, torch.Tensor):
46
+ _, h, w = image.shape
47
+ else: # PIL
48
+ w, h = image.size
49
+
50
+ min_dim = min(h, w)
51
+ transform_list = []
52
+
53
+ # Resize ONLY if the *smaller* dimension is too large
54
+ if min_dim > max_size:
55
+ scale = max_size / min_dim
56
+ new_h = int(round(h * scale))
57
+ new_w = int(round(w * scale))
58
+ transform_list.append(
59
+ transforms_v2.Resize(size=[new_h, new_w], antialias=True)
60
+ )
61
+
62
+ # Normalize
63
+ if normalize:
64
+ transform_list.append(transforms_v2.ToDtype(torch.float32, scale=True))
65
+
66
+ # Apply transforms
67
+ if transform_list:
68
+ transform = transforms_v2.Compose(transform_list)
69
+ resized = transform(image)
70
+ else:
71
+ resized = image
72
+
73
+ return resized
74
+
75
+
76
+ def preprocess_resize_torch_transform(image, max_size=1024, normalize=True):
77
+ """
78
+ Resize using torchvision.transforms.v2 (most concise, PyTorch only).
79
+
80
+ Args:
81
+ image: torch.Tensor (C, H, W) or PIL Image
82
+ max_size: maximum size for the longer dimension
83
+ normalize: whether to normalize to [0, 1] range
84
+
85
+ Returns:
86
+ torch.Tensor (C, H, W) or PIL Image (same type as input)
87
+ """
88
+ # Convert to tensor if numpy
89
+ if isinstance(image, np.ndarray):
90
+ image = torch.from_numpy(image)
91
+ if image.ndim == 3 and image.shape[2] in [1, 3]:
92
+ image = image.permute(2, 0, 1)
93
+
94
+ c, h, w = image.shape if isinstance(
95
+ image, torch.Tensor) else (None, *image.size[::-1])
96
+
97
+ # Build transform pipeline
98
+ transform_list = []
99
+
100
+ # Add resize if needed
101
+ if h > max_size or w > max_size:
102
+ transform_list.append(transforms_v2.Resize(
103
+ size=None, max_size=max_size, antialias=True))
104
+
105
+ # Add normalization
106
+ if normalize:
107
+ transform_list.append(transforms_v2.ToDtype(torch.float32, scale=True))
108
+
109
+ # Apply transforms
110
+ if transform_list:
111
+ transform = transforms_v2.Compose(transform_list)
112
+ resized = transform(image)
113
+ else:
114
+ resized = image
115
+
116
+ return resized
117
+
118
+
119
+ def upscale_mask_opencv(mask, bbox, upscaled_bbox_shape):
120
+ """Upscale using OpenCV resize with nearest neighbor."""
121
+ x1, y1, x2, y2 = map(int, bbox)
122
+ cropped_mask = mask[y1:y2, x1:x2]
123
+ mask_uint8 = cropped_mask.astype(np.uint8)
124
+ upscaled = cv2.resize(mask_uint8,
125
+ upscaled_bbox_shape,
126
+ interpolation=cv2.INTER_NEAREST)
127
+
128
+ return upscaled * 255
129
+
130
+
131
+ def upscale_bbox(bbox, original_shape, mask_shape):
132
+ """
133
+ Upscale bounding box coordinates from mask resolution to original image resolution.
134
+
135
+ Parameters:
136
+ -----------
137
+ bbox : np.ndarray or list
138
+ Bounding box coordinates in format [x_min, y_min, x_max, y_max]
139
+ in the mask's coordinate system
140
+ original_shape : tuple
141
+ Original image shape (H, W) or (H, W, C) - e.g., (4545, 5527, 3)
142
+ mask_shape : tuple
143
+ Mask shape (H, W) - e.g., (631, 768)
144
+
145
+ Returns:
146
+ --------
147
+ np.ndarray
148
+ Upscaled bounding box as integer coordinates [x_min, y_min, x_max, y_max]
149
+ """
150
+
151
+ # Ensure bbox is a numpy array
152
+ bbox = np.array(bbox)
153
+
154
+ # Extract height and width from shapes
155
+ original_h, original_w = original_shape[0], original_shape[1]
156
+ mask_h, mask_w = mask_shape[0], mask_shape[1]
157
+
158
+ # Calculate scale factors
159
+ scale_x = original_w / mask_w # Width scaling
160
+ scale_y = original_h / mask_h # Height scaling
161
+
162
+ # Unpack bbox coordinates
163
+ x_min, y_min, x_max, y_max = bbox
164
+
165
+ # Scale coordinates
166
+ x_min_scaled = x_min * scale_x
167
+ y_min_scaled = y_min * scale_y
168
+ x_max_scaled = x_max * scale_x
169
+ y_max_scaled = y_max * scale_y
170
+
171
+ # limit to range 0 to original width/height
172
+ if x_min_scaled < 0:
173
+ x_min_scaled = 0
174
+ if y_min_scaled < 0:
175
+ y_min_scaled = 0
176
+ if x_max_scaled > original_w:
177
+ x_max_scaled = original_w
178
+ if y_max_scaled > original_h:
179
+ y_max_scaled = original_h
180
+
181
+ # Convert to integers (rounding to nearest)
182
+ bbox_scaled = np.array([
183
+ x_min_scaled,
184
+ y_min_scaled,
185
+ x_max_scaled,
186
+ y_max_scaled
187
+ ]).astype(np.int32)
188
+
189
+ return bbox_scaled
190
+
191
+
192
+ def crop_line(image, polygon):
193
+ """Crops predicted text line based on the polygon coordinates
194
+ and returns pasted text line image with white background.
195
+
196
+ Args:
197
+ image: Input image array.
198
+ polygon: List of coordinate pairs defining the text line polygon.
199
+
200
+ Returns:
201
+ numpy.ndarray: Cropped text line image with white background around polygon.
202
+ """
203
+ polygon = np.array([[int(lst[0]), int(lst[1])]
204
+ for lst in polygon], dtype=np.int32)
205
+ rect = cv2.boundingRect(polygon)
206
+ cropped_image = image[rect[1]: rect[1] +
207
+ rect[3], rect[0]: rect[0] + rect[2]]
208
+ mask = np.zeros(
209
+ [cropped_image.shape[0], cropped_image.shape[1]], dtype=np.uint8)
210
+ cv2.drawContours(mask, [
211
+ polygon - np.array([[rect[0], rect[1]]])], -1, (255, 255, 255), -1, cv2.LINE_AA)
212
+ res = cv2.bitwise_and(cropped_image, cropped_image, mask=mask)
213
+ wbg = np.ones_like(cropped_image, np.uint8)*255
214
+ cv2.bitwise_not(wbg, wbg, mask=mask)
215
+ # Overlap the resulted cropped image on the white background
216
+ dst = wbg+res
217
+ return dst
218
+
219
+
220
+ def crop_lines(polygons, image):
221
+ """Returns a list of line images cropped following the
222
+ detected polygon coordinates.
223
+
224
+ Crop multiple text lines from an image based on polygon coordinates.
225
+
226
+ Args:
227
+ polygons: List of polygons, each containing coordinate pairs.
228
+ image: Input image array.
229
+
230
+ Returns:
231
+ list: List of cropped text line images.
232
+ """
233
+ cropped_lines = []
234
+ for polygon in polygons:
235
+ cropped_line = crop_line(image, polygon)
236
+ cropped_lines.append(cropped_line)
237
+ return cropped_lines