dsa-helpers 1.0.7.dev0__tar.gz → 1.0.8.dev1__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.
- {dsa_helpers-1.0.7.dev0/dsa_helpers.egg-info → dsa_helpers-1.0.8.dev1}/PKG-INFO +1 -1
- dsa_helpers-1.0.8.dev1/dsa_helpers/gpd_utils.py +172 -0
- dsa_helpers-1.0.8.dev1/dsa_helpers/utils.py +150 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1/dsa_helpers.egg-info}/PKG-INFO +1 -1
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers.egg-info/SOURCES.txt +1 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/pyproject.toml +1 -1
- dsa_helpers-1.0.7.dev0/dsa_helpers/utils.py +0 -69
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/.gitignore +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/.readthedocs.yaml +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/LICENSE +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/README.md +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/app.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/docs/requirements.txt +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/docs/source/conf.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/docs/source/index.rst +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/cli_utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/dash/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/dash/header.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/dash/header_callbacks.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/girder_utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/image_utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/imread.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/imwrite.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/callbacks.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/datasets/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/datasets/utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/evaluate.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/metrics.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/object_detection/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/object_detection/utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/transforms/__init__.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/mongo_utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/tile_utils.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/tiling.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers.egg-info/dependency_links.txt +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers.egg-info/requires.txt +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers.egg-info/top_level.txt +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/setup.cfg +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/tests/app/Dockerfile +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/tests/app/app.py +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/tests/docker-compose.yaml +0 -0
- {dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/tests/test.ipynb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dsa_helpers
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8.dev1
|
|
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>
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for working with GeoPandas.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import geopandas as gpd
|
|
7
|
+
from shapely.geometry import Polygon
|
|
8
|
+
|
|
9
|
+
import matplotlib.patches as mpatches
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def count_polygon_points(polygon: Polygon) -> int:
|
|
14
|
+
"""
|
|
15
|
+
Count the total number of points in a Shapely polygon, including
|
|
16
|
+
exterior and interior rings.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
polygon (shapely.geometry.Polygon): A Shapely polygon object.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
int: The total number of points in the polygon.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
# Count exterior points.
|
|
26
|
+
exterior_points = len(polygon.exterior.coords)
|
|
27
|
+
|
|
28
|
+
# Count interior points (holes).
|
|
29
|
+
interior_points = sum(
|
|
30
|
+
len(interior.coords) for interior in polygon.interiors
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Total points.
|
|
34
|
+
return exterior_points + interior_points
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def total_points_in_gdf(gdf: gpd.GeoDataFrame) -> int:
|
|
38
|
+
"""
|
|
39
|
+
Calculate the total number of points in all polygons in a GeoDataFrame.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
gdf (geopandas.GeoDataFrame): GeoDataFrame containing polygons.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
int: The total number of points in all polygons.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
return int(gdf.geometry.apply(count_polygon_points).sum())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def plot_gdf(
|
|
52
|
+
gdf: gpd.GeoDataFrame,
|
|
53
|
+
figsize: int | tuple[int, int] | None = None,
|
|
54
|
+
label2color: dict | None = None,
|
|
55
|
+
):
|
|
56
|
+
"""Plot a geopandas dataframe.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
gdf (geopandas.GeoDataFrame): A GeoDataFrame containing
|
|
60
|
+
polygons. Must have a 'label' column.
|
|
61
|
+
figsize (int | tuple[int, int] | None): The size of the figure.
|
|
62
|
+
If None, the figure size will be (5, 5).
|
|
63
|
+
label2color (dict | None): A dictionary mapping labels to
|
|
64
|
+
colors. If None then a legend will not be included and the
|
|
65
|
+
colors will be random. Colors should be an accepted
|
|
66
|
+
matplotlib color.
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
if figsize is None:
|
|
70
|
+
figsize = (5, 5)
|
|
71
|
+
elif isinstance(figsize, int):
|
|
72
|
+
figsize = (figsize, figsize)
|
|
73
|
+
|
|
74
|
+
gdf = gdf.copy()
|
|
75
|
+
|
|
76
|
+
# Blow up any multi-polygons.
|
|
77
|
+
gdf = gdf.explode(index_parts=False).reset_index(drop=True)
|
|
78
|
+
|
|
79
|
+
total_points = total_points_in_gdf(gdf)
|
|
80
|
+
|
|
81
|
+
if label2color is not None:
|
|
82
|
+
gdf["color"] = gdf["label"].map(label2color)
|
|
83
|
+
|
|
84
|
+
# Remove rows where color is nan.
|
|
85
|
+
gdf = gdf.dropna(subset=["color"])
|
|
86
|
+
|
|
87
|
+
# # Create the plot
|
|
88
|
+
ax = plt.subplots(figsize=figsize)[1]
|
|
89
|
+
gdf.plot(color=gdf["color"], ax=ax)
|
|
90
|
+
|
|
91
|
+
if label2color is not None:
|
|
92
|
+
# Add legend.
|
|
93
|
+
handles = [
|
|
94
|
+
mpatches.Patch(color=color, label=key)
|
|
95
|
+
for key, color in label2color.items()
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
ax.legend(
|
|
99
|
+
handles=handles,
|
|
100
|
+
title="Legend",
|
|
101
|
+
loc="upper left",
|
|
102
|
+
bbox_to_anchor=(
|
|
103
|
+
1.02,
|
|
104
|
+
1,
|
|
105
|
+
), # x > 1 pushes it to the right, y = 1 keeps it top-aligned
|
|
106
|
+
borderaxespad=0,
|
|
107
|
+
)
|
|
108
|
+
plt.title(f"Total points: {total_points:,}")
|
|
109
|
+
plt.show()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def remove_gdf_overlaps(
|
|
113
|
+
gdf: gpd.GeoDataFrame,
|
|
114
|
+
columns: str | list[str],
|
|
115
|
+
ascending: bool | list[bool] = True,
|
|
116
|
+
is_notebook: bool = False,
|
|
117
|
+
) -> gpd.GeoDataFrame:
|
|
118
|
+
"""Remove overlaps from a GeoDataFrame.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
gdf (geopandas.GeoDataFrame): The GeoDataFrame to remove
|
|
122
|
+
overlaps from.
|
|
123
|
+
columns (str | list[str]): The columns to sort dataframe by. The
|
|
124
|
+
order determines the order of removal overlapping polygon
|
|
125
|
+
regions.
|
|
126
|
+
ascending (bool): Whether to remove overlaps in ascending order.
|
|
127
|
+
is_notebook (bool): Switch which tqdm bar to use. Default is
|
|
128
|
+
False, which uses the terminal progress bar.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
gpd.GeoDataFrame: The GeoDataFrame with overlaps removed.
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
if isinstance(columns, str):
|
|
135
|
+
columns = [columns]
|
|
136
|
+
|
|
137
|
+
if isinstance(ascending, bool):
|
|
138
|
+
ascending = [ascending] * len(columns)
|
|
139
|
+
|
|
140
|
+
# Remove multi-polygons.
|
|
141
|
+
gdf = gdf.explode(index_parts=False).reset_index(drop=True)
|
|
142
|
+
|
|
143
|
+
# Sort the GeoDataFrame by the columns and ascending order.
|
|
144
|
+
gdf = gdf.sort_values(by=columns, ascending=ascending).reset_index(
|
|
145
|
+
drop=True
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if is_notebook:
|
|
149
|
+
from tqdm.notebook import tqdm
|
|
150
|
+
else:
|
|
151
|
+
from tqdm import tqdm
|
|
152
|
+
|
|
153
|
+
# Loop through each row in the GeoDataFrame.
|
|
154
|
+
for i, row in tqdm(gdf.iterrows(), total=len(gdf)):
|
|
155
|
+
# Check overlap with all following rows.
|
|
156
|
+
other_gdf = gdf[i + 1 :]
|
|
157
|
+
|
|
158
|
+
if len(other_gdf):
|
|
159
|
+
# Get the rows that overlap.
|
|
160
|
+
overlap = row.geometry.overlaps(other_gdf.geometry)
|
|
161
|
+
|
|
162
|
+
# Get the rows that overlap.
|
|
163
|
+
other_rows = other_gdf[overlap]
|
|
164
|
+
|
|
165
|
+
other_gdf.loc[:, "geometry"] = other_gdf.geometry.difference(
|
|
166
|
+
row.geometry
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# This could have created multiple polygons, explode them.
|
|
170
|
+
gdf = gdf.explode(index_parts=False).reset_index(drop=True)
|
|
171
|
+
|
|
172
|
+
return gdf
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Utility functions for DSA Helpers.
|
|
2
|
+
|
|
3
|
+
This module provides various miscellaneous utility functions that are
|
|
4
|
+
not grouped into their own modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from shapely.geometry import Polygon
|
|
8
|
+
import numpy as np
|
|
9
|
+
import cv2 as cv
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def remove_small_holes(
|
|
13
|
+
polygon: Polygon, hole_area_threshold: float
|
|
14
|
+
) -> Polygon:
|
|
15
|
+
"""Remove small holes from a shapely polygon.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
polygon (shapely.geometry.Polygon): Polygon to remove holes
|
|
19
|
+
from.
|
|
20
|
+
hole_area_threshold (float): Minimum area of a hole to keep it.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
shapely.geometry.Polygon: Polygon with small holes removed.
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
if not polygon.interiors: # if there are no holes, return as is
|
|
27
|
+
return polygon
|
|
28
|
+
|
|
29
|
+
# Filter out small holes
|
|
30
|
+
new_holes = [
|
|
31
|
+
hole
|
|
32
|
+
for hole in polygon.interiors
|
|
33
|
+
if Polygon(hole).area > hole_area_threshold
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Create a new polygon with only large holes
|
|
37
|
+
return Polygon(polygon.exterior, new_holes)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def convert_to_json_serializable(
|
|
41
|
+
data: int | float | str | list | dict,
|
|
42
|
+
) -> int | float | str | list | dict:
|
|
43
|
+
"""Convert a list, integer, float, or dictionary into a JSON
|
|
44
|
+
serializable version of the object. Uses recursion to make sure the
|
|
45
|
+
entire input data structure is converted to a JSON serializable
|
|
46
|
+
version.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
data (int | float | str | list | dict): Data to convert to
|
|
50
|
+
JSON serializable.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
int | float | str | list | dict: JSON serializable version of
|
|
54
|
+
the input data.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
if isinstance(data, np.ndarray):
|
|
58
|
+
return data.tolist()
|
|
59
|
+
elif isinstance(data, np.integer):
|
|
60
|
+
return int(data)
|
|
61
|
+
elif isinstance(data, np.floating):
|
|
62
|
+
return float(data)
|
|
63
|
+
elif isinstance(data, dict):
|
|
64
|
+
return {
|
|
65
|
+
key: convert_to_json_serializable(value)
|
|
66
|
+
for key, value in data.items()
|
|
67
|
+
}
|
|
68
|
+
elif isinstance(data, list):
|
|
69
|
+
return [convert_to_json_serializable(item) for item in data]
|
|
70
|
+
return data
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def contours_to_polygons(binary_image: np.ndarray) -> list[Polygon]:
|
|
74
|
+
"""Convert a binary image to a list of polygons.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
binary_image (numpy.ndarray): Binary image.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
list[shapely.geometry.Polygon]: List of polygons.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
contours, hierarchy = cv.findContours(
|
|
84
|
+
binary_image, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE
|
|
85
|
+
)
|
|
86
|
+
hierarchy = hierarchy[0] # shape: (n_contours, 4)
|
|
87
|
+
|
|
88
|
+
def contour_to_coords(contour):
|
|
89
|
+
return [(int(p[0][0]), int(p[0][1])) for p in contour]
|
|
90
|
+
|
|
91
|
+
polygons = []
|
|
92
|
+
used = set()
|
|
93
|
+
|
|
94
|
+
for i, (next_idx, prev_idx, child_idx, parent_idx) in enumerate(hierarchy):
|
|
95
|
+
if parent_idx != -1:
|
|
96
|
+
# Skip child contours for now (they'll be added as holes)
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
# Outer contour
|
|
100
|
+
exterior = contour_to_coords(contours[i])
|
|
101
|
+
|
|
102
|
+
if len(exterior) < 4:
|
|
103
|
+
# Skip contours that are too small
|
|
104
|
+
used.add(i)
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
holes = []
|
|
108
|
+
|
|
109
|
+
# Look for children (holes)
|
|
110
|
+
child = hierarchy[i][2]
|
|
111
|
+
while child != -1:
|
|
112
|
+
hole_coords = contour_to_coords(contours[child])
|
|
113
|
+
holes.append(hole_coords)
|
|
114
|
+
used.add(child)
|
|
115
|
+
# Check for nested children (e.g. box in a hole in a box)
|
|
116
|
+
grandchild = hierarchy[child][2]
|
|
117
|
+
if grandchild != -1:
|
|
118
|
+
# Treat grandchild as a new polygon later
|
|
119
|
+
pass
|
|
120
|
+
child = hierarchy[child][0] # next sibling hole
|
|
121
|
+
|
|
122
|
+
used.add(i)
|
|
123
|
+
polygon = Polygon(exterior, holes)
|
|
124
|
+
|
|
125
|
+
if not polygon.is_valid:
|
|
126
|
+
# Attempt to fix the polygon
|
|
127
|
+
polygon = polygon.buffer(0)
|
|
128
|
+
|
|
129
|
+
if polygon.is_valid:
|
|
130
|
+
polygons.append(polygon)
|
|
131
|
+
|
|
132
|
+
# Add any unused outer contours (e.g. nested objects)
|
|
133
|
+
for i in range(len(contours)):
|
|
134
|
+
if i not in used:
|
|
135
|
+
coords = contour_to_coords(contours[i])
|
|
136
|
+
|
|
137
|
+
if len(coords) < 4:
|
|
138
|
+
# Skip contours that are too small
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
poly = Polygon(coords)
|
|
142
|
+
|
|
143
|
+
if not poly.is_valid:
|
|
144
|
+
# Attempt to fix the polygon
|
|
145
|
+
poly = poly.buffer(0)
|
|
146
|
+
|
|
147
|
+
if poly.is_valid:
|
|
148
|
+
polygons.append(poly)
|
|
149
|
+
|
|
150
|
+
return polygons
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dsa_helpers
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8.dev1
|
|
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>
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""Utility functions for DSA Helpers.
|
|
2
|
-
|
|
3
|
-
This module provides various miscellaneous utility functions that are
|
|
4
|
-
not grouped into their own modules.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from shapely.geometry import Polygon
|
|
8
|
-
import numpy as np
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def remove_small_holes(
|
|
12
|
-
polygon: Polygon, hole_area_threshold: float
|
|
13
|
-
) -> Polygon:
|
|
14
|
-
"""Remove small holes from a shapely polygon.
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
polygon (shapely.geometry.Polygon): Polygon to remove holes
|
|
18
|
-
from.
|
|
19
|
-
hole_area_threshold (float): Minimum area of a hole to keep it.
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
shapely.geometry.Polygon: Polygon with small holes removed.
|
|
23
|
-
|
|
24
|
-
"""
|
|
25
|
-
if not polygon.interiors: # if there are no holes, return as is
|
|
26
|
-
return polygon
|
|
27
|
-
|
|
28
|
-
# Filter out small holes
|
|
29
|
-
new_holes = [
|
|
30
|
-
hole
|
|
31
|
-
for hole in polygon.interiors
|
|
32
|
-
if Polygon(hole).area > hole_area_threshold
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
# Create a new polygon with only large holes
|
|
36
|
-
return Polygon(polygon.exterior, new_holes)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def convert_to_json_serializable(
|
|
40
|
-
data: int | float | str | list | dict,
|
|
41
|
-
) -> int | float | str | list | dict:
|
|
42
|
-
"""Convert a list, integer, float, or dictionary into a JSON
|
|
43
|
-
serializable version of the object. Uses recursion to make sure the
|
|
44
|
-
entire input data structure is converted to a JSON serializable
|
|
45
|
-
version.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
data (int | float | str | list | dict): Data to convert to
|
|
49
|
-
JSON serializable.
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
int | float | str | list | dict: JSON serializable version of
|
|
53
|
-
the input data.
|
|
54
|
-
|
|
55
|
-
"""
|
|
56
|
-
if isinstance(data, np.ndarray):
|
|
57
|
-
return data.tolist()
|
|
58
|
-
elif isinstance(data, np.integer):
|
|
59
|
-
return int(data)
|
|
60
|
-
elif isinstance(data, np.floating):
|
|
61
|
-
return float(data)
|
|
62
|
-
elif isinstance(data, dict):
|
|
63
|
-
return {
|
|
64
|
-
key: convert_to_json_serializable(value)
|
|
65
|
-
for key, value in data.items()
|
|
66
|
-
}
|
|
67
|
-
elif isinstance(data, list):
|
|
68
|
-
return [convert_to_json_serializable(item) for item in data]
|
|
69
|
-
return data
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/dash/dash_paperdragon_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/object_detection/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/object_detection/tile_image.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.0.7.dev0 → dsa_helpers-1.0.8.dev1}/dsa_helpers/ml/transforms/segformer_transforms.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|