imaging-server-kit 0.0.10__tar.gz → 0.0.12__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 (127) hide show
  1. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/PKG-INFO +1 -1
  2. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/algorithm_server.md +8 -7
  3. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/getting_started.md +2 -2
  4. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/basic/auto_threshold.py +1 -1
  5. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/main.py +8 -3
  6. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/_version.py +2 -2
  7. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/decorator.py +0 -2
  8. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/geometry.py +16 -4
  9. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/hub/multialgo_server.py +34 -30
  10. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit.egg-info/PKG-INFO +1 -1
  11. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/.dockerignore +0 -0
  12. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/.gitattributes +0 -0
  13. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/.github/workflows/deploy.yml +0 -0
  14. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/.github/workflows/publish.yml +0 -0
  15. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/.gitignore +0 -0
  16. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/Dockerfile +0 -0
  17. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/Dockerfile-GPU +0 -0
  18. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/LICENSE +0 -0
  19. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/MANIFEST.in +0 -0
  20. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/README.md +0 -0
  21. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/build.sh +0 -0
  22. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/_config.yml +0 -0
  23. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/_toc.yml +0 -0
  24. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/assets/screenshot_napari_threshold.png +0 -0
  25. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/assets/screenshot_running_server.png +0 -0
  26. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/index.md +0 -0
  27. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/logo.ico +0 -0
  28. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/api_endpoints.md +0 -0
  29. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/authentication.md +0 -0
  30. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/client_apps.md +0 -0
  31. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/docker.md +0 -0
  32. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/examples.md +0 -0
  33. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/docs/sections/multi_algo_server.md +0 -0
  34. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/basic/multi_algo.py +0 -0
  35. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/basic/sample_images/blobs.tif +0 -0
  36. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/basic/threshold.py +0 -0
  37. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/docker-compose.yml +0 -0
  38. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-cellpose/Dockerfile +0 -0
  39. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-cellpose/README.md +0 -0
  40. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-cellpose/docker-compose.yml +0 -0
  41. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-cellpose/main.py +0 -0
  42. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-cellpose/requirements.txt +0 -0
  43. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-cellpose/sample_images/nuclei_2d.tif +0 -0
  44. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-orientationpy/Dockerfile +0 -0
  45. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-orientationpy/README.md +0 -0
  46. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-orientationpy/docker-compose.yml +0 -0
  47. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-orientationpy/main.py +0 -0
  48. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-orientationpy/requirements.txt +0 -0
  49. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-orientationpy/sample_images/image1_from_OrientationJ.tif +0 -0
  50. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-rembg/Dockerfile +0 -0
  51. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-rembg/README.md +0 -0
  52. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-rembg/docker-compose.yml +0 -0
  53. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-rembg/main.py +0 -0
  54. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-rembg/requirements.txt +0 -0
  55. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-rembg/sample_images/astronaut.tif +0 -0
  56. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/Dockerfile +0 -0
  57. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/README.md +0 -0
  58. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/docker-compose.yml +0 -0
  59. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/requirements.txt +0 -0
  60. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/sample_images/blobs.tif +0 -0
  61. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-skimage-log/sample_images/tracks.tif +0 -0
  62. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-stardist/Dockerfile +0 -0
  63. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-stardist/README.md +0 -0
  64. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-stardist/docker-compose.yml +0 -0
  65. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-stardist/main.py +0 -0
  66. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-stardist/requirements.txt +0 -0
  67. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-stardist/sample_images/nuclei_2d.tif +0 -0
  68. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-trackpy/Dockerfile +0 -0
  69. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-trackpy/README.md +0 -0
  70. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-trackpy/docker-compose.yml +0 -0
  71. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-trackpy/main.py +0 -0
  72. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-trackpy/requirements.txt +0 -0
  73. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/examples/servers/serverkit-trackpy/sample_images/tracks.tif +0 -0
  74. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/main.py +0 -0
  75. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/pyproject.toml +0 -0
  76. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/setup.cfg +0 -0
  77. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/__init__.py +0 -0
  78. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/__init__.py +0 -0
  79. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/authenticated_server.py +0 -0
  80. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/static/css/login.css +0 -0
  81. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/static/css/register.css +0 -0
  82. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/static/js/login_script.js +0 -0
  83. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/static/js/register_script.js +0 -0
  84. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/templates/login.html +0 -0
  85. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/templates/register.html +0 -0
  86. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/utils/__init__.py +0 -0
  87. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/utils/db.py +0 -0
  88. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/utils/schemas.py +0 -0
  89. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/auth/utils/users.py +0 -0
  90. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/cli.py +0 -0
  91. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/client/__init__.py +0 -0
  92. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/client/client.py +0 -0
  93. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/__init__.py +0 -0
  94. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/encoding.py +0 -0
  95. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/errors.py +0 -0
  96. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/serialization.py +0 -0
  97. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/server.py +0 -0
  98. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/static/css/index.css +0 -0
  99. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/static/css/info.css +0 -0
  100. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/templates/index.html +0 -0
  101. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/templates/info.html +0 -0
  102. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/core/ui_library.py +0 -0
  103. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/demo/__init__.py +0 -0
  104. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/demo/sample_images/blobs.tif +0 -0
  105. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/demo/threshold.py +0 -0
  106. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/hub/__init__.py +0 -0
  107. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/hub/algorithm_hub.py +0 -0
  108. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/cookiecutter.json +0 -0
  109. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/.dockerignore +0 -0
  110. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/.gitignore +0 -0
  111. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/Dockerfile +0 -0
  112. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/README.md +0 -0
  113. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/docker-compose.yml +0 -0
  114. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/main.py +0 -0
  115. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/requirements.txt +0 -0
  116. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit/template/serverkit-{{ cookiecutter.project_slug }}/sample_images/blobs.tif +0 -0
  117. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit.egg-info/SOURCES.txt +0 -0
  118. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit.egg-info/dependency_links.txt +0 -0
  119. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit.egg-info/entry_points.txt +0 -0
  120. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit.egg-info/requires.txt +0 -0
  121. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/src/imaging_server_kit.egg-info/top_level.txt +0 -0
  122. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/start_algorithm_hub.py +0 -0
  123. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/tests/test_error_paths.py +0 -0
  124. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/tests/test_import.py +0 -0
  125. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/tests/test_multialgo_server.py +0 -0
  126. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/tests/test_parameter_schema.py +0 -0
  127. {imaging_server_kit-0.0.10 → imaging_server_kit-0.0.12}/tests/test_simple_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imaging-server-kit
