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.Matrix;
19 import com.irurueta.geometry.CoordinatesType;
20 import com.irurueta.geometry.PinholeCamera;
21 import com.irurueta.geometry.Point2D;
22 import com.irurueta.geometry.Point3D;
23 import com.irurueta.geometry.estimators.LockedException;
24 import com.irurueta.geometry.estimators.NotReadyException;
25 import com.irurueta.numerical.EvaluationException;
26 import com.irurueta.numerical.GradientEstimator;
27 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFitter;
28 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFunctionEvaluator;
29 import com.irurueta.numerical.robust.InliersData;
30
31 import java.util.BitSet;
32 import java.util.List;
33
34
35
36
37
38
39
40
41
42 @SuppressWarnings("DuplicatedCode")
43 public class NonDecomposedPointCorrespondencePinholeCameraRefiner extends PointCorrespondencePinholeCameraRefiner {
44
45
46
47
48
49 public static final double DEFAULT_SUGGESTION_ERROR_WEIGHT = 2.0;
50
51
52
53
54 private static final int REFINE_DIMS = 12;
55
56
57
58
59
60 private double suggestionErrorWeight = DEFAULT_SUGGESTION_ERROR_WEIGHT;
61
62
63
64
65 public NonDecomposedPointCorrespondencePinholeCameraRefiner() {
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public NonDecomposedPointCorrespondencePinholeCameraRefiner(
83 final PinholeCamera initialEstimation, final boolean keepCovariance, final BitSet inliers,
84 final double[] residuals, final int numInliers, final List<Point3D> samples1, final List<Point2D> samples2,
85 final double refinementStandardDeviation) {
86 super(initialEstimation, keepCovariance, inliers, residuals, numInliers, samples1, samples2,
87 refinementStandardDeviation);
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public NonDecomposedPointCorrespondencePinholeCameraRefiner(
104 final PinholeCamera initialEstimation, final boolean keepCovariance, final InliersData inliersData,
105 final List<Point3D> samples1, final List<Point2D> samples2, final double refinementStandardDeviation) {
106 super(initialEstimation, keepCovariance, inliersData, samples1, samples2, refinementStandardDeviation);
107 }
108
109
110
111
112
113
114
115 public double getSuggestionErrorWeight() {
116 return suggestionErrorWeight;
117 }
118
119
120
121
122
123
124
125
126 public void setSuggestionErrorWeight(final double suggestionErrorWeight) throws LockedException {
127 if (isLocked()) {
128 throw new LockedException();
129 }
130 this.suggestionErrorWeight = suggestionErrorWeight;
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 @Override
148 public boolean refine(final PinholeCamera result) throws NotReadyException, LockedException, RefinerException {
149 if (isLocked()) {
150 throw new LockedException();
151 }
152 if (!isReady()) {
153 throw new NotReadyException();
154 }
155
156 locked = true;
157
158 if (listener != null) {
159 listener.onRefineStart(this, initialEstimation);
160 }
161
162 try {
163 initialEstimation.normalize();
164
165
166 final var y = new double[numInliers];
167
168
169 final var nDims = Point2D.POINT2D_HOMOGENEOUS_COORDINATES_LENGTH
170 + Point3D.POINT3D_HOMOGENEOUS_COORDINATES_LENGTH;
171 final var x = new Matrix(numInliers, nDims);
172 final var nSamples = inliers.length();
173 var pos = 0;
174 final var initParams = new double[REFINE_DIMS];
175 cameraToParameters(initialEstimation, initParams);
176
177 final var initResidual = residualPowell(initialEstimation, initParams, suggestionErrorWeight);
178
179 final var suggestionResidual = hasSuggestions() ? suggestionResidual(initParams, suggestionErrorWeight)
180 : 0.0;
181 for (var i = 0; i < nSamples; i++) {
182 if (inliers.get(i)) {
183
184 final var point2D = samples2.get(i);
185 final var point3D = samples1.get(i);
186 point2D.normalize();
187 point3D.normalize();
188 x.setElementAt(pos, 0, point2D.getHomX());
189 x.setElementAt(pos, 1, point2D.getHomY());
190 x.setElementAt(pos, 2, point2D.getHomW());
191 x.setElementAt(pos, 3, point3D.getHomX());
192 x.setElementAt(pos, 4, point3D.getHomY());
193 x.setElementAt(pos, 5, point3D.getHomZ());
194 x.setElementAt(pos, 6, point3D.getHomW());
195
196 y[pos] = Math.pow(residuals[i], 2.0) + suggestionResidual;
197 pos++;
198 }
199 }
200
201 final var evaluator = new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
202
203 private final Point2D point2D = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
204
205 private final Point3D point3D = Point3D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
206
207 private final PinholeCamera pinholeCamera = new PinholeCamera();
208
209 private final GradientEstimator gradientEstimator = new GradientEstimator(params -> {
210 parametersToCamera(params, pinholeCamera);
211 return residualLevenbergMarquardt(pinholeCamera, point3D, point2D, params, suggestionErrorWeight);
212 });
213
214 @Override
215 public int getNumberOfDimensions() {
216 return nDims;
217 }
218
219 @Override
220 public double[] createInitialParametersArray() {
221 return initParams;
222 }
223
224 @Override
225 public double evaluate(
226 final int i, final double[] point, final double[] params, final double[] derivatives)
227 throws EvaluationException {
228 point2D.setHomogeneousCoordinates(point[0], point[1], point[2]);
229 point3D.setHomogeneousCoordinates(point[3], point[4], point[5], point[6]);
230
231 point2D.normalize();
232 point3D.normalize();
233
234 parametersToCamera(params, pinholeCamera);
235 final var y = residualLevenbergMarquardt(pinholeCamera, point3D, point2D, params,
236 suggestionErrorWeight);
237 gradientEstimator.gradient(params, derivatives);
238
239 return y;
240 }
241 };
242
243 final var fitter = new LevenbergMarquardtMultiDimensionFitter(evaluator, x, y, refinementStandardDeviation);
244
245 fitter.fit();
246
247 final var finalParams = fitter.getA();
248
249 parametersToCamera(finalParams, result);
250
251 if (keepCovariance) {
252 covariance = fitter.getCovar();
253 }
254
255 final var finalResidual = residualPowell(result, finalParams, suggestionErrorWeight);
256 final var errorDecreased = finalResidual < initResidual;
257
258 if (listener != null) {
259 listener.onRefineEnd(this, initialEstimation, result,
260 errorDecreased);
261 }
262
263 return errorDecreased;
264
265 } catch (final Exception e) {
266 throw new RefinerException(e);
267 } finally {
268 locked = false;
269 }
270 }
271 }