医学图像处理——读取和解读NII文件

一 预备知识

NII文件的存储格式网上有很多资料,在此只做一点简单的描述。nii是一种文件格式,它存储的是在空间中占有一定体积的小方块的物理位置和该位置对应的像素值。这个小方块我们也称之为体素(voxel)。存储的形式是一个三维数组(3D array),具有三个索引 i , j , k i, j, k i,j,k。它存储的物理空间(3D volume)解析出来是整整齐齐的长方体。但是表示一个体积仅仅靠数组是不够的,需要打一些补丁。我们通过苏格拉底式地提问慢慢还原出nii文件的一些重要参数设置。

1. 数组只有索引,怎么表征每个体素具体的物理位置信息?

但是,仅仅有三维数组是无法表征物理位置的,因此还需要关注另外一个参数,zooms,也就是这个小方块的长宽高对应的单位长度,他们具有一定的比例关系,它所代表的具体物理单位是可以人为赋予的,只需保证人为定义之后能够保持后续图像处理程序所使用的单位一致即可,比如可以是毫米mm,也可以是厘米cm,甚至是米m。可以理解为做三维重建时的spacing参数。有了zooms,我们可以结合数组的shape计算出整个nii文件存储的3D volume在空间中的大小。例如对应一个索引为 ( i , j , k ) (i, j, k) (i,j,k)的voxel,它相对于这个数组坐标系原点的物理位置为 ( i × z o o m s [ 0 ] , j × z o o m s [ 1 ] , k × z o o m s [ 2 ] i\times zooms[0], j\times zooms[1], k\times zooms[2] i×zooms[0],j×zooms[1],k×zooms[2])。

2. 怎么知道一个物理空间中的三维体积是按照什么方向存储到数组里的?

这个问题再啰嗦一点表达就是,如果我对一个长方体用索引数据的方法(如 i m g [ 0 , : , : ] img[0,:,:] img[0,:,:])进行切片处理,要怎么知道得到的图片是按照哪个方向且出来的。 索引的坐标原点在这个长方体的那个角落?这在医学上是一个比较重要的问题,因为当我们说左右的时候,从病人的背部表示的左右和从病人的正面表示的左右是不同的。在医学的早起,甚至有出现过做手术本来要切除身体左侧病变的部分脏器,因为左右没有表达好切成了右边的脏器的悲剧,所以后来才会先在病理部位用笔标识好后才做手术的惯例。扯回原话题,在医学上有把人体看做是一个长方体,定义了冠状面,矢状面等标准面,把人体的坐标系方向表达为前后(胸部Anterior,背部Posterior),左右(左手Left,右手Right),上下(头部或颅部Superior / Cranial,脚部或尾部Inferior / Caudal)。
Orientation and Voxel-Order Terminology: RAS, LAS, LPI, RPI, XYZ and All That
这也是nii的定义,定义了所存储的数组对应的方向是指向那个部位的,如 P, I, R 表示的是数组中第一个索引 i i i 对应的 x x x 轴正方向是从人体的前胸部指向人体后背部的, j j j 对应的 y y y 轴正方向从人体的头颅指向脚部, k k k 对应的 z z z 轴正方向从人体的左手指向右手。有一点需要注意的是,这几个字母表达的是 T o To To,即指向那个部位为正方向,而不是从哪个方向开始。详细的定义可以看这个链接 Orientation and Voxel-Order Terminology: RAS, LAS, LPI, RPI, XYZ and All That: http://www.grahamwideman.com/gw/brain/orientation/orientterms.htm. 或者这篇博客:https://blog.csdn.net/zhangjipinggom/article/details/118523633

3. 如何表示像素在物理空间中的坐标?

解决问题1之后,我们仅仅只是知道了体积之间,各个体素之间的物理位置关系。如果我们用同一台设备分多次扫描了用一个物体的不同部位然后再把他们整到一起,我们就需要知道每一次扫描得到的volume相对于该设备参考坐标系(reference frame)下的相对位姿。这时候nii就需要定义一个齐次变换矩阵(affine matrix)对体素进行坐标变换了。我们可以把问题1得到的各个体素的位置看作是解剖坐标系,这个affine matrix就是把解剖坐标系下的坐标变换到参考坐标系下的一个变换。

二 示例代码和解析

为了进一步理解nii文件存储的信息,我们提供了下面的示例代码。nii文件则是从医学影像顶会MICCAI的2020挑战赛中的Spine CT中获取,nii文件可以在这个github https://github.com/anjany/verse 的util/sample文件夹中。

0. 看看nii文件长什么样子

