在上一次分享中,我们在Dataset方法里,已经使用了transform函数,这节课对transform做一个详细的介绍。
上一次视频连接:MONAI中,一定要学会的三种Dataset
transform大致可以分为以下几个类别
想要什么样类别的变换,就在该类别下去找。
目录
普通变换和字典变换的联系与区别
1.数据准备
2. 加载NIfTI 格式的文件【 LoadImage/ LoadImaged】
3. 添加通道 [AddChanneld]
4. 强度变换 [NormalizeIntensityd / ScaleIntensityRanged]
5 空间变换 [Rotate90d / Resized]
普通变换和字典变换的联系与区别
- 普通变换又可以说是基于数组的变换:image和label是以数组形式给到Dataset。字典变换是基于字典的变换(image和label是一个字典对)。
- 普通变换和字典变换的功能是一样的,只是字典变换在每个transform后面都加了一个"d", 也可以写成”D“。如LoadImage/LoadImaged, Resize/Resized
- 使用字典变换时,必须指明该变换是对image做,还是label做。如,LoadImaged(keys='image'),表明只加载image
接下来在实战中介绍部分常用的transform,其余的类似。以下演示基于字典变换。
1.数据准备
这次使用的数据是医学图像十项全能挑战赛里面的CT数据(Task09_Spleen)。数据来源:http://medicaldecathlon.com/
- 使用monai下载方法:
resource = "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task09_Spleen.tar"
md5 = "410d4a301da4e5b2f6f86ec3ddba524e"
compressed_file = os.path.join(root_dir, "Task09_Spleen.tar")
data_dir = os.path.join(root_dir, "Task09_Spleen")
if not os.path.exists(data_dir):
download_and_extract(resource, compressed_file, root_dir, md5)
# root_dir: 存储地址,如‘.data/’
使用该方法考验网络的速度,毕竟是国外的网址。
- 复制链接到迅雷下载,或浏览器直接下载
"https://msd-for-monai.s3-us-west-2.amazonaws.com/Task09_Spleen.tar"
数据是三维的CT腹部图像,任务是脾脏的分割。
下载好数据后,先把image和label的地址加载到data_dict中,便于后续做变换。
import glob
data_dir = '/Volumes/Backup Plus/data/Task09_Spleen/'
train_images = sorted(glob.glob(os.path.join(data_dir, "imagesTr", "*.nii.gz")))
train_labels = sorted(glob.glob(os.path.join(data_dir, "labelsTr", "*.nii.gz")))
data_dicts = [
{"image": image_name, "label": label_name}
for image_name, label_name in zip(train_images, train_labels)
]
train_data_dicts, val_data_dicts = data_dicts[:-9], data_dicts[-9:]
2. 加载NIfTI 格式的文件【 LoadImage/ LoadImaged】
MONAI的一个设计选择是,它不仅提供高级工作流组件,而且以最小的功能形式提供相对较低级别的api。
例如,LoadImage类是底层Nibabel映像加载器的简单可调用包装器。在使用一些必要的系统参数构造加载程序之后,使用NIfTI文件名调用加载程序实例将返回图像数据数组以及元数据,例如仿射信息和体素大小。简单说就是,如果是nii.gz格式的文件,调用LoadImage,它会自动调用Nibabel来打开数据。在python中,nii.gz一般都是通过Nibabel来打开的。
LoadImage/ LoadImaged在使用时会有细小的差别,通过举例来说明。
注意注意
如果这些变换不是在Compose里面进行组合使用,单独调用时都是需要先实例化的。
- LoadImage
loader = LoadImage(dtype=np.float32) # 实例化
image, metadata = loader(train_data_dicts[0]["image"]) # 把image的地址传给loader
print(f"input: {train_data_dicts[0]['image']}")
print(f"image shape: {image.shape}")
print(f"image affine:\n{metadata['affine']}")
print(f"image pixdim:\n{metadata['pixdim']}")
如上图所示,LoadImage是会加载图像的值和元数据的。元数据里面包含分辨率,放射值,具体包含什么可以通过获取键来查询(metadata.keys())
其中,最后一个”filename_or_obj“也是一个重要的信息,它是image的地址。
在实际应用中,我们不需要metadata的话,可以这样加载
loader = LoadImage(image_only=True, dtype=np.float32) # 表示只需要图像值
image = loader(train_data_dicts[0]["image"])
- LoadImaged
loader = LoadImaged(keys=("image", "label"))
data_dict = loader(train_data_dicts[0])
print(f"input:, {train_data_dicts[0]}")
print(f"image shape: {data_dict['image'].shape}")
print(f"label shape: {data_dict['label'].shape}")
print(f"image pixdim:\n{data_dict['image_meta_dict']['pixdim']}")
这里的参数”keys“是你在data_dicts中设置的keys。表示要对image做变换还是label做变换。如果都做,就都写。比如keys=["image", "label"], keys = 'image', keys = 'label'
我们可以通过可视化方法,查看加载进来的数据。使用LoadImage/LoadImaged加载进来的数据是numpy格式
image, label = data_dict["image"], data_dict["label"]
plt.figure("visualize", (8, 4))
plt.subplot(1, 2, 1)
plt.title("image")
plt.imshow(image[:, :, 30], cmap="gray")
plt.subplot(1, 2, 2)
plt.title("label")
plt.imshow(label[:, :, 30])
plt.show()
3. 添加通道 [AddChanneld]
使用monai的模型时,默认是通道优先的格式。要求数据尺寸为:[num_channels, spatial_dim_1, spatial_dim_2, ... ,spatial_dim_n]。如上,我们的数据尺寸为[
512, 512, 55], 需要在前面一维通道。变成[1,
512, 512, 55],使用AddChanneld就可以帮我们轻松搞定。
add_channel = AddChanneld(keys=["image", "label"])
datac_dict = add_channel(data_dict)
print(f"image shape: {datac_dict['image'].shape}")
改变换应该用在加载数据LoadImaged后面。
4. 强度变换 [NormalizeIntensityd / ScaleIntensityRanged]
这两种都是对图像值强度进行变换的,像CT和MRI的值都是从-1000—+3000多的不等,通常需要进行归一化。
- NormalizeIntensityd
查看源码发现,该函数使用的归一化方法是 img-subtrahend/divisor
参 数 介 绍
subtrahend:被减数, 可以自己指定,默认为整个图像的均值。
divisor: 除数, 可以自己指定,默认为整个图像的方差。
nonzero: 布尔值。等于True,表示只对图像的非0区域做归一化。
channel_wise: 布尔值。当不指定subtrahend和divisor,为True, 表示在每个通道上进行计算均值和方差,为False,则在整个图像上计算均值和方差
from monai.transforms import NormalizeIntensityd, ScaleIntensityRanged
norm = NormalizeIntensityd(keys='image', nonzero=False, dtype=np.float32)
datac_dict_norm = norm(datac_dict)
- ScaleIntensityRanged
ScaleIntensityRanged和NormalizeIntensityd不同之处在于, ScaleIntensityRanged可以指定把哪些范围值缩放到那个区间。
比如对脾脏的分割中,我们只在于脾脏的CT值范围(假设在-300-- +300之间),而骨头等高强度的信号(大于2000)我们不需要。如果直接将这个强度进行归一化,脾脏内部的值范围就很小。我们就可以直接把脾脏的CT值范围 [-300,+300] 进行归一化到 [0, 1], 而不在[-300,+300] 这中间的值都为0。事实上,很多论文都是这样做的。
参 数 介 绍
a_min:float,强度原始范围最小值。可以理解为需要被归一化的最小值,如我们这个例子中的-300(需要写成小数,-300.0)
a_max: float, 强度原始范围最大值。可以理解为需要被归一化的最大值,如我们这个例子中的300(需要写成小数,300.0)
b_min: float, 强度目标范围最小值。可以理解为归一化后的最小值,通常设置为0.0
b_max: float, 强度目标范围最大值。可以理解为归一化后的最大值,通常设置为1.0
clip: 布尔值。设置为True, 才会把[-300,+300]之外的值都设置为0.通常为True
scale = ScaleIntensityRanged(keys='image', a_min=-300.0, a_max=300.0, b_min=0.0, b_max=1.0, clip=True)
# 由于上面我们把datac_dict归一化了,因此,我们要重新制造一个datac_dict
data_dict = loader(train_data_dicts[2])
datac_dict = add_channel(data_dict)
data_scale = scale(datac_dict)
print('original max value', datac_dict['image'].max())
print('original min value', datac_dict['image'].min())
print('target max value', data_scale['image'].max())
print('target min value', data_scale['image'].min())
5 空间变换 [Rotate90d / Resized]
- Rotate90d:输入数组在由space_axes指定的平面中旋转90度
参 数 介 绍
prob: float, 旋转的概率,默认为0.1, 也就是10%的概率被旋转
max_k: int, 旋转90度的次数,默认为3
spatial_axes: 元祖(int,int)。指定围绕哪个面旋转,默认为(0,1),即按前两个轴旋转。
from monai.transforms import RandRotate90d, Resized
rotate = RandRotate90d(keys=['image', 'label'], prob=0.5, max_k=1, spatial_axes=(2,1))
data_rotate = rotate(data_scale)
print(data_rotate['image'].shape)
print(data_rotate['label'].shape)
注意注意
使用旋转,尤其是随机旋转的时候一定要注意,如果是分割,image和label应当一同旋转,并且旋转方式要一模一样。
- Resized
参 数 介 绍
spatial_size: 序列[int, int,], 期大小调整操作之后的空间尺寸的形状
mode: resize使用的插值方式,默认为”area, “可以选择"nearest"
, "linear"
, "bilinear"
, "bicubic"
, "trilinear"
resize = Resized(keys=['image', 'label'], spatial_size=(256,256,256))
data_resize = resize(data_rotate)
print(data_resize['image'].shape)
# (1, 256, 256, 256)
由于篇幅过长,剩下的下一篇进行分享。
标签:MONAI,变换,image,Transform,label,dict,print,用法,data From: https://blog.51cto.com/u_16159492/6481601