关于3Dslicer和SimpleITK各自的origin,direction,spacing,(和size)

这篇具有很好参考价值的文章主要介绍了关于3Dslicer和SimpleITK各自的origin,direction,spacing,(和size)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

众所周知3Dslicer和SimpleITK对三维医学图像的某些参数并不一致

。。。

在正文之前首先要介绍IJK坐标系和RAS坐标系

IJK坐标系是对图像本身而言,三维图像是一个一个一个体素组成,IJK坐标就是这些体素的坐标或者索引,IJK一般只有非负整数值。如果以三维矩阵获取图像,那么IJK就是这个矩阵的索引。在下文的GetArrayFromImage()和arrayFromVolume()函数就是以三维矩阵获取图像。

RAS坐标系也叫世界坐标系,是物理空间中的绝对坐标系或者叫大地坐标系,如果不习惯叫RAS的话,可以记住X指向R面,Y指向A面,Z指向S面(我也经常搞混,不如就XYZ等同于RAS)。这个坐标系原点是0,长度是按mm,cm这种现实中的度量。举个例子,图像的位置对于RAS坐标系可能有各种千奇百怪的状态,如下图

3d slicer对比图像,图像处理,python3d slicer对比图像,图像处理,python

 这也就是为什么会有origin和direction这两个量

 简而言之

size是体素图像的[I, J, K],I长,J宽,K高

origin是体素图像的原点坐标对于RAS坐标系的位置

spacing体素图像每个维度上像素或体素之间的间距,一般单位mm

direction是图像的方向,即图像坐标系相对世界坐标系的角度,一般采用的是方向余弦矩阵

3Dslicer和SimpleITK里这些参数相互转化的代码见文末

在SimpleITK中,图像被read成了<class 'SimpleITK.SimpleITK.Image'>的类,这个类的函数就可以获取origin,direction,spacing,和size

import SimpleITK as sitk
imagepath = "…………/图像名.nii" # 图像路径
image1 = sitk.ReadImage(imagepath)

# size是[I, J, K]形list,I长,J宽,K高
print('image1.GetSize(): ', image1.GetSize())

# origin图像的原点坐标对于RAS坐标系的位置,顺序[R,A,S]
print('image1.GetOrigin() :', image1.GetOrigin())

# spacing每个维度上像素或体素之间的间距,单位mm,顺序[I, J, K]
print('image1.GetSpacing(): ', image1.GetSpacing())

# direction图像的方向,即图像坐标系相对世界坐标系的角度,采用的是方向余弦矩阵按行主序展成一# 维
print('image1.GetDirection(): ', image1.GetDirection())

# 因为是行主序,可以用reshape变成方向余弦矩阵
direction = np.array(list(itkimage.GetDirection())).reshape(3, 3)
print("directionmatrix",direction)

在slicer中,图像的形式是'vtkMRMLScalarVolumeNode'或者'vtkMRMLLabelMapVolumeNode',这些是slicer特有的node的形式,可以直接获取origin,spacing

# 获取所有'vtkMRMLScalarVolumeNode'的名称(字符串)列表
ModelNames = getNodesByClass('vtkMRMLScalarVolumeNode')
ModelName = ModelNames[0] # 这里你需要确保列表里的ModelNames[?]是上述的类型
# 用getNode方法按名称获取节点
volumeNode = getNode(ModelName.GetName())
# 获取origin
volumeNode.GetOrigin()
# 获取spacing
volumeNode.GetSpacing()
print("volumeNode.GetOrigin()",volumeNode.GetOrigin())
print("volumeNode.GetSpacing()",volumeNode.GetSpacing())

注意注意注意!!!

这里是第一个坑点,slicer里origin的符号和sitk不一样,在I和J是反的,如果我们希望sitk里获取的origin和slicer一样,需要这样翻符号。

import SimpleITK as sitk
imagepath = "…………/图像名.nii" # 图像路径
image1 = sitk.ReadImage(imagepath)

origin = np.array(list(image1.GetOrigin()))
origin[0] = -origin[0]
origin[1] = -origin[1]
print('origin',origin)

