dsa-helpers 3.1.8.dev1__tar.gz → 3.2.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 (57) hide show
  1. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/.gitignore +1 -0
  2. {dsa_helpers-3.1.8.dev1/dsa_helpers.egg-info → dsa_helpers-3.2.0}/PKG-INFO +1 -1
  3. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/__init__.py +1 -1
  4. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/gpd_utils.py +3 -2
  5. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/yolo/inference.py +253 -18
  6. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/utils.py +2 -2
  7. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0/dsa_helpers.egg-info}/PKG-INFO +1 -1
  8. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/pyproject.toml +1 -1
  9. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/.readthedocs.yaml +0 -0
  10. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/LICENSE +0 -0
  11. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/README.md +0 -0
  12. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/docker/2.0.6-pytorch2.9.0-cuda13.0-cudnn9/Dockerfile +0 -0
  13. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/docker/README.md +0 -0
  14. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/docs/requirements.txt +0 -0
  15. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/docs/source/conf.py +0 -0
  16. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/docs/source/index.rst +0 -0
  17. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/cli_utils.py +0 -0
  18. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/dash/__init__.py +0 -0
  19. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
  20. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/dash/header.py +0 -0
  21. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/dash/header_callbacks.py +0 -0
  22. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/girder_utils.py +0 -0
  23. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/image_utils.py +0 -0
  24. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/imread.py +0 -0
  25. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/imwrite.py +0 -0
  26. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/__init__.py +0 -0
  27. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/callbacks.py +0 -0
  28. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
  29. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/datasets/__init__.py +0 -0
  30. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/datasets/utils.py +0 -0
  31. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/evaluate.py +0 -0
  32. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/inference_results.py +0 -0
  33. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/metrics.py +0 -0
  34. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/object_detection/__init__.py +0 -0
  35. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
  36. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
  37. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/object_detection/utils.py +0 -0
  38. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
  39. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/datasets.py +0 -0
  40. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/evaluate.py +0 -0
  41. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
  42. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/train.py +0 -0
  43. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/transforms.py +0 -0
  44. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/segformer_semantic_segmentation/utils.py +0 -0
  45. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/transforms/__init__.py +0 -0
  46. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
  47. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/yolo/__init__.py +0 -0
  48. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/yolo/tiling.py +0 -0
  49. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/ml/yolo/train.py +0 -0
  50. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/mongo_utils.py +0 -0
  51. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/tile_utils.py +0 -0
  52. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers/tiling.py +0 -0
  53. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers.egg-info/SOURCES.txt +0 -0
  54. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers.egg-info/dependency_links.txt +0 -0
  55. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers.egg-info/requires.txt +0 -0
  56. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/dsa_helpers.egg-info/top_level.txt +0 -0
  57. {dsa_helpers-3.1.8.dev1 → dsa_helpers-3.2.0}/setup.cfg +0 -0
@@ -1,6 +1,7 @@
1
1
  # Python environment directories.
2
2
  env/
3
3
  pyenv/
4
+ .venv/
4
5
 
5
6
  *.pyc
6
7
  *.egg-info
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dsa_helpers
3
- Version: 3.1.8.dev1
3
+ Version: 3.2.0
4
4
  Summary: Utility functions for working with the DSA girder client.
5
5
  Author-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
6
6
  Maintainer-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
@@ -3,7 +3,7 @@ from .imread import imread
3
3
  from .imwrite import imwrite
4
4
 
5
5
  # Version of the dsa-helpers package
6
- __version__ = "3.1.8dev1"
6
+ __version__ = "3.2.0"
7
7
 
8
8
  # To avoid slow downs, do not allow from dsa_helpers import * to import anything.
9
9
  __all__ = []
@@ -14,6 +14,7 @@ Functions:
14
14
 
15
15
  import geopandas as gpd
16
16
  import numpy as np
17
+ import pandas as pd
17
18
  from rasterio.features import rasterize
18
19
  from rdp import rdp
19
20
  from tqdm import tqdm
@@ -27,12 +28,12 @@ import matplotlib.pyplot as plt
27
28
 
28
29
 
29
30
  def remove_contained_boxes(
30
- df: gpd.GeoDataFrame, thr: float
31
+ df: gpd.GeoDataFrame | pd.DataFrame, thr: float
31
32
  ) -> gpd.GeoDataFrame:
32
33
  """Remove boxes contained in other boxes, or mostly contained.
33
34
 
34
35
  Args:
35
- df (geopandas.GeoDataFrame): Data for each box, must contain the
36
+ df (geopandas.GeoDataFrame | pandas.DataFrame): Data for each box, must contain the
36
37
  x1, y1, x2, y2, conf columns with point 1 being top left of
37
38
  the box and point 2 and bottom right of box.
38
39
  thr (float): The threshold of the box that must be contained by
@@ -1,5 +1,6 @@
1
- import large_image_source_openslide, ultralytics, large_image
1
+ import large_image_source_openslide, ultralytics
2
2
  import geopandas as gpd
3
+ import pandas as pd
3
4
  import cv2 as cv
4
5
  import numpy as np
5
6
  from shapely import Polygon
@@ -9,6 +10,237 @@ from ...utils import non_max_suppression, return_mag_and_resolution
9
10
  from ...gpd_utils import remove_contained_boxes
10
11
 
11
12
 
13
+ def yolo_inference_no_iterator(
14
+ model: str | ultralytics.YOLO,
15
+ wsi_fp: str,
16
+ mag: float | None = 20,
17
+ mm_px: float | None = None,
18
+ tile_size: int = 640,
19
+ overlap: int = 160,
20
+ batch_size: int = 64,
21
+ agnostic_nms: bool = True,
22
+ conf_thr: float = 0.25,
23
+ iou: float = 0.7,
24
+ tile_area_threshold: float = 0.25,
25
+ pad_rgb: tuple[int, int, int] = (114, 114, 114),
26
+ device: str | None = None,
27
+ ):
28
+ """Perform YOLO inference on a whole slide image. This version differs
29
+ from yolo_inference in that it does not use the eager iterator.
30
+
31
+ Args:
32
+ model (str | ultralytics.YOLO): YOLO model or path to the model
33
+ weights.
34
+ wsi_fp (str): File path to the whole slide image.
35
+ mag (float | None, optional): Desired magnification for tiling.
36
+ Note magnification meaning can vary among scanners. We
37
+ convert this to a corresponding resolution defined by a
38
+ standard magnification to resolution ratio (Emory scanner).
39
+ Default is None. Defined by mm_px, or if that is None than
40
+ the scan resolution along the x axis is used.
41
+ mm_px (float | None, optional): Micrometers per pixel for the
42
+ desired resolution. If not provided, will be inferred from
43
+ the mag parameter by standardizing to the Emory scanner. If
44
+ both are not provided, the scan resolution in the x
45
+ direction is used. Default is None.
46
+ tile_size (int, optional): Image is tiled into smaller images,
47
+ tiles, of this size at the desired resolution.
48
+ overlap (int, optional): The number of pixels to overlap between
49
+ tiles. Default is 160.
50
+ batch_size (int, optional): The number of tiles to process in a
51
+ single batch. Used both for the eager iterator and the YOLO
52
+ training. Default is 64.
53
+ agnostic_nms (bool, optional): Whether to use agnostic NMS.
54
+ Default is True.
55
+ conf_thr (float, optional): The confidence threshold for the
56
+ NMS. Default is 0.25.
57
+ iou (float, optional): The IoU threshold for the NMS. Default is
58
+ 0.7.
59
+ tile_area_threshold (float, optional): The threshold for the tile
60
+ area in region. Default is 0.25.
61
+ pad_rgb (tuple[int, int, int], optional): The RGB values to pad
62
+ the tiles with. Default is (114, 114, 114).
63
+ device (str | None, optional): The device to use for inference.
64
+ Default is None, will use "cuda" if available, otherwise
65
+ "cpu". You can specify values like "cuda:0", "0", or
66
+ multiple devices separated by commas.
67
+
68
+ Returns:
69
+ dict: A dictionary containing the inference results.
70
+ - gdf (geopandas.GeoDataFrame): The inference results as a
71
+ GeoDataFrame.
72
+ - mag (float): The magnification used for inference.
73
+ - mm_px (float): The micrometers per pixel used for inference.
74
+ - time (dict): A dictionary containing the time taken for
75
+ inference.
76
+ - total (float): The total time taken for inference.
77
+
78
+ Raises:
79
+ ValueError: If both mag and mm_px are provided.
80
+
81
+ """
82
+ print("This function has not been reviewed yet, use with caution.")
83
+ start_time = perf_counter()
84
+
85
+ if isinstance(model, str):
86
+ model = ultralytics.YOLO(model)
87
+
88
+ ts = large_image_source_openslide.open(wsi_fp)
89
+ ts_metadata = ts.getMetadata()
90
+
91
+ mm_x = ts_metadata["mm_x"]
92
+ mm_y = ts_metadata["mm_y"]
93
+
94
+ # Get the desired resolution.
95
+ if mag is not None and mm_px is not None:
96
+ raise ValueError("Only one of mag or mm_px can be provided.")
97
+ if mag is None and mm_px is None:
98
+ # Use the scan resolution, we use the x resolution.
99
+ mag, mm_px = return_mag_and_resolution(mm_px=mm_x)
100
+ else:
101
+ mag, mm_px = return_mag_and_resolution(mag=mag, mm_px=mm_px)
102
+
103
+ # Calculate the x and y size of the tile at scan resolution.
104
+ # desired resolution x sf_* -> scan resolution
105
+ sf_x = mm_px / mm_x
106
+ sf_y = mm_px / mm_y
107
+
108
+ scan_tile_x = int(tile_size * sf_x)
109
+ scan_tile_y = int(tile_size * sf_y)
110
+
111
+ stride = tile_size - overlap
112
+ scan_stride_x = int(stride * sf_x)
113
+ scan_stride_y = int(stride * sf_y)
114
+
115
+ # Calculate the x, y coordinates
116
+ xys = []
117
+
118
+ right = ts_metadata["sizeX"]
119
+ bottom = ts_metadata["sizeY"]
120
+
121
+ for x in range(0, right, scan_stride_x):
122
+ for y in range(0, bottom, scan_stride_y):
123
+ xys.append((x, y))
124
+
125
+ # Process in batches.
126
+ batch_indices = np.arange(0, len(xys), batch_size)
127
+
128
+ # Iterate through the WSI.
129
+ boxes = []
130
+
131
+ # Calculate the tile area to be above the threshold.
132
+ tile_area = tile_size * tile_size
133
+ minimum_tile_area = tile_area * tile_area_threshold
134
+
135
+ for batch_idx in batch_indices:
136
+ # Get the tiles.
137
+ batch_xys = xys[batch_idx : batch_idx + batch_size]
138
+
139
+ tiles = []
140
+
141
+ tile_x_coords = []
142
+ tile_y_coords = []
143
+
144
+ for xy in batch_xys:
145
+ x1, y1 = xy
146
+
147
+ x2 = x1 + scan_tile_x
148
+
149
+ if x2 > right:
150
+ x2 = right
151
+
152
+ y2 = y1 + scan_tile_y
153
+
154
+ if y2 > bottom:
155
+ y2 = bottom
156
+
157
+ # Get the tiles.
158
+ tile = ts.getRegion(
159
+ region={
160
+ "left": x1,
161
+ "top": y1,
162
+ "right": x2,
163
+ "bottom": y2,
164
+ },
165
+ format="numpy",
166
+ scale={"mm_x": mm_px, "mm_y": mm_px},
167
+ )[0][:, :, :3].copy()
168
+
169
+ # Pad the tile if it is not the right shape.
170
+ tile_h, tile_w, _ = tile.shape
171
+
172
+ tile_area = tile_h * tile_w
173
+
174
+ if tile_area < minimum_tile_area:
175
+ continue
176
+
177
+ tile_x_coords.append(x1)
178
+ tile_y_coords.append(y1)
179
+
180
+ if tile_h != tile_size or tile_w != tile_size:
181
+ tile = cv.copyMakeBorder(
182
+ tile,
183
+ 0,
184
+ tile_size - tile_h,
185
+ 0,
186
+ tile_size - tile_w,
187
+ cv.BORDER_CONSTANT,
188
+ value=pad_rgb,
189
+ )
190
+ # Convert to BGR.
191
+ tile = cv.cvtColor(tile, cv.COLOR_RGB2BGR)
192
+
193
+ tiles.append(tile)
194
+
195
+ if len(tiles) == 0:
196
+ continue
197
+
198
+ results = model(
199
+ tiles,
200
+ imgsz=tile_size,
201
+ batch=batch_size,
202
+ agnostic_nms=agnostic_nms,
203
+ verbose=False,
204
+ conf=conf_thr,
205
+ iou=iou,
206
+ device=device,
207
+ )
208
+
209
+ # Loop through tile variables.
210
+ for result, x, y in zip(results, tile_x_coords, tile_y_coords):
211
+ xyxys = result.boxes.xyxy
212
+ cls_list = result.boxes.cls
213
+ conf_list = result.boxes.conf
214
+
215
+ for xyxy, cls, conf in zip(xyxys, cls_list, conf_list):
216
+ # cls and conf are tensorts in device, convert to to int and float
217
+ cls = int(cls)
218
+ conf = float(conf)
219
+
220
+ x1, y1, x2, y2 = xyxy
221
+
222
+ # (1) scale to scan resolution, (2) shift to tile location.
223
+ x1 = int(x1 * sf_x) + x
224
+ y1 = int(y1 * sf_y) + y
225
+ x2 = int(x2 * sf_x) + x
226
+ y2 = int(y2 * sf_y) + y
227
+
228
+ # Create the polygon.
229
+ geom = Polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)])
230
+ boxes.append([cls, x1, y1, x2, y2, conf, geom])
231
+
232
+ gdf = gpd.GeoDataFrame(
233
+ boxes, columns=["label", "x1", "y1", "x2", "y2", "conf", "geometry"]
234
+ )
235
+ gdf["box_area"] = gdf.geometry.area
236
+
237
+ # Clean up predictions further.
238
+ gdf = non_max_suppression(gdf, conf_thr)
239
+ gdf = remove_contained_boxes(gdf, iou).reset_index(drop=True)
240
+
241
+ return gdf, mag, mm_px
242
+
243
+
12
244
  def yolo_inference(
13
245
  model: str | ultralytics.YOLO,
14
246
  wsi_fp: str,
@@ -80,8 +312,10 @@ def yolo_inference(
80
312
 
81
313
  Raises:
82
314
  ValueError: If both mag and mm_px are provided.
315
+
83
316
  """
