imaging-server-kit 0.0.1__tar.gz → 0.0.2__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 (64) hide show
  1. imaging_server_kit-0.0.2/.dockerignore +2 -0
  2. imaging_server_kit-0.0.2/.gitattributes +35 -0
  3. imaging_server_kit-0.0.2/.gitignore +28 -0
  4. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/Dockerfile +6 -2
  5. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/Dockerfile-GPU +4 -1
  6. imaging_server_kit-0.0.2/Dockerfile-registry +5 -0
  7. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/PKG-INFO +13 -3
  8. imaging_server_kit-0.0.2/build.sh +14 -0
  9. imaging_server_kit-0.0.2/examples/.gitkeep +0 -0
  10. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/pyproject.toml +12 -2
  11. imaging_server_kit-0.0.2/src/imaging_server_kit/__init__.py +20 -0
  12. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit/_version.py +2 -2
  13. imaging_server_kit-0.0.2/src/imaging_server_kit/assets/style.css +108 -0
  14. imaging_server_kit-0.0.2/src/imaging_server_kit/autostartup_registry.py +58 -0
  15. imaging_server_kit-0.0.2/src/imaging_server_kit/client.py +131 -0
  16. imaging_server_kit-0.0.2/src/imaging_server_kit/errors.py +41 -0
  17. imaging_server_kit-0.0.2/src/imaging_server_kit/geometry.py +148 -0
  18. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit/registry.py +42 -15
  19. imaging_server_kit-0.0.2/src/imaging_server_kit/serialization.py +135 -0
  20. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit/server.py +63 -11
  21. imaging_server_kit-0.0.2/src/imaging_server_kit/static/index.css +32 -0
  22. imaging_server_kit-0.0.2/src/imaging_server_kit/static/info.css +32 -0
  23. imaging_server_kit-0.0.2/src/imaging_server_kit/templates/index.html +25 -0
  24. imaging_server_kit-0.0.2/src/imaging_server_kit/templates/info.html +63 -0
  25. imaging_server_kit-0.0.2/src/imaging_server_kit/web_demo.py +393 -0
  26. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit.egg-info/PKG-INFO +13 -3
  27. imaging_server_kit-0.0.2/src/imaging_server_kit.egg-info/SOURCES.txt +36 -0
  28. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit.egg-info/requires.txt +7 -0
  29. imaging_server_kit-0.0.1/.dockerignore +0 -4
  30. imaging_server_kit-0.0.1/.gitignore +0 -87
  31. imaging_server_kit-0.0.1/Dockerfile-3.10 +0 -11
  32. imaging_server_kit-0.0.1/examples/example.py +0 -35
  33. imaging_server_kit-0.0.1/reference_deployment/LICENSE +0 -27
  34. imaging_server_kit-0.0.1/reference_deployment/README.md +0 -48
  35. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/README.md +0 -2
  36. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/cookiecutter.json +0 -5
  37. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/requirements.txt +0 -1
  38. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/serverkit-{{ cookiecutter.package }}/Dockerfile +0 -5
  39. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/serverkit-{{ cookiecutter.package }}/README.md +0 -6
  40. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/serverkit-{{ cookiecutter.package }}/main.py +0 -72
  41. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/serverkit-{{ cookiecutter.package }}/requirements.txt +0 -1
  42. imaging_server_kit-0.0.1/reference_deployment/cookiecutter_template/serverkit-{{ cookiecutter.package }}/sample_images/blobs.tif +0 -0
  43. imaging_server_kit-0.0.1/reference_deployment/docker-compose.yml +0 -14
  44. imaging_server_kit-0.0.1/reference_deployment/registry/Dockerfile +0 -3
  45. imaging_server_kit-0.0.1/reference_deployment/servers/serverkit-skimage/Dockerfile +0 -5
  46. imaging_server_kit-0.0.1/reference_deployment/servers/serverkit-skimage/README.md +0 -6
  47. imaging_server_kit-0.0.1/reference_deployment/servers/serverkit-skimage/main.py +0 -141
  48. imaging_server_kit-0.0.1/reference_deployment/servers/serverkit-skimage/requirements.txt +0 -1
  49. imaging_server_kit-0.0.1/reference_deployment/servers/serverkit-skimage/sample_images/blobs.tif +0 -0
  50. imaging_server_kit-0.0.1/reference_deployment/servers/serverkit-skimage/tests/test_skimage.py +0 -26
  51. imaging_server_kit-0.0.1/src/imaging_server_kit/__init__.py +0 -8
  52. imaging_server_kit-0.0.1/src/imaging_server_kit/client.py +0 -116
  53. imaging_server_kit-0.0.1/src/imaging_server_kit/serialization.py +0 -49
  54. imaging_server_kit-0.0.1/src/imaging_server_kit.egg-info/SOURCES.txt +0 -43
  55. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/.github/workflows/publish.yml +0 -0
  56. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/LICENSE +0 -0
  57. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/MANIFEST.in +0 -0
  58. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/README.md +0 -0
  59. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/main.py +0 -0
  60. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/setup.cfg +0 -0
  61. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit/encoding.py +0 -0
  62. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit.egg-info/dependency_links.txt +0 -0
  63. {imaging_server_kit-0.0.1 → imaging_server_kit-0.0.2}/src/imaging_server_kit.egg-info/top_level.txt +0 -0
  64. /imaging_server_kit-0.0.1/reference_deployment/registry/main.py → /imaging_server_kit-0.0.2/start_registry.py +0 -0
