Beispiel #1
0
def test_load_dataset_2d_png(download_data_testing_test_files,
                             loader_parameters, model_parameters,
                             transform_parameters):
    """
    Test to make sure load_dataset runs with 2D PNG files, writes corresponding NIfTI files,
    and binarizes ground-truth values to 0 and 1.
    """
    loader_parameters.update({LoaderParamsKW.MODEL_PARAMS: model_parameters})
    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    data_lst = ['sub-rat3_ses-01_sample-data9_SEM.png']
    ds = imed_loader.load_dataset(
        bids_df, **{
            **loader_parameters,
            **{
                'data_list': data_lst,
                'transforms_params': transform_parameters,
                'dataset_type': 'training'
            }
        })
    fname_png = bids_df.df[bids_df.df['filename'] ==
                           data_lst[0]]['path'].values[0]
    fname_nii = imed_loader_utils.update_filename_to_nifti(fname_png)
    assert Path(fname_nii).exists() == 1
    assert ds[0]['input'].shape == (1, 756, 764)
    assert ds[0]['gt'].shape == (1, 756, 764)
    assert np.unique(ds[0]['gt']).tolist() == [0, 1]
Beispiel #2
0
def test_get_target_filename_list_multiple_raters(loader_parameters,
                                                  model_parameters,
                                                  transform_parameters):
    """
    Test that all target_suffix are considered for target filename when list
    """
    loader_parameters.update({LoaderParamsKW.MODEL_PARAMS: model_parameters})
    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    data_lst = ['sub-rat3_ses-01_sample-data9_SEM.png']
    test_ds = imed_loader.load_dataset(
        bids_df, **{
            **loader_parameters,
            **{
                'data_list': data_lst,
                'transforms_params': transform_parameters,
                'dataset_type': 'training'
            }
        })
    target_filename = test_ds.filename_pairs[0][1]

    assert len(target_filename) == len(
        loader_parameters[LoaderParamsKW.TARGET_SUFFIX])
    assert len(target_filename[0]) == len(
        loader_parameters[LoaderParamsKW.TARGET_SUFFIX][0])
    assert len(target_filename[1]) == len(
        loader_parameters[LoaderParamsKW.TARGET_SUFFIX][1])
Beispiel #3
0
def test_bids_df_microscopy_png(download_data_testing_test_files,
                                loader_parameters):
    """
    Test for microscopy png file format
    Test for _sessions.tsv and _scans.tsv files
    Test for target_suffix as a nested list
    Test for when no contrast_params are provided
    """

    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    df_test = bids_df.df.drop(columns=['path'])
    df_test = df_test.sort_values(by=['filename']).reset_index(drop=True)
    csv_ref = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                   "df_ref.csv")
    csv_test = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                    "df_test.csv")
    df_test.to_csv(csv_test, index=False)
    diff = csv_diff.compare(csv_diff.load_csv(open(csv_ref)),
                            csv_diff.load_csv(open(csv_test)))
    assert diff == {
        'added': [],
        'removed': [],
        'changed': [],
        'columns_added': [],
        'columns_removed': []
    }
Beispiel #4
0
def test_bids_df_anat(download_data_testing_test_files, loader_parameters):
    """
    Test for MRI anat nii.gz file format
    Test for when no file extensions are provided
    Test for multiple target_suffix
    Test behavior when "roi_suffix" is not None
    """

    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    df_test = bids_df.df.drop(columns=['path'])
    df_test = df_test.sort_values(by=['filename']).reset_index(drop=True)
    csv_ref = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                   "df_ref.csv")
    csv_test = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                    "df_test.csv")
    df_test.to_csv(csv_test, index=False)
    diff = csv_diff.compare(csv_diff.load_csv(open(csv_ref)),
                            csv_diff.load_csv(open(csv_test)))
    assert diff == {
        'added': [],
        'removed': [],
        'changed': [],
        'columns_added': [],
        'columns_removed': []
    }
Beispiel #5
0
def test_bids_df_no_validate(download_data_testing_test_files,
                             loader_parameters):
    """
    Test for ct-scan nii.gz file format
    Test for when validate_BIDS is set to False for the loader
    """

    # Rename files so the loader won't pick them up if validate_BIDS is true
    Path(loader_parameters[LoaderParamsKW.PATH_DATA][0], "sub-spleen2").rename(
        Path(loader_parameters[LoaderParamsKW.PATH_DATA][0], "ssub-spleen2"))

    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    df_test = bids_df.df.drop(columns=['path'])
    df_test = df_test.sort_values(by=['filename']).reset_index(drop=True)
    csv_ref = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                   "df_ref.csv")
    csv_test = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                    "df_test.csv")
    df_test.to_csv(csv_test, index=False)
    diff = csv_diff.compare(csv_diff.load_csv(open(csv_ref)),
                            csv_diff.load_csv(open(csv_test)))

    Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
         "ssub-spleen2").rename(
             Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                  "sub-spleen2"))
    assert diff == {
        'added': [],
        'removed': [],
        'changed': [],
        'columns_added': [],
        'columns_removed': []
    }
