Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

3D convex hull from point cloud

Tags:

python

plot

3d

I need to plot a 3D point cloud (number of points: N), then a convex hull (actually a polyhedron with N vertices) from the points. I made a script in python with scipy.spatial ConvexHull for plot 8 points and plot a cube, the plot of the point cloud is ok, but the cube is not ok, because the code puts two lines going across the diagonal face of the cube in addition to the edge lines. I don't understand why plot lines across faces.

The script:

import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull  

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

points= np.array([[0,0,0],
            [4,0,0],
            [4,4,0],
            [0,4,0],
            [0,0,4],
            [4,0,4],
            [4,4,4],
            [0,4,4]])

hull=ConvexHull(points)

edges= zip(*points)

for i in hull.simplices:
    plt.plot(points[i,0], points[i,1], points[i,2], 'r-')

ax.plot(edges[0],edges[1],edges[2],'bo') 

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

ax.set_xlim3d(-5,5)
ax.set_ylim3d(-5,5)
ax.set_zlim3d(-5,5)

plt.show()

Result of the script:

enter image description here

like image 321
Feri Avatar asked Dec 03 '14 11:12

Feri


People also ask

What is convex hull point cloud?

The convex hull of a point cloud is the smallest convex set that contains all points. Open3D contains the method compute_convex_hull that computes the convex hull of a point cloud. The implementation is based on Qhull.

What is a 3d convex hull?

For a finite set of points, the convex hull is a convex polyhedron in three dimensions, or in general a convex polytope for any number of dimensions, whose vertices are some of the points in the input set. Its representation is not so simple as in the planar case, however.


2 Answers

I know this is old, but I came here from Google so I think others might too.

The problem is only in the plotting method you use. One simplex is a nD triangle defined by 3 points. But the plotting function must cycle back to the last point, otherwise only 2 of 3 simplex edges are drawn.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial import ConvexHull


# 8 points defining the cube corners
pts = np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0],
                [0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1], ])

hull = ConvexHull(pts)

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")

# Plot defining corner points
ax.plot(pts.T[0], pts.T[1], pts.T[2], "ko")

# 12 = 2 * 6 faces are the simplices (2 simplices per square face)
for s in hull.simplices:
    s = np.append(s, s[0])  # Here we cycle back to the first coordinate
    ax.plot(pts[s, 0], pts[s, 1], pts[s, 2], "r-")

# Make axis label
for i in ["x", "y", "z"]:
    eval("ax.set_{:s}label('{:s}')".format(i, i))

plt.show()

image

like image 157
Thorben Menne Avatar answered Oct 17 '22 21:10

Thorben Menne


As I understand, you want the edges only (so I don't understand Thorben's answer).

To get a convex hull and its edges, use pycddlib (the cdd library):

# -*- coding: utf-8 -*-
import numpy as np
import cdd as pcdd
import matplotlib.pyplot as plt

points= np.array([
    [0,0,0],
    [4,0,0],
    [4,4,0],
    [0,4,0],
    [0,0,4],
    [4,0,4],
    [4,4,4],
    [0,4,4]
])

# to get the convex hull with cdd, one has to prepend a column of ones
vertices = np.hstack((np.ones((8,1)), points))

# do the polyhedron
mat = pcdd.Matrix(vertices, linear=False, number_type="fraction") 
mat.rep_type = pcdd.RepType.GENERATOR
poly = pcdd.Polyhedron(mat)

# get the adjacent vertices of each vertex
adjacencies = [list(x) for x in poly.get_input_adjacency()]

# store the edges in a matrix (giving the indices of the points)
edges = [None]*(8-1)
for i,indices in enumerate(adjacencies[:-1]):
    indices = list(filter(lambda x: x>i, indices))
    l = len(indices)
    col1 = np.full((l, 1), i)
    indices = np.reshape(indices, (l, 1))
    edges[i] = np.hstack((col1, indices))
Edges = np.vstack(tuple(edges))

# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")

start = points[Edges[:,0]]
end = points[Edges[:,1]]

for i in range(12):
    ax.plot(
        [start[i,0], end[i,0]], 
        [start[i,1], end[i,1]], 
        [start[i,2], end[i,2]],
        "blue"
    )

ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")

ax.set_xlim3d(-1,5)
ax.set_ylim3d(-1,5)
ax.set_zlim3d(-1,5)

plt.show()

enter image description here

like image 1
Stéphane Laurent Avatar answered Oct 17 '22 20:10

Stéphane Laurent