3
- Version: 0.0.10
3
+ Version: 0.0.12
4
4
  Summary: Deploy image processing algorithms in FastAPI servers.
5
5
  Author-email: Mallory Wittwer <mallory.wittwer@epfl.ch>
6
6
  License: Copyright (c) 2024, EPFL
@@ -23,7 +23,7 @@ To turn this function into to an algorithm server, we first need to modify the f
23
23
 
24
24
  ## Make the function return a list of data tuples
25
25
 
26
- The function should return a list of tuples ("data tuples"). Each tuple represents one distinct output of the algorithm.
26
+ The function should return a list of tuples. Each tuple has three elements and represents a distinct output of the algorithm.
27
27
 
28
28
  Our threshold algorithm only has one output: the segmentation mask resulting from thresholding.
29
29
 
@@ -36,22 +36,22 @@ def threshold_algo_server(image: np.ndarray, threshold: float):
36
36
  The data tuples are inspired from Napari's [LayerDataTuple](https://napari.org/0.4.15/guides/magicgui.html?highlight=layerdatatuple) model. They include three elements:
37
37
 
38
38
  - The *first element* is the **data**, usually in the form of a Numpy array. The shape and interpretation of the axes depends on what the output represents (cf. table below).
39
- - The *second element* is optional **metadata** associated with the output, represented as a Python dictionary. It can be empty (`{}`). These metadata are used to affect how the output is displayed in client apps.
39
+ - The *second element* is a Python dictionary representing **metadata** associated with the output. It can be empty (`{}`). These metadata are used to affect how the output is displayed in client apps (e.g. Napari).
40
40
  - The *third element* is the output type: a string identifying what the output represents.
41
41
 
42
42
  | Output type | Description |
43
43
  | ----------------- | ----------------------------------------------------------------------------------------------------- |
44
- | `"image"` | An image or image-like data (incl. 3D and RGB) as a nD array. |
45
- | `"mask"` | A segmentation mask (2D, 3D) as integer nD array. Integers represent the **object class**. |
44
+ | `"image"` | An image or image-like data (incl. 3D and RGB) as a numpy array. |
45
+ | `"mask"` | A segmentation mask (2D, 3D) as integer numpy array. Integers represent the **object class**. |
46
46
  | `"instance_mask"` | A segmentation mask (2D, 3D) as integer nD array. Integers represent **object instances**. |
47
47
  | `"points"` | A collection of point coordinates (array of shape (N, 2) or (N, 3)). |