def split_dataset(initial_config):
    """
    Args:
        initial_config (dict): The original config file, which we use as a basis from which
            to modify our hyperparameters.

            .. code-block:: JSON

                {
                    "training_parameters": {
                        "batch_size": 18,
                        "loss": {"name": "DiceLoss"}
                    },
                    "default_model":     {
                        "name": "Unet",
                        "dropout_rate": 0.3,
                        "depth": 3
                    },
                    "model_name": "seg_tumor_t2",
                    "path_output": "./tmp/"
                }
    """
    loader_parameters = initial_config[ConfigKW.LOADER_PARAMETERS]
    path_output = Path(initial_config[ConfigKW.PATH_OUTPUT])
    if not path_output.is_dir():
        logger.info(f'Creating output path: {path_output}')
        path_output.mkdir(parents=True)
    else:
        logger.info(f'Output path already exists: {path_output}')

    bids_df = BidsDataframe(loader_parameters,
                            str(path_output),
                            derivatives=True)

    train_lst, valid_lst, test_lst = imed_loader_utils.get_new_subject_file_split(
        df=bids_df.df,
        data_testing=initial_config[ConfigKW.SPLIT_DATASET][SplitDatasetKW.DATA_TESTING],
        split_method=initial_config[ConfigKW.SPLIT_DATASET][SplitDatasetKW.SPLIT_METHOD],
        random_seed=initial_config[ConfigKW.SPLIT_DATASET][SplitDatasetKW.RANDOM_SEED],
        train_frac=initial_config[ConfigKW.SPLIT_DATASET][SplitDatasetKW.TRAIN_FRACTION],
        test_frac=initial_config[ConfigKW.SPLIT_DATASET][SplitDatasetKW.TEST_FRACTION],
        path_output="./",
        balance=initial_config[ConfigKW.SPLIT_DATASET][SplitDatasetKW.BALANCE] \
        if SplitDatasetKW.BALANCE in initial_config[ConfigKW.SPLIT_DATASET] else None
    )

    # save the subject distribution
    split_dct = {'train': train_lst, 'valid': valid_lst, 'test': test_lst}
    split_path = "./" + "common_split_datasets.joblib"
    joblib.dump(split_dct, split_path)
    initial_config[ConfigKW.SPLIT_DATASET][
        SplitDatasetKW.FNAME_SPLIT] = split_path
    return initial_config
Beispiel #7
0
def test_slice_filter(download_data_testing_test_files, transforms_dict,
                      train_lst, target_lst, roi_params, slice_filter_params):
    if "ROICrop" in transforms_dict and roi_params["suffix"] is None:
        return

    cuda_available, device = imed_utils.define_device(GPU_ID)

    loader_params = {
        "transforms_params": transforms_dict,
        "data_list": train_lst,
        "dataset_type": "training",
        "requires_undo": False,
        "contrast_params": {
            "contrast_lst": ['T2w'],
            "balance": {}
        },
        "path_data": [__data_testing_dir__],
        "target_suffix": target_lst,
        "extensions": [".nii.gz"],
        "roi_params": roi_params,
        "model_params": {
            "name": "Unet"
        },
        "slice_filter_params": slice_filter_params,
        "patch_filter_params": {
            "filter_empty_mask": False,
            "filter_empty_input": False
        },
        "slice_axis": "axial",
        "multichannel": False
    }
    # Get Training dataset
    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)
    ds_train = imed_loader.load_dataset(bids_df, **loader_params)

    logger.info(f"\tNumber of loaded slices: {len(ds_train)}")

    train_loader = DataLoader(ds_train,
                              batch_size=BATCH_SIZE,
                              shuffle=True,
                              pin_memory=True,
                              collate_fn=imed_loader_utils.imed_collate,
                              num_workers=0)
    cmpt_neg, cmpt_pos = _cmpt_slice(train_loader)
    if slice_filter_params["filter_empty_mask"]:
        assert cmpt_neg == 0
        assert cmpt_pos != 0
    else:
        # We verify if there are still some negative slices (they are removed with our filter)
        assert cmpt_neg != 0 and cmpt_pos != 0
    logger.info(f"\tNumber of Neg/Pos slices in GT: {cmpt_neg/cmpt_pos}")
Beispiel #8
0
def test_read_png_tif(download_data_testing_test_files, loader_parameters,
                      model_parameters):
    """
    Test to make sure all combinaitions of PNG/TIF, 8/16 bits, Grayscale/RGB/RGBA files
    can be loaded without errors.
    """
    metadata = {}
    metadata[MetadataKW.PIXEL_SIZE] = [0.07, 0.07]
    metadata[MetadataKW.PIXEL_SIZE_UNITS] = "um"
    loader_parameters.update({LoaderParamsKW.MODEL_PARAMS: model_parameters})
    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=False)
    file_lst = bids_df.df['path'].tolist()
    filename_pairs = [(file_lst, None, None,
                       metadata if isinstance(metadata, list) else [metadata])]
    slice_axis = imed_utils.AXIS_DCT[loader_parameters[
        LoaderParamsKW.SLICE_AXIS]]
    ds = imed_loader_mri2dseg.MRI2DSegmentationDataset(filename_pairs,
                                                       slice_axis=slice_axis,
                                                       nibabel_cache=True,
                                                       transform=[None, None],
                                                       slice_filter_fn=None)
    ds.load_filenames()
