Exemple #1
0
def launch_performance_metrics(path_prediction, path_groundtruth):
    """
    :param path_prediction : path of the prediction, output segmentation of AxonDeepSeg
    :param path_groundtruth : path of the ground truth labelling (gold standard)
    :return: axon_metrics, myelin_metrics
    """

    # Read segmentation image and get axon/myelin masks

    pred = ads.imread(path_prediction)
    pred_axon = pred > 200
    pred_myelin = np.logical_and(pred >= 50, pred <= 200)

    # Read groundtruth mask and get axon/myelin masks
    gt = ads.imread(path_groundtruth)
    gt_axon = gt > 200
    gt_myelin = np.logical_and(gt >= 50, gt <= 200)

    # Compute pixelwise metrics for axon segmentation
    axon_metrics = Metrics_calculator(pred_axon, gt_axon)
    axon_metrics_array = np.array([axon_metrics.pw_sensitivity(),axon_metrics.pw_specificity(),axon_metrics.pw_precision(),
    axon_metrics.pw_accuracy(), axon_metrics.pw_F1_score(), axon_metrics.pw_dice(), axon_metrics.pw_jaccard()])

    # Compute element-wise metrics for axon segmentation
    dice_output = axon_metrics.ew_dice('short')

    # Compute pixelwise metrics for myelin segmentation
    myelin_metrics = Metrics_calculator(pred_myelin, gt_myelin)
    myelin_metrics_array = np.array([myelin_metrics.pw_sensitivity(),myelin_metrics.pw_specificity(),myelin_metrics.pw_precision(),
    myelin_metrics.pw_accuracy(), myelin_metrics.pw_F1_score(), myelin_metrics.pw_dice(),myelin_metrics.pw_jaccard()])

    return axon_metrics, myelin_metrics
def generate_axons_from_myelin(path_prediction, path_myelin_corrected):
    """
    :param path_prediction: path of the prediction i.e. image of axon+myelin segmentation (output of AxonDeepSeg)
    :param path_myelin_corrected: path of corrected myelin by the user i.e. myelin mask (uint8 type with myelin=255, background=0)
    :return: merged and corrected axon+myelin image
    """

    # If string, convert to Path objects
    path_prediction = convert_path(path_prediction)
    path_myelin_corrected = convert_path(path_myelin_corrected)

    # read output from axondeepseg and myelin mask corrected by user
    prediction = ads.imread(path_prediction)
    myelin_corrected = ads.imread(path_myelin_corrected)

    # compute the axon mask from axondeepseg (axon=255, myelin=127, background=0)
    axon_ads = prediction > 200

    # get the myelin mask corrected by user (myelin=255, background=0)
    myelin_corrected = myelin_corrected > 200

    # compute logical OR between axondeepseg axon mask and myelin corrected mask
    fused = np.logical_or(axon_ads, myelin_corrected)

    # compute new axon mask by logical XOR between corrected myelin mask and fused
    new_axon_mask = np.logical_xor(myelin_corrected, fused)

    # merge corrected myelin mask and generated axon mask
    both = new_axon_mask * 255 + myelin_corrected * 127

    # save the corrected axon+myelin image
    path = path_prediction.parent / 'axon_myelin_mask_corrected.png'
    ads.imwrite(path, both)

    return both
    def setup(self):
        # Get the directory where this current file is saved
        self.fullPath = Path(__file__).resolve().parent

        self.test_files_path = (self.fullPath / '__test_files__' /
                                '__test_postprocessing_files__')
        print(self.test_files_path)

        self.before_floodfill_image = ads_utils.imread(
            (self.test_files_path / 'before_flood_fill.png'))
        self.after_floodfill_image = ads_utils.imread(
            (self.test_files_path / 'after_flood_fill.png'))
Exemple #4
0
def integrity_test():

    try:

        # get path of directory where AxonDeepSeg was installed
        dir_path = Path(__file__).resolve().parent

        # input parameters

        path = Path('folder_name') / 'file_name'
        path_testing = dir_path / 'data_test'
        model_name = 'default_SEM_model'
        path_model = dir_path / 'models' / model_name
        path_configfile = path_model / 'config_network.json'

        # Read the configuration file 
        print('Reading test configuration file.')
        if not path_model.exists():
            path_model.mkdir(parents=True)

        with open(path_configfile, 'r') as fd:
            config_network = json.loads(fd.read())

        # Launch the axon and myelin segmentation on test image sample provided in the installation
        print('Computing the segmentation of axon and myelin on test image.')
        prediction = axon_segmentation([path_testing], ["image.png"], path_model, config_network, prediction_proba_activate=True, verbosity_level=4)

        # Read the ground truth mask and the obtained segmentation mask
        mask = ads.imread(path_testing / 'mask.png')
        pred = ads.imread(path_testing / 'AxonDeepSeg.png')

        # Generate separate axon and myelin masks of the segmentation output
        print('Generating axon and myelin segmentation masks and saving.')
        gt_axon = mask > 200 # Generate binary image with the axons for the ground truth (myelin=127, axon=255)
        gt_myelin = np.logical_and(mask >= 50, mask <= 200) # Generate binary image with the myelin for the ground truth (myelin=127, axon=255)

        pred_axon = pred > 200 # Generate binary image with the axons for the segmentation (myelin=127, axon=255)
        pred_myelin = np.logical_and(pred >= 50, pred <= 200) # Generate binary image with the myelin for the segmentation (myelin=127, axon=255)

        # Compute Dice between segmentation and ground truth, for both axon and myelin
        dice_axon = pw_dice(pred_axon, gt_axon)
        dice_myelin = pw_dice(pred_myelin, gt_myelin)

        # If all the commands above are executed without bugs, the installation is done correctly
        print("* * * Integrity test passed. AxonDeepSeg is correctly installed. * * * ")
        return 0

    except IOError:

        # Else, there is a problem in the installation
        print("Integrity test failed... ")
        return -1
