[Python VTK] Lea los resultados de segmentación de imágenes médicas de secuencia bidimensional y realice una reconstrucción tridimensional

1. Descripción del problema

Recientemente encontré un problema de este tipo durante el desarrollo:

Durante el desarrollo de imágenes médicas, segmentamos imágenes médicas a través de algoritmos de aprendizaje profundo , y ahora queremos usar este conjunto de imágenes 2D para la reconstrucción 3D .

Estos son los resultados de la segmentación:

Figura 1: Resultados de segmentación de imágenes de resonancia magnética de próstata Figura 1: Resultados de segmentación de imágenes de resonancia magnética de próstataFigura 1: Resultados de segmentación de imágenes de resonancia magnética de próstata

La siguiente es la máscara de máscara de lectura:

Figura 2: Máscara de segmentación de imagen Figura 2: Máscara de segmentación de imagenFigura 2: Máscara de segmentación de imágenes
Cómo realizar la reconstrucción tridimensional de estas imágenes bidimensionales es un problema difícil.El autor utiliza vtk para operaciones de modelado.

Dos, la solución

0. escribir delante

La reconstrucción 3D de imágenes médicas es una tecnología candente en sí misma, y ​​esta tecnología no es nueva. El autor investigó varios blogs y otros materiales del primero, y resolvió su propia solución, con el objetivo de comunicarse con todos. Si tiene una mejor Esquema de modelado, bienvenido a comunicarse conmigo en cualquier momento.

1. Preparativos

Para la reconstrucción 3D de imágenes médicas, primero es necesario proporcionar contornos y máscaras claramente visibles. (Como se muestra en la Figura 2)

Bibliotecas utilizadas:

  • vtk, puede instalarlo pip install vtkdirectamente

2. Estructura de archivos

  • carpeta de máscara (utilizada para almacenar la máscara de resultado de segmentación, el nombre de la imagen es mask_0.png, mask_1.png, mask_2.pngetc. 20 imágenes)
  • vtk_gaussian.py (script de Python para ejecución y reconstrucción 3D)

como muestra la imagen:

Figura 3: La estructura de archivos utilizada por el proyecto Figura 3: La estructura de archivos utilizada por el proyectoFigura 3: La estructura de archivos utilizada por el proyecto

3. Explicación del código

3.0 Código completo

Sé que algunos amigos tienen prisa, así que aquí está el código completo:

import vtk

# 定义渲染窗口、交互模式
aRender = vtk.vtkRenderer()
Renwin = vtk.vtkRenderWindow()
Renwin.AddRenderer(aRender)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(Renwin)

# 定义个图片读取接口
# 读取PNG图片就换成PNG_Reader = vtk.vtkPNGReader()
PNG_Reader = vtk.vtkPNGReader()
PNG_Reader.SetNumberOfScalarComponents(1)
PNG_Reader.SetFileDimensionality(2)  # 说明图像是三维的

# 定义图像大小,本行表示图像大小为(512*512*240)
PNG_Reader.SetDataExtent(0, 256, 0, 256, 0, 19)
# 设置图像的存放位置
name_prefix = ['mask/mask_']
PNG_Reader.SetFilePrefix(name_prefix[0])

# 设置图像前缀名字
# 表示图像前缀为数字(如:0.jpg)
PNG_Reader.SetFilePattern("%s%d.png")
PNG_Reader.Update()
PNG_Reader.SetDataByteOrderToLittleEndian()
spacing = [1.0, 1.0, 2.5]  # x, y 方向上的间距为 2 像素,z 方向上的间距为 2.5 像素
PNG_Reader.GetOutput().SetSpacing(spacing)

# 高斯平滑
gauss = vtk.vtkImageGaussianSmooth()
gauss.SetInputConnection(PNG_Reader.GetOutputPort())
gauss.SetStandardDeviations(1.0, 1.0, 1.0)
gauss.SetRadiusFactors(1.0, 1.0, 1.0)
gauss.Update()

# 计算轮廓的方法
contour = vtk.vtkMarchingCubes()
gauss.GetOutput().SetSpacing(spacing)
contour.SetInputConnection(gauss.GetOutputPort())
contour.ComputeNormalsOn()
contour.SetValue(0, 100)

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(contour.GetOutputPort())
mapper.ScalarVisibilityOff()

actor = vtk.vtkActor()
actor.SetMapper(mapper)

renderer = vtk.vtkRenderer()
renderer.SetBackground([1.0, 1.0, 1.0])
renderer.AddActor(actor)

