dsa-helpers 0.7__tar.gz → 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.
Files changed (44) hide show
  1. {dsa_helpers-0.7/dsa_helpers.egg-info → dsa_helpers-0.8.dev1}/PKG-INFO +2 -1
  2. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/__init__.py +2 -0
  3. dsa_helpers-0.8.dev1/dsa_helpers/ml/tiling/__init__.py +5 -0
  4. dsa_helpers-0.8.dev1/dsa_helpers/ml/tiling/semantic_seg_tile_wsi_with_dsa_annotations.py +224 -0
  5. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1/dsa_helpers.egg-info}/PKG-INFO +2 -1
  6. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers.egg-info/SOURCES.txt +2 -0
  7. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers.egg-info/requires.txt +1 -0
  8. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/pyproject.toml +3 -2
  9. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/.gitignore +0 -0
  10. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/LICENSE +0 -0
  11. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/README.md +0 -0
  12. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/app.py +0 -0
  13. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/__init__.py +0 -0
  14. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/dash/__init__.py +0 -0
  15. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
  16. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/dash/header.py +0 -0
  17. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/dash/header_callbacks.py +0 -0
  18. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/girder_utils.py +0 -0
  19. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/image_utils.py +0 -0
  20. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/imread.py +0 -0
  21. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/imwrite.py +0 -0
  22. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
  23. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/datasets/__init__.py +0 -0
  24. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/datasets/utils.py +0 -0
  25. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/metrics/__init__.py +0 -0
  26. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/metrics/binary_dice_coefficient_metric.py +0 -0
  27. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/object_detection/__init__.py +0 -0
  28. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
  29. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
  30. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/object_detection/utils.py +0 -0
  31. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
  32. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
  33. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/transforms/__init__.py +0 -0
  34. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
  35. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/mongo_utils.py +0 -0
  36. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers/utils.py +0 -0
  37. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers.egg-info/dependency_links.txt +0 -0
  38. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/dsa_helpers.egg-info/top_level.txt +0 -0
  39. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/requirements.txt +0 -0
  40. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/setup.cfg +0 -0
  41. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/tests/app/Dockerfile +0 -0
  42. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/tests/app/app.py +0 -0
  43. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/tests/docker-compose.yaml +0 -0
  44. {dsa_helpers-0.7 → dsa_helpers-0.8.dev1}/tests/test.ipynb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsa_helpers
3
- Version: 0.7
3
+ Version: 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>
@@ -26,6 +26,7 @@ Requires-Dist: transformers[torch]
26
26
  Requires-Dist: torch
27
27
  Requires-Dist: datasets
28
28
  Requires-Dist: torchvision
29
+ Requires-Dist: large-image
29
30
 
30
31
  # Digital Slide Archive (DSA) Helpers
31
32
  Digital Slide Archive Python utility library.
@@ -5,6 +5,7 @@ from . import (
5
5
  metrics,
6
6
  transforms,
7
7
  segformer_semantic_segmentation,
8
+ tiling,
8
9
  )
9
10
 
10
11
  # Modules that are imported from * notation.
@@ -14,4 +15,5 @@ __all__ = [
14
15
  "metrics",
15
16
  "transforms",
16
17
  "segformer_semantic_segmentation",
18
+ "tiling",
17
19
  ]
@@ -0,0 +1,5 @@
1
+ from .semantic_seg_tile_wsi_with_dsa_annotations import (
2
+ semantic_seg_tile_wsi_with_dsa_annotations,
3
+ )
4
+
5
+ __all__ = ["semantic_seg_tile_wsi_with_dsa_annotations"]
@@ -0,0 +1,224 @@
1
+ import large_image
2
+ import cv2 as cv
3
+ import numpy as np
4
+ from pathlib import Path
5
+ from ...imwrite import imwrite
6
+ from multiprocessing import Pool
7
+ from tqdm import tqdm
8
+
9
+
10
+ def _process_tile(
11
+ ts,
12
+ prepend_name,
13
+ x,
14
+ y,
15
+ scan_tile_size,
16
+ tile_size,
17
+ mag,
18
+ contours,
19
+ sf,
20
+ img_dir,
21
+ mask_dir,
22
+ ):
23
+ fn = f"{prepend_name}x{x}y{y}.png"
24
+
25
+ # Get the tile.
26
+ tile_img = ts.getRegion(
27
+ region={
28
+ "left": x,
29
+ "top": y,
30
+ "right": x + scan_tile_size,
31
+ "bottom": y + scan_tile_size,
32
+ },
33
+ format=large_image.constants.TILE_FORMAT_NUMPY,
34
+ scale={"magnification": mag},
35
+ )[0][:, :, :3]
36
+
37
+ if tile_img.shape[:2] != (tile_size, tile_size):
38
+ # Pad the image with zeroes to make it the desired size.
39
+ tile_img = cv.copyMakeBorder(
40
+ tile_img,
41
+ 0,
42
+ tile_size - tile_img.shape[0],
43
+ 0,
44
+ tile_size - tile_img.shape[1],
45
+ cv.BORDER_CONSTANT,
46
+ value=0,
47
+ )
48
+
49
+ # Create a blank tile mask.
50
+ tile_mask = np.zeros(tile_img.shape[:2], dtype=np.uint8)
51
+
52
+ # Loop through contours to draw on tile mask.
53
+ for idx, contour_dict in contours.items():
54
+ # Create a mask for the specific label.
55
+ label_mask = np.zeros(tile_mask.shape, dtype=np.uint8)
56
+
57
+ points = contour_dict["points"].copy()
58
+ holes = contour_dict["holes"].copy()
59
+
60
+ # For points and hole:
61
+ # (1) Shift by top left of the current tile.
62
+ # (2) Scale by be desired magnification.
63
+ points = [((point - [x, y]) * sf).astype(int) for point in points]
64
+
65
+ corrected_holes = []
66
+
67
+ for hole in holes:
68
+ if len(hole):
69
+ corrected_holes.append(((hole - [x, y]) * sf).astype(int))
70
+
71
+ # Use points to draw contours as positive.
72
+ label_mask = cv.drawContours(label_mask, points, -1, 1, cv.FILLED)
73
+
74
+ # Draw in the holes as background class zero.
75
+ label_mask = cv.drawContours(label_mask, holes, -1, 0, cv.FILLED)
76
+
77
+ # Apply the label to tile mask where label mask is positive.
78
+ tile_mask[label_mask == 1] = idx
79
+
80
+ # Save the image and mask.
81
+ img_fp = img_dir / fn
82
+ imwrite(img_fp, tile_img)
83
+ imwrite(mask_dir / fn, tile_mask, grayscale=True)
84
+
85
+ return str(img_fp)
86
+
87
+
88
+ def semantic_seg_tile_wsi_with_dsa_annotations(
89
+ wsi_fp: str,
90
+ annotation_docs: list[dict],
91
+ label2idx: dict,
92
+ save_dir: str,
93
+ tile_size: int,
94
+ mag: float | None = None,
95
+ prepend_name: str = "",
96
+ nproc: int = 1,
97
+ ) -> list[str]:
98
+ """Tile a WSI with semantic segmentation label masks created from
99
+ DSA annotations.
100
+
101
+ Args:
102
+ wsi_fp (str): file path to WSI.
103
+ annotation_docs (list[dict]): list of annotation documents.
104
+ label2idx (dict): mapping of label names to integer indices.
105
+ save_dir (str): directory to save the tiled images.
106
+ tile_size (int): size of the tile images at desired magnificaiton.
107
+ mag (float | None, optional): magnification level of the WSI.
108
+ If None, the function will use the default magnification
109
+ level. Defaults to None.
110
+ prepend_name (str, optional): prepend name to the created tile
111
+ images and masks. Defaults to "".
112
+ nproc (int, optional): number of processes to use for tiling.
113
+
114
+ Returns:
115
+ list[str]: list of file paths to the created tile images.
116
+
117
+ """
118
+ # Format annotation documents for drawing on tile masks.
119
+ contours = {idx: {"points": [], "holes": []} for idx in label2idx.values()}
120
+
121
+ for ann_doc in annotation_docs:
122
+ # Loop through the elements of the annotation document.
123
+ elements = ann_doc["annotation"].get("elements", [])
124
+
125
+ for element in elements:
126
+ # Check if element has a label.
127
+ label = element.get("label", {}).get("value")
128
+
129
+ if label not in label2idx:
130
+ # Skip this element if it is not in the label2idx mapping.
131
+ continue
132
+
133
+ idx = label2idx[label]
134
+
135
+ element_type = element["type"]
136
+
137
+ if element_type == "polyline":
138
+ points = element.get("points", [])
139
+
140
+ if len(points):
141
+ contours[idx]["points"].append(
142
+ np.array(points, dtype=int)[:, :2]
143
+ )
144
+
145
+ holes = element.get("holes", [])
146
+
147
+ if len(holes):
148
+ for hole in holes:
149
+ contours[idx]["holes"].append(
150
+ np.array(hole, dtype=int)[:, :2]
151
+ )
152
+ elif element_type == "rectangle":
153
+ # Convert the rectangle coordinates to polygon ones.
154
+ x1, y1 = element["center"][:2]
155
+ x2 = x1 + element["width"]
156
+ y2 = y1 + element["height"]
157
+
158
+ contours[idx]["points"].append(
159
+ np.array(
160
+ [[x1, y1], [x2, y1], [x2, y2], [x1, y2]], dtype=int
161
+ )
162
+ )
163
+
164
+ # Create directory to save tile images and masks.
165
+ save_dir = Path(save_dir)
166
+ img_dir = save_dir / "images"
167
+ mask_dir = save_dir / "masks"
168
+ img_dir.mkdir(parents=True, exist_ok=True)
169
+ mask_dir.mkdir(exist_ok=True)
170
+
171
+ # Get the tile source of WSI.
172
+ ts = large_image.getTileSource(str(wsi_fp))
173
+
174
+ # Get the metadata of the WSI.
175
+ metadata = ts.getMetadata()
176
+
177
+ wsi_w, wsi_h = metadata["sizeX"], metadata["sizeY"]
178
+
179
+ # Get the magnification of the WSI.
180
+ wsi_mag = metadata["magnification"]
181
+
182
+ # Calculate the tile size at full resolution (1 if mag is None or the same as scan mag).
183
+ if mag is None:
184
+ scan_tile_size = tile_size
185
+ mag = wsi_mag
186
+ sf = 1
187
+ else:
188
+ sf = mag / wsi_mag # scan mag -> desired mag
189
+ scan_tile_size = int(tile_size / sf)
190
+
191
+ # Get a list of x,y coordinates for the tile top left corners.
192
+ xys = [
193
+ (x, y)
194
+ for x in range(0, wsi_w, scan_tile_size)
195
+ for y in range(0, wsi_h, scan_tile_size)
196
+ ]
197
+
198
+ with Pool(nproc) as pool:
199
+ jobs = [
200
+ pool.apply_async(
201
+ _process_tile,
202
+ (
203
+ ts,
204
+ prepend_name,
205
+ xy[0],
206
+ xy[1],
207
+ scan_tile_size,
208
+ tile_size,
209
+ mag,
210
+ contours,
211
+ sf,
212
+ img_dir,
213
+ mask_dir,
214
+ ),
215
+ )
216
+ for xy in xys
217
+ ]
218
+
219
+ fps = []
220
+
221
+ for job in tqdm(jobs, desc="Tiling..."):
222
+ fps.append(job.get())
223
+
224
+ return fps
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsa_helpers
3
- Version: 0.7
3
+ Version: 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>
@@ -26,6 +26,7 @@ Requires-Dist: transformers[torch]
26
26
  Requires-Dist: torch
