Medical nii image preprocessing - image cropping and resampling gray area normalization to modify image size

My main research direction is medical image registration, and the data needs to be preprocessed before using the CT data set.
General preprocessing steps: (* means this code has it)
1. Cut out the ROI area .
Purpose: Reduce image size, reduce memory consumption, reduce irrelevant information, and improve experimental accuracy
2. Resampling .
Generally, it will be resampled to isotropy, for example, resampling the image to represent the
actual volume of 1 1
1 mm per voxel 3. CT to HU , slope, intercept. [CT images are proprietary, the code in this article does not write this]
*4 . Intercept the ROI gray area .
When the processed image is a lung image, it is also called intercepting the lung window, that is, the gray range of the lung, common lung window [window width: 900, window level: -550], wide lung window [window width: 1600, window Bit: -600]
*5. Normalization
Purpose: To prevent gradient explosion
6. Resize image size .
It is determined by the algorithm (some algorithms require the input image size to be uniform, and some algorithms need to ensure the original image) or computer performance (the video memory is small, and the image can only be reduced~)

directly on the code

import nibabel as nib
import numpy as np
import torch

def get_gray_range(image):
    '获取图像信息:最大最小值'
    image = np.reshape(image, (1, image.shape[0] * image.shape[1] * image.shape[2]))
    image_gray_list = image.tolist()[0]
    image_gray_list.sort()  # 默认升序排列灰度值
    point_num = len(image_gray_list)
    min_num = round(point_num * 0.05)
    max_num = round(point_num * 0.95)
    min_gray_value = image_gray_list[min_num]
    max_gray_value = image_gray_list[max_num - 1]

    return min_gray_value, max_gray_value

def resampling_isotropic(data,voxel,spacing=[1, 1, 1]):
    """
    利用上下采样原理,恢复到各向同性
    :param data: 输入图像
    :param spacing: 各向同行或异性,默认为1*1*1
    :return:
    """
    # 获取图像信息:长宽高;
    height, weight, deepth = data.shape

    # 输出图的size
    size_X = int(height*voxel[0]/spacing[0])
    size_Y = int(weight*voxel[1]/spacing[1])
    size_Z = int(deepth*voxel[2]/spacing[2])

    # ndarray转torch;增加两维,目的是可以在"上下采样"中使用三线性插值法
    data = torch.tensor(data).unsqueeze(0).unsqueeze(0)

    # torch转.float32
    data = data.to(torch.float32)

    """
    函数说明:
    torch.nn.functional.interpolate(input, size=None, scale_factor=None, mode='nearest', align_corners=None, recompute_scale_factor=None)
    
    input (Tensor) – the input tensor
    size (int or Tuple[int] or Tuple[int, int] or Tuple[int, int, int]) – output spatial size.
    scale_factor (float or Tuple[float]) – 空间大小的乘数。如果scale_factor是一个tuple,它的长度必须匹配input.dim()。
    mode (str) – algorithm used for upsampling: nearest(Default), linear (3D-only), bilinear, bicubic (4D-only), trilinear (5D-only), area
    align_corners (bool, optional):几何上,我们把输入和输出的像素看作正方形而不是点。默认值:False
    如果设置为True,输入和输出张量将由它们的角像素中心点对齐,并保留角像素的值。
    如果设置为False,则输入和输出张量由它们的角像素的角点对齐,并且插值对边界外值使用边值填充,使得在scale_factor保持不变的情况下,该操作与输入大小无关。
    这只在mode is 'linear', 'bilinear', 'bicubic' or 'trilinear'时才有效果。
    recompute_scale_factor (bool, optional) :重新计算用于插值计算的scale_factor。
    如果为True,则必须传入scale_factor,并使用scale_factor计算输出大小。计算出的输出大小将用于为插值推断新的尺度。
    注意,当scale_factor是浮点数时,由于舍入和精度问题,它可能与重新计算的scale_factor不同。
    如果为False,那么size或scale_factor将直接用于插值。
    """
    data = torch.nn.functional.interpolate(data,
                                            size=[size_X,size_Y,size_Z],
                                            mode='trilinear',
                                            align_corners=False)
    return data[0, 0, :, :, :]

if __name__ == "__main__":
    """
    预处理主程序
    需修改的参数:
    img_name:图片名称
    old_path:数据集旧路径
    new_path:数据集新路径
    """

    img_path = old_path + img_name
    # 打开nii格式
    img = nib.load(img_path)
    img_data = np.asarray(img.dataobj)

    # 把仿射矩阵和头文件都存下来
    img_affine = img.affine
    img_hdr = img.header

    # 获取图像信息:最大最小值
    img_min, img_max = get_gray_range(img_data)

    # 获取图像信息:长宽高;
    height, weight, deepth = img_data.shape

    # 获取图像信息:voxel spacing,
	voxel = [img_hdr['pixdim'][1]]
    voxel.append(img_hdr['pixdim'][2])
    voxel.append(img_hdr['pixdim'][3])

    # 1.裁剪出感兴趣ROI区域,自定义裁剪范围数据
    ROIrange_XYZ=[40,440,20,240,10,130] 
    xmin=ROIrange_XYZ[0]
    xmax=ROIrange_XYZ[1]
    ymin=ROIrange_XYZ[2]
    ymax=ROIrange_XYZ[3]
    zmin=ROIrange_XYZ[4]
    zmax=ROIrange_XYZ[5]
    img_crop = img_data[xmin:xmax,ymin:ymax,zmin:zmax]

    # 2.重采样。可以恢复到各向同性[1,1,1]
    resample_spacing=[1,1,1]
    img_isotropic = resampling_isotropic(data=img_crop,voxel=voxel,spacing=resample_spacing)

    # 修改NIfTI affines
    scaling_affine = np.diag([resample_spacing[0], resample_spacing[1], resample_spacing[2], 1])

    # 3.裁剪出肺窗
    TonalRange=[-1000,-100]
    clamp_min = TonalRange[0] if img_min <= TonalRange[0] else img_min
    clamp_max = TonalRange[1] if img_max >= TonalRange[1] else img_max
    intrange = clamp_max - clamp_min
    img_clamp = torch.clamp(img_isotropic,min=clamp_min,max=clamp_max)

    # 4 归一化处理,灰度范围-》[0,1]
    img_normalize = (img_clamp-clamp_min) / intrange

    # 5 保存为nii格式
    # NiBabel网址 https://nipy.org/nibabel/manual.html
    img_new = nib.Nifti1Image(img_normalize, scaling_affine, img_hdr)
    img_new_path = new_path + img_name
    nib.save(img_new, img_new_path)

Guess you like

Origin blog.csdn.net/qq_45384162/article/details/127924063