Exemple #5
0
def merge_masks(path_axon, path_myelin):
    # If string, convert to Path objects
    path_axon = convert_path(path_axon)

    axon = ads.imread(path_axon)
    myelin = ads.imread(path_myelin)

    both = (axon / 255) * 255 + (myelin / 255) * 127

    # get main path
    path_folder = path_axon.parent

    # save the masks
    ads.imwrite(path_folder / 'axon_myelin_mask.png', both)

    return both
def launch_morphometrics_computation(path_img, path_prediction):
    """
    This function is equivalent to the morphometrics_extraction notebook of AxonDeepSeg.
    It automatically performs all steps (computations, savings, displays,...) of the
    morphometrics extraction of a given sample.
    :param path_img: path of the input image (microscopy sample)
    :param path_prediction: path of the segmented image (output of AxonDeepSeg)
    :return: none.
    """
    
    # If string, convert to Path objects
    path_img = convert_path(path_img)
    path_prediction = convert_path(path_prediction)

    try:
        # Read image
        img = ads.imread(path_img)

        # Read prediction
        pred = ads.imread(path_prediction)
    except (IOError, OSError) as e:
        print(("launch_morphometrics_computation: " + str(e)))
        raise
    else:

        # Get axon and myelin masks
        pred_axon = pred > 200
        pred_myelin = np.logical_and(pred >= 50, pred <= 200)

        # Get folder path
        path_folder = path_img.parent

        # Compute and save axon morphometrics
        stats_array = get_axon_morphometrics(pred_axon, path_folder)
        save_axon_morphometrics(path_folder, stats_array)

        # Generate and save displays of axon morphometrics
        fig = draw_axon_diameter(img, path_prediction, pred_axon, pred_myelin)
        save_map_of_axon_diameters(path_folder, fig)

        # Compute and save aggregate morphometrics
        aggregate_metrics = get_aggregate_morphometrics(
            pred_axon, pred_myelin, path_folder
        )
        write_aggregate_morphometrics(path_folder, aggregate_metrics)
Exemple #7
0
    def __load__(self, id_name):
        """
          Loads images and masks
          :param ids_name: String, id name of a particular image/mask.
        """

        ## Path
        image_path = self.path / ("image_" + id_name + ".png")
        mask_path = self.path / ("mask_" + id_name + ".png")
        ## Reading Image
        image = ads.imread(str(image_path))
        image = np.reshape(image, (self.image_size, self.image_size, 1))

        # -----Mask PreProcessing --------
        mask = ads.imread(str(mask_path))
        mask = descritize_mask(mask, self.thresh_indices)
        # ---------------------------
        return (image, mask)
Exemple #8
0
    def load_png_image_from_path(self,
                                 image_path,
                                 is_mask=False,
                                 add_to_overlayList=True,
                                 colormap="greyscale"):
        """
        This function converts a 2D image into a NIfTI image and loads it as an overlay.
        The parameter add_to_overlayList allows to display the overlay into FSLeyes.
        :param image_path: The location of the image, including the name and the .extension
        :type image_path: Path
        :param is_mask: (optional) Whether or not this is a segmentation mask. It will be treated as a normal
        image by default.
        :type is_mask: bool
        :param add_to_overlayList: (optional) Whether or not to add the image to the overlay list. If so, the image will
        be displayed in the application. This parameter is True by default.
        :type add_to_overlayList: bool
        :param colormap: (optional) the colormap of image that will be displayed. This parameter is set to greyscale by
        default.
        :type colormap: string
        :return: the FSLeyes overlay corresponding to the loaded image.
        :rtype: overlay
        """

        # Open the 2D image
        img_png2D = ads_utils.imread(image_path)

        if is_mask is True:
            img_png2D = img_png2D // params.intensity[
                'binary']  # Segmentation masks should be binary

        # Flip the image on the Y axis so that the morphometrics file shows the right coordinates
        img_png2D = np.flipud(img_png2D)

        # Convert image data into a NIfTI image
        # Note: PIL and NiBabel use different axis conventions, so some array manipulation has to be done.
        img_NIfTI = nib.Nifti1Image(np.rot90(img_png2D, k=1, axes=(1, 0)),
                                    np.eye(4))

        # Save the NIfTI image in a temporary directory
        img_name = image_path.stem
        out_file = self.ads_temp_dir.__str__() + "/" + img_name + ".nii.gz"
        nib.save(img_NIfTI, out_file)

        # Load the NIfTI image as an overlay
        img_overlay = ovLoad.loadOverlays(paths=[out_file],
                                          inmem=True,
                                          blocking=True)[0]

        # Display the overlay
        if add_to_overlayList is True:
            self.overlayList.append(img_overlay)
            opts = self.displayCtx.getOpts(img_overlay)
            opts.cmap = colormap

        return img_overlay
