Skip to content

Commit

Permalink
[FIX] do not panic on unreadable files (#95)
Browse files Browse the repository at this point in the history
* skip files that cannot be read by openslide

* add func test_issue_94

* reformat with black

* add torch and torchvision to install_requires
  • Loading branch information
kaczmarj authored Feb 13, 2023
1 parent 21c07d1 commit f1953f8
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 12 deletions.
8 changes: 5 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ classifiers =
packages = find:
python_requires = >= 3.7
install_requires =
# We require torch and torchvision but do not include them here because their
# installation varies by platform and hardware.
# See https://pytorch.org/get-started/locally/.
click>=8.0,<9
h5py
# OpenSlide and TIFF readers should handle all images we will encounter.
Expand All @@ -43,6 +40,11 @@ install_requires =
pillow
pyyaml
timm
# The installation fo torch and torchvision can differ by hardware. Users are
# advised to install torch and torchvision for their given hardware and then install
# wsinfer. See https://pytorch.org/get-started/locally/.
torch>=1.7
torchvision
tqdm

[options.extras_require]
Expand Down
33 changes: 32 additions & 1 deletion tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def tiff_image(tmp_path: Path) -> Path:


def test_cli_list(tmp_path: Path):

from wsinfer.cli.cli import cli

runner = CliRunner()
Expand Down Expand Up @@ -967,6 +966,7 @@ def test_jit_compile(model_name: str, weights_name: str):


def test_issue_89():
"""Do not fail if 'git' is not installed."""
from wsinfer.cli.infer import _get_info_for_save

w = get_model_weights("resnet34", "TCGA-BRCA-v1")
Expand All @@ -988,3 +988,34 @@ def test_issue_89():
assert d["runtime"]["git"] is None
finally:
os.environ["PATH"] = orig_path # reset path


def test_issue_94(tmp_path: Path, tiff_image: Path):
"""Gracefully handle unreadable slides."""
from wsinfer.cli.cli import cli

# We have a valid tiff in 'tiff_image.parent'. We put in an unreadable file too.
badpath = tiff_image.parent / "bad.svs"
badpath.touch()

runner = CliRunner()
results_dir = tmp_path / "inference"
result = runner.invoke(
cli,
[
"run",
"--wsi-dir",
str(tiff_image.parent),
"--model",
"resnet34",
"--weights",
"TCGA-BRCA-v1",
"--results-dir",
str(results_dir),
],
)
# Important part is that we run through all of the files, despite the unreadble
# file.
assert result.exit_code == 0
assert results_dir.joinpath("model-outputs").joinpath("purple.csv").exists()
assert not results_dir.joinpath("model-outputs").joinpath("bad.csv").exists()
10 changes: 7 additions & 3 deletions wsinfer/_patchlib/create_patches_fp.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ def seg_and_patch(
process_list=None,
patch_spacing=None,
):

slides = sorted(os.listdir(source))
slides = [slide for slide in slides if os.path.isfile(os.path.join(source, slide))]
if process_list is None:
Expand Down Expand Up @@ -160,7 +159,7 @@ def seg_and_patch(
df.to_csv(os.path.join(save_dir, "process_list_autogen.csv"), index=False)
idx = process_stack.index[i]
slide = process_stack.loc[idx, "slide_id"]
print("\n\nprogress: {:.2f}, {}/{}".format(i / total, i, total))
print("\n\nprogress: {:.1%}, {}/{}".format(i / total, i + 1, total))
print("processing {}".format(slide))

df.loc[idx, "process"] = 0
Expand All @@ -173,7 +172,12 @@ def seg_and_patch(

# Inialize WSI
full_path = os.path.join(source, slide)
WSI_object = WholeSlideImage(full_path)
# Some slide files might be malformed and unreadable. Skip them.
try:
WSI_object = WholeSlideImage(full_path)
except Exception:
print(f"Failed to load slide, skipping {full_path}")
continue

if use_default_params:
current_vis_params = vis_params.copy()
Expand Down
1 change: 0 additions & 1 deletion wsinfer/_patchlib/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ def generate_split(
all_val_ids.extend(val_ids)

if custom_test_ids is None: # sample test split

test_ids = np.random.choice(remaining_ids, test_num[c], replace=False)
remaining_ids = np.setdiff1d(remaining_ids, test_ids)
all_test_ids.extend(test_ids)
Expand Down
3 changes: 0 additions & 3 deletions wsinfer/_patchlib/wsi_core/WholeSlideImage.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

class WholeSlideImage(object):
def __init__(self, path):

"""
Args:
path (str): fullpath to WSI file
Expand Down Expand Up @@ -242,7 +241,6 @@ def visWSI(
seg_display=True,
annot_display=True,
):

downsample = self.level_downsamples[vis_level]
scale = [1 / downsample[0], 1 / downsample[1]]

Expand Down Expand Up @@ -456,7 +454,6 @@ def _getPatchGenerator(
count = 0
for y in range(start_y, stop_y, step_size_y):
for x in range(start_x, stop_x, step_size_x):

if not self.isInContours(
cont_check_fn,
(x, y),
Expand Down
1 change: 0 additions & 1 deletion wsinfer/_patchlib/wsi_core/wsi_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ def sample_rois(
top_left=None,
bot_right=None,
):

if len(scores.shape) == 2:
scores = scores.flatten()

Expand Down

0 comments on commit f1953f8

Please sign in to comment.