Skip to content

Commit

Permalink
Merge 983e824 into 036429b
Browse files Browse the repository at this point in the history
  • Loading branch information
alessiamarcolini committed Aug 6, 2020
2 parents 036429b + 983e824 commit c86524c
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 3 deletions.
11 changes: 11 additions & 0 deletions src/histolab/slide.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,17 @@ def name(self) -> str:
"""
return ntpath.basename(self._path).split(".")[0]

@lazyproperty
def processed_path(self) -> str:
"""Retrieve the path to store processed files generated from the slide.
Returns
-------
str
Path to store processed files generated from the slide
"""
return self._processed_path

def resampled_array(self, scale_factor: int = 32) -> np.array:
"""Retrieve the resampled array from the original slide
Expand Down
8 changes: 6 additions & 2 deletions src/histolab/tiler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from abc import abstractmethod
from typing import Tuple

Expand Down Expand Up @@ -178,7 +179,9 @@ def extract(self, slide: Slide):

for tiles_counter, (tile, tile_wsi_coords) in enumerate(grid_tiles):
tile_filename = self._tile_filename(tile_wsi_coords, tiles_counter)
tile.save(tile_filename)
root_folder = slide.processed_path
full_tile_path = os.path.join(root_folder, tile_filename)
tile.save(full_tile_path)
print(f"\t Tile {tiles_counter} saved: {tile_filename}")

print(f"{tiles_counter+1} Grid Tiles have been saved.")
Expand Down Expand Up @@ -410,7 +413,8 @@ def extract(self, slide: Slide):
tiles_counter = 0
for tiles_counter, (tile, tile_wsi_coords) in enumerate(random_tiles):
tile_filename = self._tile_filename(tile_wsi_coords, tiles_counter)
tile.save(tile_filename)
full_tile_path = os.path.join(slide.processed_path, tile_filename)
tile.save(full_tile_path)
print(f"\t Tile {tiles_counter} saved: {tile_filename}")
print(f"{tiles_counter+1} Random Tiles have been saved.")

Expand Down
91 changes: 90 additions & 1 deletion tests/unit/test_tiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,38 @@ def it_knows_its_box_mask_lvl(
box_mask_lvl.todense(), expected_box_mask_lvl.todense()
)

def it_can_extract_random_tiles(self, request, tmpdir):
tmp_path_ = tmpdir.mkdir("myslide")
image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240
image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG")
slide_path = os.path.join(tmp_path_, "mywsi.png")
slide = Slide(slide_path, os.path.join(tmp_path_, "processed"))
_random_tiles_generator = method_mock(
request, RandomTiler, "_random_tiles_generator"
)
coords = CoordinatePair(0, 10, 0, 10)
tile = Tile(image, coords)
_random_tiles_generator.return_value = [(tile, coords), (tile, coords)]
_tile_filename = method_mock(request, RandomTiler, "_tile_filename")
_tile_filename.side_effect = [
os.path.join(tmp_path_, "processed", f"tile_{i}_level2_0-10-0-10.png")
for i in range(2)
]
random_tiler = RandomTiler((10, 10), n_tiles=2, level=2)

random_tiler.extract(slide)

assert _tile_filename.call_args_list == [
call(random_tiler, coords, 0),
call(random_tiler, coords, 1),
]
assert os.path.exists(
os.path.join(tmp_path_, "processed", "tile_0_level2_0-10-0-10.png")
)
assert os.path.exists(
os.path.join(tmp_path_, "processed", "tile_1_level2_0-10-0-10.png")
)

# fixtures -------------------------------------------------------

@pytest.fixture(
Expand Down Expand Up @@ -515,7 +547,9 @@ def it_can_generate_grid_tiles(
generated_tiles = list(grid_tiler._grid_tiles_generator(slide))

_grid_coordinates_generator.assert_called_once_with(grid_tiler, slide)
_extract_tile.call_args_list == ([call(coords1, 0), call(coords2, 0)])
assert _extract_tile.call_args_list == (
[call(slide, coords1, 0), call(slide, coords2, 0)]
)
assert len(generated_tiles) == expected_n_tiles
if expected_n_tiles == 2:
assert generated_tiles == [(tile1, coords1), (tile2, coords2)]
Expand All @@ -524,6 +558,31 @@ def it_can_generate_grid_tiles(
if expected_n_tiles == 0:
assert generated_tiles == []

def but_with_wrong_coordinates(self, request, tmpdir):
tmp_path_ = tmpdir.mkdir("myslide")
image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240
image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG")
slide_path = os.path.join(tmp_path_, "mywsi.png")
slide = Slide(slide_path, "processed")
_has_enough_tissue = method_mock(request, Tile, "has_enough_tissue")
_has_enough_tissue.return_value = False
_grid_coordinates_generator = method_mock(
request, GridTiler, "_grid_coordinates_generator"
)
coords1 = CoordinatePair(600, 610, 600, 610)
coords2 = CoordinatePair(0, 10, 0, 10)
_grid_coordinates_generator.return_value = [coords1, coords2]
grid_tiler = GridTiler((10, 10), level=0, check_tissue=False)

generated_tiles = list(grid_tiler._grid_tiles_generator(slide))

_grid_coordinates_generator.assert_called_once_with(grid_tiler, slide)
assert len(generated_tiles) == 1
# generated_tiles[0][0] is a Tile object but we don't know what object it is
# because Slide.extract_tile is not mocked (for the exception to happen inside)
assert isinstance(generated_tiles[0][0], Tile)
assert generated_tiles[0][1] == coords2

def and_doesnt_raise_error_with_wrong_coordinates(self, request, tmpdir):
tmp_path_ = tmpdir.mkdir("myslide")
image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240
Expand All @@ -541,6 +600,36 @@ def and_doesnt_raise_error_with_wrong_coordinates(self, request, tmpdir):
assert len(generated_tiles) == 0
_grid_coordinates_generator.assert_called_once_with(grid_tiler, slide)

def it_can_extract_grid_tiles(self, request, tmpdir):
tmp_path_ = tmpdir.mkdir("myslide")
image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240
image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG")
slide_path = os.path.join(tmp_path_, "mywsi.png")
slide = Slide(slide_path, os.path.join(tmp_path_, "processed"))
_grid_tiles_generator = method_mock(request, GridTiler, "_grid_tiles_generator")
coords = CoordinatePair(0, 10, 0, 10)
tile = Tile(image, coords)
_grid_tiles_generator.return_value = [(tile, coords), (tile, coords)]
_tile_filename = method_mock(request, GridTiler, "_tile_filename")
_tile_filename.side_effect = [
os.path.join(tmp_path_, "processed", f"tile_{i}_level2_0-10-0-10.png")
for i in range(2)
]
grid_tiler = GridTiler((10, 10), level=2)

grid_tiler.extract(slide)

assert _tile_filename.call_args_list == [
call(grid_tiler, coords, 0),
call(grid_tiler, coords, 1),
]
assert os.path.exists(
os.path.join(tmp_path_, "processed", "tile_0_level2_0-10-0-10.png")
)
assert os.path.exists(
os.path.join(tmp_path_, "processed", "tile_1_level2_0-10-0-10.png")
)

# fixtures -------------------------------------------------------

@pytest.fixture(
Expand Down

0 comments on commit c86524c

Please sign in to comment.