Exemple #9
0
    def on_load_mask_button(self, event):
        """
        This function is called when the user presses on the loadMask button. It allows the user to select an existing
        PNG mask, convert it into a NIfTI and load it into FSLeyes.
        The mask needs to contain an axon + myelin mask. The Axons should have an intensity > 200. The myelin should
        have an intensity between 100 and 200. The data should be in uint8.
        """
        # Ask the user to select the mask image
        with wx.FileDialog(self,
                           "select mask .png file",
                           style=wx.FD_OPEN
                           | wx.FD_FILE_MUST_EXIST) as file_dialog:

            if (file_dialog.ShowModal() == wx.ID_CANCEL
                ):  # The user cancelled the operation
                return

            in_file = file_dialog.GetPath()

        # Check if the image format is valid
        image_extension = os.path.splitext(in_file)[1]
        valid_extensions = [".png", ".tif", ".jpg", ".jpeg"]
        if image_extension not in valid_extensions:
            self.show_message("Invalid file extension")
            return

        # Get the image data
        img_png2D = ads_utils.imread(in_file)

        image_name = os.path.basename(in_file)
        image_name = image_name.split(image_extension)[0]

        # Extract the Axon mask
        axon_mask = img_png2D > 200
        axon_mask = params.intensity['binary'] * np.array(axon_mask,
                                                          dtype=np.uint8)

        # Extract the Myelin mask
        myelin_mask = (img_png2D > 100) & (img_png2D < 200)
        myelin_mask = params.intensity['binary'] * np.array(myelin_mask,
                                                            dtype=np.uint8)

        # Load the masks into FSLeyes
        axon_outfile = self.ads_temp_dir.name + "/" + image_name + "-axon.png"
        ads_utils.imwrite(axon_outfile, axon_mask)
        self.load_png_image_from_path(axon_outfile,
                                      is_mask=True,
                                      colormap="blue")

        myelin_outfile = self.ads_temp_dir.name + "/" + image_name + "-myelin.png"
        ads_utils.imwrite(myelin_outfile, myelin_mask)
        self.load_png_image_from_path(myelin_outfile,
                                      is_mask=True,
                                      colormap="red")
Exemple #10
0
def load_acquisitions(path_acquisitions,
                      acquisitions_resolutions,
                      resampled_resolutions,
                      verbose_mode=0):
    """
    Load and resamples acquisitions located in the indicated folders' paths.
    :param path_acquisitions: List of paths to the acquisitions images.
    :param acquisitions_resolutions: List of float containing the resolutions the acquisitions were acquired with.
    :param resampled_resolutions: List of resolutions (floats) to resample to.
    :param verbose_mode: Int, how much information to display.
    :return:
    """
    # If string, convert to Path objects
    path_acquisitions = convert_path(path_acquisitions)

    path_acquisitions, acquisitions_resolutions, resampled_resolutions = list(
        map(ensure_list_type, [
            path_acquisitions, acquisitions_resolutions, resampled_resolutions
        ]))

    if verbose_mode >= 2:
        print("Loading acquisitions ...")

    # Reading acquisitions images and loading them in the RAM, with their respective acquisition resolution.
    # Then resampling the acquisitions images to the target resolution that the network uses.

    original_acquisitions, resampled_acquisitions, original_acquisitions_shapes = [], [], []

    for path_img in path_acquisitions:

        original_acquisitions.append(ads.imread(path_img))
        original_acquisitions_shapes.append(original_acquisitions[-1].shape)

    # Resampling acquisitions to the target resolution

    if verbose_mode >= 2:
        print("Rescaling acquisitions to the target resolution ...")

    resampling_coeffs = [
        current_acquisition_resolution / resampled_resolutions[i] for i,
        current_acquisition_resolution in enumerate(acquisitions_resolutions)
    ]

    for i, current_original_acquisition in enumerate(original_acquisitions):
        resampled_acquisitions.append(
            rescale(current_original_acquisition,
                    resampling_coeffs[i],
                    preserve_range=True).astype(int))

    return resampled_acquisitions, resampling_coeffs, original_acquisitions_shapes
    def test_generate_axon_numbers_image_returns_expected_array(self):
        # Load the test image
        expected_image = ads_utils.imread(
            (self.test_files_path / 'test_numbers_image.png'))
        expected_image = np.array(expected_image)

        # Atempt to recreate the test image
        obtained_image = postprocessing.generate_axon_numbers_image(
            centroid_index=np.array([0]),
            x0_array=[20],
            y0_array=[10],
            image_size=(30, 30),
            mean_axon_diameter_in_pixels=6)
        assert np.array_equal(expected_image, obtained_image)
