def dmri_ail_cropped(tmp_path):
    """Reorient image to sagittal for testing another orientation (and crop to save time)."""
    path_out = str(tmp_path / 'dmri_AIL_crop.nii')
    sct_image.main(argv=['-i', 'dmri/dmri.nii.gz', '-setorient', 'AIL', '-o', 'dmri/dmri_AIL.nii'])
    sct_crop_image.main(argv=['-i', 'dmri/dmri_AIL.nii', '-zmin', '19', '-zmax', '21', '-o', path_out])

    return path_out
def test_sct_image_concat_dmri(tmp_path, dmri_t_slices, dmri_in):
    """Run the CLI script and verify concatenated image matches reference image."""
    path_out = str(tmp_path / 'dmri_concat.nii.gz')
    sct_image.main(argv=['-i'] + dmri_t_slices +
                   ['-concat', 't', '-o', path_out])
    ref = Image(dmri_in)
    new = Image(path_out)
    assert np.linalg.norm(ref.data - new.data) == 0
def dmri_t_slices(tmp_path, dmri_in):
    """Filepaths for 4D dMRI image split across t axis."""
    fname_out = str(tmp_path / 'dmri.nii.gz')
    sct_image.main(argv=['-i', dmri_in, '-split', 't', '-o', fname_out])

    parent, filename, ext = extract_fname(fname_out)
    fname_slices = [
        os.path.join(parent, filename + '_T' + str(i).zfill(4) + ext)
        for i in range(7)
    ]
    return fname_slices
def test_sct_image_pad():
    """Run the CLI script and test the '-pad' option."""
    pad = 2
    path_in = 'mt/mtr.nii.gz'  # 3D
    path_out = 'sct_image_out.nii.gz'
    sct_image.main(argv=[
        '-i', path_in, '-o', 'sct_image_out.nii.gz', '-pad', f'0,0,{pad}'
    ])

    # z axis (dim[2]) should be padded, but all other values should be unchanged
    expected_dim = Image(path_in).dim[:2] + (
        Image(path_in).dim[2] + (2 * pad), ) + Image(path_in).dim[3:]
    assert Image(path_out).dim == expected_dim
