Minha principal direção de pesquisa é o registro de imagens médicas, e os dados precisam ser pré-processados antes de usar o conjunto de dados de TC.
Etapas gerais de pré-processamento: (* significa que este código contém)
1. Recorte a área ROI .
Objetivo: Reduzir o tamanho da imagem, reduzir o consumo de memória, reduzir informações irrelevantes e melhorar a precisão experimental
2. Reamostragem .
Geralmente, será reamostrada para isotropia, por exemplo, reamostragem da imagem para representar o volume real de 1 1
1 mm por voxel 3. CT para HU , inclinação, interceptação. [Imagens CT são proprietárias, este código de artigo não escreve isso ]
*4 .Interceptar a área ROI em tons de cinza .
Quando a imagem processada é uma imagem pulmonar, também é chamada de interceptação da janela pulmonar, ou seja, a faixa de cinza do pulmão, janela pulmonar comum [largura da janela: 900, nível da janela: -550], janela ampla do pulmão [largura da janela : 1600, janela Bit: -600]
* 5. Finalidade da normalização
: Para evitar explosão de gradiente
6. Redimensionar o tamanho da imagem .
É determinado pelo algoritmo (alguns algoritmos exigem que o tamanho da imagem de entrada seja uniforme e alguns algoritmos precisam garantir a imagem original) ou desempenho do computador (a memória é pequena e a imagem só pode ser reduzida ~)
direto no código
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)