예제 #1
0
 def _resample(self, image, p_resample, type_img, image_ref=None):
     """
     Resample at a fixed resolution to make sure the cord always appears with similar scale, regardless of the native
     resolution of the image. Assumes SAL orientation.
     :param image: Image() to resample
     :param p_resample: float: Resampling resolution in mm
     :param type_img: {'im', 'seg'}: If im, interpolate using spline. If seg, interpolate using linear then binarize.
     :param image_ref: Destination Image() to resample image to.
     :return:
     """
     dict_interp = {'im': 'spline', 'seg': 'linear'}
     # Create nibabel object
     nii = Nifti1Image(image.data, image.hdr.get_best_affine())
     img = nifti2nipy(nii)
     # If no reference image is provided, resample to specified resolution
     if image_ref is None:
         # Resample to px x p_resample x p_resample mm (orientation is SAL by convention in QC module)
         img_r = resample_nipy(img, new_size=str(image.dim[4]) + 'x' + str(p_resample) + 'x' + str(p_resample),
                               new_size_type='mm', interpolation=dict_interp[type_img])
     # Otherwise, resampling to the space of the reference image
     else:
         # Create nibabel object for reference image
         nii_ref = Nifti1Image(image_ref.data, image_ref.hdr.get_best_affine())
         img_ref = nifti2nipy(nii_ref)
         img_r = resample_nipy(img, img_dest=img_ref, interpolation=dict_interp[type_img])
     # If resampled image is a segmentation, binarize using threshold at 0.5
     if type_img == 'seg':
         img_r_data = (img_r.get_data() > 0.5) * 1
     else:
         img_r_data = img_r.get_data()
     nii_r = nipy2nifti(img_r)
     # Create Image objects
     image_r = Image(img_r_data, hdr=nii_r.header, dim=nii_r.header.get_data_shape()). \
         change_orientation(image.orientation)
     return image_r
예제 #2
0
 def _resample(self, image, p_resample, type, image_ref=None):
     """
     Resample at a fixed resolution to make sure the cord always appears with similar scale, regardless of the native
     resolution of the image. Assumes SAL orientation.
     :param image: Image() to resample
     :param p_resample: float: Resampling resolution in mm
     :param type: {'im', 'seg'}: If im, interpolate using spline. If seg, interpolate using linear then binarize.
     :param image_ref: Destination Image() to resample image to.
     :return:
     """
     # If no reference image is provided, create nipy object and resample using resample_nipy()
     if image_ref is None:
         dict_interp = {'im': 'spline', 'seg': 'linear'}
         # Create nibabel object
         nii = Nifti1Image(image.data, image.hdr.get_best_affine())
         img = nifti2nipy(nii)
         # Resample to px x p_resample x p_resample mm (orientation is SAL by convention in QC module)
         img_r = resample_nipy(img, new_size=str(image.dim[4]) + 'x' + str(p_resample) + 'x' + str(p_resample),
                               new_size_type='mm', interpolation=dict_interp[type])
         # If segmentation, binarize using threshold at 0.5
         if type == 'seg':
             img_r_data = (img_r.get_data() > 0.5) * 1
         else:
             img_r_data = img_r.get_data()
         nii_r = nipy2nifti(img_r)
         # Create Image objects
         image_r = Image(img_r_data, hdr=nii_r.header, dim=nii_r.header.get_data_shape()). \
             change_orientation(image.orientation)
     # If resampling to reference image, use Image() built-in resampling function to ref image
     else:
         dict_interp = {'im': 3, 'seg': 0}
         image_r = image.interpolate_from_image(image_ref, interpolation_mode=dict_interp[type], border='nearest')
     return image_r
def test_nipy_resample_image_4d(fake_4dimage_nipy):
    """Test resampling with 4D nipy image"""
    img_r = resampling.resample_nipy(fake_4dimage_nipy, new_size='2x2x1x1', new_size_type='factor', interpolation='nn')
    assert img_r.get_data().shape == (18, 18, 9, 3)
    assert img_r.get_data()[8, 8, 4, 0] == 1.0  # make sure there is no displacement in world coordinate system
    assert img_r.get_data()[8, 8, 4, 1] == 0.0
    assert nipy2nifti(img_r).header.get_zooms() == (0.5, 0.5, 1.0, 1.0)
