Last active
December 27, 2025 00:51
-
-
Save amir-saniyan/6c2f4c2aa6891c6dd64487f53f0feb6a to your computer and use it in GitHub Desktop.
VTK 2D Pipeline
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| from pathlib import Path | |
| import vtkmodules.all as vtk | |
| from PySide6.QtWidgets import QApplication, QMainWindow | |
| from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor | |
| IMAGE_PATH = "test.png" | |
| class MainWindow(QMainWindow): | |
| def __init__(self): | |
| super().__init__() | |
| self.setWindowTitle("VTK 2D Pipeline (Minimal with vtkImageViewer2)") | |
| # ========================================================================= | |
| # VTK 2D Image Viewer (minimal code, but pipeline is still clear) | |
| # ========================================================================= | |
| # | |
| # Goal | |
| # ----- | |
| # Show a 2D image (e.g., PNG/JPG/...) inside a PySide6 window using VTK, while keeping | |
| # the VTK pipeline concept explicit and understandable. | |
| # | |
| # Why this version is "minimal" | |
| # ----------------------------- | |
| # Instead of manually creating: | |
| # - vtkRenderer | |
| # - vtkRenderWindow | |
| # - vtkImageSliceMapper / vtkImageSlice (or vtkImageActor) | |
| # and wiring them together ourselves, | |
| # | |
| # we use a VTK convenience class: | |
| # - vtkImageViewer2 | |
| # | |
| # vtkImageViewer2 is a ready-made 2D viewer that internally manages the "rendering side" | |
| # of the pipeline (Actor + Renderer + RenderWindow + some camera defaults). | |
| # | |
| # Pipeline view (conceptual) | |
| # -------------------------- | |
| # Even though vtkImageViewer2 hides some objects, the pipeline concept is still the same: | |
| # | |
| # Source (Reader) → Filter(s) → Viewer (internally: Mapper+Actor+Renderer+RenderWindow) → Interactor | |
| # | |
| # - Source: | |
| # Reads image data from a file and produces vtkImageData. | |
| # - Filter(s): | |
| # Optional processing steps (flip, cast, window/level mapping, etc.). | |
| # - Viewer: | |
| # A convenience wrapper that wires the rendering pipeline for 2D display. | |
| # - Interactor: | |
| # Handles mouse/keyboard events (pan/zoom) via an interactor style. | |
| # | |
| # In short: | |
| # - We keep Source and Filter explicit (so the data pipeline is visible), | |
| # - We delegate the rendering boilerplate to vtkImageViewer2 (to minimize code). | |
| # ========================================================================= | |
| # Qt ↔ VTK bridge widget: | |
| # This widget contains a vtkRenderWindow + vtkRenderWindowInteractor. | |
| self.vtk_widget = QVTKRenderWindowInteractor() | |
| self.setCentralWidget(self.vtk_widget) | |
| # --------------------------------------------------------------------- | |
| # 1) Source (Reader) | |
| # --------------------------------------------------------------------- | |
| # Use the factory to pick the correct reader based on the file. | |
| reader = vtk.vtkImageReader2Factory.CreateImageReader2(IMAGE_PATH) | |
| if reader is None: | |
| raise RuntimeError(f"VTK could not find an image reader for: {IMAGE_PATH}") | |
| reader.SetFileName(IMAGE_PATH) | |
| # --------------------------------------------------------------------- | |
| # 2) Filter (optional processing) | |
| # --------------------------------------------------------------------- | |
| # Flip the image along an axis (0=X, 1=Y, 2=Z). | |
| # If you do not need processing, you can skip this filter and directly | |
| # feed reader.GetOutputPort() to the viewer. | |
| flip = vtk.vtkImageFlip() | |
| flip.SetInputConnection(reader.GetOutputPort()) | |
| flip.SetFilteredAxis(0) # flip X axis (example) | |
| # --------------------------------------------------------------------- | |
| # 3) Viewer (convenience rendering pipeline) | |
| # --------------------------------------------------------------------- | |
| # vtkImageViewer2 internally creates/owns: | |
| # - Image mapper / actor for 2D viewing | |
| # - Renderer | |
| # - RenderWindow (we attach it to the Qt widget's RenderWindow) | |
| # | |
| # You still provide "the output of the data pipeline" via SetInputConnection(). | |
| self.viewer = vtk.vtkImageViewer2() | |
| self.viewer.SetRenderWindow(self.vtk_widget.GetRenderWindow()) | |
| self.viewer.SetupInteractor(self.vtk_widget.GetRenderWindow().GetInteractor()) | |
| # Connect the end of our explicit data pipeline into the viewer. | |
| self.viewer.SetInputConnection(flip.GetOutputPort()) | |
| # --------------------------------------------------------------------- | |
| # 4) Interactor (+ Style) | |
| # --------------------------------------------------------------------- | |
| # Image-friendly interaction style (pan/zoom typical for 2D viewers). | |
| self.viewer.GetRenderWindow().GetInteractor().SetInteractorStyle( | |
| vtk.vtkInteractorStyleImage() | |
| ) | |
| # --------------------------------------------------------------------- | |
| # Optional cosmetics | |
| self.viewer.GetRenderer().SetBackground(0.1, 0.1, 0.1) | |
| # Fit the image into view and draw once. | |
| self.viewer.GetRenderer().ResetCamera() | |
| self.viewer.Render() | |
| # Important: initialize AFTER everything is connected. | |
| self.vtk_widget.Initialize() | |
| if __name__ == "__main__": | |
| app = QApplication(sys.argv) | |
| win = MainWindow() | |
| win.resize(900, 700) | |
| win.show() | |
| sys.exit(app.exec()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| import vtkmodules.all as vtk | |
| from PySide6.QtWidgets import QApplication, QMainWindow | |
| from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor | |
| IMAGE_PATH = "test.png" | |
| class MainWindow(QMainWindow): | |
| def __init__(self): | |
| super().__init__() | |
| self.setWindowTitle("VTK 2D Pipeline") | |
| # ========================================================================= | |
| # VTK "Pipeline" Mental Model (for images) | |
| # ========================================================================= | |
| # | |
| # VTK is built around a pipeline architecture: | |
| # | |
| # Source → Filter(s) → Mapper → Actor → Renderer → RenderWindow → Interactor | |
| # | |
| # - Source: | |
| # Produces data (e.g., reads an image file and outputs vtkImageData). | |
| # - Filter(s): | |
| # Transforms/processes the data (e.g., flip, window/level, cast, resample). | |
| # - Mapper: | |
| # Prepares the (processed) data for rendering. Think "how to draw this data". | |
| # - Actor: | |
| # A renderable object placed into a scene. It references a Mapper and has | |
| # "appearance" settings (interpolation, opacity, etc). | |
| # - Renderer: | |
| # The scene manager. It contains Actors and defines camera + background. | |
| # - RenderWindow: | |
| # The actual OpenGL window that displays the rendered scene. | |
| # - Interactor: | |
| # Handles mouse/keyboard input and triggers renders; an interactor style | |
| # defines how interactions behave (pan/zoom for images, trackball for 3D, ...). | |
| # | |
| # IMPORTANT: | |
| # - "Data pipeline" is mainly Source → Filters → Mapper. | |
| # - "Rendering pipeline" is Actor → Renderer → RenderWindow. | |
| # - Interactor sits on top to provide user interaction. | |
| # | |
| # For 2D image viewing, a common explicit pair is: | |
| # vtkImageSliceMapper (Mapper) + vtkImageSlice (Actor) | |
| # | |
| # ========================================================================= | |
| # ------------------------------------------------------------------------- | |
| # Qt ↔ VTK bridge widget | |
| # ------------------------------------------------------------------------- | |
| # QVTKRenderWindowInteractor is a Qt widget that contains a VTK RenderWindow | |
| # and an Interactor. This is how we embed VTK rendering inside PySide6. | |
| self.vtk_widget = QVTKRenderWindowInteractor() | |
| self.setCentralWidget(self.vtk_widget) | |
| # ------------------------------------------------------------------------- | |
| # 1) Source: Image Reader | |
| # ------------------------------------------------------------------------- | |
| # vtkImageReader2Factory chooses an appropriate reader based on file extension | |
| # (PNG/JPG/BMP/TIFF, etc.) and produces vtkImageData at its output port. | |
| reader = vtk.vtkImageReader2Factory.CreateImageReader2(IMAGE_PATH) | |
| if reader is None: | |
| raise RuntimeError(f"VTK could not find an image reader for: {IMAGE_PATH}") | |
| reader.SetFileName(IMAGE_PATH) | |
| # Update() forces the reader to actually load the file now. | |
| # (VTK can also be lazy, but Update() is clearer for learning.) | |
| reader.Update() | |
| # ------------------------------------------------------------------------- | |
| # 2) Filter: Flip image (optional processing stage) | |
| # ------------------------------------------------------------------------- | |
| # Filters take an input connection (another algorithm's output port), | |
| # apply a transformation, and provide a new output port. | |
| # | |
| # Here we flip along axis 0 (X axis). If your image looks mirrored or upside-down, | |
| # flipping can correct orientation. For vertical flip you would typically flip axis 1. | |
| flip = vtk.vtkImageFlip() | |
| flip.SetInputConnection(reader.GetOutputPort()) # connect Source → Filter | |
| flip.SetFilteredAxis(0) # 0 = X, 1 = Y, 2 = Z | |
| flip.Update() | |
| # ------------------------------------------------------------------------- | |
| # 3) Mapper: Convert image data into renderable primitives | |
| # ------------------------------------------------------------------------- | |
| # vtkImageSliceMapper takes image data and prepares it for rendering as a 2D slice. | |
| # Even for a single 2D image, using ImageSliceMapper is nice because it makes the | |
| # pipeline explicit (Mapper stage is visible). | |
| mapper = vtk.vtkImageSliceMapper() | |
| mapper.SetInputConnection(flip.GetOutputPort()) # connect Filter → Mapper | |
| # ------------------------------------------------------------------------- | |
| # 4) Actor: A scene object with appearance properties | |
| # ------------------------------------------------------------------------- | |
| # vtkImageSlice is the renderable scene object. It references the mapper | |
| # and exposes properties like interpolation and opacity. | |
| actor = vtk.vtkImageSlice() | |
| actor.SetMapper(mapper) # connect Mapper → Actor | |
| # Nearest-neighbor interpolation means "draw pixels sharply". | |
| # Useful for medical images and pixel-accurate inspection. | |
| # (Linear interpolation can look smoother but may blur edges.) | |
| actor.GetProperty().SetInterpolationTypeToNearest() | |
| # ------------------------------------------------------------------------- | |
| # 5) Renderer: The scene (camera, background, actors) | |
| # ------------------------------------------------------------------------- | |
| renderer = vtk.vtkRenderer() | |
| renderer.AddViewProp(actor) # add Actor to the scene | |
| renderer.SetBackground(0.1, 0.1, 0.1) | |
| # ------------------------------------------------------------------------- | |
| # 6) RenderWindow: Where rendering happens | |
| # ------------------------------------------------------------------------- | |
| # QVTKRenderWindowInteractor already owns a RenderWindow internally. | |
| # We retrieve it and attach our renderer to it. | |
| render_window = self.vtk_widget.GetRenderWindow() | |
| render_window.AddRenderer(renderer) | |
| # ------------------------------------------------------------------------- | |
| # 7) Interactor (+ Style): User interaction (mouse/keyboard) | |
| # ------------------------------------------------------------------------- | |
| # The interactor handles input events; an interactor style defines behavior. | |
| # vtkInteractorStyleImage provides image-friendly interactions (pan/zoom). | |
| interactor = render_window.GetInteractor() | |
| interactor.SetInteractorStyle(vtk.vtkInteractorStyleImage()) | |
| # ------------------------------------------------------------------------- | |
| # Final touches: camera + initial render | |
| # ------------------------------------------------------------------------- | |
| # ResetCamera() positions the camera so the full image is visible. | |
| renderer.ResetCamera() | |
| render_window.Render() | |
| # IMPORTANT: | |
| # Initialize the Qt/VTK interactor AFTER the pipeline and renderer are set up. | |
| self.vtk_widget.Initialize() | |
| if __name__ == "__main__": | |
| app = QApplication(sys.argv) | |
| win = MainWindow() | |
| win.resize(900, 700) | |
| win.show() | |
| sys.exit(app.exec()) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Virtual environment:
Test Image: