-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/implement tile masks #4
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
Merged
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
2a9a59b
feat: add tiling run_id
vojtech-cifka bb6dc1b
feat: add configs for tile masks
vojtech-cifka 8b5e5ac
feat: implement tile mask pipeline
vojtech-cifka 04283b2
feat: add sumbission script for the tile masks
vojtech-cifka 654710a
fix: group by slides before the loop
vojtech-cifka 93eecb9
fix: update submission script to use GitHub repository URL
vojtech-cifka 682a5d3
perf: load only required columns from tiles parquet
vojtech-cifka cb29d16
perf: replace PIL tile_mask with vectorized numpy implementation
vojtech-cifka 7d2e475
fix: correct shape mismatch in vectorized tile outline drawing
vojtech-cifka b94b3f8
fix: clip out-of-bounds tile pixels in vectorized outline drawing
vojtech-cifka ef4e51e
perf: add configurable downsampling to tile mask generation
vojtech-cifka f96c636
style: drop comments with ambiguous unicode multiplication sign
vojtech-cifka 8504b84
chore: increase tile mask downsample to 8x to avoid OOM
vojtech-cifka 6ad1b62
fix: tile boundaries
vojtech-cifka d2d1e7c
fix: set default downsample to 8 in base config, remove redundant ove…
vojtech-cifka fc51a29
fix: use slide id as output filename to prevent cross-source collisions
vojtech-cifka bf6ae43
fix: mypy
vojtech-cifka b951514
Merge remote-tracking branch 'origin/master' into feature/implement-t…
vojtech-cifka b32c6b6
fix(tile_masks): resolve mypy type errors
vojtech-cifka de381f3
refactor(tile_masks): use rationai.masks.tile_mask and name outputs b…
vojtech-cifka 1f6f1d9
fix(tile_masks): use 'path' column for WSI filename
vojtech-cifka 979d4ea
fix(tile_masks): ceil canvas size to avoid clipping edge tiles
vojtech-cifka 97f3951
fix: use stride as the box size
vojtech-cifka a302ed7
fix: tile masks allignment
vojtech-cifka a0f1011
feat: use ScalarMaskBuilder
vojtech-cifka 1884819
feat: compute also tile coverage with heatmap
vojtech-cifka 760e13a
feat(tile_masks): add per-class coverage heatmaps and configure submi…
vojtech-cifka 9cff04d
fix: remove heatmap generation
vojtech-cifka c35e29d
fix: downsample the images
vojtech-cifka 3984431
fix: remove directory
vojtech-cifka 98f435b
fix: remove downsample and free memory bounds
vojtech-cifka 64501fc
feat: use numpy
vojtech-cifka 67f0b2d
fix: lower concurrency
vojtech-cifka fa9c8ec
feat: bring back ray
vojtech-cifka 4e24325
refactor: drop ray, add heatmaps
vojtech-cifka 432ad82
fix: bring back ray but dump the memory
vojtech-cifka 72f12a9
fix: generate the mask in place
vojtech-cifka 096119a
fix: raise concurrency
vojtech-cifka 79c343d
fix: lower concurrency to 4
vojtech-cifka 1eecb84
feat: lower concurrency to 1
vojtech-cifka 8d02a50
refactor: drop ray
vojtech-cifka 524d116
refactor: drop Ray from tile_masks, simplify slide iteration
vojtech-cifka b9c6aab
fix: lint
vojtech-cifka c659d15
Merge branch 'master' into feature/implement-tile-masks
vojtech-cifka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # @package _global_ | ||
|
|
||
| defaults: | ||
| - /data: dataset | ||
| - _self_ | ||
|
|
||
| slides_artifact_path: train_split/slides.parquet | ||
| tiles_artifact_path: train_split/tiles.parquet | ||
| tile_percentage_cols: | ||
| - tile_coverage_Nerve | ||
| - tile_coverage_Blood | ||
| - tile_coverage_Connective-Tissue | ||
| - tile_coverage_Fat | ||
| - tile_coverage_Epithelium | ||
| - tile_coverage_Muscle | ||
| - tile_coverage_Other |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # @package _global_ | ||
|
|
||
| slides_artifact_path: ??? | ||
| tiles_artifact_path: ??? | ||
| tile_percentage_cols: ??? | ||
|
|
||
| max_concurrent: 1 | ||
| mlflow_artifact_path: tile_masks | ||
|
|
||
| metadata: | ||
| run_name: Tile Masks ${dataset.name} | ||
| description: Tile mask generation |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| from pathlib import Path | ||
| from tempfile import TemporaryDirectory | ||
| from typing import Any, cast | ||
|
|
||
| import hydra | ||
| import mlflow | ||
| import pandas as pd | ||
| import pyvips | ||
| import torch | ||
| from omegaconf import DictConfig | ||
| from rationai.masks import tile_mask, write_big_tiff | ||
| from rationai.masks.mask_builders import ScalarMaskBuilder | ||
| from rationai.mlkit import autolog, with_cli_args | ||
| from rationai.mlkit.lightning.loggers import MLFlowLogger | ||
| from tqdm import tqdm | ||
|
|
||
|
|
||
| def process_slide( | ||
| slide: dict[str, Any], | ||
| output_dir: str, | ||
| tile_percentage_cols: list[str], | ||
| tiles_path: str, | ||
| ) -> None: | ||
| slide_tiles = pd.read_parquet( | ||
| tiles_path, | ||
| columns=["x", "y", *tile_percentage_cols], | ||
| filters=[("slide_id", "==", slide["id"])], | ||
| ) | ||
| filename = f"{Path(slide['path']).stem}.tiff" | ||
|
|
||
| for percentage_col in tile_percentage_cols: | ||
| builder = ScalarMaskBuilder( | ||
| save_dir=Path(output_dir, percentage_col), | ||
| filename=filename, | ||
| extent_x=slide["extent_x"], | ||
| extent_y=slide["extent_y"], | ||
| mpp_x=slide["mpp_x"], | ||
| mpp_y=slide["mpp_y"], | ||
| extent_tile=slide["tile_extent_x"], | ||
| stride=slide["stride_x"], | ||
| ) | ||
| builder.update( | ||
| data=torch.tensor(slide_tiles[percentage_col].values).unsqueeze(1), | ||
| xs=torch.tensor(slide_tiles["x"].values), | ||
| ys=torch.tensor(slide_tiles["y"].values), | ||
| ) | ||
| builder.save() | ||
|
|
||
| mask = tile_mask( | ||
| slide_tiles, | ||
| tile_extent=(slide["tile_extent_x"], slide["tile_extent_y"]), | ||
| size=(slide["extent_x"], slide["extent_y"]), | ||
| ) | ||
| width, height = mask.size | ||
| mask_bytes = mask.tobytes() | ||
| del mask | ||
|
|
||
| write_big_tiff( | ||
| image=pyvips.Image.new_from_memory( | ||
| data=mask_bytes, | ||
| width=width, | ||
| height=height, | ||
| bands=1, | ||
| format="uchar", | ||
| ), | ||
| path=Path(output_dir, "outlines", filename), | ||
| mpp_x=slide["mpp_x"], | ||
| mpp_y=slide["mpp_y"], | ||
| ) | ||
|
|
||
|
|
||
| @with_cli_args(["+preprocessing=tile_masks"]) | ||
| @hydra.main(config_path="../configs", config_name="preprocessing", version_base=None) | ||
| @autolog | ||
| def main(config: DictConfig, logger: MLFlowLogger) -> None: | ||
| tiling_run_id = config.dataset.mlflow_artifacts.tiling_run_id | ||
| tile_percentage_cols: list[str] = list(config.tile_percentage_cols) | ||
|
|
||
| slides_path = mlflow.artifacts.download_artifacts( | ||
| run_id=tiling_run_id, | ||
| artifact_path=config.slides_artifact_path, | ||
| ) | ||
| tiles_path = mlflow.artifacts.download_artifacts( | ||
| run_id=tiling_run_id, | ||
| artifact_path=config.tiles_artifact_path, | ||
| ) | ||
| slides = pd.read_parquet(slides_path) | ||
|
|
||
| items = cast("list[dict[str, Any]]", slides.to_dict(orient="records")) | ||
|
|
||
| with TemporaryDirectory() as output_dir: | ||
| Path(output_dir, "outlines").mkdir() | ||
| for slide in tqdm(items): | ||
| process_slide( | ||
| slide, | ||
| output_dir=output_dir, | ||
| tile_percentage_cols=tile_percentage_cols, | ||
| tiles_path=tiles_path, | ||
| ) | ||
|
|
||
| logger.log_artifacts( | ||
| local_dir=output_dir, artifact_path=config.mlflow_artifact_path | ||
| ) | ||
|
vojtech-cifka marked this conversation as resolved.
|
||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| from kube_jobs import storage, submit_job | ||
|
|
||
|
|
||
| submit_job( | ||
| job_name="tissue-classification-tile-masks", | ||
| username=..., | ||
| cpu=8, | ||
| memory="64Gi", | ||
| gpu=None, | ||
| public=False, | ||
| script=[ | ||
| "git clone https://github.com/RationAI/tissue-classification.git workdir", | ||
| "cd workdir", | ||
| "uv sync", | ||
| "uv run python -m preprocessing.tile_masks +experiment=...", | ||
| ], | ||
| storage=[storage.secure.DATA], | ||
| ) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.