1.monai——Dataset加载数据

monai使用

一、Dataset 数据加载

MONAI提供了非常多的Dataset, 包括Dataset⭐️, IterableDataset, PersistentDataset, CacheDataset⭐️, SmartCacheDataset, ZipDataset, ArrayDataset⭐️, ImageDataset,在官网Data — MONAI 0 Documentation,data栏中查看文档

在这里插入图片描述

1.1 monai.data.Dataset

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XAz8z9Dp-1667220467771)(C:\Users\qiaoqiang\AppData\Roaming\marktext\images\2022-10-31-18-55-10-image.png)]

参数简介

data: 将image 和 label 的地址或值存为字典。label可以是分割的图像,也可以是分类的值(如0, 1, 2…)

transform: 根据分类或分割任务来定义

from glob import glob  # 注意这是两个glob
import monai
from monai.data import Dataset

imglist = sorted(glob('/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/imagesTr/*.gz'))[:11]
labellist = sorted(glob('/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/labelsTr/*.gz'))[:11]

data_dict = [{'image': image, 'label': label} for image, label in zip(imglist, labellist)]

print(data_dict)

输出:

[{'image': '/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/imagesTr/002chenxiaojiao_t2_0000.nii.gz',

'label': '/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/labelsTr/002chenxiaojiao_t2.nii.gz'},

{'image': '/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/imagesTr/002liushide_t2_0000.nii.gz',

'label': '/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/labelsTr/002liushide_t2.nii.gz'},

……]

得到的是字典类型,一个字典项中对应图片与标签的地址,这就把image和label搞好了,接下来就是把配对好的数据组装成dataset和dataloder即可。

如果我想添加一个通道维,并且放到首位应该怎么处理?

from monai.transforms import LoadImaged,EnsureChannelFirstd

# 先创建一个加载nii数据的loader
loader = LoadImaged(keys=("image", "label"))
# 读取刚构建字典中的第一个数据
data_dict = loader(data_dict[0])
#打印信息
print(f"image shape: {data_dict['image'].shape}")
print(f"label shape: {data_dict['label'].shape}")
#添加维度
add_channel_first = EnsureChannelFirstd(keys=("image", "label"))
data_dict_addc = add_channel_first(data_dict)
#输出添加通道维后的结果
print(f"AddChanneld after image shape: {data_dict_addc['image'].shape}")

我的数据集是3D数据,所以,resize需要三个数,monai在Version0.8后,AddChannel被弃用,之后使用EnsureChannelFirst同时添加一个通道维度,并且将通道维置于首位

输出:

image shape: (640, 640, 24) label shape: (640, 640, 24) AddChanneld after image shape: (1, 640, 640, 24)

其中参数keys就是设置 作用的对象,你可以仅仅对图像做处理,也可以对图像和标签都做处理,后续介绍见transform处

如果我想对数据Resize怎么做呢?同理:

from monai.transforms import  Resized
#创建一个resize的方法
resize = Resized(keys=['image', 'label'], spatial_size=(640, 640, 32))
# 对刚才添加维度后的数据进行resize操作
data_dict_resize = resize(data_dict_addc)
print(f"image shape: {data_dict_resize['image'].shape}")
print(f"label shape: {data_dict_resize['label'].shape}")

输出:

image shape: (1, 640, 640, 32) label shape: (1, 640, 640, 32)

如果有很多处理分开写太过麻烦,所以用到了transform,可以整合到一起,方便看和使用,此处简单演示,下一篇详细介绍

from monai.transforms import Compose, LoadImaged,EnsureChannelFirstd,ToMetaTensord, ToTensord, Resized
transform = Compose(
    [
        LoadImaged(keys=['image','label']), # 做分类,这里image需要加载
        # AddChanneld(keys=['image','label']),   # 给加载进来的图像加个通道,只针对图像。
        EnsureChannelFirstd(keys=['image','label']),
        Resized(keys=['image','label'], spatial_size=(640,640,32)),
        ToMetaTensord(keys=['image','label']),   # 把图像转成tensor格式
    ]

train_ds = Dataset(data=data_dict[:7], transform=transform)
val_ds = Dataset(data=data_dict[-4:], transform=transform)
train_loader = monai.data.DataLoader(
    train_ds, batch_size=4, shuffle=True, num_workers=2)
test_loader = monai.data.DataLoader(
    val_ds, batch_size=2, shuffle=True, num_workers=2
)

对处理后的数据进行展示

import matplotlib.pyplot as plt

img = train_ds[0]["image"]
labels = train_ds[0]["label"]

img_shape = img.shape
label_shape = labels.shape
print(f"image shape: {img_shape}, label shape: {label_shape}")
plt.figure("image", (18, 6))
plt.subplot(1, 2, 1)
plt.title("image")
plt.imshow(img[0, :, :, 20].detach().cpu(), cmap="gray")
plt.subplot(1, 2, 2)
plt.title("label")
plt.imshow(labels[0, :, :,20].detach().cpu())
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQRKANnE-1667220467771)(C:\Users\qiaoqiang\AppData\Roaming\marktext\images\2022-10-31-19-46-11-image.png)]

1.2 monai.data.CacheDataset

具有缓存机制的数据集,即在第一个epoch前,把所有训练的数据都加载进缓存,这样可以加快网络提取数据的速度。第一个是,它缓存的是确定性变换(或者说是非随机变换)后的数据,而不是原始数据。比如在transform中,使用的变换,其中,LoadImaged, EnsureChannelFirstd, Spacingd, Orientationd,SacleIntensityRanged都是确定性变化,于是缓存的是经过这些变换之后的数据。而剩余的变换包括(RandCropByPosNegLabels, ToTensord)是每次迭代的时候进行的。
在这里插入图片描述

总 结

CacheDataset缓存确定性变换后的结果,其余的随机变换在每次迭代的时候进行。根据这个规则,我们应该把尽可能多的确定性变换放在随机变换之前。而一般来讲,ToTensord()是确定性的,但通常应该是最后一个变换。

在使用带缓存机制的dataset的时候,会在训练前处理数据,因此加载数据的过程会很慢,但是训练的时候会比普通的快。一般也是用CacheDataset比较多。
参数介绍

data和transform同前面的Dataset.

cache_num: 要缓存的项目数(int)。默认值为sys.maxsize

cache_rate: 缓存数据占总数的百分比。默认为1(即全部缓存)

num_workers: 要使用的工作进程数目

操作步骤和上面Dataset一样,他会预先加载数据到缓存
在这里插入图片描述

1.3 monai.data.ArrayDataset

前面两种Dataset都是基于字典的,如果你不喜欢用字典模式,就可以考虑使用这个数组模式。

普通变换和字典变换的联系与区别
普通变换又可以说是基于数组的变换:image和label是以数组形式给到Dataset。字典变换是基于字典的变换(image和label是一个字典对)。
普通变换和字典变换的功能是一样的,只是字典变换在每个transform后面都加了一个"d", 也可以写成”D“。如LoadImage/LoadImaged, Resize/Resized, 使用字典变换时,必须指明该变换是对image做,还是label做。如,LoadImaged(keys=‘image’),表明只加载image
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fKTjEsVj-1667220467773)(C:\Users\qiaoqiang\AppData\Roaming\marktext\images\2022-10-31-20-15-13-image.png)]

不管是分类还是分割,都可以使用该数组模式。在该模式下,可以确保为图像,分割,分割标签和分类标签用相同的随机数种子。

注意注意

使用ArrayDataset加载数据时,必须设定image_only=True,只加载图像值而不加载元数据(下一节transform中数据load中会说明什么是元数据,有哪些类型),否则会报错。

参数介绍

img: 图像地址

img_transform: 用于图像的transform

seg: 如果是分割,为图像地址,如果是分类,不填即可

seg_transform: 如果是分割,填写对mask的变换

label: 如果是分类, 为数组

label_transform: 如果是分类,对label做的变换,一般不做变换

from glob import glob  # 注意这是两个glob
import monai
from monai.data import ArrayDataset
from monai.transforms import Compose, LoadImage,EnsureChannelFirst, ToTensor, Resize

imglist = sorted(glob('/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/imagesTr/*.gz'))[:11]
labellist = sorted(glob('/home/qiaoqiang/nnUNetFrame/DATASET/nnUNet_raw/nnUNet_raw_data/Task012_Naoxue_t2/labelsTr/*.gz'))[:11]



由于加载的是数组,这里所有的transform都不加‘d’。

transform = Compose(
    [
        LoadImage(image_only=True), # # 使用数组加载数据时,必须设定image_only=True,只加载图像值而不加载元数
        EnsureChannelFirst(),
        Resize(spatial_size=(640,640,32)),
        ToTensor(),
    ]
)
train_ds = ArrayDataset(img=imglist, img_transform=transform,seg=labellist,seg_transform=transform)
train_loader = monai.data.DataLoader(
    train_ds, batch_size=4, shuffle=True, num_workers=2)

for image, label in train_loader:
    print(image.shape)
    print(label.shape)
    break

输出结果:

在这里插入图片描述

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,首先需要导入monai和numpy库: ```python import monai import numpy as np ``` 接着,我们需要定义一个函数,使用`monai.transform`中的`Rotate`来实现旋转操作。该函数需要输入标签数据和旋转角度,输出旋转后的标签数据。 ```python def rotate_label(label, angle): # 将标签数据转换为numpy数组 label_np = label.numpy() # 找到标签为1的位置 indices = np.where(label_np == 1) # 如果没有标签为1的位置,则返回原始标签数据 if len(indices[0]) == 0: return label # 计算旋转中心点 center = [np.mean(indices[0]), np.mean(indices[1]), np.mean(indices[2])] # 定义旋转操作 rotate = monai.transforms.Rotate(angle=angle, center=center, reshape=False) # 执行旋转操作 label_np_rot = rotate(label_np) # 将旋转后的标签数据转换为tensor并返回 return torch.from_numpy(label_np_rot) ``` 在这个函数中,我们首先将标签数据转换为numpy数组,然后找到标签为1的位置,并计算旋转中心点。接着,我们使用`monai.transforms.Rotate`定义旋转操作,其中`angle`表示旋转角度,`center`表示旋转中心点,`reshape=False`表示不改变数组形状。最后,我们执行旋转操作得到旋转后的标签数据,并将其转换为tensor返回。 接下来,我们可以使用定义好的函数来对标签为1的数据做旋转,例如: ```python # 假设label是一个形状为(1, 1, 64, 64, 64)的tensor,其中1表示batch size label_rotated = rotate_label(label[0, 0], angle=30) ``` 这样,我们就可以得到旋转30度后的标签数据了。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值