dsa-helpers 1.0.3.dev0__tar.gz → 1.0.4.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 (50) hide show
  1. {dsa_helpers-1.0.3.dev0/dsa_helpers.egg-info → dsa_helpers-1.0.4.dev1}/PKG-INFO +4 -14
  2. dsa_helpers-1.0.4.dev1/README.md +13 -0
  3. dsa_helpers-1.0.4.dev1/dsa_helpers/image_utils.py +101 -0
  4. dsa_helpers-1.0.4.dev1/dsa_helpers/utils.py +35 -0
  5. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1/dsa_helpers.egg-info}/PKG-INFO +4 -14
  6. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers.egg-info/SOURCES.txt +1 -0
  7. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/pyproject.toml +1 -1
  8. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/tests/test.ipynb +22 -10
  9. dsa_helpers-1.0.3.dev0/README.md +0 -24
  10. dsa_helpers-1.0.3.dev0/dsa_helpers/utils.py +0 -78
  11. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/.gitignore +0 -0
  12. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/.readthedocs.yaml +0 -0
  13. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/LICENSE +0 -0
  14. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/app.py +0 -0
  15. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/docs/requirements.txt +0 -0
  16. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/docs/source/conf.py +0 -0
  17. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/docs/source/index.rst +0 -0
  18. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/__init__.py +0 -0
  19. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/dash/__init__.py +0 -0
  20. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
  21. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/dash/header.py +0 -0
  22. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/dash/header_callbacks.py +0 -0
  23. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/girder_utils.py +0 -0
  24. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/imread.py +0 -0
  25. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/imwrite.py +0 -0
  26. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/__init__.py +0 -0
  27. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/callbacks.py +0 -0
  28. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
  29. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/datasets/__init__.py +0 -0
  30. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/datasets/utils.py +0 -0
  31. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/evaluate.py +0 -0
  32. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/metrics.py +0 -0
  33. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/object_detection/__init__.py +0 -0
  34. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
  35. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
  36. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/object_detection/utils.py +0 -0
  37. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
  38. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
  39. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/transforms/__init__.py +0 -0
  40. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
  41. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/mongo_utils.py +0 -0
  42. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/tile_utils.py +0 -0
  43. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers/tiling.py +0 -0
  44. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers.egg-info/dependency_links.txt +0 -0
  45. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers.egg-info/requires.txt +0 -0
  46. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/dsa_helpers.egg-info/top_level.txt +0 -0
  47. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/setup.cfg +0 -0
  48. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/tests/app/Dockerfile +0 -0
  49. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/tests/app/app.py +0 -0
  50. {dsa_helpers-1.0.3.dev0 → dsa_helpers-1.0.4.dev1}/tests/docker-compose.yaml +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: dsa_helpers
3
- Version: 1.0.3.dev0
3
+ Version: 1.0.4.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: datasets
27
27
  Requires-Dist: torchvision
28
28
  Requires-Dist: large-image
29
+ Dynamic: license-file
29
30
 
30
31
  # Digital Slide Archive (DSA) Helpers
31
32
  Digital Slide Archive Python utility library.
@@ -39,15 +40,4 @@ This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
39
40
  ## Extra dependencies
40
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).
41
42
 
