View Javadoc
1   /*
2    * Copyright (C) 2017 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.irurueta.ar.sfm;
18  
19  import com.irurueta.geometry.MetricTransformation3D;
20  import com.irurueta.geometry.Point3D;
21  
22  import java.util.ArrayList;
23  
24  /**
25   * Class in charge of estimating cameras and 3D reconstructed points from sparse
26   * image point correspondences from multiple views and known initial camera baseline
27   * (camera separation), so that cameras and reconstructed points are obtained with
28   * exact scale.
29   */
30  public class KnownBaselineSparseReconstructor extends
31          BaseSparseReconstructor<KnownBaselineSparseReconstructorConfiguration, KnownBaselineSparseReconstructor,
32                  KnownBaselineSparseReconstructorListener> {
33  
34      /**
35       * Constructor.
36       *
37       * @param configuration configuration for this re-constructor.
38       * @param listener      listener in charge of handling events.
39       * @throws NullPointerException if listener or configuration is not
40       *                              provided.
41       */
42      public KnownBaselineSparseReconstructor(
43              final KnownBaselineSparseReconstructorConfiguration configuration,
44              final KnownBaselineSparseReconstructorListener listener) {
45          super(configuration, listener);
46      }
47  
48      /**
49       * Constructor with default configuration.
50       *
51       * @param listener listener in charge of handling events.
52       * @throws NullPointerException if listener is not provided.
53       */
54      public KnownBaselineSparseReconstructor(final KnownBaselineSparseReconstructorListener listener) {
55          this(new KnownBaselineSparseReconstructorConfiguration(), listener);
56      }
57  
58      /**
59       * Called when processing one frame is successfully finished. This can be done to estimate scale on
60       * those implementations where scale can be measured or is already known.
61       *
62       * @param isInitialPairOfViews true if initial pair of views is being processed, false otherwise.
63       * @return true if post-processing succeeded, false otherwise.
64       */
65      @SuppressWarnings("DuplicatedCode")
66      @Override
67      protected boolean postProcessOne(final boolean isInitialPairOfViews) {
68          try {
69              final var metricCamera1 = previousMetricEstimatedCamera.getCamera();
70              final var metricCamera2 = currentMetricEstimatedCamera.getCamera();
71  
72              metricCamera1.decompose();
73              metricCamera2.decompose();
74  
75              final double scale;
76              if (isInitialPairOfViews) {
77                  // reconstruction succeeded, so we update scale of cameras and
78                  // reconstructed points
79                  final var baseline = configuration.getBaseline();
80  
81                  final var center1 = metricCamera1.getCameraCenter();
82                  final var center2 = metricCamera2.getCameraCenter();
83  
84                  final var estimatedBaseline = center1.distanceTo(center2);
85  
86                  scale = currentScale = baseline / estimatedBaseline;
87              } else {
88                  scale = currentScale;
89              }
90  
91              final var sqrScale = scale * scale;
92  
93              final var scaleTransformation = new MetricTransformation3D(scale);
94  
95              // update scale of cameras
96              final var euclideanCamera1 = scaleTransformation.transformAndReturnNew(metricCamera1);
97              final var euclideanCamera2 = scaleTransformation.transformAndReturnNew(metricCamera2);
98  
99              previousEuclideanEstimatedCamera = new EstimatedCamera();
100             previousEuclideanEstimatedCamera.setCamera(euclideanCamera1);
101             previousEuclideanEstimatedCamera.setViewId(previousMetricEstimatedCamera.getViewId());
102             previousEuclideanEstimatedCamera.setQualityScore(previousMetricEstimatedCamera.getQualityScore());
103             if (previousMetricEstimatedCamera.getCovariance() != null) {
104                 previousEuclideanEstimatedCamera.setCovariance(previousMetricEstimatedCamera.getCovariance()
105                         .multiplyByScalarAndReturnNew(sqrScale));
106             }
107 
108             currentEuclideanEstimatedCamera = new EstimatedCamera();
109             currentEuclideanEstimatedCamera.setCamera(euclideanCamera2);
110             currentEuclideanEstimatedCamera.setViewId(currentMetricEstimatedCamera.getViewId());
111             currentEuclideanEstimatedCamera.setQualityScore(currentMetricEstimatedCamera.getQualityScore());
112             if (currentMetricEstimatedCamera.getCovariance() != null) {
113                 currentEuclideanEstimatedCamera.setCovariance(
114                         currentMetricEstimatedCamera.getCovariance().multiplyByScalarAndReturnNew(sqrScale));
115             }
116 
117             // update scale of reconstructed points
118             final var numPoints = activeMetricReconstructedPoints.size();
119             final var metricReconstructedPoints3D = new ArrayList<Point3D>();
120             for (final var activeMetricReconstructedPoint : activeMetricReconstructedPoints) {
121                 metricReconstructedPoints3D.add(activeMetricReconstructedPoint.getPoint());
122             }
123 
124             final var euclideanReconstructedPoints3D = scaleTransformation.transformPointsAndReturnNew(
125                     metricReconstructedPoints3D);
126 
127             // set scaled points into result
128             activeEuclideanReconstructedPoints = new ArrayList<>();
129             ReconstructedPoint3D euclideanPoint;
130             ReconstructedPoint3D metricPoint;
131             for (var i = 0; i < numPoints; i++) {
132                 metricPoint = activeMetricReconstructedPoints.get(i);
133 
134                 euclideanPoint = new ReconstructedPoint3D();
135                 euclideanPoint.setId(metricPoint.getId());
136                 euclideanPoint.setPoint(euclideanReconstructedPoints3D.get(i));
137                 euclideanPoint.setInlier(metricPoint.isInlier());
138                 euclideanPoint.setQualityScore(metricPoint.getQualityScore());
139                 if (metricPoint.getCovariance() != null) {
140                     euclideanPoint.setCovariance(metricPoint.getCovariance().multiplyByScalarAndReturnNew(sqrScale));
141                 }
142                 euclideanPoint.setColorData(metricPoint.getColorData());
143 
144                 activeEuclideanReconstructedPoints.add(euclideanPoint);
145             }
146 
147             return true;
148         } catch (final Exception e) {
149             failed = true;
150             listener.onFail(this);
151 
152             return false;
153         }
154     }
155 }