Shape Signatures#
Pose-invariant shape representations for comparing and classifying 3D shapes based on statistical shape distributions.
Note
Prerequisites: Basic understanding of 3D meshes and shape geometry
Shape Signatures Module#
The Shape Signatures module provides shape distribution signatures for comparing and classifying 3D shapes. It implements the methods described in the paper “Shape Distributions” by Osada et al. (2002).
Overview#
Shape signatures are compact representations of 3D geometry that enable:
Shape comparison: Measure similarity between two shapes
Shape classification: Group similar shapes together
Shape retrieval: Find shapes similar to a query shape
Pose-invariant matching: Compare shapes regardless of position/orientation
The key insight is that certain geometric measurements (distances, angles, areas) sampled from a shape’s surface form characteristic distributions that are:
Rotation invariant: Same distribution regardless of orientation
Translation invariant: Same distribution regardless of position
Scale normalizable: Can be normalized for scale independence
Signature Types#
The module implements five signature types from the Osada et al. paper:
Signature |
Measurement |
Description |
|---|---|---|
|
Angle |
Angle formed by three random surface points |
|
Distance |
Distance from centroid to random surface point |
|
Distance |
Distance between two random surface points |
|
Area |
Square root of triangle area (three random points) |
|
Volume |
Cube root of tetrahedron volume (four random points) |
Choosing a Signature Type#
D2Signature: Best general-purpose choice, good discrimination
A3Signature: Good for shapes with distinctive angular features
D1Signature: Simple, fast, but less discriminative
D3Signature: Good for surface-dominated shapes
D4Signature: Best for volumetric shapes, but requires more points
Class: Signature#
Description#
Base class for all shape signatures. Subclasses implement specific measurement methods.
Constructor#
from volmdlr_tools.shapes.utils.signature import D2Signature
signature = D2Signature(
shape_distribution: list[float],
name: str = ""
)
Parameters:
shape_distribution: Pre-computed distribution valuesname: Instance name
Creating Signatures#
From a VolumeModel#
from volmdlr.model import VolumeModel
from volmdlr_tools.shapes.utils.signature import D2Signature
# Load shape
volume_model = VolumeModel.from_step("part.step")
# Create signature
signature = D2Signature.from_volume_model(
volume_model,
meshing_tolerance=0.001,
meshing_angular_tolerance=0.5,
n_points=1024**2 # ~1 million sample points
)
From a Mesh#
from volmdlr_tools.shapes.utils.signature import D2Signature
# Create signature from existing mesh
signature = D2Signature.from_mesh(
mesh=mesh,
n_points=1024**2
)
From Point Cloud#
import numpy as np
from volmdlr_tools.shapes.utils.signature import D2Signature
# Create signature from point cloud
points = np.array([...]) # Nx3 array of surface points
signature = D2Signature.from_points(points)
Computing Similarity#
Basic Similarity#
# Create signatures for two shapes
sig1 = D2Signature.from_volume_model(model1)
sig2 = D2Signature.from_volume_model(model2)
# Compute similarity (0 to 1, higher = more similar)
similarity = sig1.similarity(sig2, n_bins=1024)
print(f"Similarity: {similarity:.4f}")
Histogram Bins#
The n_bins parameter controls histogram resolution:
# Higher bins = more precise but sensitive to noise
high_res_similarity = sig1.similarity(sig2, n_bins=2048)
# Lower bins = more robust but less discriminative
low_res_similarity = sig1.similarity(sig2, n_bins=256)
Normalization#
For scale-independent comparison:
# Normalize the signature (subtract mean)
normalized_sig = signature.normalization()
# Compare normalized signatures
similarity = normalized_sig1.similarity(normalized_sig2)
Visualization#
Shape Distribution Graph#
# Display the shape distribution histogram
graph = signature.graph(n_bins=1024)
# Or use plot_data view decorator
signature.plot() # Opens interactive plot
Complete Example#
from pathlib import Path
from volmdlr.model import VolumeModel
from volmdlr_tools.shapes.utils.signature import D2Signature, A3Signature
# Load multiple shapes
shapes_folder = Path("data/step/")
shape_files = list(shapes_folder.glob("*.step"))
# Compute signatures for all shapes
signatures = {}
for shape_file in shape_files:
model = VolumeModel.from_step(str(shape_file))
sig = D2Signature.from_volume_model(model, n_points=100000)
signatures[shape_file.stem] = sig
# Find similar shapes
query_name = "bolt_m6"
query_sig = signatures[query_name]
print(f"Shapes similar to {query_name}:")
for name, sig in signatures.items():
if name != query_name:
similarity = query_sig.similarity(sig)
if similarity > 0.8:
print(f" {name}: {similarity:.4f}")
Signature Classes#
A3Signature - Angle Distribution#
Measures angles formed by triplets of random surface points.
from volmdlr_tools.shapes.utils.signature import A3Signature
sig = A3Signature.from_volume_model(model)
Best for: Shapes with distinctive angular features (corners, edges)
D1Signature - Centroid Distance#
Measures distance from the shape centroid to random surface points.
from volmdlr_tools.shapes.utils.signature import D1Signature
sig = D1Signature.from_volume_model(model)
Best for: Simple, fast comparison; shapes with distinctive radial profiles
D2Signature - Point-to-Point Distance#
Measures distance between pairs of random surface points.
from volmdlr_tools.shapes.utils.signature import D2Signature
sig = D2Signature.from_volume_model(model)
Best for: General-purpose shape comparison; most commonly used
D3Signature - Triangle Area#
Measures square root of triangle areas formed by triplets of random points.
from volmdlr_tools.shapes.utils.signature import D3Signature
sig = D3Signature.from_volume_model(model)
Best for: Shapes where surface area distribution is distinctive
D4Signature - Tetrahedron Volume#
Measures cube root of tetrahedron volumes formed by quadruplets of random points.
from volmdlr_tools.shapes.utils.signature import D4Signature
sig = D4Signature.from_volume_model(model)
Best for: Volumetric shapes; requires more sample points for accuracy
Performance Considerations#
Sample Points#
More points = better accuracy but slower computation:
# Fast, lower accuracy (good for initial filtering)
sig_fast = D2Signature.from_volume_model(model, n_points=10000)
# Balanced (good for most use cases)
sig_balanced = D2Signature.from_volume_model(model, n_points=100000)
# High accuracy (for final comparison)
sig_accurate = D2Signature.from_volume_model(model, n_points=1000000)
Caching#
Signatures cache their histogram computations:
# First call computes and caches
bins_1024 = sig.get_bins(n_bins=1024)
# Subsequent calls with same n_bins use cache
bins_1024_again = sig.get_bins(n_bins=1024) # Fast, uses cache
Meshing Parameters#
Control mesh quality for signature computation:
sig = D2Signature.from_volume_model(
model,
meshing_tolerance=0.001, # Linear tolerance
meshing_angular_tolerance=0.5, # Angular tolerance in degrees
n_points=100000
)
Use Cases#
Shape Classification#
# Define reference signatures for each class
class_signatures = {
"bolt": D2Signature.from_volume_model(bolt_model),
"nut": D2Signature.from_volume_model(nut_model),
"washer": D2Signature.from_volume_model(washer_model),
}
# Classify unknown shape
unknown_sig = D2Signature.from_volume_model(unknown_model)
best_match = None
best_similarity = 0
for class_name, ref_sig in class_signatures.items():
similarity = unknown_sig.similarity(ref_sig)
if similarity > best_similarity:
best_similarity = similarity
best_match = class_name
print(f"Shape classified as: {best_match} (similarity: {best_similarity:.4f})")
Duplicate Detection#
# Find duplicate shapes in a collection
duplicates = []
for i, (name1, sig1) in enumerate(signatures.items()):
for name2, sig2 in list(signatures.items())[i+1:]:
if sig1.similarity(sig2) > 0.95:
duplicates.append((name1, name2))
Shape Retrieval#
# Find top-N most similar shapes to a query
def find_similar(query_sig, database_sigs, top_n=5):
similarities = [
(name, query_sig.similarity(sig))
for name, sig in database_sigs.items()
]
similarities.sort(key=lambda x: x[1], reverse=True)
return similarities[:top_n]
results = find_similar(query_signature, shape_database, top_n=10)
Reference#
The implementation is based on:
Osada, R., Funkhouser, T., Chazelle, B., & Dobkin, D. (2002). Shape distributions. ACM Transactions on Graphics, 21(4), 807-832. http://graphics.stanford.edu/courses/cs468-08-fall/pdf/osada.pdf
See Also#
Sheet Metal Shapes - Sheet metal shape classification
Swept Shapes - Swept shape recognition
Pattern Matching - Pattern matching in assemblies