Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rendering point clouds #7646

Open
dzenanz opened this issue Mar 19, 2024 · 3 comments
Open

Support rendering point clouds #7646

dzenanz opened this issue Mar 19, 2024 · 3 comments
Labels
Type: Enhancement Improvement to functionality

Comments

@dzenanz
Copy link
Member

dzenanz commented Mar 19, 2024

Is your feature request related to a problem? Please describe.

Loading a .ply file as a model works, but nothing is rendered.

Describe the solution you'd like

When a model has points, bot no cells (vertices, lines, polygons, tetrahedra), some implicit vertices (one per point) should be assumed, so that a point cloud is rendered. Ideally, there would be a button in the user interface to toggle this on/off.

Describe alternatives you've considered

As a workaround, I have come up with a function below, based on this code. It works, but it is pretty hacky and substantially slower (due to ~15x increase in number of vertices).

def makePointCloudVisible(pointCloudName, pointRadius=0.02):
    """
    Render spheres for points
    It takes name of a node of the point cloud (e.g. file.ply drag-dropped from disk)
    optimal pointRadius depends on the density of the point cloud
    """
    pc = slicer.util.getNode(pointCloudName)
    polydata = pc.GetPolyData()

    sphere = vtk.vtkSphereSource()
    sphere.SetThetaResolution(5)
    sphere.SetPhiResolution(5)
    sphere.SetRadius(pointRadius)

    glyph = vtk.vtkGlyph3D()
    glyph.SetInputData(polydata)
    glyph.SetSourceConnection(sphere.GetOutputPort())
    glyph.SetScaleModeToDataScalingOff ()
    pointCloudModelNode = slicer.modules.models.logic().AddModel(glyph.GetOutputPort())

Additional context

samplePointCloud.zip

makePointCloudVisible("samplePointCloud", 5.0)
@dzenanz dzenanz added the Type: Enhancement Improvement to functionality label Mar 19, 2024
@RafaelPalomar
Copy link
Contributor

RafaelPalomar commented Mar 30, 2024

@dzenanz, in my view there are two aspects to consider in this issue:

1) Representing clouds of points

You are correct. When a model does not have cells, it won't be rendered. This happens even if you try to set the representation to Points in the Models module. In addition, and as you point out, gliph rendering of a cloud of points is not efficient as it creates many more points (and cells) to render. However you can still avoid the use of glyph representation by setting a single vertex topology and setting the representation to Points in the Models module. Here is a simple function that will add the vertex topology to a model (I add a remove topology function, in case anyone would like to try from a model which already has topology):

def addSingleVertexTopology(modelNodeName):
    # Get the model node by name
    modelNode = slicer.util.getNode(modelNodeName)
    if not modelNode:
        print("Model node not found.")
        return

    # Access the polydata of the model
    polyData = modelNode.GetPolyData()
    if not polyData:
        print("No polydata found in the model node.")
        return

    # Ensure there are points to work with
    if polyData.GetNumberOfPoints() == 0:
        print("The model has no points.")
        return

    # Create a vtkPolyVertex that includes all points
    polyVertex = vtk.vtkPolyVertex()
    polyVertex.GetPointIds().SetNumberOfIds(polyData.GetNumberOfPoints())
    for i in range(polyData.GetNumberOfPoints()):
        polyVertex.GetPointIds().SetId(i, i)

    # Create a new vtkCellArray and add the single polyVertex to it
    vertices = vtk.vtkCellArray()
    vertices.InsertNextCell(polyVertex)

    # Update the verts in polyData to only include the single polyVertex
    polyData.SetVerts(vertices)

    # Notify the model node and its display node, if any, to update
    modelNode.Modified()
    displayNode = modelNode.GetDisplayNode()
    if displayNode:
        displayNode.Modified()

def removeCellsFromModelNode(modelNodeName):
    # Get the model node by name
    modelNode = slicer.util.getNode(modelNodeName)
    if modelNode is None:
        print("Model node not found.")
        return

    # Get the polydata from the model node
    polyData = modelNode.GetPolyData()
    if polyData is None:
        print("No polydata found in the model node.")
        return

    # Create a new vtkPolyData object without cells
    newPolyData = vtk.vtkPolyData()
    newPolyData.SetPoints(polyData.GetPoints())  # Copy points to the new polydata

    # Update the model node with the new polydata
    modelNode.SetAndObservePolyData(newPolyData)
    print("All cells have been removed from the model.")

2 Utilities to extract/create clouds of points

Maybe we could add a new Extract vertices to the Surface Toolbox module to do exactly this? @pieper, @lassoan what do you think? Maybe the new function could be called Extract and have the sub-options to extract edges and/or points?

@cpinter
Copy link
Member

cpinter commented Apr 1, 2024

I think point cloud rendering worked in the past and it stopped working some time ago. I was able to visualize points without cell data, see this screenshot for example:

image

Just commenting this in case this feature can be restored more easily than finding new solutions.

@dzenanz
Copy link
Member Author

dzenanz commented Apr 1, 2024

@RafaelPalomar I forgot to share another function I created (a poor-man's version of your):

def addVerticesToPolyData(pointCloudName):
    """Add vertices to specified polydata to make the point cloud visible"""
    pc = slicer.util.getNode(pointCloudName)
    polydata = pc.GetPolyData()

    vertices = vtk.vtkCellArray()
    cell_ids = [0]

    for i in range(polydata.GetPoints().GetNumberOfPoints()):
        cell_ids[0] = i
        vertices.InsertNextCell(1, cell_ids)

    polydata.SetVerts(vertices)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Enhancement Improvement to functionality
Development

No branches or pull requests

3 participants