Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add Support for QuPath Annotation Imports #721

Merged
merged 9 commits into from
Nov 22, 2023

Conversation

measty
Copy link
Collaborator

@measty measty commented Oct 10, 2023

This PR is intended to make importing objects from QuPath (and other applications) easier. I have updated it to follow the suggestion of John to do it via dependency injection.

If you export some objects in .geojson from some versions of QuPath, and they have measurements, all the measurements are in a 'measurements' property in the geojson. This means, when you import into annotation store, it will import fine but the properties dict will look something like:

properties = {a couple other props, "measurements": [{'name': 'propA', 'value': valA}, {'name': 'propB', 'value': valB}, etc]}

Which is awkward for many downstream things with the nested data structure, and doesn't really mesh with how annotations are intended to be represented in the store (usually a flat dict of properties unless there's a very good reason for nesting).

This PR adds an option to provide a transform to deal with any application-specific formatting, which for example can be used to unpack measurements when importing from QuPath to give instead in a properties dict with the measurement properties unpacked:

properties = {a couple other props, 'propA': valA, 'propB': valB, etc}

Importing from QuPath is probably one of the main use-cases for this, and an example is provided in the docstring for this case, but the additional flexibility means it can be used for other use-cases too.

@codecov
Copy link

codecov bot commented Oct 10, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (cca7443) 99.85% compared to head (dd16891) 99.85%.
Report is 1 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop     #721   +/-   ##
========================================
  Coverage    99.85%   99.85%           
========================================
  Files           65       65           
  Lines         7517     7520    +3     
  Branches      1460     1461    +1     
========================================
+ Hits          7506     7509    +3     
  Misses           4        4           
  Partials         7        7           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@shaneahmed shaneahmed changed the title ENH: easier qupath imports ✨ Add support for QuPath Annotation Imports Oct 10, 2023
@shaneahmed shaneahmed added the enhancement New feature or request label Oct 10, 2023
@shaneahmed shaneahmed added this to the Release v1.5.0 milestone Oct 10, 2023
@shaneahmed shaneahmed changed the title ✨ Add support for QuPath Annotation Imports ✨ Add Support for QuPath Annotation Imports Oct 10, 2023
@John-P
Copy link
Contributor

John-P commented Oct 13, 2023

An alternative approach would be to use dependency injection to achieve the same result. In this approach, you could provide a "transform" parameter (similar to PyTorch datasets) that accepts any callable and applies it to the annotation or properties during loading. The main advantage of this method is that it allows for different types of transformations to be applied during import, without the need to specify a separate set of keyword arguments for each transformation. also having many kwargs that could conflict may be annoying. You could even chain or compose transforms (much like image transforms). This can be particularly useful when importing from another application, where different types of transformations may be required. You could then show a 'recipe' (see itertools recipes) for QuPath in the examples docstring or even provide some named transform functiosn for common imports.

e.g.

def qupath_transform(properties: dict) -> dict:
    """An example QuPath transform."""
    measurements = properties.pop("measurements", {})
    properties.update(measurements)
    return properties

def another_app_transform(properties: dict) -> dict:
    """An example transform for annotation from another application."""
    ...

store.from_geojson("qupath_annotation.json", transform=qupath_transform)

This could alternatively be a transform that takes an annotation instead of just the properties dict e.g. if you wanted to apply a geometric transform based on the properties etc.

Not required of course, but I think it would make things easier to test and add greater flexibility.

Copy link
Member

@shaneahmed shaneahmed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can you make the changes as suggested by @John-P and discussed during the meeting on Friday?

Copy link
Member

@shaneahmed shaneahmed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @measty

@shaneahmed shaneahmed merged commit 1d3f039 into develop Nov 22, 2023
14 checks passed
@shaneahmed shaneahmed deleted the easier-qupath-imports branch November 22, 2023 10:02
@shaneahmed shaneahmed mentioned this pull request Dec 15, 2023
shaneahmed added a commit that referenced this pull request Dec 15, 2023
## 1.5.0 (2023-12-15)

### Major Updates and Feature Improvements

- Adds the bokeh visualization tool. #684
  - The tool allows a user to launch a server on their machine to visualise whole slide images, overlay the results of deep learning algorithms or to select a patch from whole slide image and run TIAToolbox deep learning engines.
  - This tool powers the TIA demos server. For details please see https://tiademos.dcs.warwick.ac.uk/.
- Extends Annotation to Support Init from WKB #639
- Adds `IOConfig` for NuClick in `pretrained_model.yaml` #709
- Adds functions to save the TIAToolbox Engine outputs to Zarr and AnnotationStore files. #724
- Adds Support for QuPath Annotation Imports #721

### Changes to API

- Adds `model.to(device)` and `model.load_model_from_file()` functionality to make it compatible with PyTorch API. #733
- Replaces `pretrained` with `weights` to make the engines compatible with the new PyTorch API. #621
- Adds support for high-level imports for various utility functions and classes such as `WSIReader`, `PatchPredictor` and `imread` #606, #607,
- Adds `tiatoolbox.typing` for type hints. #619
- Fixes incorrect file size saved by `save_tiles`, issue with certain WSIs raised by @TomastpPereira
- TissueMasker transform now returns mask instead of a list. #748
  - Fixes #732