42
- ## Instructions for Development
43
- 1. Install requirements for building and distributing package:
44
- ```
45
- $ python3 -m pip install --upgrade build # for building package
46
- $ python3 -m pip install --upgrade twine # for uploading to PyPI
47
- ```
48
- 2. When ready to build:
49
- - Modify "pyproject.toml" file, change the "version" key to a different value than a version already pushed.
50
- - Run ```$ python3 -m build```. This will create a dist directory and put your new wheel and tar distribution files there.
51
- 3. To install locally for development: ```$ pip3 install dist/dsa_helpers-*.whl```, choosing the wheel version you want to test.
52
- 4. Upload to PyPI using twine: ```python3 -m twine upload --repository pypi dist/*```
53
- * You can specify specific files to upload, the line above pushes everything in dist directory.
43
+ A readthedocs for this library can be found [here](https://david-andrew-gutman-dsa-helpers.readthedocs-hosted.com/en/latest/index.html).
@@ -0,0 +1,13 @@
1
+ # Digital Slide Archive (DSA) Helpers
2
+ Digital Slide Archive Python utility library.
3
+
4
+ This library is available for installation through [Python Package Index (PyPI)](https://pypi.org/).
5
+
6
+ This library was tested using Python version 3.11.8 and uses the dependencies described in requirements.txt.
7
+
8
+ This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
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
+
13
+ A readthedocs for this library can be found [here](https://david-andrew-gutman-dsa-helpers.readthedocs-hosted.com/en/latest/index.html).
@@ -0,0 +1,101 @@
1
+ """Image utility functions for DSA Helpers.
2
+
3
+ This module provides various utility functions for image processing and
4
+ manipulation.
5
+
6
+ It includes functions for:
7
+ - Exctracting contours from binary masks, and formatting them into a
8
+ GeoDataFrame.
9
+
10
+ Note:
11
+ This module requires external dependencies such as OpenCV and
12
+ shapely.
13
+ """
14
+
15
+ import numpy as np
16
+ import cv2 as cv
17
+ from .imread import imread
18
+
19
+ from shapely.affinity import translate
20
+ from shapely.geometry import Polygon
21
+
22
+
23
+ def label_mask_to_polygons(
24
+ mask: str | np.ndarray,
25
+ x_offset: int = 0,
26
+ y_offset: int = 0,
27
+ exclude_labels: int | list[int] = 0,
28
+ min_area: int = 0,
29
+ ) -> list[Polygon, int]:
30
+ """
31
+ Extract contours from a label mask and convert them into shapely
32
+ polygons.
33
+
34
+ Args:
35
+ mask (str | np.ndarray): Path to the mask image or the mask.
36
+ x_offset (int): Offset to add to x coordinates of polygons.
37
+ y_offset (int): Offset to add to y coordinates of polygons.
38
+ exclude_labels (int | list[int]): Label(s) to exclude from the
39
+ output.
40
+ min_area (int): Minimum area of polygons to include.
41
+
42
+ Returns:
43
+ list[Polygon, int]: List of polygons and their corresponding
44
+ labels.
45
+
46
+ """
47
+ # Convert exclude_labels to a list if it's an integer.
48
+ if isinstance(exclude_labels, int):
49
+ exclude_labels = [exclude_labels]
50
+
51
+ # Read the mask if it's a path.
52
+ if isinstance(mask, str):
53
+ mask = imread(mask, grayscale=True)
54
+
55
+ # Unique labels, excluding those specified in exclude_labels.
56
+ labels = [
57
+ label for label in np.unique(mask) if label not in exclude_labels
58
+ ]
59
+
60
+ polygons = [] # track all polygons
61
+
62
+ # Loop through unique label index.
63
+ for label in labels:
64
+ # Filter to mask for this label.
65
+ label_mask = (mask == label).astype(np.uint8)
66
+
67
+ # Find contours.
68
+ contours, hierarchy = cv.findContours(
69
+ label_mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE
70
+ )
71
+
72
+ # Process the contours.
73
+ polygons_dict = {}
74
+
75
+ for idx, (contour, h) in enumerate(zip(contours, hierarchy[0])):
76
+ # The contour should contain at least three points, otherwise it's
77
+ # not a polygon.
78
+ if len(contour) > 3:
79
+ # To include holes, group the polygons by their parent index.
80
+ if idx not in polygons_dict:
81
+ polygons_dict[idx] = {"holes": []}
82
+
83
+ if h[3] == -1:
84
+ # This does not have a parent so its a polygon.
85
+ polygons_dict[idx]["polygon"] = contour.reshape(-1, 2)
86
+ else:
87
+ # This has a parent so its a hole.
88
+ polygons_dict[h[3]]["holes"].append(contour.reshape(-1, 2))
89
+
90
+ # Convert to shapely polygon objects.
91
+ for data in polygons_dict.values():
92
+ polygon = Polygon(data["polygon"], holes=data["holes"])
93
+
94
+ # Shift the polygon by the offset.
95
+ polygon = translate(polygon, xoff=x_offset, yoff=y_offset)
96
+
97
+ # Include the polygon if it's greater than the minimum area.
98
+ if polygon.area >= min_area:
99
+ polygons.append([polygon, int(label)])
100
+
101
+ return polygons
@@ -0,0 +1,35 @@
1
+ """Utility functions for DSA Helpers.
2
+
3
+ This module provides various miscellaneous utility functions that are
4
+ not grouped into their own modules.
5
+ """
6
+
7
+ from shapely.geometry import Polygon
8
+
9
+
10
+ def remove_small_holes(
11
+ polygon: Polygon, hole_area_threshold: float
12
+ ) -> Polygon:
13
+ """Remove small holes from a shapely polygon.
14
+
15
+ Args:
16
+ polygon (shapely.geometry.Polygon): Polygon to remove holes
17
+ from.
18
+ hole_area_threshold (float): Minimum area of a hole to keep it.
19
+
20
+ Returns:
21
+ shapely.geometry.Polygon: Polygon with small holes removed.
22
+
23
+ """
24
+ if not polygon.interiors: # if there are no holes, return as is
25
+ return polygon
26
+
27
+ # Filter out small holes
28
+ new_holes = [
29
+ hole
30
+ for hole in polygon.interiors
31
+ if Polygon(hole).area > hole_area_threshold
32
+ ]
33
+
34
+ # Create a new polygon with only large holes
35
+ return Polygon(polygon.exterior, new_holes)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: dsa_helpers
3
- Version: 1.0.3.dev0
3
+ Version: 1.0.4.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: datasets
27
27
  Requires-Dist: torchvision
28
28
  Requires-Dist: large-image
29
+ Dynamic: license-file
29
30
 
30
31
  # Digital Slide Archive (DSA) Helpers
31
32
  Digital Slide Archive Python utility library.
@@ -39,15 +40,4 @@ This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
39
40
  ## Extra dependencies
40
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).
41
42
 
42
- ## Instructions for Development
43
- 1. Install requirements for building and distributing package:
44
- ```
45
- $ python3 -m pip install --upgrade build # for building package
46
- $ python3 -m pip install --upgrade twine # for uploading to PyPI
47
- ```
48
- 2. When ready to build:
49
- - Modify "pyproject.toml" file, change the "version" key to a different value than a version already pushed.
50
- - Run ```$ python3 -m build```. This will create a dist directory and put your new wheel and tar distribution files there.
51
- 3. To install locally for development: ```$ pip3 install dist/dsa_helpers-*.whl```, choosing the wheel version you want to test.
52
- 4. Upload to PyPI using twine: ```python3 -m twine upload --repository pypi dist/*```
53
- * You can specify specific files to upload, the line above pushes everything in dist directory.
43
+ A readthedocs for this library can be found [here](https://david-andrew-gutman-dsa-helpers.readthedocs-hosted.com/en/latest/index.html).
@@ -9,6 +9,7 @@ docs/source/conf.py
9
9
  docs/source/index.rst
10
10
  dsa_helpers/__init__.py
11
11
  dsa_helpers/girder_utils.py
12
+ dsa_helpers/image_utils.py
12
13
  dsa_helpers/imread.py
13
14
  dsa_helpers/imwrite.py
14
15
  dsa_helpers/mongo_utils.py
@@ -35,7 +35,7 @@ classifiers = [
35
35
  "License :: OSI Approved :: MIT License",
36
36
  ]
37
37
  keywords = ["dsa", "girder", "girder_client"]
38
- version = "v1.0.3.dev0"
38
+ version = "v1.0.4.dev1"
39
39
 
40
40
  [project.urls]
41
41
  Repository = "https://github.com/Gutman-Lab/dsa-python-utils"
@@ -8,15 +8,6 @@
8
8
  "Test different functions of the library."
9
9
  ]
10
10
  },
11
- {
12
- "cell_type": "code",
13
- "execution_count": 1,
14
- "metadata": {},
15
- "outputs": [],
16
- "source": [
17
- "import dsa_helpers.dash"
18
- ]
19
- },
20
11
  {
21
12
  "cell_type": "markdown",
22
13
  "metadata": {},
@@ -101,7 +92,28 @@
101
92
  "cell_type": "code",
102
93
  "execution_count": null,
103
94
  "metadata": {},
104
- "outputs": [],
95
+ "outputs": [
96
+ {
97
+ "name": "stdout",
98
+ "output_type": "stream",
99
+ "text": [
100
+ "Help on function imwrite in module dsa_helpers.imwrite:\n",
101
+ "\n",
102
+ "imwrite(fp: str, img: numpy.ndarray, grayscale: bool = False) -> None\n",
103
+ " Save an image to disk.\n",
104
+ "\n",
105
+ " Args:\n",
106
+ " img (np.ndarray): The image to save.\n",
107
+ " fp (str): The file path to save the image.\n",
108
+ " grayscale (bool, optional): Whether to save the image as grayscale.\n",
109
+ " Defaults to False.\n",
110
+ "\n",
111
+ " Raises:\n",
112
+ " ValueError: The image must be grayscale, RGB, or RGBA.\n",
113
+ "\n"
114
+ ]
115
+ }
116
+ ],
105
117
  "source": []
106
118
  }
107
119
  ],