84
317
  start_time = perf_counter()
318
+
85
319
  if isinstance(model, str):
86
320
  model = ultralytics.YOLO(model)
87
321
 
@@ -144,7 +378,7 @@ def yolo_inference(
144
378
 
145
379
  # Loop through tile variables.
146
380
  for result, x, y in zip(results, tile_x_coords, tile_y_coords):
147
- xyxys = result.boxes.xyxyn
381
+ xyxys = result.boxes.xyxy
148
382
  cls_list = result.boxes.cls
149
383
  conf_list = result.boxes.conf
150
384
 
@@ -156,10 +390,10 @@ def yolo_inference(
156
390
  x1, y1, x2, y2 = xyxy
157
391
 
158
392
  # (1) scale to scan resolution, (2) shift to tile location.
159
- x1 = int(x1 * scan_tile_x) + x
160
- y1 = int(y1 * scan_tile_y) + y
161
- x2 = int(x2 * scan_tile_x) + x
162
- y2 = int(y2 * scan_tile_y) + y
393
+ x1 = int(x1 * sf_x) + x
394
+ y1 = int(y1 * sf_y) + y
395
+ x2 = int(x2 * sf_x) + x
396
+ y2 = int(y2 * sf_y) + y
163
397
 
164
398
  # Create the polygon.
165
399
  geom = Polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)])