Exemple #12
0
def patched_to_dataset(path_patched_data,
                       path_dataset,
                       type_,
                       random_seed=None):
    """
    Creates a dataset using already created patches.
    :param path_patched_data: Path to where to find the folders where the patches folders are located.
    :param path_dataset: Path to where to create the newly formed dataset.
    :param type_: String, either 'unique' or 'mixed'. Unique means that we create a dataset with only TEM or only SEM
    data. "Mixed" means that we are creating a dataset with both type of images.
    :param random_seed: Int, the random seed to use to be able to consistenly recreate generated datasets.
    :return: None.
    """

    # If string, convert to Path objects
    path_patched_data = convert_path(path_patched_data)
    path_dataset = convert_path(path_dataset)

    # Using the randomseed fed so that given a fixed input, the generation of the datasets is always the same.
    np.random.seed(random_seed)

    # First we define where we are going to store the patched data
    if not path_dataset.exists():
        path_dataset.mkdir(parents=True)

    # First case: there is only one type of acquisition to use.
    if type_ == 'unique':

        i = 0  # Total patches index

        # We loop through all folders containing patches
        patches_folder_names = [f for f in path_patched_data.iterdir()]
        for patches_folder in tqdm(patches_folder_names):

            path_patches_folder = path_patched_data / patches_folder.name
            if path_patches_folder.is_dir():

                # We are now in the patches folder
                L_img, L_mask = [], []
                filenames = [f for f in path_patches_folder.iterdir()]
                for data in filenames:
                    root, index = data.stem.split('_')

                    if 'image' in data.name:
                        img = ads.imread(path_patches_folder / data.name)
                        L_img.append((img, int(index)))

                    elif 'mask' in data.name:
                        mask = ads.imread(path_patches_folder / data.name)
                        L_mask.append((mask, int(index)))

                # Now we sort the patches to be sure we get them in the right order
                L_img_sorted, L_mask_sorted = sort_list_files(L_img, L_mask)

                # Saving the images in the new folder
                for img, k in L_img_sorted:
                    ads.imwrite(path_dataset.joinpath('image_%s.png' % i), img)
                    ads.imwrite(path_dataset.joinpath('mask_%s.png' % i),
                                L_mask_sorted[k][0])
                    i = i + 1  # Using the global i here.

    # Else we are using different types of acquisitions. It's important to have them separated in a SEM folder
    # and in a TEM folder.
    elif type_ == 'mixed':
        # We determine which acquisition type we are going to upsample (understand : take the same images multiple times)
        SEM_patches_folder = path_patched_data / 'SEM'
        TEM_patches_folder = path_patched_data / 'TEM'

        minority_patches_folder, len_minority, majority_patches_folder, len_majority = find_minority_type(
            SEM_patches_folder, TEM_patches_folder)

        # First we move all patches from the majority acquisition type to the new dataset
        foldernames = [
            folder.name for folder in majority_patches_folder.iterdir()
        ]
        i = 0
        for patches_folder in tqdm(foldernames):

            path_patches_folder = majority_patches_folder / patches_folder
            if path_patches_folder.is_dir():
                # We are now in the patches folder
                L_img, L_mask = [], []
                filenames = [f for f in path_patches_folder.iterdir()]
                for data in path_patches_folder.iterdir():
                    root, index = data.stem.split('_')

                    if 'image' in data.name:
                        img = ads.imread(path_patches_folder / data.name)
                        L_img.append((img, int(index)))

                    elif 'mask' in data.name:
                        mask = ads.imread(path_patches_folder / data.name)
                        L_mask.append((mask, int(index)))
                # Now we sort the patches to be sure we get them in the right order
                L_img_sorted, L_mask_sorted = sort_list_files(L_img, L_mask)

                # Saving the images in the new folder
                for img, k in L_img_sorted:
                    ads.imwrite(path_dataset.joinpath('image_%s.png' % i), img)
                    ads.imwrite(path_dataset.joinpath('mask_%s.png' % i),
                                L_mask_sorted[k][0])
                    i = i + 1
        # Then we stratify - oversample the minority acquisition to the new dataset

        # We determine the ratio to take
        ratio_oversampling = float(len_majority) / len_minority

        # We go through each image folder in the minorty patches
        foldernames = [
            folder.name for folder in minority_patches_folder.iterdir()
        ]
        for patches_folder in tqdm(foldernames):

            path_patches_folder = minority_patches_folder / patches_folder
            if path_patches_folder.is_dir():

                # We are now in the patches folder
                # We load every image
                filenames = [f for f in path_patches_folder.iterdir()]
                n_img = np.floor(len(filenames) / 2)

                for data in filenames:
                    root, index = data.stem.split('_')
                    if 'image' in data.name:
                        img = ads.imread(path_patches_folder / data.name)
                        L_img.append((img, int(index)))

                    elif 'mask' in data.name:
                        mask = ads.imread(path_patches_folder / data.name)
                        L_mask.append((mask, int(index)))

                # Now we sort the patches to be sure we get them in the right order
                L_img_sorted, L_mask_sorted = sort_list_files(L_img, L_mask)
                L_merged_sorted = np.asarray([
                    L_img_sorted[j] + L_mask_sorted[j]
                    for j in range(len(L_img_sorted))
                ])

                # We create a new array composed of enough elements so that the two types of acquisitions are balanced
                # (oversampling)
                L_elements_to_save = L_merged_sorted[
                    np.random.choice(int(L_merged_sorted.shape[0]),
                                     int(np.ceil(ratio_oversampling * n_img)),
                                     replace=True), :]

                # Finally we save all the images in order at the new dataset path.
                for j in range(L_elements_to_save.shape[0]):
                    img = L_elements_to_save[j][0]
                    mask = L_elements_to_save[j][2]
                    ads.imwrite(path_dataset.joinpath('image_%s.png' % i), img)
                    ads.imwrite(path_dataset.joinpath('mask_%s.png' % i), mask)
                    i = i + 1