Beispiel #9
0
def test_bids_df_multi(download_data_testing_test_files, loader_parameters):
    """
    Test for multiple folders in path_data
    """

    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    df_test = bids_df.df.drop(columns=['path'])
    df_test = df_test.sort_values(by=['filename']).reset_index(drop=True)
    csv_ref = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                   "df_ref_multi.csv")
    csv_test = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                    "df_test_multi.csv")
    df_test.to_csv(csv_test, index=False)
    diff = csv_diff.compare(csv_diff.load_csv(open(csv_ref)),
                            csv_diff.load_csv(open(csv_test)))
    assert diff == {
        'added': [],
        'removed': [],
        'changed': [],
        'columns_added': [],
        'columns_removed': []
    }
Beispiel #10
0
def test_bids_df_ctscan(download_data_testing_test_files, loader_parameters):
    """
    Test for ct-scan nii.gz file format
    Test for when dataset_description.json is not present in derivatives folder
    """

    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    df_test = bids_df.df.drop(columns=['path'])
    df_test = df_test.sort_values(by=['filename']).reset_index(drop=True)
    csv_ref = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                   "df_ref.csv")
    csv_test = Path(loader_parameters[LoaderParamsKW.PATH_DATA][0],
                    "df_test.csv")
    df_test.to_csv(csv_test, index=False)
    diff = csv_diff.compare(csv_diff.load_csv(open(csv_ref)),
                            csv_diff.load_csv(open(csv_test)))
    assert diff == {
        'added': [],
        'removed': [],
        'changed': [],
        'columns_added': [],
        'columns_removed': []
    }
Beispiel #11
0
def test_config_sha256(download_data_testing_test_files, initial_config):
    file_lst = ["sub-unf01_T2w.nii.gz"]
    loader_params = {
        "transforms_params": {},
        "data_list": ['sub-unf01'],
        "dataset_type": "testing",
        "requires_undo": True,
        "contrast_params": {"contrast_lst": ['T2w'], "balance": {}},
        "path_data": [__data_testing_dir__],
        "target_suffix": ["_lesion-manual"],
        "extensions": [".nii.gz"],
        "roi_params": {"suffix": "_seg-manual", "slice_filter_roi": 10},
        "slice_filter_params": {
            "filter_empty_mask": False,
            "filter_empty_input": True
        },
        "slice_axis": "axial",
        "multichannel": False
    }

    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)
    generate_sha_256(initial_config, bids_df.df, file_lst)
    assert(initial_config['training_sha256']['sub-unf01_T2w.nii.gz'] ==
           'f020b368fea15399fa112badd28b2df69e044dba5d23b3fe1646d12d7d3d39ac')
Beispiel #12
0
def test_2d_patches_and_resampling(download_data_testing_test_files,
                                   loader_parameters, model_parameters,
                                   transform_parameters):
    """
    Test that 2d patching is done properly.
    Test that microscopy pixelsize and resampling are applied on the right dimensions.
    """
    loader_parameters.update({LoaderParamsKW.MODEL_PARAMS: model_parameters})
    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)
    data_lst = ['sub-rat3_ses-01_sample-data9_SEM.png']
    ds = imed_loader.load_dataset(
        bids_df, **{
            **loader_parameters,
            **{
                'data_list': data_lst,
                'transforms_params': transform_parameters,
                'dataset_type': 'training'
            }
        })
    assert ds.is_2d_patch == True
    assert ds[0]['input'].shape == (1, 256, 128)
    assert ds[0]['input_metadata'][0].metadata[MetadataKW.INDEX_SHAPE] == (
        1512, 382)
    assert len(ds) == 28
def test_patch_filter(download_data_testing_test_files, transforms_dict,
                      train_lst, target_lst, patch_filter_params,
                      dataset_type):

    cuda_available, device = imed_utils.define_device(GPU_ID)

    loader_params = {
        "transforms_params": transforms_dict,
        "data_list": train_lst,
        "dataset_type": dataset_type,
        "requires_undo": False,
        "contrast_params": {
            "contrast_lst": ['SEM'],
            "balance": {}
        },
        "path_data": [os.path.join(__data_testing_dir__, "microscopy_png")],
        "bids_config": f"{path_repo_root}/ivadomed/config/config_bids.json",
        "target_suffix": target_lst,
        "extensions": [".png"],
        "roi_params": {
            "suffix": None,
            "slice_filter_roi": None
        },
        "model_params": {
            "name": "Unet",
            "length_2D": [32, 32],
            "stride_2D": [32, 32]
        },
        "slice_filter_params": {
            "filter_empty_mask": False,
            "filter_empty_input": False
        },
        "patch_filter_params": patch_filter_params,
        "slice_axis": "axial",
        "multichannel": False
    }
    # Get Training dataset
    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)
    ds = imed_loader.load_dataset(bids_df, **loader_params)

    logger.info(f"\tNumber of loaded patches: {len(ds)}")

    loader = DataLoader(ds,
                        batch_size=BATCH_SIZE,
                        shuffle=True,
                        pin_memory=True,
                        collate_fn=imed_loader_utils.imed_collate,
                        num_workers=0)
    logger.info("\tNumber of Neg/Pos patches in GT.")
    cmpt_neg, cmpt_pos = _cmpt_slice(loader)
    if patch_filter_params["filter_empty_mask"]:
        if dataset_type == "testing":
            # Filters on patches are not applied at testing time
            assert cmpt_neg + cmpt_pos == len(ds)
        else:
            # Filters on patches are applied at training time
            assert cmpt_neg == 0
            assert cmpt_pos != 0
    else:
        # We verify if there are still some negative patches (they are removed with our filter)
        assert cmpt_neg != 0 and cmpt_pos != 0
