def DATA_AUGMENTATION(self, data_set_ids, augmentation_factor):
        """
            Perform data augmentation operations on a dataset
                - Data are augmented "augmentation_factor" times
            
            data_set_ids : [(PET_id_1,CT_id_1,MASK_id_1),(PET_id_2,CT_id_2,MASK_id_2)...]
        """

        n_patients = len(data_set_ids)
        new_data_set_ids = []

        for i, data_set_id in enumerate(data_set_ids):

            # load files
            PET_id, CT_id, MASK_id = data_set_id

            PET_img = sitk.ReadImage(PET_id, sitk.sitkFloat32)
            CT_img = sitk.ReadImage(CT_id, sitk.sitkFloat32)
            MASK = sitk.ReadImage(MASK_id, sitk.sitkUInt8)

            # display a loading  bar
            display_loading_bar(iteration=i,
                                length=n_patients,
                                add_char=basename(MASK_id) + '             ')

            for factor in range(1, augmentation_factor):

                # generate random deformation
                def_ratios = self.DATA_AUGMENTATION_DeformationRatios()

                # apply same transformation to PET / CT / Mask
                new_PET_img = self.DATA_AUGMENTATION_AffineTransformation(
                    image=PET_img,
                    interpolator=sitk.sitkBSpline,
                    deformations=def_ratios)
                new_CT_img = self.DATA_AUGMENTATION_AffineTransformation(
                    image=CT_img,
                    interpolator=sitk.sitkBSpline,
                    deformations=def_ratios)
                new_MASK_img = self.DATA_AUGMENTATION_AffineTransformation(
                    image=MASK,
                    interpolator=sitk.sitkNearestNeighbor,
                    deformations=def_ratios)

                # generates names
                new_PET_id = splitext(PET_id)[0] + '_augm' * factor + '.nii'
                new_CT_id = splitext(CT_id)[0] + '_augm' * factor + '.nii'
                new_Mask_id = splitext(MASK_id)[0] + '_augm' * factor + '.nii'

                # saves
                sitk.WriteImage(new_PET_img, new_PET_id)
                sitk.WriteImage(new_CT_img, new_CT_id)
                sitk.WriteImage(new_MASK_img, new_Mask_id)

                new_data_set_ids.append((new_PET_id, new_CT_id, new_Mask_id))

        return new_data_set_ids
    def PREPROCESS(self,
                   data_set_ids,
                   path_output,
                   output_shape=None,
                   pixel_size=None,
                   resample=True,
                   normalize=True):
        """
            Perform preprocessing and save new datas from a dataset
            
            data_set_ids : [(PET_id_1,CT_id_1,MASK_id_1),(PET_id_2,CT_id_2,MASK_id_2)...]
        """
        n_patients = len(data_set_ids)

        preprocessed_data_set_ids = []

        for i, data_set_id in enumerate(data_set_ids):

            # display a loading  bar
            display_loading_bar(iteration=i,
                                length=n_patients,
                                add_char=basename(data_set_id[0]) + '    ')

            # load data set
            self.PET_id, self.CT_id, self.MASK_id = data_set_id

            self.PET_img = sitk.ReadImage(self.PET_id, sitk.sitkFloat32)
            self.CT_img = sitk.ReadImage(self.CT_id, sitk.sitkFloat32)
            self.MASK_img = sitk.ReadImage(self.MASK_id, sitk.sitkUInt8)

            if normalize:
                self.PREPROCESS_normalize()

            if resample:
                self.PREPROCESS_resample_CT_to_TEP()
                self.PREPROCESS_resample_TEPCT_to_CNN(
                    output_shape[::-1], pixel_size[::-1])  #reorder to [x,y,z]

            # save preprocess data
            new_PET_id = path_output + '/' + splitext(basename(
                data_set_id[0]))[0] + '.nii'
            new_CT_id = path_output + '/' + splitext(basename(
                data_set_id[1]))[0] + '.nii'
            new_MASK_id = path_output + '/' + splitext(basename(
                data_set_id[2]))[0] + '.nii'

            preprocessed_data_set_ids.append(
                (new_PET_id, new_CT_id, new_MASK_id))

            self.PREPROCESS_save(new_filenames=preprocessed_data_set_ids[i])

        # clear
        del self.PET_id, self.CT_id, self.MASK_id
        del self.PET_img, self.CT_img, self.MASK_img

        return preprocessed_data_set_ids
    def PREDICT_MASK(self, data_set_ids, path_predictions, trained_model):
        """
            Perform and save prediction on a data set filenames
            
            data_set_ids : [(PET_id_1,CT_id_1),(PET_id_2,CT_id_2)...]
        """

        # generates folder
        if not os.path.exists(path_predictions):
            os.makedirs(path_predictions)

        filenames_predicted_masks = []
        n_patients = len(data_set_ids)

        for i, data_set_id in enumerate(data_set_ids):

            # display a loading  bar
            display_loading_bar(iteration=i,
                                length=n_patients,
                                add_char=basename(data_set_id[0]) + '    ')

            PET_id, CT_id = data_set_id

            # load files and prepare model input
            PET_scan = tf.constant(sitk.GetArrayFromImage(
                sitk.ReadImage(PET_id, sitk.sitkFloat32)),
                                   dtype=tf.float32)
            CT_scan = tf.constant(sitk.GetArrayFromImage(
                sitk.ReadImage(CT_id, sitk.sitkFloat32)),
                                  dtype=tf.float32)

            scan = tf.stack((PET_scan, CT_scan), axis=-1)
            scan = tf.expand_dims(scan, axis=0)

            scan_dataset = tf.data.Dataset.from_tensor_slices(scan).batch(1)

            prediction = trained_model.predict(scan_dataset)

            prediction = tf.argmax(prediction,
                                   axis=-1)  # output from a multiclass softmax
            prediction = tf.squeeze(prediction).numpy()

            # conversion in unsigned int 8 to store mask with less memory requirement
            mask = np.asarray(prediction, dtype=np.uint8)

            new_filename = path_predictions + "/pred_" + splitext(
                basename(data_set_id[0]))[0] + '.nii'
            filenames_predicted_masks.append(new_filename)
            sitk.WriteImage(sitk.GetImageFromArray(mask), new_filename)

        return filenames_predicted_masks
    def GENERATES_MIP_PREDICTION(self,
                                 path_output,
                                 data_set_ids,
                                 pred_ids,
                                 filename=None):
        """
            Generate MIP projection of PET/CT files with its predicted mask

            data_set_ids : [(PET_id_1,CT_id_1),(PET_id_2,CT_id_2)...]
        """

        if filename is None:
            filename = path_output + "/PETCT_MIP_" + time.strftime(
                "%m%d%H%M%S") + ".pdf"
        else:
            filename = path_output + '/' + filename

        # generates folder
        if not os.path.exists(path_output):
            os.makedirs(path_output)

        n_patients = len(data_set_ids)

        transparency = 0.7

        color_CT = plt.cm.gray
        color_PET = plt.cm.plasma
        color_MASK = copy(plt.cm.Greys)
        color_MASK.set_bad('white', 0.0)

        with PdfPages(filename) as pdf:

            # loop on files to get MIP visualisation
            for i, (DataSet_id,
                    Pred_id) in enumerate(zip(data_set_ids, pred_ids)):

                display_loading_bar(iteration=i,
                                    length=n_patients,
                                    add_char=basename(DataSet_id[0]) + '    ')

                # load imgs
                PET_id, CT_id = DataSet_id
                PET_scan = sitk.GetArrayFromImage(sitk.ReadImage(PET_id))
                CT_scan = sitk.GetArrayFromImage(sitk.ReadImage(CT_id))
                PRED = sitk.GetArrayFromImage(sitk.ReadImage(Pred_id))

                # for TEP visualisation
                PET_scan = np.where(PET_scan > 1.0, 1.0, PET_scan)
                PET_scan = np.where(PET_scan < 0.0, 0.0, PET_scan)

                # for CT visualisation
                CT_scan = np.where(CT_scan > 1.0, 1.0, CT_scan)
                CT_scan = np.where(CT_scan < 0.0, 0.0, CT_scan)

                # for correct visualisation
                PET_scan = np.flip(PET_scan, axis=0)
                CT_scan = np.flip(CT_scan, axis=0)
                PRED = np.flip(PRED, axis=0)

                # stacked projections
                PET_scan = np.hstack((np.amax(PET_scan,
                                              axis=1), np.amax(PET_scan,
                                                               axis=2)))
                CT_scan = np.hstack(
                    (np.amax(CT_scan, axis=1), np.amax(CT_scan, axis=2)))
                PRED = np.hstack((np.amax(PRED, axis=1), np.amax(PRED,
                                                                 axis=2)))

                ##################################################################
                f = plt.figure(figsize=(15, 10))
                f.suptitle(splitext(basename(PET_id))[0], fontsize=15)

                plt.subplot(121)
                plt.imshow(CT_scan, cmap=color_CT, origin='lower')
                plt.imshow(PET_scan,
                           cmap=color_PET,
                           alpha=transparency,
                           origin='lower')
                plt.axis('off')
                plt.title('PET/CT', fontsize=20)

                plt.subplot(122)
                plt.imshow(CT_scan, cmap=color_CT, origin='lower')
                plt.imshow(PET_scan,
                           cmap=color_PET,
                           alpha=transparency,
                           origin='lower')
                plt.imshow(np.where(PRED, 0, np.nan),
                           cmap=color_MASK,
                           origin='lower')
                plt.axis('off')
                plt.title('Prediction', fontsize=20)

                pdf.savefig()  # saves the current figure into a pdf page
                plt.close()
    def POSTPROCESS(self,
                    path_output,
                    data_set_ids,
                    prediction_ids,
                    input_pixel_size,
                    resize=True):
        """
            Perform postprocessing on a prediction mask, based on data its corresponding data set
            
            TODO :
            /!\ by default postprocess to PET size
            
            TODO :
            /!\ add all header parameters when generating postprocess mask
            
            data_set_ids : [(PET_id_1,CT_id_1),(PET_id_2,CT_id_2)...]
        """

        new_filenames = []
        n_patient = len(data_set_ids)

        for i, (data_set_id,
                pred_id) in enumerate(zip(data_set_ids, prediction_ids)):

            display_loading_bar(iteration=i,
                                length=n_patient,
                                add_char=basename(data_set_id[0]) + '     ')

            # load data set
            PET_id, CT_id = data_set_id
            PET_img = sitk.ReadImage(PET_id, sitk.sitkFloat32)

            # gather input parameters
            PET_shape = list(PET_img.GetSize())
            PET_shape.reverse()  # axis order is reversed when img->array
            PET_pixelsize = list(PET_img.GetSpacing())
            PET_pixelsize.reverse()  # axis order is reversed when img->array

            # ordered as [z,y,x]
            prediction = sitk.GetArrayFromImage(
                sitk.ReadImage(pred_id, sitk.sitkUInt8))

            if resize:
                prediction = isometric_resample(
                    prediction,
                    input_pixel_size=input_pixel_size,
                    output_shape=PET_shape,
                    output_pixel_size=PET_pixelsize,
                    interpolation_order=0)

            # save postprocessed data
            new_filename = path_output + '/' + splitext(
                basename(pred_id))[0] + '.nii'
            # save spacing a.k.a pixel size in the file
            sitk_img = sitk.GetImageFromArray(prediction)
            sitk_img.SetSpacing(PET_pixelsize[::-1])
            sitk.WriteImage(sitk_img, new_filename)

            new_filenames.append(new_filename)

        return new_filenames
    def VISUALISATION_MIP_PREDICTION(self,
                                     path_output,
                                     data_set_ids,
                                     pred_ids,
                                     filename=None):
        """
            Generate MIP projection of PET/CT files with its predicted mask

            data_set_ids : [(PET_id_1,CT_id_1),(PET_id_2,CT_id_2)...]
        """

        if filename is None:
            filename = path_output + "/PETCT_MIP_" + time.strftime(
                "%m%d%H%M%S") + ".pdf"
        else:
            filename = path_output + '/' + filename

        # generates folder
        if not os.path.exists(path_output):
            os.makedirs(path_output)

        n_patients = len(data_set_ids)

        transparency = 0.7

        color_CT = plt.cm.gray
        color_PET = plt.cm.plasma
        color_MASK = copy(plt.cm.Greys)
        color_MASK.set_bad('white', 0.0)

        with PdfPages(filename) as pdf:

            ############################ BOXPLOTS GENERATION #############################################

            # get boxplots
            num_class = len(self.labels_numbers)
            # initialize values board
            Conf = np.zeros((num_class, num_class), dtype=np.int32)
            TruePos = []
            GTsum = []
            PRsum = []

            TruePos, GTsum, PRsum = self.compute_metrics(
                data_set_ids, pred_ids)

            accuracy_TAB = TruePos / PRsum
            dice_TAB = (2 * TruePos + 0.1) / (GTsum + PRsum + 0.1)

            f = plt.figure(figsize=(15, 10))
            f.suptitle('Metrics evaluation', fontsize=15)

            plt.subplot(121)
            plt.boxplot(accuracy_TAB[:, :],
                        sym='x',
                        whis=5,
                        labels=self.labels_names)
            accuracy_median_tumor = np.median(accuracy_TAB[:, 1])
            plt.ylim(0.1)
            plt.title("Accuracy Boxplot : tumor=%5.3f" % accuracy_median_tumor,
                      fontsize=15)

            plt.subplot(122)
            plt.boxplot(dice_TAB[:, :],
                        sym='x',
                        whis=5,
                        labels=self.labels_names)
            dice_median_tumor = np.median(dice_TAB[:, 1])
            plt.ylim(0.1)
            plt.title("Dice Boxplot : tumor=%5.3f" % dice_median_tumor,
                      fontsize=15)

            pdf.savefig()  # saves the current figure into a pdf page
            plt.close()

            ############################# MIP GENERATION ################################################

            # loop on files to get MIP visualisation
            for i, (DataSet_id,
                    Pred_id) in enumerate(zip(data_set_ids, pred_ids)):

                display_loading_bar(iteration=i,
                                    length=n_patients,
                                    add_char=basename(DataSet_id[0]) + '    ')

                # load imgs
                PET_id, CT_id, Mask_id = DataSet_id
                PET_scan = sitk.GetArrayFromImage(sitk.ReadImage(PET_id))
                CT_scan = sitk.GetArrayFromImage(sitk.ReadImage(CT_id))
                MASK = sitk.GetArrayFromImage(sitk.ReadImage(Mask_id))
                PRED = sitk.GetArrayFromImage(sitk.ReadImage(Pred_id))

                # for TEP visualisation
                PET_scan = np.where(PET_scan > 1.0, 1.0, PET_scan)
                PET_scan = np.where(PET_scan < 0.0, 0.0, PET_scan)

                # for CT visualisation
                CT_scan = np.where(CT_scan > 1.0, 1.0, CT_scan)
                CT_scan = np.where(CT_scan < 0.0, 0.0, CT_scan)

                # for correct visualisation
                PET_scan = np.flip(PET_scan, axis=0)
                CT_scan = np.flip(CT_scan, axis=0)
                MASK = np.flip(MASK, axis=0)
                PRED = np.flip(PRED, axis=0)

                # stacked projections
                PET_scan = np.hstack((np.amax(PET_scan,
                                              axis=1), np.amax(PET_scan,
                                                               axis=2)))
                CT_scan = np.hstack(
                    (np.amax(CT_scan, axis=1), np.amax(CT_scan, axis=2)))
                MASK = np.hstack((np.amax(MASK, axis=1), np.amax(MASK,
                                                                 axis=2)))
                PRED = np.hstack((np.amax(PRED, axis=1), np.amax(PRED,
                                                                 axis=2)))

                ##################################################################
                f = plt.figure(figsize=(15, 10))
                f.suptitle(splitext(basename(PET_id))[0], fontsize=15)

                plt.subplot(121)
                plt.imshow(CT_scan, cmap=color_CT, origin='lower')
                plt.imshow(PET_scan,
                           cmap=color_PET,
                           alpha=transparency,
                           origin='lower')
                plt.imshow(np.where(MASK, 0, np.nan),
                           cmap=color_MASK,
                           origin='lower')
                plt.axis('off')
                plt.title('Ground Truth', fontsize=20)

                plt.subplot(122)
                plt.imshow(CT_scan, cmap=color_CT, origin='lower')
                plt.imshow(PET_scan,
                           cmap=color_PET,
                           alpha=transparency,
                           origin='lower')
                plt.imshow(np.where(PRED, 0, np.nan),
                           cmap=color_MASK,
                           origin='lower')
                plt.axis('off')
                plt.title('Prediction', fontsize=20)

                pdf.savefig()  # saves the current figure into a pdf page
                plt.close()