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