Пример #1
0
    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_
Пример #2
0
    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
Пример #3
0
    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")
Пример #4
0
    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'"
Пример #5
0
    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
Пример #6
0
    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
Пример #7
0
    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
Пример #8
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
Пример #9
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)]
Пример #10
0
    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
Пример #11
0
    def it_knows_its_coords(self):
        _coords = CoordinatePair(0, 0, 50, 50)
        tile = Tile(None, _coords, 0)

        coords = tile.coords

        assert coords == _coords
Пример #12
0
    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
Пример #13
0
    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)
Пример #14
0
    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
Пример #15
0
    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
Пример #16
0
    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)
Пример #17
0
    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)
Пример #18
0
    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)
Пример #19
0
    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)],
        )
Пример #20
0
    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)]
Пример #21
0
    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_
Пример #22
0
    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 == []
Пример #23
0
    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]
Пример #24
0
    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()
Пример #25
0
    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)
Пример #26
0
    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
Пример #27
0
    def it_knows_its_level(self):
        tile = Tile(None, None, 0)

        level = tile.level

        assert level == 0
Пример #28
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")
Пример #29
0
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
Пример #30
0
    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