def it_can_remap_negative_level_indices(self, level, expected_value, levels_prop): levels_prop.return_value = [0, 1, 2, 3, 4, 5, 6, 7, 8] slide = Slide("path", "processed") assert slide._remap_level(level) == expected_value
def it_can_resample_itself(self, tmpdir, resampled_dims_): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_155_249_240 image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, "processed") resampled_dims_.return_value = (100, 200, 300, 400) _resample = slide._resample(32) # image array assertions assert type(_resample[1]) == np.ndarray # ---The np array shape should be (new_h X new_w X channels),--- # ---in this case (look at resampled_dims mock) the new_h is 400--- # ---the new_w is 300 and the color channels of the image are 3--- assert _resample[1].shape == (400, 300, 3) # ---Here we prove that the 3 channels are compliant with the color--- # ---definition and that each channel is a np.array (400x300) filled--- # ---with the related color expressed during the image creation--- np.testing.assert_almost_equal(_resample[1][:, :, 0], np.full((400, 300), 155)) np.testing.assert_almost_equal(_resample[1][:, :, 1], np.full((400, 300), 249)) np.testing.assert_almost_equal(_resample[1][:, :, 2], np.full((400, 300), 240)) # PIL image assertions assert type(_resample[0]) == PIL.Image.Image assert _resample[0].size == (300, 400) assert _resample[0].width == 300 assert _resample[0].height == 400 assert _resample[0].mode == "RGB"
def it_knows_its_region_coordinates(self): region = Region(index=0, area=14, bbox=(0, 1, 1, 2), center=(0.5, 0.5)) slide = Slide("a/b", "c/d") region_coords_ = slide._region_coordinates(region) assert region_coords_ == CoordinatePair(x_ul=1, y_ul=0, x_br=2, y_br=1)
def it_knows_regions_from_binary_mask(self, request): binary_mask = np.array([[True, False], [True, True]]) label = function_mock(request, "histolab.slide.label") regionprops = function_mock(request, "histolab.slide.regionprops") RegionProps = namedtuple("RegionProps", ("area", "bbox", "centroid")) regions_props = [ RegionProps(3, (0, 0, 2, 2), (0.6666666666666666, 0.3333333333333333)) ] regionprops.return_value = regions_props label(binary_mask).return_value = [[0, 1], [1, 1]] slide = Slide("/a/b", "c/d") regions_from_binary_mask_ = slide._regions_from_binary_mask( binary_mask) regionprops.assert_called_once_with(label(binary_mask)) assert type(regions_from_binary_mask_) == list assert len(regions_from_binary_mask_) == 1 assert type(regions_from_binary_mask_[0]) == Region assert regions_from_binary_mask_ == [ Region( index=0, area=regions_props[0].area, bbox=regions_props[0].bbox, center=regions_props[0].centroid, ) ]
def it_knows_its_scaled_image_path(self, resampled_dims_, slide_path, proc_path, slide_dims, expected_value): resampled_dims_.return_value = slide_dims slide = Slide(slide_path, proc_path) scaled_img_path = slide.scaled_image_path(scale_factor=22) assert scaled_img_path == expected_value
def but_it_raises_error_when_it_doesnt_exist(self): slide = Slide("a/b", "processed") with pytest.raises(FileNotFoundError) as err: slide.show() assert (str(err.value) == "Cannot display the slide thumbnail:[Errno 2] No such file or " "directory: 'processed/thumbnails/b.png'")
def it_calculate_resampled_nparray_from_small_region_svs_image(self): slide = Slide(SVS.CMU_1_SMALL_REGION, os.path.join(SVS.CMU_1_SMALL_REGION, "processed")) resampled_array = slide.resampled_array(scale_factor=32) expected_value = load_expectation( "svs-images/small-region-svs-resampled-array", type_="npy") np.testing.assert_almost_equal(resampled_array, expected_value)
def it_can_show_its_thumbnail(self, 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, "processed") slide.save_thumbnail() assert ImageShow.show(PIL.Image.open(slide.thumbnail_path))
def but_it_raises_error_when_it_doesnt_exist(self): slide = Slide("a/b", "processed") with pytest.raises(FileNotFoundError) as err: slide.show() assert ( str(err.value) == "Cannot display the slide thumbnail: The wsi path resource doesn't " "exist: a/b")
def it_knows_its_level_dimensions(self, 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, "processed") level_dimensions = slide.level_dimensions(level=0) assert level_dimensions == (500, 500)
def but_it_raises_an_error_when_n_is_not_between_1_and_number_of_regions( self, n): regions = [ Region(i, i + 1, (0, 0, 2, 2), (0.5, 0.5)) for i in range(4) ] slide = Slide("a/b", "c/d") with pytest.raises(ValueError) as err: slide._biggest_regions(regions, n) assert str( err.value) == f"n should be between 1 and {len(regions)}, got {n}"
def but_it_raises_error_when_it_doesnt_exist(self): slide = Slide("a/b", "processed") with pytest.raises(FileNotFoundError) as err: slide.show() assert ( str(err.value) == "Cannot display the slide thumbnail:[Errno 2] No such file or " f"directory: {repr(os.path.join('processed', 'thumbnails', 'b.png'))}" )
def it_knows_its_scaled_image(self, tmpdir, resampled_dims_): 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")) resampled_dims_.return_value = (100, 200, 300, 400) scaled_image = slide.scaled_image(32) assert type(scaled_image) == PIL.Image.Image
def it_knows_if_coords_are_valid(self, coords, expected_result, 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, "processed") _are_valid = slide._has_valid_coords(coords) assert type(_are_valid) == bool assert _are_valid == expected_result
def but_it_raises_expection_when_level_does_not_exist(self, tmpdir): tmp_path_ = tmpdir.mkdir("myslide") image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, "processed") with pytest.raises(ValueError) as err: slide.level_dimensions(level=3) assert str(err.value ) == "Level 3 not available. Number of available levels: 1"
def it_knows_its_resampled_array(self, tmpdir, resampled_dims_): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_155_249_240 image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, "processed") resampled_dims_.return_value = (100, 200, 300, 400) resampled_array = slide.resampled_array(scale_factor=32) assert type(resampled_array) == np.ndarray assert resampled_array.shape == (400, 300, 3)
def it_knows_its_biggest_regions(self): regions = [ Region(index=0, area=14, bbox=(0, 0, 2, 2), center=(0.5, 0.5)), Region(index=1, area=2, bbox=(0, 0, 2, 2), center=(0.5, 0.5)), Region(index=2, area=5, bbox=(0, 0, 2, 2), center=(0.5, 0.5)), Region(index=3, area=10, bbox=(0, 0, 2, 2), center=(0.5, 0.5)), ] slide = Slide("a/b", "c/d") biggest_regions = slide._biggest_regions(regions, 2) assert biggest_regions == [regions[0], regions[3]]
def it_locates_the_biggest_bbox(self, tmpdir, slide_fixture, tissue_mask, expectation): slide = Slide(slide_fixture, os.path.join(tmpdir, "processed")) expected_img = load_expectation( os.path.join("bbox-location-images", expectation), type_="png", ) bbox_location_img = slide.locate_biggest_tissue_box( tissue_mask=tissue_mask, scale_factor=3) np.testing.assert_array_almost_equal(np.asarray(bbox_location_img), expected_img)
def or_it_has_wrong_processed_path(self, request): """This test simulates a wrong user behaviour, using a None object instead of a str, or a path as processed_path param""" _resampled_dimensions = method_mock(request, Slide, "_resampled_dimensions") _resampled_dimensions.return_value = (1, 2, 3, 4) with pytest.raises(TypeError) as err: slide = Slide("path", None) slide.scaled_image_path(32) assert isinstance(err.value, TypeError) assert (str(err.value) == "expected str, bytes or os.PathLike object, not NoneType")
def it_can_save_thumbnail(self, tmpdir, resampled_dims_): 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")) resampled_dims_.return_value = (100, 200, 300, 400) slide.save_thumbnail() assert slide.thumbnail_path == os.path.join(tmp_path_, "processed", "thumbnails", "mywsi.png") assert os.path.exists(os.path.join(tmp_path_, slide.thumbnail_path))
def it_resamples_with_the_correct_scale_factor(self, tmpdir, resampled_dims_): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_155_249_240 image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, "processed") resampled_dims_.return_value = (500, 500, 15, 15) _resample = slide._resample(32) assert _resample[1].shape == (math.floor(500 / 32), math.floor(500 / 32), 3)
def but_it_raises_zero_division_error_when_scalefactor_is_0( self, dimensions_): """Considering the teset above, this one prove that a wrong behaviour of the user can cause a zerodivision error. In this case the scale_factor=0 generates the ZeroDivision Exception """ dimensions_.return_value = (300, 500) slide = Slide("/a/b/foo", "processed") with pytest.raises(ZeroDivisionError) as err: slide._resampled_dimensions(scale_factor=0) assert isinstance(err.value, ZeroDivisionError) assert str(err.value) == "division by zero"
def it_locates_the_mask(self, request, tmpdir, slide_fixture, tissue_mask, binary_mask, expectation): slide = Slide(slide_fixture, os.path.join(tmpdir, "processed")) expected_img = load_expectation( os.path.join("mask-location-images", expectation), type_="png", ) mask_location_img = slide.locate_mask(binary_mask, tissue_mask=tissue_mask, scale_factor=3) np.testing.assert_array_almost_equal(np.asarray(mask_location_img), expected_img)
def it_knows_its_thumbnails(self, request, tmpdir): tmp_path_ = tmpdir.mkdir("myslide") thumbnail_ = property_mock(request, Slide, "thumbnail") slide1 = Slide("foo/bar", "proc") slide2 = Slide("foo/bar", "proc") slideset = SlideSet(tmp_path_, os.path.join(tmp_path_, "processed"), []) slides = method_mock(request, SlideSet, "__iter__") slides.return_value = [slide1, slide2] slideset.thumbnails() assert thumbnail_.call_args_list == [call(), call()]
def it_knows_its_resampled_dimensions(self, dimensions_): """This test prove that given the dimensions (mock object here), it does the correct maths operations: `large_w, large_h = self.dimensions` `new_w = math.floor(large_w / self._scale_factor)` `new_h = math.floor(large_h / self._scale_factor)` `return large_w, large_h, new_w, new_h` """ dimensions_.return_value = (300, 500) slide = Slide("/a/b/foo", "processed") _resampled_dims = slide._resampled_dimensions(scale_factor=32) assert _resampled_dims == (300, 500, 9, 15)
def it_knows_its_name(self): slide = Slide(SVS.CMU_1_SMALL_REGION, os.path.join(SVS.CMU_1_SMALL_REGION, "processed")) name = slide.name assert name == ntpath.basename(SVS.CMU_1_SMALL_REGION).split(".")[0]
def it_can_construct_tissue_mask(self, wsi, expected_array): slide = Slide(wsi, os.path.join(wsi, "processed")) expected_array = load_expectation(expected_array, type_="npy") tissue_mask = TissueMask() np.testing.assert_array_almost_equal(tissue_mask(slide), expected_array)
def but_it_raises_error_with_negative_n_tiles_value(self, request, tmpdir): tmp_path_ = tmpdir.mkdir("myslide") image = PILIMG.RGBA_COLOR_500X500_155_249_240 image.save(os.path.join(tmp_path_, "mywsi.png"), "PNG") slide_path = os.path.join(tmp_path_, "mywsi.png") slide = Slide(slide_path, os.path.join(tmp_path_, "processed")) _scores = method_mock(request, ScoreTiler, "_scores") coords = CP(0, 10, 0, 10) _scores.return_value = [ (0.7, coords), (0.5, coords), (0.2, coords), (0.8, coords), (0.1, coords), ] _scorer = instance_mock(request, RandomScorer) score_tiler = ScoreTiler(_scorer, (10, 10), -1, 0) binary_mask = BiggestTissueBoxMask() with pytest.raises(ValueError) as err: score_tiler.extract(slide, binary_mask) assert isinstance(err.value, ValueError) assert str(err.value) == "'n_tiles' cannot be negative (-1)" _scores.assert_called_once_with(score_tiler, slide, binary_mask)
def it_constructs_from_args(self, request, slide_path, processed_path): _init_ = initializer_mock(request, Slide) slide = Slide(slide_path, processed_path) _init_.assert_called_once_with(ANY, slide_path, processed_path) assert isinstance(slide, Slide)
def but_it_raises_an_exception_if_file_not_found(self): with pytest.raises(FileNotFoundError) as err: slide = Slide("wrong/path/fake.wsi", "processed") slide._wsi assert isinstance(err.value, FileNotFoundError) assert str(err.value) == "The wsi path resource doesn't exist"