@@ -0,0 +1,2 @@
1
+ reference_deployment/
2
+ examples/
@@ -0,0 +1,35 @@
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,28 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ old/
5
+ data/
6
+ env/
7
+ build/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ *.egg-info/
13
+ .installed.cfg
14
+ *.egg
15
+ .tox/
16
+ .coverage
17
+ .coverage.*
18
+ .cache
19
+ docs/_build/
20
+ .idea/
21
+ venv/
22
+ .vscode/
23
+ .ipynb_checkpoints
24
+ .python-version
25
+ .DS_Store
26
+ **/_version.py
27
+
28
+ push.sh
@@ -1,6 +1,10 @@
1
+ # Doc: https://github.com/conda-forge/miniforge-images
1
2
  FROM condaforge/miniforge3:latest
2
3
 
3
- RUN conda install python=3.9 -y
4
+ ARG PYTHON_VERSION=3.9
5
+ RUN conda install python=$PYTHON_VERSION -y
6
+
7
+ WORKDIR /app
4
8
 
5
9
  COPY . .
6
10
 
@@ -8,4 +12,4 @@ RUN python -m pip install -e .
8
12
 
9
13
  EXPOSE 8000
10
14
 
11
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--timeout-keep-alive", "300"]
15
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
@@ -1,5 +1,8 @@
1
+ # Doc: https://github.com/iot-salzburg/gpu-jupyter
1
2
  FROM cschranz/gpu-jupyter:v1.6_cuda-12.0_ubuntu-22.04_python-only
3
+
2
4
  ENV DEBIAN_FRONTEND=noninteractive
5
+
3
6
  USER root
4
7
 
5
8
  COPY . .
@@ -8,4 +11,4 @@ RUN python -m pip install -e .
8
11
 
9
12
  EXPOSE 8000
10
13
 
11
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--timeout-keep-alive", "300"]
14
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
@@ -0,0 +1,5 @@
1
+ FROM registry.rcp.epfl.ch/imaging-server-kit/imaging-server-kit:3.9
2
+
3
+ COPY start_registry.py .
4
+
5
+ CMD ["uvicorn", "start_registry:app", "--host", "0.0.0.0", "--port", "8000"]
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: imaging-server-kit
3
- Version: 0.0.1
4
- Summary: Run image processing algorithms in a FastAPI server.
3
+ Version: 0.0.2
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
7
7
  All rights reserved.
@@ -34,12 +34,15 @@ License: Copyright (c) 2024, EPFL
34
34
  Project-URL: homepage, https://gitlab.com/epfl-center-for-imaging/imaging-server-kit
35
35
  Project-URL: repository, https://gitlab.com/epfl-center-for-imaging/imaging-server-kit
36
36
  Classifier: Development Status :: 2 - Pre-Alpha