def test_bounding_box(download_data_testing_test_files, train_lst, target_lst, config):
    # Create mask
    mask_coord = [20, 40, 20, 90, 0, 25]
    mx1, mx2, my1, my2, mz1, mz2 = mask_coord
    mask = np.zeros((96, 96, 96))
    mask[mx1:mx2 + 1, my1:my2 + 1, mz1:mz2 + 1] = 1
    coord = imed_obj_detect.get_bounding_boxes(mask)
    assert coord[0] == mask_coord

    loader_params = {
        "data_list": train_lst,
        "dataset_type": "training",
        "requires_undo": False,
        "path_data": [__data_testing_dir__],
        "target_suffix": target_lst,
        "extensions": [".nii.gz"],
        "slice_filter_params": {"filter_empty_mask": False, "filter_empty_input": True},
        "patch_filter_params": {"filter_empty_mask": False, "filter_empty_input": False},
        "slice_axis": "axial"
    }

    if "Modified3DUNet" in config:
        config['model_params']["name"] = "Modified3DUNet"
        config['model_params'].update(config["Modified3DUNet"])

    bounding_box_dict = {}
    bounding_box_path = Path(PATH_OUTPUT, 'bounding_boxes.json')
    if not Path(PATH_OUTPUT).exists():
        PATH_OUTPUT.mkdir(parents=True, exist_ok=True)
    current_dir = Path.cwd()
    sub = train_lst[0].split('_')[0]
    contrast = config['contrast_params']['contrast_lst'][0]
    bb_path = str(Path(current_dir, __data_testing_dir__, sub, "anat", sub + "_" + contrast + ".nii.gz"))
    bounding_box_dict[bb_path] = coord
    with open(bounding_box_path, 'w') as fp:
        json.dump(bounding_box_dict, fp, indent=4)

    # Update loader_params with config
    loader_params.update(config)

    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)

    ds = imed_loader.load_dataset(bids_df, **loader_params)

    handler = ds.handlers if "Modified3DUNet" in config else ds.indexes
    for index in range(len(handler)):

        if "Modified3DUNet" in config:
            if ds.disk_cache:
                path_seg_pair, _ = handler[index]
                with path_seg_pair.open('rb') as f:
                    seg_pair = pickle.load(f)
            else:
                seg_pair, _ = handler[index]
            assert seg_pair['input'][0].shape[-3:] == (mx2 - mx1, my2 - my1, mz2 - mz1)
        else:
            if ds.disk_cache:
                path_seg_pair = handler[index]
                with path_seg_pair.open('rb') as f:
                    seg_pair, _ = pickle.load(f)
            else:
                seg_pair, _ = handler[index]
            assert seg_pair['input'][0].shape[-2:] == (mx2 - mx1, my2 - my1)

    shutil.rmtree(PATH_OUTPUT)
def test_sampler(download_data_testing_test_files, transforms_dict, train_lst,
                 target_lst, roi_params):
    cuda_available, device = imed_utils.define_device(GPU_ID)

    loader_params = {
        "transforms_params": transforms_dict,
        "data_list": train_lst,
        "dataset_type": "training",
        "requires_undo": False,
        "contrast_params": {
            "contrast_lst": ['T2w'],
            "balance": {}
        },
        "path_data": [__data_testing_dir__],
        "target_suffix": target_lst,
        "extensions": [".nii.gz"],
        "roi_params": roi_params,
        "model_params": {
            "name": "Unet"
        },
        "slice_filter_params": {
            "filter_empty_mask": False,
            "filter_empty_input": True
        },
        "patch_filter_params": {
            "filter_empty_mask": False,
            "filter_empty_input": False
        },
        "slice_axis": "axial",
        "multichannel": False
    }
    # Get Training dataset
    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)
    ds_train = imed_loader.load_dataset(bids_df, **loader_params)

    logger.info("\nLoading without sampling")
    train_loader = DataLoader(ds_train,
                              batch_size=BATCH_SIZE,
                              shuffle=True,
                              pin_memory=True,
                              collate_fn=imed_loader_utils.imed_collate,
                              num_workers=0)
    neg_percent, pos_percent = _cmpt_label(train_loader)
    assert abs(neg_percent - pos_percent) > 20

    logger.info("\nLoading with sampling")
    train_loader_balanced = DataLoader(
        ds_train,
        batch_size=BATCH_SIZE,
        sampler=BalancedSampler(ds_train),
        shuffle=False,
        pin_memory=True,
        collate_fn=imed_loader_utils.imed_collate,
        num_workers=0)

    neg_percent_bal, pos_percent_bal = _cmpt_label(train_loader_balanced)
    # Check if the loader is more balanced. The actual distribution comes from a probabilistic model
    # This is however not very efficient to get close to 50 %
    # in the case where we have 16 slices, with 87.5 % of one class (positive sample).
    assert abs(neg_percent_bal - pos_percent_bal) <= abs(neg_percent -
                                                         pos_percent)
