monai学习

这篇具有很好参考价值的文章主要介绍了monai学习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

MetaTensor

这是monai特有的类型
metaTensor=tensor+metaObj
也是原本的tensor,带上meta data,例如affine,spacing,direction,origin

#a: MetaTensor
a.meta['spatial_orgin_shape']
a.meta['affine']
a.meta['original_affine']
a.affine

两个MetaTensor相加,如果is_batch为False,则复制第一个的metadata

数据增强

一般都在monai.transforms底下
有两种版本,一种是增强一张图片,另一种是增强一组图片(例如图片+标签),这种结尾会带一个d
增强完了类型都会变成metatensor

结尾带d的需要传入dict,会返回dict

再训练的时候,可以考虑

data['image'].as_tensor()

来转为普通的tensor,减少计算开销

加载图片

class monai.transforms.LoadImaged(keys, reader=None, dtype=<class 'numpy.float32'>, 
meta_keys=None, meta_key_postfix='meta_dict', overwriting=False, image_only=False, 
ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', 
allow_missing_keys=False, *args, **kwargs)

例如

trans=LoadImaged(['flair', 't1', 't1ce', 't2', 'seg'], image_only=True, allow_missing_keys=True)
trans({'flair':'/mnt/data/xxx', 't1':'/mnt/data/xxxx'})

其中image_only表示只返回图片,False的话,会额外往返回的dict中添加key_meta_dict
allow_missing_keys表示有存在的key也不会报错
读取图片的reader,默认如下
(nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), (npz, npy -> NumpyReader), (dcm, DICOM series and others -> ITKReader).

类型转换

class monai.transforms.CastToTyped(keys, dtype=<class 'numpy.float32'>, 
allow_missing_keys=False)

例如

CastToTyped(keys=['seg'], dtype=torch.long, allow_missing_keys=True)

添加通道

class monai.transforms.EnsureChannelFirstd(keys, meta_keys=None, meta_key_postfix='meta_dict', strict_check=True, allow_missing_keys=False, channel_dim=None)

随机旋转

class monai.transforms.RandRotated(keys, range_x=0.0, range_y=0.0, range_z=0.0, prob=0.1, 
keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, 
align_corners=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)

例如

RandRotated(keys=['image', 'seg'], range_x=10, range_y=10, range_z=10, allow_missing_keys=True),  # (-10, 10)

需要注意的是,这个默认的只有float能旋转,而且返回是float,所以可以考虑跟一个转类型的

翻转

class monai.transforms.RandFlipd(keys, prob=0.1, spatial_axis=None, allow_missing_keys=False)

需要注意,这个翻转的话,是指定的轴一起翻转
所以可以考虑多写几个

RandFlipd(keys=["image", "seg"], prob=0.5, spatial_axis=0, allow_missing_keys=True),
RandFlipd(keys=["image", "seg"], prob=0.5, spatial_axis=1, allow_missing_keys=True),
RandFlipd(keys=["image", "seg"], prob=0.5, spatial_axis=2, allow_missing_keys=True)

pad

class monai.transforms.Padd(keys, padder, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False)

这个似乎必须带有通道维度,并且的pad形状要跟带通道后的形状一样

clip

这个确实没找到,但是找到了一种代替的方法

# clip(-325, 325)
transforms.ThresholdIntensityd(keys=['image'], threshold=-325, above=True, cval=-325),  # max(img, -325)
transforms.ThresholdIntensityd(keys=['image'], threshold=325, above=False, cval=325),  # min(img, 325)

z-score

class monai.transforms.NormalizeIntensityd(keys, subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)

例如

transforms.NormalizeIntensityd(keys=['image']),  # z-score

分位点

from monai.transforms import MapTransform

class Percentile(MapTransform):
    def __init__(
            self,
            keys: KeysCollection,
            lower_percentile: float = 0.,
            upper_percentile: float = 100.,
            allow_missing_keys: bool = False,
    ) -> None:
        """
        Args:
            keys: keys of the corresponding items to be transformed.
                See also: :py:class:`monai.transforms.compose.MapTransform`
            lower_percentile: lower percentile(0-100)
            upper_percentile: upper percentile(0-100)
            allow_missing_keys: don't raise exception if key is missing.

        """
        MapTransform.__init__(self, keys, allow_missing_keys)
        self.lower_percentile = lower_percentile / 100.
        self.upper_percentile = upper_percentile / 100.

    def __call__(self, data):
        # the first dim of data should be the channel(CHW[D])
        d = dict(data)
        for key in self.key_iterator(d):
            images = data[key]

            lower = torch.quantile(images, self.lower_percentile)
            upper = torch.quantile(images, self.upper_percentile)
            images = torch.clip(images, lower, upper)
            d[key] = images
        return d

