def but_it_raises_exception_when_number_of_regions_is_wrong( self, n, expected_message): binary_mask = BiggestTissueBoxMask() with pytest.raises(ValueError) as e: binary_mask._regions([], n) assert str(e.value) == expected_message
def it_can_construct_big_tissue_box_mask(self, wsi, expected_array): slide = Slide(wsi, os.path.join(wsi, "processed")) expected_array = load_expectation(expected_array, type_="npy") biggest_tissue_box = BiggestTissueBoxMask() np.testing.assert_array_almost_equal(biggest_tissue_box(slide), expected_array)
def it_can_calculate_scores(self, request): slide = instance_mock(request, Slide) coords = CP(0, 10, 0, 10) image = PILIMG.RGB_RANDOM_COLOR_500X500 tile = Tile(image, coords) _tiles_generator = method_mock(request, GridTiler, "_tiles_generator") # it needs to be a generator _tiles_generator.return_value = ((tile, coords) for i in range(3)) _scorer = instance_mock(request, RandomScorer) _scorer.side_effect = [0.5, 0.7] score_tiler = ScoreTiler(_scorer, (10, 10), 2, 0) binary_mask = BiggestTissueBoxMask() scores = score_tiler._scores(slide, binary_mask) assert _tiles_generator.call_args_list == [ call(score_tiler, slide, binary_mask), call(score_tiler, slide, binary_mask), ] assert _scorer.call_args_list == [call(tile), call(tile)] assert type(scores) == list assert type(scores[0]) == tuple assert type(scores[0][0]) == float assert type(scores[0][1]) == CP assert scores == [(0.5, coords), (0.7, coords)]
def but_it_raises_error_with_negative_n_tiles_value(self, request, tmpdir): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_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")) _scores = method_mock(request, ScoreTiler, "_scores") coords = CP(0, 10, 0, 10) _scores.return_value = [ (0.7, coords), (0.5, coords), (0.2, coords), (0.8, coords), (0.1, coords), ] _scorer = instance_mock(request, RandomScorer) score_tiler = ScoreTiler(_scorer, (10, 10), -1, 0) binary_mask = BiggestTissueBoxMask() with pytest.raises(ValueError) as err: score_tiler.extract(slide, binary_mask) assert isinstance(err.value, ValueError) assert str(err.value) == "'n_tiles' cannot be negative (-1)" _scores.assert_called_once_with(score_tiler, slide, binary_mask)
def it_can_generate_random_coordinates(self, request, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) _box_mask_thumb = method_mock(request, BiggestTissueBoxMask, "__call__") _box_mask_thumb.return_value = NpArrayMock.ONES_500X500_BOOL _tile_size = property_mock(request, RandomTiler, "tile_size") _tile_size.return_value = (128, 128) _random_choice_true_mask2d = function_mock( request, "histolab.tiler.random_choice_true_mask2d") _random_choice_true_mask2d.return_value = (0, 0) _scale_coordinates = function_mock(request, "histolab.tiler.scale_coordinates") random_tiler = RandomTiler((128, 128), 10, 0) binary_mask = BiggestTissueBoxMask() random_tiler._random_tile_coordinates(slide, binary_mask) _box_mask_thumb.assert_called_once_with(binary_mask, slide) _tile_size.assert_has_calls([call((128, 128))]) _random_choice_true_mask2d.assert_called_once_with( NpArrayMock.ONES_500X500_BOOL) _scale_coordinates.assert_called_once_with( reference_coords=CP(x_ul=0, y_ul=0, x_br=128, y_br=128), reference_size=(500, 500), target_size=(500, 500), )
def it_knows_its_mask( self, request, tmpdir, RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, RemoveSmallHoles_, RemoveSmallObjects_, ): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) regions = [ Region(index=0, area=33, bbox=(0, 0, 2, 2), center=(0.5, 0.5), coords=None) ] main_tissue_areas_mask_filters_ = property_mock( request, _SlideFiltersComposition, "tissue_mask_filters") main_tissue_areas_mask_filters_.return_value = Compose([ RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, RemoveSmallHoles_, RemoveSmallObjects_, ]) regions_from_binary_mask = function_mock( request, "histolab.masks.regions_from_binary_mask") regions_from_binary_mask.return_value = regions biggest_regions_ = method_mock(request, BiggestTissueBoxMask, "_regions", autospec=False) biggest_regions_.return_value = regions region_coordinates_ = function_mock( request, "histolab.masks.region_coordinates") region_coordinates_.return_values = CP(0, 0, 2, 2) rectangle_to_mask_ = function_mock(request, "histolab.util.rectangle_to_mask") rectangle_to_mask_((1000, 1000), CP(0, 0, 2, 2)).return_value = [ [True, True], [False, True], ] biggest_mask_tissue_box = BiggestTissueBoxMask() binary_mask = biggest_mask_tissue_box(slide) np.testing.assert_almost_equal(binary_mask, np.zeros((500, 500))) region_coordinates_.assert_called_once_with(regions[0]) biggest_regions_.assert_called_once_with(regions, n=1) rectangle_to_mask_.assert_called_once_with((1000, 1000), CP(x_ul=0, y_ul=0, x_br=2, y_br=2))
def or_it_has_wrong_seed(self, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGB_RANDOM_COLOR_500X500) random_tiler = RandomTiler((128, 128), 10, 0, seed=-1) binary_mask = BiggestTissueBoxMask() with pytest.raises(ValueError) as err: random_tiler.extract(slide, binary_mask) assert isinstance(err.value, ValueError) assert str(err.value) == "Seed must be between 0 and 2**32 - 1"
def or_it_has_not_available_level_value(self, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGB_RANDOM_COLOR_500X500) binary_mask = BiggestTissueBoxMask() grid_tiler = GridTiler((128, 128), 3) with pytest.raises(LevelError) as err: grid_tiler.extract(slide, binary_mask) assert isinstance(err.value, LevelError) assert str(err.value ) == "Level 3 not available. Number of available levels: 1"
def or_it_raises_levelerror_if_has_not_available_level_value(self, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGB_RANDOM_COLOR_500X500) score_tiler = ScoreTiler(None, (10, 10), 2, 3) binary_mask = BiggestTissueBoxMask() with pytest.raises(LevelError) as err: score_tiler.extract(slide, binary_mask) assert isinstance(err.value, LevelError) assert str(err.value ) == "Level 3 not available. Number of available levels: 1"
def it_can_extract_score_tiles_and_save_report(self, request, tmpdir): _extract_tile = method_mock(request, Slide, "extract_tile") tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_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")) coords = CP(0, 10, 0, 10) _tiles_generator = method_mock( request, ScoreTiler, "_tiles_generator", ) tile = Tile(image, coords) _extract_tile.return_value = tile _tiles_generator.return_value = ( [(0.8, coords), (0.7, coords)], [(0.8, coords), (0.7, coords)], ) _tile_filename = method_mock(request, GridTiler, "_tile_filename") _tile_filename.side_effect = [ f"tile_{i}_level2_0-10-0-10.png" for i in range(2) ] _save_report = method_mock(request, ScoreTiler, "_save_report", autospec=False) random_scorer = RandomScorer() score_tiler = ScoreTiler(random_scorer, (10, 10), 2, 0) binary_mask = BiggestTissueBoxMask() score_tiler.extract(slide, binary_mask, "report.csv") assert _extract_tile.call_args_list == [ call(slide, coords, 0, (10, 10)), call(slide, coords, 0, (10, 10)), ] _tiles_generator.assert_called_with(score_tiler, slide, binary_mask) assert _tile_filename.call_args_list == [ call(score_tiler, coords, 0), call(score_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")) _save_report.assert_called_once_with( "report.csv", [(0.8, coords), (0.7, coords)], [(0.8, coords), (0.7, coords)], [f"tile_{i}_level2_0-10-0-10.png" for i in range(2)], )
def and_doesnt_raise_error_with_wrong_coordinates(self, request, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) coords = CP(5800, 6000, 5800, 6000) _grid_coordinates_generator = method_mock( request, GridTiler, "_grid_coordinates_generator") _grid_coordinates_generator.return_value = [coords] grid_tiler = GridTiler((10, 10)) binary_mask = BiggestTissueBoxMask() generated_tiles = list(grid_tiler._tiles_generator(slide, binary_mask)) assert len(generated_tiles) == 0 _grid_coordinates_generator.assert_called_once_with( grid_tiler, slide, binary_mask)
def test_extract_tiles_respecting_the_given_tile_size( self, tmpdir, tile_size, level, seed, n_tiles): processed_path = os.path.join(tmpdir, "processed") slide = Slide(SVS.TCGA_CR_7395_01A_01_TS1, processed_path) random_tiles_extractor = RandomTiler( tile_size=tile_size, n_tiles=n_tiles, level=level, seed=seed, check_tissue=True, ) binary_mask = BiggestTissueBoxMask() random_tiles_extractor.extract(slide, binary_mask) for tile in os.listdir(processed_path): assert Image.open(os.path.join(processed_path, tile)).size == tile_size
def but_it_raises_runtimeerror_if_no_tiles_are_extracted(self, request): slide = instance_mock(request, Slide) _tiles_generator = method_mock(request, GridTiler, "_tiles_generator") # it needs to be an empty generator _tiles_generator.return_value = (n for n in []) score_tiler = ScoreTiler(None, (10, 10), 2, 0) binary_mask = BiggestTissueBoxMask() with pytest.raises(RuntimeError) as err: score_tiler._scores(slide, binary_mask) _tiles_generator.assert_called_once_with(score_tiler, slide, binary_mask) assert isinstance(err.value, RuntimeError) assert ( str(err.value) == "No tiles have been generated. This could happen if `check_tissue=True`" )
def it_can_generate_random_tiles_even_when_coords_are_not_valid( self, tmpdir, _random_tile_coordinates): random_tiler = RandomTiler((10, 10), 1, level=0, max_iter=1, check_tissue=False) _random_tile_coordinates.side_effect = [ CP(-1, -1, -1, -1), CP(0, 10, 0, 10) ] slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) binary_mask = BiggestTissueBoxMask() generated_tiles = list( random_tiler._tiles_generator(slide, binary_mask)) assert generated_tiles[0][1] == CP(0, 10, 0, 10) assert isinstance(generated_tiles[0][0], Tile)
def it_can_extract_grid_tiles(self, request, tmpdir, caplog): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_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")) _tiles_generator = method_mock(request, GridTiler, "_tiles_generator") coords = CP(0, 10, 0, 10) tile = Tile(image, coords) _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) ] _has_valid_tile_size = method_mock(request, GridTiler, "_has_valid_tile_size") _has_valid_tile_size.return_value = True grid_tiler = GridTiler((10, 10), level=0) binary_mask = BiggestTissueBoxMask() with caplog.at_level(logging.ERROR): grid_tiler.extract(slide, binary_mask) assert caplog.text == "" 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")) _has_valid_tile_size.assert_called_once_with(grid_tiler, slide) _tiles_generator.assert_called_once_with(grid_tiler, slide, binary_mask) assert _tile_filename.call_args_list == [ call(grid_tiler, coords, 0), call(grid_tiler, coords, 1), ]
def it_can_generate_grid_tiles_with_check_tissue( self, request, tmpdir, tile1, tile2, has_enough_tissue, expected_n_tiles, ): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) _extract_tile = method_mock(request, Slide, "extract_tile") _has_enough_tissue = method_mock(request, Tile, "has_enough_tissue") _has_enough_tissue.side_effect = has_enough_tissue _grid_coordinates_generator = method_mock( request, GridTiler, "_grid_coordinates_generator") _grid_coordinates_generator.return_value = [ CP(0, 10, 0, 10), CP(0, 10, 0, 10) ] _extract_tile.side_effect = [tile1, tile2] grid_tiler = GridTiler((10, 10), level=0, check_tissue=True, tissue_percent=60) tiles = [tile1, tile2] binary_mask = BiggestTissueBoxMask() generated_tiles = list(grid_tiler._tiles_generator(slide, binary_mask)) _grid_coordinates_generator.assert_called_once_with( grid_tiler, slide, binary_mask) assert _extract_tile.call_args_list == ([ call(slide, CP(0, 10, 0, 10), 0, (10, 10)), call(slide, CP(0, 10, 0, 10), 0, (10, 10)), ]) assert _has_enough_tissue.call_args_list == [ call(tile1, 60), call(tile2, 60) ] assert len(generated_tiles) == expected_n_tiles for i, tile in enumerate(generated_tiles): assert tile[0] == tiles[i]
def it_locates_tiles_on_the_slide(self, request, fixture_slide, level, check_tissue, expectation, tmpdir): slide = Slide(fixture_slide, os.path.join(tmpdir, "processed")) grid_tiles_extractor = GridTiler( tile_size=(512, 512), level=level, check_tissue=check_tissue, ) expected_img = load_expectation(expectation, type_="png") mask = BiggestTissueBoxMask() tiles_location_img = grid_tiles_extractor.locate_tiles(slide, mask, scale_factor=10) # --- Expanding test report with actual and expected images --- expand_tests_report(request, expected=expected_img, actual=tiles_location_img) np.testing.assert_array_almost_equal(tiles_location_img, expected_img)
def it_can_calculate_highest_score_tiles(self, request, n_tiles, expected_value): slide = instance_mock(request, Slide) _scores = method_mock(request, ScoreTiler, "_scores") coords = CP(0, 10, 0, 10) _scores.return_value = [ (0.7, coords), (0.5, coords), (0.2, coords), (0.8, coords), (0.1, coords), ] _scorer = instance_mock(request, RandomScorer) score_tiler = ScoreTiler(_scorer, (10, 10), n_tiles, 0) binary_mask = BiggestTissueBoxMask() highest_score_tiles = score_tiler._tiles_generator(slide, binary_mask) _scores.assert_called_once_with(score_tiler, slide, binary_mask) assert highest_score_tiles == expected_value
def but_it_raises_tilesizeerror_if_tilesize_larger_than_slidesize( self, request, tmpdir, image, size): tmp_path_ = tmpdir.mkdir("myslide") 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")) _has_valid_tile_size = method_mock(request, GridTiler, "_has_valid_tile_size") _has_valid_tile_size.return_value = False grid_tiler = GridTiler((50, 52), level=0) binary_mask = BiggestTissueBoxMask() with pytest.raises(TileSizeError) as err: grid_tiler.extract(slide, binary_mask) assert isinstance(err.value, TileSizeError) assert (str( err.value ) == f"Tile size (50, 52) is larger than slide size {size} at level 0") _has_valid_tile_size.assert_called_once_with(grid_tiler, slide)
def it_can_generate_random_tiles_with_check_tissue( self, request, tmpdir, tile1, tile2, has_enough_tissue, max_iter, expected_value, _random_tile_coordinates, ): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) _extract_tile = method_mock(request, Slide, "extract_tile") _has_enough_tissue = method_mock(request, Tile, "has_enough_tissue") _has_enough_tissue.side_effect = has_enough_tissue * (max_iter // 2) binary_mask = BiggestTissueBoxMask() tiles = [tile1, tile2] _extract_tile.side_effect = tiles * (max_iter // 2) random_tiler = RandomTiler( (10, 10), 2, level=0, max_iter=max_iter, check_tissue=True, tissue_percent=60, ) generated_tiles = list( random_tiler._tiles_generator(slide, binary_mask)) _random_tile_coordinates.assert_called_with(random_tiler, slide, binary_mask) assert _has_enough_tissue.call_args_list == [ call(tile1, 60), call(tile2, 60) ] assert _random_tile_coordinates.call_count <= random_tiler.max_iter assert len(generated_tiles) == expected_value for i, tile in enumerate(generated_tiles): assert tile[0] == tiles[i]
def it_can_extract_random_tiles(self, request, tmpdir, caplog): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_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")) _tiles_generator = method_mock(request, RandomTiler, "_tiles_generator") coords = CP(0, 10, 0, 10) tile = Tile(image, coords) _tiles_generator.return_value = [(tile, coords), (tile, coords)] _tile_filename = method_mock(request, RandomTiler, "_tile_filename") _tile_filename.side_effect = [ f"tile_{i}_level2_0-10-0-10.png" for i in range(2) ] _has_valid_tile_size = method_mock(request, RandomTiler, "_has_valid_tile_size") _has_valid_tile_size.return_value = True random_tiler = RandomTiler((10, 10), n_tiles=2, level=0) binary_mask = BiggestTissueBoxMask() with caplog.at_level(logging.INFO): random_tiler.extract(slide, binary_mask) assert re.sub(r":+\d{3}", "", caplog.text).splitlines() == [ "INFO tiler:tiler.py \t Tile 0 saved: tile_0_level2_0-10-0-10.png", "INFO tiler:tiler.py \t Tile 1 saved: tile_1_level2_0-10-0-10.png", "INFO tiler:tiler.py 2 Random Tiles have been saved.", ] 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")) _has_valid_tile_size.assert_called_once_with(random_tiler, slide)
def it_can_generate_random_tiles_with_check_tissue_but_tiles_without_tissue( self, request, tmpdir, _random_tile_coordinates, ): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) _extract_tile = method_mock(request, Slide, "extract_tile") _has_enough_tissue = method_mock(request, Tile, "has_enough_tissue") _has_enough_tissue.side_effect = [False, False] * 5 binary_mask = BiggestTissueBoxMask() tiles = [ Tile(PILIMG.RGBA_COLOR_500X500_155_249_240, CP(0, 10, 0, 10)), Tile(PILIMG.RGBA_COLOR_500X500_155_249_240, CP(0, 10, 0, 10)), ] _extract_tile.side_effect = tiles * 5 random_tiler = RandomTiler( (10, 10), 2, level=0, max_iter=10, check_tissue=True, tissue_percent=60, ) generated_tiles = list( random_tiler._tiles_generator(slide, binary_mask)) _random_tile_coordinates.assert_called_with(random_tiler, slide, binary_mask) assert (_has_enough_tissue.call_args_list == [ call(tiles[0], 60), call(tiles[1], 60), ] * 5) assert _random_tile_coordinates.call_count <= random_tiler.max_iter assert len(generated_tiles) == 0 for i, tile in enumerate(generated_tiles): assert tile[0] == tiles[i]
def but_with_wrong_coordinates(self, request, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) _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 = CP(600, 610, 600, 610) coords2 = CP(0, 10, 0, 10) _grid_coordinates_generator.return_value = [coords1, coords2] grid_tiler = GridTiler((10, 10), level=0, check_tissue=False) binary_mask = BiggestTissueBoxMask() generated_tiles = list(grid_tiler._tiles_generator(slide, binary_mask)) _grid_coordinates_generator.assert_called_once_with( grid_tiler, slide, binary_mask) 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
import pytest from histolab.masks import BiggestTissueBoxMask, TissueMask from histolab.slide import Slide from histolab.util import random_choice_true_mask2d from ..fixtures import SVS, TIFF @pytest.mark.parametrize( "fixture_slide, binary_mask", [ (TIFF.KIDNEY_48_5, BiggestTissueBoxMask()), (TIFF.KIDNEY_48_5, TissueMask()), (SVS.TCGA_CR_7395_01A_01_TS1, BiggestTissueBoxMask()), (SVS.TCGA_CR_7395_01A_01_TS1, TissueMask()), ], ) def test_random_choice_true_mask2d_find_right_coordinates(fixture_slide, binary_mask): slide = Slide(fixture_slide, "") bbox = binary_mask(slide) x, y = random_choice_true_mask2d(bbox) assert bbox[x, y]
class DescribeRandomTiler: @pytest.mark.parametrize( "fixture_slide, binary_mask, tile_size, level, check_tissue, expectation", [ ( SVS.CMU_1_SMALL_REGION, BiggestTissueBoxMask(), (512, 512), 0, False, "tiles-location-images/cmu-1-small-region-tl-random-BTB-false-512x512", ), ( SVS.TCGA_CR_7395_01A_01_TS1, TissueMask(), (512, 512), -2, False, "tiles-location-images/tcga-cr-7395-01a-01-ts1-tl-random-TM-f-512x512", ), ( SVS.TCGA_CR_7395_01A_01_TS1, BiggestTissueBoxMask(), (512, 530), 0, False, "tiles-location-images/tcga-cr-7395-01a-01-ts1-tl-random-BTB-f-512x530", ), ( SVS.CMU_1_SMALL_REGION, TissueMask(), (512, 530), 0, True, "tiles-location-images/cmu-1-small-region-tl-random-TM-true-512x530", ), ( SVS.TCGA_CR_7395_01A_01_TS1, TissueMask(), (128, 128), 0, True, "tiles-location-images/tcga-cr-7395-01a-01-ts1-tl-random-TM-t-128x128", ), ( TIFF.KIDNEY_48_5, TissueMask(), (10, 10), 0, True, "tiles-location-images/kidney-48-5-random-TM-true-10x10", ), ( TIFF.KIDNEY_48_5, BiggestTissueBoxMask(), (20, 20), 0, False, "tiles-location-images/kidney-48-5-random-TM-false-20x20", ), ], ) def it_locates_tiles_on_the_slide( self, request, fixture_slide, binary_mask, tile_size, level, check_tissue, expectation, ): slide = Slide(fixture_slide, "") random_tiles_extractor = RandomTiler( tile_size=tile_size, n_tiles=2, level=level, seed=42, check_tissue=check_tissue, ) expected_img = load_expectation( expectation, type_="png", ) tiles_location_img = random_tiles_extractor.locate_tiles( slide, binary_mask, scale_factor=10) # --- Expanding test report with actual and expected images --- expand_tests_report(request, actual=tiles_location_img, expected=expected_img) np.testing.assert_array_almost_equal(tiles_location_img, expected_img) @pytest.mark.parametrize( "fixture_slide, tile_size, level, seed, n_tiles", ( # Squared tile size (SVS.TCGA_CR_7395_01A_01_TS1, (128, 128), 1, 42, 20), (SVS.TCGA_CR_7395_01A_01_TS1, (128, 128), 0, 42, 10), (SVS.TCGA_CR_7395_01A_01_TS1, (128, 128), 1, 2, 20), (SVS.TCGA_CR_7395_01A_01_TS1, (128, 128), 0, 2, 10), (TIFF.KIDNEY_48_5, (10, 10), 0, 20, 20), (TIFF.KIDNEY_48_5, (20, 20), 0, 20, 10), # Not squared tile size (SVS.TCGA_CR_7395_01A_01_TS1, (135, 128), 1, 42, 20), (SVS.TCGA_CR_7395_01A_01_TS1, (135, 128), 0, 42, 10), (SVS.TCGA_CR_7395_01A_01_TS1, (135, 128), 1, 2, 20), (TIFF.KIDNEY_48_5, (10, 20), 0, 2, 10), (TIFF.KIDNEY_48_5, (20, 10), 0, 20, 20), (TIFF.KIDNEY_48_5, (10, 15), 0, 20, 10), ), ) def test_extract_tiles_respecting_the_given_tile_size( self, tmpdir, fixture_slide, tile_size, level, seed, n_tiles): processed_path = os.path.join(tmpdir, "processed") slide = Slide(fixture_slide, processed_path) random_tiles_extractor = RandomTiler( tile_size=tile_size, n_tiles=n_tiles, level=level, seed=seed, check_tissue=True, ) binary_mask = BiggestTissueBoxMask() random_tiles_extractor.extract(slide, binary_mask) for tile in os.listdir(processed_path): assert Image.open(os.path.join(processed_path, tile)).size == tile_size
class Describe_Slide: def it_knows_its_name(self): slide = Slide(SVS.CMU_1_SMALL_REGION, os.path.join(SVS.CMU_1_SMALL_REGION, "processed")) name = slide.name assert name == ntpath.basename(SVS.CMU_1_SMALL_REGION).split(".")[0] def it_calculate_resampled_nparray_from_small_region_svs_image(self): slide = Slide(SVS.CMU_1_SMALL_REGION, os.path.join(SVS.CMU_1_SMALL_REGION, "processed")) resampled_array = slide.resampled_array(scale_factor=32) expected_value = load_expectation( "svs-images/small-region-svs-resampled-array", type_="npy") np.testing.assert_almost_equal(resampled_array, expected_value) def it_knows_the_right_slide_dimension(self): slide = Slide(SVS.CMU_1_SMALL_REGION, os.path.join(SVS.CMU_1_SMALL_REGION, "processed")) image = PIL.Image.open(SVS.CMU_1_SMALL_REGION) dimensions = slide.dimensions assert image.size == dimensions assert slide.dimensions == (2220, 2967) assert image.size == (2220, 2967) def it_raises_openslideerror_with_broken_wsi(self): slide = Slide(SVS.BROKEN, os.path.join(SVS.BROKEN, "processed")) with pytest.raises(PIL.UnidentifiedImageError) as err: slide._wsi assert isinstance(err.value, PIL.UnidentifiedImageError) assert (str(err.value) == "Your wsi has something broken inside, a doctor is needed") @pytest.mark.parametrize( "slide_fixture, tissue_mask, binary_mask, expectation", [ ( SVS.CMU_1_SMALL_REGION, True, BiggestTissueBoxMask(), "cmu-1-small-region-bbox-location-tissue-mask-true", ), ( SVS.CMU_1_SMALL_REGION, False, BiggestTissueBoxMask(), "cmu-1-small-region-bbox-location-tissue-mask-false", ), ( SVS.TCGA_CR_7395_01A_01_TS1, True, BiggestTissueBoxMask(), "tcga-cr-7395-01a-01-ts1-bbox-location-tissue-mask-true", ), ( SVS.TCGA_CR_7395_01A_01_TS1, False, BiggestTissueBoxMask(), "tcga-cr-7395-01a-01-ts1-bbox-location-tissue-mask-false", ), ( SVS.CMU_1_SMALL_REGION, True, TissueMask(), "cmu-1-small-region-tissue-location-tissue-mask-true", ), ( SVS.CMU_1_SMALL_REGION, False, TissueMask(), "cmu-1-small-region-tissue-location-tissue-mask-false", ), ( SVS.TCGA_CR_7395_01A_01_TS1, True, TissueMask(), "tcga-cr-7395-01a-01-ts1-tissue-location-tissue-mask-true", ), ( SVS.TCGA_CR_7395_01A_01_TS1, False, TissueMask(), "tcga-cr-7395-01a-01-ts1-tissue-location-tissue-mask-false", ), ], ) def it_locates_the_mask(self, tmpdir, slide_fixture, tissue_mask, binary_mask, expectation): slide = Slide(slide_fixture, os.path.join(tmpdir, "processed")) expected_img = load_expectation( os.path.join("mask-location-images", expectation), type_="png", ) mask_location_img = slide.locate_mask(binary_mask, tissue_mask=tissue_mask, scale_factor=3) np.testing.assert_array_almost_equal(np.asarray(mask_location_img), expected_img) def it_knows_its_properties(self): slide = Slide(SVS.CMU_1_SMALL_REGION, "processed") properties = slide.properties assert isinstance(properties, dict) assert properties == load_python_expression( "python-expr/slide_properties_dict")
class DescribeGridTiler: @pytest.mark.parametrize( "fixture_slide, binary_mask, tile_size, level,check_tissue, expectation", [ ( SVS.CMU_1_SMALL_REGION, BiggestTissueBoxMask(), (512, 512), 0, False, "tiles-location-images/cmu-1-small-region-tl-grid-BTB-false-512x512", ), ( SVS.TCGA_CR_7395_01A_01_TS1, TissueMask(), (512, 550), -2, False, "tiles-location-images/tcga-cr-7395-01a-01-ts1-tl-grid-TM-f-512x550", ), ( SVS.TCGA_CR_7395_01A_01_TS1, BiggestTissueBoxMask(), (512, 512), 0, False, "tiles-location-images/tcga-cr-7395-01a-01-ts1-tl-grid-BTB-f-512x512", ), ( SVS.CMU_1_SMALL_REGION, TissueMask(), (128, 120), 0, True, "tiles-location-images/cmu-1-small-region-tl-grid-TM-true-128x120", ), ( SVS.CMU_1_SMALL_REGION, TissueMask(), (128, 120), 0, False, "tiles-location-images/cmu-1-small-region-tl-grid-TM-false-128x120", ), ( SVS.TCGA_CR_7395_01A_01_TS1, TissueMask(), (128, 128), 0, True, "tiles-location-images/tcga-cr-7395-01a-01-ts1-tl-grid-TM-true-128x128", ), ( TIFF.KIDNEY_48_5, TissueMask(), (15, 10), 0, True, "tiles-location-images/kidney-48-5-grid-TM-true-15x10", ), ( TIFF.KIDNEY_48_5, BiggestTissueBoxMask(), (20, 20), 0, False, "tiles-location-images/kidney-48-5-grid-TM-false-20x20", ), pytest.param( EXTERNAL_SVS.CMU_3, BiggestTissueBoxMask(), (512, 512), 1, False, "tiles-location-images/external-cmu-3-grid-BTB-false-512x512", marks=pytest.mark.skipif(not on_ci(), reason="To run only on CI"), ), pytest.param( EXTERNAL_SVS.CMU_3, TissueMask(), (512, 512), 1, False, "tiles-location-images/external-cmu-3-grid-TM-false-512x512", marks=pytest.mark.skipif(not on_ci(), reason="To run only on CI"), ), ], ) def it_locates_tiles_on_the_slide( self, request, fixture_slide, binary_mask, tile_size, level, check_tissue, expectation, ): slide = Slide(fixture_slide, "") grid_tiles_extractor = GridTiler( tile_size=tile_size, level=level, check_tissue=check_tissue, ) expected_img = load_expectation(expectation, type_="png") tiles_location_img = grid_tiles_extractor.locate_tiles(slide, binary_mask, scale_factor=10) # --- Expanding test report with actual and expected images --- expand_tests_report(request, expected=expected_img, actual=tiles_location_img) np.testing.assert_array_almost_equal(tiles_location_img, expected_img)