首页 > 其他分享 >MONAI(3)—一文看懂各种Transform用法(上)

MONAI(3)—一文看懂各种Transform用法(上)

时间:2023-06-14 21:32:13浏览次数:57  
标签:MONAI 变换 image Transform label dict print 用法 data


在上一次分享中,我们在Dataset方法里,已经使用了transform函数,这节课对transform做一个详细的介绍。

上一次视频连接:MONAI中,一定要学会的三种Dataset

transform大致可以分为以下几个类别

MONAI(3)—一文看懂各种Transform用法(上)_加载

想要什么样类别的变换,就在该类别下去找。

目录

普通变换和字典变换的联系与区别

1.数据准备

2. 加载NIfTI 格式的文件【 LoadImage/ LoadImaged】

 3. 添加通道 [AddChanneld]

4. 强度变换 [NormalizeIntensityd / ScaleIntensityRanged]

5 空间变换 [Rotate90d / Resized]


普通变换和字典变换的联系与区别

  1. 普通变换又可以说是基于数组的变换:image和label是以数组形式给到Dataset。字典变换是基于字典的变换(image和label是一个字典对)。
  2. 普通变换和字典变换的功能是一样的,只是字典变换在每个transform后面都加了一个"d", 也可以写成”D“。如LoadImage/LoadImaged, Resize/Resized
  3. 使用字典变换时,必须指明该变换是对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腹部图像,任务是脾脏的分割。

MONAI(3)—一文看懂各种Transform用法(上)_加载_02

下载好数据后,先把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:]

 

MONAI(3)—一文看懂各种Transform用法(上)_加载_03

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']}")

MONAI(3)—一文看懂各种Transform用法(上)_归一化_04

如上图所示,LoadImage是会加载图像的值和元数据的。元数据里面包含分辨率,放射值,具体包含什么可以通过获取键来查询(metadata.keys())

MONAI(3)—一文看懂各种Transform用法(上)_归一化_05

其中,最后一个”filename_or_obj“也是一个重要的信息,它是image的地址。

MONAI(3)—一文看懂各种Transform用法(上)_加载_06

在实际应用中,我们不需要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()

 

MONAI(3)—一文看懂各种Transform用法(上)_归一化_07

 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}")

MONAI(3)—一文看懂各种Transform用法(上)_归一化_08

改变换应该用在加载数据LoadImaged后面。

4. 强度变换 [NormalizeIntensityd / ScaleIntensityRanged]

这两种都是对图像值强度进行变换的,像CT和MRI的值都是从-1000—+3000多的不等,通常需要进行归一化。

MONAI(3)—一文看懂各种Transform用法(上)_数据_09

  • NormalizeIntensityd

MONAI(3)—一文看懂各种Transform用法(上)_归一化_10

查看源码发现,该函数使用的归一化方法是  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)

MONAI(3)—一文看懂各种Transform用法(上)_归一化_11

  • ScaleIntensityRanged

MONAI(3)—一文看懂各种Transform用法(上)_加载_12

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())

MONAI(3)—一文看懂各种Transform用法(上)_归一化_13

5 空间变换 [Rotate90d / Resized]

  • Rotate90d:输入数组在由space_axes指定的平面中旋转90度

MONAI(3)—一文看懂各种Transform用法(上)_数据_14

参 数 介 绍

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)

MONAI(3)—一文看懂各种Transform用法(上)_归一化_15

注意注意

使用旋转,尤其是随机旋转的时候一定要注意,如果是分割,image和label应当一同旋转,并且旋转方式要一模一样。

  • Resized

MONAI(3)—一文看懂各种Transform用法(上)_加载_16

参 数 介 绍

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(3)—一文看懂各种Transform用法(上)_加载_17

 

 

标签:MONAI,变换,image,Transform,label,dict,print,用法,data
From: https://blog.51cto.com/u_16159492/6481601

相关文章

  • MONAI中,一定要学会的三种Dataset使用方法
    在正式学习MONAI功能函数前,以下的网址必须要收藏。1.MONAIAPI: https://docs.monai.io/en/latest/index.html作用:查询功能函数的用法,主要分为以下几类2.MONAIGitHub项目地址: https://github.com/Project-MONAI   作用:如果上述API介绍的不够完整,可以去项目里面找一些例子......
  • Runtime.getRuntime().exec("ipconfig") 的用法
    ​ `Runtime.getRuntime().exec()`是Java中的一个方法,可以在Java程序中执行外部程序。这个方法返回一个`Process`对象,可以用于控制和查看执行的外部程序。`exec()`方法有多个重载版本,可以传递不同的参数来控制执行的外部程序。例如:importjava.io.BufferedReader;import......
  • Runtime.getRuntime().exec("ipconfig") 的用法
    ​ `Runtime.getRuntime().exec()`是Java中的一个方法,可以在Java程序中执行外部程序。这个方法返回一个`Process`对象,可以用于控制和查看执行的外部程序。`exec()`方法有多个重载版本,可以传递不同的参数来控制执行的外部程序。例如:importjava.io.BufferedReader;import......
  • vue watch deep 用法
    简单案例<template><div><h1>watchdeep</h1><p>obj:{{obj}}</p><p>调用watch次数:{{times}}</p><button@click="chgObj">改变对象</button></div></t......
  • 使用NSTimer和CGAffineTransformMakeRotation实现旋转动画
     使用NSTimer和CGAffineTransformMakeRotation实现旋转动画 首先定义需要用到的变量   floatangle;   NSTimer*timer; #pragmamark------------------->旋转图片<--------------------(void)_doRotateImage{//演员初始化UIImageView*ivImage=[[UII......
  • javascript:eval()的用法
    eval()是JavaScript中的一个全局函数,它可以计算或执行参数。如果参数是表达式,则eval()计算表达式;如果参数是一个或多个JavaScript语句,则eval()执行这些语句。以下是一些常见的用法:-计算表达式的值:`varresult=eval("3+5");`-执行JavaScript代码字符串:`varcode......
  • Transformer原论文
    相关工作文献阅读与总结AttentionIsAllYouNeed知乎笔记摘要dominantsequencetransduction显性序列转导模型传统的:基于包括编码器和解码器的复杂递归/CNN卷积神经网络文章提出:Transformer模型,完全基于注意力机制,放弃了循环和卷积介绍最先进的序列模型和转导问题(......
  • 最好的Transformer讲解:The Illustrated Transformer + The Annotated Transformer
    TheIllustratedTransformerhttps://jalammar.github.io/illustrated-transformer/TheAnnotatedTransformerhttp://nlp.seas.harvard.edu/annotated-transformer/......
  • TStringList的用法
    TStringList的用法TStrings是一个抽象类,在实际开发中,是除了基本类型外,应用得最多的。常规的用法大家都知道,现在来讨论它的一些高级的用法。先把要讨论的几个属性列出来:1、CommaText2、Delimiter&DelimitedText3、Names&Values&ValueFromIndex先看第一个:CommaText。怎......
  • 田渊栋新作:打开1层Transformer黑盒,注意力机制没那么神秘
    前言 AI理论再进一步,破解ChatGPT指日可待?本文转载自新智元仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV各大方向专栏与各个部署框架最全教程整理【CV技术指南】CV全栈指导班、基础入门......