def test_nipy_resample_image_4d(fake_4dimage_nipy):
    """Test resampling with 4D nipy image"""
    img_r = resampling.resample_nipy(fake_4dimage_nipy, new_size='2x2x1x1', new_size_type='factor', interpolation='nn')
    assert img_r.get_data().shape == (18, 18, 9, 3)
    assert img_r.get_data()[8, 8, 4, 0] == 1.0  # make sure there is no displacement in world coordinate system
    assert img_r.get_data()[8, 8, 4, 1] == 0.0
    assert nipy2nifti(img_r).header.get_zooms() == (0.5, 0.5, 1.0, 1.0)
예제 #5
0
def test_nipy_resample_image_3d_to_dest(fake_3dimage_nipy,
                                        fake_3dimage_nipy_big):
    """Test resampling with 3D nipy image"""
    img_r = resampling.resample_nipy(fake_3dimage_nipy,
                                     img_dest=fake_3dimage_nipy_big,
                                     interpolation='linear')
    assert img_r.get_data().shape == (29, 39, 19)
    assert img_r.get_data()[4, 4, 4] == 1.0
예제 #6
0
def segment_file(input_filename, output_filename,
                 model_name, threshold, verbosity,
                 use_tta):
    """Segment a volume file.

    :param input_filename: the input filename.
    :param output_filename: the output filename.
    :param model_name: the name of model to use.
    :param threshold: threshold to apply in predictions (if None,
                      no threshold will be applied)
    :param verbosity: the verbosity level.
    :param use_tta: whether it should use TTA (test-time augmentation)
                    or not.
    :return: the output filename.
    """
    nii_original = nipy.load_image(input_filename)
    pixdim = nii_original.header["pixdim"][3]
    target_resample = "0.25x0.25x{:.5f}".format(pixdim)

    nii_resampled = resampling.resample_nipy(nii_original, new_size=target_resample, new_size_type='mm',
                                             interpolation='linear', verbose=verbosity)
    nii_resampled = nipy2nifti(nii_resampled)
    pred_slices = segment_volume(nii_resampled, model_name, threshold,
                                 use_tta)

    original_res = "{:.5f}x{:.5f}x{:.5f}".format(
        nii_original.header["pixdim"][1],
        nii_original.header["pixdim"][2],
        nii_original.header["pixdim"][3])

    volume_affine = nii_resampled.affine
    volume_header = nii_resampled.header
    nii_segmentation = nib.Nifti1Image(pred_slices, volume_affine,
                                       volume_header)
    nii_segmentation = nifti2nipy(nii_segmentation)

    nii_resampled_original = resampling.resample_nipy(nii_segmentation, new_size=original_res, new_size_type='mm',
                                                      interpolation='linear', verbose=verbosity)
    res_data = nii_resampled_original.get_data()

    # Threshold after resampling, only if specified
    if threshold is not None:
        res_data = threshold_predictions(res_data, 0.5)

    nipy.save_image(nii_resampled_original, output_filename)
    return output_filename
