重新采样DICOM图像以调整大小和间距,并对齐到相同的原点

时间:2018-10-27 17:17:20

标签: python simpleitk

我有一组4个DICOM CT卷,正在使用SimpleITK ImageSeriesReader进行阅读。其中两个图像表示手术前后患者的CT。另外两个图像是在前2个CT图像上分割的二进制分割蒙版。细分是其原始CT的ROI。

所有4张CT图像的大小,间距,原点和方向都不同。我尝试应用此GitHub要点https://gist.github.com/zivy/79d7ee0490faee1156c1277a78e4a4c4将我的图片调整为512x512x512和Spacing 1x1x1。但是,它不会将图像放置在正确的位置。从图中可以看到,分割后的结构始终位于CT图像的中心,而不是正确的位置。

这是我的“原始” DICOM图像,具有肿瘤分割(橙色斑点)。

enter image description here

这是在“调整大小”算法之后并写入磁盘(与之前相同的图像,只是因为不一致,肿瘤被染成绿色的斑点): enter image description here

用于将所有4个DICOM卷重采样为相同尺寸的代码:

def resize_resample_images(images):
    """ Resize all the images to the same dimensions, spacing and origin.
        Usage: newImage = resize_image(source_img_plan, source_img_validation, ROI(ablation/tumor)_mask)
        1. translate to same origin
        2. largest number of slices and interpolate the others.
        3. same resolution 1x1x1 mm3 - resample
        4. (physical space)
        Slice Thickness (0018,0050)
        ImagePositionPatient (0020,0032)
        ImageOrientationPatient (0020,0037)
        PixelSpacing (0028,0030)
        Frame Of Reference UID (0020,0052)
    """
    # %% Define tuple to store the images
    tuple_resized_imgs = collections.namedtuple('tuple_resized_imgs',
                                                ['img_plan',
                                                 'img_validation',
                                                 'ablation_mask',
                                                 'tumor_mask'])
    # %% Create Reference image with zero origin, identity direction cosine matrix and isotropic dimension
    dimension = images.img_plan.GetDimension()  #
    reference_direction = np.identity(dimension).flatten()
    reference_size = [512] * dimension
    reference_origin = np.zeros(dimension)
    data = [images.img_plan, images.img_validation, images.ablation_mask, images.tumor_mask]

    reference_spacing = np.ones(dimension) # resize to isotropic size
    reference_image = sitk.Image(reference_size, images.img_plan.GetPixelIDValue())
    reference_image.SetOrigin(reference_origin)
    reference_image.SetSpacing(reference_spacing)
    reference_image.SetDirection(reference_direction)
    reference_center = np.array(
        reference_image.TransformContinuousIndexToPhysicalPoint(np.array(reference_image.GetSize()) / 2.0))

    #%% Paste the GT segmentation masks before transformation
    tumor_mask_paste = (paste_roi_image(images.img_plan, images.tumor_mask))
    ablation_mask_paste = (paste_roi_image(images.img_validation, images.ablation_mask))
    images.tumor_mask = tumor_mask_paste
    images.ablation_mask = ablation_mask_paste


    # %%  Apply transforms
    data_resized = []
    for idx,img in enumerate(data):
        transform = sitk.AffineTransform(dimension) # use affine transform with 3 dimensions
        transform.SetMatrix(img.GetDirection()) # set the cosine direction matrix
        # TODO: check translation when computing the segmentations
        transform.SetTranslation(np.array(img.GetOrigin()) - reference_origin) # set the translation.
        # Modify the transformation to align the centers of the original and reference image instead of their origins.
        centering_transform = sitk.TranslationTransform(dimension)
        img_center = np.array(img.TransformContinuousIndexToPhysicalPoint(np.array(img.GetSize()) / 2.0))
        centering_transform.SetOffset(np.array(transform.GetInverse().TransformPoint(img_center) - reference_center))
        centered_transform = sitk.Transform(transform)
        centered_transform.AddTransform(centering_transform)
        # Using the linear interpolator as these are intensity images, if there is a need to resample a ground truth
        # segmentation then the segmentation image should be resampled using the NearestNeighbor interpolator so that
        # no new labels are introduced.

        if (idx==1 or idx==2): # temporary solution to resample the GT image with NearestNeighbour
            resampled_img = sitk.Resample(img, reference_image, centered_transform, sitk.sitkNearestNeighbor, 0.0)

        else:
             resampled_img = sitk.Resample(img, reference_image, centered_transform, sitk.sitkLinear, 0.0)
        # append to list
        data_resized.append(resampled_img)


    # assuming the order stays the same, reassigng back to tuple
    resized_imgs = tuple_resized_imgs(img_plan=data_resized[0],
                                      img_validation=data_resized[1],
                                      ablation_mask=data_resized[2],
                                      tumor_mask=data_resized[3])

用于将ROI细分图像“粘贴”到正确大小的代码。可能是多余的。:

def paste_roi_image(image_source, image_roi):
    """ Resize ROI binary mask to size, dimension, origin of its source/original img.
        Usage: newImage = paste_roi_image(source_img_plan, roi_mask)

    """
    newSize = image_source.GetSize()
    newOrigin = image_source.GetOrigin()

    newSpacing = image_roi.GetSpacing()
    newDirection = image_roi.GetDirection()

    if image_source.GetSpacing() != image_roi.GetSpacing():
        print('the spacing of the source and derived mask differ')
    # re-cast the pixel type of the roi mask
    pixelID = image_source.GetPixelID()
    caster = sitk.CastImageFilter()
    caster.SetOutputPixelType(pixelID)
    image_roi = caster.Execute(image_roi)

    # black 3D image
    outputImage = sitk.Image(newSize, image_source.GetPixelIDValue())
    outputImage.SetOrigin(newOrigin)
    outputImage.SetSpacing(newSpacing)
    outputImage.SetDirection(newDirection)
    # transform from physical point to index the origin of the ROI image
    # img_center = np.array(img.TransformContinuousIndexToPhysicalPoint(np.array(img.GetSize()) / 2.0))
    destinationIndex = outputImage.TransformPhysicalPointToIndex(image_roi.GetOrigin())
    # paste the roi mask into the re-sized image
    pasted_img = sitk.Paste(outputImage, image_roi, image_roi.GetSize(), destinationIndex=destinationIndex)
    return pasted_img

0 个答案:

没有答案