Hello everyone,
my goal is it to have a python code that reads DICOM files and then turns them into a 3D .obj file, the problem is that I don’t know how the values of vtkMarchingCubes work because I can’t seem to get all the data but only parts of it. So to say, sometimes I only get the Skull as a model or just the skin, the important thing for me would be the brain tho.
I have tried to render everything in python before, where nothing went missing and at least this part worked well but because of other problems with vtk not liking tk inter I made up my mind to use Godot for the UI stuff. Now using Godot I need the before mentioned .obj file to kind of import it into the 3D environment as a node or something like that.
(I’m a Godot noob so I also have no idea on how to implement the .obj but first I gotta find out how to solve my python problem)
My first full python try looked like this (the # are in german):
import tkinter as tk
from tkinter import filedialog
import vtk
class DICOMViewer:
def __init__(self, root):
self.root = root
self.root.title("DICOM Viewer")
# Initialisieren Sie die VTK-Variable
self.vtk_window = None
# Menü erstellen
menu_bar = tk.Menu(root)
root.config(menu=menu_bar)
file_menu = tk.Menu(menu_bar, tearoff=0)
menu_bar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Open DICOM", command=self.open_dicom_folder)
def open_dicom_folder(self):
folder_path = filedialog.askdirectory(title="Open DICOM Folder")
if folder_path:
# Verzögere das Laden der VTK-Operationen im Hauptthread
self.root.after(0, self.load_dicom_and_render, folder_path)
def load_dicom_and_render(self, folder_path):
# Laden aller DICOM-Dateien im ausgewählten Ordner
dicom_reader = vtk.vtkDICOMImageReader()
dicom_reader.SetDirectoryName(folder_path)
dicom_reader.Update()
dicom_volume = dicom_reader.GetOutput()
# 3D-Rekonstruktion
volume_mapper = vtk.vtkGPUVolumeRayCastMapper()
volume_mapper.SetInputData(dicom_volume)
volume_property = vtk.vtkVolumeProperty()
volume_property.ShadeOn()
volume_property.SetInterpolationTypeToLinear()
volume_color = vtk.vtkColorTransferFunction()
volume_color.AddRGBPoint(-1024, 0.0, 0.0, 0.0)
volume_color.AddRGBPoint(0, 1.0, 0.5, 0.5)
volume_color.AddRGBPoint(3071, 1.0, 1.0, 1.0)
volume_scalar_opacity = vtk.vtkPiecewiseFunction()
volume_scalar_opacity.AddPoint(-1024, 0.0)
volume_scalar_opacity.AddPoint(0, 0.2)
volume_scalar_opacity.AddPoint(3071, 0.2)
volume_property.SetColor(volume_color)
volume_property.SetScalarOpacity(volume_scalar_opacity)
volume_actor = vtk.vtkVolume()
volume_actor.SetMapper(volume_mapper)
volume_actor.SetProperty(volume_property)
# Rendering
renderer = vtk.vtkRenderer()
renderer.SetBackground(0.0, 0.0, 0.0)
render_window = vtk.vtkRenderWindow()
render_window.SetWindowName("DICOM 3D Viewer")
render_window.SetSize(self.root.winfo_screenwidth() // 2, self.root.winfo_screenheight())
render_window.AddRenderer(renderer)
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(render_window)
renderer.AddVolume(volume_actor)
renderer.ResetCamera()
# Interaktionsstil erstellen
interactor_style = vtk.vtkInteractorStyleTrackballCamera()
interactor.SetInteractorStyle(interactor_style)
# Ändere die Position des VTK-Fensters
self.root.update_idletasks()
render_window.SetPosition(self.root.winfo_width(), 0)
# Speichern Sie die Referenz auf das VTK-Fenster
self.vtk_window = render_window
# Starte die Interaktion
interactor.Start()
if __name__ == "__main__":
root = tk.Tk()
app = DICOMViewer(root)
# Setze die Tkinter-GUI auf die gesamte linke Seite
root.geometry("{}x{}+0+0".format(root.winfo_screenwidth() // 2, root.winfo_screenheight()))
root.mainloop()
my current attempt looks more like this:
import vtk
def load_dicom_series(a):
reader = vtk.vtkDICOMImageReader()
reader.SetDirectoryName(a)
reader.Update()
return reader
def create_3d_model(b):
# Apply Marching Cubes to extract the surface
marching_cubes = vtk.vtkMarchingCubes()
marching_cubes.SetInputConnection(b.GetOutputPort())
marching_cubes.SetValue(10, 1500) # Adjust this value to include more/less of the data
marching_cubes.Update()
return marching_cubes
def save_obj(c, d):
# Convert to PolyData
poly_data = d.GetOutput()
# Write to OBJ file
obj_writer = vtk.vtkOBJWriter()
obj_writer.SetFileName(c)
obj_writer.SetInputData(poly_data)
obj_writer.Write()
# Pfad zur DICOM-Serie
dicom_path = 'C:\\Users\\User\\Desktop\\python\\privat\\Germanische\\Scans'
output_path = 'C:\\Users\\User\\Desktop\\model.obj'
# Laden der DICOM-Daten
dicom_reader = load_dicom_series(dicom_path)
# Erzeugung des 3D-Modells
e = create_3d_model(dicom_reader)
# Speichern des 3D-Modells in einer .obj-Datei
save_obj(output_path, e)