def dummy_segmentation(size_arr=(256, 256, 256),
                       pixdim=(1, 1, 1),
                       dtype=np.float64,
                       orientation='LPI',
                       shape='rectangle',
                       angle_RL=0,
                       angle_AP=0,
                       angle_IS=0,
                       radius_RL=5.0,
                       radius_AP=3.0,
                       zeroslice=[],
                       debug=False):
    """Create a dummy Image with a ellipse or ones running from top to bottom in the 3rd dimension, and rotate the image
    to make sure that compute_csa and compute_shape properly estimate the centerline angle.
    :param size_arr: tuple: (nx, ny, nz)
    :param pixdim: tuple: (px, py, pz)
    :param dtype: Numpy dtype.
    :param orientation: Orientation of the image. Default: LPI
    :param shape: {'rectangle', 'ellipse'}
    :param angle_RL: int: angle around RL axis (in deg)
    :param angle_AP: int: angle around AP axis (in deg)
    :param angle_IS: int: angle around IS axis (in deg)
    :param radius_RL: float: 1st radius. With a, b = 50.0, 30.0 (in mm), theoretical CSA of ellipse is 4712.4
    :param radius_AP: float: 2nd radius
    :param zeroslice: list int: zero all slices listed in this param
    :param debug: Write temp files for debug
    :return: img: Image object
    """
    # Initialization
    padding = 15  # Padding size (isotropic) to avoid edge effect during rotation
    # Create a 3d array, with dimensions corresponding to x: RL, y: AP, z: IS
    nx, ny, nz = [int(size_arr[i] * pixdim[i]) for i in range(3)]
    data = np.random.random((nx, ny, nz)) * 0.
    xx, yy = np.mgrid[:nx, :ny]
    # loop across slices and add object
    for iz in range(nz):
        if shape == 'rectangle':  # theoretical CSA: (a*2+1)(b*2+1)
            data[:, :, iz] = ((abs(xx - nx / 2) <= radius_RL) &
                              (abs(yy - ny / 2) <= radius_AP)) * 1
        if shape == 'ellipse':
            data[:, :, iz] = (((xx - nx / 2) / radius_RL)**2 +
                              ((yy - ny / 2) / radius_AP)**2 <= 1) * 1

    # Pad to avoid edge effect during rotation
    data = np.pad(data, padding, 'reflect')

    # ROTATION ABOUT IS AXIS
    # rotate (in deg), and re-grid using linear interpolation
    data_rotIS = rotate(data,
                        angle_IS,
                        resize=False,
                        center=None,
                        order=1,
                        mode='constant',
                        cval=0,
                        clip=False,
                        preserve_range=False)

    # ROTATION ABOUT RL AXIS
    # Swap x-z axes (to make a rotation within y-z plane, because rotate will apply rotation on the first 2 dims)
    data_rotIS_swap = data_rotIS.swapaxes(0, 2)
    # rotate (in deg), and re-grid using linear interpolation
    data_rotIS_swap_rotRL = rotate(data_rotIS_swap,
                                   angle_RL,
                                   resize=False,
                                   center=None,
                                   order=1,
                                   mode='constant',
                                   cval=0,
                                   clip=False,
                                   preserve_range=False)
    # swap back
    data_rotIS_rotRL = data_rotIS_swap_rotRL.swapaxes(0, 2)

    # ROTATION ABOUT AP AXIS
    # Swap y-z axes (to make a rotation within x-z plane)
    data_rotIS_rotRL_swap = data_rotIS_rotRL.swapaxes(1, 2)
    # rotate (in deg), and re-grid using linear interpolation
    data_rotIS_rotRL_swap_rotAP = rotate(data_rotIS_rotRL_swap,
                                         angle_AP,
                                         resize=False,
                                         center=None,
                                         order=1,
                                         mode='constant',
                                         cval=0,
                                         clip=False,
                                         preserve_range=False)
    # swap back
    data_rot = data_rotIS_rotRL_swap_rotAP.swapaxes(1, 2)

    # Crop image (to remove padding)
    data_rot_crop = data_rot[padding:nx + padding, padding:ny + padding,
                             padding:nz + padding]

    # Zero specified slices
    if zeroslice is not []:
        data_rot_crop[:, :, zeroslice] = 0

    # Create nibabel object
    xform = np.eye(4)
    for i in range(3):
        xform[i][i] = 1  # in [mm]
    nii = nib.nifti1.Nifti1Image(data_rot_crop.astype('float32'), xform)
    # Create nipy object and resample to desired resolution
    nii_nipy = nifti2nipy(nii)
    nii_nipy_r = resample_nipy(nii_nipy,
                               new_size='x'.join([str(i) for i in pixdim]),
                               new_size_type='mm',
                               interpolation='linear',
                               dtype=dtype)
    nii_r = nipy2nifti(nii_nipy_r)
    # Create Image object. Default orientation is LPI.
    # For debugging add .save() at the end of the command below
    img = Image(nii_r.get_data(),
                hdr=nii_r.header,
                dim=nii_r.header.get_data_shape())
    # Update orientation
    img.change_orientation(orientation)
    if debug:
        img.save('tmp_dummy_seg_' + datetime.now().strftime("%Y%m%d%H%M%S%f") +
                 '.nii.gz')
    return img
def test_nipy_resample_image_3d_to_dest(fake_3dimage_nipy, fake_3dimage_nipy_big):
    """Test resampling with 3D nipy image"""
    img_r = resampling.resample_nipy(fake_3dimage_nipy, img_dest=fake_3dimage_nipy_big, interpolation='linear')
    assert img_r.get_data().shape == (29, 39, 19)
    assert img_r.get_data()[4, 4, 4] == 1.0