37
+ Classifier: Intended Audience :: Developers
37
38
  Classifier: License :: OSI Approved :: BSD License
39
+ Classifier: Operating System :: OS Independent
40
+ Classifier: Programming Language :: Python
38
41
  Classifier: Programming Language :: Python :: 3
42
+ Classifier: Programming Language :: Python :: 3 :: Only
39
43
  Classifier: Programming Language :: Python :: 3.9
40
44
  Classifier: Programming Language :: Python :: 3.10
41
45
  Classifier: Programming Language :: Python :: 3.11
42
- Classifier: Programming Language :: Python :: 3.12
43
46
  Classifier: Topic :: Scientific/Engineering :: Image Processing
44
47
  Requires-Python: >=3.9
45
48
  Description-Content-Type: text/markdown
@@ -50,6 +53,13 @@ Requires-Dist: python-multipart
50
53
  Requires-Dist: tifffile
51
54
  Requires-Dist: numpy
52
55
  Requires-Dist: scikit-image
56
+ Requires-Dist: geojson
57
+ Requires-Dist: docker
58
+ Requires-Dist: jinja2
59
+ Requires-Dist: pyyaml
60
+ Requires-Dist: dash
61
+ Requires-Dist: dash-bootstrap-components
62
+ Requires-Dist: pytest
53
63
 