Exemple #13
0
def raw_img_to_patches(path_raw_data,
                       path_patched_data,
                       thresh_indices=[0, 0.2, 0.8],
                       patch_size=512,
                       resampling_resolution=0.1):
    """
    Transform a raw acquisition to a folder of patches of size indicated in the arguments. Also performs resampling.
    Note: this functions needs to be run as many times as there are different general pixel size
    (thus different acquisition types / resolutions).
    :param path_raw_data: Path to where the raw image folders are located.
    :param path_patched_data: Path to where we will store the patched acquisitions.
    :param thresh_indices: List of float, determining the thresholds separating the classes.
    :param patch_size: Int, size of the patches to generate (and consequently input size of the network).
    :param resampling_resolution: Float, the resolution we need to resample to so that each sample
    has the same resolution in a dataset.
    :return: Nothing.
    """

    # If string, convert to Path objects
    path_raw_data = convert_path(path_raw_data)
    path_patched_data = convert_path(path_patched_data)

    # First we define where we are going to store the patched data and we create the directory if it does not exist.
    if not path_patched_data.exists():
        path_patched_data.mkdir(parents=True)

    # Loop over each raw image folder
    img_folder_names = [im.name for im in path_raw_data.iterdir()]
    for img_folder in tqdm(img_folder_names):
        path_img_folder = path_raw_data / img_folder
        if path_img_folder.is_dir():

            # We are now in the image folder.
            file = open(path_img_folder / 'pixel_size_in_micrometer.txt', 'r')
            pixel_size = float(file.read())
            resample_coeff = float(
                pixel_size
            ) / resampling_resolution  # Used to set the resolution to the general_pixel_size

            # We go through every file in the image folder
            data_names = [d.name for d in path_img_folder.iterdir()]
            for data in data_names:
                if 'image' in data:  # If it's the raw image.

                    img = ads.imread(path_img_folder / data)
                    img = rescale(img,
                                  resample_coeff,
                                  preserve_range=True,
                                  mode='constant').astype(int)

                elif 'mask' in data:
                    mask_init = ads.imread(path_img_folder / data)
                    mask = rescale(mask_init,
                                   resample_coeff,
                                   preserve_range=True,
                                   mode='constant',
                                   order=0)

                    # Set the mask values to the classes' values
                    mask = labellize_mask_2d(
                        mask, thresh_indices
                    )  # shape (size, size), values float 0.0-1.0

            to_extract = [img, mask]
            patches = extract_patch(to_extract, patch_size)
            # The patch extraction is done, now we put the new patches in the corresponding folders

            # We create it if it does not exist
            path_patched_folder = path_patched_data / img_folder
            if not path_patched_folder.exists():
                path_patched_folder.mkdir(parents=True)

            for j, patch in enumerate(patches):
                ads.imwrite(path_patched_folder.joinpath('image_%s.png' % j),
                            patch[0])
                ads.imwrite(path_patched_folder.joinpath('mask_%s.png' % j),
                            patch[1])
Exemple #14
0
def segment_image(path_testing_image,
                  path_model,
                  overlap_value,
                  config,
                  resolution_model,
                  acquired_resolution=None,
                  verbosity_level=0):
    '''
    Segment the image located at the path_testing_image location.
    :param path_testing_image: the path of the image to segment.
    :param path_model: where to access the model
    :param overlap_value: the number of pixels to be used for overlap when doing prediction. Higher value means less
    border effects but more time to perform the segmentation.
    :param config: dict containing the configuration of the network
    :param resolution_model: the resolution the model was trained on.
    :param verbosity_level: Level of verbosity. The higher, the more information is given about the segmentation
    process.
    :return: Nothing.
    '''

    # If string, convert to Path objects
    path_testing_image = convert_path(path_testing_image)
    path_model = convert_path(path_model)

    if path_testing_image.exists():

        # Extracting the image name and its folder path from the total path.
        path_parts = path_testing_image.parts
        acquisition_name = Path(path_parts[-1])
        path_acquisition = Path(*path_parts[:-1])

        # Get type of model we are using
        selected_model = path_model.name

        # Read image
        img = ads.imread(str(path_testing_image))

        # Generate tmp file
        fp = open(path_acquisition / '__tmp_segment__.png', 'wb+')

        img_name_original = acquisition_name.stem

        ads.imwrite(fp, img, format='png')

        acquisition_name = Path(fp.name).name
        segmented_image_name = img_name_original + '_seg-axonmyelin' + '.png'

        # Performing the segmentation

        axon_segmentation(path_acquisitions_folders=path_acquisition,
                          acquisitions_filenames=[acquisition_name],
                          path_model_folder=path_model,
                          config_dict=config,
                          ckpt_name='model',
                          inference_batch_size=1,
                          overlap_value=overlap_value,
                          segmentations_filenames=segmented_image_name,
                          resampled_resolutions=resolution_model,
                          verbosity_level=verbosity_level,
                          acquired_resolution=acquired_resolution,
                          prediction_proba_activate=False,
                          write_mode=True)

        if verbosity_level >= 1:
            print(("Image {0} segmented.".format(path_testing_image)))

        # Remove temporary file used for the segmentation
        fp.close()
        (path_acquisition / '__tmp_segment__.png').unlink()

    else:
        print(("The path {0} does not exist.".format(path_testing_image)))

    return None
