1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.ar.sfm;
17
18 import com.irurueta.ar.slam.BaseCalibrationData;
19 import com.irurueta.ar.slam.BaseSlamEstimator;
20 import com.irurueta.geometry.GeometryException;
21 import com.irurueta.geometry.InhomogeneousPoint3D;
22 import com.irurueta.geometry.MetricTransformation3D;
23 import com.irurueta.geometry.PinholeCamera;
24 import com.irurueta.geometry.PinholeCameraIntrinsicParameters;
25 import com.irurueta.geometry.Point3D;
26 import com.irurueta.geometry.Quaternion;
27
28 import java.util.ArrayList;
29
30
31
32
33
34
35
36
37
38
39
40
41
42 @SuppressWarnings("DuplicatedCode")
43 public abstract class BaseSlamSparseReconstructor<
44 D extends BaseCalibrationData,
45 C extends BaseSlamSparseReconstructorConfiguration<D, C>,
46 R extends BaseSlamSparseReconstructor<D, C, R, L, S>,
47 L extends BaseSlamSparseReconstructorListener<R>,
48 S extends BaseSlamEstimator<D>> extends BaseSparseReconstructor<C, R, L> {
49
50
51
52
53
54 protected S slamEstimator;
55
56
57
58
59 protected final InhomogeneousPoint3D slamPosition = new InhomogeneousPoint3D();
60
61
62
63
64 private final PinholeCamera slamCamera = new PinholeCamera();
65
66
67
68
69 private final Quaternion slamRotation = new Quaternion();
70
71
72
73
74
75
76
77
78
79 protected BaseSlamSparseReconstructor(final C configuration, final L listener) {
80 super(configuration, listener);
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 public void updateAccelerometerSample(final long timestamp, final float accelerationX, final float accelerationY,
99 final float accelerationZ) {
100 if (slamEstimator != null) {
101 slamEstimator.updateAccelerometerSample(timestamp, accelerationX, accelerationY, accelerationZ);
102 }
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public void updateAccelerometerSample(final long timestamp, final float[] data) {
119 if (slamEstimator != null) {
120 slamEstimator.updateAccelerometerSample(timestamp, data);
121 }
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 public void updateGyroscopeSample(final long timestamp, final float angularSpeedX,
138 final float angularSpeedY, final float angularSpeedZ) {
139 if (slamEstimator != null) {
140 slamEstimator.updateGyroscopeSample(timestamp, angularSpeedX, angularSpeedY, angularSpeedZ);
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155 public void updateGyroscopeSample(final long timestamp, final float[] data) {
156 if (slamEstimator != null) {
157 slamEstimator.updateGyroscopeSample(timestamp, data);
158 }
159 }
160
161
162
163
164 protected void setUpCalibrationData() {
165 final var calibrationData = configuration.getCalibrationData();
166 if (calibrationData != null) {
167 slamEstimator.setCalibrationData(calibrationData);
168 }
169 }
170
171
172
173
174 protected void setUpSlamEstimatorListener() {
175 slamEstimator.setListener(new BaseSlamEstimator.BaseSlamEstimatorListener<>() {
176 @Override
177 public void onFullSampleReceived(final BaseSlamEstimator<D> estimator) {
178
179 }
180
181 @Override
182 public void onFullSampleProcessed(final BaseSlamEstimator<D> estimator) {
183 notifySlamStateAndCamera();
184 }
185
186 @Override
187 public void onCorrectWithPositionMeasure(final BaseSlamEstimator<D> estimator) {
188
189 }
190
191 @Override
192 public void onCorrectedWithPositionMeasure(final BaseSlamEstimator<D> estimator) {
193 notifySlamStateAndCamera();
194 }
195
196 private void notifySlamStateAndCamera() {
197 notifySlamStateIfNeeded();
198 notifySlamCameraIfNeeded();
199 }
200 });
201 }
202
203
204
205
206
207
208
209 protected boolean updateScale(final boolean isInitialPairOfViews) {
210 try {
211 var metricCamera1 = previousMetricEstimatedCamera.getCamera();
212 var metricCamera2 = currentMetricEstimatedCamera.getCamera();
213
214 double slamPosX;
215 double slamPosY;
216 double slamPosZ;
217 double scale;
218 if (isInitialPairOfViews) {
219
220 slamPosX = slamEstimator.getStatePositionX();
221 slamPosY = slamEstimator.getStatePositionY();
222 slamPosZ = slamEstimator.getStatePositionZ();
223
224 slamPosition.setInhomogeneousCoordinates(slamPosX, slamPosY, slamPosZ);
225
226 if (!metricCamera1.isCameraCenterAvailable()) {
227 metricCamera1.decompose(false, true);
228 }
229 if (!metricCamera2.isCameraCenterAvailable()) {
230 metricCamera2.decompose(false, true);
231 }
232
233 final var center1 = metricCamera1.getCameraCenter();
234 final var center2 = metricCamera2.getCameraCenter();
235
236 final var baseline = center1.distanceTo(slamPosition);
237 final var estimatedBaseline = center1.distanceTo(center2);
238
239 scale = currentScale = baseline / estimatedBaseline;
240 } else {
241 scale = currentScale;
242 }
243
244 final var scaleTransformation = new MetricTransformation3D(scale);
245
246
247 final var euclideanCamera1 = scaleTransformation.transformAndReturnNew(metricCamera1);
248 final var euclideanCamera2 = scaleTransformation.transformAndReturnNew(metricCamera2);
249
250 if (!euclideanCamera2.isCameraCenterAvailable()) {
251 euclideanCamera2.decompose(false, true);
252 }
253 slamEstimator.correctWithPositionMeasure(euclideanCamera2.getCameraCenter(),
254 configuration.getCameraPositionCovariance());
255
256 if (!isInitialPairOfViews) {
257
258 slamPosX = slamEstimator.getStatePositionX();
259 slamPosY = slamEstimator.getStatePositionY();
260 slamPosZ = slamEstimator.getStatePositionZ();
261 slamPosition.setInhomogeneousCoordinates(slamPosX, slamPosY, slamPosZ);
262
263
264 final var euclideanCenter2 = euclideanCamera2.getCameraCenter();
265
266 final var euclideanPosX = euclideanCenter2.getInhomX();
267 final var euclideanPosY = euclideanCenter2.getInhomY();
268 final var euclideanPosZ = euclideanCenter2.getInhomZ();
269
270 final var scaleVariationX = euclideanPosX / slamPosX;
271 final var scaleVariationY = euclideanPosY / slamPosY;
272 final var scaleVariationZ = euclideanPosZ / slamPosZ;
273
274 final var scaleVariation = (scaleVariationX + scaleVariationY + scaleVariationZ) / 3.0;
275 scale *= scaleVariation;
276 currentScale = scale;
277 scaleTransformation.setScale(currentScale);
278
279
280 scaleTransformation.transform(metricCamera2, euclideanCamera2);
281 }
282 final var sqrScale = scale * scale;
283
284 previousEuclideanEstimatedCamera = new EstimatedCamera();
285 previousEuclideanEstimatedCamera.setCamera(euclideanCamera1);
286 previousEuclideanEstimatedCamera.setViewId(previousMetricEstimatedCamera.getViewId());
287 previousEuclideanEstimatedCamera.setQualityScore(previousMetricEstimatedCamera.getQualityScore());
288 if (previousMetricEstimatedCamera.getCovariance() != null) {
289 previousEuclideanEstimatedCamera.setCovariance(
290 previousMetricEstimatedCamera.getCovariance().multiplyByScalarAndReturnNew(sqrScale));
291 }
292
293 currentEuclideanEstimatedCamera = new EstimatedCamera();
294 currentEuclideanEstimatedCamera.setCamera(euclideanCamera2);
295 currentEuclideanEstimatedCamera.setViewId(currentMetricEstimatedCamera.getViewId());
296 currentEuclideanEstimatedCamera.setQualityScore(currentMetricEstimatedCamera.getQualityScore());
297 if (currentMetricEstimatedCamera.getCovariance() != null) {
298 currentEuclideanEstimatedCamera.setCovariance(
299 currentMetricEstimatedCamera.getCovariance().multiplyByScalarAndReturnNew(sqrScale));
300 }
301
302
303 final var numPoints = activeMetricReconstructedPoints.size();
304 final var metricReconstructedPoints3D = new ArrayList<Point3D>();
305 for (final var reconstructedPoint : activeMetricReconstructedPoints) {
306 metricReconstructedPoints3D.add(reconstructedPoint.getPoint());
307 }
308
309 final var euclideanReconstructedPoints3D = scaleTransformation.transformPointsAndReturnNew(
310 metricReconstructedPoints3D);
311
312
313 activeEuclideanReconstructedPoints = new ArrayList<>();
314 ReconstructedPoint3D euclideanPoint;
315 ReconstructedPoint3D metricPoint;
316 for (var i = 0; i < numPoints; i++) {
317 metricPoint = activeMetricReconstructedPoints.get(i);
318
319 euclideanPoint = new ReconstructedPoint3D();
320 euclideanPoint.setId(metricPoint.getId());
321 euclideanPoint.setPoint(euclideanReconstructedPoints3D.get(i));
322 euclideanPoint.setInlier(metricPoint.isInlier());
323 euclideanPoint.setQualityScore(metricPoint.getQualityScore());
324 if (metricPoint.getCovariance() != null) {
325 euclideanPoint.setCovariance(metricPoint.getCovariance().multiplyByScalarAndReturnNew(sqrScale));
326 }
327 euclideanPoint.setColorData(metricPoint.getColorData());
328
329 activeEuclideanReconstructedPoints.add(euclideanPoint);
330 }
331
332 return true;
333 } catch (final Exception e) {
334 failed = true;
335
336 listener.onFail((R) this);
337
338 return false;
339 }
340 }
341
342
343
344
345 private void notifySlamStateIfNeeded() {
346 if (!configuration.isNotifyAvailableSlamDataEnabled()) {
347 return;
348 }
349
350 final var positionX = slamEstimator.getStatePositionX();
351 final var positionY = slamEstimator.getStatePositionY();
352 final var positionZ = slamEstimator.getStatePositionZ();
353
354 final var velocityX = slamEstimator.getStateVelocityX();
355 final var velocityY = slamEstimator.getStateVelocityY();
356 final var velocityZ = slamEstimator.getStateVelocityZ();
357
358 final var accelerationX = slamEstimator.getStateAccelerationX();
359 final var accelerationY = slamEstimator.getStateAccelerationY();
360 final var accelerationZ = slamEstimator.getStateAccelerationZ();
361
362 final var quaternionA = slamEstimator.getStateQuaternionA();
363 final var quaternionB = slamEstimator.getStateQuaternionB();
364 final var quaternionC = slamEstimator.getStateQuaternionC();
365 final var quaternionD = slamEstimator.getStateQuaternionD();
366
367 final var angularSpeedX = slamEstimator.getStateAngularSpeedX();
368 final var angularSpeedY = slamEstimator.getStateAngularSpeedY();
369 final var angularSpeedZ = slamEstimator.getStateAngularSpeedZ();
370
371
372 listener.onSlamDataAvailable((R) this, positionX, positionY, positionZ,
373 velocityX, velocityY, velocityZ,
374 accelerationX, accelerationY, accelerationZ,
375 quaternionA, quaternionB, quaternionC, quaternionD,
376 angularSpeedX, angularSpeedY, angularSpeedZ, slamEstimator.getStateCovariance());
377 }
378
379
380
381
382
383 private void notifySlamCameraIfNeeded() {
384 if (!configuration.isNotifyEstimatedSlamCameraEnabled()) {
385 return;
386 }
387
388
389 var camera = currentEuclideanEstimatedCamera != null ? currentEuclideanEstimatedCamera.getCamera() : null;
390 if (camera == null) {
391
392 camera = previousEuclideanEstimatedCamera != null ? previousEuclideanEstimatedCamera.getCamera() : null;
393 }
394
395 try {
396 PinholeCameraIntrinsicParameters intrinsicParameters = null;
397 if (camera != null) {
398 if (!camera.areIntrinsicParametersAvailable()) {
399
400 camera.decompose();
401 }
402
403 intrinsicParameters = camera.getIntrinsicParameters();
404 } else if (configuration.getInitialIntrinsic1() != null) {
405 intrinsicParameters = configuration.getInitialIntrinsic1();
406 } else if (configuration.getInitialIntrinsic2() != null) {
407 intrinsicParameters = configuration.getInitialIntrinsic2();
408 } else if (configuration.getAdditionalCamerasIntrinsics() != null) {
409 intrinsicParameters = configuration.getAdditionalCamerasIntrinsics();
410 }
411
412 if (intrinsicParameters == null) {
413 return;
414 }
415
416 final var positionX = slamEstimator.getStatePositionX();
417 final var positionY = slamEstimator.getStatePositionY();
418 final var positionZ = slamEstimator.getStatePositionZ();
419 slamPosition.setInhomogeneousCoordinates(positionX, positionY, positionZ);
420
421 final var quaternionA = slamEstimator.getStateQuaternionA();
422 final var quaternionB = slamEstimator.getStateQuaternionB();
423 final var quaternionC = slamEstimator.getStateQuaternionC();
424 final var quaternionD = slamEstimator.getStateQuaternionD();
425 slamRotation.setA(quaternionA);
426 slamRotation.setB(quaternionB);
427 slamRotation.setC(quaternionC);
428 slamRotation.setD(quaternionD);
429
430 slamCamera.setIntrinsicAndExtrinsicParameters(intrinsicParameters, slamRotation, slamPosition);
431
432
433 listener.onSlamCameraEstimated((R) this, slamCamera);
434
435 } catch (final GeometryException ignore) {
436
437 }
438 }
439 }