Skip to content

Commit f6c0712

Browse files
John-Pshaneahmedghadjigeorghiou
authored
NEW: Add Simple Zoomify Viewer For WSIReader (#212)
Adds a simple Zoomify viewer class. Key Features ======== - Take a dictionary of `WSIReader` objects - Display then as layers in the web browser (`localhost:5000`) by serving Zoomify tiles - Togglable layers - Adjustable opacity - Layer re-ordering - Mini-map - Fullscreen - Rotation (shift + alt) with reset button - Mouse coordinates in baseline pixels (bottom right) - Scale bar in microns - Works with JP2 images but can be very slow and a little glitchy sometimes. To Do ==== - [x] Rename class / module? - [x] Unit tests Dependencies ========= - OpenLayers (BSD) and ol-ext (BSD-like, French version) Javascript for the frontend. - Flask (BSD) for the server. Demo ==== https://user-images.githubusercontent.com/4615004/144947393-aa492644-6012-48d9-b73b-5109e67393bd.mp4 Update: Added Graticule & Screen Space Graticule (Grids) ------------------------------------------------------------------------------ https://user-images.githubusercontent.com/4615004/145240617-57f21e9a-faa8-4f9e-896b-b89c41c0df0b.mp4 Demo Code ------------- ```python from tiatoolbox.wsiscore.wsireader import WSIReader from tiatoolbox.visualization.tileserver import TileServer wsi = WSIReader.open("/home/john/Downloads/CMU-1.svs") wsi2 = WSIReader.open("/home/john/Downloads/JP2K-33003-1.svs") app = TileServer( title="Testing TileServer", layers={ "JPEG SVS": wsi, "JP2 SVS": wsi2, }, ) app.run() ``` # Future Extensions - Allow annotation store as layer to display vector polygons / rendered as tiles - IPython support for embedding in notebooks - Make the layers UI a bit prettier Co-authored-by: John Pocock <[email protected]> Co-authored-by: Shan E Ahmed Raza <[email protected]> Co-authored-by: George Hadjigeorgiou <[email protected]>
1 parent 263b55f commit f6c0712

30 files changed

+10058
-0
lines changed

requirements.conda.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies:
99
- Click
1010
- cython
1111
- defusedxml
12+
- flask
1213
- glymur
1314
- h5py
1415
- imagecodecs

requirements.dev.conda.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies:
1212
- defusedxml
1313
- flake8
1414
- flake8-bugbear
15+
- flask
1516
- glymur
1617
- h5py
1718
- imagecodecs

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
albumentations>0.5.0
22
Click>=7.0
33
defusedxml
4+
flask
45
glymur
56
imagecodecs
67
jupyterlab

requirements.win64.conda.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies:
99
- Click
1010
- cython
1111
- defusedxml
12+
- flask
1213
- glymur
1314
- h5py
1415
- imagecodecs

requirements_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Click>=7.0
55
coverage==5.1
66
flake8-bugbear
77
flake8==3.7.8
8+
flask
89
glymur
910
imagecodecs
1011
jupyterlab

tests/test_tileserver.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Tests for tileserver."""
2+
import pytest
3+
4+
from tiatoolbox.visualization.tileserver import TileServer
5+
from tiatoolbox.wsicore.wsireader import WSIReader
6+
7+
8+
@pytest.fixture()
9+
def app(remote_sample) -> TileServer:
10+
"""Create a testing TileServer WSGI app."""
11+
wsi = WSIReader.open(remote_sample("ndpi-1"))
12+
app = TileServer(title="test", layers={"ndpi": wsi})
13+
app.config.from_mapping({"TESTING": True})
14+
return app
15+
16+
17+
def test_get_tile(app) -> None:
18+
"""Get a single tile and check the status code and content type."""
19+
with app.test_client() as client:
20+
response = client.get("/layer/ndpi/zoomify/TileGroup0/0-0-0.jpg")
21+
assert response.status_code == 200
22+
assert response.content_type == "image/jpeg"
23+
24+
25+
def test_get_tile_404(app) -> None:
26+
"""Request a tile with an index."""
27+
with app.test_client() as client:
28+
response = client.get("/layer/ndpi/zoomify/TileGroup0/10-0-0.jpg")
29+
assert response.status_code == 404
30+
assert response.get_data(as_text=True) == "Tile not found"
31+
32+
33+
def test_get_tile_layer_key_error(app) -> None:
34+
"""Request a tile with an invalid layer key."""
35+
with app.test_client() as client:
36+
response = client.get("/layer/foo/zoomify/TileGroup0/0-0-0.jpg")
37+
assert response.status_code == 404
38+
assert response.get_data(as_text=True) == "Layer not found"
39+
40+
41+
def test_get_index(app) -> None:
42+
"""Get the index page and check that it is HTML."""
43+
with app.test_client() as client:
44+
response = client.get("/")
45+
assert response.status_code == 200
46+
assert response.content_type == "text/html; charset=utf-8"

tiatoolbox/data/visualization/static/css/fontawesome-all.min.css

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tiatoolbox/data/visualization/static/css/ol-ext.min.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tiatoolbox/data/visualization/static/css/ol.css

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tiatoolbox/data/visualization/static/js/ol-ext.min.js

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)