Beispiel #16
0
def test_microscopy_pixelsize(download_data_testing_test_files,
                              loader_parameters, model_parameters):
    """
    Test that PixelSize and PixelSizeUnits microscopy metadata
    are handled properly for PixelSizeUnits: "mm", "um" and "nm"
    """
    loader_parameters.update({LoaderParamsKW.MODEL_PARAMS: model_parameters})
    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)

    # PixelSizeUnits: "mm"
    data_lst = ['sub-rat2_sample-data5_SEM.png']
    transform_parameters = {
        TransformationKW.RESAMPLE: {
            "wspace": 0.000093,
            "hspace": 0.000093
        }
    }
    ds = imed_loader.load_dataset(
        bids_df, **{
            **loader_parameters,
            **{
                'data_list': data_lst,
                'transforms_params': transform_parameters,
                'dataset_type': 'training'
            }
        })
    assert ds[0]['input'].shape == (1, 725, 725)

    # PixelSizeUnits: "um"
    data_lst = ['sub-rat3_ses-02_sample-data11_run-1_SEM.png']
    transform_parameters = {
        TransformationKW.RESAMPLE: {
            "wspace": 0.0001,
            "hspace": 0.0001
        }
    }
    ds = imed_loader.load_dataset(
        bids_df, **{
            **loader_parameters,
            **{
                'data_list': data_lst,
                'transforms_params': transform_parameters,
                'dataset_type': 'training'
            }
        })
    assert ds[0]['input'].shape == (1, 839, 769)

    # PixelSizeUnits: "nm"
    data_lst = ['sub-rat3_ses-02_sample-data10_SEM.png']
    transform_parameters = {
        TransformationKW.RESAMPLE: {
            "wspace": 0.0001,
            "hspace": 0.0001
        }
    }
    ds = imed_loader.load_dataset(
        bids_df, **{
            **loader_parameters,
            **{
                'data_list': data_lst,
                'transforms_params': transform_parameters,
                'dataset_type': 'training'
            }
        })
    assert ds[0]['input'].shape == (1, 758, 737)
def test_inference_2d_microscopy(download_data_testing_test_files, transforms_dict, test_lst, target_lst, roi_params,
        testing_params):
    """
    This test checks if the number of NifTI predictions equals the number of test subjects on 2d microscopy data.
    Used to catch a bug where the last slice of the last volume wasn't appended to the prediction
    (see: https://github.com/ivadomed/ivadomed/issues/823)
    Also tests the conversions to PNG predictions when source files are not Nifti and checks if the number of PNG
    predictions is 2x the number of test subjects (2-class model, outputs 1 PNG per class per subject).
    """
    cuda_available, device = imed_utils.define_device(GPU_ID)

    model_params = {"name": "Unet", "is_2d": True, "out_channel": 3}
    loader_params = {
        "transforms_params": transforms_dict,
        "data_list": test_lst,
        "dataset_type": "testing",
        "requires_undo": True,
        "contrast_params": {"contrast_lst": ['SEM'], "balance": {}},
        "path_data": [str(Path(__data_testing_dir__, "microscopy_png"))],
        "bids_config": f"{path_repo_root}/ivadomed/config/config_bids.json",
        "target_suffix": target_lst,
        "extensions": [".png"],
        "roi_params": roi_params,
        "slice_filter_params": {"filter_empty_mask": False, "filter_empty_input": True},
        "patch_filter_params": {"filter_empty_mask": False, "filter_empty_input": False},
        "slice_axis": SLICE_AXIS,
        "multichannel": False
    }
    loader_params.update({"model_params": model_params})

    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)

    # Get Testing dataset
    ds_test = imed_loader.load_dataset(bids_df, **loader_params)
    test_loader = DataLoader(ds_test, batch_size=BATCH_SIZE,
                             shuffle=False, pin_memory=True,
                             collate_fn=imed_loader_utils.imed_collate,
                             num_workers=0)

    # Undo transform
    val_undo_transform = imed_transforms.UndoCompose(imed_transforms.Compose(transforms_dict))

    # Update testing_params
    testing_params.update({
        "slice_axis": loader_params["slice_axis"],
        "target_suffix": loader_params["target_suffix"],
        "undo_transforms": val_undo_transform
    })

    # Model
    model = imed_models.Unet(out_channel=model_params['out_channel'])

    if cuda_available:
        model.cuda()
    model.eval()

    if not __output_dir__.is_dir():
        __output_dir__.mkdir(parents=True, exist_ok=True)

    preds_npy, gt_npy = imed_testing.run_inference(test_loader=test_loader,
                                                   model=model,
                                                   model_params=model_params,
                                                   testing_params=testing_params,
                                                   ofolder=str(__output_dir__),
                                                   cuda_available=cuda_available)

    assert len([x for x in __output_dir__.iterdir() if x.name.endswith(".nii.gz")]) == len(test_lst)
    assert len([x for x in __output_dir__.iterdir() if x.name.endswith(".png")]) == 2*len(test_lst)