spacing在sitk和slicer里顺序和符号都一样。

至于size,我没有找到slicer中有直接从node中获取的方法,但是可以先获取矩阵,然后查看矩阵的shape,这就等同于sitk里的size

import numpy as np
# 获取所有'vtkMRMLScalarVolumeNode'的名称(字符串)列表
ModelNames = getNodesByClass('vtkMRMLScalarVolumeNode')
ModelName = ModelNames[0] # 这里你需要确保列表里的ModelNames[?]是上述的类型
# 用getNode方法按名称获取节点
volumeNode = getNode(ModelName.GetName())
a = arrayFromVolume(volumeNode)
a = np.array(a)
print(a.shape)

注意注意注意!!!

这里是第二个坑点,在3Dslicer和SimpleITK里的这种arrayfrom的函数,返回的都是[K,J,I]的顺序

所以一般都需要transpose一下

# 对sitk
import SimpleITK as sitk
imagepath = "…………/图像名.nii" # 图像路径
image1 = sitk.ReadImage(imagepath)
label = sitk.GetArrayFromImage(image1).transpose(2,1,0)

# 对slicer
volumeNode = getNode(ModelName.GetName())
a = arrayFromVolume(volumeNode)
a = np.array(a)
a.transpose(2,1,0)

到了direction这里,事情就很麻烦了

slicer里有3个函数分别获取I,J,K轴对于RAS坐标系的方向

# 因为这些函数是直接改变list,所以要先定义后使用
I_AXISToRAS=[0.0,0.0,0.0]
J_AXISToRAS=[0.0,0.0,0.0]
K_AXISToRAS=[0.0,0.0,0.0]

volumeNode.GetIToRASDirection(I_AXISToRAS)
volumeNode.GetJToRASDirection(J_AXISToRAS)
volumeNode.GetKToRASDirection(K_AXISToRAS)

print(I_AXISToRAS)
print(J_AXISToRAS)
print(K_AXISToRAS)

返回的是I对R,A,S分别的余弦值,J对R,A,S分别的余弦值,K对R,A,S分别的余弦值

再对比前面sitk得到的direction,会发现有很大出入,有2行负号是相反的

import SimpleITK as sitk
imagepath = "…………/图像名.nii" # 图像路径
image1 = sitk.ReadImage(imagepath)
direction = np.array(list(image1.GetDirection())).reshape(3, 3)
print('direction\n',direction)

>>> direction
[[ 9.94355000e-01 -4.89225598e-08 -1.06104344e-01]
 [-3.40554847e-05  9.99999948e-01 -3.19611396e-04]
 [ 1.06104348e-01  3.21420675e-04  9.94354950e-01]]
I_AXIS=[0.0,0.0,0.0]
J_AXIS=[0.0,0.0,0.0]
K_AXIS=[0.0,0.0,0.0]

volumeNode.GetIToRASDirection(I_AXIS)
volumeNode.GetJToRASDirection(J_AXIS)
volumeNode.GetKToRASDirection(K_AXIS)
>>> print(I_AXIS)
[-0.9943550000701419, 3.4055484670310706e-05, 0.1061043480529044]
>>> print(J_AXIS)
[4.892255980967634e-08, -0.9999999483443722, 0.000321420675237069]
>>> print(K_AXIS)
[0.10610434399770712, 0.0003196113955094529, 0.9943549497203562]

那么如何把这3个向量拼成方向余弦矩阵呢,当时做到这里我直接妈妈生的。。。

SimpleITK官网

3d slicer对比图像,图像处理,python

Slicer官网

3d slicer对比图像,图像处理,python 

都没有详细说这里面的元素如何对应 

去查了一下方向余弦矩阵的概念

做控制要知道的刚体旋转知识(四)旋转矩阵/方向余弦矩阵 - 知乎 (zhihu.com)

3d slicer对比图像,图像处理,python

这篇文章里面单位向量 I, J, K 表示为世界坐标系(global)的XYZ轴,三个小写的向量 i, j, k是本体坐标系的三个单位向量 

其实slicer里获取到的是方向余弦矩阵的列向量

那么slicer里的方向余弦矩阵可以这样得到

# !!!注意把direction拼成二维!!!
I_AXIS_ras_slicer = [0.0,0.0,0.0]
J_AXIS_ras_slicer = [0.0,0.0,0.0]
K_AXIS_ras_slicer = [0.0,0.0,0.0]

labelmapNode.GetIToRASDirection(I_AXIS_ras_slicer)
labelmapNode.GetJToRASDirection(J_AXIS_ras_slicer)
labelmapNode.GetKToRASDirection(K_AXIS_ras_slicer)

direction = np.array([I_AXIS_ras_slicer,J_AXIS_ras_slicer,K_AXIS_ras_slicer]).reshape(3,3).swapaxes(0, 1)
print('direction',direction)

那么上面的结果中sitk的I轴和K轴负号相反,K轴不变

对于sitk,如果我们希望把得到的direction在slicer里使用,则应该这样计算方向余弦矩阵

​# 如果我们希望sitk里获取的origin和slicer一样,需要这样翻符号。

import SimpleITK as sitk
imagepath = "…………/图像名.nii" # 图像路径
image1 = sitk.ReadImage(imagepath)

origin = np.array(list(image1.GetOrigin()))
origin[0] = -origin[0]
origin[1] = -origin[1]
print('origin',origin)

# 如果我们希望把得到的direction在slicer里使用,则应该这样计算方向余弦矩阵
import SimpleITK as sitk
imagepath = "…………/图像名.nii" # 图像路径
image1 = sitk.ReadImage(imagepath)
direction = np.array(list(image1.GetDirection())).reshape(3, 3)
direction[0, 0] = -direction[0, 0]
direction[0, 1] = -direction[0, 1]
direction[1, 0] = -direction[1, 0]
direction[1, 1] = -direction[1, 1]
direction[0, 2] = -direction[0, 2]
direction[1, 2] = -direction[1, 2]

print('direction',direction)

如果我们想把slicer里的结果用于sitk,则应该这样产生origin向量和direction向量

labelmapNode_origin = np.array(labelmapNode.GetOrigin())
labelmapNode_origin[0] = -labelmapNode_origin[0]
labelmapNode_origin[1] = -labelmapNode_origin[1]
# !!!注意这里sitk要翻负号!!!
labelmapNode_I_direction = [0.0,0.0,0.0]
labelmapNode_J_direction = [0.0,0.0,0.0]
labelmapNode_K_direction = [0.0,0.0,0.0]
labelmapNode.GetIToRASDirection(labelmapNode_I_direction)
labelmapNode.GetJToRASDirection(labelmapNode_J_direction)
labelmapNode.GetKToRASDirection(labelmapNode_K_direction)
# !!!注意sitk的direction是一维!!!
labelmapNode_direction = np.array([-labelmapNode_I_direction[0],-labelmapNode_J_direction[0],-labelmapNode_K_direction[0],
                                  -labelmapNode_I_direction[1],-labelmapNode_J_direction[1],-labelmapNode_K_direction[1],
                                  labelmapNode_I_direction[2],labelmapNode_J_direction[2],labelmapNode_K_direction[2]])

labelmapNode_spacing = np.array(labelmapNode.GetSpacing())

 文章来源地址https://www.toymoban.com/news/detail-808826.html

