WSI Visualization in LazySlide#

LazySlide implements different plotting functions to showcase different parts of WSIData.

import lazyslide as zs

wsi = zs.datasets.gtex_artery()
wsi
WSI: /home/runner/.cache/huggingface/hub/datasets--RendeiroLab--LazySlide-data/snapshots/3ae589f240b9897db973b706f77636559b100696/GTEX-1117F-0526.svs
Reader: openslide
Dimensions: 19958×19919 (h×w), 3 Pyramids
Pixel physical size: 0.49 MPP (20X)
SpatialData object, with associated Zarr store: /home/runner/.cache/huggingface/hub/datasets--RendeiroLab--LazySlide-data/snapshots/3ae589f240b9897db973b706f77636559b100696/GTEX-1117F-0526.zarr
├── Images
│     └── 'wsi_thumbnail': DataArray[cyx] (3, 1996, 1992)
├── Shapes
│     ├── 'annotations': GeoDataFrame shape: (14, 5) (2D shapes)
│     ├── 'dl-tissue': GeoDataFrame shape: (2, 2) (2D shapes)
│     ├── 'tiles': GeoDataFrame shape: (253, 9) (2D shapes)
│     └── 'tissues': GeoDataFrame shape: (2, 2) (2D shapes)
└── Tables
      ├── 'resnet50_tiles': AnnData (253, 2048)
      └── 'uni2_tiles': AnnData (253, 1536)
with coordinate systems:
    ▸ 'global', with elements:
        wsi_thumbnail (Images), annotations (Shapes), dl-tissue (Shapes), tiles (Shapes), tissues (Shapes)

Visualize tissue#

The tissues have been detected, so you will see both colored contours and id of tissues.

zs.pl.tissue(wsi)
../_images/de05dc85cad4f75fef79e4909b24e51367b78b0dadf18b773d4c9788f1a6afab.png

If you want to zoom in to specific tissue, you can specify the tissue id.

zs.pl.tissue(wsi, tissue_id=0)
../_images/8f3c0c168cfe654122ae401498984e53144030c3845abbc376e978c7231b044d.png

If this is not enough, you can add a zoom view to highlight specific region.

zs.pl.tissue(wsi, tissue_id=0, zoom=[0.6, 0.9, 0.3, 0.6])
../_images/dbd9c5d6536cb61dd5def68446cd65c0b4886948276c1d9b4138a924047a43e8.png

You can also render all tissue pieces at once

zs.pl.tissue(wsi, tissue_id="all")
../_images/9b742690209d9b9bf2f1a6a8350e847170c11fe9b4410a50d9967219e7e6dc19.png

Visualize tiles#

zs.pl.tiles(wsi)
../_images/140e368be9df150641725028bf385bb161f6d16fee6b5a6931bf01573cd59525.png

By default, it will only display the tile grid on the tissues.

If not clear enough, you can also zoom in.

zs.pl.tiles(wsi, tissue_id=0, zoom=[0.6, 0.9, 0.3, 0.6])
../_images/0c770f4263806377d953a0fa76ffb074a3b814fe234f3d93c5ad9659eeb239ca.png

Furthermore, we can project many features onto the tile visualization.

