-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathICP_finite.m
373 lines (320 loc) · 12.9 KB
/
ICP_finite.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
function [Points_Moved,M]=ICP_finite(Points_Static, Points_Moving, Options)
% This function ICP_FINITE is an kind of Iterative Closest Point
% registration algorithm for point clouds (vertice data) using finite
% difference methods.
%
% Normal ICP solves translation and rotation with analytical equations.
% By using finite difference this function can also solve resize and shear.
%
% This function is first sorts the static points into a grid of
% overlapping blocks. The block nearest to a moving point will contain
% its closest static point, thus the grid allows faster registration.
%
% [Points_Moved,M]=ICP_finite(Points_Static, Points_Moving, Options);
%
% inputs,
% Points_Static : An N x 3 array with XYZ points which describe the
% registration target
% Points_Moving : An M x 3 array with XYZ points which will move and
% be registered on the static points.
% Options : A struct with registration options:
% Options.Registration: 'Rigid', Translation and Rotation (default)
% 'Size', Rigid + Resize
% 'Affine', Translation, Rotation, Resize
% and Shear.
% Options.TolX: Registration Position Tollerance, default is the
% largest side of a volume containing the points divided by 1000
% Options.TolP: Allowed tollerance on distance error default
% 0.001 (Range [0 1])
% Options.Optimizer : optimizer used, 'fminlbfgs' (default)
% ,'fminsearch' and 'lsqnonlin'.
% Options.Verbose : if true display registration information (default)
%
% outputs,
% Points_Moved : An M x 3 array with the register moving points
% M : The transformation matrix. Can be used with function movepoints
% to transform other arrays with 3D points.
%
% example,
% % Make Static Points
% npoinst=10000;
% x=rand(npoinst,1)*100-50; y=rand(npoinst,1)*100-50; z=sqrt(x.^2+y.^2);
% Points_Static=[x y z];
%
% % Make Moving Points
% x=rand(npoinst-100,1)*100-50; y=rand(npoinst-100,1)*100-50; z=sqrt(x.^2+y.^2);
% Points_Moving=[x y z];
% M=[1.4 -0.1710 0.1736 10.0000; 0.1795 0.9832 -0.0344 5.0000; -0.1648 0.0645 0.9842 20.0000; 0 0 0 1.0000]
% Points_Moving=movepoints(M,Points_Moving);
%
% % Register the points
% [Points_Moved,M]=ICP_finite(Points_Static, Points_Moving, struct('Registration','Size'));
%
% % Show start
% figure, hold on;
% plot3(Points_Static(:,1),Points_Static(:,2),Points_Static(:,3),'b*');
% plot3(Points_Moving(:,1),Points_Moving(:,2),Points_Moving(:,3),'m*');
% view(3);
% % Show result
% figure, hold on;
% plot3(Points_Static(:,1),Points_Static(:,2),Points_Static(:,3),'b*');
% plot3(Points_Moved(:,1),Points_Moved(:,2),Points_Moved(:,3),'m*');
% view(3);
%
% Function is written by D.Kroon University of Twente (May 2009)
% Display registration process
defaultoptions=struct('Registration','Rigid','TolX',0.001,'TolP',0.001,'Optimizer','lsqnonlin','Verbose', false);
if(~exist('Options','var')),
Options=defaultoptions;
else
tags = fieldnames(defaultoptions);
for i=1:length(tags)
if(~isfield(Options,tags{i})), Options.(tags{i})=defaultoptions.(tags{i}); end
end
if(length(tags)~=length(fieldnames(Options))),
warning('register_images:unknownoption','unknown options found');
end
end
% Process Inputs
if(size(Points_Static,2)~=3),
error('ICP_finite:inputs','Points Static is not a m x 3 matrix');
end
if(size(Points_Moving,2)~=3),
error('ICP_finite:inputs','Points Moving is not a m x 3 matrix');
end
% Inputs array must be double
Points_Static=double(Points_Static);
Points_Moving=double(Points_Moving);
% Make Optimizer name lower case
Options.Optimizer=lower(Options.Optimizer);
% Set initial values depending on registration type
switch (lower(Options.Registration(1)))
case 'r',
if(Options.Verbose), disp('Start Rigid registration'); end% drawnow; end
% Parameter scaling of the Translation and Rotation
scale=[1 1 1 0.01 0.01 0.01];
% Set initial rigid parameters
par=[0 0 0 0 0 0];
case 's',
if(Options.Verbose), disp('Start Affine registration');end % drawnow; end
% Parameter scaling of the Translation, Rotation and Resize
scale=[1 1 1 0.01 0.01 0.01 0.01 0.01 0.01];
% Set initial rigid parameters
par=[0 0 0 0 0 0 100 100 100];
case 'a'
if(Options.Verbose), disp('Start Affine registration'); end% drawnow; end
% Parameter scaling of the Translation, Rotation, Resize and Shear
scale=[1 1 1 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01];
% Set initial rigid parameters
par=[0 0 0 0 0 0 100 100 100 0 0 0 0 0 0];
otherwise
warning('ICP_finite:inputs','unknown registration method');
end
% Distance error in last itteration
fval_old=inf;
% Change in distance error between two itterations
fval_perc=0;
% Array which contains the transformed points
Points_Moved=Points_Moving;
% Number of itterations
itt=0;
% Get the minimum and maximum coordinates of the static points
maxP=max(Points_Static);
minP=min(Points_Static);
Options.TolX=max(maxP-minP)/1000;
% Display information of current itteration
if(Options.Verbose)
s=sprintf(' Itteration Error'); disp(s);
end
% Make a uniform grid of points
% These will be used to sort the points into local groups
% to speed up the distance measurements.
spacing=size(Points_Static,1)^(1/6)*sqrt(3);
spacing_dist=max(maxP(:)-minP(:))/spacing;
xa=minP(1):spacing_dist:maxP(1);
xb=minP(2):spacing_dist:maxP(2);
xc=minP(3):spacing_dist:maxP(3);
[x,y,z]=ndgrid(xa,xb,xc);
Points_Group=[x(:) y(:) z(:)];
% Calculate the radius of a point from the uniform grid.
radius=spacing_dist*sqrt(3);
% Sort the points in to groups
Cell_Group_Static=cell(1,size(Points_Group,1));
for i=1:size(Points_Group,1)
% Calculate distance of an uniform group point to all static points
%distance=sum((Points_Static-repmat(Points_Group(i,:),size(Points_Static,1),1)).^2,2);
%check=(distance<(mult*radius^2));
check=(Points_Static(:,1)>(Points_Group(i,1)-radius))&(Points_Static(:,1)<(Points_Group(i,1)+radius))...
&(Points_Static(:,2)>(Points_Group(i,2)-radius))&(Points_Static(:,2)<(Points_Group(i,2)+radius))...
&(Points_Static(:,3)>(Points_Group(i,3)-radius))&(Points_Static(:,3)<(Points_Group(i,3)+radius));
% Add the closest static points, if none, increase the radius of point
% search
mult=1;
while(isempty(Cell_Group_Static{i}))
Cell_Group_Static{i}=Points_Static(check,:);
% Increase radius
mult=mult+1.5;
check=(Points_Static(:,1)>(Points_Group(i,1)-mult*radius))&(Points_Static(:,1)<(Points_Group(i,1)+mult*radius))...
&(Points_Static(:,2)>(Points_Group(i,2)-mult*radius))&(Points_Static(:,2)<(Points_Group(i,2)+mult*radius))...
&(Points_Static(:,3)>(Points_Group(i,3)-mult*radius))&(Points_Static(:,3)<(Points_Group(i,3)+mult*radius));
end
end
% closest points for all points
Points_Match=zeros(size(Points_Moved));
while(fval_perc<(1-Options.TolP))
itt=itt+1;
% Calculate closest point for all points
for i=1:size(Points_Moved,1)
% Find closest group point
Point=Points_Moved(i,:);
dist=(Points_Group(:,1)-Point(1)).^2+(Points_Group(:,2)-Point(2)).^2+(Points_Group(:,3)-Point(3)).^2;
[mindist,j]=min(dist);
% Find closest point in group
Points_Group_Static=Cell_Group_Static{j};
dist=(Points_Group_Static(:,1)-Point(1)).^2+(Points_Group_Static(:,2)-Point(2)).^2+(Points_Group_Static(:,3)-Point(3)).^2;
[mindist,j]=min(dist);
Points_Match(i,:)=Points_Group_Static(j,:);
end
% Calculate the parameters which minimize the distance error between
% the current closest points
switch(Options.Optimizer)
case 'fminlbfgs'
% Set Registration Tollerance
optim=struct('Display','off','TolX',Options.TolX);
[par,fval]=fminlbfgs(@(par)affine_registration_error(par,scale,Points_Moving,Points_Match),par,optim);
case 'fminsearch'
% Set Registration Tollerance
optim=struct('Display','off','TolX',Options.TolX);
[par,fval]=fminsearch(@(par)affine_registration_error(par,scale,Points_Moving,Points_Match),par,optim);
case 'lsqnonlin'
% Set Registration Tollerance
optim=optimset('Display','off','TolX',Options.TolX,'Algorithm','levenberg-marquardt');
[par,fval]=lsqnonlin(@(par)affine_registration_array(par,scale,Points_Moving,Points_Match),par,[],[],optim);
otherwise
disp('Unknown Optimizer.')
end
% Calculate change in error between itterations
fval_perc=fval/fval_old;
if(Options.Verbose)
s=sprintf(' %5.0f %13.6g ',itt,fval );
disp(s);
end
% Store error value
fval_old=fval;
% Make the transformation matrix
M=getransformation_matrix(par,scale);
% Transform the Points
Points_Moved=movepoints(M,Points_Moving);
end
function [e,egrad]=affine_registration_error(par,scale,Points_Moving,Points_Static)
% Stepsize used for finite differences
delta=1e-8;
% Get current transformation matrix
M=getransformation_matrix(par,scale);
% Calculate distance error
e=calculate_distance_error(M,Points_Moving,Points_Static);
% If asked calculate finite difference error gradient
if(nargout>1)
egrad=zeros(1,length(par));
for i=1:length(par)
par2=par; par2(i)=par(i)+delta;
M=getransformation_matrix(par2,scale);
egrad(i)=calculate_distance_error(M,Points_Moving,Points_Static)/delta;
end
end
function [dist_total]=calculate_distance_error(M,Points_Moving,Points_Static)
% First transform the points with the transformation matrix
Points_Moved=movepoints(M,Points_Moving);
% Calculate the squared distance between the points
dist=sum((Points_Moved-Points_Static).^2,2);
% calculate the total distanse
dist_total=sum(dist);
function [earray]=affine_registration_array(par,scale,Points_Moving,Points_Static)
% Get current transformation matrix
M=getransformation_matrix(par,scale);
% First transform the points with the transformation matrix
Points_Moved=movepoints(M,Points_Moving);
% Calculate the squared distance between the points
%earray=sum((Points_Moved-Points_Static).^2,2);
earray=(Points_Moved-Points_Static);
function Po=movepoints(M,P)
% Transform all xyz points with the transformation matrix
Po=zeros(size(P));
Po(:,1)=P(:,1)*M(1,1)+P(:,2)*M(1,2)+P(:,3)*M(1,3)+M(1,4);
Po(:,2)=P(:,1)*M(2,1)+P(:,2)*M(2,2)+P(:,3)*M(2,3)+M(2,4);
Po(:,3)=P(:,1)*M(3,1)+P(:,2)*M(3,2)+P(:,3)*M(3,3)+M(3,4);
function M=getransformation_matrix(par,scale)
% This function will transform the parameter vector in to a
% a transformation matrix
% Scale the input parameters
par=par.*scale;
switch(length(par))
case 6 % Translation and Rotation
M=make_transformation_matrix(par(1:3),par(4:6));
case 9 % Translation, Rotation and Resize
M=make_transformation_matrix(par(1:3),par(4:6),par(7:9));
case 15 % Translation, Rotation, Resize and Shear
M=make_transformation_matrix(par(1:3),par(4:6),par(7:9),par(10:15));
end
function M=make_transformation_matrix(t,r,s,h)
% This function make_transformation_matrix.m creates an affine
% 2D or 3D transformation matrix from translation, rotation, resize and shear parameters
%
% M=make_transformation_matrix.m(t,r,s,h)
%
% inputs (3D),
% t: vector [translateX translateY translateZ]
% r: vector [rotateX rotateY rotateZ]
% s: vector [resizeX resizeY resizeZ]
% h: vector [ShearXY, ShearXZ, ShearYX, ShearYZ, ShearZX, ShearZY]
%
% outputs,
% M: 3D affine transformation matrix
%
% examples,
% % 3D
% M=make_transformation_matrix([0.5 0 0],[1 1 1.2],[0 0 0])
%
% Function is written by D.Kroon University of Twente (October 2008)
% Process inputs
if(~exist('r','var')||isempty(r)), r=[0 0 0]; end
if(~exist('s','var')||isempty(s)), s=[1 1 1]; end
if(~exist('h','var')||isempty(h)), h=[0 0 0 0 0 0]; end
% Calculate affine transformation matrix
if(length(t)==2)
% Make the transformation matrix
M=mat_tra_2d(t)*mat_siz_2d(s)*mat_rot_2d(r)*mat_shear_2d(h);
else
% Make the transformation matrix
M=mat_tra_3d(t)*mat_siz_3d(s)*mat_rot_3d(r)*mat_shear_3d(h);
end
function M=mat_rot_3d(r)
r=r*(pi/180);
Rx=[1 0 0 0;
0 cos(r(1)) -sin(r(1)) 0;
0 sin(r(1)) cos(r(1)) 0;
0 0 0 1];
Ry=[cos(r(2)) 0 sin(r(2)) 0;
0 1 0 0;
-sin(r(2)) 0 cos(r(2)) 0;
0 0 0 1];
Rz=[cos(r(3)) -sin(r(3)) 0 0;
sin(r(3)) cos(r(3)) 0 0;
0 0 1 0;
0 0 0 1];
M=Rx*Ry*Rz;
function M=mat_siz_3d(s)
M=[s(1) 0 0 0;
0 s(2) 0 0;
0 0 s(3) 0;
0 0 0 1];
function M=mat_shear_3d(h)
M=[1 h(1) h(2) 0;
h(3) 1 h(4) 0;
h(5) h(6) 1 0;
0 0 0 1];
function M=mat_tra_3d(t)
M=[1 0 0 t(1);
0 1 0 t(2);
0 0 1 t(3);
0 0 0 1];