def it_can_generate_random_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") _box_mask_lvl = method_mock(request, RandomTiler, "box_mask_lvl") _box_mask_lvl.return_value = SparseArrayMock.ONES_500X500_BOOL _tile_size = property_mock(request, RandomTiler, "tile_size") _tile_size.return_value = (128, 128) _np_random_choice1 = function_mock(request, "numpy.random.choice") _np_random_choice1.return_value = 0 _np_random_choice2 = function_mock(request, "numpy.random.choice") _np_random_choice2.return_value = 0 _scale_coordinates = function_mock(request, "histolab.tiler.scale_coordinates") random_tiler = RandomTiler((128, 128), 10, 0) random_tiler._random_tile_coordinates(slide) _box_mask_lvl.assert_called_once_with(random_tiler, slide) _tile_size.assert_has_calls([call((128, 128))]) _scale_coordinates.assert_called_once_with( reference_coords=CoordinatePair(x_ul=0, y_ul=0, x_br=128, y_br=128), reference_size=(500, 500), target_size=(500, 500), )
def it_can_generate_random_tiles_with_no_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) 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=False, ) generated_tiles = list(random_tiler._tiles_generator(slide)) _random_tile_coordinates.assert_called_with(random_tiler, slide) _has_enough_tissue.assert_not_called() 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_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_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)
def it_can_generate_random_tiles( self, request, tmpdir, tile1, tile2, check_tissue, has_enough_tissue, max_iter, expected_value, _random_tile_coordinates, ): 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, "processed") _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) 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=check_tissue ) generated_tiles = list(random_tiler._tiles_generator(slide)) _random_tile_coordinates.assert_called_with(random_tiler, slide) 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): 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) 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", "tiles", "tile_0_level2_0-10-0-10.png")) assert os.path.exists( os.path.join(tmp_path_, "processed", "tiles", "tile_1_level2_0-10-0-10.png")) _has_valid_tile_size.assert_called_once_with(random_tiler, slide)
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) with pytest.raises(ValueError) as err: random_tiler.extract(slide) assert isinstance(err.value, ValueError) assert str(err.value) == "Seed must be between 0 and 2**32 - 1"
def it_knows_its_tile_filename(self, level, prefix, suffix, tile_coords, tiles_counter, expected_filename): random_tiler = RandomTiler((512, 512), 10, level, 7, True, 80, prefix, suffix) _filename = random_tiler._tile_filename(tile_coords, tiles_counter) assert type(_filename) == str assert _filename == expected_filename
def or_it_has_not_available_level_value(self, tmpdir): slide, _ = base_test_slide(tmpdir, PILIMG.RGB_RANDOM_COLOR_500X500) random_tiler = RandomTiler((128, 128), 10, 3) with pytest.raises(LevelError) as err: random_tiler.extract(slide) assert isinstance(err.value, LevelError) assert str(err.value ) == "Level 3 not available. Number of available levels: 1"
def it_knows_if_it_has_valid_tile_size(self, tmpdir, tile_size, expected_result): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) random_tiler = RandomTiler(tile_size, 10, 0, 7) result = random_tiler._has_valid_tile_size(slide) assert type(result) == bool assert result == expected_result
def or_it_has_wrong_seed(self, request, tmpdir): tmp_path_ = tmpdir.mkdir("myslide") image = PILImageMock.DIMS_50X50_RGB_RANDOM_COLOR image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, "processed") random_tiler = RandomTiler((512, 512), 10, 0, seed=-1) with pytest.raises(ValueError) as err: random_tiler.extract(slide) 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): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGB_RANDOM_COLOR_500X500 image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, "processed") random_tiler = RandomTiler((128, 128), 10, 3) with pytest.raises(LevelError) as err: random_tiler.extract(slide) assert isinstance(err.value, LevelError) assert str(err.value) == "Level 3 not available. Number of available levels: 1"
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)] 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, "processed") generated_tiles = list(random_tiler._tiles_generator(slide)) assert generated_tiles[0][1] == CP(0, 10, 0, 10) assert isinstance(generated_tiles[0][0], Tile)
def it_knows_its_box_mask_lvl(self, request, tmpdir, level, box_mask, expected_box_mask_lvl): 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") _box_mask = method_mock(request, RandomTiler, "box_mask") _box_mask.return_value = box_mask random_tiler = RandomTiler((128, 128), 10, level) box_mask_lvl = random_tiler.box_mask_lvl(slide) assert type(box_mask_lvl) == sparse._coo.core.COO np.testing.assert_array_almost_equal(box_mask_lvl.todense(), expected_box_mask_lvl.todense())
def it_knows_its_tile_size(self, tile_size): random_tiler = RandomTiler(tile_size, 10, 0) tile_size_ = random_tiler.tile_size assert type(tile_size_) == tuple assert tile_size_ == tile_size
def it_knows_its_max_iter(self, max_iter): random_tiler = RandomTiler((128, 128), 10, 0, max_iter=max_iter) max_iter_ = random_tiler.max_iter assert type(max_iter_) == int assert max_iter_ == max_iter
def it_knows_its_box_mask(self, request, tmpdir, check_tissue, expected_box): 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, "processed") _biggest_tissue_box_mask = property_mock( request, Slide, "biggest_tissue_box_mask" ) _biggest_tissue_box_mask.return_value = expected_box random_tiler = RandomTiler((128, 128), 10, 0, check_tissue=check_tissue) box_mask = random_tiler.box_mask(slide) _biggest_tissue_box_mask.assert_called_once_with() assert type(box_mask) == np.ndarray np.testing.assert_array_almost_equal(box_mask, expected_box)
def it_knows_its_box_mask(self, request, tmpdir, check_tissue, expected_box): slide, _ = base_test_slide(tmpdir, PILIMG.RGBA_COLOR_500X500_155_249_240) _biggest_tissue_box_mask = property_mock(request, Slide, "biggest_tissue_box_mask") _biggest_tissue_box_mask.return_value = expected_box random_tiler = RandomTiler((128, 128), 10, 0, check_tissue=check_tissue) box_mask = random_tiler.box_mask(slide) _biggest_tissue_box_mask.assert_called_once_with() assert type(box_mask) == np.ndarray np.testing.assert_array_almost_equal(box_mask, expected_box)
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) generated_tiles = list(random_tiler._tiles_generator(slide)) assert generated_tiles[0][1] == CP(0, 10, 0, 10) assert isinstance(generated_tiles[0][0], Tile)
def it_constructs_from_args(self, request): _init = initializer_mock(request, RandomTiler) random_tiler = RandomTiler((512, 512), 10, 2, 7, True, "", ".png", 1e4) _init.assert_called_once_with(ANY, (512, 512), 10, 2, 7, True, "", ".png", 1e4) assert isinstance(random_tiler, RandomTiler) assert isinstance(random_tiler, Tiler)
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 or_it_has_wrong_max_iter(self): with pytest.raises(ValueError) as err: RandomTiler((512, 512), 10, 0, max_iter=3) assert isinstance(err.value, ValueError) assert ( str(err.value) == "The maximum number of iterations (3) must be grater than or equal to " "the maximum number of tiles (10).")
def extract_random_tiles( dataset_dir: str, processed_path: str, tile_size: Tuple[int, int], n_tiles: int, level: int, seed: int, check_tissue: bool, ) -> None: """Save random tiles extracted from WSIs in `dataset_dir` into `processed_path`/tiles/ Parameters ---------- dataset_dir : str Path were the WSIs are saved processed_path : str Path where to store the tiles (will be concatenated with /tiles) tile_size : Tuple[int, int] width and height of the cropped tiles n_tiles : int Maximum number of tiles to extract level : int Magnification level from which extract the tiles seed : int Seed for RandomState check_tissue : bool Whether to check if the tile has enough tissue to be saved """ slideset = SlideSet(dataset_dir, processed_path, valid_extensions=[".svs"]) for slide in tqdm(slideset.slides): prefix = f"{slide.name}_" random_tiles_extractor = RandomTiler( tile_size=tile_size, n_tiles=n_tiles, level=level, seed=seed, check_tissue=check_tissue, prefix=prefix, ) random_tiles_extractor.extract(slide)
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, RandomTiler, "_has_valid_tile_size") _has_valid_tile_size.return_value = False random_tiler = RandomTiler((50, 52), n_tiles=10, level=0) with pytest.raises(TileSizeError) as err: random_tiler.extract(slide) 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(random_tiler, slide)
def it_knows_its_tile_filename(self, request, tile_filename_fixture): ( tile_size, n_tiles, level, seed, check_tissue, prefix, suffix, tile_coords, tiles_counter, expected_filename, ) = tile_filename_fixture random_tiler = RandomTiler(tile_size, n_tiles, level, seed, check_tissue, prefix, suffix) _filename = random_tiler._tile_filename(tile_coords, tiles_counter) assert type(_filename) == str assert _filename == expected_filename
def it_can_generate_random_tiles( self, request, tmpdir, coords1, coords2, check_tissue, has_enough_tissue, max_iter, expected_n_tiles, ): 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") _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) _random_tile_coordinates = method_mock(request, RandomTiler, "_random_tile_coordinates") _random_tile_coordinates.side_effect = [coords1, coords2 ] * (max_iter // 2) tile1 = Tile(image, coords1) tile2 = Tile(image, coords2) _extract_tile.side_effect = [tile1, tile2] * (max_iter // 2) random_tiler = RandomTiler((10, 10), 2, level=0, max_iter=max_iter, check_tissue=check_tissue) generated_tiles = list(random_tiler._random_tiles_generator(slide)) _random_tile_coordinates.assert_called_with(random_tiler, slide) assert _random_tile_coordinates.call_count <= random_tiler.max_iter _extract_tile.call_args_list == ([call(coords1, 0), call(coords2, 0)]) assert len(generated_tiles) == expected_n_tiles if expected_n_tiles == 2: assert generated_tiles == [(tile1, coords1), (tile2, coords2)]
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( 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_locates_tiles_on_the_slide(self, request, fixture_slide, expectation, tmpdir): slide = Slide(fixture_slide, os.path.join(tmpdir, "processed")) slide.save_scaled_image(10) random_tiles_extractor = RandomTiler(tile_size=(512, 512), n_tiles=2, level=0, seed=42, check_tissue=False) expected_img = load_expectation( expectation, type_="png", ) tiles_location_img = random_tiles_extractor.locate_tiles( slide, 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(np.asarray(tiles_location_img), expected_img)
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]