Quickstart
==========
Before you start diving deep into `paquo` we strongly recommend to read the excellent
`QuPath Documentation `_. Since `paquo` is
just providing a pythonic interface to QuPath functionality very many concepts map
directly between the python and java world.
.. danger::
`Paquo` is undergoing heavy development. Expect things to change before we reach version `1.0.0`
Working with projects
---------------------
QuPath projects are accessed in `paquo` via :class:`paquo.projects.QuPathProject`.
It's best to use them via their contextmanager interface, because then :meth:`paquo.projects.QuPathProject.save`
get's automatically called after you made changes:
.. code-block:: python
from paquo.projects import QuPathProject
qp = QuPathProject('./my_qupath_project/project.qpproj')
... # science
By default `paquo` opens projects in readonly mode.
Images on a project are provided via a sequence-like proxy interface (basically a tuple) and they
can be added via the projects :meth:`paquo.projects.QuPathProject.add_image` method.
.. code-block:: python
>>> from paquo.projects import QuPathProject
>>> from paquo.images import QuPathImageType
>>> qp = QuPathProject('./my_qupath_project', mode='a') # open project for appending
>>> qp.images # <- access images via this
ImageEntries(['image_0.svs', 'image_1.svs', 'image_2.svs'])
>>> qp.add_image('/path/to/my/image.svs', image_type=QuPathImageType.OTHER)
When you open an existing project, it might be possible that some of the images in the project
have been moved around. (Maybe you send the project to a friend, and they have the same images on
a network share but the path is of course different.) To check this, projects provide a method
:meth:`paquo.projects.QuPathProject.is_readable`. It returns image ids (URIs in the current default)
and a boolean indicating if the file can be reached:
.. code-block:: python
>>> from paquo.projects import QuPathProject
>>> qp = QuPathProject('./my_qupath_project', mode='r')
>>> qp.images # <- access images via this
ImageEntries(['image_0.svs', 'image_1.svs', 'image_2.svs'])
>>> qp.is_readable()
{'file:/share/image_0.svs': True,
'file:/somewhere_else/image_1.svs': False,
'file:/share/image_2.svs': True}
With default settings you can reassign missing images via :meth:`paquo.projects.QuPathProject.update_image_paths`
which takes a uri to uri mapping as an input:
.. code-block:: python
with QuPathProject('./my_qupath_project', mode='r+') as qp:
qp.update_image_paths(uri2uri={"file:/somewhere_else/image_1.svs": "file:/share/image_1.svs"})
assert all(qp.is_readable().values())
.. danger::
There's a few things to know about this. The way images are passed in `paquo` uses an abstraction layer
named :class:`paquo.images.ImageProvider`. It's default implementation doesn't do anything smart and
uses image URIs to identify images. (It also only supports 'file:/' uris for now.) The idea behind this
is that we can provide a caching layer for gathering images from many different sources. Follow
`Paquo Issue #13 `_ for updates.
We will add additional documentation once the implementation details are sorted out.
Projects also serve as a container for classes. They are exposed via another sequence-like proxy:
.. code-block:: python
>>> from paquo.projects import QuPathProject
>>> qp = QuPathProject('./my_qupath_project', mode='r')
>>> qp.path_classes # <- access classes via this attribute
(QuPathPathClass('myclass_0'), QuPathPathClass('myclass_1'), QuPathPathClass('myclass_2'))
Refer to the class example :ref:`class example` for more details.
Working with annotations
------------------------
`paquo` uses `shapely `_ to provide a pythonic
interface to Qupath's annotations. It's recommended to make yourself familiar
with shapely. Annotations are accessed on a hierarchy of a `QuPathProjectEntry`.
You access them through a set-like readonly proxy object. If you want to add additional
annotations use the :meth:`paquo.hierarchy.QuPathPathObjectHierarchy.add_annotations` method.
.. code-block:: python
>>> qp = QuPathProject('./my_new_project/project.qpproj', mode='r') # open an existing project
>>> image = qp.images[0] # get the first image
>>> image.hierarchy.annotations # annotations are stored in a set like proxy object
QuPathPathAnnotationObjectSet(n=3)
>>> for annotation in image.hierarchy.annotations:
... print(annotation.name, annotation.path_class, annotation.roi)
...
None QuPathPathClass('myclass_1') POLYGON ((50 50, 50 150, 150 150, 150 50, 50 50))
MyAnnotation QuPathPathClass('myclass_2') POLYGON ((50 650, 50 750, 150 750, 150 650, 50 650))
Another None POLYGON ((650 650, 650 750, 750 750, 750 650, 650 650))
Examples
--------
You can find the code for many use case examples
To get started setup a python environment with `paquo`. Git clone the repository and cd to the
examples directory.
.. code-block:: console
user@computer:~$ git clone git@github.com:bayer-science-for-a-better-life/paquo.git
user@computer:~$ cd paquo/examples
user@computer:examples$ python prepare_resources.py
This will create a folder `images` and a folder `projects` with example data.
These are required for all of the examples to run. Refer to the examples to
quickly learn how to solve a certain problem with paquo. In case your specific
problem does not have an example yet, feel free to open a new issue in `paquo`'s
`issue tracker `_.
.. tip::
If you already have a solution for a problem and think it might
have value for others *(NOTE: it always does!)* feel free to fork the `paquo`
repository and create a Pull Request adding the new example.
Reading annotations
^^^^^^^^^^^^^^^^^^^
To read annotations from an existing project follow the code as shown here:
.. literalinclude:: ../../examples/example_01_read_annotations.py
:language: python
:linenos:
Add annotations to a project
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To add annotations to a project you simply need to define them as `shapely` Geometries and
then add them to your QuPath project as demonstrated here:
.. literalinclude:: ../../examples/example_02_add_annotations.py
:language: python
:linenos:
.. _class example:
Predefine classes in a project
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sometimes you might want to create projects with many predefined classes so that different users
wont mistype the class names, or that you can enforce a unique coloring scheme accross your projects.
Adding classes is as simple as:
.. literalinclude:: ../../examples/example_03_project_with_classes.py
:language: python
:linenos:
Add image metadata
^^^^^^^^^^^^^^^^^^
Especially in bigger projects it can be useful if you know, you annotated a certain image, or what's
the current state of the annotations. For those things it's best to use metadata like this:
.. literalinclude:: ../../examples/example_04_project_with_image_metadata.py
:language: python
:linenos:
Drawing tiled overlays
^^^^^^^^^^^^^^^^^^^^^^
If you want to display additional information on top of your image, that can be easily hidden by a user,
You can use `TileDetectionObjects` to build a grid containing measurement values:
.. literalinclude:: ../../examples/example_05_draw_tiles_on_image.py
:language: python
:linenos:
This will allow you to display extra data like this:
.. image:: _static/screenshot_example_05.png
:width: 400
:alt: Tile Overlay Example 05
Putting Detection Measurements into a Pandas DataFrame
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Extracting data from QuPath into Pandas is similarly straightforward:
.. code-block:: python
import pandas as pd
qp = QuPathProject('./my_new_project/project.qpproj', mode='r') # open an existing project
image = qp.images[0] # get the first image
detections = image.hierarchy.detections # detections are stored in a set like proxy object
df = pd.DataFrame(detection.measurements for detection in detections) # put the measurements dictionary for each detection into a pandas DataFrame
More examples
^^^^^^^^^^^^^
We need your input! |:bow:|
.. tip::
In case you need another example for the specific thing you'd like to do, please feel free to open a new
issue on `paquo`'s `issue tracker `_.
We'll try our best to help you |:+1:|