27
27
  Requires-Dist: datasets
28
28
  Requires-Dist: torchvision
29
+ Requires-Dist: large-image
29
30
 
30
31
  # Digital Slide Archive (DSA) Helpers
31
32
  Digital Slide Archive Python utility library.
@@ -32,6 +32,8 @@ dsa_helpers/ml/object_detection/tile_image.py
32
32
  dsa_helpers/ml/object_detection/utils.py
33
33
  dsa_helpers/ml/segformer_semantic_segmentation/__init__.py
34
34
  dsa_helpers/ml/segformer_semantic_segmentation/inference.py
35
+ dsa_helpers/ml/tiling/__init__.py
36
+ dsa_helpers/ml/tiling/semantic_seg_tile_wsi_with_dsa_annotations.py
35
37
  dsa_helpers/ml/transforms/__init__.py
36
38
  dsa_helpers/ml/transforms/segformer_transforms.py
37
39
  tests/docker-compose.yaml
@@ -13,3 +13,4 @@ transformers[torch]
13
13
  torch
14
14
  datasets
15
15
  torchvision
16
+ large-image
@@ -18,7 +18,8 @@ dependencies = [
18
18
  "transformers[torch]",
19
19
  "torch",
20
20
  "datasets",
21
- "torchvision"
21
+ "torchvision",
22
+ "large-image"
22
23
  ]
23
24
  name = "dsa_helpers"
24
25
  authors = [
@@ -35,7 +36,7 @@ classifiers = [
35
36
  "License :: OSI Approved :: MIT License",
36
37
  ]
37
38
  keywords = ["dsa", "girder", "girder_client"]
38
- version = "v0.7"
39
+ version = "v0.8dev1"
39
40
 
40
41
  [project.urls]
41
42
  Repository = "https://github.com/Gutman-Lab/dsa-python-utils"
File without changes
File without changes
File without changes
File without changes
File without changes