dsa-helpers 1.dev0__tar.gz → 1.0.1.dev0__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 (47) hide show
  1. {dsa_helpers-1.dev0/dsa_helpers.egg-info → dsa_helpers-1.0.1.dev0}/PKG-INFO +1 -1
  2. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/__init__.py +2 -0
  3. dsa_helpers-1.0.1.dev0/dsa_helpers/ml/evaluate.py +136 -0
  4. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/tile_utils.py +14 -2
  5. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/utils.py +6 -3
  6. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0/dsa_helpers.egg-info}/PKG-INFO +1 -1
  7. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers.egg-info/SOURCES.txt +1 -0
  8. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/pyproject.toml +1 -1
  9. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/.gitignore +0 -0
  10. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/.readthedocs.yaml +0 -0
  11. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/LICENSE +0 -0
  12. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/README.md +0 -0
  13. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/app.py +0 -0
  14. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/docs/requirements.txt +0 -0
  15. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/docs/source/conf.py +0 -0
  16. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/docs/source/index.rst +0 -0
  17. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/__init__.py +0 -0
  18. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/dash/__init__.py +0 -0
  19. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
  20. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/dash/header.py +0 -0
  21. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/dash/header_callbacks.py +0 -0
  22. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/girder_utils.py +0 -0
  23. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/imread.py +0 -0
  24. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/imwrite.py +0 -0
  25. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/callbacks.py +0 -0
  26. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
  27. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/datasets/__init__.py +0 -0
  28. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/datasets/utils.py +0 -0
  29. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/metrics.py +0 -0
  30. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/object_detection/__init__.py +0 -0
  31. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
  32. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
  33. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/object_detection/utils.py +0 -0
  34. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
  35. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
  36. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/transforms/__init__.py +0 -0
  37. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
  38. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/mongo_utils.py +0 -0
  39. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers/tiling.py +0 -0
  40. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers.egg-info/dependency_links.txt +0 -0
  41. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers.egg-info/requires.txt +0 -0
  42. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/dsa_helpers.egg-info/top_level.txt +0 -0
  43. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/setup.cfg +0 -0
  44. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/tests/app/Dockerfile +0 -0
  45. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/tests/app/app.py +0 -0
  46. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/tests/docker-compose.yaml +0 -0
  47. {dsa_helpers-1.dev0 → dsa_helpers-1.0.1.dev0}/tests/test.ipynb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsa_helpers
3
- Version: 1.dev0
3
+ Version: 1.0.1.dev0
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>
@@ -5,6 +5,7 @@ from . import (
5
5
  transforms,
6
6
  segformer_semantic_segmentation,
7
7
  callbacks,
8
+ evaluate,
8
9
  )
9
10
 
10
11
  # Modules that are imported from * notation.
@@ -15,4 +16,5 @@ __all__ = [
15
16
  "transforms",
16
17
  "segformer_semantic_segmentation",
17
18
  "callbacks",
19
+ "evaluate",
18
20
  ]
