package photogrammetry.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import Jama.Matrix;
import photogrammetry.util.model.Feature;
import photogrammetry.util.model.HasCoordinates2d;
import photogrammetry.util.model.Point3d;
import photogrammetry.util.model.SceneView;
import photogrammetry.util.model.models.Model;
/**
* Reconstructs models from two views.
*
* @author johannes
*/
public class TwoViewReconstructor {
private TwoViewReconstructor() {
}
/**
* Reconstructs models from pair of scene views.
*
* @param camera the camera used to capture both views
* @param view1 the first view
* @param view2 the second view
* @param allModels if set to true, all generated models will be returned.
* @return a list of possible models matching the two views
*/
public static List<Model> getPossibleModels(Camera camera, SceneView view1, SceneView view2,
boolean allModels) {
final EssentialMatrixEstimator essEst = new EssentialMatrixEstimator(view1, view2, camera);
final RotationTranslationEstimator rotTransEst = new RotationTranslationEstimator(
essEst.getRefinedEssentialMatrix());
final Collection<Feature> commonFeatures = view1.getCommonFeatures(view2);
final Matrix r1 = rotTransEst.getR1();
final Matrix r2 = rotTransEst.getR2();
Matrix t = rotTransEst.getT();
List<Model> candidateModels = new ArrayList<>(4);
List<Integer> noInvPts = new ArrayList<>(4);
try {
noInvPts.add(reconstructModel(camera, candidateModels, commonFeatures, t, r1, view1,
view2));
} catch (RuntimeException e) {
}
try {
noInvPts.add(reconstructModel(camera, candidateModels, commonFeatures, t, r2, view1,
view2));
} catch (RuntimeException e) {
}
t = t.times(-1);
try {
noInvPts.add(reconstructModel(camera, candidateModels, commonFeatures, t, r1, view1,
view2));
} catch (RuntimeException e) {
}
try {
noInvPts.add(reconstructModel(camera, candidateModels, commonFeatures, t, r2, view1,
view2));
} catch (RuntimeException e) {
}
// TODO check whether these two models are really the ones we want!
if (allModels) {
return candidateModels;
}
// the maximum is the model that is behind both cameras
List<Model> resultList = new ArrayList<>(2);
int idx = getMaxIndex(noInvPts);
resultList.add(candidateModels.remove(idx));
noInvPts.remove(idx);
// the minimum is the model that is in front of both cameras
idx = getMinIndex(noInvPts);
resultList.add(candidateModels.remove(idx));
noInvPts.remove(idx);
return resultList;
}
private static int getMaxIndex(List<Integer> l) {
int max = l.get(0);
int maxIndex = 0;
for (int i = 1; i < l.size(); i++) {
if (l.get(i) > max) {
max = l.get(i);
maxIndex = i;
}
}
return maxIndex;
}
private static int getMinIndex(List<Integer> l) {
int min = l.get(0);
int minIndex = 0;
for (int i = 1; i < l.size(); i++) {
if (l.get(i) < min) {
min = l.get(i);
minIndex = i;
}
}
return minIndex;
}
/**
* Create a Model using triangulation.
*
* @param candidateModels the model will be added to this list
* @param commonFeatures set of all features that should be triangulated
* @param t location of the second view
* @param r rotation of the second view
* @param i1 first view
* @param i2 second view
* @return the number of points that lie behind the camera
*/
private static int reconstructModel(Camera camera, List<Model> candidateModels,
Collection<Feature> commonFeatures, Matrix t, Matrix r, SceneView i1, SceneView i2) {
Model m = new Model();
Matrix c = r.transpose().times(t).times(-1);
Matrix zeroTrans = new Matrix(3, 1);
Matrix identityRot = Matrix.identity(3, 3);
final SceneView left = i1;
final SceneView right = i2;
int result = 0;
for (Feature f : commonFeatures) {
final HasCoordinates2d fLeft = left.getLocationInView(f);
final HasCoordinates2d fRight = right.getLocationInView(f);
Matrix x = Triangulator.triangulate(camera, r, c, fLeft, fRight);
m.addPoint(f, new Point3d(x.get(0, 0), x.get(1, 0), x.get(2, 0)));
if (camera.isPointBehindCamera(x, t, r)) {
result++;
}
if (camera.isPointBehindCamera(x, zeroTrans, identityRot)) {
result++;
}
}
candidateModels.add(m);
return result;
}
}