完整的例子

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import numpy as np
import torch
from monai.config import KeysCollection
from monai.data import NibabelWriter
from monai.transforms import MapTransform, Compose, LoadImaged, CastToTyped, EnsureChannelFirstd, RandSpatialCropd, \
    RandRotated, RandScaleIntensityd, RandShiftIntensityd, RandFlipd, Pad, Padd

data = {
    'flair': '/mnt/data/datasets/BraTS_2018/MICCAI_BraTS_2018_Data_Training/HGG/Brats18_2013_2_1/Brats18_2013_2_1_flair.nii.gz',
    'seg': '/mnt/data/datasets/BraTS_2018/MICCAI_BraTS_2018_Data_Training/HGG/Brats18_2013_2_1/Brats18_2013_2_1_seg.nii.gz',
    't1': '/mnt/data/datasets/BraTS_2018/MICCAI_BraTS_2018_Data_Training/HGG/Brats18_2013_2_1/Brats18_2013_2_1_t1.nii.gz',
    't1ce': '/mnt/data/datasets/BraTS_2018/MICCAI_BraTS_2018_Data_Training/HGG/Brats18_2013_2_1/Brats18_2013_2_1_t1ce.nii.gz',
    't2': '/mnt/data/datasets/BraTS_2018/MICCAI_BraTS_2018_Data_Training/HGG/Brats18_2013_2_1/Brats18_2013_2_1_t2.nii.gz'
}



class StackImagesd(MapTransform):
    """
    stack images
    add the result in the dict with the key 'image'
    """

    def __call__(self, data):
        d = dict(data)
        result = []
        for key in self.key_iterator(d):
            result.append(d[key])
        d['image'] = torch.stack(result, dim=0)  # (H, W, D)->(4, H, W, D)
        return d


class PercentileAndZScored(MapTransform):
    def __init__(
            self,
            keys: KeysCollection,
            lower_percentile: float = 0.,
            upper_percentile: float = 100.,
            allow_missing_keys: bool = False,
    ) -> None:
        """
        Args:
            keys: keys of the corresponding items to be transformed.
                See also: :py:class:`monai.transforms.compose.MapTransform`
            lower_percentile: lower percentile(0-100)
            upper_percentile: upper percentile(0-100)
            allow_missing_keys: don't raise exception if key is missing.

        """
        MapTransform.__init__(self, keys, allow_missing_keys)
        self.lower_percentile = lower_percentile / 100.
        self.upper_percentile = upper_percentile / 100.

    def __call__(self, data):
        # the first dim of data should be the channel(CHW[D])
        d = dict(data)
        for key in self.key_iterator(d):
            images = data[key]
            C = images.size(0)
            mask = images.sum(0) > 0  # brain
            for k in range(C):
                x = images[k, ...]
                y = x[mask]
                lower = torch.quantile(y, self.lower_percentile)
                upper = torch.quantile(y, self.upper_percentile)

                x[mask & (x < lower)] = lower
                x[mask & (x > upper)] = upper

                # z-score across the brain
                y = x[mask]
                x -= y.mean()
                x /= y.std()

                images[k, ...] = x
            d[key] = images
        return d


