dsa-helpers 0.6.dev0__tar.gz → 0.8__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 (45) hide show
  1. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/.gitignore +2 -1
  2. {dsa_helpers-0.6.dev0/dsa_helpers.egg-info → dsa_helpers-0.8}/PKG-INFO +5 -1
  3. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/README.md +3 -0
  4. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/girder_utils.py +148 -5
  5. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/__init__.py +2 -0
  6. dsa_helpers-0.8/dsa_helpers/ml/tiling/__init__.py +5 -0
  7. dsa_helpers-0.8/dsa_helpers/ml/tiling/semantic_seg_tile_wsi_with_dsa_annotations.py +225 -0
  8. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/mongo_utils.py +7 -1
  9. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8/dsa_helpers.egg-info}/PKG-INFO +5 -1
  10. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers.egg-info/SOURCES.txt +2 -0
  11. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers.egg-info/requires.txt +1 -0
  12. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/pyproject.toml +3 -2
  13. dsa_helpers-0.8/tests/test.ipynb +209 -0
  14. dsa_helpers-0.6.dev0/tests/test.ipynb +0 -133
  15. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/LICENSE +0 -0
  16. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/app.py +0 -0
  17. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/__init__.py +0 -0
  18. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/dash/__init__.py +0 -0
  19. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
  20. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/dash/header.py +0 -0
  21. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/dash/header_callbacks.py +0 -0
  22. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/image_utils.py +0 -0
  23. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/imread.py +0 -0
  24. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/imwrite.py +0 -0
  25. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
  26. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/datasets/__init__.py +0 -0
  27. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/datasets/utils.py +0 -0
  28. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/metrics/__init__.py +0 -0
  29. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/metrics/binary_dice_coefficient_metric.py +0 -0
  30. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/object_detection/__init__.py +0 -0
  31. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
  32. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
  33. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/object_detection/utils.py +0 -0
  34. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
  35. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
  36. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/transforms/__init__.py +0 -0
  37. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
  38. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers/utils.py +0 -0
  39. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers.egg-info/dependency_links.txt +0 -0
  40. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/dsa_helpers.egg-info/top_level.txt +0 -0
  41. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/requirements.txt +0 -0
  42. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/setup.cfg +0 -0
  43. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/tests/app/Dockerfile +0 -0
  44. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/tests/app/app.py +0 -0
  45. {dsa_helpers-0.6.dev0 → dsa_helpers-0.8}/tests/docker-compose.yaml +0 -0
@@ -8,4 +8,5 @@ dist/
8
8
  .env
9
9
 
10
10
  # Ignore data directory created for testing the package.
11
- tests/data/
11
+ tests/data/
12
+ tests/models/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsa_helpers
3
- Version: 0.6.dev0
3
+ Version: 0.8
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.
@@ -36,6 +37,9 @@ This library was tested using Python version 3.11.8 and uses the dependencies de
36
37
 
37
38
  This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
38
39
 