### Bug Fixes and Other Changes

- Fixes `pixman` incompability error on Colab #601
- Removes `shapely.speedups`. The module no longer has any affect in Shapely >=2.0. #622
- Fixes errors in the slidegraph example notebook #608
- Fixes bugs in WSI Registration #645, #670, #693
- Fixes the situation where PatchExtractor.get_coords() can return patch coords which lie fully outside the bounds of a slide. #712
  - Fixes #710
- Fixes #738 raised by @xiachenrui

### Development related changes

- Replaces `flake8` and `isort` with `ruff` #625, #666
- Adds `mypy` checks to `root` and `utils` package. This will be rolled out in phases to other modules. #723
- Adds a module to detect file types using magic number/signatures #616
- Uses `poetry` for version updates instead of `bump2version`. #638
- Removes `setup.cfg` and uses `pyproject.toml` for project configurations.
- Reduces runtime for some unit tests e.g., #627, #630, #631, #629
- Reuses models and datasets in tests on GitHub actions by utilising cache #641, #644
- Set up parallel tests locally #671

**Full Changelog:** v1.4.0...v1.5.0

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: mostafajahanifar <74412979+mostafajahanifar@users.noreply.github.com>
Co-authored-by: John Pocock <John-P@users.noreply.github.com>
Co-authored-by: DavidBAEpstein <David.Epstein@warwick.ac.uk>
Co-authored-by: David Epstein <22086916+DavidBAEpstein@users.noreply.github.com>
Co-authored-by: Ruqayya Awan <18444369+ruqayya@users.noreply.github.com>
Co-authored-by: Mark Eastwood <20169086+measty@users.noreply.github.com>
Co-authored-by: adamshephard <39619155+adamshephard@users.noreply.github.com>
Co-authored-by: adamshephard <adam.shephard@warwick.ac.uk>
Co-authored-by: Abdol <a@fkrtech.com>
Co-authored-by: Jiaqi-Lv <60471431+Jiaqi-Lv@users.noreply.github.com>
Co-authored-by: Abishek <abishekraj6797@gmail.com>
Co-authored-by: Dmitrii Blaginin <blaginin@mbp.lan>
@shaneahmed shaneahmed mentioned this pull request Dec 15, 2023
shaneahmed added a commit that referenced this pull request Dec 15, 2023
## 1.5.0 (2023-12-15)

### Major Updates and Feature Improvements

- Adds the bokeh visualization tool. #684
  - The tool allows a user to launch a server on their machine to visualise whole slide images, overlay the results of deep learning algorithms or to select a patch from whole slide image and run TIAToolbox deep learning engines.
  - This tool powers the TIA demos server. For details please see https://tiademos.dcs.warwick.ac.uk/.
- Extends Annotation to Support Init from WKB #639
- Adds `IOConfig` for NuClick in `pretrained_model.yaml` #709
- Adds functions to save the TIAToolbox Engine outputs to Zarr and AnnotationStore files. #724
- Adds Support for QuPath Annotation Imports #721

### Changes to API

- Adds `model.to(device)` and `model.load_model_from_file()` functionality to make it compatible with PyTorch API. #733
- Replaces `pretrained` with `weights` to make the engines compatible with the new PyTorch API. #621
- Adds support for high-level imports for various utility functions and classes such as `WSIReader`, `PatchPredictor` and `imread` #606, #607,
- Adds `tiatoolbox.typing` for type hints. #619
- Fixes incorrect file size saved by `save_tiles`, issue with certain WSIs raised by @TomastpPereira
- TissueMasker transform now returns mask instead of a list. #748
  - Fixes #732

### Bug Fixes and Other Changes

- Fixes `pixman` incompability error on Colab #601
- Removes `shapely.speedups`. The module no longer has any affect in Shapely >=2.0. #622
- Fixes errors in the slidegraph example notebook #608
- Fixes bugs in WSI Registration #645, #670, #693
- Fixes the situation where PatchExtractor.get_coords() can return patch coords which lie fully outside the bounds of a slide. #712
  - Fixes #710
- Fixes #738 raised by @xiachenrui

### Development related changes

- Replaces `flake8` and `isort` with `ruff` #625, #666
- Adds `mypy` checks to `root` and `utils` package. This will be rolled out in phases to other modules. #723
- Adds a module to detect file types using magic number/signatures #616
- Uses `poetry` for version updates instead of `bump2version`. #638
- Removes `setup.cfg` and uses `pyproject.toml` for project configurations.
- Reduces runtime for some unit tests e.g., #627, #630, #631, #629
- Reuses models and datasets in tests on GitHub actions by utilising cache #641, #644
- Set up parallel tests locally #671

**Full Changelog:** v1.4.0...v1.5.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants