def dmri_mask(tmp_path):
    """Mask image for testing."""
    path_out = str(tmp_path / 'mask.nii')
    sct_create_mask.main(argv=['-i', 'dmri/dmri_T0000.nii.gz', '-p', 'center', '-size', '5mm', '-f', 'gaussian',
                               '-o', path_out])

    return path_out
Example #2
0
def test_sct_register_multimodal_with_softmask(tmp_path):
    """
    Verify that softmask is actually applied during registration.

    NB: For 'gaussian', ANTs binaries can't handle softmasks natively, so SCT should be applying
        the mask directly to the image. Related links:
            * https://github.com/ivadomed/pipeline-hemis/issues/3.
            * https://github.com/spinalcordtoolbox/spinalcordtoolbox/issues/3075
    """
    fname_mask = str(tmp_path / 'mask_t2.nii.gz')
    fname_t2 = sct_test_path('t2', 't2.nii.gz')
    fname_t1 = sct_test_path('t1', 't1w.nii.gz')
    fname_warp = str(tmp_path / "warp_t1w2t2.nii.gz")

    sct_create_mask.main([
        '-i', fname_t2, '-p',
        f"centerline,{sct_test_path('t2', 't2_centerline-manual.nii.gz')}",
        '-o', fname_mask, '-f', 'gaussian'
    ])
    sct_register_multimodal.main([
        '-i', fname_t1, '-d', fname_t2, '-dseg',
        sct_test_path('t2', 't2_seg-manual.nii.gz'), '-param',
        "step=1,type=im,algo=slicereg,metric=CC", '-m', fname_mask, '-ofolder',
        str(tmp_path), '-r', '0', '-v', '2'
    ])

    # If registration was successful, the warping field should be non-empty
    assert np.any(Image(fname_warp).data)
Example #3
0
def test_sct_register_multimodal_mask_files_exist(tmp_path):
    """
    Run the script without validating results.

    - TODO: Write a check that verifies the registration results.
    - TODO: Parametrize this test to add '-initwarpinv warp_anat2template.nii.gz',
            after the file is added to sct_testing_data:
            https://github.com/spinalcordtoolbox/spinalcordtoolbox/pull/3407#discussion_r646895013
    """
    fname_mask = str(tmp_path / 'mask_mt1.nii.gz')
    sct_create_mask.main([
        '-i',
        sct_test_path('mt', 'mt1.nii.gz'), '-p',
        f"centerline,{sct_test_path('mt', 'mt1_seg.nii.gz')}", '-size', '35mm',
        '-f', 'cylinder', '-o', fname_mask
    ])
    sct_register_multimodal.main([
        '-i',
        sct_dir_local_path('data/PAM50/template/', 'PAM50_t2.nii.gz'), '-iseg',
        sct_dir_local_path('data/PAM50/template/', 'PAM50_cord.nii.gz'), '-d',
        sct_test_path('mt', 'mt1.nii.gz'), '-dseg',
        sct_test_path('mt', 'mt1_seg.nii.gz'), '-param',
        'step=1,type=seg,algo=centermass:step=2,type=seg,algo=bsplinesyn,slicewise=1,iter=3',
        '-m', fname_mask, '-initwarp',
        sct_test_path('t2', 'warp_template2anat.nii.gz'), '-ofolder',
        str(tmp_path)
    ])

    for path in ["PAM50_t2_reg.nii.gz", "warp_PAM50_t22mt1.nii.gz"]:
        assert os.path.exists(tmp_path / path)

    # Because `-initwarp` was specified (but `-initwarpinv` wasn't) the dest->seg files should NOT exist
    for path in ["mt1_reg.nii.gz", "warp_mt12PAM50_t2.nii.gz"]:
        assert not os.path.exists(tmp_path / path)
Example #4
0
def test_sct_register_multimodal_mask_no_checks(tmp_path):
    """Run the script without validating results.

    TODO: Write a check that verifies the registration results as part of
    https://github.com/spinalcordtoolbox/spinalcordtoolbox/pull/3246."""
    fname_mask = str(tmp_path / 'mask_mt1.nii.gz')
    sct_create_mask.main([
        '-i',
        sct_test_path('mt', 'mt1.nii.gz'), '-p',
        f"centerline,{sct_test_path('mt', 'mt1_seg.nii.gz')}", '-size', '35mm',
        '-f', 'cylinder', '-o', fname_mask
    ])
    sct_register_multimodal.main([
        '-i',
        sct_dir_local_path('data/PAM50/template/', 'PAM50_t2.nii.gz'), '-iseg',
        sct_dir_local_path('data/PAM50/template/', 'PAM50_cord.nii.gz'), '-d',
        sct_test_path('mt', 'mt1.nii.gz'), '-dseg',
        sct_test_path('mt', 'mt1_seg.nii.gz'), '-param',
        'step=1,type=seg,algo=centermass:step=2,type=seg,algo=bsplinesyn,slicewise=1,iter=3',
        '-m', fname_mask, '-initwarp',
        sct_test_path('t2', 'warp_template2anat.nii.gz')
    ])
def test_sct_create_mask_no_checks(path_input, process, size):
    """Run the CLI script without checking results.
    TODO: Check the results. (This test replaces the 'sct_testing' test, which did not implement any checks.)"""
    sct_create_mask.main(argv=['-i', path_input, '-p', process, '-size', size, '-r', '0'])
def pre_processing(fname_target,
                   fname_sc_seg,
                   fname_level=None,
                   fname_manual_gmseg=None,
                   new_res=0.3,
                   square_size_size_mm=22.5,
                   denoising=True,
                   verbose=1,
                   rm_tmp=True,
                   for_model=False):
    printv('\nPre-process data...', verbose, 'normal')

    tmp_dir = tmp_create()

    copy(fname_target, tmp_dir)
    fname_target = ''.join(extract_fname(fname_target)[1:])
    copy(fname_sc_seg, tmp_dir)
    fname_sc_seg = ''.join(extract_fname(fname_sc_seg)[1:])

    curdir = os.getcwd()
    os.chdir(tmp_dir)

    original_info = {
        'orientation': None,
        'im_sc_seg_rpi': None,
        'interpolated_images': []
    }

    im_target = Image(fname_target).copy()
    im_sc_seg = Image(fname_sc_seg).copy()

    # get original orientation
    printv('  Reorient...', verbose, 'normal')
    original_info['orientation'] = im_target.orientation

    # assert images are in the same orientation
    assert im_target.orientation == im_sc_seg.orientation, "ERROR: the image to segment and it's SC segmentation are not in the same orientation"

    im_target_rpi = im_target.copy().change_orientation(
        'RPI', generate_path=True).save()
    im_sc_seg_rpi = im_sc_seg.copy().change_orientation(
        'RPI', generate_path=True).save()
    original_info['im_sc_seg_rpi'] = im_sc_seg_rpi.copy(
    )  # target image in RPI will be used to post-process segmentations

    # denoise using P. Coupe non local means algorithm (see [Manjon et al. JMRI 2010]) implemented in dipy
    if denoising:
        printv('  Denoise...', verbose, 'normal')
        # crop image before denoising to fasten denoising
        nx, ny, nz, nt, px, py, pz, pt = im_target_rpi.dim
        size_x, size_y = (square_size_size_mm + 1) / px, (square_size_size_mm +
                                                          1) / py
        size = int(np.ceil(max(size_x, size_y)))
        # create mask
        fname_mask = 'mask_pre_crop.nii.gz'
        sct_create_mask.main([
            '-i', im_target_rpi.absolutepath, '-p',
            'centerline,' + im_sc_seg_rpi.absolutepath, '-f', 'box', '-size',
            str(size), '-o', fname_mask
        ])
        # crop image
        cropper = ImageCropper(im_target_rpi)
        cropper.get_bbox_from_mask(Image(fname_mask))
        im_target_rpi_crop = cropper.crop()
        # crop segmentation
        cropper = ImageCropper(im_sc_seg_rpi)
        cropper.get_bbox_from_mask(Image(fname_mask))
        im_sc_seg_rpi_crop = cropper.crop()
        # denoising
        from spinalcordtoolbox.math import denoise_nlmeans
        block_radius = 3
        block_radius = int(
            im_target_rpi_crop.data.shape[2] /
            2) if im_target_rpi_crop.data.shape[2] < (block_radius *
                                                      2) else block_radius
        patch_radius = block_radius - 1
        data_denoised = denoise_nlmeans(im_target_rpi_crop.data,
                                        block_radius=block_radius,
                                        patch_radius=patch_radius)
        im_target_rpi_crop.data = data_denoised

        im_target_rpi = im_target_rpi_crop
        im_sc_seg_rpi = im_sc_seg_rpi_crop
    else:
        fname_mask = None

    # interpolate image to reference square image (resample and square crop centered on SC)
    printv('  Interpolate data to the model space...', verbose, 'normal')
    list_im_slices = interpolate_im_to_ref(im_target_rpi,
                                           im_sc_seg_rpi,
                                           new_res=new_res,
                                           sq_size_size_mm=square_size_size_mm)
    original_info[
        'interpolated_images'] = list_im_slices  # list of images (not Slice() objects)

    printv('  Mask data using the spinal cord segmentation...', verbose,
           'normal')
    list_sc_seg_slices = interpolate_im_to_ref(
        im_sc_seg_rpi,
        im_sc_seg_rpi,
        new_res=new_res,
        sq_size_size_mm=square_size_size_mm,
        interpolation_mode=1)
    for i in range(len(list_im_slices)):
        # list_im_slices[i].data[list_sc_seg_slices[i].data == 0] = 0
        list_sc_seg_slices[i] = binarize(list_sc_seg_slices[i],
                                         thr_min=0.5,
                                         thr_max=1)
        list_im_slices[
            i].data = list_im_slices[i].data * list_sc_seg_slices[i].data

    printv('  Split along rostro-caudal direction...', verbose, 'normal')
    list_slices_target = [
        Slice(slice_id=i, im=im_slice.data, gm_seg=[], wm_seg=[])
        for i, im_slice in enumerate(list_im_slices)
    ]

    # load vertebral levels
    if fname_level is not None:
        printv('  Load vertebral levels...', verbose, 'normal')
        # copy level file to tmp dir
        os.chdir(curdir)
        copy(fname_level, tmp_dir)
        os.chdir(tmp_dir)
        # change fname level to only file name (path = tmp dir now)
        fname_level = ''.join(extract_fname(fname_level)[1:])
        # load levels
        list_slices_target = load_level(list_slices_target, fname_level)

    os.chdir(curdir)

    # load manual gmseg if there is one (model data)
    if fname_manual_gmseg is not None:
        printv('\n\tLoad manual GM segmentation(s) ...', verbose, 'normal')
        list_slices_target = load_manual_gmseg(list_slices_target,
                                               fname_manual_gmseg,
                                               tmp_dir,
                                               im_sc_seg_rpi,
                                               new_res,
                                               square_size_size_mm,
                                               for_model=for_model,
                                               fname_mask=fname_mask)

    if rm_tmp:
        # remove tmp folder
        rmtree(tmp_dir)
    return list_slices_target, original_info