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

Feat/qupath #209

Merged
merged 14 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,9 @@ cython_debug/
# Version
/wsinfer/_version.py

#QuPath Project
/QuPathProject
*.backup

# Extras
.DS_Store
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ dev = [
"types-Pillow",
"types-tqdm",
"Flake8-pyproject",
"paquo"
kaczmarj marked this conversation as resolved.
Show resolved Hide resolved
]
docs = [
"pydata-sphinx-theme",
Expand All @@ -80,6 +81,7 @@ docs = [
"sphinx-copybutton",
]
openslide = ["openslide-python"]
qupath = ["paquo"]

[project.urls]
Homepage = "https://wsinfer.readthedocs.io"
Expand Down
11 changes: 11 additions & 0 deletions wsinfer/cli/infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from ..modellib.run_inference import run_inference
from ..patchlib import segment_and_patch_directory_of_slides
from ..write_geojson import write_geojsons
from ..qupath import make_qupath_project


def _num_cpus() -> int:
Expand Down Expand Up @@ -252,6 +253,13 @@ def get_stdout(args: list[str]) -> str:
help="JIT-compile the model and apply inference optimizations. This imposes a"
" startup cost but may improve performance overall.",
)
@click.option(
"--qupath",
is_flag=True,
default=False,
show_default=True,
help="Create a QuPath project containing the inference results",
)
def run(
ctx: click.Context,
*,
Expand All @@ -263,6 +271,7 @@ def run(
batch_size: int,
num_workers: int = 0,
speedup: bool = False,
qupath: bool = False,
) -> None:
"""Run model inference on a directory of whole slide images.

Expand Down Expand Up @@ -378,3 +387,5 @@ def run(

csvs = list((results_dir / "model-outputs-csv").glob("*.csv"))
write_geojsons(csvs, results_dir, num_workers)
if qupath:
make_qupath_project(wsi_dir, results_dir)
62 changes: 62 additions & 0 deletions wsinfer/qupath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from __future__ import annotations

import sys
import json
from pathlib import Path


def add_image_and_geojson(
qupath_proj: QuPathProject, *, image_path: Path | str, geojson_path: Path | str
) -> None:
with open(geojson_path) as f:
# FIXME: check that a 'features' key is present and raise a useful error if not
geojson_features = json.load(f)["features"]

entry = qupath_proj.add_image(image_path)
# FIXME: test that the 'load_geojson' function exists. If not, raise a useful error
try:
entry.hierarchy.load_geojson(geojson_features) # type: ignore
except Exception as e:
print(f"Failed to run load_geojson function with error:: {e}")


def make_qupath_project(wsi_dir, results_dir):
try:
from paquo.projects import QuPathProject
except Exception as e:
print(
f"""Cannot find QuPath with error:: {e}.
QuPath is required to use this functionality but it cannot be found.
If QuPath is installed, please use define the environment variable
PAQUO_QUPATH_DIR with the location of the QuPath installation.
If QuPath is not installed, please install it from https://qupath.github.io/."""
)
sys.exit(1)

print("Found QuPath successfully!")
kaczmarj marked this conversation as resolved.
Show resolved Hide resolved
QUPATH_PROJECT_DIRECTORY = results_dir / "model-outputs-qupath"

csv_files = list((results_dir / "model-outputs-csv").glob("*.csv"))
slides_and_geojsons = []

for csv_file in csv_files:
file_name = csv_file.stem

json_file = results_dir / "model-outputs-geojson" / (file_name + ".json")
image_file = wsi_dir / (file_name + ".svs")

if json_file.exists() and image_file.exists():
matching_pair = (image_file, json_file)
slides_and_geojsons.append(matching_pair)
else:
print(f"Skipping CSV: {csv_file.name} (No corresponding JSON)")

with QuPathProject(QUPATH_PROJECT_DIRECTORY, mode="w") as qp:
for image_path, geojson_path in slides_and_geojsons:
try:
add_image_and_geojson(
qp, image_path=image_path, geojson_path=geojson_path
)
except Exception as e:
print(f"Failed to add image/geojson with error:: {e}")
print("Successfully created QuPath Project!")
Loading