1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.geometry.refiners;
17
18 import com.irurueta.algebra.AlgebraException;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.geometry.Plane;
21 import com.irurueta.geometry.ProjectiveTransformation3D;
22 import com.irurueta.geometry.estimators.LockedException;
23 import com.irurueta.geometry.estimators.NotReadyException;
24 import com.irurueta.numerical.EvaluationException;
25 import com.irurueta.numerical.GradientEstimator;
26 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFitter;
27 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFunctionEvaluator;
28 import com.irurueta.numerical.robust.InliersData;
29
30 import java.util.BitSet;
31 import java.util.List;
32
33
34
35
36
37
38
39
40
41 @SuppressWarnings("DuplicatedCode")
42 public class PlaneCorrespondenceProjectiveTransformation3DRefiner extends
43 ProjectiveTransformation3DRefiner<Plane, Plane> {
44
45
46
47
48 private final Plane residualTestPlane = new Plane();
49
50
51
52
53 public PlaneCorrespondenceProjectiveTransformation3DRefiner() {
54 }
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public PlaneCorrespondenceProjectiveTransformation3DRefiner(
71 final ProjectiveTransformation3D initialEstimation, final boolean keepCovariance, final BitSet inliers,
72 final double[] residuals, final int numInliers, final List<Plane> samples1, final List<Plane> samples2,
73 final double refinementStandardDeviation) {
74 super(initialEstimation, keepCovariance, inliers, residuals, numInliers, samples1, samples2,
75 refinementStandardDeviation);
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 public PlaneCorrespondenceProjectiveTransformation3DRefiner(
92 final ProjectiveTransformation3D initialEstimation, final boolean keepCovariance,
93 final InliersData inliersData, final List<Plane> samples1, final List<Plane> samples2,
94 final double refinementStandardDeviation) {
95 super(initialEstimation, keepCovariance, inliersData, samples1, samples2, refinementStandardDeviation);
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 @Override
113 public boolean refine(final ProjectiveTransformation3D result) throws NotReadyException, LockedException,
114 RefinerException {
115 if (isLocked()) {
116 throw new LockedException();
117 }
118 if (!isReady()) {
119 throw new NotReadyException();
120 }
121
122 locked = true;
123
124 if (listener != null) {
125 listener.onRefineStart(this, initialEstimation);
126 }
127
128 final var initialTotalResidual = totalResidual(initialEstimation);
129
130 try {
131 final var initParams = new double[
132 ProjectiveTransformation3D.HOM_COORDS * ProjectiveTransformation3D.HOM_COORDS];
133
134 System.arraycopy(initialEstimation.getT().getBuffer(), 0, initParams, 0, initParams.length);
135
136
137 final var y = new double[numInliers];
138
139 final var nDims = 2 * Plane.PLANE_NUMBER_PARAMS;
140 final var x = new Matrix(numInliers, nDims);
141 final var nSamples = inliers.length();
142 var pos = 0;
143 for (var i = 0; i < nSamples; i++) {
144 if (inliers.get(i)) {
145
146 final var inputPlane = samples1.get(i);
147 final var outputPlane = samples2.get(i);
148 inputPlane.normalize();
149 outputPlane.normalize();
150 x.setElementAt(pos, 0, inputPlane.getA());
151 x.setElementAt(pos, 1, inputPlane.getB());
152 x.setElementAt(pos, 2, inputPlane.getC());
153 x.setElementAt(pos, 3, inputPlane.getD());
154 x.setElementAt(pos, 4, outputPlane.getA());
155 x.setElementAt(pos, 5, outputPlane.getB());
156 x.setElementAt(pos, 6, outputPlane.getC());
157 x.setElementAt(pos, 7, outputPlane.getD());
158
159 y[pos] = residuals[i];
160 pos++;
161 }
162 }
163
164 final var evaluator = new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
165
166 private final Plane inputPlane = new Plane();
167
168 private final Plane outputPlane = new Plane();
169
170 private final ProjectiveTransformation3D transformation = new ProjectiveTransformation3D();
171
172 private final GradientEstimator gradientEstimator = new GradientEstimator(params -> {
173
174 System.arraycopy(params, 0, transformation.getT().getBuffer(), 0, params.length);
175 return residual(transformation, inputPlane, outputPlane);
176 });
177
178 @Override
179 public int getNumberOfDimensions() {
180 return nDims;
181 }
182
183 @Override
184 public double[] createInitialParametersArray() {
185 return initParams;
186 }
187
188 @Override
189 public double evaluate(final int i, final double[] point, final double[] params,
190 final double[] derivatives) throws EvaluationException {
191 inputPlane.setParameters(point[0], point[1], point[2], point[3]);
192 outputPlane.setParameters(point[4], point[5], point[6], point[7]);
193
194
195 System.arraycopy(params, 0, transformation.getT().getBuffer(), 0, params.length);
196
197 final var y = residual(transformation, inputPlane, outputPlane);
198 gradientEstimator.gradient(params, derivatives);
199
200 return y;
201 }
202 };
203
204 final var fitter = new LevenbergMarquardtMultiDimensionFitter(evaluator, x, y,
205 getRefinementStandardDeviation());
206
207 fitter.fit();
208
209
210 final var params = fitter.getA();
211
212
213
214
215 System.arraycopy(params, 0, result.getT().getBuffer(), 0, params.length);
216
217 if (keepCovariance) {
218
219 covariance = fitter.getCovar();
220 }
221
222 final var finalTotalResidual = totalResidual(result);
223 final var errorDecreased = finalTotalResidual < initialTotalResidual;
224
225 if (listener != null) {
226 listener.onRefineEnd(this, initialEstimation, result, errorDecreased);
227 }
228
229 return errorDecreased;
230
231 } catch (final Exception e) {
232 throw new RefinerException(e);
233 } finally {
234 locked = false;
235 }
236 }
237
238
239
240
241
242
243
244
245
246
247 private double residual(final ProjectiveTransformation3D transformation, final Plane inputPlane,
248 final Plane outputPlane) {
249 try {
250 inputPlane.normalize();
251 outputPlane.normalize();
252
253 transformation.transform(inputPlane, residualTestPlane);
254 return 1.0 - Math.abs(outputPlane.dotProduct(residualTestPlane));
255 } catch (final AlgebraException e) {
256 return 1.0;
257 }
258 }
259
260
261
262
263
264
265
266 private double totalResidual(final ProjectiveTransformation3D transformation) {
267 var result = 0.0;
268
269 final var nSamples = inliers.length();
270 for (var i = 0; i < nSamples; i++) {
271 if (inliers.get(i)) {
272
273 final var inputPlane = samples1.get(i);
274 final var outputPlane = samples2.get(i);
275 inputPlane.normalize();
276 outputPlane.normalize();
277 result += residual(transformation, inputPlane, outputPlane);
278 }
279 }
280
281 return result;
282 }
283 }