Skip to content

Commit e877ee2

Browse files
committed
registration cleanup
1 parent 2105e3d commit e877ee2

File tree

3 files changed

+53
-39
lines changed

3 files changed

+53
-39
lines changed

tests/test_registration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def test_mesh(self):
100100
truth_to_points, cost = truth.register(points)
101101
truth.apply_transform(truth_to_points)
102102
distance = truth.nearest.on_surface(points)[1]
103+
103104
assert distance.mean() < noise
104105

105106

trimesh/registration.py

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,12 @@
1616
from .transformations import transform_points
1717

1818

19-
def key_points(mesh, count):
20-
"""
21-
Return a combination of mesh vertices and surface samples
22-
with vertices chosen by likelyhood to be important to registation
23-
"""
24-
stack = []
25-
if len(mesh.vertices) < (count / 2):
26-
return np.vstack((
27-
mesh.vertices,
28-
mesh.sample(count - len(mesh.vertices))))
29-
else:
30-
return mesh.sample(count)
31-
32-
33-
def mesh_other(mesh, other, samples=500, icp_first=10, icp_final=50):
19+
def mesh_other(mesh,
20+
other,
21+
samples=500,
22+
scale=False,
23+
icp_first=10,
24+
icp_final=50):
3425
"""
3526
Align a mesh with another mesh or a PointCloud using
3627
the principal axes of inertia as a starting point which
@@ -44,6 +35,8 @@ def mesh_other(mesh, other, samples=500, icp_first=10, icp_final=50):
4435
Mesh or points in space
4536
samples : int
4637
Number of samples from mesh surface to align
38+
scale : bool
39+
Allow scaling in transform
4740
icp_first : int
4841
How many ICP iterations for the 9 possible
4942
combinations of
@@ -59,6 +52,19 @@ def mesh_other(mesh, other, samples=500, icp_first=10, icp_final=50):
5952
Average squared distance per point
6053
"""
6154

55+
def key_points(m, count):
56+
"""
57+
Return a combination of mesh vertices and surface samples
58+
with vertices chosen by likelyhood to be important
59+
to registation.
60+
"""
61+
if len(m.vertices) < (count / 2):
62+
return np.vstack((
63+
m.vertices,
64+
m.sample(count - len(m.vertices))))
65+
else:
66+
return m.sample(count)
67+
6268
if not util.is_instance_named(mesh, 'Trimesh'):
6369
raise ValueError('mesh must be Trimesh object!')
6470

@@ -67,15 +73,15 @@ def mesh_other(mesh, other, samples=500, icp_first=10, icp_final=50):
6773
# if both are meshes use the smaller one for searching
6874
if util.is_instance_named(other, 'Trimesh'):
6975
if len(mesh.vertices) > len(other.vertices):
76+
# do the expensive tree construction on the
77+
# smaller mesh and query the others points
7078
search = other
7179
inverse = False
72-
points = key_points(mesh=mesh,
73-
count=samples)
80+
points = key_points(m=mesh, count=samples)
7481
points_mesh = mesh
7582
else:
7683
points_mesh = other
77-
points = key_points(mesh=other,
78-
count=samples)
84+
points = key_points(m=other, count=samples)
7985

8086
if points_mesh.is_volume:
8187
points_PIT = points_mesh.principal_inertia_transform
@@ -89,13 +95,18 @@ def mesh_other(mesh, other, samples=500, icp_first=10, icp_final=50):
8995
else:
9096
raise ValueError('other must be mesh or (n, 3) points!')
9197

98+
# get the transform that aligns the search mesh principal
99+
# axes of inertia with the XYZ axis at the origin
92100
if search.is_volume:
93101
search_PIT = search.principal_inertia_transform
94102
else:
95103
search_PIT = search.bounding_box_oriented.principal_inertia_transform
96104

97-
# move from mesh a to mesh b
98-
search_to_points = np.dot(np.linalg.inv(points_PIT), search_PIT)
105+
# transform that moves the principal axes of inertia
106+
# of the search mesh to be aligned with the best- guess
107+
# principal axes of the points
108+
search_to_points = np.dot(np.linalg.inv(points_PIT),
109+
search_PIT)
99110

100111
# permutations of cube rotations
101112
# the principal inertia transform has arbitrary sign
@@ -111,40 +122,42 @@ def mesh_other(mesh, other, samples=500, icp_first=10, icp_final=50):
111122
[1, -1, -1],
112123
[-1, -1, -1]]])
113124

114-
#from IPython import embed
115-
# embed()
116-
117-
# loop through permutations and run iterative closest point on each
118-
costs, transforms = [], []
125+
# loop through permutations and run iterative closest point
126+
costs = np.ones(len(cubes)) * np.inf
127+
transforms = [None] * len(cubes)
119128
centroid = search.centroid
120-
for flip in cubes:
121-
a_to_b = np.dot(transformations.transform_around(flip, centroid),
122-
np.linalg.inv(search_to_points))
123129

124-
# import trimesh
125-
# vpt = trimesh.PointCloud(points)
126-
# vpt.apply_transform(a_to_b)
127-
# trimesh.Scene([search, vpt]).show()
130+
for i, flip in enumerate(cubes):
131+
# transform from points to search mesh
132+
# flipped around the centroid of search
133+
a_to_b = np.dot(
134+
transformations.transform_around(flip, centroid),
135+
np.linalg.inv(search_to_points))
128136

129137
# run first pass ICP
130138
matrix, junk, cost = icp(a=points,
131139
b=search,
132140
initial=a_to_b,
133141
max_iterations=int(icp_first),
134-
scale=False)
135-
transforms.append(matrix)
136-
costs.append(cost)
142+
scale=scale)
143+
144+
# save transform and costs from ICP
145+
transforms[i] = matrix
146+
costs[i] = cost
137147

138148
# run a final ICP refinement step
139149
matrix, junk, cost = icp(a=points,
140150
b=search,
141151
initial=transforms[np.argmin(costs)],
142152
max_iterations=int(icp_final),
143-
scale=False)
153+
scale=scale)
144154

145-
# convert square sum distance to squared average distance
155+
# convert to per- point distance average
146156
cost /= len(points)
147157

158+
# we picked the smaller mesh to construct the tree
159+
# on so we may have calculated a transform backwards
160+
# to save computation, so just invert matrix here
148161
if inverse:
149162
mesh_to_other = np.linalg.inv(matrix)
150163
else:

trimesh/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.33.39'
1+
__version__ = '2.33.40'

0 commit comments

Comments
 (0)