def test_inference_target_suffix(download_data_testing_test_files, transforms_dict, test_lst, target_lst, roi_params,
        testing_params):
    """
    This test checks if the filename(s) of the prediction(s) saved as NifTI file(s) in the pred_masks
    dir conform to the target_suffix or not. Thus, independent of underscore(s) in the target_suffix. As a result,
    _seg-axon-manual or _seg-axon_manual should yield the same filename(s).
    (c.f: https://github.com/ivadomed/ivadomed/issues/1135)
    """
    cuda_available, device = imed_utils.define_device(GPU_ID)

    model_params = {"name": "Unet", "is_2d": True, "out_channel": 3}
    loader_params = {
        "transforms_params": transforms_dict,
        "data_list": test_lst,
        "dataset_type": "testing",
        "requires_undo": True,
        "contrast_params": {"contrast_lst": ['SEM'], "balance": {}},
        "path_data": [str(Path(__data_testing_dir__, "microscopy_png"))],
        "bids_config": f"{path_repo_root}/ivadomed/config/config_bids.json",
        "target_suffix": target_lst,
        "extensions": [".png"],
        "roi_params": roi_params,
        "slice_filter_params": {"filter_empty_mask": False, "filter_empty_input": True},
        "patch_filter_params": {"filter_empty_mask": False, "filter_empty_input": False},
        "slice_axis": SLICE_AXIS,
        "multichannel": False
    }
    loader_params.update({"model_params": model_params})

    # restructuring the dataset 
    gt_path = f'{loader_params["path_data"][0]}/derivatives/labels/'
    for file_path in Path(gt_path).rglob('*.png'):
      src_filename = file_path.resolve()
      dst_filename = '_'.join(str(src_filename).rsplit('-', 1))
      src_filename.rename(Path(dst_filename))

    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)

    ds_test = imed_loader.load_dataset(bids_df, **loader_params)
    test_loader = DataLoader(ds_test, batch_size=BATCH_SIZE,
                             shuffle=False, pin_memory=True,
                             collate_fn=imed_loader_utils.imed_collate,
                             num_workers=0)

    # Undo transform
    val_undo_transform = imed_transforms.UndoCompose(imed_transforms.Compose(transforms_dict))

    # Update testing_params
    testing_params.update({
        "slice_axis": loader_params["slice_axis"],
        "target_suffix": loader_params["target_suffix"],
        "undo_transforms": val_undo_transform
    })

    # Model
    model = imed_models.Unet(out_channel=model_params['out_channel'])

    if cuda_available:
        model.cuda()
    model.eval()

    if not __output_dir__.is_dir():
        __output_dir__.mkdir(parents=True, exist_ok=True)

    preds_npy, gt_npy = imed_testing.run_inference(test_loader=test_loader,
                                                   model=model,
                                                   model_params=model_params,
                                                   testing_params=testing_params,
                                                   ofolder=str(__output_dir__),
                                                   cuda_available=cuda_available)

    for x in __output_dir__.iterdir():
      if x.name.endswith('_pred.nii.gz'):
        assert x.name.rsplit('_', 1)[0].endswith(loader_params['contrast_params']['contrast_lst'][-1]), ( 
            'Incompatible filename(s) of the prediction(s) saved as NifTI file(s)!'
        )
def test_inference(download_data_testing_test_files, transforms_dict, test_lst, target_lst, roi_params, testing_params):
    cuda_available, device = imed_utils.define_device(GPU_ID)

    model_params = {"name": "Unet", "is_2d": True}
    loader_params = {
        "transforms_params": transforms_dict,
        "data_list": test_lst,
        "dataset_type": "testing",
        "requires_undo": True,
        "contrast_params": {"contrast_lst": ['T2w'], "balance": {}},
        "path_data": [__data_testing_dir__],
        "target_suffix": target_lst,
        "extensions": [".nii.gz"],
        "roi_params": roi_params,
        "slice_filter_params": {"filter_empty_mask": False, "filter_empty_input": True},
        "patch_filter_params": {"filter_empty_mask": False, "filter_empty_input": False},
        "slice_axis": SLICE_AXIS,
        "multichannel": False
    }
    loader_params.update({"model_params": model_params})

    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)

    # Get Testing dataset
    ds_test = imed_loader.load_dataset(bids_df, **loader_params)
    test_loader = DataLoader(ds_test, batch_size=BATCH_SIZE,
                             shuffle=False, pin_memory=True,
                             collate_fn=imed_loader_utils.imed_collate,
                             num_workers=0)

    # Undo transform
    val_undo_transform = imed_transforms.UndoCompose(imed_transforms.Compose(transforms_dict))

    # Update testing_params
    testing_params.update({
        "slice_axis": loader_params["slice_axis"],
        "target_suffix": loader_params["target_suffix"],
        "undo_transforms": val_undo_transform
    })

    # Model
    model = imed_models.Unet()

    if cuda_available:
        model.cuda()
    model.eval()

    metric_fns = [imed_metrics.dice_score,
                  imed_metrics.hausdorff_score,
                  imed_metrics.precision_score,
                  imed_metrics.recall_score,
                  imed_metrics.specificity_score,
                  imed_metrics.intersection_over_union,
                  imed_metrics.accuracy_score]

    metric_mgr = imed_metrics.MetricManager(metric_fns)

    if not __output_dir__.is_dir():
        __output_dir__.mkdir(parents=True, exist_ok=True)

    preds_npy, gt_npy = imed_testing.run_inference(test_loader=test_loader,
                                                   model=model,
                                                   model_params=model_params,
                                                   testing_params=testing_params,
                                                   ofolder=str(__output_dir__),
                                                   cuda_available=cuda_available)

    metric_mgr(preds_npy, gt_npy)
    metrics_dict = metric_mgr.get_results()
    metric_mgr.reset()
    logger.debug(metrics_dict)