@@ -0,0 +1,136 @@
1
+ """This module contains functions for evaluating model performance on
2
+ datasets."""
3
+
4
+ import torch
5
+ from torch import nn
6
+ from tqdm import tqdm
7
+ from transformers.trainer import Trainer
8
+ import pandas as pd
9
+
10
+ from .datasets.utils import create_segformer_segmentation_dataset
11
+ from .transforms.segformer_transforms import val_transforms
12
+ from ..tile_utils import merge_tile_masks
13
+
14
+
15
+ def evaluate_semantic_segmentation_segformer_group(
16
+ trainer: Trainer,
17
+ df: pd.DataFrame,
18
+ group_col: str,
19
+ batch_size: int = 16,
20
+ tile_size: int = 512,
21
+ background_label: int | None = None,
22
+ ) -> float:
23
+ """Evaluate a segformer trainer model on a specific dataset provided
24
+ as a dataframe, grouped by a specific column. For a unique value of
25
+ the column it will predict on the images for that group and then
26
+ merge the predictions into a single multipolygon. The mean IoU will
27
+ be calculated agains the ground truth masks for that group. The
28
+ reported mean IoU will be the average of all the mean IoUs for each
29
+ group.
30
+
31
+ Args:
32
+ trainer (Trainer): The trainer object with the model to evaluate.
33
+ df (pandas.DataFrame): The dataframe with the tile metadata.
34
+ Must have the fp column (file path), x column (x coordinate),
35
+ y column (y coordinate), and group_col.
36
+ group_col (str): The column to group the tiles by.
37
+ batch_size (int): The batch size to use for prediction. Note that
38
+ this is not the batch size use for predictions, rather it is
39
+ how many masks will be stored in memory. Batch size used
40
+ for predictions is specified on the trainer input. Default is
41
+ 16.
42
+ tile_size (int): The size of the tiles. Default is 512.
43
+ background_label (int | None): The label to use for the background
44
+ class, which will be ignored. If None then no label will be
45
+ ignored. Default is None.
46
+
47
+ Returns:
48
+ float: The mean IoU across all groups.
49
+
50
+ """
51
+ ious = []
52
+
53
+ # Predict on the images for each group separately.
54
+ groups = list(df[group_col].unique())
55
+ for group in tqdm(groups, desc="Evaluating groups"):
56
+ # Get the tiles in this group.
57
+ group_df = df[df[group_col] == group]
58
+
59
+ pred_tile_list = []
60
+ true_tile_list = []
61
+
62
+ # Predict on the group in batches.
63
+ for i in range(0, len(group_df), batch_size):
64
+ # Create dataset for this batch.
65
+ batch_df = group_df.iloc[i : i + batch_size]
66
+ x_list = batch_df["x"].tolist()
67
+ y_list = batch_df["y"].tolist()
68
+
69
+ # Create dataset for this batch.
70
+ group_dataset = create_segformer_segmentation_dataset(
71
+ batch_df, transforms=val_transforms
72
+ )
73
+
74
+ # Predict on the batch.
75
+ out = trainer.predict(group_dataset)
76
+ preds = out[0]
77
+ true = out[1]
78
+
79
+ # Reshape the prediction logits to tile size.
80
+ preds = torch.from_numpy(preds).cpu()
81
+
82
+ preds = nn.functional.interpolate(
83
+ preds,
84
+ size=(tile_size, tile_size),
85
+ mode="bilinear",
86
+ align_corners=False,
87
+ ).argmax(
88
+ dim=1
89
+ ) # logits -> class predictions
90
+
91
+ preds = preds.detach().numpy()
92
+
93
+ # Append to the tile list.
94
+ pred_tile_list.extend(
95
+ [(pred, x, y) for x, y, pred in zip(x_list, y_list, preds)]
96
+ )
97
+
98
+ true_tile_list.extend(
99
+ [(mask, x, y) for x, y, mask in zip(x_list, y_list, true)]
100
+ )
101
+
102
+ # Merge the tile masks.
103
+ pred_gdf = merge_tile_masks(
104
+ pred_tile_list, background_label=background_label
105
+ )
106
+ true_gdf = merge_tile_masks(
107
+ true_tile_list, background_label=background_label
108
+ )
109
+
110
+ # Get the unique labels in either the prediction or the ground truth.
111
+ labels = set(pred_gdf["label"].unique()) | set(
112
+ true_gdf["label"].unique()
113
+ )
114
+
115
+ # Calculate the IoU for each label.
116
+ ious = []
117
+
118
+ for label in labels:
119
+ pred_label_gdf = pred_gdf[pred_gdf["label"] == label]
120
+ true_label_gdf = true_gdf[true_gdf["label"] == label]
121
+
122
+ # If either the prediction or the ground truth is empty, the IoU is 0.
123
+ if pred_label_gdf.empty or true_label_gdf.empty:
124
+ ious.append(0)
125
+ else:
126
+ # Calculate the IoU.
127
+ geom1 = pred_label_gdf.iloc[0]["geometry"]
128
+ geom2 = true_label_gdf.iloc[0]["geometry"]
129
+
130
+ intersection_area = geom1.intersection(geom2).area
131
+ union = geom1.union(geom2).area
132
+
133
+ ious.append(intersection_area / union)
134
+
135
+ # Return the average of the ious.
136
+ return float(sum(ious) / len(ious))
@@ -8,7 +8,9 @@ from geopandas import GeoDataFrame
8
8
  from .utils import mask_to_shapely
