nnUNet 训练 AMOS22数据集 Task216(抽丝剥茧指令+原理篇)

news2024/11/28 8:36:33

环境准备篇 

  1. 安装hiddenlayer(用来生成什么网络拓扑图?管他呢,装吧) pip install --upgrade git+https://github.com/nanohanno/hiddenlayer.git@bugfix/get_trace_graph#egg=hiddenlayer

  2. 安装环境,由于服务器已经装好pytorch哈哈,现在在setup.py里面,把pytorch删掉!其他按照指令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e . (清华源快点哦

dataset_conversion篇

  1. 修改python Task216_Amos2022_task1.py,需要修改一下amos_base的路径,注意,这里是数据集的初始路径

  2. 其次,需要在root路径下面vim .bashrc并输入以下内容:

    export nnUNet_raw_data_base="/root/autodl-tmp/amos22_raw"
    export nnUNet_preprocessed="/root/autodl-tmp/amos22_pre"
    export RESULTS_FOLDER="/root/autodl-tmp/nnUNet_trained_models"

    注意看!第一行是格式化命名后的数据集,第二行是预处理后的数据集,第三行是训练的模型,这里都放在数据盘!

    然后 source .bashrc

  3. 运行python Task216_Amos2022_task1.py,

目的:重新格式化文件名,全都化成这样的形式:

nnUNet_plan_and_preprocess篇

运行指令:

  • nnUNet_plan_and_preprocess -t 216 -pl3d ExperimentPlanner3D_residual_v21_bfnnUNet_31 -pl2d None

输入指令后,参数将传给 nnUNet_plan_and_preprocess.py 文件,然后会调用到三个函数:

crop(task_name, False, tf) #用于crop出有器官的地方,去除边界部分

exp_planner.plan_experiment() #应该是用于调用模型

exp_planner.run_preprocessing(threads) #用于进行预处理

我们输入的指令是:nnUNet_plan_and_preprocess -t 216 -pl3d ExperimentPlanner3D_residual_v21_bfnnUNet_31 -pl2d None

说明我们采用的planner是ExperimentPlanner3D_residual_v21_bfnnUNet_31

继承关系是ExperimentPlanner->ExperimentPlanner3D_v21->ExperimentPlanner3D_residual_v21_bfnnUNet->ExperimentPlanner3D_residual_v21_bfnnUNet_31,

这个预处理看起来很复杂,不要慌,我们一个一个往前看:

ExperimentPlanner3D_residual_v21_bfnnUNet_31

初始化:鉴别器,预处理器:Preprocessor3DBetterResampling

def __init__(self, folder_with_cropped_data, preprocessed_output_folder):
        super().__init__(folder_with_cropped_data, preprocessed_output_folder)
        self.data_identifier = "nnUNetData_bfnnUNet_31"
        self.plans_fname = join(self.preprocessed_output_folder,
                                "nnUNetPlans_bfnnUNet_fabresnet_31_plans_3D.pkl")
        self.preprocessor_name = 'Preprocessor3DBetterResampling'

plan_experiments:继承了父类

run_preprocessing:

def run_preprocessing(self, num_threads):
        if os.path.isdir(join(self.preprocessed_output_folder, "gt_segmentations")):
            shutil.rmtree(join(self.preprocessed_output_folder, "gt_segmentations"))
        shutil.copytree(join(self.folder_with_cropped_data, "gt_segmentations"), join(self.preprocessed_output_folder,
                                                                                      "gt_segmentations"))
                            
        normalization_schemes = self.plans['normalization_schemes'] #设置模态,单/多模态
        use_nonzero_mask_for_normalization = self.plans['use_mask_for_norm'] #是否采取掩膜,是没必要的,因为task216 全是CT
        intensityproperties = self.plans['dataset_properties']['intensityproperties']
        preprocessor_class = recursive_find_python_class([join(nnunet.__path__[0], "preprocessing")],
                                                         self.preprocessor_name, current_module="nnunet.preprocessing")
        assert preprocessor_class is not None
        preprocessor = preprocessor_class(normalization_schemes, use_nonzero_mask_for_normalization,
                                          self.transpose_forward,
                                          intensityproperties) #创建预处理器
        target_spacings = [i["current_spacing"] for i in self.plans_per_stage.values()] #获取每阶段的spacing,stage0大概是[2,1.3,1.3],stage1大概是[1.5,1,1]
        if self.plans['num_stages'] > 1 and not isinstance(num_threads, (list, tuple)):
            num_threads = (default_num_threads, num_threads)
        elif self.plans['num_stages'] == 1 and isinstance(num_threads, (list, tuple)):
            num_threads = num_threads[-1]
        preprocessor.run(target_spacings, self.folder_with_cropped_data, self.preprocessed_output_folder,
                         self.plans['data_identifier'], num_threads, force_separate_z=False)# 进行预处理

数据格式转换

每一个病人的数据都会被转换成一个四维numpy数组和一个pickle文件。

四维数组array(CXYZ)中,第一个维度的最后array[-1..]存储的是分割标注结果。而第一个维度的前面存储不同模态的数据,如果仅单模态,则四维数组第一维度长度仅为2,分别表示影像数据以及标注数据。四维数组array的后三个维度代表x,;.z三个坐标表示的三维数据,对于原始影像数据,值大小代表强度,而对于标注结果,后三个维度的三维数据值分别为0,1,…..表示不同的标注类别。

Preprocessor3DBetterResampling

该类继承GenericPreprocessor,所以直接看他的run函数:

可以看到首先初始化了input和output的路径,然后加载了一些config文件,调用了p.starmap(self._run_internal, all_args) ,这个_run_internal才是预处理的关键。

  1. 先对data和金标准进行transpose
  2. 根据target_spacing重采样和归一化
  3. 将data,seg组成all_data
  4. 期望每个类sample 10000 locations或样本数的0.01
  5. 保存class_locations
  6. 保存all_data
def _run_internal(self, target_spacing, case_identifier, output_folder_stage, cropped_output_dir, force_separate_z,
                      all_classes):
        data, seg, properties = self.load_cropped(cropped_output_dir, case_identifier)

        data = data.transpose((0, *[i + 1 for i in self.transpose_forward]))
        seg = seg.transpose((0, *[i + 1 for i in self.transpose_forward]))
        # 首先进行transpose
        # 然后根据target_spacing进行重采样和归一化
        data, seg, properties = self.resample_and_normalize(data, target_spacing,
                                                            properties, seg, force_separate_z)
        all_data = np.vstack((data, seg)).astype(np.float32)

        # we need to find out where the classes are and sample some random locations
        # let's do 10.000 samples per class
        # seed this for reproducibility!
        num_samples = 10000
        min_percent_coverage = 0.01 # at least 1% of the class voxels need to be selected, otherwise it may be too sparse
        rndst = np.random.RandomState(1234)
        class_locs = {}
        for c in all_classes:
            all_locs = np.argwhere(all_data[-1] == c)
            if len(all_locs) == 0:
                class_locs[c] = []
                continue
            target_num_samples = min(num_samples, len(all_locs))
            target_num_samples = max(target_num_samples, int(np.ceil(len(all_locs) * min_percent_coverage)))

            selected = all_locs[rndst.choice(len(all_locs), target_num_samples, replace=False)]
            class_locs[c] = selected
            print(c, target_num_samples)
        properties['class_locations'] = class_locs

        print("saving: ", os.path.join(output_folder_stage, "%s.npz" % case_identifier))
        np.savez_compressed(os.path.join(output_folder_stage, "%s.npz" % case_identifier),
                            data=all_data.astype(np.float32))
        with open(os.path.join(output_folder_stage, "%s.pkl" % case_identifier), 'wb') as f:
            pickle.dump(properties, f)

归一化部分详解:resample and nomalized

回到Preprocessor3DBetterResampling,对于CT而言:调用numpy中的函数统计整个训练集的均值,标准差,0.5%分位HU值,99.5%分位HU值,并利用这些统计信息对每张图像进行clip以及z-scoring。代码中用use_nonzero_mask表示是否只在nonzero区域进行normalization.

  if scheme == "CT":
       # clip to lb and ub from train data foreground and use foreground mn and sd from training data
       assert self.intensityproperties is not None, "ERROR: if there is a CT then we need intensity properties"
       mean_intensity = self.intensityproperties[c]['mean']
       std_intensity = self.intensityproperties[c]['sd']
       lower_bound = self.intensityproperties[c]['percentile_00_5']
       upper_bound = self.intensityproperties[c]['percentile_99_5']
       data[c] = np.clip(data[c], lower_bound, upper_bound)
       data[c] = (data[c] - mean_intensity) / std_intensity
       if use_nonzero_mask[c]:
             data[c][seg[-1] < 0] = 0

重采样部分详解:get_target_spacing+resample_patient+resample_data_or_seg

第一步是确定重采样的目标空间大小。在之前数据格式转换的时候,每个数据的spacing信息存储在对应的pickle文件中,需要依次进行读取,然后一起存放在一个列表spacings当中。之后调用numpy中函数统计每个维度spacing的中值即可。
接下来根据中值spacing进行判断,数据集是否存在各向异性的问题。nnUNet设定的判断标准是,中值spacing中三个维度,是否有一个维度spacing大于另一个维度spacing的3倍,并且,该维度的中值size小于另一个维度中值size的1/3。如果存在各向异性,对spacing特别大的维度,取数据集中该维度spacing值的10%分位点作为该维度的目标空间大小。

get_target_spacing中:

anisotropy_threshold = 3
 
worst_spacing_axis = np.argmax(target)
other_axes = [i for i in range(len(target)) if i != worst_spacing_axis]
other_spacings = [target[i] for i in other_axes]
 
has_aniso_spacing = target[worst_spacing_axis] > (anisotropy_threshold * min(other_spacings))
has_aniso_voxels = target_size[worst_spacing_axis] * self.anisotropy_threshold < min(other_sizes)
if has_aniso_spacing and has_aniso_voxels:
    spacings_of_that_axis = np.vstack(spacings)[:, worst_spacing_axis]
    target_spacing_of_that_axis = np.percentile(spacings_of_that_axis, 10)
    # don't let the spacing of that axis get higher than the other axes
    if target_spacing_of_that_axis < min(other_spacings):
        target_spacing_of_that_axis = max(min(other_spacings), target_spacing_of_that_axis) + 1e-5
    target[worst_spacing_axis] = target_spacing_of_that_axis

第二步:resample_patient中,根据target_spacing重新确定目标尺寸。

new_shape = np.round(((np.array(original_spacing) / np.array(target_spacing)).astype(float) * shape)).astype(int)

第三步:调用resample_data_or_seg,skimage库中的reisze函数对每张图像进行resize即可,但在nnUNet中会根据图像是否存在各向异性进行不同的resize策略。如果不存在各向异性,对整个三维图像进行3阶spline插值即可。如果图像存在各向异性,设spacing大的维度为z轴,则仅在图像的xy平面进行3阶spline插值,而在z轴采用最近邻插值。而对于分割的标注图像,无论各向异性与否,在三个维度上都采用最近邻插值。下面代码中,用do_seperate_z表示是否存在各向异性,用axis表示各向异性图像中spacing最大的轴。

resample_data_or_seg中:

from skimage.transform import resize
if do_separate_z:
    if axis == 0:
        new_shape_2d = new_shape[1:]
    elif axis == 1:
        new_shape_2d = new_shape[[0, 2]]
    else:
        new_shape_2d = new_shape[:-1]
    reshaped_final_data = []
    for c in range(data.shape[0]):
        reshaped_data = []
        for slice_id in range(shape[axis]):
            if axis == 0:
                reshaped_data.append(resize(data[c, slice_id], new_shape_2d, order=3))
            elif axis == 1:
                reshaped_data.append(resize(data[c, :, slice_id], new_shape_2d, order=3))
            else:
                reshaped_data.append(resize(data[c, :, :, slice_id], new_shape_2d, order=3))
 
    if shape[axis] != new_shape[axis]:
        # The following few lines are blatantly copied and modified from sklearn's resize()
        rows, cols, dim = new_shape[0], new_shape[1], new_shape[2]
        orig_rows, orig_cols, orig_dim = reshaped_data.shape
 
        row_scale = float(orig_rows) / rows
        col_scale = float(orig_cols) / cols
        dim_scale = float(orig_dim) / dim
 
        map_rows, map_cols, map_dims = np.mgrid[:rows, :cols, :dim]
        map_rows = row_scale * (map_rows + 0.5) - 0.5
        map_cols = col_scale * (map_cols + 0.5) - 0.5
        map_dims = dim_scale * (map_dims + 0.5) - 0.5
 
        coord_map = np.array([map_rows, map_cols, map_dims])
        reshaped_final_data.append(map_coordinates(reshaped_data, coord_map, order=0, cval=cval,
                                                                   mode='nearest')[None])
else:
    reshaped = []
    for c in range(data.shape[0]):
        reshaped.append(resize(data[c], new_shape, order=3)[None])
    reshaped_final_data = np.vstack(reshaped)
 
reshaped_seg = resize(seg, new_shape, order=0)

实验结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全日益严峻下计算机主机加固的意义

​ 近年来&#xff0c;计算机以及互联网应用在中国得到普及和发展&#xff0c;已经深入到社会每个角落&#xff0c;政府&#xff0c;经济&#xff0c;军事&#xff0c;社会&#xff0c;文化和人们生活等各方面都越来越依赖于计算机和网络&#xff0c;电子政务&#xff0c;无纸办…

【计算机体系结构】指令集体系结构、微体系结构简介

1. “虚拟” to “现实” 首先可以看这张图片&#xff0c;下面的 Physics 所指的是我们的物理世界中看得见摸得到或者是客观存在的事物&#xff0c;而人类希望将自己的工作内容或者需求以某种方式映射到物理层面上&#xff0c;用物理变化带来的影响来完成人类工作内容。例如早期…

《Linux Shell脚本攻略》学习笔记-第二章

2.1 简介 本章将为你介绍一些最值得关注同时也是最实用的命令。 2.2 用cat进行拼接 cat命令能够显示或者拼接文件内容。cat能够将标准输入数据与文件数据组合在一起。 通常的做法是将stdin重定向到一个文件&#xff0c;然后再合并两个文件。而cat命令一次就能搞定这些操作。 1&…

【Linux】基础常见指令

目录​​​​​​​ 前言 一、Linux的环境搭建与远程控制 Linux 环境的搭建方式主要有三种 使用 XShell 远程登陆到 Linux 二、常见指令 1. ls 指令 2. pwd命令 3. cd 指令 4. touch指令 5. mkdir指令 6. rmdir指令 && rm 指令 7. man指令 8. cp指令 9. mv指令 10.…

【微信小程序】实用教程02-添加全局页面配置、页面、底部导航

开始前&#xff0c;请先完成项目创建&#xff0c;详见 【微信小程序】实用教程01-注册登录账号&#xff0c;获取 AppID、下载安装开发工具、创建项目、上传体验 前期准备 因我们的项目是根据模板创建的&#xff0c;需先清理掉无效的页面代码&#xff0c;具体操作方式如下&…

LeetCode 64. 最小路径和

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 64. 最小路径和&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetCode 64…

【Redis】使用阻塞队列+Redis优化秒杀业务

【Redis】使用阻塞队列Redis优化秒杀业务 文章目录【Redis】使用阻塞队列Redis优化秒杀业务1. 为什么要优化2. 怎么优化2.1 查询优惠卷2.2 判断秒杀库存2.3 校验一人一单2.4 减库存2.5 创建订单2.6 保证redis操作的原子性3. 确认优化方案4. 实现优化方案4.1 编写lua脚本4.2 定义…

jvm系列(3)--运行时数据区Runtime

目录运行时数据区概述及线程前言运行时数据区结构运行时数据区与内存线程的内存空间Runtime类线程JVM 线程JVM 系统线程程序计数器(PC寄存器)PC寄存器介绍PC寄存器的作用举例两个面试题CPU 时间片本地方法接口本地方法举例为什么要使用 Native Method&#xff1f;与Java环境外交…

Tomcat打破双亲委派模型

tomcat的类加载器结构tomcat的类加载&#xff08;loadClass&#xff09;过程和原本的双亲委派模型思路差不多&#xff0c;先看有没有加载过。先在本地 Cache 查找该类是否已经加载过&#xff0c;也就是说 Tomcat 的类加载器是否已经加载过这个类。如果 Tomcat 类加载器没有加载…

【学习笔记】【Pytorch】张量(Tensor)的基础操作

【学习笔记】【Pytorch】张量&#xff08;Tensor&#xff09;的基础操作一、创建张量1.使用数据创建张量2.无需数据的创建选项3.torch.Tensor与torch.tensor的区别4.PyTorch中张量的创建方法的选择二、张量的属性1.张量的 torch.dtype2.张量的 torch.device3.张量的 torch.layo…

阿维塔冲击年10万台订单,第二款车型Q2发布

1月13日&#xff0c;阿维塔科技在重庆总部召开渠道合作伙伴大会。今年&#xff0c;阿维塔计划推出&#xff1a; •阿维塔11后驱版本 •并发布第二款产品&#xff0c;代号E12&#xff0c;定位中大型轿车。阿维塔今年计划冲击10万辆订单目标。在当前CHN平台的基础上&#xff0c;阿…

Openresty记录笔记

最近由于项目需要学习了安全代理的相关知识&#xff0c;其实刚开始的时候是非常需要一个入门的介绍&#xff0c;大概说明下这个到底是个什么东西&#xff0c;能干啥&#xff0c;简单的原理是什么&#xff0c;为此我记录下我看完用完的心得&#xff0c;记录成笔记。 一般我们代码…

Redis 持久化详解

目录一、简介二、RDB持久化2.1、SAVE2.2、BGSAVE2.3、SAVE选项2.4、RDB文件结构2.5、RDB文件载入三、AOF持久化3.1、开启AOF功能3.2、配置AOF文件的冲洗频率3.3、AOF重写3.3.1、BGREWRITEAOF命令&#xff08;手动&#xff09;3.3.2、AOF重写配置选项&#xff08;自动&#xff0…

Android | Service

Android Service Service 概念 实现程序后台运行的解决方案&#xff0c;一种可在后台执行长时间运行操作而不提供界面的应用组件。Service 的运行不依赖于任何用户界面&#xff0c;即使程序被切换到后台&#xff0c;或者用户打开了另外一个应用程序&#xff0c;Service 仍然能…

Vue3——第十五章(计算属性:computed)

一、基础示例 模板中的表达式虽然方便&#xff0c;但也只能用来做简单的操作。如果在模板中写太多逻辑&#xff0c;会让模板变得臃肿&#xff0c;难以维护。推荐使用计算属性来描述依赖响应式状态的复杂逻辑。 在这里定义了一个计算属性 publishedBooksMessage。computed() 方…

【设计模式】创建型模式·原型模式

设计模式学习之旅(五) 查看更多可关注后查看主页设计模式DayToDay专栏 一. 概述 用一个已经创建的实例作为原型&#xff0c;通过复制(克隆)该原型对象来创建一个和原型对象相同的新对象。 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a;规定了具体原型对象必须实现…

Java基础(二)

1.标识符标识符&#xff1a;由数字、字符、下划线、$组成&#xff08;不能以数字、下划线开头&#xff09;java严格区分大小写2.命名规范包名&#xff1a;多单词组成时所有字母全部小写类名、接口名&#xff1a;多单词组成时&#xff0c;所有单词首字母大写变量名、方法名&…

屏幕录制工具哪个好用?分享3款相见恨晚的软件

在我们的日常生活中&#xff0c;我们经常使用截图和手机屏幕记录功能来记录一些重要的内容。然而&#xff0c;录制的图片清晰度很低&#xff0c;或者需要不断的截图&#xff0c;这很容易出错一些重要的内容&#xff0c;这个时候就需要进行录屏了。那么电脑上的屏幕录制工具哪个…

group by详解

group by功能 在SQL中group by主要用来进行分组统计&#xff0c;分组字段放在group by的后面&#xff1b;分组结果一般需要借助聚合函数实现。 group by语法结构 1、常用语法 语法结构 SELECT column_name1,column_name2, … 聚合函数1,聚合函数2 , … FROM table_name GROUP…

电脑删除了大文件怎么恢复?看看这四种方法

电脑能够帮助我们存储大量的文件&#xff0c;比如视频、文档、音频等&#xff0c;但是随着时间的流逝&#xff0c;有些文件所存在的意义也变得毫无价值了&#xff0c;这时候很多小伙伴都会选择删除操作&#xff0c;可是由于电脑磁盘内容过多&#xff0c;容易面临重要文件被误删…