Communicating with QuPath
Communicating with QuPath¶
This notebook will show how to exchange information between QuPath and Python.
A few classes and functions of the QuBaLab package will be presented throughout this notebook. To have more details on them, you can go to the documentation on https://qupath.github.io/qubalab/ and type a class/function name in the search bar. You will then see details on parameters functions take.
The QuPath Py4J Extension is a QuPath extension that helps bring the QuPath and QuBaLab worlds much closer together, thanks to Py4J. To begin, you'll need to open QuPath and make sure that this extension is installed. Installation basically involves dragging a jar file onto QuPath's main viewer, and then allowing QuPath to copy the jar to its extensions folder.
If that works, you should see a new Python icon in QuPath's toolbar, and also a command Extensions → Py4J → Start Py4J Gateway.
Both the toolbar button and menu command do the same thing - so click on either to open a gateway from QuPath to Python. You can use the default port and define a token if you want.
For more info, see the Py4J documentation For more details about Py4J and security, see this FAQ page.
Creating the gateway¶
The following code will create a gateway from Python. It assumes that QuPath is launched and a gateway is started from here.
If no error is thrown, it means the connection is established.
from qubalab.qupath import qupath_gateway
token = None # change the value of this variable if you provided a token while creating the QuPath gateway
port = 25333 # change the value of this variable if you provided a different port while creating the QuPath gateway
gateway = qupath_gateway.create_gateway(auth_token=token, port=port)
print("Gateway created")
Gateway created
Communicating with the entry point¶
You can communicate with QuPath through gateway.entry_point
.
This object represents the QuPathEntryPoint
Java class. If you look at the source code of this class, you'll see that it has a few public functions. Let's try the getExtensionVersion()
function:
print(f"Extension version: {gateway.entry_point.getExtensionVersion()}")
Extension version: 0.0.1-SNAPSHOT
The QuPathEntryPoint
class extends the qupath.lib.gui.scripting.QPEx
class, which extends the qupath.lib.scripting.QP
class. This means that all functions of the QPEx
and QP
classes are also available through the entry point.
The availables functions are listed:
Let try the QP.getCurrentImageName()
function:
print(f"Current image name: {gateway.entry_point.getCurrentImageName()}")
Current image name: CMU-1.tiff
When a function of the entry point returns a custom object, a Python JavaObject
is returned. This object contains all fields and functions of the corresponding Java object. For example:
qupath_GUI = gateway.entry_point.getQuPath() # qupath_GUI represents a QuPathGUI Java object
print(f"Type of qupath_GUI: {type(qupath_GUI)}") # qupath_GUI is a JavaObject
# The documentation of a QuPathGUI Java object can be found here:
# https://qupath.github.io/javadoc/docs/qupath/lib/gui/QuPathGUI.html
# For example, we can call the imageData property and the getVersion() function:
print(f"Image data: {qupath_GUI.imageData}")
print(f"Version: {qupath_GUI.getVersion()}")
Type of qupath_GUI: <class 'py4j.java_gateway.JavaObject'> Image data: <py4j.java_gateway.JavaMember object at 0x7f0408d1fd70> Version: 0.6.0-SNAPSHOT
Communicating with qupath_gateway
¶
The qubalab.qupath.qupath_gateway
also provides a set of functions that are easier to use than the entry point. For example, let's show a screenshot of the current QuPath interface:
import matplotlib.pyplot as plt
plt.imshow(qupath_gateway.create_snapshot())
plt.axis(False)
plt.show()
In general:
- Try to use one of the functions of
qubalab.qupath.qupath_gateway
. - If no function exists for your use case, use
gateway.entry_point
.
Accessing an ImageServer
¶
This section will show how to access the metadata and pixel values in Python of an image opened in QuPath. It assumes that an image is currently opened in QuPath.
You can access the QuPath image with the QuPathServer
qubalab class. This class is an implementation of the qubalab ImageServer
which is described in the opening_images.ipynb notebook, so it is recommended that you go through this notebook first.
from qubalab.images.qupath_server import QuPathServer
server = QuPathServer(gateway)
server
is an ImageServer
, so all functions described in opening_images.ipynb are also available here:
# Access image metadata
metadata = server.metadata
print(f'Image path: {metadata.path}')
print(f'Image name: {metadata.name}')
print()
print('Levels:')
for level, shape in enumerate(metadata.shapes):
print(f'Shape of level {level}: {shape}')
print()
print('Pixel calibration:')
print(f'Pixel length on x-axis: {metadata.pixel_calibration.length_x}')
print(f'Pixel length on y-axis: {metadata.pixel_calibration.length_y}')
print()
print(f'Pixel type: {metadata.dtype}')
print()
print(f'Downsamples: {metadata.downsamples}')
print()
print('Channels:')
for channel in metadata.channels:
print(channel)
Image path: /home/leo/Documents/IGC/Images/CMU-1.tiff Image name: CMU-1.tiff Levels: Shape of level 0: ImageShape(x=46000, y=32914, t=1, c=3, z=1) Shape of level 1: ImageShape(x=11500, y=8228, t=1, c=3, z=1) Shape of level 2: ImageShape(x=2875, y=2057, t=1, c=3, z=1) Pixel calibration: Pixel length on x-axis: PixelLength(length=0.499, unit='micrometer') Pixel length on y-axis: PixelLength(length=0.499, unit='micrometer') Pixel type: uint8 Downsamples: (1.0, 4.0, 16.0) Channels: ImageChannel(name='Red', color=(1.0, 0.0, 0.0)) ImageChannel(name='Green', color=(0.0, 1.0, 0.0)) ImageChannel(name='Blue', color=(0.0, 0.0, 1.0))
# Read and show lowest resolution image with read_region
highest_downsample = server.metadata.downsamples[-1]
lowest_resolution = server.read_region(highest_downsample)
print(f'Image shape: {lowest_resolution.shape}')
# This calls a utility function from qubalab to plot the image
# If the image is RGB, all channels will be displayed. Otherwise, only the first channel will be displayed
from qubalab.display.plot import plotImage
_, ax = plt.subplots(figsize=(10, 8))
plotImage(ax, lowest_resolution)
Image shape: (3, 2057, 2875)
<Axes: >
# Read and show lowest resolution image with level_to_dask
last_level = server.metadata.n_resolutions - 1
lowest_resolution = server.level_to_dask(last_level)
# Pixel values are not read yet, but you can get the shape of the image
print(f'Image shape: {lowest_resolution.shape}')
# Compute array. This will read the pixel values
lowest_resolution = lowest_resolution.compute()
_, ax = plt.subplots(figsize=(10, 8))
plotImage(ax, lowest_resolution)
Image shape: (3, 2057, 2875)
<Axes: >