将 sub-verse004_ct.nii.gz 文件拖拽到可视化软件中,如3D slicer (怎么搞自己网上搜),或者imfusion文件,可以看到如下图片:
在这里插入图片描述

1. 读取nii文件

import os
import numpy as np
import nibabel as nib
import nibabel.orientations as nio
import matplotlib.pyplot as plt
from data_utilities import *

directory = 'xxxxx'
img_nib = nib.load(os.path.join(directory,'sub-verse005_ct.nii.gz'))

2. 获取数组并查看nii的相关参数

# array info
img_data = img_nib.get_fdata() 
print('img shape: ', img_nib.shape)
print('data shape: ', img_data.shape)
print('data type: ', type(img_data))
# volume info
zooms = img_nib.header.get_zooms() # similar to the spacing settings in the 3D compounding
print('zooms of the voxel: ', zooms)
axs_code = nio.ornt2axcodes(nio.io_orientation(img_nib.affine))
print('img orientation code: {}'.format(axs_code)) 
# global info
print('affine matrix: ', img_nib.affine)

3. 查看切片进一步理解方向表示

def show_slices(slices):
    fig, axes = plt.subplots(1, len(slices))
    for i, slice in enumerate(slices):
        axes[i].imshow(slice, cmap='gray')
        
slice_0 = img_data[img_shape[0]//2, :, :]
slice_1 = img_data[:, img_shape[1]//2, :]
slice_2 = img_data[:, :, img_shape[2]//2]
show_slices([slice_0, slice_1, slice_2])
plt.suptitle("Center slices for body image") 
plt.show()

根据步骤2输出的结果:img orientation code: (‘P’, ‘I’, ‘R’)。我们可以判断出切片的 x x x轴(第一个索引, 图像的行)和 y y y(第二个索引, 图像的列)。坐标系的表达如下面几张图所示:
在这里插入图片描述
切片中的坐标系的定义如下:
在这里插入图片描述

以上,
2023年7月12号
Dianye

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ESP32连接云服务器【WebSocket】

ESP32连接云服务器【ESP32宝塔面板】 文章目录 ESP32连接云服务器【ESP32宝塔面板】👨‍🏫内容1:背景👨‍⚖️内容2:服务器配置👨‍💻内容3:ESP32配置 👨‍🏫…

k8s 就绪探针

【k8s 系列】k8s 学习二十,就绪探针 提起探针,不知兄dei 们是否有印象,之前我们分享过存活探针,分享存活探针是如何确保异常容器自动重启来保持应用程序的正常运行,感兴趣的可以查看文章 k8s 系列k8s 学习十七&#x…

windows下使用arp 协议

/ //自动扫描局域网存活主机 本程序是利用arp协议去获取局域网中的存活主机 arp协议概述 地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请…

音视频——码率、帧率越高越清晰?分辨率、像素、dpi的关系

一 前言 本期我介绍一下视频的一些基础概念,如帧率、码率、分辨率、像素、dpi、视频帧、I帧、P帧、gop等。我i初步学习音视频,给这些专业词汇进行扫盲 会解释多少码率是清晰的,是否帧率越高越流畅等问题。 这些概念是比较杂乱的&#xff0c…

CentOS 7镜像下载 以及 DVD ISO 和 Minimal ISO 等各版本的区别介绍

1.官网下载 官网下载地址:官网下载链接 点击进入下载页面,随便选择一个下载即可(不推荐,推荐阿里云下载,见下文) 阿里云下载站点(速度非常快推荐) 阿里云下载链接: http…

二叉树(上)——“数据结构与算法”

各位CSDN的uu们好呀,好久没有更新我的数据结构与算法专栏啦,今天,小雅兰继续来更新二叉树的内容,下面,让我们进入链式二叉树的世界吧!!! 二叉树链式结构的实现 二叉树链式结构的实现…

MySQL进阶SQL语句(二)

MySQL进阶SQL语句(二) 一、MySQL进阶SQL语句1.1 连接查询1.2 CREATE VIEW视图,可以被当作是虚拟表或存储查询1.3 UNION 联集1.4 CASE1.5 空值(NULL) 和 无值() 的区别1.6 正则表达式 二、存储过程2.1 存储过程定义2.2 存储过程的优点2.3 存储…

【数据结构】栈和队列详解

⭐️ 往期相关文章 ✨链接1:数据结构和算法的概念以及时间复杂度空间复杂度详解 ✨链接2:【数据结构】手撕顺序表 ✨链接3:【数据结构】手撕单链表 ✨链接4:【数据结构】双向带头循环链表 ⭐️ 栈和队列 🌠 栈 栈是…

OpenCV对图片进行缩放处理

在下面的代码中,我会为你优化和解释这段程序: #include <opencv2/opencv.hpp>using namespace cv;int main() {// 读取源图片Mat srcImage = imread("../51_resize.jpg"

目标检测学习

目录 1、目标定位 2、特征点检测 3、目标检测 4、滑动窗口的卷积实现 5、Bounding Box 预测&#xff08;Bounding box predictions&#xff09; 6、交并化 7、非极大值抑制 8、Anchor Boxes 9、YOLO算法 1、目标定位 2、特征点检测 如何检测特征点&#xff08;以人的部…

HTML5新特性总结

新增语义化标签 新增了很多语义化标签&#xff0c;如header、footer、nav、article、section(页面中的某段文字&#xff0c;或文章中的某段文字)、aside、main 其中article标签里可以包含多个section&#xff1b; section强调的是分段或分块&#xff0c;若想将一块内容分成几…

【Vscode】解决 An SSH installation couldn‘t be found

【Vscode】解决 An SSH installation couldn‘t be found 背景描述&#xff1a;在vscode中使用ssh进行连接到时候&#xff0c;已经安装了ssh romote的plugin插件&#xff0c;但是在输入了ssh连接命令之后&#xff0c;仍然出现报错&#xff1a;an ssh installation could not be…

OpenCV 入门教程:中值滤波和双边滤波

OpenCV 入门教程&#xff1a;中值滤波和双边滤波 导语一、中值滤波二、双边滤波三、示例应用3.1 图像去噪3.2 图像平滑 总结 导语 在图像处理和计算机视觉领域&#xff0c;中值滤波和双边滤波是两种常见的滤波方法&#xff0c;用于平滑图像、去除噪声等。 OpenCV 提供了中值滤…

sap abap,forms,smartforms 导出pdf

4种方法&#xff1a; 1.安装pdf程序&#xff0c;Foxit Reader,先敲回车 自动带出&#xff0c;如下图&#xff1a; 直接打印就会弹出保存pdf文档路径&#xff0c;点保存。这种方式是最简单的&#xff0c;可 forms 和 smartforms 。 2. forms 和 smartforms 打印到spool 中&…

Maven工程开发中的继承与聚合

1. 聚合工程概念 设置一个空的maven工程&#xff0c;工程里面只有pom文件&#xff0c;另外将这个工程的打包方式设置为pom。 在聚合工程里面添加聚合工程里面管理的模块 2.聚合总结 3.继承 例如下面02工程继承上面的01工程&#xff0c;在02工程的pom文件中要配置要继承的父工…

保护你的JavaScript代码:深入了解JS混淆加密及其特点

当涉及到JavaScript代码的保护和隐藏时&#xff0c;混淆加密是一种常见的技术。它通过对代码进行转换和重组&#xff0c;使其难以理解和逆向工程。以下是JS混淆加密的几个特点以及它们各自的优缺点&#xff1a; 变量和函数名压缩&#xff1a; 特点&#xff1a;将代码中的变量和…

Hystrix熔断器

雪崩 当山坡积雪内部的内聚力抗拒不了它所受到的重力拉引时&#xff0c;积雪便向下滑动&#xff0c;引起⼤量雪体崩塌&#xff0c;人们把这种自然现象称作雪崩 微服务中&#xff0c;一个请求可能需要多个微服务接口才能实现&#xff0c;会形成复杂的调用链路 …

Holoens证书过期

1. 删除Assets\WSATestCertificate.pfx证书文件。 2. 在Player Settings的Publishing Settings下找到Certificate,选择一个有效的证书。 3. 如果没有其他有效证书,需要生成一个新的测试证书: - Windows: MakeCert.exe -r -pe -n "CNTemporary Certificate" -ss MY …

Linux--操作系统进程的状态

【Linux】进程概念 —— 进程状态_linux d状态进程_Hello_World_213的博客-CSDN博客 新建&#xff1a;字面意思&#xff0c;将你的task_struct创建出来并且还未入队列 运行&#xff1a;task_struct结构体在运行队列中排队&#xff0c;就叫做运行态 阻塞&#xff1a; 等待非C…

GPT(Generative Pre-Training)论文解读及实现(一)

1 GPT Framework 1.1 Unsupervised pre-training Given an unsupervised corpus of tokens U {u1, . . . , un}, we use a standard language modeling objective to maximize the following likelihood: 在给定语料上下文环境下&#xff0c;目标时最大化下面的语言模型&…