def dummy_segmentation(size_arr=(256, 256, 256), pixdim=(1, 1, 1), dtype=np.float64, orientation='LPI',
                       shape='rectangle', angle_RL=0, angle_AP=0, angle_IS=0, radius_RL=5.0, radius_AP=3.0,
                       zeroslice=[], debug=False):
    """Create a dummy Image with a ellipse or ones running from top to bottom in the 3rd dimension, and rotate the image
    to make sure that compute_csa and compute_shape properly estimate the centerline angle.
    :param size_arr: tuple: (nx, ny, nz)
    :param pixdim: tuple: (px, py, pz)
    :param dtype: Numpy dtype.
    :param orientation: Orientation of the image. Default: LPI
    :param shape: {'rectangle', 'ellipse'}
    :param angle_RL: int: angle around RL axis (in deg)
    :param angle_AP: int: angle around AP axis (in deg)
    :param angle_IS: int: angle around IS axis (in deg)
    :param radius_RL: float: 1st radius. With a, b = 50.0, 30.0 (in mm), theoretical CSA of ellipse is 4712.4
    :param radius_AP: float: 2nd radius
    :param zeroslice: list int: zero all slices listed in this param
    :param debug: Write temp files for debug
    :return: img: Image object
    """
    # Initialization
    padding = 15  # Padding size (isotropic) to avoid edge effect during rotation
    # Create a 3d array, with dimensions corresponding to x: RL, y: AP, z: IS
    nx, ny, nz = [int(size_arr[i] * pixdim[i]) for i in range(3)]
    data = np.random.random((nx, ny, nz)) * 0.
    xx, yy = np.mgrid[:nx, :ny]
    # loop across slices and add object
    for iz in range(nz):
        if shape == 'rectangle':  # theoretical CSA: (a*2+1)(b*2+1)
            data[:, :, iz] = ((abs(xx - nx / 2) <= radius_RL) & (abs(yy - ny / 2) <= radius_AP)) * 1
        if shape == 'ellipse':
            data[:, :, iz] = (((xx - nx / 2) / radius_RL) ** 2 + ((yy - ny / 2) / radius_AP) ** 2 <= 1) * 1

    # Pad to avoid edge effect during rotation
    data = np.pad(data, padding, 'reflect')

    # ROTATION ABOUT IS AXIS
    # rotate (in deg), and re-grid using linear interpolation
    data_rotIS = rotate(data, angle_IS, resize=False, center=None, order=1, mode='constant', cval=0, clip=False,
                        preserve_range=False)

    # ROTATION ABOUT RL AXIS
    # Swap x-z axes (to make a rotation within y-z plane, because rotate will apply rotation on the first 2 dims)
    data_rotIS_swap = data_rotIS.swapaxes(0, 2)
    # rotate (in deg), and re-grid using linear interpolation
    data_rotIS_swap_rotRL = rotate(data_rotIS_swap, angle_RL, resize=False, center=None, order=1, mode='constant',
                                   cval=0, clip=False, preserve_range=False)
    # swap back
    data_rotIS_rotRL = data_rotIS_swap_rotRL.swapaxes(0, 2)

    # ROTATION ABOUT AP AXIS
    # Swap y-z axes (to make a rotation within x-z plane)
    data_rotIS_rotRL_swap = data_rotIS_rotRL.swapaxes(1, 2)
    # rotate (in deg), and re-grid using linear interpolation
    data_rotIS_rotRL_swap_rotAP = rotate(data_rotIS_rotRL_swap, angle_AP, resize=False, center=None, order=1,
                                         mode='constant', cval=0, clip=False, preserve_range=False)
    # swap back
    data_rot = data_rotIS_rotRL_swap_rotAP.swapaxes(1, 2)

    # Crop image (to remove padding)
    data_rot_crop = data_rot[padding:nx+padding, padding:ny+padding, padding:nz+padding]

    # Zero specified slices
    if zeroslice is not []:
        data_rot_crop[:, :, zeroslice] = 0

    # Create nibabel object
    xform = np.eye(4)
    for i in range(3):
        xform[i][i] = 1  # in [mm]
    nii = nib.nifti1.Nifti1Image(data_rot_crop.astype('float32'), xform)
    # Create nipy object and resample to desired resolution
    nii_nipy = nifti2nipy(nii)
    nii_nipy_r = resample_nipy(nii_nipy, new_size='x'.join([str(i) for i in pixdim]), new_size_type='mm',
                               interpolation='linear', dtype=dtype)
    nii_r = nipy2nifti(nii_nipy_r)
    # Create Image object. Default orientation is LPI.
    # For debugging add .save() at the end of the command below
    img = Image(nii_r.get_data(), hdr=nii_r.header, dim=nii_r.header.get_data_shape())
    # Update orientation
    img.change_orientation(orientation)
    if debug:
        img.save('tmp_dummy_seg_'+datetime.now().strftime("%Y%m%d%H%M%S%f")+'.nii.gz')
    return img