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.AffineTransformation2D;
21 import com.irurueta.geometry.Line2D;
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 LineCorrespondenceAffineTransformation2DRefiner extends AffineTransformation2DRefiner<Line2D, Line2D> {
43
44
45
46
47 private final Line2D residualTestLine = new Line2D();
48
49
50
51
52 public LineCorrespondenceAffineTransformation2DRefiner() {
53 }
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public LineCorrespondenceAffineTransformation2DRefiner(
70 final AffineTransformation2D initialEstimation, final boolean keepCovariance,
71 final BitSet inliers, final double[] residuals, final int numInliers,
72 final List<Line2D> samples1, final List<Line2D> samples2, final double refinementStandardDeviation) {
73 super(initialEstimation, keepCovariance, inliers, residuals, numInliers, samples1, samples2,
74 refinementStandardDeviation);
75 }
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public LineCorrespondenceAffineTransformation2DRefiner(
91 final AffineTransformation2D initialEstimation, final boolean keepCovariance,
92 final InliersData inliersData, final List<Line2D> samples1,
93 final List<Line2D> samples2, final double refinementStandardDeviation) {
94 super(initialEstimation, keepCovariance, inliersData, samples1, samples2, refinementStandardDeviation);
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 @Override
112 public boolean refine(final AffineTransformation2D result) throws NotReadyException, LockedException,
113 RefinerException {
114 if (isLocked()) {
115 throw new LockedException();
116 }
117 if (!isReady()) {
118 throw new NotReadyException();
119 }
120
121 locked = true;
122
123 if (listener != null) {
124 listener.onRefineStart(this, initialEstimation);
125 }
126
127 final var initialTotalResidual = totalResidual(initialEstimation);
128
129 try {
130 final var initParams = new double[AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS
131 + AffineTransformation2D.NUM_TRANSLATION_COORDS];
132
133 System.arraycopy(initialEstimation.getA().getBuffer(), 0,
134 initParams, 0,
135 AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS);
136
137 System.arraycopy(initialEstimation.getTranslation(), 0,
138 initParams, AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS,
139 AffineTransformation2D.NUM_TRANSLATION_COORDS);
140
141
142 final var y = new double[numInliers];
143
144 final var nDims = 2 * Line2D.LINE_NUMBER_PARAMS;
145 final var x = new Matrix(numInliers, nDims);
146 final var nSamples = inliers.length();
147 var pos = 0;
148 for (var i = 0; i < nSamples; i++) {
149 if (inliers.get(i)) {
150
151 final var inputLine = samples1.get(i);
152 final var outputLine = samples2.get(i);
153 inputLine.normalize();
154 outputLine.normalize();
155 x.setElementAt(pos, 0, inputLine.getA());
156 x.setElementAt(pos, 1, inputLine.getB());
157 x.setElementAt(pos, 2, inputLine.getC());
158 x.setElementAt(pos, 3, outputLine.getA());
159 x.setElementAt(pos, 4, outputLine.getB());
160 x.setElementAt(pos, 5, outputLine.getC());
161
162 y[pos] = residuals[i];
163 pos++;
164 }
165 }
166
167 final var evaluator = new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
168
169 private final Line2D inputLine = new Line2D();
170
171 private final Line2D outputLine = new Line2D();
172
173 private final AffineTransformation2D transformation = new AffineTransformation2D();
174
175 private final GradientEstimator gradientEstimator = new GradientEstimator(params -> {
176
177 System.arraycopy(params, 0, transformation.getA().getBuffer(), 0,
178 AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS);
179
180 System.arraycopy(params,
181 AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS,
182 transformation.getTranslation(), 0, AffineTransformation2D.NUM_TRANSLATION_COORDS);
183
184 return residual(transformation, inputLine, outputLine);
185 });
186
187 @Override
188 public int getNumberOfDimensions() {
189 return nDims;
190 }
191
192 @Override
193 public double[] createInitialParametersArray() {
194 return initParams;
195 }
196
197 @Override
198 public double evaluate(
199 final int i, final double[] point, final double[] params, final double[] derivatives)
200 throws EvaluationException {
201 inputLine.setParameters(point[0], point[1], point[2]);
202 outputLine.setParameters(point[3], point[4], point[5]);
203
204
205 System.arraycopy(params, 0, transformation.getA().getBuffer(), 0,
206 AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS);
207
208 System.arraycopy(params,
209 AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS,
210 transformation.getTranslation(), 0, AffineTransformation2D.NUM_TRANSLATION_COORDS);
211
212 final var y = residual(transformation, inputLine, outputLine);
213 gradientEstimator.gradient(params, derivatives);
214
215 return y;
216 }
217 };
218
219 final var fitter = new LevenbergMarquardtMultiDimensionFitter(evaluator, x, y,
220 getRefinementStandardDeviation());
221
222 fitter.fit();
223
224
225 final var params = fitter.getA();
226
227
228
229
230 System.arraycopy(params, 0, result.getA().getBuffer(), 0,
231 AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS);
232
233 System.arraycopy(params, AffineTransformation2D.INHOM_COORDS * AffineTransformation2D.INHOM_COORDS,
234 result.getTranslation(), 0, AffineTransformation2D.NUM_TRANSLATION_COORDS);
235
236 if (keepCovariance) {
237
238 covariance = fitter.getCovar();
239 }
240
241 final var finalTotalResidual = totalResidual(result);
242 final var errorDecreased = finalTotalResidual < initialTotalResidual;
243
244 if (listener != null) {
245 listener.onRefineEnd(this, initialEstimation, result, errorDecreased);
246 }
247
248 return errorDecreased;
249
250 } catch (final Exception e) {
251 throw new RefinerException(e);
252 } finally {
253 locked = false;
254 }
255 }
256
257
258
259
260
261
262
263
264
265
266 private double residual(final AffineTransformation2D transformation, final Line2D inputLine,
267 final Line2D outputLine) {
268 try {
269 inputLine.normalize();
270 outputLine.normalize();
271
272 transformation.transform(inputLine, residualTestLine);
273 return 1.0 - Math.abs(outputLine.dotProduct(residualTestLine));
274 } catch (final AlgebraException e) {
275 return 1.0;
276 }
277 }
278
279
280
281
282
283
284
285 private double totalResidual(final AffineTransformation2D transformation) {
286 var result = 0.0;
287
288 final var nSamples = inliers.length();
289 for (var i = 0; i < nSamples; i++) {
290 if (inliers.get(i)) {
291
292 final var inputLine = samples1.get(i);
293 final var outputLine = samples2.get(i);
294 inputLine.normalize();
295 outputLine.normalize();
296 result += residual(transformation, inputLine, outputLine);
297 }
298 }
299
300 return result;
301 }
302 }