window = vtk.vtkRenderWindow()
window.SetSize(512, 512)
window.AddRenderer(renderer)

interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)

# 开始显示
if __name__ == '__main__':
    window.Render()
    interactor.Initialize()
    interactor.Start()

3.1 Definir la ventana de renderizado y el modo interactivo

import vtk

# 定义渲染窗口、交互模式
aRender = vtk.vtkRenderer()
Renwin = vtk.vtkRenderWindow()
Renwin.AddRenderer(aRender)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(Renwin)

Casi todos mis scripts de reconstrucción 3D incluyen este contenido, y esta es también la parte para inicializar la ventana interactiva de vtk Para obtener más información, puede consultar los documentos oficiales de vtk u otros blogs técnicos.

3.2 Lectura de secuencias de imágenes 2D: definición de la interfaz de lectura

Para leer todas las imágenes mask_0.pngrecibidas mask_19.png, debe definir una interfaz de lectura de imágenes (vtk.vtkXxxReader).

La imagen del autor está en .pngformato, así que use vtk.vtkPNGReader()para leer la imagen.

PNG_Reader = vtk.vtkPNGReader()
PNG_Reader.SetNumberOfScalarComponents(1)
PNG_Reader.SetFileDimensionality(2)  # 说明图像是二维的

Aquí, puede elegir diferentes vtkReader según diferentes formatos de imagen.Si es una imagen en .jpgformato , puede realizar los siguientes cambios:

JPG_Reader = vtk.vtkJPEGReader()
# your code here...

3.3 Lectura de secuencias de imágenes bidimensionales: configuración previa y lectura de imágenes

# 定义图像大小,本人表示图像大小为(256*256)
# 后两个参数是图片的数目,本人这里所用的图像共20张,所以就输入0, 19
# 在后续读取的时候,会根据这个序列进行读取
PNG_Reader.SetDataExtent(0, 256, 0, 256, 0, 19)
# 设置图像的存放位置
name_prefix = ['mask/mask_']
PNG_Reader.SetFilePrefix(name_prefix[0])

# 表示图像前缀为数字(如:0.jpg)
PNG_Reader.SetFilePattern("%s%d.png")
PNG_Reader.Update()
PNG_Reader.SetDataByteOrderToLittleEndian()

Este código es una configuración previa para la lectura de imágenes. SetDataExtent()La configuración de parámetros de la función tendrá un cierto impacto en el procesamiento posterior de la imagen, complete el tamaño y la cantidad de imágenes que usa correctamente.

SetFilePrefix()La función se bloqueará de acuerdo con la cadena pasada. Se debe prestar especial atención aquí :

  • Mis imágenes se almacenan en maskcarpetas, y el nombre de cada imagen es: mask_0.png, mask_1.pngetc.
  • Al configurar el Prefijo aquí, es necesario ingresarmask/mask_
  • La siguiente SetFilePattern()función leerá automáticamente el número, porque el prefijo se ha configurado y no es necesario realizar algunas operaciones regulares del operador aquí.

3.4 La cantidad de datos de imagen es pequeña y el resultado del modelado 3D es muy plano

Solución: puede aumentar el espacio entre las imágenes para solucionar este problema, siguiendo el código anterior:

PNG_Reader.SetDataByteOrderToLittleEndian()
spacing = [1.0, 1.0, 2.5]  # x, y 方向上的间距为 2 像素,z 方向上的间距为 2.5 像素
PNG_Reader.GetOutput().SetSpacing(spacing)

Después de leer la imagen, puede establecer spacingla , que representan x, y, zel espaciado de las tres dimensiones, y zaumentar el espaciado de las dimensiones a 2.5, tal operación tendrá un efecto significativo en el modelado posterior. El contenido específico es el siguiente:

Figura 4: Los resultados del modelo de dos espacios diferentes, la imagen de la izquierda es z = 1,0, la imagen de la derecha es z = 2,5 Figura 4: Los resultados del modelo de dos espacios diferentes, la imagen de la izquierda es z = 1,0, la imagen de la derecha es z =2.5Figura 4: Resultados del modelado de dos espacios diferentes, la imagen de la izquierda es z=1.0 , la imagen de la derecha es z=2.5

Para que el resultado del modelado se acerque más a la forma del órgano, recomiendo establecer el espacio entre las imágenes 2D .

3.5 Suavizado de resultados de reconstrucción 3D - Suavizado gaussiano

