예제 #1
0
    def it_can_save_report(self, request, tmpdir):
        tmp_path_ = tmpdir.mkdir("path")
        coords = CP(0, 10, 0, 10)
        highest_score_tiles = [(0.8, coords), (0.7, coords)]
        highest_scaled_score_tiles = [(0.1, coords), (0.0, coords)]
        filenames = ["tile0.png", "tile1.png"]
        random_scorer_ = instance_mock(request, RandomScorer)
        score_tiler = ScoreTiler(random_scorer_, (10, 10), 2, 2)
        report_ = [
            "filename,score,scaled_score",
            "tile0.png,0.8,0.1",
            "tile1.png,0.7,0.0",
        ]

        score_tiler._save_report(
            os.path.join(tmp_path_, "report.csv"),
            highest_score_tiles,
            highest_scaled_score_tiles,
            filenames,
        )

        assert os.path.exists(os.path.join(tmp_path_, "report.csv"))
        with open(os.path.join(tmp_path_, "report.csv"), newline="") as f:
            reader = csv.reader(f)
            report = [",".join(row) for row in reader]
            assert report == report_
예제 #2
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)]
예제 #3
0
    def but_it_raises_error_with_negative_n_tiles_value(self, request, tmpdir):
        tmp_path_ = tmpdir.mkdir("myslide")
        image = PILIMG.RGBA_COLOR_500X500_155_249_240
        image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG")
        slide_path = os.path.join(tmp_path_, "mywsi.png")
        slide = Slide(slide_path, os.path.join(tmp_path_, "processed"))
        _scores = method_mock(request, ScoreTiler, "_scores")
        coords = CP(0, 10, 0, 10)
        _scores.return_value = [
            (0.7, coords),
            (0.5, coords),
            (0.2, coords),
            (0.8, coords),
            (0.1, coords),
        ]
        _scorer = instance_mock(request, RandomScorer)
        score_tiler = ScoreTiler(_scorer, (10, 10), -1, 0)
        binary_mask = BiggestTissueBoxMask()

        with pytest.raises(ValueError) as err:
            score_tiler.extract(slide, binary_mask)

        assert isinstance(err.value, ValueError)
        assert str(err.value) == "'n_tiles' cannot be negative (-1)"
        _scores.assert_called_once_with(score_tiler, slide, binary_mask)
예제 #4
0
    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)
        score_tiler = ScoreTiler(RandomScorer(), tile_size, 2, 0)

        result = score_tiler._has_valid_tile_size(slide)

        assert type(result) == bool
        assert result == expected_result
예제 #5
0
    def or_it_raises_levelerror_if_has_not_available_level_value(self, tmpdir):
        slide, _ = base_test_slide(tmpdir, PILIMG.RGB_RANDOM_COLOR_500X500)
        score_tiler = ScoreTiler(None, (10, 10), 2, 3)

        with pytest.raises(LevelError) as err:
            score_tiler.extract(slide)

        assert isinstance(err.value, LevelError)
        assert str(err.value
                   ) == "Level 3 not available. Number of available levels: 1"
예제 #6
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)],
        )
예제 #7
0
    def it_can_scale_scores(self):
        coords = [CP(0, 10 * i, 0, 10) for i in range(3)]
        scores = [0.3, 0.4, 0.7]
        scores_ = list(zip(scores, coords))
        score_tiler = ScoreTiler(None, (10, 10), 2, 0)
        expected_scaled_coords = list(zip([0.0, 0.2500000000000001, 1.0], coords))

        scaled_scores = score_tiler._scale_scores(scores_)

        for (score, coords_), (expected_score, expected_coords) in zip(
            scaled_scores, expected_scaled_coords
        ):
            assert round(score, 5) == round(expected_score, 5)
            assert coords_ == expected_coords
예제 #8
0
    def but_it_raises_runtimeerror_if_no_tiles_are_extracted(self, request):
        slide = instance_mock(request, Slide)
        _tiles_generator = method_mock(request, GridTiler, "_tiles_generator")
        # it needs to be an empty generator
        _tiles_generator.return_value = (n for n in [])
        score_tiler = ScoreTiler(None, (10, 10), 2, 0)

        with pytest.raises(RuntimeError) as err:
            score_tiler._scores(slide)

        _tiles_generator.assert_called_once_with(score_tiler, slide)
        assert isinstance(err.value, RuntimeError)
        assert (
            str(err.value) ==
            "No tiles have been generated. This could happen if `check_tissue=True`"
        )
예제 #9
0
    def it_knows_its_n_tiles(self):
        n_tiles = 4
        score_tiler = ScoreTiler(RandomScorer(), (512, 512), n_tiles, 0)

        n_tiles_ = score_tiler.n_tiles

        assert type(n_tiles_) == int
        assert n_tiles_ == n_tiles