def test_unet_time(download_data_testing_test_files, train_lst, target_lst, config):
    cuda_available, device = imed_utils.define_device(GPU_ID)

    loader_params = {
        "data_list": train_lst,
        "dataset_type": "training",
        "requires_undo": False,
        "path_data": [__data_testing_dir__],
        "target_suffix": target_lst,
        "extensions": [".nii.gz"],
        "slice_filter_params": {"filter_empty_mask": False, "filter_empty_input": True},
        "patch_filter_params": {"filter_empty_mask": False, "filter_empty_input": False},
        "slice_axis": "axial"
    }
    # Update loader_params with config
    loader_params.update(config)
    # Get Training dataset
    bids_df = BidsDataframe(loader_params, __tmp_dir__, derivatives=True)
    ds_train = imed_loader.load_dataset(bids_df, **loader_params)

    # Loader
    train_loader = DataLoader(ds_train,
                              batch_size=1 if config["model_params"]["name"] == "Modified3DUNet" else BATCH_SIZE,
                              shuffle=True, pin_memory=True,
                              collate_fn=imed_loader_utils.imed_collate,
                              num_workers=1)

    # MODEL
    model_params = loader_params["model_params"]
    model_params.update(MODEL_DEFAULT)
    # Get in_channel from contrast_lst
    if loader_params["multichannel"]:
        model_params["in_channel"] = len(loader_params["contrast_params"]["contrast_lst"])
    else:
        model_params["in_channel"] = 1
    # Get out_channel from target_suffix
    model_params["out_channel"] = len(loader_params["target_suffix"])
    model_class = getattr(imed_models, model_params["name"])
    model = model_class(**model_params)

    logger.debug(f"Training {model_params['name']}")
    if cuda_available:
        model.cuda()

    step_scheduler_batch = False
    # TODO: Add optim in pytest
    optimizer = optim.Adam(model.parameters(), lr=INIT_LR)
    scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, N_EPOCHS)

    # TODO: add to pytest
    loss_fct = imed_losses.DiceLoss()

    load_lst, pred_lst, opt_lst, schedule_lst, init_lst, gen_lst = [], [], [], [], [], []
    for epoch in tqdm(range(1, N_EPOCHS + 1), desc="Training"):
        start_time = time.time()

        start_init = time.time()

        model.train()

        tot_init = time.time() - start_init
        init_lst.append(tot_init)

        num_steps = 0
        start_gen = 0
        for i, batch in enumerate(train_loader):
            if i > 0:
                tot_gen = time.time() - start_gen
                gen_lst.append(tot_gen)

            start_load = time.time()
            input_samples = imed_utils.cuda(batch["input"], cuda_available)
            gt_samples = imed_utils.cuda(batch["gt"], cuda_available, non_blocking=True)

            tot_load = time.time() - start_load
            load_lst.append(tot_load)

            start_pred = time.time()

            if 'film_layers' in model_params:
                preds = model(input_samples, [[0, 1]])
            else:
                preds = model(input_samples)
            tot_pred = time.time() - start_pred
            pred_lst.append(tot_pred)

            start_opt = time.time()
            loss = loss_fct(preds, gt_samples)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            if step_scheduler_batch:
                scheduler.step()

            num_steps += 1
            tot_opt = time.time() - start_opt
            opt_lst.append(tot_opt)

            start_gen = time.time()

        start_schedule = time.time()
        if not step_scheduler_batch:
            scheduler.step()
        tot_schedule = time.time() - start_schedule
        schedule_lst.append(tot_schedule)

        end_time = time.time()
        total_time = end_time - start_time
        tqdm.write("Epoch {} took {:.2f} seconds.".format(epoch, total_time))

    logger.info(f"Mean SD init {np.mean(init_lst)} -- {np.std(init_lst)}")
    logger.info(f"Mean SD load {np.mean(load_lst)} -- {np.std(load_lst)}")
    logger.info(f"Mean SD pred {np.mean(pred_lst)} -- {np.std(pred_lst)}")
    logger.info(f"Mean SDopt {np.mean(opt_lst)} --  {np.std(opt_lst)}")
    logger.info(f"Mean SD gen {np.mean(gen_lst)} -- {np.std(gen_lst)}")
    logger.info(f"Mean SD scheduler {np.mean(schedule_lst)} -- {np.std(schedule_lst)}")