Exemple #15
0
def main(argv=None):
    '''
    Main loop.
    :return: Exit code.
        0: Success
        2: Invalid argument value
        3: Missing value or file
    '''
    print(('AxonDeepSeg v.{}'.format(AxonDeepSeg.__version__)))
    ap = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)

    requiredName = ap.add_argument_group('required arguments')

    # Setting the arguments of the segmentation
    requiredName.add_argument(
        '-t',
        '--type',
        required=True,
        choices=['SEM', 'TEM'],
        help='Type of acquisition to segment. \n' +
        'SEM: scanning electron microscopy samples. \n' +
        'TEM: transmission electron microscopy samples. ')
    requiredName.add_argument(
        '-i',
        '--imgpath',
        required=True,
        nargs='+',
        help='Path to the image to segment or path to the folder \n' +
        'where the image(s) to segment is/are located.')

    ap.add_argument(
        "-m",
        "--model",
        required=False,
        help='Folder where the model is located. \n' +
        'The default SEM model path is: \n' + str(default_SEM_path) + '\n' +
        'The default TEM model path is: \n' + str(default_TEM_path) + '\n')
    ap.add_argument(
        '-s',
        '--sizepixel',
        required=False,
        help='Pixel size of the image(s) to segment, in micrometers. \n' +
        'If no pixel size is specified, a pixel_size_in_micrometer.txt \n' +
        'file needs to be added to the image folder path. The pixel size \n' +
        'in that file will be used for the segmentation.',
        default=None)
    ap.add_argument(
        '-v',
        '--verbose',
        required=False,
        type=int,
        choices=list(range(0, 4)),
        help='Verbosity level. \n' +
        '0 (default) : Displays the progress bar for the segmentation. \n' +
        '1: Also displays the path of the image(s) being segmented. \n' +
        '2: Also displays the information about the prediction step \n' +
        '   for the segmentation of current sample. \n' +
        '3: Also displays the patch number being processed in the current sample.',
        default=0)
    ap.add_argument(
        '-o',
        '--overlap',
        required=False,
        type=int,
        help=
        'Overlap value (in pixels) of the patches when doing the segmentation. \n'
        +
        'Higher values of overlap can improve the segmentation at patch borders, \n'
        + 'but also increase the segmentation time. \n' + 'Default value: ' +
        str(default_overlap) + '\n' +
        'Recommended range of values: [10-100]. \n',
        default=25)
    ap._action_groups.reverse()

    # Processing the arguments
    args = vars(ap.parse_args(argv))
    type_ = str(args["type"])
    verbosity_level = int(args["verbose"])
    overlap_value = int(args["overlap"])
    if args["sizepixel"] is not None:
        psm = float(args["sizepixel"])
    else:
        psm = None
    path_target_list = [Path(p) for p in args["imgpath"]]
    new_path = Path(args["model"]) if args["model"] else None

    # Preparing the arguments to axon_segmentation function
    path_model, config = generate_default_parameters(type_, new_path)
    resolution_model = generate_resolution(type_,
                                           config["trainingset_patchsize"])

    # Tuple of valid file extensions
    validExtensions = (".jpeg", ".jpg", ".tif", ".tiff", ".png")

    # Going through all paths passed into arguments
    for current_path_target in path_target_list:

        if not current_path_target.is_dir():

            if current_path_target.suffix.lower() in validExtensions:

                # Handle cases if no resolution is provided on the CLI
                if psm == None:

                    # Check if a pixel size file exists, if so read it.
                    if (current_path_target.parent /
                            'pixel_size_in_micrometer.txt').exists():

                        resolution_file = open(
                            current_path_target.parent /
                            'pixel_size_in_micrometer.txt', 'r')

                        psm = float(resolution_file.read())

                    else:

                        print(
                            "ERROR: No pixel size is provided, and there is no pixel_size_in_micrometer.txt file in image folder. ",
                            "Please provide a pixel size (using argument -s), or add a pixel_size_in_micrometer.txt file ",
                            "containing the pixel size value.")
                        sys.exit(3)

                # Check that image size is large enough for given resolution to reach minimum patch size after resizing.

                try:
                    height, width, _ = ads.imread(
                        str(current_path_target)).shape
                except:
                    try:
                        height, width = ads.imread(
                            str(current_path_target)).shape
                    except Exception as e:
                        raise e

                image_size = [height, width]
                minimum_resolution = config[
                    "trainingset_patchsize"] * resolution_model / min(
                        image_size)

                if psm < minimum_resolution:
                    print(
                        "EXCEPTION: The size of one of the images ({0}x{1}) is too small for the provided pixel size ({2}).\n"
                        .format(height, width, psm),
                        "The image size must be at least {0}x{0} after resampling to a resolution of {1} to create standard sized patches.\n"
                        .format(config["trainingset_patchsize"],
                                resolution_model),
                        "One of the dimensions of the image has a size of {0} after resampling to that resolution.\n"
                        .format(round(psm * min(image_size) /
                                      resolution_model)),
                        "Image file location: {0}".format(current_path_target))

                    sys.exit(2)

                # Performing the segmentation over the image
                segment_image(current_path_target,
                              path_model,
                              overlap_value,
                              config,
                              resolution_model,
                              acquired_resolution=psm,
                              verbosity_level=verbosity_level)

                print("Segmentation finished.")

            else:
                print(
                    "The path(s) specified is/are not image(s). Please update the input path(s) and try again."
                )
                break

        else:

            # Handle cases if no resolution is provided on the CLI
            if psm == None:

                # Check if a pixel size file exists, if so read it.
                if (current_path_target /
                        'pixel_size_in_micrometer.txt').exists():

                    resolution_file = open(
                        current_path_target / 'pixel_size_in_micrometer.txt',
                        'r')

                    psm = float(resolution_file.read())

                else:

                    print(
                        "ERROR: No pixel size is provided, and there is no pixel_size_in_micrometer.txt file in image folder. ",
                        "Please provide a pixel size (using argument -s), or add a pixel_size_in_micrometer.txt file ",
                        "containing the pixel size value.")
                    sys.exit(3)

            # Performing the segmentation over all folders in the specified folder containing acquisitions to segment.
            segment_folders(current_path_target,
                            path_model,
                            overlap_value,
                            config,
                            resolution_model,
                            acquired_resolution=psm,
                            verbosity_level=verbosity_level)

            print("Segmentation finished.")

    sys.exit(0)
