Bayer-Group / paquo

PAthological QUpath Obsession - QuPath and Python conversations

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Example to show how to add annotations as children of other annotations

Mena-SA-Kamel opened this issue · comments

Hi, I would like to know if there is a way I can use Paquo to add annotations as children of other annotations as described here: https://qupath.readthedocs.io/en/stable/docs/concepts/object_hierarchy.html

The documentation does a good job of explaining how to add annotations as children of the image root (all having the same level in the hierarchy). It would be great to have a way to add annotations while specifying the parent that they belong to, as seen in the image below:
Screen Shot 2022-09-16 at 4 07 56 PM

Hi @Mena-SA-Kamel

Thank you for your question. Currently this functionality is not fully exposed in paquo, but I'll provide a workaround for you below.

The parent child relationships are normally resolved in QuPath by determining the overlap between the annotations and determine if an annotation is completely within the parent. (Best to ask in the image.sc forums how the exact behavior is defined)

Below you find an example how to trigger the parent/child resolve on a hierarchy via paquo once you added all your annotations:

# example_06_add_hierarchical_annotations.py 
"""example showing how to create a project with annotations"""
from pathlib import Path
from paquo.projects import QuPathProject
from paquo.images import QuPathImageType
from shapely.geometry import Polygon

EXAMPLE_PROJECT = Path(__file__).parent.absolute() / "projects" / "example_06_project"
EXAMPLE_IMAGE = Path(__file__).parent.absolute() / "images" / "image_1.svs"

ANNOTATIONS = {
    'Annotation 1': Polygon.from_bounds(400, 400, 600, 600),
    'Annotation 2': Polygon.from_bounds(700, 700, 800, 800),
    'Child 1-1': Polygon.from_bounds(410, 410, 450, 450),
    'Child 1-2': Polygon.from_bounds(410, 460, 450, 540),
    'Child 1-3': Polygon.from_bounds(500, 500, 530, 540),
    'Child 2-1': Polygon.from_bounds(720, 720, 750, 750),
}

# create a the new project
with QuPathProject(EXAMPLE_PROJECT, mode='x') as qp:
    print("created", qp.name)

    # add a new image:
    entry = qp.add_image(EXAMPLE_IMAGE, image_type=QuPathImageType.BRIGHTFIELD_H_E)

    for name, roi in ANNOTATIONS.items():
        # add the annotations without a class set
        annotation = entry.hierarchy.add_annotation(roi=roi)
        annotation.name = name

    # THIS IS THE CALL THAT TRIGGERS THE HIERARCHY RESOLVE
    entry.hierarchy.java_object.resolveHierarchy()

    print(f"done. Please look at {qp.name} in QuPath.")

I will think about if this should be the default behavior and integrate it better in the next version of paquo 😃

Let me know if you have any other questions!

Cheers,
Andreas 😃

Hi Andreas,

Apologies for the late reply. Thank you for your reply. I think this makes sense. Thanks a lot! :)

Mena

Hi!

I am starting to learn to use paquo, and wanted to pipe in occasionally as I see QuPath related things I understand!
One additional function that might give slightly more control is insertSelectedObjectsInHierarchy so that in complex situations you do not need to resolve everything.

For even more precise control, you have addObjectBelowParent() where you can pass both the parent and the object you want to add to that parent.