9
9
 
10
10
 
11
- def merge_tile_masks(tile_list: list, buffer: int = 1) -> GeoDataFrame:
11
+ def merge_tile_masks(
12
+ tile_list: list, buffer: int = 1, background_label: int | None = 0
13
+ ) -> GeoDataFrame:
12
14
  """
13
15
  Merge the tile masks into a single mask for a large image.
14
16
 
@@ -17,6 +19,9 @@ def merge_tile_masks(tile_list: list, buffer: int = 1) -> GeoDataFrame:
17
19
  (1) fp or array for mask, (2) x-coordinate for tile, (3)
18
20
  y-coordinate for tile.
19
21
  buffer (int): Buffer to apply to the polygons before dissolving.
22
+ background_label (int | None): Label value of the background class,
23
+ which is ignored. Default is 0. If None then all labels are
24
+ considered.
20
25
 
21
26
  Returns:
22
27
  GeoDataFrame: GeoDataFrame with the merged mask.
@@ -30,7 +35,9 @@ def merge_tile_masks(tile_list: list, buffer: int = 1) -> GeoDataFrame:
30
35
 
31
36
  # Process the mask by converting it to polygons.
32
37
  polygons_and_labels.extend(
33
- mask_to_shapely(tile, x_offset=x, y_offset=y)
38
+ mask_to_shapely(
39
+ tile, x_offset=x, y_offset=y, background_label=background_label
40
+ )
34
41
  )
35
42
 
36
43
  # Convert polygons and labels into a GeoDataFrame.
@@ -45,4 +52,9 @@ def merge_tile_masks(tile_list: list, buffer: int = 1) -> GeoDataFrame:
45
52
  # Remove the buffer.
46
53
  gdf["geometry"] = gdf["geometry"].buffer(-buffer)
47
54
 
55
+ # Check that all polygons are valid.
56
+ for i, row in gdf.iterrows():
57
+ if not row["geometry"].is_valid:
58
+ gdf.loc[i, "geometry"] = row["geometry"].buffer(0)
59
+
48
60
  return gdf
@@ -10,7 +10,7 @@ def mask_to_shapely(
10
10
  mask: str | np.ndarray,
11
11
  x_offset: int = 0,
12
12
  y_offset: int = 0,
13
- background_label: int = 0,
13
+ background_label: int | None = 0,
14
14
  min_area: int = 0,
15
15
  ) -> list[Polygon, int]:
16
16
  """
@@ -20,9 +20,12 @@ def mask_to_shapely(
20
20
  Args:
21
21
  mask (str | np.ndarray): Path to the mask image or the mask.
22
22
  x_offset (int): Offset to add to x coordinates of polygons.
23
+ Default is 0.
23
24
  y_offset (int): Offset to add to y coordinates of polygons.
24
- background_label (int): Label value of the background class,
25
- which is ignored.
25
+ Default is 0.
26
+ background_label (int | None): Label value of the background class,
27
+ which is ignored. Default is 0. If None then all labels are
28
+ considered.
26
29
 
27
30
  Returns:
28
31
  list[Polygon, int]: List of polygons and their corresponding
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsa_helpers
3
- Version: 1.dev0
3
+ Version: 1.0.1.dev0
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 @@ dsa_helpers/dash/header.py
26
26
  dsa_helpers/dash/header_callbacks.py
27
27
  dsa_helpers/ml/__init__.py
28
28
  dsa_helpers/ml/callbacks.py
29
+ dsa_helpers/ml/evaluate.py
29
30
  dsa_helpers/ml/metrics.py
30
31
  dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py
31
32
  dsa_helpers/ml/datasets/__init__.py
@@ -35,7 +35,7 @@ classifiers = [
35
35
  "License :: OSI Approved :: MIT License",
36
36
  ]
37
37
  keywords = ["dsa", "girder", "girder_client"]
38
- version = "v1dev0"
38
+ version = "v1.0.1dev0"
39
39
 
40
40
  [project.urls]
41
41
  Repository = "https://github.com/Gutman-Lab/dsa-python-utils"
File without changes
File without changes
File without changes
File without changes