Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/main/_static/tiatoolbox_tutorial.ipynb
Views: 712
Whole Slide Image Classification Using PyTorch and TIAToolbox
Introduction
In this tutorial, we will show how to classify Whole Slide Images (WSIs) using PyTorch deep learning models with help from TIAToolbox. A WSI represents human tissues taken through an operation or a biopsy and scanned using specialized scanners. They are used by pathologists and computational pathology researchers to study cancer at the microscopic level in order to understand for example tumor growth and help improve treatment for patients.
What makes WSIs challenging to process is their enormous size. For example, a typical slide image has in the order of 100,000x100,000 pixels where each pixel can correspond to about 0.25x0.25 microns on the slide. This introduces challenges in loading and processing such images, not to mention hundreds or even thousands of WSIs in a single study (larger studies produce better results)!
Conventional image processing pipelines are not suitable for WSI processing so we need better tools. This where TIAToolbox can help as it brings a set of useful tools to import and process tissue slides in a fast and computationally efficient manner. Typically, WSIs are saved in a pyramid structure with multiple copies of the same image at various magnification levels optimized for visualization. The level 0 (or the bottom level) of the pyramid contains the image at the highest magnification or zoom level, whereas the higher levels in the pyramid have a lower resolution copy of the base image. The pyramid structure is sketched below.
WSI pyramid stack (source)
TIAToolbox allows us to automate common downstream analysis tasks such as tissue classification. In this tutorial we will show you how you can:
Load WSI images using TIAToolbox; and
Use different PyTorch models to classify slides at the batch-level. In this tutorial, we will provide an example of using TorchVision's
ResNet18
model and customHistoEncoder
model.
Let's get started!
Setting up the environment
To run the examples provided in this tutorial, the following packages are required as prequisites..
OpenJpeg
OpenSlide
Pixman
TIAToolbox
HistoEncoder (for a custom model example)
Please run the following command in your terminal to install these packages:
Alternatively, you can run brew install openjpeg openslide
to install the prerequistite packages on MacOS instead of apt-get
. Further information on installation can be found here. You will likely need to restart the runtime in the runtime menu at the top of the page to continue with the rest of the tutorial, in order for the newly installed dependencies to be picked up.
Importing related libraries
Clean-up before a run
To ensure proper clean-up (for example in abnormal termination), all files downloaded or created in this run are saved in a single directory global_save_dir
, which we set equal to "./tmp/". To simplify maintenance, the name of the directory occurs only at this one place, so that it can easily be changed, if desired.
Downloading the data
For our sample data, we will use one whole-slide image, and patches from the validation subset of Kather 100k dataset.
Reading the data
We create a list of patches and a list of corresponding labels. For example, the first label in label_list
will indicate the class of the first image patch in patch_list
.
As you can see for this patch dataset, we have 9 classes/labels with IDs 0-8 and associated class names. describing the dominant tissue type in the patch:
BACK ⟶ Background (empty glass region)
LYM ⟶ Lymphocytes
NORM ⟶ Normal colon mucosa
DEB ⟶ Debris
MUS ⟶ Smooth muscle
STR ⟶ Cancer-associated stroma
ADI ⟶ Adipose
MUC ⟶ Mucus
TUM ⟶ Colorectal adenocarcinoma epithelium
Classify image patches
We demonstrate how to obtain a prediction for each patch within a digital slide first with the patch
mode and then with a large slide using wsi
mode.
Define PatchPredictor
model
The PatchPredictor class runs a CNN-based classifier written in PyTorch.
model
can be any trained PyTorch model with the constraint that it should follow thetiatoolbox.models.abc.ModelABC
class structure. For more information on this matter, please refer to our example notebook on advanced model techniques. In order to load a custom model, you need to write a small preprocessing function, as inpreproc_func(img)
, which make sures the input tensors are in the right format for the loaded network.Alternatively, you can pass
pretrained_model
as a string argument. This specifies the CNN model that performs the prediction, and it must be one of the models listed here. The command will look like this:predictor = PatchPredictor(pretrained_model='resnet18-kather100k', pretrained_weights=weights_path, batch_size=32)
.pretrained_weights
: When using apretrained_model
, the corresponding pretrained weights will also be downloaded by default. You can override the default with your own set of weights via thepretrained_weight
argument.batch_size
: Number of images fed into the model each time. Higher values for this parameter require a larger (GPU) memory capacity.
Predict patch labels
We create a predictor object and then call the predict
method using the patch
mode. We then compute the classification accuracy and confusion matrix.
Predict patch labels for a whole slide
We also introduce IOPatchPredictorConfig
, a class that specifies the configuration of image reading and prediction writing for the model prediction engine. This is required to inform the classifier which level of the WSI pyramid the classifier should read, process data and generate output.
Parameters of IOPatchPredictorConfig
are defined as:
input_resolutions
: A list, in the form of a dictionary, specifying the resolution of each input. List elements must be in the same order as in the targetmodel.forward()
. If your model accepts only one input, you just need to put one dictionary specifying'units'
and'resolution'
. Note that TIAToolbox supports a model with more than one input. For more information on units and resolution, please see TIAToolbox documentation.patch_input_shape
: Shape of the largest input in (height, width) format.stride_shape
: The size of a stride (steps) between two consecutive patches, used in the patch extraction process. If the user setsstride_shape
equal topatch_input_shape
, patches will be extracted and processed without any overlap.
The predict
method applies the CNN on the input patches and get the results. Here are the arguments and their descriptions:
mode
: Type of input to be processed. Choose frompatch
,tile
orwsi
according to your application.imgs
: List of inputs, which should be a list of paths to the input tiles or WSIs.return_probabilities
: Set to True to get per class probabilities alongside predicted labels of input patches. If you wish to merge the predictions to generate prediction maps fortile
orwsi
modes, you can setreturn_probabilities=True
.ioconfig
: set the IO configuration information using theIOPatchPredictorConfig
class.resolution
andunit
(not shown below): These arguments specify the level or micron-per-pixel resolution of the WSI levels from which we plan to extract patches and can be used instead ofioconfig
. Here we specify the WSI's level as'baseline'
, which is equivalent to level 0. In general, this is the level of greatest resolution. In this particular case, the image has only one level. More information can be found in the documentation.masks
: A list of paths corresponding to the masks of WSIs in theimgs
list. These masks specify the regions in the original WSIs from which we want to extract patches. If the mask of a particular WSI is specified asNone
, then the labels for all patches of that WSI (even background regions) would be predicted. This could cause unnecessary computation.merge_predictions
: You can set this parameter toTrue
if it's required to generate a 2D map of patch classification results. However, for large WSIs this will require large available memeory. An alternative (default) solution is to setmerge_predictions=False
, and then generate the 2D prediction maps using themerge_predictions
function as you will see later on.
Since we are using a large WSI the patch extraction and prediction processes may take some time (make sure to set the ON_GPU=True
if you have access to Cuda enabled GPU and PyTorch+Cuda).
We see how the prediction model works on our whole-slide images by visualizing the wsi_output
. We first need to merge patch prediction outputs and then visualize them as an overlay on the original image. As before, the merge_predictions
method is used to merge the patch predictions. Here we set the parameters resolution=1.25, units='power'
to generate the prediction map at 1.25x magnification. If you would like to have higher/lower resolution (bigger/smaller) prediction maps, you need to change these parameters accordingly. When the predictions are merged, use the overlay_patch_prediction
function to overlay the prediction map on the WSI thumbnail, which should be extracted at the resolution used for prediction merging.
Overlaying the prediction map on this image as below gives:
Feature extraction with a pathology-specific model
In this section, we will show how to extract features from a pretrained pytorch model that exists outside TIAToolbox, using the WSI inference engines provided by tiatoolbox. To illustrate this we will use HistoEncoder, a computational-pathology specific model that has been trained in a self-supervised fashion to extract features from histology images. The model has been made available here:
'HistoEncoder: Foundation models for digital pathology' (https://github.com/jopo666/HistoEncoder) by Pohjonen, Joona and team at the University of Helsinki.
We will plot a umap reduction into 3D (rgb) of the feature map to visualize how the features capture the differences between some of the above mentioned tissue types.
TIAToolbox defines a ModelABC which is a class inheriting PyTorch nn.Module and specifies how a model should look in order to be used in the TIAToolbox inference engines. The histoencoder model doesn't follow this structure, so we need to wrap it in a class whose output and methods are those that the TIAToolbox engine expects.
Now that we have our wrapper, we will create our feature extraction model and instantiate a DeepFeatureExtractor to allow us to use this model over a WSI. We will use the same WSI as above, but this time we will extract features from the patches of the WSI using the HistoEncoder model, rather than predicting some label for each patch.
When we create the DeepFeatureExtractor
, we will pass the auto_generate_mask=True
argument. This will automatically create a mask of the tissue region using otsu thresholding, so that the extractor processes only those patches containing tissue.
These features could be used to train a downstream model, but here in order to get some intuition for what the features represent, we will use a UMAP reduction to visualize the features in RGB space. The points labeled in a similar color should have similar features, so we can check if the features naturally separate out into the different tissue regions when we overlay the UMAP reduction on the WSI thumbnail. We will plot it along with the patch-level prediction map from above to see how the features compare to the patch-level predictions in the following cells.
We see that the prediction map from our patch-level predictor, and the feature map from our self-supervised feature encoder, capture similar information about the tissue types in the WSI. This is a good sanity check that our models are working as expected. It also shows that the features extracted by the HistoEncoder model are capturing the differences between the tissue types, and so that they are encoding histologically relevant information.
Where to Go From Here
In this notebook, we show how we can use the PatchPredictor
and DeepFeatureExtractor
classes and their predict
method to predict the label, or extract features, for patches of big tiles and WSIs. We introduce merge_predictions
and overlay_prediction_mask
helper functions that merge the patch prediction outputs and visualize the resulting prediction map as an overlay on the input image/WSI.
All the processes take place within TIAToolbox and we can easily put the pieces together, following our example code. Please make sure to set inputs and options correctly. We encourage you to further investigate the effect on the prediction output of changing predict
function parameters. We have demonstrated how to use your own pretrained model or one provided by the research community for a specific task in the TIAToolbox framework to do inference on large WSIs even if the model structure is not defined in the TIAToolbox model class.
You can learn more through the following resources: