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

ENH: update dockerfiles and add version to config spec #50

Merged
merged 4 commits into from
Dec 26, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
FROM pytorch/pytorch:1.12.1-cuda11.3-cudnn8-runtime
WORKDIR /opt/wsi-classification
WORKDIR /opt/wsinfer
COPY . .
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc git libopenslide0 \
Expand All @@ -15,4 +15,5 @@ RUN mkdir -p "$TORCH_HOME" \
&& chmod a+s "$TORCH_HOME"
WORKDIR /work
ENTRYPOINT ["wsinfer"]
CMD ["--help"]
LABEL maintainer="Jakub Kaczmarzyk <jakub.kaczmarzyk@stonybrookmedicine.edu>"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Original H&E | Heatmap of Tumor Probability
🔥 🚀 Blazingly fast pipeline to run patch-based classification models on whole slide images.

![Continuous Integration](https://github.com/kaczmarj/patch-classification-pipeline/actions/workflows/ci.yml/badge.svg)
![Supported Python versions](https://img.shields.io/pypi/pyversions/wsinfer)
![Version on PyPI](https://img.shields.io/pypi/v/wsinfer.svg)

# Table of contents

Expand Down Expand Up @@ -268,6 +270,8 @@ Define a new model with a YAML configuration file. Please see the example below
an overview of the specification.

```yaml
# The version of the spec. At this time, only "1.0" is valid. (str)
version: "1.0"
# Models are referenced by the pair of (architecture, weights), so this pair must be unique.
# The name of the architecture. We use timm to supply hundreds or network architectures,
# so the name can be one of those models. If the architecture is not provided in timm,
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/tils.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Note about versioning: We should not use the 'latest' tag because it is a moving
# target. We should prefer using a versioned release of the wsinfer pipeline.
FROM kaczmarj/patch-classification-pipeline:v0.2.1
FROM kaczmarj/wsinfer:v0.2.1

# The CLI will use these env vars for model and weights.
ENV WSINFER_MODEL="inceptionv4"
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/tumor-brca.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Note about versioning: We should not use the 'latest' tag because it is a moving
# target. We should prefer using a versioned release of the wsinfer pipeline.
FROM kaczmarj/patch-classification-pipeline:v0.2.1
FROM kaczmarj/wsinfer:v0.2.1

# The CLI will use these env vars for model and weights.
ENV WSINFER_MODEL="resnet34"
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/tumor-luad.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Note about versioning: We should not use the 'latest' tag because it is a moving
# target. We should prefer using a versioned release of the wsinfer pipeline.
FROM kaczmarj/patch-classification-pipeline:v0.2.1
FROM kaczmarj/wsinfer:v0.2.1

# The CLI will use these env vars for model and weights.
ENV WSINFER_MODEL="resnet34"
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/tumor-paad.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Note about versioning: We should not use the 'latest' tag because it is a moving
# target. We should prefer using a versioned release of the wsinfer pipeline.
FROM kaczmarj/patch-classification-pipeline:v0.2.1
FROM kaczmarj/wsinfer:v0.2.1

# The CLI will use these env vars for model and weights.
ENV WSINFER_MODEL="resnet34_preact"
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/tumor-prad.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Note about versioning: We should not use the 'latest' tag because it is a moving
# target. We should prefer using a versioned release of the wsinfer pipeline.
FROM kaczmarj/patch-classification-pipeline:v0.2.1
FROM kaczmarj/wsinfer:v0.2.1

# The CLI will use these env vars for model and weights.
ENV WSINFER_MODEL="resnet34"
Expand Down
24 changes: 12 additions & 12 deletions scripts/build_docker_images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,33 @@ fi
version=$1

# Main image.
docker build -t kaczmarj/patch-classification-pipeline:$version .
docker build -t kaczmarj/wsinfer:$version .

# TILs
docker build -t kaczmarj/patch-classification-pipeline:$version-tils - < dockerfiles/tils.dockerfile
docker build -t kaczmarj/wsinfer:$version-tils - < dockerfiles/tils.dockerfile

# Tumor BRCA
docker build -t kaczmarj/patch-classification-pipeline:$version-tumor-brca - < dockerfiles/tumor-brca.dockerfile
docker build -t kaczmarj/wsinfer:$version-tumor-brca - < dockerfiles/tumor-brca.dockerfile

# Tumor LUAD
docker build -t kaczmarj/patch-classification-pipeline:$version-tumor-luad - < dockerfiles/tumor-luad.dockerfile
docker build -t kaczmarj/wsinfer:$version-tumor-luad - < dockerfiles/tumor-luad.dockerfile

# Tumor PAAD
docker build -t kaczmarj/patch-classification-pipeline:$version-tumor-paad - < dockerfiles/tumor-paad.dockerfile
docker build -t kaczmarj/wsinfer:$version-tumor-paad - < dockerfiles/tumor-paad.dockerfile

# Tumor PRAD
docker build -t kaczmarj/patch-classification-pipeline:$version-tumor-prad - < dockerfiles/tumor-prad.dockerfile
docker build -t kaczmarj/wsinfer:$version-tumor-prad - < dockerfiles/tumor-prad.dockerfile

# Push images.
push_images="${2:-0}"
if [ $push_images -eq 0 ]; then
echo "Not pushing images. Pass a 1 to the script to push images."
else
echo "Pushing images."
docker push kaczmarj/patch-classification-pipeline:$version
docker push kaczmarj/patch-classification-pipeline:$version-tils
docker push kaczmarj/patch-classification-pipeline:$version-tumor-brca
docker push kaczmarj/patch-classification-pipeline:$version-tumor-luad
docker push kaczmarj/patch-classification-pipeline:$version-tumor-paad
docker push kaczmarj/patch-classification-pipeline:$version-tumor-prad
docker push kaczmarj/wsinfer:$version
docker push kaczmarj/wsinfer:$version-tils
docker push kaczmarj/wsinfer:$version-tumor-brca
docker push kaczmarj/wsinfer:$version-tumor-luad
docker push kaczmarj/wsinfer:$version-tumor-paad
docker push kaczmarj/wsinfer:$version-tumor-prad
fi
57 changes: 57 additions & 0 deletions tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def test_cli_list(tmp_path: Path):
config_root_single.mkdir()
configs = [
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -73,6 +74,7 @@ def test_cli_list(tmp_path: Path):
class_names=["tumor"],
),
dict(
version="1.0",
name="foo2",
architecture="resnet34",
url="foo",
Expand Down Expand Up @@ -412,6 +414,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
dict(name="foo", architecture="resnet34"),
# Missing url
dict(
version="1.0",
name="foo",
architecture="resnet34",
# url="foo",
Expand All @@ -424,6 +427,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# missing url_file_name when url is given
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -436,6 +440,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# url and file used together
dict(
version="1.0",
name="foo",
architecture="resnet34",
file=__file__,
Expand All @@ -449,6 +454,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# nonexistent file
dict(
version="1.0",
name="foo",
architecture="resnet34",
file="path/to/fake/file",
Expand All @@ -462,6 +468,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# num_classes missing
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -474,6 +481,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# num classes not equal to len of class names
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -486,6 +494,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform missing
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -498,6 +507,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.resize_size missing
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -510,6 +520,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.mean missing
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -522,6 +533,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.std missing
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -534,6 +546,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.resize_size non int
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -546,6 +559,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.resize_size non int
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -560,6 +574,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.mean not a list of three floats
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -572,6 +587,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.mean not a list of three floats
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -584,6 +600,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.mean not a list of three floats
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -596,6 +613,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.std not a list of three floats
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -608,6 +626,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.std not a list of three floats
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -620,6 +639,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# transform.std not a list of three floats
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -632,6 +652,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid patch_size_pixels -- list
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -644,6 +665,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid patch_size_pixels -- float
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -656,6 +678,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid patch_size_pixels -- negative
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -668,6 +691,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid spacing_um_px -- zero
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -680,6 +704,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid spacing_um_px -- list
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -692,6 +717,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid class_names -- str
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -704,6 +730,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid class_names -- len not equal to num_classes
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -716,6 +743,7 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
),
# invalid class_names -- not list of str
dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -726,6 +754,33 @@ def test_cli_run_from_config(tiff_image: Path, tmp_path: Path):
spacing_um_px=0.25,
class_names=[1],
),
# unknown key
dict(
fakekey="foobar",
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
url_file_name="foo",
num_classes=1,
transform=dict(resize_size=299, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
patch_size_pixels=350,
spacing_um_px=0.25,
class_names=["foo"],
),
# version != '1.0'
dict(
version="2.0",
name="foo",
architecture="resnet34",
url="foo",
url_file_name="foo",
num_classes=1,
transform=dict(resize_size=299, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
patch_size_pixels=350,
spacing_um_px=0.25,
class_names=["foo"],
),
],
)
def test_invalid_modeldefs(modeldef, tmp_path: Path):
Expand All @@ -744,6 +799,7 @@ def test_model_registration(tmp_path: Path):

# Test that registering duplicate weights will error.
d = dict(
version="1.0",
name="foo",
architecture="resnet34",
url="foo",
Expand All @@ -768,6 +824,7 @@ def test_model_registration(tmp_path: Path):
path = tmp_path / "configs" / "foobar.yaml"
path.parent.mkdir()
d = dict(
version="1.0",
name="foo2",
architecture="resnet34",
url="foo",
Expand Down
Loading