@@ -201,7 +435,7 @@ def yolo_inference_on_region(
201
435
  tile_area_threshold: float = 0.25,
202
436
  nms_iou_thr: float = 0.25,
203
437
  remove_contained_thr: float = 0.7,
204
- ) -> tuple[gpd.GeoDataFrame, float, float]:
438
+ ) -> tuple[gpd.GeoDataFrame | pd.DataFrame, float, float]:
205
439
  """Inference on a region of a WSI using a YOLO model.
206
440
 
207
441
  Args:
@@ -317,10 +551,8 @@ def yolo_inference_on_region(
317
551
  minimum_tile_area = tile_area * tile_area_threshold
318
552
 
319
553
  for batch_idx in batch_indices:
320
- xy_index = batch_idx * batch_size
321
-
322
554
  # Get the tiles.
323
- batch_xys = xys[xy_index : xy_index + batch_size]
555
+ batch_xys = xys[batch_idx : batch_idx + batch_size]
324
556
 
325
557
  tiles = []
326
558
 
@@ -348,7 +580,7 @@ def yolo_inference_on_region(
348
580
  "right": x2,
349
581
  "bottom": y2,
350
582
  },
351
- format=large_image.constants.TILE_FORMAT_NUMPY,
583
+ format="numpy",
352
584
  scale={"mm_x": mm_px, "mm_y": mm_px},
353
585
  )[0][:, :, :3].copy()
354
586
 
@@ -378,6 +610,9 @@ def yolo_inference_on_region(
378
610
 
379
611
  tiles.append(tile)
380
612
 
613
+ if len(tiles) == 0:
614
+ continue
615
+
381
616
  results = model(
382
617
  tiles,
383
618
  imgsz=tile_size,
@@ -391,7 +626,7 @@ def yolo_inference_on_region(
391
626
 
392
627
  # Loop through tile variables.
393
628
  for result, x, y in zip(results, tile_x_coords, tile_y_coords):
394
- xyxys = result.boxes.xyxyn
629
+ xyxys = result.boxes.xyxy
395
630
  cls_list = result.boxes.cls
396
631
  conf_list = result.boxes.conf
397
632
 
@@ -403,10 +638,10 @@ def yolo_inference_on_region(
403
638
  x1, y1, x2, y2 = xyxy
404
639
 
405
640
  # (1) scale to scan resolution, (2) shift to tile location.
406
- x1 = int(x1 * scan_tile_x) + x
407
- y1 = int(y1 * scan_tile_y) + y
408
- x2 = int(x2 * scan_tile_x) + x
409
- y2 = int(y2 * scan_tile_y) + y
641
+ x1 = int(x1 * sf_x) + x
642
+ y1 = int(y1 * sf_y) + y
643
+ x2 = int(x2 * sf_x) + x
644
+ y2 = int(y2 * sf_y) + y
410
645
 
411
646
  # Create the polygon.
412
647
  geom = Polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)])
@@ -418,9 +653,9 @@ def yolo_inference_on_region(
418
653
  gdf["box_area"] = gdf.geometry.area
419
654
 
420
655
  # Clean up predictions further.
421
- if nms_iou_thr:
656
+ if nms_iou_thr > 0:
422
657
  gdf = non_max_suppression(gdf, nms_iou_thr)
423
- if remove_contained_thr:
658
+ if remove_contained_thr > 0:
424
659
  gdf = remove_contained_boxes(gdf, remove_contained_thr)
425
660
 
426
661
  return gdf, mag, mm_px
@@ -62,7 +62,7 @@ def non_max_suppression(df: pd.DataFrame, thr: float) -> pd.DataFrame:
62
62
 
63
63
 
64
64
  def return_mag_and_resolution(
65
- mag: int | None = None,
65
+ mag: float | int | None = None,
66
66
  mm_px: float | None = None,
67
67
  standard_mag: int = 40,
68
68
  standard_mm_px: float = 0.0002519,
@@ -72,7 +72,7 @@ def return_mag_and_resolution(
72
72
  standard_mm_per_px.
73
73
 
74
74
  Args:
75
- mag (int | None, optional): Magnification to use. Defaults to
75
+ mag (float | int | None, optional): Magnification to use. Defaults to
76
76
  None.
77
77
  mm_px (float | None, optional): Resolution to use. Defaults to
78
78
  None.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dsa_helpers
3
- Version: 3.1.8.dev1
3
+ Version: 3.2.0
4
4
  Summary: Utility functions for working with the DSA girder client.
5
5
  Author-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
6
6
  Maintainer-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
@@ -39,7 +39,7 @@ classifiers = [
39
39
  "License :: OSI Approved :: MIT License",
40
40
  ]
41
41
  keywords = ["dsa", "girder", "girder_client"]
42
- version = "v3.1.8dev1"
42
+ version = "v3.2.0"
43
43
 
44
44
  [project.optional-dependencies]
45
45
  opencv = ["opencv-python"]
File without changes
File without changes
File without changes