wsi["tiles"].head()
tile_id tissue_id geometry diagnostic_quality visual_cleanliness focus_issue staining_issue tissue_folding_present misc_artifacts_present
0 0 0 POLYGON ((4052 16394, 4052 16653, 3793 16653, ... 0.663754 0.198376 0.480506 0.549304 0.072527 0.116421
1 1 0 POLYGON ((4052 16653, 4052 16912, 3793 16912, ... 0.742671 0.121662 0.386750 0.444288 0.036697 0.074886
2 2 0 POLYGON ((4311 15617, 4311 15876, 4052 15876, ... 0.791731 0.142956 0.264362 0.382962 0.278880 0.274505
3 3 0 POLYGON ((4311 15876, 4311 16135, 4052 16135, ... 0.742936 0.149261 0.250248 0.435397 0.248712 0.476643
4 4 0 POLYGON ((4311 16135, 4311 16394, 4052 16394, ... 0.753808 0.188161 0.238122 0.335339 0.426094 0.438732
zs.pl.tiles(wsi, tissue_id=0, color=["diagnostic_quality", "focus_issue"], smooth=True)
../_images/133163857ad7652f7864053083a08dcdea7e4b241201a91613be23d1cc706776.png
zs.pl.tiles(wsi, tissue_id=0, color="diagnostic_quality", zoom=[0.6, 0.9, 0.3, 0.6], smooth=True)
../_images/feaa433d03cb5f232bc4e20b6d83e84101015bf05eb76148ee51bd0443fb10ee.png
zs.pl.tiles(wsi, tissue_id=0, feature_key="resnet50", color=["1", "100"], smooth=True)
../_images/dd822a0679d8bb0aedb2601052254e025887fa5971ace3595ce55f83e7f44762.png

Visualize annotations#

If you have imported pathological annotations, they can also be visualized.

wsi["annotations"].head()
tissue_id id objectType name geometry
0 0 f56c25e5-ce21-4d42-bcda-b67b9fd98870 annotation sclerosis POLYGON ((4631.5 16627, 4629 16628, 4594 16628...
1 0 c799bbe2-5a08-467a-9e5d-6376b718cee8 annotation sclerosis POLYGON ((4528 16331, 4527.67 16331.17, 4527 1...
2 0 bcc06960-003b-43f0-9e88-496579dffd34 annotation sclerosis POLYGON ((5598.73 17098.36, 5596 17102, 5594.8...
3 0 e3ba7093-e973-424a-9c77-66caa03569fb annotation sclerosis POLYGON ((5964 17053, 5956 17055, 5954.2 17056...
4 0 370d0dc5-f5d1-40d9-bce6-d47f63fc4cbc annotation sclerosis POLYGON ((5649 16849, 5652 16848, 5655 16848, ...
zs.pl.annotations(wsi, "annotations", tissue_id=0)
../_images/f1357d4615d299458653ae909772e2baf2eb7b81831c45179a5513979310dcc3.png

You can also add labels onto the image.

zs.pl.annotations(
    wsi, "annotations", tissue_id=0, label="name", zoom=[0.6, 0.9, 0.3, 0.6]
)
../_images/a246522985a0265ab5eacfe056588d3269173481c6291e1a2dd9dee4d5c6f5ef.png

Declarative visualization in LazySlide#

Since WSIData extends from SpatialData, you may use spatialdata-plot to visualize the WSI. However, LazySlide implements an super fast and efficient plotting system to help visualize WSI from macro structures to single cells.

We start with an empty viewer, you will need to choose what to add on top of the visualization.

v = zs.pl.WSIViewer(wsi)
v.show()
<Axes: >
../_images/e106f1624b127713207add3c2b162a06b4c338a9e86aed0e5e993c1ff8bf36bf.png

In most situation, we need the slide image as the background.

v.add_image()
v.show()
<Axes: >
../_images/ba071968f102f8a58747287aaa2f8d2a20644aa3dc38c8e78d6cbab0820998bb.png
v.add_contours("tissues")
v.show()
<Axes: >
../_images/17dc49671dc0c984a55af0c53c7ca30b499c8e724008b10cddc38c4b6946e08c.png
v.add_polygons("annotations")
v.show()
<Axes: >
../_images/553befc32d9355bb9abfb1a38318a69bcf76dcb594d97058139c77da9f2809f4.png
v.set_tissue_id(0)
v.show()
<Axes: >
../_images/6d9ee143a7fbc8cc722304e180391350dd3597997adba7c1d8be19cbe0d4e54c.png
v.add_zoom(0.6, 0.9, 0.3, 0.6)
v.add_scalebar()
v.mark_origin()
v.show()
<Axes: >
../_images/21d2a715d40c8ccdc7929b5b861a01d1bbba0ed78cbd6568ab438f3e226bd57f.png

To summarize, you will have the following code

v = zs.pl.WSIViewer(wsi)
v.add_image()
v.add_contours("tissues")
v.add_polygons("annotations")
v.set_tissue_id(0)
v.add_zoom(0.6, 0.9, 0.3, 0.6)
v.add_scalebar()
v.mark_origin()
<lazyslide.plotting._wsi_viewer.WSIViewer at 0x7fc6fc3f46e0>

If we don’t called the .show() method, nothing will happen. The plotting will be lazily evaluated.

Now let’s called the .show() to render the final image.

v.show()
<Axes: >
../_images/21d2a715d40c8ccdc7929b5b861a01d1bbba0ed78cbd6568ab438f3e226bd57f.png

You can control if an element shoud be displayed in the zoom view.

Here we disabled the display of yellow annotations in the zoom view.

v = zs.pl.WSIViewer(wsi)
v.add_image()
v.add_polygons("annotations", in_zoom=False)
v.set_tissue_id(0)
v.add_zoom(0.6, 0.9, 0.3, 0.6)
v.show()
<Axes: >
../_images/c88196331ad70842e13dcf463b21de34147ab4c520e376b516e2e36f24d4fd66.png

Efficient debugging of visualization#

The progressive adding components on top of the images is cool, it gives you finer control on what to visualize.

However, it’s not efficient to debug, once you add a component, you cannot delete it.

If you have a large image and you create a new viewer everytime when you want to modify some tiny details, you waste your time on re-computing the image rendering process.

Luckily, we have a solution for this. When you add a component, simply set cache=False, and it will only be rendered in the next rendering.

v = zs.pl.WSIViewer(wsi)
v.add_image()
v.add_polygons("annotations", cache=False)
v.set_tissue_id(0)
v.show()
<Axes: >
../_images/358baeac1c4e7731a8b0f8537ee5921cdf74cb0345cf8108e9d906eb5c495ec7.png

If we called the .show() again, the annotation will disappered.

v.show()
<Axes: >
../_images/a9648313260e7df462c5e478648f426151d9d0461992a034d591c4dae5dcc3e9.png

In this way, you can easily change color without recomputing the image rendering process.

You may not notice the difference in this example, but if you have a huge WSI. This can make significant difference.

v.add_polygons("annotations", color="#604FDD", cache=False)
v.show()
<Axes: >
../_images/97d8c52279f5915404bef970baccf2d8ec8b19a9c9e6cae9f5c68cf05fab50af.png