40
+ ## Extra dependencies
41
+ If you are getting a large image error, then install large image using the instructions provided in their [GitHub page](https://github.com/girder/large_image#:~:text=pip%20install%20large%2Dimage%5Ball%5D%20%2D%2Dfind%2Dlinks%20https%3A//girder.github.io/large_image_wheels).
42
+
39
43
  ## Instructions for Development
40
44
  1. Install requirements for building and distributing package:
41
45
  ```
@@ -7,6 +7,9 @@ This library was tested using Python version 3.11.8 and uses the dependencies de
7
7
 
8
8
  This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
9
9
 
10
+ ## Extra dependencies
11
+ If you are getting a large image error, then install large image using the instructions provided in their [GitHub page](https://github.com/girder/large_image#:~:text=pip%20install%20large%2Dimage%5Ball%5D%20%2D%2Dfind%2Dlinks%20https%3A//girder.github.io/large_image_wheels).
12
+
10
13
  ## Instructions for Development
11
14
  1. Install requirements for building and distributing package:
12
15
  ```
@@ -63,7 +63,9 @@ def get_thumbnail(
63
63
  elif len(fill) == 4:
64
64
  fill = f"rgba({fill[0]},{fill[1]},{fill[2]},{fill[3]})"
65
65
 
66
- params.extend([f"width={width}", f"height={height}", f"fill={fill}"])
66
+ params.extend(
67
+ [f"width={width}", f"height={height}", f"fill={fill}"]
68
+ )
67
69
  elif width is not None:
68
70
  params.append(f"width={width}")
69
71
  elif height is not None:
@@ -246,7 +248,10 @@ def _rotate_point_list(point_list, rotation, center=(0, 0)):
246
248
  y = point[1] - center[1]
247
249
 
248
250
  point_list_rotated.append(
249
- (int(x * cos - y * sin + center[0]), int(x * sin + y * cos + center[1]))
251
+ (
252
+ int(x * cos - y * sin + center[0]),
253
+ int(x * sin + y * cos + center[1]),
254
+ )
250
255
  )
251
256
 
252
257
  return point_list_rotated
@@ -274,12 +279,19 @@ def get_rectangle_element_coords(element):
274
279
  x_max = center_x + w // 2
275
280
  y_min = center_y - h // 2
276
281
  y_max = center_y + h // 2
277
- corner_coords = [(x_min, y_min), (x_max, y_min), (x_max, y_max), (x_min, y_max)]
282
+ corner_coords = [
283
+ (x_min, y_min),
284
+ (x_max, y_min),
285
+ (x_max, y_max),
286
+ (x_min, y_max),
287
+ ]
278
288
 
279
289
  # if there is rotation rotate
280
290
  if element["rotation"]:
281
291
  corner_coords = _rotate_point_list(
282
- corner_coords, rotation=element["rotation"], center=(center_x, center_y)
292
+ corner_coords,
293
+ rotation=element["rotation"],
294
+ center=(center_x, center_y),
283
295
  )
284
296
 
285
297
  corner_coords = np.array(corner_coords, dtype=np.int32)
@@ -332,7 +344,13 @@ def get_items(gc: GirderClient, parend_id: str) -> list[dict]:
332
344
  list[dict]: The list of items.
333
345
 
334
346
  """
335
- params = {"type": "folder", "limit": 0, "offset": 0, "sort": "_id", "sortdir": 1}
347
+ params = {
348
+ "type": "folder",
349
+ "limit": 0,
350
+ "offset": 0,
351
+ "sort": "_id",
352
+ "sortdir": 1,
353
+ }
336
354
 
337
355
  request_url = f"resource/{parend_id}/items"
338
356
 
@@ -344,3 +362,128 @@ def get_items(gc: GirderClient, parend_id: str) -> list[dict]:
344
362
  items = gc.get(request_url, parameters=params)
345
363
 
346
364
  return items
365
+
366
+
367
+ def get_roi_with_yolo_labels_from_single_doc(
368
+ gc: GirderClient,
369
+ ann_doc: dict,
370
+ roi_element_group: str,
371
+ group_map: dict[str, int],
372
+ mag: float | None = None,
373
+ rgb_fill: tuple[int, int, int] = (114, 114, 114),
374
+ ) -> tuple[np.ndarray, str]:
375
+ """Get the ROI image with the YOLO labels given a DSA annotation document.
376
+
377
+ Args:
378
+ gc (girder_client.GirderClient): Authenticated girder client.
379
+ ann_doc (dict): Annotation document metadata.
380
+ roi_element_group (str): The group name of the ROI element.
381
+ group_map (dict[str, int]): Mapping of group names to group IDs.
382
+ mag (float, optional): The magnification to use for the ROI. Defaults to
383
+ None which uses the default magnification.
384
+ rgb_fill (tuple[int, int, int], optional): The RGB color to fill the ROI
385
+ with. Defaults to (114, 114, 114).
386
+
387
+ Returns:
388
+ tuple[np.ndarray, str]: The ROI image and YOLO labels data as string.
389
+
390
+ Raises:
391
+ ValueError: If multiple ROI elements are found in the document.
392
+ ValueError: If ROI element type is not polyline.
393
+
394
+ """
395
+ roi_element = None
396
+ box_elements = []
397
+
398
+ # Loop through all elements in the annotation document.
399
+ for element in ann_doc.get("annotation", {}).get("elements", []):
400
+ if element.get("group") == roi_element_group:
401
+ # Append the ROI element, there can't more than one.
402
+ if roi_element is not None:
403
+ raise ValueError("Multiple ROI elements found in the document.")
404
+
405
+ roi_element = element
406
+ elif element.get("group") in group_map:
407
+ # Append the box elements.
408
+ box_elements.append(element)
409
+
410
+ # Get the large image metadata.
411
+ large_image_metadata = get_item_large_image_metadata(gc, ann_doc["itemId"])
412
+ scan_mag = large_image_metadata["magnification"]
413
+
414
+ # Specify magnification to use.
415
+ if mag is None:
416
+ mag = scan_mag
417
+
418
+ # Calculate the factor to convert from scan mag to desired mag.
419
+ sf = mag / scan_mag
420
+
421
+ # Get the ROI image.
422
+ if roi_element.get("type") == "polyline":
423
+ # Grab the smallest bounding box.
424
+ points = np.array(roi_element["points"])[:, :2] # remove z-axis
425
+ roi_x1, roi_y1 = points.min(axis=0)
426
+ roi_x2, roi_y2 = points.max(axis=0)
427
+
428
+ img = get_region(
429
+ gc,
430
+ ann_doc["itemId"],
431
+ roi_x1,
432
+ roi_y1,
433
+ roi_x2 - roi_x1,
434
+ roi_y2 - roi_y1,
435
+ mag=mag,
436
+ )[
437
+ :, :, :3
438
+ ] # make sure it is RGB
439
+
440
+ # Shift the points to be relative to smallest bounding box.
441
+ points -= [roi_x1, roi_y1]
442
+
443
+ # Create a mask and draw the ROI filled on it (handles rotated ROIs).
444
+ mask = np.zeros(img.shape[:2], dtype=np.uint8)
445
+ mask = cv.drawContours(
446
+ mask, [(points * sf).astype(np.int32)], 0, 1, cv.FILLED
447
+ )
448
+
449
+ # Gray out regions outside of ROI.
450
+ img[np.where(mask == 0)] = rgb_fill
451
+ else:
452
+ raise ValueError("Only polyline ROIs are supported.")
453
+
454
+ roi_h, roi_w = img.shape[:2]
455
+
456
+ # Convert the box elements into YOLO format.
457
+ labels_data = ""
458
+
459
+ for element in box_elements:
460
+ if element.get("group") in group_map:
461
+ if element.get("type") != "rectangle":
462
+ print(f"Skipping element: {element.get('type')} not supported.")
463
+ continue
464
+
465
+ group_id = group_map[element.get("group")]
466
+
467
+ # Get the center of box and shift to be relative to ROI.
468
+ x_center, y_center = (
469
+ np.array(element["center"])[:2] - [roi_x1, roi_y1]
470
+ ) * sf
471
+
472
+ # Normalize the coordinates.
473
+ x_center /= roi_w
474
+ y_center /= roi_h
475
+
476
+ # Scale the box width and height and then normalize.
477
+ width, height = (
478
+ element["width"] * sf / roi_w,
479
+ element["height"] * sf / roi_h,
480
+ )
481
+
482
+ labels_data += f"{group_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n"
483
+ else:
484
+ print(
485
+ f"Skipping element: group {element.get('group')} not found in group map."
486
+ )
487
+
488
+ # Return the ROI image and labels data.
489
+ return img, labels_data.strip()
@@ -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,225 @@
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), x, y
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]: A list of tuples: (tile file path, x, y coordinates
116
+ of tile).
117
+
118
+ """
119
+ # Format annotation documents for drawing on tile masks.
120
+ contours = {idx: {"points": [], "holes": []} for idx in label2idx.values()}
121
+
122
+ for ann_doc in annotation_docs:
123
+ # Loop through the elements of the annotation document.
124
+ elements = ann_doc["annotation"].get("elements", [])
125
+
126
+ for element in elements:
127
+ # Check if element has a label.
128
+ label = element.get("label", {}).get("value")
129
+
130
+ if label not in label2idx:
131
+ # Skip this element if it is not in the label2idx mapping.
132
+ continue
133
+
134
+ idx = label2idx[label]
135
+
136
+ element_type = element["type"]
137
+
138
+ if element_type == "polyline":
139
+ points = element.get("points", [])
140
+
141
+ if len(points):
142
+ contours[idx]["points"].append(
143
+ np.array(points, dtype=int)[:, :2]
144
+ )
145
+
146
+ holes = element.get("holes", [])
147
+
148
+ if len(holes):
149
+ for hole in holes:
150
+ contours[idx]["holes"].append(
151
+ np.array(hole, dtype=int)[:, :2]
152
+ )
153
+ elif element_type == "rectangle":
154
+ # Convert the rectangle coordinates to polygon ones.
155
+ x1, y1 = element["center"][:2]
156
+ x2 = x1 + element["width"]
157
+ y2 = y1 + element["height"]
158
+
159
+ contours[idx]["points"].append(
160
+ np.array(
161
+ [[x1, y1], [x2, y1], [x2, y2], [x1, y2]], dtype=int
162
+ )
163
+ )
164
+
165
+ # Create directory to save tile images and masks.
166
+ save_dir = Path(save_dir)
167
+ img_dir = save_dir / "images"
168
+ mask_dir = save_dir / "masks"
169
+ img_dir.mkdir(parents=True, exist_ok=True)
170
+ mask_dir.mkdir(exist_ok=True)
171
+
172
+ # Get the tile source of WSI.
173
+ ts = large_image.getTileSource(str(wsi_fp))
174
+
175
+ # Get the metadata of the WSI.
176
+ metadata = ts.getMetadata()
177
+
178
+ wsi_w, wsi_h = metadata["sizeX"], metadata["sizeY"]
179
+
180
+ # Get the magnification of the WSI.
181
+ wsi_mag = metadata["magnification"]
182
+
183
+ # Calculate the tile size at full resolution (1 if mag is None or the same as scan mag).
184
+ if mag is None:
185
+ scan_tile_size = tile_size
186
+ mag = wsi_mag
187
+ sf = 1
188
+ else:
189
+ sf = mag / wsi_mag # scan mag -> desired mag
190
+ scan_tile_size = int(tile_size / sf)
191
+
192
+ # Get a list of x,y coordinates for the tile top left corners.
193
+ xys = [
194
+ (x, y)
195
+ for x in range(0, wsi_w, scan_tile_size)
196
+ for y in range(0, wsi_h, scan_tile_size)
197
+ ]
198
+
199
+ with Pool(nproc) as pool:
200
+ jobs = [
201
+ pool.apply_async(
202
+ _process_tile,
203
+ (
204
+ ts,
205
+ prepend_name,
206
+ xy[0],
207
+ xy[1],
208
+ scan_tile_size,
209
+ tile_size,
210
+ mag,
211
+ contours,
212
+ sf,
213
+ img_dir,
214
+ mask_dir,
215
+ ),
216
+ )
217
+ for xy in xys
218
+ ]
219
+
220
+ tile_info = []
221
+
222
+ for job in tqdm(jobs, desc="Tiling..."):
223
+ tile_info.append(job.get())
224
+
225
+ return tile_info
@@ -9,6 +9,7 @@ from typing import Union
9
9
 
10
10
 
11
11
  def get_mongo_client(
12
+ protocol: str | None = None,
12
13
  host: str | None = None,
13
14
  username: str | None = None,
14
15
  password: str | None = None,
@@ -20,6 +21,9 @@ def get_mongo_client(
20
21
  not present it sets them to default values.
21
22
 
22
23
  Args:
24
+ protocol (str, optional): The protocol of the mongo server. If None it
25
+ look for the environment variable "MONGO_PROTOCOL". If this is
26
+ missing it will default to "mongodb".
23
27
  host (str, optional): The host name of the mongo server. If None it will
24
28
  look for the environment variable "MONGO_HOST_NAME". If this is
25
29
  missing it will default to "mongodb".
@@ -39,6 +43,8 @@ def get_mongo_client(
39
43
  The mongo client.
40
44
 
41
45
  """
46
+ if protocol is None:
47
+ protocol = getenv("MONGO_PROTOCOL", "mongodb")
42
48
  if username is None:
43
49
  username = getenv("MONGO_INITDB_ROOT_USERNAME", "docker")
44
50
  if password is None:
@@ -48,7 +54,7 @@ def get_mongo_client(
48
54
  if port is None:
49
55
  port = int(getenv("MONGO_HOST_PORT", 27017))
50
56
 
51
- return pymongo.MongoClient(f"mongodb://{username}:{password}@{host}:{port}")
57
+ return pymongo.MongoClient(f"{protocol}://{username}:{password}@{host}:{port}")
52
58
 
53
59
 
54
60
  def chunks(lst: list, n=500):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsa_helpers
3
- Version: 0.6.dev0
3
+ Version: 0.8
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.
@@ -36,6 +37,9 @@ This library was tested using Python version 3.11.8 and uses the dependencies de
36
37
 
37
38
  This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
38
39
 
40
+ ## Extra dependencies
41
+ If you are getting a large image error, then install large image using the instructions provided in their [GitHub page](https://github.com/girder/large_image#:~:text=pip%20install%20large%2Dimage%5Ball%5D%20%2D%2Dfind%2Dlinks%20https%3A//girder.github.io/large_image_wheels).
42
+
39
43
  ## Instructions for Development
40
44
  1. Install requirements for building and distributing package:
41
45
  ```
@@ -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.6dev"
39
+ version = "v0.8"
39
40
 
40
41
  [project.urls]
41
42
  Repository = "https://github.com/Gutman-Lab/dsa-python-utils"