if __name__ == '__main__':
    transform = Compose([
        LoadImaged(['flair', 't1', 't1ce', 't2', 'seg'], image_only=True, allow_missing_keys=True),

        CastToTyped(keys=['seg'], dtype=torch.long, allow_missing_keys=True),
        EnsureChannelFirstd(keys=['seg'], allow_missing_keys=True),

        StackImagesd(keys=['flair', 't1', 't1ce', 't2']),  # add ['image']
        PercentileAndZScored(keys=['image'], lower_percentile=0.2, upper_percentile=99.8),

        RandSpatialCropd(keys=['image', 'seg'], roi_size=(128, 128, 128), random_size=False, allow_missing_keys=True),
        RandRotated(keys=['image', 'seg'], range_x=10, range_y=10, range_z=10, allow_missing_keys=True),  # (-10, 10)
        CastToTyped(keys=['seg'], dtype=torch.long, allow_missing_keys=True),
        RandScaleIntensityd(keys=['image'], factors=0.1),  # (-0.1, 0.1), img * (1 + scale)
        RandShiftIntensityd(keys=['image'], offsets=0.1),  # (-0.1, 0.1), img + offset
        RandFlipd(keys=["image", "seg"], prob=0.5, spatial_axis=0, allow_missing_keys=True),
        RandFlipd(keys=["image", "seg"], prob=0.5, spatial_axis=1, allow_missing_keys=True),
        RandFlipd(keys=["image", "seg"], prob=0.5, spatial_axis=2, allow_missing_keys=True),
        CastToTyped(keys=['seg'], dtype=torch.long, allow_missing_keys=True),
        Padd(keys=["image", "seg"], padder=Pad([(0, 0), (0, 0), (0, 0), (0, 5)]), allow_missing_keys=True),
    ])
    result = transform(data)
    print(result['seg'].shape)

保存

nii

默认是会重采样的,重采样的依据是metadata的spatial_shape,affine,original_affine

writer = NibabelWriter(output_dtype=torch.uint8)
writer.set_data_array(mask.squeeze(0), channel_dim=0) # (C, H, W, D)
writer.set_metadata(mask.meta, resample=True, mode='nearest')
writer.write('faQ.nii.gz', verbose=True)

不重采样

下面这段,会把affine直接写入数据中,而忽略原来的affine

writer = NibabelWriter(output_dtype=torch.uint8)
writer.set_data_array(mask, channel_dim=0)# (C, H, W, D)
writer.set_metadata({
    'spatial_shape': result['image'].meta['spatial_shape'],
    'affine': result['image'].meta['original_affine'],
    'original_affine': result['image'].meta['original_affine']
}, resample=False, mode='nearest')

writer.write('faQ.nii.gz')

Loss

都在monai.losses里
常见有:dice,focal loss, GeneralizedDiceLoss

metric

需要注意的是,很多metric要求传进去one_hot形式

具体可以看下面的例子文章来源地址https://www.toymoban.com/news/detail-432122.html

import argparse
import os
from glob import glob

import torch
from monai import transforms
from monai.metrics import DiceMetric
from monai.networks import one_hot
from monai.utils import MetricReduction
from tqdm.contrib import tzip

metric = DiceMetric(include_background=False)
loader = transforms.LoadImage(image_only=True, dtype=torch.uint8)
predict_paths = get_data(args.predict_path)
gt_paths = get_data(args.gt_path)
CLASS_NUMBER = 5

for predict_path, gt_path in tzip(predict_paths, gt_paths):
    predict = loader(predict_path).unsqueeze(0)
    gt = loader(gt_path).unsqueeze(0)
    predict = one_hot(predict, CLASS_NUMBER, dtype=torch.uint8, dim=0).unsqueeze(0)
    gt = one_hot(gt, CLASS_NUMBER, dtype=torch.uint8, dim=0).unsqueeze(0)
    dice = metric(predict, gt) # (B, C) =(B,5-1) (include_background=False)

print('mean_dice: liver={:.2f}, kidney={:.2f}, spleen={:.2f}, pancreas={:.2f}'.format(
    *metric.aggregate(MetricReduction.MEAN_BATCH)))
print('mean_dice: {:.2f}'.format(metric.aggregate(MetricReduction.MEAN).item()))

