def it_calls_tile_tissue_mask_filters(self, ): image = PILIMG.RGBA_COLOR_50X50_155_0_0 tile = Tile(image, None, 0) tile._has_only_some_tissue() assert type(tile._has_only_some_tissue()) == np.bool_
def it_can_construct_nuclei_scorer(self, request): image = PILIMG.RGB_RANDOM_COLOR_10X10 tissue_ratio_ = property_mock(request, Tile, "tissue_ratio") tissue_ratio_.return_value = 0.7 apply_filters_ = method_mock(request, Tile, "apply_filters") apply_filters_.return_value = Tile(PIL.Image.fromarray(COMPLEX_MASK), None, 0) mask_difference_ = function_mock(request, "histolab.scorer.mask_difference") mask_difference_.return_value = np.zeros_like(COMPLEX_MASK).astype( "bool") tile = Tile(image, None, 0) nuclei_scorer = scorer.NucleiScorer() score = nuclei_scorer(tile) tissue_ratio_.assert_called_once() assert len(apply_filters_.call_args_list) == 2 # not possible to test filters compositions instances used in the call assert apply_filters_.call_args_list[0][0][0] == tile assert apply_filters_.call_args_list[1][0][0] == tile np.testing.assert_array_equal(mask_difference_.call_args_list[0][0][0], COMPLEX_MASK) np.testing.assert_array_equal(mask_difference_.call_args_list[0][0][1], COMPLEX_MASK) assert isinstance(nuclei_scorer, scorer.NucleiScorer) assert type(score) == np.float64 assert score == 0 # to avoid float representation issues
def and_it_can_save_the_tile_image_also_without_ext(self, tmpdir): tmp_path_ = os.path.join(tmpdir.mkdir("mydir"), "mytile") _image = PILIMG.RGBA_COLOR_50X50_155_0_0 tile = Tile(_image, None, 0) tile.save(tmp_path_) assert os.path.exists(tmp_path_ + ".png")
def but_it_has_wrong_image_type(self): """This test simulates a wrong user behaviour, using a None object instead of a PIL Image for image param""" with pytest.raises(AttributeError) as err: tile = Tile(None, CoordinatePair(0, 0, 50, 50), 0) tile.has_enough_tissue() assert isinstance(err.value, AttributeError) assert str(err.value) == "'NoneType' object has no attribute 'convert'"
def it_knows_if_has_tissue_more_than_percent(self, request, tissue_mask, percent, expected_value): _tissue_mask = method_mock(request, Tile, "_tissue_mask") _tissue_mask.return_value = tissue_mask tile = Tile(None, None, 0) has_tissue_more_than_percent = tile._has_tissue_more_than_percent( percent) assert has_tissue_more_than_percent == expected_value
def it_knows_if_has_tissue_more_than_percent(self, request, tissue_mask, percent, expected_value): _compose_call = method_mock(request, Compose, "__call__") _compose_call.return_value = tissue_mask tile = Tile(None, None, 0) has_tissue_more_than_percent = tile._has_tissue_more_than_percent( percent) assert has_tissue_more_than_percent == expected_value
def it_knows_how_to_apply_filters_PIL(self, RgbToGrayscale_): image_before = PILIMG.RGB_RANDOM_COLOR_10X10 image_after = PILIMG.GRAY_RANDOM_10X10 RgbToGrayscale_.return_value = image_after tile = Tile(image_before, None, 0) filtered_image = tile.apply_filters(RgbToGrayscale_) RgbToGrayscale_.assert_called_once_with(tile.image) assert isinstance(filtered_image, Tile) assert filtered_image.image == image_after assert filtered_image.coords is None assert filtered_image.level == 0
def it_knows_how_to_apply_filters_np(self, OtsuThreshold_): image_before = PILIMG.RGB_RANDOM_COLOR_10X10 image_after = PILIMG.GRAY_RANDOM_10X10 OtsuThreshold_.return_value = np.array(image_after) tile = Tile(image_before, None, 0) filtered_image = tile.apply_filters(OtsuThreshold_) OtsuThreshold_.assert_called_once_with(tile.image) assert isinstance(filtered_image, Tile) assert filtered_image.image == image_after assert filtered_image.coords is None assert filtered_image.level == 0
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 it_knows_its_tissue_ratio( self, request, RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_, ): _tile_tissue_mask_filters = property_mock(request, _TileFiltersComposition, "tissue_mask_filters") filters = Compose([ RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_ ]) _tile_tissue_mask_filters.return_value = filters _call = method_mock(request, Compose, "__call__") _call.return_value = COMPLEX_MASK image = PILIMG.RGB_RANDOM_COLOR_10X10 tile = Tile(image, None, 0) tissue_ratio = tile.tissue_ratio _tile_tissue_mask_filters.assert_called_once() _call.assert_called_once_with(filters, image) assert type(tissue_ratio) == float assert tissue_ratio == 0.61
def it_knows_its_coords(self): _coords = CoordinatePair(0, 0, 50, 50) tile = Tile(None, _coords, 0) coords = tile.coords assert coords == _coords
def it_knows_its_image(self): _image = PILIMG.RGBA_COLOR_50X50_155_0_0 tile = Tile(_image, None, 0) image = tile.image assert image == _image
def it_can_extract_grid_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, 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", "tiles", 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) 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", "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(grid_tiler, slide)
def it_knows_if_is_is_almost_white(self, tile_img, expected_result): coords = CP(0, 512, 0, 512) tile = Tile(tile_img, coords) is_almost_white = tile._is_almost_white assert is_almost_white == expected_result
def it_knows_if_it_has_enough_tissue( self, _is_almost_white, _has_only_some_tissue, _has_tissue_more_than_percent, almost_white, only_some_tissue, tissue_more_than_percent, expected_value, ): _is_almost_white.return_value = almost_white _has_only_some_tissue.return_value = only_some_tissue _has_tissue_more_than_percent.return_value = tissue_more_than_percent tile = Tile(None, None, 0) has_enough_tissue = tile.has_enough_tissue() assert has_enough_tissue == expected_value
def it_knows_its_tissue_mask( self, request, RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_, ): BinaryFillHoles_.return_value = np.zeros((50, 50)) filters = Compose([ RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_ ]) image = PILIMG.RGBA_COLOR_50X50_155_0_0 tile = Tile(image, None, 0) tissue_mask = tile._tissue_mask(filters) assert tissue_mask.shape == (50, 50)
def it_constructs_from_args(self, request): _init = initializer_mock(request, Tile) _image = PILIMG.RGBA_COLOR_50X50_155_0_0 _level = 0 _coords = CoordinatePair(0, 0, 50, 50) tile = Tile(_image, _coords, _level) _init.assert_called_once_with(ANY, _image, _coords, _level) assert isinstance(tile, Tile)
def it_knows_nuclei_score(self, tile_img, expected_score): tile = Tile(tile_img, None) nuclei_scorer = scorer.NucleiScorer() expected_warning_regex = ( r"Input image must be RGB. NOTE: the image will be converted to RGB before" r" HED conversion.") with pytest.warns(UserWarning, match=expected_warning_regex): score = nuclei_scorer(tile) assert round(score, 5) == round(expected_score, 5)
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 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_calls_tile_tissue_mask_filters( self, request, RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_, ): _tissue_mask_filters = property_mock(request, _TileFiltersComposition, "tissue_mask_filters") BinaryFillHoles_.return_value = np.zeros((50, 50)) _tissue_mask_filters.return_value = Compose([ RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_ ]) image = PILIMG.RGBA_COLOR_50X50_155_0_0 tile = Tile(image, None, 0) tile._has_only_some_tissue() _tissue_mask_filters.assert_called_once() assert type(tile._has_only_some_tissue()) == np.bool_
def it_can_generate_grid_tiles( self, request, tmpdir, grid_tiles_fixture, ): ( coords1, coords2, check_tissue, has_enough_tissue, expected_n_tiles, ) = grid_tiles_fixture 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 _grid_coordinates_generator = method_mock( request, GridTiler, "_grid_coordinates_generator") _grid_coordinates_generator.return_value = [coords1, coords2] tile1 = Tile(image, coords1) tile2 = Tile(image, coords2) _extract_tile.side_effect = [tile1, tile2] grid_tiler = GridTiler((10, 10), level=0, check_tissue=check_tissue) 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 len(generated_tiles) == expected_n_tiles if expected_n_tiles == 2: assert generated_tiles == [(tile1, coords1), (tile2, coords2)] if expected_n_tiles == 1: assert generated_tiles == [(tile1, coords1)] if expected_n_tiles == 0: assert generated_tiles == []
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 it_can_extract_score_tiles(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")) _highest_score_tiles = method_mock(request, ScoreTiler, "_highest_score_tiles") coords = CP(0, 10, 0, 10) tile = Tile(image, coords) _extract_tile.return_value = tile _highest_score_tiles.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") random_scorer = RandomScorer() score_tiler = ScoreTiler(random_scorer, (10, 10), 2, 2) score_tiler.extract(slide) assert _extract_tile.call_args_list == [ call(slide, coords, 2), call(slide, coords, 2), ] _highest_score_tiles.assert_called_once_with(score_tiler, slide) 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", "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") ) _save_report.assert_not_called()
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_knows_tissue_areas_mask_filters_composition(self, RgbToGrayscale_, OtsuThreshold_, BinaryDilation_, BinaryFillHoles_): tile = Tile(None, None, 0) _enough_tissue_mask_filters_ = tile._enough_tissue_mask_filters RgbToGrayscale_.assert_called_once() OtsuThreshold_.assert_called_once() BinaryDilation_.assert_called_once() BinaryFillHoles_.assert_called_once() np.testing.assert_almost_equal( BinaryFillHoles_.call_args_list[0][1]["structure"], np.ones( (5, 5))) assert _enough_tissue_mask_filters_.filters == [ RgbToGrayscale_(), OtsuThreshold_(), BinaryDilation_(), BinaryFillHoles_(), ] assert type(_enough_tissue_mask_filters_) == Compose
def it_knows_its_level(self): tile = Tile(None, None, 0) level = tile.level assert level == 0
class Describe_RandomTiler: @pytest.mark.parametrize("level", (2, -2)) def it_constructs_from_args(self, level, request): _init = initializer_mock(request, RandomTiler) random_tiler = RandomTiler((512, 512), 10, level, 7, True, "", ".png", int(1e4)) _init.assert_called_once_with(ANY, (512, 512), 10, level, 7, True, "", ".png", int(1e4)) assert isinstance(random_tiler, RandomTiler) assert isinstance(random_tiler, Tiler) def but_it_has_wrong_tile_size_value(self): with pytest.raises(ValueError) as err: RandomTiler((512, -1), 10, 0) assert isinstance(err.value, ValueError) assert str(err.value) == "Tile size must be greater than 0 ((512, -1))" 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) binary_mask = BiggestTissueBoxMask() with pytest.raises(LevelError) as err: random_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_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 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" @pytest.mark.parametrize("tile_size", ((512, 512), (128, 128), (10, 10))) 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 @pytest.mark.parametrize("max_iter", (1000, 10, 3000)) 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 @pytest.mark.parametrize( "level, prefix, suffix, tile_coords, tiles_counter, expected_filename", ( (3, "", ".png", CP(0, 512, 0, 512), 3, "tile_3_level3_0-512-0-512.png"), ( 0, "folder/", ".png", CP(4, 127, 4, 127), 10, "folder/tile_10_level0_4-127-4-127.png", ), ), ) 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 @pytest.mark.parametrize("tile_size, expected_result", [((512, 512), False), ((200, 200), True)]) 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 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), ) @pytest.mark.parametrize( "tile1, tile2, has_enough_tissue, max_iter, expected_value", ( ( 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)), [True, True], 10, 2, ), ( 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)), [True, False], 2, 1, ), ( Tile(PILIMG.RGBA_COLOR_500X500_155_249_240, CP(0, 10, 0, 10)), Tile(PILIMG.RGBA_COLOR_500X500_155_249_240, CP(5900, 6000, 5900, 6000)), [True, True], 2, 2, ), ), ) 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_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] @pytest.mark.parametrize( "tile1, tile2, has_enough_tissue, max_iter, expected_value", ( ( 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)), [True, True], 10, 2, ), ( 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)), [False, False], 10, 2, ), ), ) 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] binary_mask = BiggestTissueBoxMask() _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, binary_mask)) _random_tile_coordinates.assert_called_with(random_tiler, slide, binary_mask) _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_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_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) @pytest.mark.parametrize( "image, size", [ (PILIMG.RGBA_COLOR_50X50_155_0_0, (50, 50)), (PILIMG.RGBA_COLOR_49X51_155_0_0, (49, 51)), ], ) 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) binary_mask = BiggestTissueBoxMask() with pytest.raises(TileSizeError) as err: random_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(random_tiler, slide) # fixture components --------------------------------------------- @pytest.fixture def _random_tile_coordinates(self, request): return method_mock(request, RandomTiler, "_random_tile_coordinates")
class Describe_GridTiler: @pytest.mark.parametrize("level", (2, -2)) def it_constructs_from_args(self, level, request): _init = initializer_mock(request, GridTiler) grid_tiler = GridTiler((512, 512), level, True, 80, 0, "", ".png") _init.assert_called_once_with(ANY, (512, 512), level, True, 80, 0, "", ".png") assert isinstance(grid_tiler, GridTiler) assert isinstance(grid_tiler, Tiler) def but_it_has_wrong_tile_size_value(self): with pytest.raises(ValueError) as err: GridTiler((512, -1)) assert isinstance(err.value, ValueError) assert str(err.value) == "Tile size must be greater than 0 ((512, -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" @pytest.mark.parametrize("tile_size", ((512, 512), (128, 128), (10, 10))) def it_knows_its_tile_size(self, tile_size): grid_tiler = GridTiler(tile_size, 10, True, 0) tile_size_ = grid_tiler.tile_size assert type(tile_size_) == tuple assert tile_size_ == tile_size @pytest.mark.parametrize( "level, pixel_overlap, prefix, tile_coords, tiles_counter, expected_filename", ( (3, 0, "", CP(0, 512, 0, 512), 3, "tile_3_level3_0-512-0-512.png"), ( 0, 0, "folder/", CP(4, 127, 4, 127), 10, "folder/tile_10_level0_4-127-4-127.png", ), ), ) def it_knows_its_tile_filename( self, level, pixel_overlap, prefix, tile_coords, tiles_counter, expected_filename, ): grid_tiler = GridTiler((512, 512), level, True, 80.0, pixel_overlap, prefix, ".png") _filename = grid_tiler._tile_filename(tile_coords, tiles_counter) assert type(_filename) == str assert _filename == expected_filename @pytest.mark.parametrize("tile_size, expected_result", [((512, 512), False), ((200, 200), True)]) 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) grid_tiler = GridTiler(tile_size, 0, True) result = grid_tiler._has_valid_tile_size(slide) assert type(result) == bool assert result == expected_result @pytest.mark.parametrize( "bbox_coordinates, pixel_overlap, expected_n_tiles_row", ( (CP(x_ul=0, y_ul=0, x_br=6060, y_br=1917), 0, 11), (CP(x_ul=0, y_ul=0, x_br=1921, y_br=2187), 0, 3), (CP(x_ul=0, y_ul=0, x_br=1921, y_br=2187), 128, 5), (CP(x_ul=0, y_ul=0, x_br=1921, y_br=2187), -128, 3), ), ) def it_can_calculate_n_tiles_row(self, bbox_coordinates, pixel_overlap, expected_n_tiles_row): grid_tiler = GridTiler((512, 512), 2, True, 80.0, pixel_overlap) n_tiles_row = grid_tiler._n_tiles_row(bbox_coordinates) assert type(n_tiles_row) == int assert n_tiles_row == expected_n_tiles_row @pytest.mark.parametrize( "bbox_coordinates, pixel_overlap, expected_n_tiles_column", ( (CP(x_ul=0, y_ul=0, x_br=6060, y_br=1917), 0, 3), (CP(x_ul=0, y_ul=0, x_br=6060, y_br=1917), -1, 3), (CP(x_ul=0, y_ul=0, x_br=1921, y_br=2187), 0, 4), (CP(x_ul=0, y_ul=0, x_br=1921, y_br=2187), 128, 5), ), ) def it_can_calculate_n_tiles_column(self, bbox_coordinates, pixel_overlap, expected_n_tiles_column): grid_tiler = GridTiler((512, 512), 2, True, 80, pixel_overlap) n_tiles_column = grid_tiler._n_tiles_column(bbox_coordinates) assert type(n_tiles_column) == int assert n_tiles_column == expected_n_tiles_column @pytest.mark.parametrize( "tile1, tile2, has_enough_tissue, expected_n_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)), [True, True], 2, ), ( 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)), [False, False], 0, ), ( 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)), [True, False], 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] @pytest.mark.parametrize( "tile1, tile2, has_enough_tissue, expected_n_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)), [True, True], 2, ), ( 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)), [False, False], 2, ), ), ) def it_can_generate_grid_tiles_with_no_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=False) 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)), ]) _has_enough_tissue.assert_not_called() assert len(generated_tiles) == expected_n_tiles 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 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 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), ] @pytest.mark.parametrize( "image, size", [ (PILIMG.RGBA_COLOR_50X50_155_0_0, (50, 50)), (PILIMG.RGBA_COLOR_49X51_155_0_0, (49, 51)), ], ) 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) @pytest.mark.parametrize( "tile_coords, expected_result", [ (CP(0, 0, 2, 2), False), # completely outside of region (CP(0, 0, 8, 8), False), # only 205 (CP(2, 3, 6, 6), True), # 85% in (CP(3, 3, 5, 5), True), # 100% in ], ) def it_knows_whether_coordinates_are_within_extraction_mask( self, tile_coords, expected_result): grid_tiler = GridTiler((2, 2), level=0) # tile size doens't matter here mask = COMPLEX_MASK4 coords_within_extraction_mask = ( grid_tiler._are_coordinates_within_extraction_mask( tile_coords, mask)) assert type(coords_within_extraction_mask) == bool assert coords_within_extraction_mask == expected_result
def it_knows_if_it_has_only_some_tissue(self, tile_image, expected_value): tile = Tile(tile_image, CP(5, 5, 5, 5)) assert tile._has_only_some_tissue() == expected_value