def test_image_orientation(download_data_testing_test_files,
                           loader_parameters):
    device = torch.device("cuda:" +
                          str(GPU_ID) if torch.cuda.is_available() else "cpu")
    cuda_available = torch.cuda.is_available()
    if cuda_available:
        torch.cuda.set_device(device)
        logger.info(f"Using GPU ID {device}")

    bids_df = BidsDataframe(loader_parameters, __tmp_dir__, derivatives=True)

    contrast_params = loader_parameters["contrast_params"]
    target_suffix = loader_parameters["target_suffix"]
    roi_params = loader_parameters["roi_params"]

    train_lst = ['sub-unf01_T1w.nii.gz']

    training_transform_dict = {
        "Resample": {
            "wspace": 1.5,
            "hspace": 1,
            "dspace": 3
        },
        "CenterCrop": {
            "size": [176, 128, 160]
        },
        "NormalizeInstance": {
            "applied_to": ['im']
        }
    }

    tranform_lst, training_undo_transform = imed_transforms.prepare_transforms(
        training_transform_dict)

    model_params = {
        "name": "Modified3DUNet",
        "dropout_rate": 0.3,
        "bn_momentum": 0.9,
        "depth": 2,
        "in_channel": 1,
        "out_channel": 1,
        "length_3D": [176, 128, 160],
        "stride_3D": [176, 128, 160],
        "attention": False,
        "n_filters": 8
    }

    for dim in ['2d', '3d']:
        for slice_axis in [0, 1, 2]:
            if dim == '2d':
                ds = BidsDataset(bids_df=bids_df,
                                 subject_file_lst=train_lst,
                                 target_suffix=target_suffix,
                                 contrast_params=contrast_params,
                                 model_params=model_params,
                                 metadata_choice=False,
                                 slice_axis=slice_axis,
                                 transform=tranform_lst,
                                 multichannel=False)
                ds.load_filenames()
            else:
                ds = Bids3DDataset(bids_df=bids_df,
                                   subject_file_lst=train_lst,
                                   target_suffix=target_suffix,
                                   model_params=model_params,
                                   contrast_params=contrast_params,
                                   metadata_choice=False,
                                   slice_axis=slice_axis,
                                   transform=tranform_lst,
                                   multichannel=False)

            loader = DataLoader(ds,
                                batch_size=1,
                                shuffle=True,
                                pin_memory=True,
                                collate_fn=imed_loader_utils.imed_collate,
                                num_workers=1)

            input_filename, gt_filename, roi_filename, metadata = ds.filename_pairs[
                0]
            segpair = SegmentationPair(input_filename,
                                       gt_filename,
                                       metadata=metadata,
                                       slice_axis=slice_axis)
            nib_original = nib.load(gt_filename[0])
            # Get image with original, ras and hwd orientations
            input_init = nib_original.get_fdata()
            input_ras = nib.as_closest_canonical(nib_original).get_fdata()
            img, gt = segpair.get_pair_data()
            input_hwd = gt[0]

            pred_tmp_lst, z_tmp_lst = [], []
            for i, batch in enumerate(loader):
                # batch["input_metadata"] = batch["input_metadata"][0]  # Take only metadata from one input
                # batch["gt_metadata"] = batch["gt_metadata"][0]  # Take only metadata from one label

                for smp_idx in range(len(batch['gt'])):
                    # undo transformations
                    if dim == '2d':
                        preds_idx_undo, metadata_idx = training_undo_transform(
                            batch["gt"][smp_idx],
                            batch["gt_metadata"][smp_idx],
                            data_type='gt')

                        # add new sample to pred_tmp_lst
                        pred_tmp_lst.append(preds_idx_undo[0])
                        z_tmp_lst.append(
                            int(batch['input_metadata'][smp_idx][0]
                                ['slice_index']))

                    else:
                        preds_idx_undo, metadata_idx = training_undo_transform(
                            batch["gt"][smp_idx],
                            batch["gt_metadata"][smp_idx],
                            data_type='gt')

                    fname_ref = metadata_idx[0]['gt_filenames'][0]

                    if (pred_tmp_lst and i == len(loader) - 1) or dim == '3d':
                        # save the completely processed file as a nii
                        nib_ref = nib.load(fname_ref)
                        nib_ref_can = nib.as_closest_canonical(nib_ref)

                        if dim == '2d':
                            tmp_lst = []
                            for z in range(nib_ref_can.header.get_data_shape()
                                           [slice_axis]):
                                tmp_lst.append(
                                    pred_tmp_lst[z_tmp_lst.index(z)])
                            arr = np.stack(tmp_lst, axis=-1)
                        else:
                            arr = np.array(preds_idx_undo[0])

                        # verify image after transform, undo transform and 3D reconstruction
                        input_hwd_2 = imed_postpro.threshold_predictions(arr)
                        # Some difference are generated due to transform and undo transform
                        # (e.i. Resample interpolation)
                        assert imed_metrics.dice_score(input_hwd_2,
                                                       input_hwd) >= 0.8
                        input_ras_2 = imed_loader_utils.orient_img_ras(
                            input_hwd_2, slice_axis)
                        assert imed_metrics.dice_score(input_ras_2,
                                                       input_ras) >= 0.8
                        input_init_2 = imed_loader_utils.reorient_image(
                            input_hwd_2, slice_axis, nib_ref, nib_ref_can)
                        assert imed_metrics.dice_score(input_init_2,
                                                       input_init) >= 0.8

                        # re-init pred_stack_lst
                        pred_tmp_lst, z_tmp_lst = [], []
loader_params = context["loader_parameters"]
loader_params["contrast_params"]["contrast_lst"] = loader_params[
    "contrast_params"]["training_validation"]

# CHOOSE TO INDEX DERIVATIVES OR NOT
# As per pybids, the indexing of derivatives works only if a "dataset_description.json" file
# is present in "derivatives" or "labels" folder with minimal content:
# {"Name": "Example dataset", "BIDSVersion": "1.0.2", "PipelineDescription": {"Name": "Example pipeline"}}
derivatives = True

# CREATE OUTPUT PATH
path_output = context["path_output"]
if not os.path.isdir(path_output):
    logger.info(f"Creating output path: {path_output}")
    os.makedirs(path_output)
else:
    logger.warning(f"Output path already exists: {path_output}")

# CREATE BIDSDataframe OBJECT
bids_df = BidsDataframe(loader_params, path_output, derivatives)
df = bids_df.df

# DROP "path" COLUMN AND SORT BY FILENAME FOR TESTING PURPOSES WITH data-testing
df = df.drop(columns=['path'])
df = df.sort_values(by=['filename']).reset_index(drop=True)

# SAVE DATAFRAME TO CSV FILE FOR data-testing
path_csv = "test_df_new_loader.csv"
df.to_csv(path_csv, index=False)
logger.debug(df)