Exemple #16
0
def segment_folders(path_testing_images_folder,
                    path_model,
                    overlap_value,
                    config,
                    resolution_model,
                    acquired_resolution=None,
                    verbosity_level=0):
    '''
    Segments the images contained in the image folders located in the path_testing_images_folder.
    :param path_testing_images_folder: the folder where all image folders are located (the images to segment are located
    in those image folders)
    :param path_model: where to access the model.
    :param overlap_value: the number of pixels to be used for overlap when doing prediction. Higher value means less
    border effects but more time to perform the segmentation.
    :param config: dict containing the configuration of the network
    :param resolution_model: the resolution the model was trained on.
    :param verbosity_level: Level of verbosity. The higher, the more information is given about the segmentation
    process.
    :return: Nothing.
    '''

    # If string, convert to Path objects
    path_testing_images_folder = convert_path(path_testing_images_folder)
    path_model = convert_path(path_model)

    # Update list of images to segment by selecting only image files (not already segmented or not masks)
    img_files = [
        file for file in path_testing_images_folder.iterdir()
        if (file.suffix.lower() in ('.png', '.jpg', '.jpeg', '.tif', '.tiff'))
        and (not str(file).endswith(('_seg-axonmyelin.png', '_seg-axon.png',
                                     '_seg-myelin.png', 'mask.png')))
    ]

    # Pre-processing: convert to png if not already done and adapt to model contrast
    for file_ in tqdm(img_files, desc="Segmentation..."):
        print(path_testing_images_folder / file_)
        try:
            height, width, _ = ads.imread(
                str(path_testing_images_folder / file_)).shape
        except:
            try:
                height, width = ads.imread(
                    str(path_testing_images_folder / file_)).shape
            except Exception as e:
                raise e

        image_size = [height, width]
        minimum_resolution = config[
            "trainingset_patchsize"] * resolution_model / min(image_size)

        if acquired_resolution < minimum_resolution:
            print(
                "EXCEPTION: The size of one of the images ({0}x{1}) is too small for the provided pixel size ({2}).\n"
                .format(height, width, acquired_resolution),
                "The image size must be at least {0}x{0} after resampling to a resolution of {1} to create standard sized patches.\n"
                .format(config["trainingset_patchsize"], resolution_model),
                "One of the dimensions of the image has a size of {0} after resampling to that resolution.\n"
                .format(
                    round(acquired_resolution * min(image_size) /
                          resolution_model)),
                "Image file location: {0}".format(
                    str(path_testing_images_folder / file_)))

            sys.exit(2)

        selected_model = path_model.name

        # Read image for conversion
        img = ads.imread(str(path_testing_images_folder / file_))

        # Generate tmpfile for segmentation pipeline
        fp = open(path_testing_images_folder / '__tmp_segment__.png', 'wb+')

        img_name_original = file_.stem

        ads.imwrite(fp, img, format='png')

        acquisition_name = Path(fp.name).name
        segmented_image_name = img_name_original + '_seg-axonmyelin' + '.png'

        axon_segmentation(path_acquisitions_folders=path_testing_images_folder,
                          acquisitions_filenames=[acquisition_name],
                          path_model_folder=path_model,
                          config_dict=config,
                          ckpt_name='model',
                          inference_batch_size=1,
                          overlap_value=overlap_value,
                          segmentations_filenames=[segmented_image_name],
                          acquired_resolution=acquired_resolution,
                          verbosity_level=verbosity_level,
                          resampled_resolutions=resolution_model,
                          prediction_proba_activate=False,
                          write_mode=True)

        if verbosity_level >= 1:
            tqdm.write("Image {0} segmented.".format(
                str(path_testing_images_folder / file_)))

        # Remove temporary file used for the segmentation
        fp.close()
        (path_testing_images_folder / '__tmp_segment__.png').unlink()

    return None
def generate_statistics(path_model_folder,
                        path_images_folder,
                        resampled_resolution,
                        overlap_value,
                        verbosity_level=0):
    """
    Generates the implemented statistics for all the checkpoints of a given model, for each requested image.
    :param path_model_folder: Path to the model to use.
    :param path_images_folder: Path to the folders that contain the images to compute the metrics on.
    :param resampled_resolution: Float, the resolution to resample to to make the predictions.
    :param overlap_value: Int, the number of pixels to use for overlap.
    :param verbosity_level: Int. The higher, the more displayed information.
    :return:
    """
    print(path_images_folder)
    # If string, convert to Path objects
    path_model_folder = convert_path(path_model_folder)
    path_images_folder = convert_path(path_images_folder)
    print(path_images_folder)

    model_statistics_dict = {"date": time.strftime("%Y-%m-%d"), "data": {}}
    # First we load the network parameters from the config file
    with open(path_model_folder / 'config_network.json', 'r') as fd:
        config_network = json.loads(fd.read())

    n_classes = config_network['n_classes']
    model_name = path_model_folder.parts[
        -2]  # Extraction of the name of the model.

    # We loop over all checkpoint files to compute statistics for each checkpoint.
    for checkpoint in path_model_folder.iterdir():

        if str(checkpoint)[-10:] == '.ckpt.meta':

            result_model = {}
            name_checkpoint = str(checkpoint)[:-10]

            result_model.update({
                'id_model': model_name,
                'ckpt': name_checkpoint,
                'config': config_network
            })

            # 1/ We load the saved training statistics, which are independent of the testing images

            try:
                f = open(path_model_folder + '/' + name_checkpoint + '.pkl',
                         'r')
                res = pickle.load(f)
                acc_stats = res['accuracy']
                loss_stats = res['loss']
                epoch_stats = res['steps']

            except:
                print('No stats file found...')
                #f = open(path_model_folder + '/evolution.pkl', 'r')
                #res = pickle.load(f)
                #epoch_stats = max(res['steps'])
                #acc_stats = np.mean(res['accuracy'][-10:])
                #loss_stats = np.mean(res['loss'][-10:])

                epoch_stats = None
                acc_stats = None
                loss_stats = None

            result_model.update({
                'training_stats': {
                    'training_epoch': epoch_stats,
                    'training_mvg_avg10_acc': acc_stats,
                    'training_mvg_avg10_loss': loss_stats
                },
                'testing_stats': {}
            })

            # 2/ Computation of the predictions / outputs of the network for each image at the same time.

            predictions, outputs_network = axon_segmentation(
                path_images_folder, ['image.png'] * len(path_images_folder),
                path_model_folder,
                config_network,
                ckpt_name=name_checkpoint,
                overlap_value=overlap_value,
                resampled_resolutions=[resampled_resolution] *
                len(path_images_folder),
                prediction_proba_activate=True,
                write_mode=False,
                gpu_per=1.0,
                verbosity_level=verbosity_level)
            # These two variables are list, as long as the number of images that are tested.

            if verbosity_level >= 2:
                print('Statistics extraction...')

            # 3/ Computation of the statistics for each image.
            for i, image_folder in tqdm(enumerate(path_images_folder)):

                current_prediction = predictions[i]
                current_network_output = outputs_network[i]

                # Reading the images and processing them
                mask_raw = ads.imread(image_folder / 'mask.png')
                mask = labellize(mask_raw)

                # We infer the name of the different files
                name_image = image_folder.name

                # Computing metrics and storing them in the json file.
                current_proba = output_network_to_proba(
                    current_network_output, n_classes)
                testing_stats_dict = compute_metrics(current_prediction,
                                                     current_proba, mask,
                                                     n_classes)
                result_model['testing_stats'].update(
                    {name_image: testing_stats_dict})

            # We add the metrics for all the checkpoints from this model (on all images) to the data list.
            model_statistics_dict["data"].update(
                {name_checkpoint: result_model})

    return model_statistics_dict
