imaging-server-kit 0.0.2__tar.gz → 0.0.3__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 (43) hide show
  1. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/Dockerfile-registry +1 -1
  2. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/PKG-INFO +74 -44
  3. imaging_server_kit-0.0.3/README.md +121 -0
  4. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/pyproject.toml +1 -1
  5. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/__init__.py +6 -2
  6. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/_version.py +2 -2
  7. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/client.py +1 -1
  8. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/registry.py +2 -2
  9. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/server.py +15 -7
  10. imaging_server_kit-0.0.3/src/imaging_server_kit/static/index.css +47 -0
  11. imaging_server_kit-0.0.3/src/imaging_server_kit/static/info.css +124 -0
  12. imaging_server_kit-0.0.3/src/imaging_server_kit/templates/index.html +33 -0
  13. imaging_server_kit-0.0.3/src/imaging_server_kit/templates/info.html +78 -0
  14. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit.egg-info/PKG-INFO +74 -44
  15. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit.egg-info/SOURCES.txt +0 -1
  16. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit.egg-info/requires.txt +1 -1
  17. imaging_server_kit-0.0.2/README.md +0 -91
  18. imaging_server_kit-0.0.2/src/imaging_server_kit/autostartup_registry.py +0 -58
  19. imaging_server_kit-0.0.2/src/imaging_server_kit/static/index.css +0 -32
  20. imaging_server_kit-0.0.2/src/imaging_server_kit/static/info.css +0 -32
  21. imaging_server_kit-0.0.2/src/imaging_server_kit/templates/index.html +0 -25
  22. imaging_server_kit-0.0.2/src/imaging_server_kit/templates/info.html +0 -63
  23. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/.dockerignore +0 -0
  24. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/.gitattributes +0 -0
  25. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/.github/workflows/publish.yml +0 -0
  26. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/.gitignore +0 -0
  27. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/Dockerfile +0 -0
  28. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/Dockerfile-GPU +0 -0
  29. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/LICENSE +0 -0
  30. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/MANIFEST.in +0 -0
  31. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/build.sh +0 -0
  32. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/examples/.gitkeep +0 -0
  33. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/main.py +0 -0
  34. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/setup.cfg +0 -0
  35. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/assets/style.css +0 -0
  36. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/encoding.py +0 -0
  37. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/errors.py +0 -0
  38. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/geometry.py +0 -0
  39. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/serialization.py +0 -0
  40. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit/web_demo.py +0 -0
  41. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit.egg-info/dependency_links.txt +0 -0
  42. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/src/imaging_server_kit.egg-info/top_level.txt +0 -0
  43. {imaging_server_kit-0.0.2 → imaging_server_kit-0.0.3}/start_registry.py +0 -0
@@ -1,4 +1,4 @@
1
- FROM registry.rcp.epfl.ch/imaging-server-kit/imaging-server-kit:3.9
1
+ FROM mallorywittwerepfl/imaging-server-kit:3.9
2
2
 
3
3
  COPY start_registry.py .
4
4
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: imaging-server-kit
3
- Version: 0.0.2
3
+ Version: 0.0.3
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
@@ -54,92 +54,122 @@ Requires-Dist: tifffile
54
54
  Requires-Dist: numpy
55
55
  Requires-Dist: scikit-image
56
56
  Requires-Dist: geojson
57
- Requires-Dist: docker
58
57
  Requires-Dist: jinja2
59
58
  Requires-Dist: pyyaml
60
59
  Requires-Dist: dash
61
60
  Requires-Dist: dash-bootstrap-components
61
+ Requires-Dist: a2wsgi
62
62
  Requires-Dist: pytest
63
63
 
