Point clouds often contain measurements that lie inside the region formed by their outermost samples. A convex hull reduces that cloud to its smallest convex boundary, which makes the perimeter and enclosed area available for later geometry work.
SciPy's scipy.spatial.ConvexHull accepts a two-dimensional NumPy array and delegates hull construction to Qhull. The returned vertices indexes preserve counterclockwise order for 2-D input, so indexing the original array produces an ordered polygon boundary.
Two interior samples are included to show that they do not become boundary vertices. For a 2-D hull, SciPy reports the perimeter through hull.area and the polygon area through hull.volume; a shoelace calculation independently cross-checks the area.
$ cat > convex_hull_demo.py <<'PY'
import numpy as np
from scipy.spatial import ConvexHull
points = np.array([
[0.0, 0.0],
[2.0, 0.0],
[2.0, 1.5],
[0.0, 1.0],
[0.8, 0.5],
[1.2, 0.9],
])
PY
The first four points form the outer quadrilateral, while the last two points lie inside it.
$ cat >> convex_hull_demo.py <<'PY' hull = ConvexHull(points) boundary = points[hull.vertices] PY
$ cat >> convex_hull_demo.py <<'PY'
print("boundary vertex indexes:", hull.vertices.tolist())
print("boundary coordinates:")
for index, coordinates in zip(hull.vertices, boundary):
print(f" {index}: ({coordinates[0]:.1f}, {coordinates[1]:.1f})")
print(f"perimeter: {hull.area:.3f}")
print(f"area: {hull.volume:.3f}")
PY
The last two points sit inside the quadrilateral, so they should not appear in hull.vertices.
$ cat >> convex_hull_demo.py <<'PY'
next_boundary = np.roll(boundary, -1, axis=0)
shoelace_area = 0.5 * abs(
np.sum(boundary[:, 0] * next_boundary[:, 1])
- np.sum(boundary[:, 1] * next_boundary[:, 0])
)
np.testing.assert_allclose(hull.volume, shoelace_area)
print(f"shoelace area check: {shoelace_area:.3f}")
PY
For 2-D input, hull.area is the perimeter and hull.volume is the polygon area. In higher dimensions, the same attributes mean surface area and volume.
$ python3 convex_hull_demo.py boundary vertex indexes: [0, 1, 2, 3] boundary coordinates: 0: (0.0, 0.0) 1: (2.0, 0.0) 2: (2.0, 1.5) 3: (0.0, 1.0) perimeter: 6.562 area: 2.500 shoelace area check: 2.500