def evaluate_model_predictions( process_id: int, config: SegmentationModelBase, dataset: FullImageDataset, results_folder: Path) -> Tuple[PatientMetadata, MetricsDict]: """ Evaluates model segmentation predictions, dice scores and surface distances are computed. Generated contours are plotted and saved in results folder. The function is intended to be used in parallel for loop to process each image in parallel. :param process_id: Identifier for the process calling the function :param config: Segmentation model config object :param dataset: Dataset object, it is used to load intensity image, labels, and patient metadata. :param results_folder: Path to results folder :returns [PatientMetadata, list[list]]: Patient metadata and list of computed metrics for each image. """ sample = dataset.get_samples_at_index(index=process_id)[0] logging.info(f"Evaluating predictions for patient {sample.patient_id}") patient_results_folder = get_patient_results_folder( results_folder, sample.patient_id) segmentation = load_nifti_image(patient_results_folder / DEFAULT_RESULT_IMAGE_NAME).image metrics_per_class = metrics.calculate_metrics_per_class( segmentation, sample.labels, ground_truth_ids=config.ground_truth_ids, voxel_spacing=sample.image_spacing, patient_id=sample.patient_id) thumbnails_folder = results_folder / THUMBNAILS_FOLDER thumbnails_folder.mkdir(exist_ok=True) plotting.plot_contours_for_all_classes( sample, segmentation=segmentation, foreground_class_names=config.ground_truth_ids, result_folder=thumbnails_folder, image_range=config.output_range) return sample.metadata, metrics_per_class
def test_plot_contours_for_all_classes( test_output_dirs: TestOutputDirectories) -> None: size = (3, 3, 3) image = np.zeros((1, ) + size) for i, (z, y, x) in enumerate( itertools.product(range(size[0]), range(size[1]), range(size[2]))): image[0, z, y, x] = i # Create a fake label array: For each class, there is exactly 1 pixel foreground, at the z slice that is # equal to the class index labels = np.zeros((3, ) + size) labels[0, 0, 1, 1] = 1 labels[1, 1, 1, 1] = 1 labels[2, 2, 1, 1] = 1 # Fake segmentation: Classifies all foreground pixels correctly... segmentation = np.zeros(size) segmentation[1, 1, 1] = 1 segmentation[2, 1, 1] = 2 # ...but has an extra foreground pixel in the largest z slice in either top left or bottom right corner: segmentation[1, 0, 0] = 1 segmentation[2, 2, 2] = 2 sample = Sample(image=image, labels=labels, mask=np.ones(size), metadata=DummyPatientMetadata) plots = plotting.plot_contours_for_all_classes( sample, segmentation, foreground_class_names=["class1", "class2"], result_folder=Path(test_output_dirs.root_dir), result_prefix="prefix") expected = [ "prefix042_class1_slice_001.png", "prefix042_class2_slice_002.png" ] compare_files(plots, expected) with pytest.raises(ValueError) as err: plotting.plot_contours_for_all_classes( sample, segmentation, foreground_class_names=["background", "class1", "class2"], result_folder=Path(test_output_dirs.root_dir), result_prefix="prefix") assert "3 classes" in str(err) assert "background" in str(err)