64
64
  ![EPFL Center for Imaging logo](https://imaging.epfl.ch/resources/logo-for-gitlab.svg)
65
65
  # 🪐 Imaging Server Kit
66
66
 
67
- Run seamlessly interoperable algorithms on images using FastAPI.
67
+ Deploy image processing algorithms in FastAPI servers and easily run them in Napari, QuPath, and more.
68
68
 
69
- ## Setup
70
-
71
- Build the `imaging-server-kit` image with `docker`:
69
+ ## Usage
72
70
 
73
- ```
74
- docker build -t imaging-server-kit:3.9 .
71
+ ### Algorithm servers
72
+
73
+ Set up and run a server with one, or multiple algorithms from the Imaging Server Kit collection (or your own):
74
+
75
+ - [StarDist](https://github.com/Imaging-Server-Kit/serverkit-stardist)
76
+ - [CellPose](https://github.com/Imaging-Server-Kit/serverkit-cellpose)
77
+ - [Spotiflow](https://github.com/Imaging-Server-Kit/serverkit-spotiflow)
78
+ - [Rembg](https://github.com/Imaging-Server-Kit/serverkit-rembg)
79
+ - [Scikit-image LoG detector](https://github.com/Imaging-Server-Kit/serverkit-skimage-LoG)
80
+ - [Orientationpy](https://github.com/Imaging-Server-Kit/serverkit-orientationpy)
81
+ <!-- - [Tau Fibrils Detector](https://github.com/Imaging-Server-Kit/serverkit-tau-fibrils-yolo) -->
82
+
83
+ To do that, the recommended way is to edit a `docker-compose.yml` file and pull server images from `registry.rcp.epfl.ch` (EPFL users only):
84
+
85
+ ```{docker-compose.yml}
86
+ services:
87
+ servers_registry:
88
+ image: mallorywittwerepfl/imaging-server-kit:registry
89
+ ports:
90
+ - "8000:8000"
91
+ rembg:
92
+ image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-rembg:latest
93
+ depends_on:
94
+ - servers_registry
95
+ skimage-log:
96
+ image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-skimage-log:latest
97
+ depends_on:
98
+ - servers_registry
75
99
  ```
76
100
 
77
- or build specific images:
101
+ Start the server with
78
102
 
79
103
  ```
80
- docker build -t imaging-server-kit:3.10 --file Dockerfile-3.10 .
81
- docker build -t imaging-server-kit:gpu --file Dockerfile-GPU .
104
+ docker compose up
82
105
  ```
83
106
 
84
- **Run an algorithm server**
107
+ The server should be accessible at http://localhost:8000.
85
108
 
86
- The server will be running on `http://localhost:8000`.
109
+ **Build the docker images yourself**
87
110
 
88
- ```bash
89
- docker build -t serverkit/rembg .
90
- docker run -it --rm -p 8000:8000 serverkit/rembg
91
- ```
111
+ To build the algorithm server images yourself (e.g. if you are not from EPFL), see [Reference Deployment](https://github.com/Imaging-Server-Kit/serverkit-deploy-docker).
92
112
 
93
- **Run an algorithm server with multiple algorithms**
113
+ **Create a server for your project**
94
114
 
95
- See [deployment](./reference_deployment/README.md). The server will be running on `http://localhost:7000`.
115
+ To learn how to create a server for your project, see [Algorithm Server Template](https://github.com/Imaging-Server-Kit/cookiecutter-serverkit).
96
116
 
97
- ## Usage
98
-
99
- **Python client**
117
+ ### Python client
100
118
 
101
- Install the `imaging-server-kit` package with `pip`:
102
-
103
- ```
104
- pip install git+https://gitlab.com/epfl-center-for-imaging/imaging-server-kit.git
105
- ```
106
-
107
- Connect to an algorithm server and run algorithms from Python:
119
+ Once the server is running, connect to it and run algorithms from Python:
108
120
 
109
121
  ```python
110
122
  from imaging_server_kit import Client
111
123
 
112
- client = Client()
113
-
114
- client.connect("http://localhost:7000")
124
+ client = Client("http://localhost:8000")
115
125
 
116
126
  print(client.algorithms)
117
- # [`rembg`, `stardist`, `sam2`]
127
+ # [`rembg`, `stardist`, `cellpose`]
118
128
 
119
- data_tuple = client.run_algorithm(
129
+ algo_output = client.run_algorithm(
120
130
  algorithm="rembg",
121
131
  image=(...),
122
132
  rembg_model_name="silueta",
123
133
  )
124
134
  ```
125
135
 
126
- More [examples](./examples/).
136
+ ### Napari plugin
137
+
138
+ Once the server is running, use the [Napari Server Kit](https://github.com/Imaging-Server-Kit/napari-serverkit) plugin to connect to it and run algorithms in Napari.
127
139
 
128
- **Napari client**
140
+ ### QuPath extension
129
141
 
130
- Coming soon.
142
+ Once the server is running, use the [QuPath Extension Server Kit](https://github.com/Imaging-Server-Kit/qupath-extension-serverkit) to connect to it and run algorithms from within QuPath.
131
143
 
132
- **Web client**
144
+ ## Install locally
133
145
 
134
- Coming soon.
146
+ Install the `imaging-server-kit` package with `pip`:
147
+
148
+ ```
149
+ pip install imaging-server-kit
150
+ ```
135
151
 
136
- **QuPath client**
152
+ or clone the project and install the development version:
153
+
154
+ ```
155
+ git clone https://github.com/Imaging-Server-Kit/imaging-server-kit.git
156
+ cd imaging-server-kit
157
+ pip install -e .
158
+ ```
137
159
 
138
- Coming soon.
160
+ ## Build with `docker`
139
161
 
140
- **Fiji plugin**
162
+ Build the `imaging-server-kit` docker image for a specific Python version:
141
163
 
142
- Coming soon.
164
+ ```
165
+ docker build --build-arg PYTHON_VERSION=3.9 -t imaging-server-kit:3.9 .
166
+ ```
167
+
168
+ Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the server kit registry:
169
+
170
+ ```
171
+ bash ./build.sh
172
+ ```
143
173
 
144
174
  ## Contributing
145
175
 
@@ -0,0 +1,121 @@
1
+ ![EPFL Center for Imaging logo](https://imaging.epfl.ch/resources/logo-for-gitlab.svg)
2
+ # 🪐 Imaging Server Kit
3
+
4
+ Deploy image processing algorithms in FastAPI servers and easily run them in Napari, QuPath, and more.
5
+
6
+ ## Usage
7
+
8
+ ### Algorithm servers
9
+
10
+ Set up and run a server with one, or multiple algorithms from the Imaging Server Kit collection (or your own):
11
+
12
+ - [StarDist](https://github.com/Imaging-Server-Kit/serverkit-stardist)
13
+ - [CellPose](https://github.com/Imaging-Server-Kit/serverkit-cellpose)
14
+ - [Spotiflow](https://github.com/Imaging-Server-Kit/serverkit-spotiflow)
15
+ - [Rembg](https://github.com/Imaging-Server-Kit/serverkit-rembg)
16
+ - [Scikit-image LoG detector](https://github.com/Imaging-Server-Kit/serverkit-skimage-LoG)
17
+ - [Orientationpy](https://github.com/Imaging-Server-Kit/serverkit-orientationpy)
18
+ <!-- - [Tau Fibrils Detector](https://github.com/Imaging-Server-Kit/serverkit-tau-fibrils-yolo) -->
19
+
20
+ To do that, the recommended way is to edit a `docker-compose.yml` file and pull server images from `registry.rcp.epfl.ch` (EPFL users only):
21
+
22
+ ```{docker-compose.yml}
23
+ services:
24
+ servers_registry:
25
+ image: mallorywittwerepfl/imaging-server-kit:registry
26
+ ports:
27
+ - "8000:8000"
28
+ rembg:
29
+ image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-rembg:latest
30
+ depends_on:
31
+ - servers_registry
32
+ skimage-log:
33
+ image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-skimage-log:latest
34
+ depends_on:
35
+ - servers_registry
36
+ ```
37
+
38
+ Start the server with
39
+
40
+ ```
41
+ docker compose up
42
+ ```
43
+
44
+ The server should be accessible at http://localhost:8000.
45
+
46
+ **Build the docker images yourself**
47
+
48
+ To build the algorithm server images yourself (e.g. if you are not from EPFL), see [Reference Deployment](https://github.com/Imaging-Server-Kit/serverkit-deploy-docker).
49
+
50
+ **Create a server for your project**
51
+
52
+ To learn how to create a server for your project, see [Algorithm Server Template](https://github.com/Imaging-Server-Kit/cookiecutter-serverkit).
53
+
54
+ ### Python client
55
+
56
+ Once the server is running, connect to it and run algorithms from Python:
57
+
58
+ ```python
59
+ from imaging_server_kit import Client
60
+
61
+ client = Client("http://localhost:8000")
62
+
63
+ print(client.algorithms)
64
+ # [`rembg`, `stardist`, `cellpose`]
65
+
66
+ algo_output = client.run_algorithm(
67
+ algorithm="rembg",
68
+ image=(...),
69
+ rembg_model_name="silueta",
70
+ )
71
+ ```
72
+
73
+ ### Napari plugin
74
+
75
+ Once the server is running, use the [Napari Server Kit](https://github.com/Imaging-Server-Kit/napari-serverkit) plugin to connect to it and run algorithms in Napari.
76
+
77
+ ### QuPath extension
78
+
79
+ Once the server is running, use the [QuPath Extension Server Kit](https://github.com/Imaging-Server-Kit/qupath-extension-serverkit) to connect to it and run algorithms from within QuPath.
80
+
81
+ ## Install locally
82
+
83
+ Install the `imaging-server-kit` package with `pip`:
84
+
85
+ ```
86
+ pip install imaging-server-kit
87
+ ```
88
+
89
+ or clone the project and install the development version:
90
+
91
+ ```
92
+ git clone https://github.com/Imaging-Server-Kit/imaging-server-kit.git
93
+ cd imaging-server-kit
94
+ pip install -e .
95
+ ```
96
+
97
+ ## Build with `docker`
98
+
99
+ Build the `imaging-server-kit` docker image for a specific Python version:
100
+
101
+ ```
102
+ docker build --build-arg PYTHON_VERSION=3.9 -t imaging-server-kit:3.9 .
103
+ ```
104
+
105
+ Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the server kit registry:
106
+
107
+ ```
108
+ bash ./build.sh
109
+ ```
110
+
111
+ ## Contributing
112
+
113
+ Contributions are very welcome.
114
+
115
+ ## License
116
+
117
+ This software is distributed under the terms of the [BSD-3](http://opensource.org/licenses/BSD-3-Clause) license.
118
+
119
+ ## Issues
120
+
121
+ If you encounter any problems, please file an issue along with a detailed description.
@@ -29,11 +29,11 @@ dependencies = [
29
29
  "numpy",
30
30
  "scikit-image",
31
31
  "geojson",
32
- "docker", # For the autostartup regsitry
33
32
  "jinja2", # For the /info route
34
33
  "pyyaml", # For the /info route
35
34
  "dash", # For the dash app
36
35
  "dash-bootstrap-components", # For the dash app
36
+ "a2wsgi", # For the dash app
37
37
  "pytest", # For testing
38
38
  ]
39
39
 
@@ -1,8 +1,12 @@
1
- from imaging_server_kit._version import __version__
1
+ from ._version import version as __version__
2
+ try:
3
+ from ._version import version as __version__
4
+ except ImportError:
5
+ __version__ = "unknown"
6
+
2
7
  from imaging_server_kit.client import Client
3
8
  from imaging_server_kit.server import Server, Parameters
4
9
  from imaging_server_kit.registry import Registry
5
- from imaging_server_kit.autostartup_registry import AutoStartupRegistry
6
10
  from imaging_server_kit.encoding import encode_contents, decode_contents
7
11
  from imaging_server_kit.serialization import (
8
12
  serialize_result_tuple,
@@ -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.2'
16
- __version_tuple__ = version_tuple = (0, 0, 2)
15
+ __version__ = version = '0.0.3'
16
+ __version_tuple__ = version_tuple = (0, 0, 3)
@@ -52,7 +52,7 @@ class Client:
52
52
  algo_params_encoded = self._encode_numpy_parameters(algo_params)
53
53
 
54
54
  try:
55
- endpoint = f"{self.server_url}/{algorithm}/"
55
+ endpoint = f"{self.server_url}/{algorithm}/process"
56
56
  response = requests.post(
57
57
  endpoint,
58
58
  json=algo_params_encoded,
@@ -80,11 +80,11 @@ class Registry:
80
80
  response = requests.get(f"{algo_url}/")
81
81
  return response.json()
82
82
 
83
- @self.app.post("/{algorithm}", status_code=status.HTTP_201_CREATED)
83
+ @self.app.post("/{algorithm}/process", status_code=status.HTTP_201_CREATED)
84
84
  async def run_algorithm(algorithm, request: Request):
85
85
  algo_url = self.services.get(algorithm)
86
86
  data = await request.json()
87
- response = requests.post(f"{algo_url}/{algorithm}/", json=data)
87
+ response = requests.post(f"{algo_url}/{algorithm}/process", json=data)
88
88
  return response.json()
89
89
 
90
90
  @self.app.get("/{algorithm}/parameters")
@@ -11,7 +11,9 @@ from fastapi.staticfiles import StaticFiles
11
11
  from fastapi.templating import Jinja2Templates
12
12
  from imaging_server_kit.web_demo import generate_dash_app
13
13
  from pydantic import BaseModel, ConfigDict
14
- from starlette.middleware.wsgi import WSGIMiddleware
14
+ from a2wsgi import WSGIMiddleware
15
+ # from starlette.middleware.wsgi import WSGIMiddleware
16
+ from contextlib import asynccontextmanager
15
17
 
16
18
  templates_dir = importlib.resources.files("imaging_server_kit").joinpath("templates")
17
19
  static_dir = importlib.resources.files("imaging_server_kit").joinpath("static")
@@ -47,7 +49,7 @@ class Server:
47
49
  self.algorithm_name = algorithm_name
48
50
  self.parameters_model = parameters_model
49
51
 
50
- self.app = FastAPI(title=algorithm_name)
52
+ self.app = FastAPI(title=algorithm_name, lifespan=self.lifespan)
51
53
  self.app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
52
54
 
53
55
  # Set up the web demo app
@@ -63,14 +65,20 @@ class Server:
63
65
 
64
66
  self.app.mount(f"/{algorithm_name}/demo", WSGIMiddleware(dash_app.server))
65
67
 
66
- self.app.on_event("startup")(self.register_with_registry)
67
- self.app.on_event("shutdown")(self.deregister_from_registry)
68
+ # self.app.on_event("startup")(self.register_with_registry)
69
+ # self.app.on_event("shutdown")(self.deregister_from_registry)
68
70
 
69
71
  self.register_routes()
70
72
 
71
73
  self.services = [self.algorithm_name]
72
74
 
73
- def register_with_registry(self):
75
+ @asynccontextmanager
76
+ async def lifespan(self, app: FastAPI):
77
+ await self.register_with_registry()
78
+ yield
79
+ await self.deregister_from_registry()
80
+
81
+ async def register_with_registry(self):
74
82
  try:
75
83
  response = requests.get(f"{REGISTRY_URL}/")
76
84
  except Exception:
@@ -89,7 +97,7 @@ class Server:
89
97
  else:
90
98
  print(f"Failed to register {self.algorithm_name}: {response.json()}")
91
99
 
92
- def deregister_from_registry(self):
100
+ async def deregister_from_registry(self):
93
101
  deregister_url = f"{REGISTRY_URL}/deregister"
94
102
  response = requests.post(deregister_url, json={"name": self.algorithm_name})
95
103
  if response.status_code == 201:
@@ -124,7 +132,7 @@ class Server:
124
132
  },
125
133
  )
126
134
 
127
- @self.app.post(f"/{self.algorithm_name}/", status_code=status.HTTP_201_CREATED)
135
+ @self.app.post(f"/{self.algorithm_name}/process", status_code=status.HTTP_201_CREATED)
128
136
  async def run_algo(algo_params: self.parameters_model):
129
137
  result_data_tuple = self.run_algorithm(**algo_params.dict())
130
138
  serialized_results = serverkit.serialize_result_tuple(result_data_tuple)
@@ -0,0 +1,47 @@
1
+ body {
2
+ font-family: Arial, sans-serif;
3
+ margin: 0;
4
+ padding: 0;
5
+ background-color: #f4f4f4;
6
+ color: #333;
7
+ }
8
+ .container {
9
+ max-width: 800px;
10
+ margin: 50px auto;
11
+ padding: 20px;
12
+ background-color: #fff;
13
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
14
+ border-radius: 8px;
15
+ }
16
+ p {
17
+ line-height: 1.6;
18
+ }
19
+ a {
20
+ color: #333;
21
+ text-decoration: none;
22
+ }
23
+
24
+ .card-product {
25
+ border-radius: 5px;
26
+ overflow: hidden;
27
+ /* height: 80px; */
28
+ background: white;
29
+ /* box-shadow: 0 0 15px rgba(0,0,0,0.2); */
30
+ display: flex;
31
+ align-items: center;
32
+ }
33
+
34
+ .card-product h2 {
35
+ font-size: 16px;
36
+ font-weight: bold;
37
+ margin: 0;
38
+ }
39
+
40
+ .card-product .card-product-infos {
41
+ padding: 16px;
42
+ }
43
+
44
+ .card-product:hover {
45
+ background-color: #f4f4f4;
46
+ }
47
+
@@ -0,0 +1,124 @@
1
+ body {
2
+ font-family: Arial, sans-serif;
3
+ margin: 0;
4
+ padding: 0;
5
+ background-color: #f4f4f4;
6
+ color: #333;
7
+ }
8
+
9
+ .container {
10
+ max-width: 800px;
11
+ margin: 50px auto;
12
+ padding: 20px;
13
+ background-color: #fff;
14
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
15
+ border-radius: 8px;
16
+ }
17
+
18
+ p {
19
+ line-height: 1.6;
20
+ }
21
+
22
+ a {
23
+ color: #333;
24
+ text-decoration: none;
25
+ }
26
+
27
+ a:hover {
28
+ background-color: #f4f4f4;
29
+ border-radius: 5px;
30
+ }
31
+
32
+ .keyword {
33
+ border-radius: 2px;
34
+ background-color: #f4f4f4;
35
+ padding: 2px 4px;
36
+ }
37
+
38
+ .card-category {
39
+ flex-direction: column;
40
+ display: flex;
41
+ justify-content: center;
42
+ align-items: center;
43
+ font-size: 18px;
44
+ border-radius: 5px;
45
+ }
46
+
47
+ .card-category i {
48
+ font-size: 36px;
49
+ padding-top: 20px;
50
+ }
51
+
52
+ .cards {
53
+ display: grid;
54
+ grid-template-columns: 1fr 1fr 1fr;
55
+ grid-gap: 16px;
56
+ }
57
+
58
+ @media (min-width: 100px) and (max-width: 575px) {
59
+ .cards {
60
+ grid-template-columns: 1fr;
61
+ }
62
+ }
63
+
64
+ @media (min-width: 576px) {
65
+ .cards {
66
+ grid-template-columns: 1fr;
67
+ }
68
+ }
69
+
70
+ @media (min-width: 768px) {
71
+ .cards {
72
+ grid-template-columns: 1fr 1fr;
73
+ }
74
+ }
75
+
76
+ @media (min-width: 992px) {
77
+ .cards {
78
+ grid-template-columns: 1fr 1fr 1fr;
79
+ }
80
+ }
81
+
82
+ @media (min-width: 1200px) {
83
+ .cards {
84
+ grid-template-columns: 1fr 1fr 1fr;
85
+ }
86
+ }
87
+
88
+ table {
89
+ width: 100%; /* Make the table span the full width of its container */
90
+ border-collapse: collapse; /* Remove gaps between table cells */
91
+ margin: 1rem 0; /* Add some spacing above and below the table */
92
+ font-size: 0.9rem; /* Slightly smaller font size for a subtle look */
93
+ color: #333; /* Dark gray text for readability */
94
+ }
95
+
96
+ thead {
97
+ color: #333; /* Ensure the text color contrasts well */
98
+ font-weight: bold; /* Bold text for the header */
99
+ text-align: left; /* Align text to the left for clarity */
100
+ }
101
+
102
+ thead th {
103
+ padding: 0.8rem; /* Add padding for better spacing */
104
+ border-bottom: 1px solid #ddd; /* Subtle bottom border */
105
+ }
106
+
107
+ tbody tr {
108
+ border-bottom: 1px solid #ddd; /* Add borders between rows */
109
+ }
110
+
111
+ tbody td {
112
+ padding: 0.8rem; /* Add padding for cell spacing */
113
+ vertical-align: top; /* Align content to the top for readability */
114
+ }
115
+
116
+ tbody td:first-child {
117
+ font-weight: bold; /* Emphasize the first column */
118
+ color: #555; /* Slightly darker text for the first column */
119
+ }
120
+
121
+ tbody td:last-child {
122
+ font-style: italic; /* Italicize the last column for variation */
123
+ color: #666; /* Subtle gray for less prominent data */
124
+ }
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
8
+ integrity="sha384-rbsA2VBKQat6jboKpBhxGzJLysoH5Wr1xEAWmupmCJA7D6hzBI7tWtK2p9jxX0/e" crossorigin="anonymous">
9
+ <link rel="stylesheet" href="/static/index.css">
10
+ </head>
11
+
12
+ <body>
13
+ <div class="container">
14
+ <h1>Running servers</h1>
15
+ {% for service in services %}
16
+ <a href="/{{ service }}/info">
17
+ <div class="card-product">
18
+ <div class="card-product-infos">
19
+ <h2>{{ service }}</h2>
20
+ </div>
21
+ </div>
22
+ </a>
23
+ {% endfor %}
24
+ </div>
25
+ <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
26
+ integrity="sha384-QFfYf8qpCIem7U+Uz5XYW6XPiA5sdP7ePLCTDBSMF0Os3J8nt9wM5L3yZGOI+73s"
27
+ crossorigin="anonymous"></script>
28
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
29
+ integrity="sha384-Q8KdjMRRhHpXEmsdpu2x9NkRBe/HHU6hLmjNw07uGpD7jz1zPoj2a9t8WbQhAjc"
30
+ crossorigin="anonymous"></script>
31
+ </body>
32
+
33
+ </html>
@@ -0,0 +1,78 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
8
+ integrity="sha384-rbsA2VBKQat6jboKpBhxGzJLysoH5Wr1xEAWmupmCJA7D6hzBI7tWtK2p9jxX0/e" crossorigin="anonymous">
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
10
+ <link rel="stylesheet" href="/static/info.css">
11
+ </head>
12
+
13
+ <body>
14
+ <div class="container">
15
+ <h1>{{ algo_info.project_name }}</h1>
16
+
17
+ <p>{{ algo_info.description }}</p>
18
+
19
+ <p><strong>Keywords:</strong>
20
+ {% for used_for_tag in algo_info.used_for %}
21
+ <span class="keyword">{{ used_for_tag }}</span>
22
+ {% endfor %}
23
+ {% for tag in algo_info.tags %}
24
+ <span class="keyword">{{ tag }}</span>
25
+ {% endfor %}
26
+ </p>
27
+
28
+ <div class="cards">
29
+ <a href="{{ algo_info.project_url }}">
30
+ <div class="card-category">
31
+ <i class="fa-solid fa-house"></i>
32
+ <p>Project Homepage</p>
33
+ </div>
34
+ </a>
35
+ <a href="{{ algo_info.serverkit_repo_url }}">
36
+ <div class="card-category">
37
+ <i class="fa-brands fa-github"></i>
38
+ <p>Server Kit Repository</p>
39
+ </div>
40
+ </a>
41
+ <a href="/demo">
42
+ <div class="card-category">
43
+ <i class="fa-solid fa-rocket"></i>
44
+ <p>Web demo</p>
45
+ </div>
46
+ </a>
47
+ </div>
48
+
49
+ <!-- <h3><strong>Parameters</strong></h3> -->
50
+ <table>
51
+ <thead>
52
+ <tr>
53
+ <th>Parameter</th>
54
+ <th></th>
55
+ <th>Default</th>
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ {% for key, val in algo_params.items() %}
60
+ <tr>
61
+ <td>{{ key }}</td>
62
+ <td>{{ val.get("description") }}</td>
63
+ <td>{{ val.get("default") }}</td>
64
+ </tr>
65
+ {% endfor %}
66
+ </tbody>
67
+ </table>
68
+ </div>
69
+ <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
70
+ integrity="sha384-QFfYf8qpCIem7U+Uz5XYW6XPiA5sdP7ePLCTDBSMF0Os3J8nt9wM5L3yZGOI+73s"
71
+ crossorigin="anonymous"></script>
72
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
73
+ integrity="sha384-Q8KdjMRRhHpXEmsdpu2x9NkRBe/HHU6hLmjNw07uGpD7jz1zPoj2a9t8WbQhAjc"
74
+ crossorigin="anonymous"></script>
75
+
76
+ </body>
77
+
78
+ </html>
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: imaging-server-kit
3
- Version: 0.0.2
3
+ Version: 0.0.3
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
@@ -54,92 +54,122 @@ Requires-Dist: tifffile
54
54
  Requires-Dist: numpy
55
55
  Requires-Dist: scikit-image
56
56
  Requires-Dist: geojson
57
- Requires-Dist: docker
58
57
  Requires-Dist: jinja2
59
58
  Requires-Dist: pyyaml
60
59
  Requires-Dist: dash
61
60
  Requires-Dist: dash-bootstrap-components
61
+ Requires-Dist: a2wsgi
62
62
  Requires-Dist: pytest
63
63
 
64
64
  ![EPFL Center for Imaging logo](https://imaging.epfl.ch/resources/logo-for-gitlab.svg)
65
65
  # 🪐 Imaging Server Kit
66
66
 
67
- Run seamlessly interoperable algorithms on images using FastAPI.
67
+ Deploy image processing algorithms in FastAPI servers and easily run them in Napari, QuPath, and more.
68
68
 
69
- ## Setup
70
-
71
- Build the `imaging-server-kit` image with `docker`:
69
+ ## Usage
72
70
 
73
- ```
74
- docker build -t imaging-server-kit:3.9 .
71
+ ### Algorithm servers
72
+
73
+ Set up and run a server with one, or multiple algorithms from the Imaging Server Kit collection (or your own):
74
+
75
+ - [StarDist](https://github.com/Imaging-Server-Kit/serverkit-stardist)
76
+ - [CellPose](https://github.com/Imaging-Server-Kit/serverkit-cellpose)
77
+ - [Spotiflow](https://github.com/Imaging-Server-Kit/serverkit-spotiflow)
78
+ - [Rembg](https://github.com/Imaging-Server-Kit/serverkit-rembg)
79
+ - [Scikit-image LoG detector](https://github.com/Imaging-Server-Kit/serverkit-skimage-LoG)
80
+ - [Orientationpy](https://github.com/Imaging-Server-Kit/serverkit-orientationpy)
81
+ <!-- - [Tau Fibrils Detector](https://github.com/Imaging-Server-Kit/serverkit-tau-fibrils-yolo) -->
82
+
83
+ To do that, the recommended way is to edit a `docker-compose.yml` file and pull server images from `registry.rcp.epfl.ch` (EPFL users only):
84
+
85
+ ```{docker-compose.yml}
86
+ services:
87
+ servers_registry:
88
+ image: mallorywittwerepfl/imaging-server-kit:registry
89
+ ports:
90
+ - "8000:8000"
91
+ rembg:
92
+ image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-rembg:latest
93
+ depends_on:
94
+ - servers_registry
95
+ skimage-log:
96
+ image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-skimage-log:latest
97
+ depends_on:
98
+ - servers_registry
75
99
  ```
76
100
 
77
- or build specific images:
101
+ Start the server with
78
102
 
79
103
  ```
80
- docker build -t imaging-server-kit:3.10 --file Dockerfile-3.10 .
81
- docker build -t imaging-server-kit:gpu --file Dockerfile-GPU .
104
+ docker compose up
82
105
  ```
83
106
 
84
- **Run an algorithm server**
107
+ The server should be accessible at http://localhost:8000.
85
108
 
86
- The server will be running on `http://localhost:8000`.
109
+ **Build the docker images yourself**
87
110
 
88
- ```bash
89
- docker build -t serverkit/rembg .
90
- docker run -it --rm -p 8000:8000 serverkit/rembg
91
- ```
111
+ To build the algorithm server images yourself (e.g. if you are not from EPFL), see [Reference Deployment](https://github.com/Imaging-Server-Kit/serverkit-deploy-docker).
92
112
 
93
- **Run an algorithm server with multiple algorithms**
113
+ **Create a server for your project**
94
114
 
95
- See [deployment](./reference_deployment/README.md). The server will be running on `http://localhost:7000`.
115
+ To learn how to create a server for your project, see [Algorithm Server Template](https://github.com/Imaging-Server-Kit/cookiecutter-serverkit).
96
116
 
97
- ## Usage
98
-
99
- **Python client**
117
+ ### Python client
100
118
 
101
- Install the `imaging-server-kit` package with `pip`:
102
-
103
- ```
104
- pip install git+https://gitlab.com/epfl-center-for-imaging/imaging-server-kit.git
105
- ```
106
-
107
- Connect to an algorithm server and run algorithms from Python:
119
+ Once the server is running, connect to it and run algorithms from Python:
108
120
 
109
121
  ```python
110
122
  from imaging_server_kit import Client
111
123
 
112
- client = Client()
113
-
114
- client.connect("http://localhost:7000")
124
+ client = Client("http://localhost:8000")
115
125
 
116
126
  print(client.algorithms)
117
- # [`rembg`, `stardist`, `sam2`]
127
+ # [`rembg`, `stardist`, `cellpose`]
118
128
 
119
- data_tuple = client.run_algorithm(
129
+ algo_output = client.run_algorithm(
120
130
  algorithm="rembg",
121
131
  image=(...),
122
132
  rembg_model_name="silueta",
123
133
  )
124
134
  ```
125
135
 
126
- More [examples](./examples/).
136
+ ### Napari plugin
137
+
138
+ Once the server is running, use the [Napari Server Kit](https://github.com/Imaging-Server-Kit/napari-serverkit) plugin to connect to it and run algorithms in Napari.
127
139
 
128
- **Napari client**
140
+ ### QuPath extension
129
141
 
130
- Coming soon.
142
+ Once the server is running, use the [QuPath Extension Server Kit](https://github.com/Imaging-Server-Kit/qupath-extension-serverkit) to connect to it and run algorithms from within QuPath.
131
143
 
132
- **Web client**
144
+ ## Install locally
133
145
 
134
- Coming soon.
146
+ Install the `imaging-server-kit` package with `pip`:
147
+
148
+ ```
149
+ pip install imaging-server-kit
150
+ ```
135
151
 
136
- **QuPath client**
152
+ or clone the project and install the development version:
153
+
154
+ ```
155
+ git clone https://github.com/Imaging-Server-Kit/imaging-server-kit.git
156
+ cd imaging-server-kit
157
+ pip install -e .
158
+ ```
137
159
 
138
- Coming soon.
160
+ ## Build with `docker`
139
161
 
140
- **Fiji plugin**
162
+ Build the `imaging-server-kit` docker image for a specific Python version:
141
163
 
142
- Coming soon.
164
+ ```
165
+ docker build --build-arg PYTHON_VERSION=3.9 -t imaging-server-kit:3.9 .
166
+ ```
167
+
168
+ Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the server kit registry:
169
+
170
+ ```
171
+ bash ./build.sh
172
+ ```
143
173
 
144
174
  ## Contributing
145
175
 
@@ -15,7 +15,6 @@ start_registry.py
15
15
  examples/.gitkeep
16
16
  src/imaging_server_kit/__init__.py
17
17
  src/imaging_server_kit/_version.py
18
- src/imaging_server_kit/autostartup_registry.py
19
18
  src/imaging_server_kit/client.py
20
19
  src/imaging_server_kit/encoding.py
21
20
  src/imaging_server_kit/errors.py
@@ -5,9 +5,9 @@ tifffile
5
5
  numpy
6
6
  scikit-image
7
7
  geojson
8
- docker
9
8
  jinja2
10
9
  pyyaml
11
10
  dash
12
11
  dash-bootstrap-components
12
+ a2wsgi
13
13
  pytest
@@ -1,91 +0,0 @@
1
- ![EPFL Center for Imaging logo](https://imaging.epfl.ch/resources/logo-for-gitlab.svg)
2
- # 🪐 Imaging Server Kit
3
-
4
- Run seamlessly interoperable algorithms on images using FastAPI.
5
-
6
- ## Setup
7
-
8
- Build the `imaging-server-kit` image with `docker`:
9
-
10
- ```
11
- docker build -t imaging-server-kit:3.9 .
12
- ```
13
-
14
- or build specific images:
15
-
16
- ```
17
- docker build -t imaging-server-kit:3.10 --file Dockerfile-3.10 .
18
- docker build -t imaging-server-kit:gpu --file Dockerfile-GPU .
19
- ```
20
-
21
- **Run an algorithm server**
22
-
23
- The server will be running on `http://localhost:8000`.
24
-
25
- ```bash
26
- docker build -t serverkit/rembg .
27
- docker run -it --rm -p 8000:8000 serverkit/rembg
28
- ```
29
-
30
- **Run an algorithm server with multiple algorithms**
31
-
32
- See [deployment](./reference_deployment/README.md). The server will be running on `http://localhost:7000`.
33
-
34
- ## Usage
35
-
36
- **Python client**
37
-
38
- Install the `imaging-server-kit` package with `pip`:
39
-
40
- ```
41
- pip install git+https://gitlab.com/epfl-center-for-imaging/imaging-server-kit.git
42
- ```
43
-
44
- Connect to an algorithm server and run algorithms from Python:
45
-
46
- ```python
47
- from imaging_server_kit import Client
48
-
49
- client = Client()
50
-
51
- client.connect("http://localhost:7000")
52
-
53
- print(client.algorithms)
54
- # [`rembg`, `stardist`, `sam2`]
55
-
56
- data_tuple = client.run_algorithm(
57
- algorithm="rembg",
58
- image=(...),
59
- rembg_model_name="silueta",
60
- )
61
- ```
62
-
63
- More [examples](./examples/).
64
-
65
- **Napari client**
66
-
67
- Coming soon.
68
-
69
- **Web client**
70
-
71
- Coming soon.
72
-
73
- **QuPath client**
74
-
75
- Coming soon.
76
-
77
- **Fiji plugin**
78
-
79
- Coming soon.
80
-
81
- ## Contributing
82
-
83
- Contributions are very welcome.
84
-
85
- ## License
86
-
87
- This software is distributed under the terms of the [BSD-3](http://opensource.org/licenses/BSD-3-Clause) license.
88
-
89
- ## Issues
90
-
91
- If you encounter any problems, please file an issue along with a detailed description.
@@ -1,58 +0,0 @@
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)
@@ -1,32 +0,0 @@
1
- body {
2
- font-family: Arial, sans-serif;
3
- margin: 0;
4
- padding: 0;
5
- background-color: #f4f4f4;
6
- color: #333;
7
- }
8
- .container {
9
- max-width: 800px;
10
- margin: 50px auto;
11
- padding: 20px;
12
- background-color: #fff;
13
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
14
- border-radius: 8px;
15
- }
16
- h1 {
17
- color: #007BFF;
18
- }
19
- p {
20
- line-height: 1.6;
21
- }
22
- a {
23
- color: #007BFF;
24
- text-decoration: none;
25
- }
26
- a:hover {
27
- text-decoration: underline;
28
- }
29
- ul {
30
- list-style-type: disc;
31
- padding-left: 20px;
32
- }
@@ -1,32 +0,0 @@
1
- body {
2
- font-family: Arial, sans-serif;
3
- margin: 0;
4
- padding: 0;
5
- background-color: #f4f4f4;
6
- color: #333;
7
- }
8
- .container {
9
- max-width: 800px;
10
- margin: 50px auto;
11
- padding: 20px;
12
- background-color: #fff;
13
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
14
- border-radius: 8px;
15
- }
16
- h1 {
17
- color: #007BFF;
18
- }
19
- p {
20
- line-height: 1.6;
21
- }
22
- a {
23
- color: #007BFF;
24
- text-decoration: none;
25
- }
26
- a:hover {
27
- text-decoration: underline;
28
- }
29
- ul {
30
- list-style-type: disc;
31
- padding-left: 20px;
32
- }
@@ -1,25 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <link rel="stylesheet" href="/static/index.css">
8
- </head>
9
-
10
- <body>
11
- <div class="container">
12
- <h1>Running servers</h1>
13
-
14
- <ul>
15
- {% for service in services %}
16
- <li>
17
- <a href="/{{ service }}/info">{{ service }}</a>
18
- </li>
19
- {% endfor %}
20
- </ul>
21
-
22
- </div>
23
- </body>
24
-
25
- </html>
@@ -1,63 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <link rel="stylesheet" href="/static/info.css">
8
- </head>
9
-
10
- <body>
11
- <div class="container">
12
- <h1>{{ algo_info.project_name }}</h1>
13
- <p>Package name: {{ algo_info.package_name }}</p>
14
- <p>Serverkit project author: {{ algo_info.serverkit_author }}</p>
15
- <p>Description: {{ algo_info.description }}</p>
16
- <p><a href="{{ algo_info.project_url }}">Original Project</a></p>
17
- <p><a href="{{ algo_info.serverkit_repo_url }}">ServerKit Repository</a></p>
18
- <p><a href="/demo">Web demo</a></p>
19
- <p>Used for:
20
- <ul>
21
- {% for used_for_tag in algo_info.used_for %}
22
- <li>{{ used_for_tag }}</li>
23
- {% endfor %}
24
- </ul>
25
- </p>
26
- <p>Tags:
27
- <ul>
28
- {% for tag in algo_info.tags %}
29
- <li>{{ tag }}</li>
30
- {% endfor %}
31
- </ul>
32
- </p>
33
- <p>Algorithm parameters:</p>
34
- <table>
35
- <thead>
36
- <tr>
37
- <th>Parameter</th>
38
- <th>Title</th>
39
- <th>Description</th>
40
- <th>Type</th>
41
- <th>Widget Type</th>
42
- <th>Default</th>
43
- <th>Required</th>
44
- </tr>
45
- </thead>
46
- <tbody>
47
- {% for key, val in algo_params.items() %}
48
- <tr>
49
- <td>{{ key }}</td>
50
- <td>{{ val.get("title") }}</td>
51
- <td>{{ val.get("description") }}</td>
52
- <td>{{ val.get("type") }}</td>
53
- <td>{{ val.get("widget_type") }}</td>
54
- <td>{{ val.get("default") }}</td>
55
- <td>{{ val.get("required") }}</td>
56
- </tr>
57
- {% endfor %}
58
- </tbody>
59
- </table>
60
- </div>
61
- </body>
62
-
63
- </html>