El resultado del modelado original es "claramente en capas" y no particularmente hermoso. El autor utiliza un esquema de suavizado gaussiano para suavizar la imagen.

Si tiene una mejor solución de suavizado, ¡bienvenido a comunicarse conmigo!

# 高斯平滑
gauss = vtk.vtkImageGaussianSmooth()
gauss.SetInputConnection(PNG_Reader.GetOutputPort())
gauss.SetStandardDeviations(1.0, 1.0, 1.0)
gauss.SetRadiusFactors(1.0, 1.0, 1.0)
gauss.Update()

Figura 5: Los resultados del suavizado de dos espaciados diferentes, la imagen de la izquierda no está suavizada, la imagen de la derecha usa suavizado gaussiano Figura 5: Los resultados del suavizado de dos espaciados diferentes, la imagen de la izquierda no está suavizada, la imagen de la derecha es suavizado gaussianoFigura 5: Los resultados del suavizado de dos espacios diferentes, la imagen de la izquierda no está suavizada y la imagen de la derecha es un suavizado gaussiano

3.6 Cálculo de la extracción de contornos y bordes

Después del suavizado gaussiano, se realiza la extracción de bordes.

# 计算轮廓的方法
contour = vtk.vtkMarchingCubes()
gauss.GetOutput().SetSpacing(spacing)
contour.SetInputConnection(gauss.GetOutputPort())
contour.ComputeNormalsOn()
contour.SetValue(0, 100)

3.7 Operación de tubería y presentación visual

El siguiente código es la parte de visualización de vtk. El autor generalmente no lo toca, y también es el contenido inherente a cada script. Si está interesado en esta parte, debe consultar el documento oficial de vtk.

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(contour.GetOutputPort())
mapper.ScalarVisibilityOff()

actor = vtk.vtkActor()
actor.SetMapper(mapper)

renderer = vtk.vtkRenderer()
renderer.SetBackground([1.0, 1.0, 1.0])
renderer.AddActor(actor)

window = vtk.vtkRenderWindow()
window.SetSize(512, 512)
window.AddRenderer(renderer)

interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)

# 开始显示
if __name__ == '__main__':
    window.Render()
    interactor.Initialize()
    interactor.Start()

3. Algunas experiencias compartidas de la biblioteca vtk

1. pitón vtk

Personalmente, siento que el desarrollo de python vtk no tiene un estilo pythonic. Los desarrolladores han aportado algunas ideas de desarrollo de cpp al diseño de la biblioteca/interfaz de python, lo que me hace escribir como la cera de mascar. También se puede ver en el código anterior. que tiene un fuerte estilo cpp.

Pero mientras el código pueda ejecutarse, debo decir que vtk sigue siendo una herramienta de reconstrucción 3D muy poderosa.

2. Dos formas de entrada de datos

2.1 .GetOutputPort()y.SetInputConnection()

Verás oraciones contour.SetInputConnection(gauss.GetOutputPort())como .

Estas dos funciones generalmente aparecen en pares y se transmiten hacia arriba y hacia abajo.

2.1 .GetOutput()y.SetInputData()

Cuando me refiero a los blogs de otros bloggers, también veo este tipo de escritura, por ejemplo:

contour.SetInputData(gauss.GetOutput())

Estas dos funciones generalmente aparecen en pares, y los resultados del procesamiento se transmiten hacia arriba y hacia abajo.

3. vtkImageData y vtkPolyData

Durante el proceso de desarrollo, puede encontrar muchos errores, entre los cuales definitivamente habrá errores del tipo de datos vtk. Armé un formulario:

Nombre Tipo de entrada Tipo de devolución Variable
vtk.vtkPNGReader() ? vtkImageData Lector_PNG
vtk.vtkImagenGaussianSmooth() vtkImageData vtkImageData gauss
vtk.vtkCubos que marchan() vtkImageData vtkPolyData contorno
vtk.vtkPolyDataNormals() vtkImageData vtkPolyData filtro de norma

Espero poder ayudarte a resolver algunas dudas en el proceso de desarrollo.

posdata

Hay pocos blogs sobre la reconstrucción 3D de imágenes médicas. ¡El autor espera dar algunas chispas y avanzar juntos!

El código de reconstrucción utilizado por el autor se ha empaquetado en el disco de la nube de Baidu, puede descargarlo a través del siguiente enlace:

enlace de descarga

Código de extracción: jpt3

Supongo que te gusta

Origin blog.csdn.net/Samurai_Bushido/article/details/129915969
Recomendado
Clasificación