예제 #10
0
    def it_knows_its_scorer(self):
        random_scorer = RandomScorer()
        score_tiler = ScoreTiler(random_scorer, (512, 512), 4, 0)

        scorer_ = score_tiler.scorer

        assert callable(scorer_)
        assert isinstance(scorer_, RandomScorer)
예제 #11
0
    def it_can_calculate_highest_score_tiles(self, request, n_tiles, expected_value):
        slide = instance_mock(request, Slide)
        _scores = method_mock(request, ScoreTiler, "_scores")
        coords = CP(0, 10, 0, 10)
        _scores.return_value = [
            (0.7, coords),
            (0.5, coords),
            (0.2, coords),
            (0.8, coords),
            (0.1, coords),
        ]
        _scorer = instance_mock(request, RandomScorer)
        score_tiler = ScoreTiler(_scorer, (10, 10), n_tiles, 0)

        highest_score_tiles = score_tiler._highest_score_tiles(slide)

        _scores.assert_called_once_with(score_tiler, slide)
        assert highest_score_tiles == expected_value
예제 #12
0
    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, ScoreTiler,
                                           "_has_valid_tile_size")
        _has_valid_tile_size.return_value = False
        score_tiler = ScoreTiler(None, (50, 52), 2, 0)

        with pytest.raises(TileSizeError) as err:
            score_tiler.extract(slide)

        assert isinstance(err.value, TileSizeError)
        assert (str(
            err.value
        ) == f"Tile size (50, 52) is larger than slide size {size} at level 0")
        _has_valid_tile_size.assert_called_once_with(score_tiler, slide)
예제 #13
0
    def but_it_raises_error_with_negative_n_tiles_value(self, request):
        slide = instance_mock(request, Slide)
        _scores = method_mock(request, ScoreTiler, "_scores")
        coords = CP(0, 10, 0, 10)
        _scores.return_value = [
            (0.7, coords),
            (0.5, coords),
            (0.2, coords),
            (0.8, coords),
            (0.1, coords),
        ]
        _scorer = instance_mock(request, RandomScorer)
        score_tiler = ScoreTiler(_scorer, (10, 10), -1, 0)

        with pytest.raises(ValueError) as err:
            score_tiler.extract(slide)

        assert isinstance(err.value, ValueError)
        assert str(err.value) == "'n_tiles' cannot be negative (-1)"
예제 #14
0
    def it_constructs_from_args(self, request):
        _init = initializer_mock(request, ScoreTiler)
        rs = RandomScorer()
        grid_tiler = ScoreTiler(rs, (512, 512), 4, 2, True, 0, "", ".png")

        _init.assert_called_once_with(ANY, rs, (512, 512), 4, 2, True, 0, "", ".png")

        assert isinstance(grid_tiler, ScoreTiler)
        assert isinstance(grid_tiler, GridTiler)
        assert isinstance(grid_tiler, Tiler)
예제 #15
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()
예제 #16
0
    def it_locates_tiles_on_the_slide(self, request, fixture_slide,
                                      expectation, tmpdir):
        slide = Slide(fixture_slide, os.path.join(tmpdir, "processed"))
        scored_tiles_extractor = ScoreTiler(
            scorer=NucleiScorer(),
            tile_size=(512, 512),
            n_tiles=2,
            level=0,
            check_tissue=True,
        )
        expected_img = load_expectation(
            expectation,
            type_="png",
        )
        tiles_location_img = scored_tiles_extractor.locate_tiles(
            slide, scale_factor=10)
        # --- Expanding test report with actual and expected images ---
        expand_tests_report(request,
                            expected=expected_img,
                            actual=tiles_location_img)

        np.testing.assert_array_almost_equal(np.asarray(tiles_location_img),
                                             expected_img)
예제 #17
0
    def it_locates_tiles_on_the_slide(
        self,
        request,
        fixture_slide,
        tile_size,
        level,
        check_tissue,
        expectation,
    ):
        slide = Slide(fixture_slide, "")
        scored_tiles_extractor = ScoreTiler(
            scorer=NucleiScorer(),
            tile_size=tile_size,
            n_tiles=2,
            level=level,
            check_tissue=check_tissue,
        )
        expected_img = load_expectation(
            expectation,
            type_="png",
        )
        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):
            # no binary mask object passed to locate_tiles
            # default value = BiggestTissueBoxMask
            tiles_location_img = scored_tiles_extractor.locate_tiles(
                slide, scale_factor=8)

        # --- Expanding test report with actual and expected images ---
        expand_tests_report(request,
                            expected=expected_img,
                            actual=tiles_location_img)

        np.testing.assert_array_almost_equal(tiles_location_img, expected_img)