Exemple #18
0
def visualize_segmentation(path):
    """
    :param path: path of the folder including the data and the results obtained
        after by the segmentation process.
    :return: list of matplotlib.figure.Figure
    if there is a mask (ground truth) in the folder,
    scores are calculated: sensitivity, errors and dice
    figure(1) segmentation without mrf
    figure(2) segmentation with mrf
    if there is MyelinSeg.jpg in the folder, myelin and image, myelin and axon
    segmentated, myelin and groundtruth are represented
    """
    # If string, convert to Path objects
    path = convert_path(path)

    figs = []

    def _create_fig_helper(overlayed_img, fig_title):
        """
        Helper function to create a figure
        :param overlayed_img: the image to add on top on image_init
        :param fig_title: str title of the figure
        :return: matplotlib.figure.Figure
        """
        fig = Figure()
        FigureCanvas(fig)
        ax = fig.subplots()
        ax.set_title(fig_title)
        ax.imshow(image_init, cmap="gray")
        ax.imshow(overlayed_img, cmap="hsv", alpha=0.7)
        return fig

    path_img = path / "image.png"
    mask = False
    cur_dir_items = [item.name for item in path]

    if "results.pkl" not in cur_dir_items:
        print("results not present")

    file = open(path + "/results.pkl", "r")
    res = pickle.load(file)

    prediction_mrf = res["prediction_mrf"]
    prediction = res["prediction"]

    image_init = ads.imread(path_img)
    predict = np.ma.masked_where(prediction == 0, prediction)
    predict_mrf = np.ma.masked_where(prediction_mrf == 0, prediction_mrf)

    title = "Axon Segmentation (with mrf) mask"
    fig1 = _create_fig_helper(predict_mrf, title)
    figs.append(fig1)

    title = "Axon Segmentation (without mrf) mask"
    fig2 = _create_fig_helper(predict, title)
    figs.append(fig2)

    if "mask.png" in cur_dir_items:
        Mask = True
        path_mask = path / "mask.png"
        mask = preprocessing.binarize(ads.imread(path_mask), threshold=125)

        acc = accuracy_score(prediction.reshape(-1, 1), mask.reshape(-1, 1))
        score = score_analysis(image_init, mask, prediction)
        Dice = dice(image_init, mask, prediction)["dice"]
        Dice_mean = Dice.mean()
        acc_mrf = accuracy_score(prediction_mrf.reshape(-1, 1),
                                 mask.reshape(-1, 1))
        score_mrf = score_analysis(image_init, mask, prediction_mrf)
        Dice_mrf = dice(image_init, mask, prediction_mrf)["dice"]
        Dice_mrf_mean = Dice_mrf.mean()

        headers = [
            "MRF", "accuracy", "sensitivity", "precision", "diffusion", "Dice"
        ]
        table = [
            ["False", acc, score[0], score[1], score[2], Dice_mean],
            [
                "True", acc_mrf, score_mrf[0], score_mrf[1], score_mrf[2],
                Dice_mrf_mean
            ],
        ]

        subtitle2 = "\n\n---Scores---\n\n"
        scores = tabulate(table, headers)
        text = subtitle2 + scores

        subtitle3 = "\n\n---Dice Percentiles---\n\n"
        headers = ["MRF", "Dice 10th", "50th", "90th"]
        table = [
            [
                "False",
                np.percentile(Dice, 10),
                np.percentile(Dice, 50),
                np.percentile(Dice, 90),
            ],
            [
                "True",
                np.percentile(Dice_mrf, 10),
                np.percentile(Dice_mrf, 50),
                np.percentile(Dice_mrf, 90),
            ],
        ]
        scores_2 = tabulate(table, headers)

        text = text + subtitle3 + subtitle3 + scores_2
        print(text)

        file = open(path / "Report_results.txt", "w")
        file.write(text)
        file.close()

    if "MyelinSeg.jpg" in cur_dir_items:
        path_myelin = path / "MyelinSeg.jpg"
        myelin = preprocessing.binarize(ads.imread(path_myelin), threshold=125)
        myelin = np.ma.masked_where(myelin == 0, myelin)

        title = "Myelin Segmentation"
        fig3 = _create_fig_helper(myelin, title)
        figs.append(fig3)

        if Mask:
            # New base image for plotting
            image_init = mask
            # Create figure
            title = "Myelin - GroundTruth"
            fig4 = _create_fig_helper(myelin, title)
            figs.append(fig4)
    return figs