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

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

前言

SimpleITK是专门处理医学影像的软件,是ITK的简化接口,使用起来非常便捷,SimpleITK支持8种编程语言,包括c++PythonRJavac#LuaRubyTCL。在SimpleITK中,图像的概念与我们在计算机视觉中常用的RGB图像差异很大,后者只是一个多维矩阵,是一个数学上的概念,而在SimpleITK中图像是一个物理实体,图像中的每一个像素都是物理空间中的一个点,不光有着像素值,还有坐标、间距、方向等概念(这些概念我们后续会介绍)。

下面我们用pythonSimpleITK库中常用的函数进行举例说明。

在使用SimpleITK库之前,需要将SimpleITK库导入进来,如下

import SimpleITK as sitk

1 sitk中的常见属性值

sitk中有以下四种常见的属性,分别可以使用get的方式获取,代码如下所示:

print(image.GetSize()) #获取图像的大小,size为图像的每一个维度的长度,即每个维度像素点的个数
print(image.GetOrigin()) #获取图像的原点坐标
print(image.GetSpacing()) #获取每个维度上像素或体素之间的间距,单位mm,其中对于二维图像是像素,三维图像是体素
print(image.GetDirection()) #获取图像的方向,即图像坐标系相对世界坐标系的角度,角度采用的是方向余弦矩阵

以二维图像为例,如下图所示,左下图是世界坐标系,右下图是图像坐标系。属性值如下所示:

print(image.GetSize())
(7, 6#在SimpleITK中,读取的顺序为[X, Y, Z], 即先宽度,后高度,再深度,对应的三维医学图像中为:先矢状位,后冠状位,再横断位。

print(image.GetOrigin())
(60.0, 70.0#读取顺序同GetSize()方法

print(image.GetSpacing())
(20.0, 30.0#读取顺序同GetSize()方法, 每个维度可以具有不同的间距,且每个维度不一定是正交的。

print(image.GetDirection())
(1.0, 0.00.01.0

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

2 读取和保存图像

SimpleITK可以读取如.mhd , .nii, .nrrd等图像数据格式。

#以读取和保存mhd图像为例
imagepath = "xxx.mhd"   #图像路径
writepath = "xxx.mhd"  #保存图像路径
image = sitk.ReadImage(imagepath)
sitk.Write(image, writepath)
image1 = sitk.ReadImage(imagepath, sitk.sitkFloat32) #你可以指定读取的数据类型

Note1:图像访问是以xyz顺序GetPixel(x,y,z) image[x,y,z], 从0开始索引。

Note2:默认的mask图像类型和默认值为sitkUInt8或标量图像uint8_t, 默认值为0和1,其中1代表的是mask。

3 像素类型

像素类型表示为枚举类型,下面是部分枚举类型的表。

数据类型 含义
sitkUInt8 无符号8位整数
sitkInt8 有符号的8位整数
sitkUInt16 无符号16位整数
sitkInt16 有符号的16位整数
sitkFloat32 32位浮点
sitkFloat64 64位浮点

还有sitkUnknown类型,用于未定义或错误的像素ID,值为-1。

Note:64位整数类型并非在所有的发行版上都可用,如果不可用,则值为sitkUnknown,将图像保存为nii文件,用ITKsnap读取时就会出现的错误如下:
SimpleITK图像基础(三)——SimpleITK学习笔记

4 SimpleITK图像数据和Numpy矩阵数据之间的转换

般我们会用SimpleITK读取图像,再转换为numpy矩阵格式,这样方便数据的处理。

Note

SimpleITK中,各术语对应如下

Width: 宽度,X轴,矢状面(Sagittal
Height: 高度,Y轴,冠状面(Coronal
Depth: 深度, Z轴,横断面(Axial

SimpleITK图像顺序是x,y,z三个方向的大小(在第一节中也讲过),而numpy矩阵的顺序是z,y,x三个方向的大小, 要注意索引位置。

举个例子:假设实验用的图片大小为320*250*80,即矢状面(x轴方向)切片数为320,冠状面(y轴方向)切片数为250,横断面(z轴方向)片数为80。

SimpleITK2Numpy:

GetArrayFromImage():返回图像数据的副本。然后可以自由地修改数据,因为它对原始SimpleITK图像没有影响。

# sitk image to numpy 
shape_img = image.GetSize() #输出形状为:(Width, Height, Depth),即原始SimpleITK数据的存储形式
print("image size:", shape_img)
#image size:(320, 250, 80)

datanp_array = sitk.GetArrayFromImage(image) # 将SimpleITK对象转换为ndarray,X轴与Z轴发生了对调,输出形状为:(Depth, Height, Width)
print("np_array size:", np_array.shape)
# np_array size:(80, 250, 320)

Numpy2SimpleITK:

GetImageFromArray():返回一个SimpleITK图像,原点设置为0,所有维度的间距设置为1,方向余弦矩阵设置为identity,强度数据从numpy数组中复制。在大多数情况下需要设置适当的元数据值。

# numpy data to sitk 
imagesitk_image = sitk.GetImageFromArray(np_array) 
sitk_image.SetOrigin(origin)
sitk_image.SetSpacing(spacing)
sitk_image.SetDirection(direction)

5 访问像素和切片

两种方式:一是使用GetPixelSetPixel函数,二是使用python的切片操作符。例子如下:

#法一:使用GetPixel和SetPixel函数
image_3D = sitk.Image(256, 128, 64, sitk.sitkInt16)
print(image_3D.GetPixel(0, 0, 0))  #注意:是从0开始索引
image_3D.SetPixel(0, 0, 0, 1)
print(image_3D.GetPixel(0, 0, 0))

#法二:使用python的切片操作符
print(image_3D[0,0,1])
image_3D[0,0,1] = 2
print(image_3D[0,0,1])

6 图像重采样

重采样目的是将医疗图像中不同的体素归一化到相同的大小,可分为两种方案,方案一:按目标Spacing缩放,方案二:按目标Size缩放。

这两种方案具体操作分为三个步骤:

1.使用SimpleITK读取数据,获得原始图像的Spacing以及Size

2.如果是方案一,则图像原始Size乘以原始Spacing除以新Spacing得到新Size,如果是方案二,则图像原始Size乘以原始Spacing除以新Size得到新Spacing

3.最后将新Spacing和新Size赋值到读取的数据即可。

核心代码如下:

resampler = sitk.ResampleImageFilter() #初始化一个图像重采样滤波器resampler
resampler.SetReferenceImage(itkimage) #设置需要重新采样的目标图像
resampler.SetSize(newSize.tolist()) #设置目标尺寸
resampler.SetTransform(sitk.Transform(3, sitk.sitkIdentity))
resampler.SetInterpolator(sitk.sitkNearestNeighbor) #设置插值方式,体数据采用线性插值,mask采用最近邻插值
itkimgResampled = resampler.Execute(itkimage) #得到重新采样后的图像

下面以指定Spacing大小,对原始数据进行重采样,例子如下:

def resampleImage(image, targetSpacing, resamplemethod=sitk.sitkNearestNeighbor):
    """
    将体数据重采样的指定的spacing大小
    paras:
    image:sitk读取的image信息,这里是体数据
    targetSpacing:指定的spacing,例如[1,1,1]
    resamplemethod:插值类型
    return:重采样后的数据
    """
    targetsize = [0, 0, 0]
    #读取原始数据的size和spacing信息
    ori_size = image.GetSize()
    ori_spacing = image.GetSpacing()
    transform = sitk.Transform()
    transform.SetIdentity()
    #计算改变spacing后的size,用物理尺寸/体素的大小
    targetsize[0] = round(ori_size[0] * ori_spacing[0] / targetsize[0])
    targetsize[1] = round(ori_size[1] * ori_spacing[1] / targetsize[1])
    targetsize[2] = round(ori_size[2] * ori_spacing[2] / targetsize[2])
    #设定重采样的一些参数
    resampler = sitk.ResampleImageFilter()
    resampler.SetTransform(transform)
    resampler.SetSize(targetsize)
    resampler.SetOutputOrigin(image.GetOrigin())
    resampler.SetOutputSpacing(targetSpacing)
    resampler.SetOutputDirection(image.GetDirection())
    resampler.SetInterpolator(resamplemethod)
    if resamplemethod=sitk.sitkNearestNeighbor:
        #mask用最近邻插值,保存为uint8
        resampler.SetOutputPixelType(sitk.sitkUInt8)
    else:
    	#体数据用线性插值,保存为float32
        resampler.SetOutputPixelType(sitk.sitkFloat32)
    newImage = resampler.Execute(image)
    return newImage

7 图像分割

图像二值化分割: 是分割方法中最基础的,通过定义lowerThresholdupperThreshold两个像素临界点,只要像素值在两者之间,则该像素值改为insideValue,否则改为outsideValue。这种方法只是简单的基于灰度范围标记图像像素,不考虑几何或连通性。

#sitk API
sitk.BinaryThreshold(
    sitk_src, 
    lowerThreshold=lowervalue, 
    upperThreshold=uppervalue, 
    insideValue=255, 
    outsideValue=0) 

#例子:将0-500之间的像素置为1,其他置为0
image = sitk.ReadImage("./***.nii.gz")
outimage = sitk.BinaryThreshold(
        image,
        lowerThreshold=0,
        upperThreshold=500,
        insideValue=1,
        outsideValue=0
    )

图像区域生长分割: 需要确定种子点、生长准则和终止条件。具体来说,对每一个需要分割的区域找一个种子像素作为生长的起点,根据生长准则将种子像素邻域中与种子像素具有相同或相似的像素合并到种子像素所在的区域,直到没有满足条件的像素可以被包进来就终止。在SimpleITK中,首先会计算当前区域中包含的所有像素点灰度值的标准差和期望,通过定义multiplier因子(乘以标准差)来计算以期望为中心的灰度值范围,如果initialNeighborhoodRadius邻域半径内的灰度值位于这个范围就被包进来,灰度值改为replaceValue,当遍历了所有邻域像素,即认为完成了一次迭代,下一次迭代时,像素点的灰度值期望和标准差是以新的像素区域为基础进行计算的,一共要迭代numberOfIterations次。

sitk.ConfidenceConnected(
    image,
    seedList=[seed],
    numberOfIterations=1,
    multiplier=2.5,
    initialNeighborhoodRadius=1,
    replaceValue=1)

8 图像的形态学操作

# 图像的形态学操作:开、闭、膨胀、腐蚀
sitk.BinaryMorphologicalOpening(sitk.ReadImage() != 0, kernelsize) 
sitk.BinaryMorphologicalClosing(sitk.ReadImage() != 0, kernelsize)
sitk.BinaryDilate(sitk.ReadImage() != 0, kernelsize)
sitk.BinaryErode(sitk.ReadImage() != 0, kernelsize)

9 连通域分析

连通域分析一般是针对二值图像,将具有相同像素值且相邻的像素找出来并标记,其中二维图像连通域一般包括4连通和8连通,三维图像连通域包括6连通、18连通和26连通。

# ConnectedComponentImageFilter类,用于连通域划分
ccfilter = sitk.ConnectedComponentImageFilter()
ccfilter.SetFullyConnected(True)
output = ccfilter.Execute(image)  #image为要进行连通域划分的二值图像,output为输出被标记的图像
count = ccfilter.GetObjectCount()

# LabelShapeStatisticsImageFilter类,用于获取各label的形状属性
lssfilter = sitk.LabelShapeStatisticsImageFilter()
lssfilter.Execute(output) 

#根据连通域的体积大小保留最大连通域
area_max_label = 0  #记录最大的连通域的label
area_max = 0 #记录连通域中最大的体积
for i in range(1, count + 1):
    area = lssfilter.GetNumberOfPixels(i)  # 根据label获取连通域体积
    if area > area_max:
        area_max_label = i
        area_max = area
output_array = sitk.GetArrayFromImage(output)
res = np.zeros_like(output_array)
res[output_array == area_max_label] = 1 #获取最大连通域

OpenIssue

1 图片读取的宽高顺序

先上结论:

① 图片一般表示为(width,height),其中原点在图片的左上角,widthcolumnheightrow。举个例子,一张图片为1280x960的分辨率,即width为1280个像素,height为960个像素。

② 图片的存储形式为矩阵,在数学概念中,一般矩阵的表达中第一个值为row,第二个值为column,对应为(height,width)。因此图片为(width,height)转化为numpy数组后变为(height,width)。

下面分别用opencvpillowskimage读取图片。

import cv2
from PIL import Image
import numpy as np
from skimage.io import imread

filepath = './xxx.jpg'   #读取图片的路径

#opencv读取:opencv读进来的图片已经是一个numpy矩阵了
cv2_im = cv2.imread(filepath)
print('cv2_im shape ',cv2_im.shape) # (height, width, ch)

#pillow读取:pillow都进来的图片是一个对象,因此还需要进一步转化为numpy矩阵
im = Image.open(filepath)
print('PIL image size', im.size) # (width, height, ch)
pil_im = np.asarray(im)
print('PIL_im shape ',pil_im.shape) # (height, width, ch)

#skimage读取:同opencv
sk_im = imread(filepath)
print('sk_im shape', sk_im.shape) # (height, width, ch)

summary:

opencv读取和存储的格式是BGR,而不是主流的RGB。

②在深度学习进行推理阶段的前处理时,一般要将RGB图像或者BGR图像转化为CHW格式,一般来说,opencvpillowskimage读取图片并转化为矩阵都是HWC格式,而CHW更适合CNN,因为网络是逐个通道的对图像进行卷积运算,希望在访问同一个channel的像素是连续的,一般存储为CHW格式,这样访问内存的时候是连续的,比较方便。

#yolov5读取图片
img = cv2.imread(path)
img = img[:, :, ::-1].transpose(2, 0, 1)  # img[:, :, ::-1]是将BGR to RGB,transpose(2, 0, 1)是将HWC转换成CHW格式

2 图像重采样可用的工具包

下面分别介绍PILopencv工具包对图像进行重采样。

#PIL重采样
from PIL import Image
image = Image.open("./xxx.jpg")
new_image = image.resize((width, height), Image.BILINEAR)
new_image.save("./xxx.jpg")

#opencv重采样
import cv2
image = cv2.imread("../xxx.jpg")
new_image= cv2.resize(image, (width,height), interpolation=cv2.INTER_CUBIC)

Note:PILCV2一般针对2D图像做处理,比如普通的自然图像,或者CT三维图像中的某一层。所以,并不适用于3D的医学图像处理。文章来源地址https://www.toymoban.com/news/detail-441317.html

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

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

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

相关文章

  • 【C++】C++学习前言

    C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。

    2024年03月12日
    浏览(56)
  • 课程学习前言

    app 抓包分析可以看到有签名有加固,毕竟需要 APK 去访问服务、获取数据,都需要 APK 有完整的信息,而这些信息、代码经过各种加密,还是放在 APK 里面。说白了,就是门锁紧了,钥匙藏在门口某个地方,也许就是地垫下面 逆向流程 拿到 App 应用的 apk ; 使用工具进行查壳

    2024年02月06日
    浏览(45)
  • 【自制视频课程】C++OpnecV基础35讲——第一章 前言

            首先,opencv是一个广泛使用的计算机视觉库,它提供了丰富的图像处理和计算机视觉算法,可以帮助我们快速地开发出高质量的图像处理应用程序;         其次,opencv是一个开源库,可以免费使用和修改,这为我们提供了一个学习和研究计算机视觉的良好平

    2024年02月05日
    浏览(55)
  • Gowin FPGA学习记录——前言

            好久没有写博客了,想想是不是又该写点啥东西了么,准备写点国产FPGA的使用经历吧                  得益于目前国内的政策对国产化芯片扶持,越来越要求核心器件能够自主可控,因此作为核心芯片FPGA,国产FPGA的势头也发展很快。          现在FPGA的这

    2024年02月16日
    浏览(43)
  • 【自制C++深度学习框架】前言

    此GitHub项目是一个初学者的深度学习框架,使用C++编写,旨在为用户提供一种简单、易于理解的深度学习实现方式。以下是本项目的主要特点和功能: 计算图:使用计算图来描述深度学习模型的计算过程,利用计算图将神经网络的计算过程视为一个有向无环图。通过构建计算

    2024年02月07日
    浏览(43)
  • FPGA学习实践之旅——前言及目录

    很早就有在博客中记录技术细节,分享一些自己体会的想法,拖着拖着也就到了现在。毕业至今已经半年有余,随着项目越来越深入,感觉可以慢慢进行总结工作了。趁着2024伊始,就先开个头吧,这篇博客暂时作为汇总篇,记录在这几个月以及之后从FPGA初学者到也算有一定

    2024年02月03日
    浏览(58)
  • 大数据、人工智能、机器学习、深度学习关系联系前言

    1.大数据和人工智能关系 2.机器学习、深度学习、人工智能关系 3.监督学习、无监督学习、半监督学习、强化学习、迁移学习关系 4.机器学习具体内容 1.数据驱动的人工智能 :人工智能系统需要大量的数据来进行训练和学习。大数据提供了海量的信息,可以用于训练机器学习

    2024年02月12日
    浏览(62)
  • 【C++初阶(一)】学习前言以及命名空间

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++初阶之路⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习排序知识   🔝🔝 对于复杂的问题,规模较大的程序 需要高度的抽象和建模时 C语言不再适合应用于这种场景 于是在1982年 C++创始人 Bjarne Stroustrup 在C语言

    2024年02月11日
    浏览(53)
  • 图像处理学习笔记(一)

    一、基础知识 1、彩色图像 (1)RGB RGB色彩模式是工业界的一种颜色标准,通过对红R、绿G、蓝B三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是运用最广的颜色系统之一。 图像中每个像素都分成R、

    2024年02月16日
    浏览(49)
  • 图像处理学习笔记

    图像处理的流程:获取图像-分割区域-特征提取。 嵌入式工业读码器 :包括DM码、QR码、vericode码 Blob分析与形态学 1.Blob区域是Blobs这一数据类型在halcon中的一种贴切的表达形式。 采集图像-区域分割,最后通过特征(如圆度、面积、矩形度等)筛选,这一过程被称为Blob(bin

    2024年02月14日
    浏览(48)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包