48
- | `"boxes"` | A collection of boxes (array of shape (N, 4) representing the top-left and bottom-right box corners). |
48
+ | `"boxes"` | A collection of boxes (array of shape (N, 4, 2) representing the box corners). |
49
49
  | `"vectors"` | Array of vectors in the Napari [Vectors](https://napari.org/stable/howtos/layers/vectors.html) format.|
50
50
  | `"tracks"` | Array of tracks in the Napari [Tracks](https://napari.org/stable/howtos/layers/tracks.html) format. |
51
51
  | `"class"` | A class label (for image classification). |
52
52
  | `"text"` | A string of text (for example, for image captioning). |
53
53
 
54
- The function can return an arbitrary number of outputs, following the pattern:
54
+ The function can return multiple outputs, following the pattern:
55
55
 
56
56
  ```
57
57
  # Body of the function
@@ -68,10 +68,11 @@ The function parameters can only be **numpy arrays**, **numeric values** (`int`,
68
68
 
69
69
  ## Decorate the function with `@algorithm_server`
70
70
 
71
- This serves several purposes:
71
+ This has several purposes:
72
72
 
73
73
  - It converts the python function to a FastAPI server with predefined routes (cf. [API Endpoints](api_endpoints)).
74
74
  - It enables the server to validate algorithm parameters when receiving requests.
75
+ - It tells client apps (Napari, QuPath) how to render the parameters in the user interface.
75
76
  - Optional info about the algorithm server can be added to populate its `info` page.
76
77
 
77
78
  Below is our decorated threshold algorithm function:
@@ -10,7 +10,7 @@ serverkit demo
10
10
 
11
11
  This will start a demo web server for a simple intensity threshold algorithm.
12
12
 
13
- The server will be running on http://localhost:8000. Open your web browser and navigate to this page to check that the server is running. You should see a web page displaying information about the running threshold algorithm server.
13
+ The server will be running on http://localhost:8000. Open your web browser and navigate to this page to check that the server is running. You should see a web page displaying information about the threshold algorithm server.
14
14
 
15
15
  ![screenshot](../assets/screenshot_running_server.png)
16
16
 
@@ -42,7 +42,7 @@ You can also interact with the algorithm server via QuPath. To try it out, you n
42
42
 
43
43
  ## Usage from Python
44
44
 
45
- It is also possible to connect to an algorithm server and run computation from Python directly:
45
+ It is also possible to connect to an algorithm server and run computations from Python directly:
46
46
 
47
47
  ```python
48
48
  from imaging_server_kit import Client
@@ -14,7 +14,7 @@ from imaging_server_kit import algorithm_server, ImageUI, DropDownUI
14
14
  parameters={
15
15
  "image": ImageUI(),
16
16
  "method": DropDownUI(
17
- default="otsu",
17
+ default="Otsu",
18
18
  title="Method",
19
19
  description="Auto-threshold method.",
20
20
  items=["Otsu", "Li"],
@@ -127,9 +127,14 @@ def skimage_log_server(
127
127
  }, # sigmas = numpy array representing the point size
128
128
  }
129
129
 
130
- return [
131
- (points, points_params, "points"),
132
- ]
130
+ if len(points):
131
+ return [
132
+ (points, points_params, "points"),
133
+ ]
134
+ else:
135
+ return [
136
+ ("No points were detected.", {}, "text")
137
+ ]
133
138
 
134
139
 
135
140
  if __name__ == "__main__":
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.10'
21
- __version_tuple__ = version_tuple = (0, 0, 10)
20
+ __version__ = version = '0.0.12'
21
+ __version_tuple__ = version_tuple = (0, 0, 12)
@@ -11,7 +11,6 @@ from .encoding import decode_contents
11
11
  class BaseParamsModel(BaseModel):
12
12
  model_config = ConfigDict(extra="forbid")
13
13
 
14
-
15
14
  def decode_image_array(cls, v, dimensionality) -> "np.ndarray":
16
15
  image_array = decode_contents(v)
17
16
 
@@ -58,7 +57,6 @@ def parse_params(parameters: dict) -> BaseModel:
58
57
  "widget_type"
59
58
  ] = param_details.widget_type
60
59
 
61
-
62
60
  if param_details.widget_type in ["image", "mask"]:
63
61
  validated_func = partial(
64
62
  decode_image_array, dimensionality=param_details.dimensionality
@@ -27,7 +27,11 @@ def mask2features(segmentation_mask: np.ndarray) -> List[Feature]:
27
27
  for detection_id, contour in enumerate(polygons.points, start=1):
28
28
  coords = np.array(contour)
29
29
  coords = np.vstack([coords, coords[0]]) # Close the polygon for QuPath
30
- geom = Polygon(coordinates=[coords.tolist()])
30
+ try:
31
+ geom = Polygon(coordinates=[coords.tolist()], validate=True)
32
+ except ValueError:
33
+ # TODO: not sure what to do when this happens (very occasionally)
34
+ print("Found an invalid polygon...")
31
35
  feature = Feature(
32
36
  geometry=geom,
33
37
  properties={
@@ -75,7 +79,11 @@ def instance_mask2features(segmentation_mask: np.ndarray) -> List[Feature]:
75
79
  for contour in polygons.points:
76
80
  coords = np.array(contour)
77
81
  coords = np.vstack([coords, coords[0]]) # Close the polygon for QuPath
78
- geom = Polygon(coordinates=[coords.tolist()])
82
+ try:
83
+ geom = Polygon(coordinates=[coords.tolist()], validate=True)
84
+ except ValueError:
85
+ # TODO: not sure what to do when this happens (very occasionally)
86
+ print("Found an invalid polygon...")
79
87
  feature = Feature(
80
88
  geometry=geom,
81
89
  properties={
@@ -170,14 +178,18 @@ def boxes2features(boxes: np.ndarray) -> List[Feature]:
170
178
  coords.append(
171
179
  coords[0]
172
180
  ) # Add the first element at the end to close the Polygon
173
- geom = Polygon(coordinates=[coords])
181
+ try:
182
+ geom = Polygon(coordinates=[coords], validate=True)
183
+ except ValueError:
184
+ print("Invalid box polygon found. Expected an array of shape (N, 4, D) representing the corners of the box.")
185
+ geom = None # TODO: should we handle this better?
174
186
  features.append(Feature(geometry=geom, properties={"Detection ID": i}))
175
187
  return features
176
188
 
177
189
 
178
190
  def features2boxes(features):
179
191
  boxes = np.array([feature["geometry"]["coordinates"] for feature in features])
180
- boxes = np.squeeze(boxes)
192
+ boxes = np.array([box[0] for box in boxes]) # We get back a shape of (N, 1, 5, 2) - so we remove dim. 1
181
193
  boxes = boxes[:, :-1] # Remove the last element
182
194
  boxes = boxes[:, :, ::-1] # Invert XY
183
195
  return boxes
@@ -13,9 +13,15 @@ from contextlib import asynccontextmanager
13
13
  import asyncio
14
14
  import imaging_server_kit as serverkit
15
15
  from imaging_server_kit import AlgorithmServer
16
- from imaging_server_kit.core import parse_algo_params_schema, encode_contents, decode_contents
17
-
18
- templates_dir = importlib.resources.files("imaging_server_kit.core").joinpath("templates")
16
+ from imaging_server_kit.core import (
17
+ parse_algo_params_schema,
18
+ encode_contents,
19
+ decode_contents,
20
+ )
21
+
22
+ templates_dir = importlib.resources.files("imaging_server_kit.core").joinpath(
23
+ "templates"
24
+ )
19
25
  static_dir = importlib.resources.files("imaging_server_kit.core").joinpath("static")
20
26
 
21
27
  templates = Jinja2Templates(directory=str(templates_dir))
@@ -31,7 +37,9 @@ class MultiAlgorithmServer: # TODO: could this inherit from algoserver somehow?
31
37
 
32
38
  # Centralized exception handlers
33
39
  @self.app.exception_handler(RequestValidationError)
34
- async def validation_exception_handler(request: Request, exc: RequestValidationError):
40
+ async def validation_exception_handler(
41
+ request: Request, exc: RequestValidationError
42
+ ):
35
43
  return JSONResponse(
36
44
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
37
45
  content={"error_type": "validation_error", "detail": exc.errors()},
@@ -40,12 +48,14 @@ class MultiAlgorithmServer: # TODO: could this inherit from algoserver somehow?
40
48
  @self.app.exception_handler(Exception)
41
49
  async def generic_exception_handler(request: Request, exc: Exception):
42
50
  from fastapi import HTTPException
51
+
43
52
  if isinstance(exc, HTTPException):
44
53
  raise exc
45
54
  return JSONResponse(
46
55
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
47
56
  content={"error_type": "internal_server_error", "message": str(exc)},
48
57
  )
58
+
49
59
  self.app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
50
60
  self.services = [server_name]
51
61
 
@@ -119,8 +129,10 @@ class MultiAlgorithmServer: # TODO: could this inherit from algoserver somehow?
119
129
  request: Request = ...,
120
130
  ):
121
131
  if algorithm_name not in self.algorithms:
122
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
123
- detail=f"Algorithm {algorithm_name} not found")
132
+ raise HTTPException(
133
+ status_code=status.HTTP_404_NOT_FOUND,
134
+ detail=f"Algorithm {algorithm_name} not found",
135
+ )
124
136
  algo_info = self.algorithms[algorithm_name].get("algo_info")
125
137
  algo_params_schema = get_algo_params(algorithm_name)
126
138
  algo_params = parse_algo_params_schema(algo_params_schema)
@@ -142,8 +154,10 @@ class MultiAlgorithmServer: # TODO: could this inherit from algoserver somehow?
142
154
  request: Request = ...,
143
155
  ):
144
156
  if algorithm_name not in self.algorithms:
145
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
146
- detail=f"Algorithm {algorithm_name} not found")
157
+ raise HTTPException(
158
+ status_code=status.HTTP_404_NOT_FOUND,
159
+ detail=f"Algorithm {algorithm_name} not found",
160
+ )
147
161
 
148
162
  parameters_model = self.algorithms.get(algorithm_name, {}).get(
149
163
  "parameters_model"
@@ -156,34 +170,26 @@ class MultiAlgorithmServer: # TODO: could this inherit from algoserver somehow?
156
170
  except ValidationError as e:
157
171
  raise HTTPException(status_code=422, detail=e.errors())
158
172
 
159
- # `array` fields must be decoded (TODO: is there a better way of handling this?)
160
- if "image" in data.keys():
161
- data["image"] = decode_contents(data["image"])
162
- elif "mask" in data.keys():
163
- data["mask"] = decode_contents(data["mask"])
164
- elif "points" in data.keys():
165
- data["points"] = decode_contents(data["points"])
166
- elif "vectors" in data.keys():
167
- data["vectors"] = decode_contents(data["vectors"])
168
- elif "shapes" in data.keys():
169
- data["shapes"] = decode_contents(data["shapes"])
170
- elif "tracks" in data.keys():
171
- data["tracks"] = decode_contents(data["tracks"])
172
-
173
- return await self._run_algo_logic(algorithm_name, data)
173
+ decoded_params = algo_params.dict()
174
+
175
+ return await self._run_algo_logic(algorithm_name, decoded_params)
174
176
 
175
177
  @self.app.get("/{algorithm_name}/parameters", response_model=dict)
176
178
  def get_algo_params(algorithm_name):
177
179
  if algorithm_name not in self.algorithms:
178
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
179
- detail=f"Algorithm {algorithm_name} not found")
180
+ raise HTTPException(
181
+ status_code=status.HTTP_404_NOT_FOUND,
182
+ detail=f"Algorithm {algorithm_name} not found",
183
+ )
180
184
  return self.get_algorithm_params(algorithm_name)
181
185
 
182
186
  @self.app.get("/{algorithm_name}/sample_images", response_model=dict)
183
187
  def get_sample_images(algorithm_name):
184
188
  if algorithm_name not in self.algorithms:
185
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
186
- detail=f"Algorithm {algorithm_name} not found")
189
+ raise HTTPException(
190
+ status_code=status.HTTP_404_NOT_FOUND,
191
+ detail=f"Algorithm {algorithm_name} not found",
192
+ )
187
193
  return self.get_algorithm_sample_images(algorithm_name)
188
194
 
189
195
  async def _serialize_result_tuple(self, result_data_tuple):
@@ -228,7 +234,5 @@ class MultiAlgorithmServer: # TODO: could this inherit from algoserver somehow?
228
234
  server = self.algorithms.get(algorithm_name)
229
235
  load_funct = server.get("load_funct")
230
236
  images = load_funct()
231
- encoded_images = [
232
- {"sample_image": encode_contents(image)} for image in images
233
- ]
237
+ encoded_images = [{"sample_image": encode_contents(image)} for image in images]
234
238
  return {"sample_images": encoded_images}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imaging-server-kit
3
- Version: 0.0.10
3
+ Version: 0.0.12
4
4
  Summary: Deploy image processing algorithms in FastAPI servers.
5
5
  Author-email: Mallory Wittwer <mallory.wittwer@epfl.ch>
6
6
  License: Copyright (c) 2024, EPFL