1616from .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 :
0 commit comments