54
64
  ![EPFL Center for Imaging logo](https://imaging.epfl.ch/resources/logo-for-gitlab.svg)
55
65
  # 🪐 Imaging Server Kit
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ PYTHON_VERSIONS=("3.9" "3.10" "3.11")
4
+
5
+ for version in "${PYTHON_VERSIONS[@]}"; do
6
+ echo "Building image for Python $version..."
7
+ docker build --build-arg PYTHON_VERSION=$version -t imaging-server-kit:$version .
8
+ done
9
+
10
+ # echo "Building image for Python 3.9, GPU-compatible..."
11
+ # docker build -t imaging-server-kit:gpu --file Dockerfile-GPU .
12
+
13
+ echo "Building image for Python 3.9, serverkit registry..."
14
+ docker build -t imaging-server-kit:registry --file Dockerfile-registry .
File without changes
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "imaging-server-kit"
3
3
  dynamic = ["version"]
4
- description = "Run image processing algorithms in a FastAPI server."
4
+ description = "Deploy image processing algorithms in FastAPI servers. "
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
7
7
  license = { file = "LICENSE" }
@@ -9,12 +9,15 @@ authors = [{ name = "Mallory Wittwer", email = "mallory.wittwer@epfl.ch" }]
9
9
 
10
10
  classifiers = [
11
11
  "Development Status :: 2 - Pre-Alpha",
12
+ "Intended Audience :: Developers",
12
13
  "License :: OSI Approved :: BSD License",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python",
13
16
  "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
14
18
  "Programming Language :: Python :: 3.9",
15
19
  "Programming Language :: Python :: 3.10",
16
20
  "Programming Language :: Python :: 3.11",
17
- "Programming Language :: Python :: 3.12",
18
21
  "Topic :: Scientific/Engineering :: Image Processing",
19
22
  ]
20
23
 
@@ -25,6 +28,13 @@ dependencies = [
25
28
  "tifffile",
26
29
  "numpy",
27
30
  "scikit-image",
31
+ "geojson",
32
+ "docker", # For the autostartup regsitry
33
+ "jinja2", # For the /info route
34
+ "pyyaml", # For the /info route
35
+ "dash", # For the dash app
36
+ "dash-bootstrap-components", # For the dash app
37
+ "pytest", # For testing
28
38
  ]
29
39
 
30
40
  [project.urls]
@@ -0,0 +1,20 @@
1
+ from imaging_server_kit._version import __version__
2
+ from imaging_server_kit.client import Client
3
+ from imaging_server_kit.server import Server, Parameters
4
+ from imaging_server_kit.registry import Registry
5
+ from imaging_server_kit.autostartup_registry import AutoStartupRegistry
6
+ from imaging_server_kit.encoding import encode_contents, decode_contents
7
+ from imaging_server_kit.serialization import (
8
+ serialize_result_tuple,
9
+ deserialize_result_tuple,
10
+ )
11
+ from imaging_server_kit.geometry import (
12
+ mask2features,
13
+ features2mask,
14
+ boxes2features,
15
+ features2boxes,
16
+ points2features,
17
+ features2points,
18
+ vectors2features,
19
+ features2vectors,
20
+ )
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.0.1'
16
- __version_tuple__ = version_tuple = (0, 0, 1)
15
+ __version__ = version = '0.0.2'
16
+ __version_tuple__ = version_tuple = (0, 0, 2)
@@ -0,0 +1,108 @@
1
+ body {
2
+ padding: 0;
3
+ margin: 0;
4
+ background-color: rgb(40, 40, 40);
5
+ color: white;
6
+ height: 100vh;
7
+ display: flex;
8
+ flex-direction: column;
9
+ }
10
+
11
+ .main-container {
12
+ height: 100vh;
13
+ display: flex;
14
+ flex-direction: column;
15
+ /* background-color: orange; */
16
+ }
17
+
18
+ header {
19
+ margin: 10px 0px;
20
+ text-align: center;
21
+ height: 120px;
22
+ line-height: 120px;
23
+ }
24
+
25
+ footer {
26
+ margin: 10px 0px;
27
+ text-align: center;
28
+ height: 120px;
29
+ line-height: 120px;
30
+ }
31
+
32
+ .main-row {
33
+ display: flex;
34
+ flex: 1 1;
35
+ overflow: auto;
36
+ width: 100%;
37
+ margin: 10px;
38
+ margin-left: 0px;
39
+ /* background-color: purple; */
40
+ }
41
+
42
+ .col {
43
+ padding: 0;
44
+ }
45
+
46
+ .parameters-column,
47
+ .layer-column,
48
+ .graph-column {
49
+ margin: 0 !important;
50
+ padding: 15px;
51
+ background-color: rgb(50, 50, 50);
52
+ margin-bottom: 0px;
53
+ height: 100%;
54
+ overflow: auto;
55
+ }
56
+
57
+ .layer-column {
58
+ /* flex: 0 1 400px; */
59
+ /* background-color: blue; */
60
+ }
61
+
62
+ .parameters-column {
63
+ /* flex: 0 1 400px; */
64
+ min-width: 400px;
65
+ /* background-color: green; */
66
+ }
67
+
68
+ .graph-column {
69
+ padding: 0px;
70
+ margin: 0;
71
+ flex: 1;
72
+ min-width: 800px;
73
+ /* background-color: orange; */
74
+ }
75
+
76
+
77
+ .layer-column .btn-secondary,
78
+ .parameters-column .btn-primary {
79
+ width: 100%;
80
+ margin-top: 10px;
81
+ }
82
+
83
+ .dash-dropdown * {
84
+ background-color: rgb(70, 70, 70);
85
+ color: white;
86
+ }
87
+
88
+ .dash-dropdown .Select-value-label {
89
+ color: white !important;
90
+ }
91
+
92
+ .upload-data {
93
+ background-color: rgb(0, 0, 0);
94
+ border: 1px dashed rgb(240, 240, 240);
95
+ padding: 10px;
96
+ text-align: center;
97
+ }
98
+
99
+ .graph-container {
100
+ background-color: burlywood;
101
+ flex: 1;
102
+ height: 90%;
103
+ }
104
+
105
+ #graph {
106
+ background-color: rgb(0, 0, 0);
107
+ height: 100%;
108
+ }
@@ -0,0 +1,58 @@
1
+ from fastapi import FastAPI
2
+ import docker
3
+
4
+ class AutoStartupRegistry:
5
+ def __init__(self, algo_servers) -> None:
6
+ self.algo_servers = algo_servers
7
+ self.app = FastAPI()
8
+ self.app.on_event("shutdown")(self.stop_all_algorithm_containers) # This doesn't work.
9
+ self.docker_client = docker.from_env()
10
+ self.services = {}
11
+ self.register_routes()
12
+
13
+ def register_routes(self):
14
+ @self.app.get("/")
15
+ def home():
16
+ return list_services()
17
+
18
+ @self.app.get("/services")
19
+ def list_services():
20
+ return {"services": list(self.services.keys())}
21
+
22
+ @self.app.get("/{algorithm}")
23
+ def get_algorithm_url(algorithm):
24
+ if algorithm not in self.services.keys():
25
+ container_url = self.start_algorithm_container(algorithm)
26
+ else:
27
+ container_url = self.services.get(algorithm).get("url")
28
+ return {"url": container_url}
29
+
30
+ def start_algorithm_container(self, algorithm):
31
+ algorithm_server_image = self.algo_servers[algorithm].get('image')
32
+
33
+ port = self.algo_servers[algorithm].get('port')
34
+
35
+ container = self.docker_client.containers.run(
36
+ image=algorithm_server_image,
37
+ ports={"8000/tcp": port},
38
+ detach=True,
39
+ remove=True,
40
+ name=f"serverkit-server-{algorithm}"
41
+ )
42
+
43
+ container_url = f"http://localhost:{port}"
44
+
45
+ self.services[algorithm] = {
46
+ "container": container,
47
+ "url": container_url,
48
+ }
49
+
50
+ return container_url
51
+
52
+ def stop_algorithm_container(self, algorithm):
53
+ container = self.services[algorithm].get("container")
54
+ container.stop()
55
+
56
+ def stop_all_algorithm_containers(self):
57
+ for algorithm in self.services.keys():
58
+ self.stop_algorithm_container(algorithm)
@@ -0,0 +1,131 @@
1
+ import requests
2
+ from typing import List, Dict, Tuple
3
+ import numpy as np
4
+ import imaging_server_kit as serverkit
5
+ from imaging_server_kit.errors import (
6
+ AlgorithmNotFoundError,
7
+ AlgorithmServerError,
8
+ InvalidAlgorithmParametersError,
9
+ ServerRequestError,
10
+ )
11
+
12
+
13
+ class Client:
14
+ def __init__(self, server_url: str = "") -> None:
15
+ self._server_url = server_url
16
+ self._algorithms = {}
17
+ if server_url != "":
18
+ self.connect(server_url)
19
+
20
+ def connect(self, server_url: str) -> None:
21
+ self.server_url = server_url
22
+ endpoint = f"{self.server_url}/services"
23
+ try:
24
+ response = requests.get(endpoint)
25
+ except requests.exceptions.RequestException as e:
26
+ raise ServerRequestError(endpoint, e)
27
+
28
+ if response.status_code == 200:
29
+ services = response.json().get("services")
30
+ self.algorithms = services
31
+ else:
32
+ raise AlgorithmServerError(response.status_code, response.json())
33
+
34
+ @property
35
+ def server_url(self) -> str:
36
+ return self._server_url
37
+
38
+ @server_url.setter
39
+ def server_url(self, server_url: str):
40
+ self._server_url = server_url
41
+
42
+ @property
43
+ def algorithms(self) -> Dict[str, str]:
44
+ return self._algorithms
45
+
46
+ @algorithms.setter
47
+ def algorithms(self, algorithms: Dict[str, str]):
48
+ self._algorithms = algorithms
49
+
50
+ def run_algorithm(self, algorithm=None, **algo_params) -> List[Tuple]:
51
+ algorithm = self._validate_algorithm(algorithm)
52
+ algo_params_encoded = self._encode_numpy_parameters(algo_params)
53
+
54
+ try:
55
+ endpoint = f"{self.server_url}/{algorithm}/"
56
+ response = requests.post(
57
+ endpoint,
58
+ json=algo_params_encoded,
59
+ headers={
60
+ "Content-Type": "application/json",
61
+ "accept": "application/json",
62
+ },
63
+ )
64
+ except requests.exceptions.RequestException as e:
65
+ raise ServerRequestError(endpoint, e)
66
+
67
+ if response.status_code == 201:
68
+ return serverkit.deserialize_result_tuple(response.json())
69
+ elif response.status_code == 422:
70
+ raise InvalidAlgorithmParametersError(response.status_code, response.json())
71
+ else:
72
+ raise AlgorithmServerError(response.status_code, response.json())
73
+
74
+ def get_algorithm_parameters(self, algorithm=None) -> Dict:
75
+ algorithm = self._validate_algorithm(algorithm)
76
+
77
+ endpoint = f"{self.server_url}/{algorithm}/parameters"
78
+
79
+ try:
80
+ response = requests.get(endpoint)
81
+ except requests.exceptions.RequestException as e:
82
+ raise ServerRequestError(endpoint, e)
83
+
84
+ if response.status_code == 200:
85
+ return response.json()
86
+ else:
87
+ raise AlgorithmServerError(response.status_code, response.json())
88
+
89
+ def get_sample_images(
90
+ self, algorithm=None, first_only: bool = False
91
+ ) -> List["np.ndarray"]:
92
+ algorithm = self._validate_algorithm(algorithm)
93
+
94
+ endpoint = f"{self.server_url}/{algorithm}/sample_images"
95
+
96
+ try:
97
+ response = requests.get(endpoint)
98
+ except requests.exceptions.RequestException as e:
99
+ raise ServerRequestError(endpoint, e)
100
+
101
+ if response.status_code == 200:
102
+ images = []
103
+ for content in response.json().get("sample_images"):
104
+ encoded_image = content.get("sample_image")
105
+ image = serverkit.decode_contents(encoded_image)
106
+ images.append(image)
107
+ if first_only:
108
+ return image
109
+ return images
110
+ else:
111
+ print(
112
+ f"Error status while attempting to get algorithm sample images: {response.status_code}"
113
+ )
114
+ return -1
115
+
116
+ def _validate_algorithm(self, algorithm=None) -> str:
117
+ if algorithm is None:
118
+ if len(self.algorithms) > 0:
119
+ algorithm = self.algorithms[0]
120
+ else:
121
+ raise AlgorithmNotFoundError(algorithm)
122
+ else:
123
+ if algorithm not in self.algorithms:
124
+ raise AlgorithmNotFoundError(algorithm)
125
+ return algorithm
126
+
127
+ def _encode_numpy_parameters(self, algo_params: dict) -> dict:
128
+ for param in algo_params:
129
+ if isinstance(algo_params[param], np.ndarray):
130
+ algo_params[param] = serverkit.encode_contents(algo_params[param])
131
+ return algo_params
@@ -0,0 +1,41 @@
1
+ class AlgorithmNotFoundError(Exception):
2
+ """Exception raised when a specified algorithm is not found."""
3
+
4
+ def __init__(self, algorithm_name, message="Algorithm not found"):
5
+ self.algorithm_name = algorithm_name
6
+ self.message = f"{message}: {algorithm_name}"
7
+ super().__init__(self.message)
8
+
9
+
10
+ class AlgorithmServerError(Exception):
11
+ """Exception raised when a request to an algorithm server returns an unexpected status code."""
12
+
13
+ def __init__(self, status_code, response_body, message="Algorithm server returned an unexpected status_code"):
14
+ self.status_code = status_code
15
+ self.response_body = response_body
16
+ self.message = f"{message}: {status_code}\nResponse body: {response_body}"
17
+ super().__init__(self.message)
18
+
19
+
20
+ class InvalidAlgorithmParametersError(Exception):
21
+ """Exception raised when a request to an algorithm server is made with invalid algorithm parameters."""
22
+
23
+ def __init__(self, status_code, response_body, message="Algorithm parameters were invalidated by the server"):
24
+ self.status_code = status_code
25
+ self.response_body = response_body
26
+ pydantic_details = response_body.get("detail")[0]
27
+ pydantic_validation_msg = pydantic_details.get("msg")
28
+ pydantic_failing_param = pydantic_details.get("loc")[1]
29
+ pydantic_failing_param_value = pydantic_details.get("input")
30
+ self.message = f"{message}: Parameter: {pydantic_failing_param}. {pydantic_validation_msg}. Received: {pydantic_failing_param_value}."
31
+ super().__init__(self.message)
32
+
33
+
34
+ class ServerRequestError(Exception):
35
+ """Exception raised when HTTP requests fail."""
36
+
37
+ def __init__(self, url: str, error: Exception, message="Request to server failed"):
38
+ self.url = url
39
+ self.error = error
40
+ self.message = f"{message} ({url=}): {error}"
41
+ super().__init__(self.message)