How to include markup annotations in high-resolution screen capture

oothomas
Mon, 29 Apr 2024 18:13:50 +0000
Development

Hello Slicer Community,

I am currently developing an extension for high-resolution screen captures within Slicer. My goal is to programmatically capture images of a reconstructed scene that include markup annotations such as landmark names, angles, and distance measurements. However, I’ve encountered an issue where these markup labels do not appear in the off-screen rendering used for capturing the scene. My research points to the existence of a vtkMRMLDisplayableManagerGroup object that should be associated with my threeDView. This group should contain the displayable managers responsible for rendering the markup labels. How would I go about accessing this object so I can incorporate these markup annotations into the newly rendered scene?

Below is the core snippet of my screen capture logic:

    def runScreenCapture(self) -> None:
        if self.resolution and self.outputPath:
            vtk.vtkGraphicsFactory()
            gf = vtk.vtkGraphicsFactory()
            gf.SetOffScreenOnlyMode(1)
            gf.SetUseMesaClasses(1)
            rw = vtk.vtkRenderWindow()
            rw.SetOffScreenRendering(1)
            ren = vtk.vtkRenderer()
            rw.SetSize(self.resolution[0], self.resolution[1])

            lm = slicer.app.layoutManager()

            threeDViewWidget = lm.threeDWidget(self.threeDViewIndex)
            threeDView = threeDViewWidget.threeDView()

            renderers = threeDView.renderWindow().GetRenderers()
            ren3d = renderers.GetFirstRenderer()

            # Set the background color of the off-screen renderer to match the original
            backgroundColor = ren3d.GetBackground()
            ren.SetBackground(backgroundColor)

            camera = ren3d.GetActiveCamera()

            while ren3d:

                actors = ren3d.GetActors()
                for index in range(actors.GetNumberOfItems()):
                    actor = actors.GetItemAsObject(index)

                    actor_class_name = actor.GetClassName()  # Get the class name using VTK's method
                    # Alternatively, use Python's type function: actor_type = type(actor).__name__
                    print("Actor index:", index, "Class name:", actor_class_name)

                    property = actor.GetProperty()
                    # print("Actor Property:", property)
                    representation = property.GetRepresentation()

                    # vtkProperty defines three representation types:
                    # vtkProperty.VTK_POINTS, vtkProperty.VTK_WIREFRAME, vtkProperty.VTK_SURFACE
                    if representation == vtk.VTK_POINTS:
                        print("Actor index:", index, "is represented as points.")
                    elif representation == vtk.VTK_WIREFRAME:
                        print("Actor index:", index, "is represented as wireframe.")
                    elif representation == vtk.VTK_SURFACE:
                        print("Actor index:", index, "is represented as a surface.")
                    else:
                        print("Actor index:", index, "has an unknown representation.")

                    print("Actor index:", index, "Visibility -", actor.GetVisibility(), "|", isActorVisible(camera, actor))
                    if actor.GetVisibility():  # and isActorVisible(camera, actor):
                        ren.AddActor(actor)  # Add only visible actors

                lights = ren3d.GetLights()
                for index in range(lights.GetNumberOfItems()):
                    ren.AddLight(lights.GetItemAsObject(index))

                volumes = ren3d.GetVolumes()
                for index in range(volumes.GetNumberOfItems()):
                    ren.AddVolume(volumes.GetItemAsObject(index))

                ren3d = renderers.GetNextItem()

            ren.SetActiveCamera(camera)

            rw.AddRenderer(ren)
            rw.Render()

            wti = vtk.vtkWindowToImageFilter()
            wti.SetInput(rw)
            wti.Update()
            writer = vtk.vtkPNGWriter()
            writer.SetInputConnection(wti.GetOutputPort())
            writer.SetFileName(self.outputPath)
            writer.Update()
            writer.Write()
            i = wti.GetOutput()

25 posts - 6 participants