dsa-helpers 0.5.dev0__tar.gz → 0.6__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.5.dev0 → dsa_helpers-0.6}/.gitignore +2 -1
  2. {dsa_helpers-0.5.dev0/dsa_helpers.egg-info → dsa_helpers-0.6}/PKG-INFO +3 -3
  3. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/image_utils.py +8 -2
  4. dsa_helpers-0.6/dsa_helpers/ml/__init__.py +17 -0
  5. dsa_helpers-0.6/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +3 -0
  6. dsa_helpers-0.6/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +121 -0
  7. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/mongo_utils.py +7 -1
  8. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6/dsa_helpers.egg-info}/PKG-INFO +3 -3
  9. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/SOURCES.txt +2 -0
  10. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/requires.txt +1 -1
  11. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/pyproject.toml +3 -3
  12. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/requirements.txt +6 -2
  13. dsa_helpers-0.6/tests/test.ipynb +209 -0
  14. dsa_helpers-0.5.dev0/dsa_helpers/ml/__init__.py +0 -2
  15. dsa_helpers-0.5.dev0/tests/test.ipynb +0 -268
  16. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/LICENSE +0 -0
  17. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/README.md +0 -0
  18. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/app.py +0 -0
  19. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/__init__.py +0 -0
  20. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/__init__.py +0 -0
  21. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
  22. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/header.py +0 -0
  23. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/header_callbacks.py +0 -0
  24. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/girder_utils.py +0 -0
  25. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/imread.py +0 -0
  26. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/imwrite.py +0 -0
  27. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
  28. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/datasets/__init__.py +0 -0
  29. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/datasets/utils.py +0 -0
  30. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/metrics/__init__.py +0 -0
  31. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/metrics/binary_dice_coefficient_metric.py +0 -0
  32. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/__init__.py +0 -0
  33. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
  34. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
  35. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/utils.py +0 -0
  36. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/transforms/__init__.py +0 -0
  37. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
  38. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/utils.py +0 -0
  39. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/dependency_links.txt +0 -0
  40. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/top_level.txt +0 -0
  41. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/setup.cfg +0 -0
  42. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/tests/app/Dockerfile +0 -0
  43. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/tests/app/app.py +0 -0
  44. {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/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.5.dev0
3
+ Version: 0.6
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>
@@ -8,7 +8,7 @@ Project-URL: Repository, https://github.com/Gutman-Lab/dsa-python-utils
8
8
  Keywords: dsa,girder,girder_client
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
- Requires-Python: >=3.9
11
+ Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: girder-client
@@ -22,9 +22,9 @@ Requires-Dist: dash
22
22
  Requires-Dist: dash_bootstrap_components
23
23
  Requires-Dist: shapely
24
24
  Requires-Dist: geopandas
25
+ Requires-Dist: transformers[torch]
25
26
  Requires-Dist: torch
26
27
  Requires-Dist: datasets
27
- Requires-Dist: transformers
28
28
  Requires-Dist: torchvision
29
29
 
30
30
  # Digital Slide Archive (DSA) Helpers
@@ -14,6 +14,7 @@ def tile_image(
14
14
  fill: int | tuple = (255, 255, 255),
15
15
  prepend_name: str = "",
16
16
  overwrite: bool = False,
17
+ grayscale: bool = False,
17
18
  ) -> pd.DataFrame:
18
19
  """Tile an image into smaller images.
19
20
 
@@ -28,6 +29,8 @@ def tile_image(
28
29
  prepend_name (str, optional): A string to prepend to the tile names.
29
30
  overwrite (bool, optional): Whether to overwrite existing images.
30
31
  Defaults to False.
32
+ grayscale (bool, optional): Whether to save images as grayscale instead
33
+ of the default RGB. Defaults to False.
31
34
 
32
35
  Returns:
33
36
  pandas.DataFrame: A DataFrame with the tile locations.
@@ -50,18 +53,21 @@ def tile_image(
50
53
 
51
54
  xys = [(x, y) for x in range(0, w, stride) for y in range(0, h, stride)]
52
55
 
56
+ if prepend_name:
57
+ prepend_name += "_"
58
+
53
59
  for xy in xys:
54
60
  # Get the top left and bottom right coordinates of the tile.
55
61
  x, y = xy
56
62
 
57
63
  # Filepath for saving tile.
58
- save_fp = save_loc / f"{prepend_name}_x{x}y{y}tilesize{tile_size}.png"
64
+ save_fp = save_loc / f"{prepend_name}x{x}y{y}tilesize{tile_size}.png"
59
65
 
60
66
  if not save_fp.is_file() or overwrite:
61
67
  # Get the tile from the image.
62
68
  tile_img = img[y : y + tile_size, x : x + tile_size]
63
69
 
64
- imwrite(save_fp, tile_img)
70
+ imwrite(save_fp, tile_img, grayscale=grayscale)
65
71
 
66
72
  df_data.append([save_fp, x, y, tile_size])
67
73
 
@@ -0,0 +1,17 @@
1
+ # Allow modules to be imported.
2
+ from . import (
3
+ object_detection,
4
+ datasets,
5
+ metrics,
6
+ transforms,
7
+ segformer_semantic_segmentation,
8
+ )
9
+
10
+ # Modules that are imported from * notation.
11
+ __all__ = [
12
+ "object_detection",
13
+ "datasets",
14
+ "metrics",
15
+ "transforms",
16
+ "segformer_semantic_segmentation",
17
+ ]
@@ -0,0 +1,3 @@
1
+ from .inference import inference
2
+
3
+ __all__ = ["inference"]
@@ -0,0 +1,121 @@
1
+ import numpy as np
2
+ from pathlib import Path
3
+ import cv2 as cv
4
+ import shutil
5
+
6
+ import transformers
7
+ from transformers import SegformerImageProcessor
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+
12
+ from ... import imread
13
+ from ...image_utils import tile_image
14
+
15
+
16
+ def inference(
17
+ img: np.ndarray,
18
+ model: transformers.models.segformer.modeling_segformer.SegformerForSemanticSegmentation,
19
+ device: str | None = None,
20
+ tile_size: int = 512,
21
+ temp_dir: str = ".temp",
22
+ batch_size: int = 8,
23
+ ) -> tuple[np.ndarray, list[np.ndarray]]:
24
+ """Inference on an image using SegFormer model for semantic segmentation.
25
+
26
+ Args:
27
+ img (numpy.ndarray): Image to segment.
28
+ model (transformers.models.segformer.modeling_segformer.SegformerForSemanticSegmentation):
29
+ Pre-trained model.
30
+ device (str | None): Device to use. Defaults to None, in which case
31
+ it is inferred based on devices available.
32
+ tile_size (int): Size of the tiles. Defaults to 512.
33
+ temp_dir (str): Temporary directory to save tiles. Defaults to ".temp".
34
+ batch_size (int): Batch size. Defaults to 8.
35
+
36
+ Returns:
37
+ tuple[numpy.ndarray, list[numpy.ndarray]]: The predicted label mask and
38
+ the contours of the mask.
39
+
40
+ """
41
+ # Create location to save tile images.
42
+ tile_dir = Path(temp_dir)
43
+ tile_dir.mkdir(exist_ok=True, parents=True)
44
+
45
+ # Tile the image.
46
+ tiles_df = tile_image(
47
+ img,
48
+ str(tile_dir),
49
+ tile_size=tile_size,
50
+ )
51
+
52
+ # Create instance of the image processor.
53
+ img_processor = SegformerImageProcessor()
54
+
55
+ if device is None:
56
+ device = "cuda" if torch.cuda.is_available() else "cpu"
57
+
58
+ print("Using device:", device)
59
+
60
+ # Set model to eval mode.
61
+ model.to(device) # to device
62
+ model.eval()
63
+
64
+ # Create an empty mask the size of the image.
65
+ h, w = img.shape[:2]
66
+ predicted_mask = np.zeros((h + tile_size, w + tile_size), dtype=np.uint8)
67
+
68
+ # Loop through tile images in batch size.
69
+ for i in range(0, len(tiles_df), batch_size):
70
+ # Get batch of tile images.
71
+ batch = tiles_df.iloc[i : i + batch_size]
72
+
73
+ # Read all the images in the batch.
74
+ images = [imread(fp) for fp in batch["fp"]]
75
+
76
+ # Process all the images.
77
+ inputs = img_processor(
78
+ images=images,
79
+ return_tensors="pt",
80
+ )
81
+
82
+ # Send to the same device as model.
83
+ inputs = inputs.to(device)
84
+
85
+ with torch.no_grad():
86
+ # Sent inputs through the model.
87
+ outputs = model(**inputs)
88
+
89
+ # Get out the logits.
90
+ logits = outputs.logits
91
+
92
+ # Reshape the logits to the original image size.
93
+ upsampled_logits = nn.functional.interpolate(
94
+ logits,
95
+ size=(tile_size, tile_size), # (height, width)
96
+ mode="bilinear",
97
+ align_corners=False,
98
+ )
99
+
100
+ # Take the max value of the class dimension.
101
+ pred_seg = upsampled_logits.argmax(dim=1)
102
+
103
+ # Place tile predictions into the overall predicted mask.
104
+ for j in range(len(batch)):
105
+ r = batch.iloc[j]
106
+ x, y = r["x"], r["y"]
107
+ predicted_mask[y : y + tile_size, x : x + tile_size] = (
108
+ pred_seg[j].cpu().numpy()
109
+ ).astype(np.uint8)
110
+
111
+ # Remove the temp directory.
112
+ shutil.rmtree(temp_dir)
113
+
114
+ # Remove edges.
115
+ predicted_mask = predicted_mask[:h, :w]
116
+
117
+ # Extract the contours of the mask.
118
+ contours = cv.findContours(predicted_mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)[0]
119
+
120
+ # Return the mask.
121
+ return predicted_mask, contours
@@ -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.5.dev0
3
+ Version: 0.6
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>
@@ -8,7 +8,7 @@ Project-URL: Repository, https://github.com/Gutman-Lab/dsa-python-utils
8
8
  Keywords: dsa,girder,girder_client
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
- Requires-Python: >=3.9
11
+ Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: girder-client
@@ -22,9 +22,9 @@ Requires-Dist: dash
22
22
  Requires-Dist: dash_bootstrap_components
23
23
  Requires-Dist: shapely
24
24
  Requires-Dist: geopandas
25
+ Requires-Dist: transformers[torch]
25
26
  Requires-Dist: torch
26
27
  Requires-Dist: datasets
27
- Requires-Dist: transformers
28
28
  Requires-Dist: torchvision
29
29
 
30
30
  # Digital Slide Archive (DSA) Helpers
@@ -30,6 +30,8 @@ dsa_helpers/ml/object_detection/__init__.py
30
30
  dsa_helpers/ml/object_detection/get_pearce_roi_images.py
31
31
  dsa_helpers/ml/object_detection/tile_image.py
32
32
  dsa_helpers/ml/object_detection/utils.py
33
+ dsa_helpers/ml/segformer_semantic_segmentation/__init__.py
34
+ dsa_helpers/ml/segformer_semantic_segmentation/inference.py
33
35
  dsa_helpers/ml/transforms/__init__.py
34
36
  dsa_helpers/ml/transforms/segformer_transforms.py
35
37
  tests/docker-compose.yaml
@@ -9,7 +9,7 @@ dash
9
9
  dash_bootstrap_components
10
10
  shapely
11
11
  geopandas
12
+ transformers[torch]
12
13
  torch
13
14
  datasets
14
- transformers
15
15
  torchvision
@@ -15,9 +15,9 @@ dependencies = [
15
15
  'dash_bootstrap_components',
16
16
  'shapely',
17
17
  'geopandas',
18
+ "transformers[torch]",
18
19
  "torch",
19
20
  "datasets",
20
- "transformers",
21
21
  "torchvision"
22
22
  ]
23
23
  name = "dsa_helpers"
@@ -29,13 +29,13 @@ maintainers = [
29
29
  ]
30
30
  description = "Utility functions for working with the DSA girder client."
31
31
  readme = "README.md"
32
- requires-python = ">=3.9"
32
+ requires-python = ">=3.8"
33
33
  classifiers = [
34
34
  "Programming Language :: Python :: 3",
35
35
  "License :: OSI Approved :: MIT License",
36
36
  ]
37
37
  keywords = ["dsa", "girder", "girder_client"]
38
- version = "v0.5dev0"
38
+ version = "v0.6"
39
39
 
40
40
  [project.urls]
41
41
  Repository = "https://github.com/Gutman-Lab/dsa-python-utils"
@@ -2,18 +2,22 @@
2
2
  build
3
3
  twine
4
4
 
5
+ # For tests.
6
+ matplotlib
7
+ jupyter
8
+
9
+ # Mirrored requirements from pyproject.toml
5
10
  girder-client
6
11
  numpy
7
12
  setuptools
8
13
  opencv-python
9
14
  pandas
10
15
  pymongo
11
- jupyter
12
16
  pillow
13
17
  dash
14
18
  dash_bootstrap_components
15
19
  python-dotenv
20
+ transformers[torch]
16
21
  torch
17
22
  datasets
18
- transformers
19
23
  torchvision