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.
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/.gitignore +2 -1
- {dsa_helpers-0.5.dev0/dsa_helpers.egg-info → dsa_helpers-0.6}/PKG-INFO +3 -3
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/image_utils.py +8 -2
- dsa_helpers-0.6/dsa_helpers/ml/__init__.py +17 -0
- dsa_helpers-0.6/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +3 -0
- dsa_helpers-0.6/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +121 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/mongo_utils.py +7 -1
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6/dsa_helpers.egg-info}/PKG-INFO +3 -3
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/SOURCES.txt +2 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/requires.txt +1 -1
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/pyproject.toml +3 -3
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/requirements.txt +6 -2
- dsa_helpers-0.6/tests/test.ipynb +209 -0
- dsa_helpers-0.5.dev0/dsa_helpers/ml/__init__.py +0 -2
- dsa_helpers-0.5.dev0/tests/test.ipynb +0 -268
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/LICENSE +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/README.md +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/app.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/__init__.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/__init__.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/header.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/dash/header_callbacks.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/girder_utils.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/imread.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/imwrite.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/datasets/__init__.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/datasets/utils.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/metrics/__init__.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/metrics/binary_dice_coefficient_metric.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/__init__.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/object_detection/utils.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/transforms/__init__.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers/utils.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/dependency_links.txt +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/dsa_helpers.egg-info/top_level.txt +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/setup.cfg +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/tests/app/Dockerfile +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/tests/app/app.py +0 -0
- {dsa_helpers-0.5.dev0 → dsa_helpers-0.6}/tests/docker-compose.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dsa_helpers
|
|
3
|
-
Version: 0.
|
|
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.
|
|
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}
|
|
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,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"
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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.
|
|
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.
|
|
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
|