到了这里,关于monai学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 259k+ Star!这是我见过最全的开发者技术学习路线!

    大家好,我是 Java陈序员 。 自从上班后,身体是一天不如一天了,也很少有时间可以去学习新技术了。程序员如果技术跟不上,很容易就被淘汰。 而碎片化的学习效率又不高,往往今天学了,明天就忘了。有时候更是不知道要学习什么技术! 今天给大家推荐一个 开发者技术

    2024年02月05日
    浏览(44)
  • 医学图像的 AI 框架 MONAI 详细教程(一)

    前言 安装步骤 基于 MONAI 的 MedMNIST 数据集分类任务 导入依赖 下载数据 读取图像信息 MONAI transforms 定义 Dataset、网络和优化器 训练 测试 总结 参考链接 最近在读 CVPR 2023 上和医学图像方向相关的论文,发现其中的 Label-Free Liver Tumor Segmentation 这篇论文使用了 MONAI 这个框架。之

    2024年02月11日
    浏览(35)
  • 【图像去噪研究】现有的主流图像去噪研究成果学习笔记

    简述 造成图像退化的关键原因: 大气干扰、光线干扰、噪声干扰、相机抖动、时延拍摄 。一副图像由不同的图像成分组成:平滑区域、边缘和细节。为了获得高质量去噪结果的图像,对同一特征图的不同性质像素点应该进行区别化的处理:对光滑的区域进行更强的约束,对

    2024年02月08日
    浏览(37)
  • MONAI 3D目标检测官方demo实践与理解(二)模型理解

    在上一节介绍了MONAI的3D目标检测案例,以及如何运行训练代码。 MONAI 3D目标检测官方demo实践与理解(一)项目搭建,训练部分的运行 本篇主要是对该项目的模型进行理解 该部分代码如下: 主要作用是根据图像每个像素点生成不同尺寸和大小的anchor 对于anchor的理解推荐阅读

    2024年02月20日
    浏览(37)
  • 使用 Monai 和 PyTorch 预处理 3D Volumes以进行肿瘤分割

    针对在使用传统图像处理工具时可能遇到的困难,深度学习已成为医疗保健领域的主要解决方案。 因为医学图像比标准图像更难处理(高对比度、人体的广泛变化……)深度学习用于分类、对象检测,尤其是分割任务。 在分割方面,深度学习用于分割人体器官,如肝脏、肺

    2023年04月11日
    浏览(60)
  • 这是关于“树先生“的故事

    树的定义:树是指由N(N=0)个有限结点组成的具有层次性关系的集合,是一种简单的非线性结构。当N=0时,称为空树。 前序遍历 中序遍历 后序遍历 对于前中后序遍历使用的是根节点的位置决定前中序。 层序遍历 对于层序来说就是一层一层的进行遍历,由上面一层的根节遍

    2024年02月05日
    浏览(52)
  • 这是什么代码帮我看看

    data=\\\'www.\\\' +\\\'mingrisoft\\\'+\\\'.com\\\' train1=\\\'www.\\\'+str(12306)+ \\\'.com\\\' train2=\\\'www.\\\'+\\\'12306\\\'+\\\'.com\\\' print(data ) print(train1) print(train2) name=input(\\\'姓名:\\\') phone=input(\\\'电话:\\\') university=input(\\\'学校:\\\') data=name,phone,university print(data ) print(\\\' \\\'.join(data) ) print(name,phone,university) print (\\\'mingrisoft\\\'\\\'.com\\\') print (\\\'mingrisoft\\\'   \\\'.c

    2024年02月16日
    浏览(28)
  • 【单元测试】测还是不测,这是一个问题

    这篇文章也可以在我的博客中查看 相信大家从小就被千叮万嘱要做单元测试。然后秉承这一信念,成为了一个测试狂魔。凡有代码,测!覆盖!最终,一波操作猛如虎:467测试,0错误, 0自信 。 第二天。 你为了优化,颤抖着手更改了一行代码。果不其然发现牵连了 1e9 个测

    2024年02月03日
    浏览(49)
  • 哎,这是个 Windows 的bug

    因为业务需要,需要验证 Windows 的更新系统在某个场景下是否正常工作。开始的时候一切如预期,结果后半段卡住了。不敢轻易怀疑是 Windows 的问题,毕竟 Windows 是服务亿万用户的,尤其是更新系统,用于 x86、x64、arm 等各个平台 Windows 系统的更新。只能从自己身上找原因:

    2024年02月05日
    浏览(33)
  • 解决:js 根据图片链接(image url)下载,有的打开预览,有的下载

    1、问题描述 https://*****/drugTestReport/20230515/202305151106111386737.png https://*****/drugTestReport/20230605/202306051540314553141.jpg 同样结构的两个图片链接,使用window.open(url),一个是打开预览,另一个是下载   2、解决方法,通过fetch请求url,获取blob类型,区分情况,统一成下载。  

    2024年02月09日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包