@@ -1,24 +0,0 @@
1
- # Digital Slide Archive (DSA) Helpers
2
- Digital Slide Archive Python utility library.
3
-
4
- This library is available for installation through [Python Package Index (PyPI)](https://pypi.org/).
5
-
6
- This library was tested using Python version 3.11.8 and uses the dependencies described in requirements.txt.
7
-
8
- This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
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
-
13
- ## Instructions for Development
14
- 1. Install requirements for building and distributing package:
15
- ```
16
- $ python3 -m pip install --upgrade build # for building package
17
- $ python3 -m pip install --upgrade twine # for uploading to PyPI
18
- ```
19
- 2. When ready to build:
20
- - Modify "pyproject.toml" file, change the "version" key to a different value than a version already pushed.
21
- - Run ```$ python3 -m build```. This will create a dist directory and put your new wheel and tar distribution files there.
22
- 3. To install locally for development: ```$ pip3 install dist/dsa_helpers-*.whl```, choosing the wheel version you want to test.
23
- 4. Upload to PyPI using twine: ```python3 -m twine upload --repository pypi dist/*```
24
- * You can specify specific files to upload, the line above pushes everything in dist directory.
@@ -1,78 +0,0 @@
1
- import numpy as np
2
- from . import imread
3
- import cv2 as cv
4
-
5
- from shapely.geometry import Polygon
6
- from shapely.affinity import translate
7
-
8
-
9
- def mask_to_shapely(
10
- mask: str | np.ndarray,
11
- x_offset: int = 0,
12
- y_offset: int = 0,
13
- background_label: int | None = 0,
14
- min_area: int = 0,
15
- ) -> list[Polygon, int]:
16
- """
17
- Extract contours from a label mask and convert them into shapely
18
- polygons.
19
-
20
- Args:
21
- mask (str | np.ndarray): Path to the mask image or the mask.
22
- x_offset (int): Offset to add to x coordinates of polygons.
23
- Default is 0.
24
- y_offset (int): Offset to add to y coordinates of polygons.
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.
29
-
30
- Returns:
31
- list[Polygon, int]: List of polygons and their corresponding
32
- labels.
33
-
34
- """
35
- if isinstance(mask, str):
36
- mask = imread(mask, grayscale=True)
37
-
38
- # Find unique labels (excluding background 0)
39
- labels = [label for label in np.unique(mask) if label != background_label]
40
-
41
- polygons = [] # Track all polygons.
42
-
43
- # Loop through unique label index.
44
- for label in labels:
45
- # Filter to mask for this label.
46
- label_mask = (mask == label).astype(np.uint8)
47
-
48
- # Find contours.
49
- contours, hierarchy = cv.findContours(
50
- label_mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE
51
- )
52
-
53
- # Process the contours.
54
- polygons_dict = {}
55
-
56
- for idx, (contour, h) in enumerate(zip(contours, hierarchy[0])):
57
- if len(contour) > 3:
58
- if idx not in polygons_dict:
59
- polygons_dict[idx] = {"holes": []}
60
-
61
- if h[3] == -1:
62
- polygons_dict[idx]["polygon"] = contour.reshape(-1, 2)
63
- else:
64
- polygons_dict[h[3]]["holes"].append(contour.reshape(-1, 2))
65
-
66
- # Now that we know the polygon and the holes, create a Polygon object for each.
67
- for data in polygons_dict.values():
68
- if "polygon" in data:
69
- polygon = Polygon(data["polygon"], holes=data["holes"])
70
-
71
- # Shift the polygon by the offset.
72
- polygon = translate(polygon, xoff=x_offset, yoff=y_offset)
73
-
74
- # Skip small polygons.
75
- if polygon.area >= min_area:
76
- polygons.append([polygon, label])
77
-
78
- return polygons