예제 #5
0
def check_and_correct_segmentation(fname_segmentation,
                                   fname_centerline,
                                   folder_output='',
                                   threshold_distance=5.0,
                                   remove_temp_files=1,
                                   verbose=0):
    """
    This function takes the outputs of isct_propseg (centerline and segmentation) and check if the centerline of the
    segmentation is coherent with the centerline provided by the isct_propseg, especially on the edges (related
    to issue #1074).
    Args:
        fname_segmentation: filename of binary segmentation
        fname_centerline: filename of binary centerline
        threshold_distance: threshold, in mm, beyond which centerlines are not coherent
        verbose:

    Returns: None
    """
    printv('\nCheck consistency of segmentation...', verbose)
    # creating a temporary folder in which all temporary files will be placed and deleted afterwards
    path_tmp = tmp_create(basename="propseg")
    convert(fname_segmentation,
            os.path.join(path_tmp, "tmp.segmentation.nii.gz"),
            verbose=0)
    convert(fname_centerline,
            os.path.join(path_tmp, "tmp.centerline.nii.gz"),
            verbose=0)
    fname_seg_absolute = os.path.abspath(fname_segmentation)

    # go to tmp folder
    curdir = os.getcwd()
    os.chdir(path_tmp)

    # convert segmentation image to RPI
    im_input = Image('tmp.segmentation.nii.gz')
    image_input_orientation = im_input.orientation

    sct_image.main(
        "-i tmp.segmentation.nii.gz -setorient RPI -o tmp.segmentation_RPI.nii.gz -v 0"
        .split())
    sct_image.main(
        "-i tmp.centerline.nii.gz -setorient RPI -o tmp.centerline_RPI.nii.gz -v 0"
        .split())

    # go through segmentation image, and compare with centerline from propseg
    im_seg = Image('tmp.segmentation_RPI.nii.gz')
    im_centerline = Image('tmp.centerline_RPI.nii.gz')

    # Get size of data
    printv('\nGet data dimensions...', verbose)
    nx, ny, nz, nt, px, py, pz, pt = im_seg.dim

    # extraction of centerline provided by isct_propseg and computation of center of mass for each slice
    # the centerline is defined as the center of the tubular mesh outputed by propseg.
    centerline, key_centerline = {}, []
    for i in range(nz):
        slice = im_centerline.data[:, :, i]
        if np.any(slice):
            x_centerline, y_centerline = ndi.measurements.center_of_mass(slice)
            centerline[str(i)] = [x_centerline, y_centerline]
            key_centerline.append(i)

    minz_centerline = np.min(key_centerline)
    maxz_centerline = np.max(key_centerline)
    mid_slice = int((maxz_centerline - minz_centerline) / 2)

    # for each slice of the segmentation, check if only one object is present. If not, remove the slice from segmentation.
    # If only one object (the spinal cord) is present in the slice, check if its center of mass is close to the centerline of isct_propseg.
    slices_to_remove = [
        False
    ] * nz  # flag that decides if the slice must be removed
    for i in range(minz_centerline, maxz_centerline + 1):
        # extraction of slice
        slice = im_seg.data[:, :, i]
        distance = -1
        label_objects, nb_labels = ndi.label(
            slice)  # count binary objects in the slice
        if nb_labels > 1:  # if there is more that one object in the slice, the slice is removed from the segmentation
            slices_to_remove[i] = True
        elif nb_labels == 1:  # check if the centerline is coherent with the one from isct_propseg
            x_centerline, y_centerline = ndi.measurements.center_of_mass(slice)
            slice_nearest_coord = min(key_centerline, key=lambda x: abs(x - i))
            coord_nearest_coord = centerline[str(slice_nearest_coord)]
            distance = np.sqrt((
                (x_centerline - coord_nearest_coord[0]) * px)**2 + (
                    (y_centerline - coord_nearest_coord[1]) * py)**2 +
                               ((i - slice_nearest_coord) * pz)**2)

            if distance >= threshold_distance:  # threshold must be adjusted, default is 5 mm
                slices_to_remove[i] = True

    # Check list of removal and keep one continuous centerline (improve this comment)
    # Method:
    # starting from mid-centerline (in both directions), the first True encountered is applied to all following slices
    slice_to_change = False
    for i in range(mid_slice, nz):
        if slice_to_change:
            slices_to_remove[i] = True
        elif slices_to_remove[i]:
            slice_to_change = True

    slice_to_change = False
    for i in range(mid_slice, 0, -1):
        if slice_to_change:
            slices_to_remove[i] = True
        elif slices_to_remove[i]:
            slice_to_change = True

    for i in range(0, nz):
        # remove the slice
        if slices_to_remove[i]:
            im_seg.data[:, :, i] *= 0

    # saving the image
    im_seg.save('tmp.segmentation_RPI_c.nii.gz')

    # replacing old segmentation with the corrected one
    sct_image.main(
        '-i tmp.segmentation_RPI_c.nii.gz -setorient {} -o {} -v 0'.format(
            image_input_orientation, fname_seg_absolute).split())

    os.chdir(curdir)

    # display information about how much of the segmentation has been corrected

    # remove temporary files
    if remove_temp_files:
        # printv("\nRemove temporary files...", verbose)
        rmtree(path_tmp)
def test_sct_image_display_warp_check_output_exists():
    """Run the CLI script and check that the warp image file was created."""
    fname_in = 'warp_template2anat.nii.gz'
    fname_out = 'grid_3_resample_' + fname_in
    sct_image.main(argv=['-i', sct_test_path('t2', fname_in), '-display-warp'])
    assert os.path.exists(sct_test_path('t2', fname_out))
def test_sct_image_show_header_no_checks(output_format):
    """Run the CLI script without checking results. The rationale for not checking results is
    provided here: https://github.com/spinalcordtoolbox/spinalcordtoolbox/pull/3317#issuecomment-811429547"""
    sct_image.main(argv=[
        '-i', sct_test_path('t2', 't2.nii.gz'), '-header', output_format
    ])
def test_sct_image_getorient(path_in):
    """Run the CLI script and ."""
    sct_image.main(argv=['-i', path_in, '-getorient'])