到了这里,关于关于3Dslicer和SimpleITK各自的origin,direction,spacing,(和size)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • SimpleITK图像基础(三)——SimpleITK学习笔记

    SimpleITK 是专门处理医学影像的软件,是ITK的简化接口,使用起来非常便捷,SimpleITK支持8种编程语言,包括 c++ 、 Python 、 R 、 Java 、 c# 、 Lua 、 Ruby 和 TCL 。在SimpleITK中,图像的概念与我们在计算机视觉中常用的RGB图像差异很大,后者只是一个多维矩阵,是一个数学上的概念

    2024年02月04日
    浏览(61)
  • 关于刷新nginx配置报nginx: [emerg] unknown directive错误的问题

    朋友在windows服务器用记事本编辑nginx配置文件后,刷新nginx配置,nginx返回以下错误: unknown directive 是未知指令的意思,后面带着未知指令的内容是\\\"锘?user\\\"。由此判断,是nginx配置文件编码不正确的原因。 事实上,windows的记事本编辑文本后,会以ASCII码编码进行保存。这与

    2024年02月21日
    浏览(44)
  • 【Python】SimpleITK使用笔记

    nii 格式是保存医学图像非常重要一种格式, nii 格式就是后缀名为 .nii 或 .nii.gz 的文件,该格式又叫 NIfTI-1 。核磁共振成像(MRI)或者CT图像通常会以这种格式保存。详见 这种格式的作用,简单来理解就是将索引坐标映射到体素坐标。在nii格式中,为了将 索引坐标(数组下标

    2024年02月05日
    浏览(33)
  • No ‘Access-Control-Allow-Origin‘ header is present on the requested resource关于vue跨域问题 重复Access-Control-Allow-Origin

            浏览器为了安全考虑,有一个最基本的安全策略,即同源策略。         同源策略规定:浏览器在解析Ajax请求时,要求浏览器的路径与Ajax的请求的路径必须满足三个要求,即请求的协议、域名、端口号都相同,满足同源策略,可以访问服务器,否则,只要有一个不

    2024年02月06日
    浏览(46)
  • No ‘Access-Control-Allow-Origin‘ header is present on the requested resource关于vue跨域问题 重复Access-Control-Allow-Origin

            浏览器为了安全考虑,有一个最基本的安全策略,即同源策略。         同源策略规定:浏览器在解析Ajax请求时,要求浏览器的路径与Ajax的请求的路径必须满足三个要求,即请求的协议、域名、端口号都相同,满足同源策略,可以访问服务器,否则,只要有一个不

    2023年04月17日
    浏览(62)
  • No ‘Access-Control-Allow-Origin‘ header is present on the requested resource关于vue跨域问题

            浏览器为了安全考虑,有一个最基本的安全策略,即同源策略。         同源策略规定:浏览器在解析Ajax请求时,要求浏览器的路径与Ajax的请求的路径必须满足三个要求,即请求的协议、域名、端口号都相同,满足同源策略,可以访问服务器,否则,只要有一个不

    2024年02月08日
    浏览(44)
  • VUE2:关于axios的跨域问题(No ‘Access-Control-Allow-Origin‘ )以及解决方案(亲测有用,全流程配图文版,建议收藏)

    今天在开发中新写了一个前端的项目作为集成,公司是保密项目所以公司代码肯定是不能粘贴了,回家之后还是觉得记录一下比较好,所以自己模拟了一个来给大家解决一下。 在VUE2的开发过程中跨域问题肯定是前端程序员都遇到过的问题,尤其还是像我这种半吊子前端,报

    2024年02月04日
    浏览(44)
  • HTTP协议各自特点

    目录 HTTP/1.1 相比 HTTP/1.0 性能上的改进: HTTP/2 做了什么优化? HTTP/3 做了哪些优化? 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。 但

    2024年02月15日
    浏览(37)
  • FPGA——PLD的区别以及各自的特点

    可编程逻辑器件(PLD-Programmable Logic Device):器件的功能不是固定不变的,而是可根据用户的需要进行改变 ,即由编程的方法来确定器件的逻辑功能。 1、集成度高,可以替代多至几千块通用 IC 芯片,极大减小电路的面积,降低功耗,提高可靠性。 2、具有完善先进的开发工具

    2024年02月15日
    浏览(45)
  • 好用的DNS服务器及各自特点

    1、114.114.114.114:在国内是比较出名的解析服务,因为解析速度快。同时连接三网(移动、电信、联通)横跨服务器延迟也不高。也可以满足用户需求。 2、123.125.81.6:现在有很多人用360软件也会随着使用360DNS服务器。而且安全快捷。这个DNS服务器大部分360软件人群在使用,能

    2024年02月04日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包