PlanarFundamentalMatrixEstimator.java
/*
* Copyright (C) 2017 Alberto Irurueta Carro (alberto@irurueta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.ar.epipolar.estimators;
import com.irurueta.ar.epipolar.EssentialMatrix;
import com.irurueta.ar.epipolar.FundamentalMatrix;
import com.irurueta.geometry.GeometryException;
import com.irurueta.geometry.HomogeneousPoint2D;
import com.irurueta.geometry.PinholeCameraIntrinsicParameters;
import com.irurueta.geometry.Transformation2D;
import com.irurueta.geometry.estimators.LockedException;
import com.irurueta.geometry.estimators.NotReadyException;
import java.util.ArrayList;
import java.util.List;
/**
* This class takes an input 2D homography (e.g. transformation) and a given
* pair of intrinsic parameters for left and right views, and estimates all
* possible fundamental matrices generating such homography in a planar scene.
* This estimator will generate either 2 or 4 possible solutions, however only
* 1 solution will be physically possible, which corresponds to the solution
* that generates triangulated points located in front of the cameras generating
* such epipolar geometries.
* This class is useful in planar scenes where 8-point and 7-point algorithms
* will fail since this kind of geometry is a degenerate configuration of
* points.
*/
public class PlanarFundamentalMatrixEstimator {
/**
* 2D transformation relating two views (left view to right view).
*/
private Transformation2D homography;
/**
* Intrinsic parameters to be used on left view.
*/
private PinholeCameraIntrinsicParameters leftIntrinsics;
/**
* Intrinsic parameters to be used on right view.
*/
private PinholeCameraIntrinsicParameters rightIntrinsics;
/**
* Listener to attend events generated by this instance.
*/
private PlanarFundamentalMatrixEstimatorListener listener;
/**
* Indicates whether estimator is locked while estimating fundamental
* matrix.
*/
private boolean locked;
/**
* Constructor.
*/
public PlanarFundamentalMatrixEstimator() {
}
/**
* Constructor.
*
* @param homography 2D transformation relating two views (left view to
* right view).
* @param leftIntrinsics intrinsic parameters to be used on left view.
* @param rightIntrinsics intrinsic parameters to be used on right view.
*/
public PlanarFundamentalMatrixEstimator(final Transformation2D homography,
final PinholeCameraIntrinsicParameters leftIntrinsics,
final PinholeCameraIntrinsicParameters rightIntrinsics) {
this.homography = homography;
this.leftIntrinsics = leftIntrinsics;
this.rightIntrinsics = rightIntrinsics;
}
/**
* Constructor.
*
* @param homography 2D transformation relating two views (left view to
* right view).
* @param leftIntrinsics intrinsic parameters to be used on left view.
* @param rightIntrinsics intrinsic parameters to be used on right view.
* @param listener listener to attend events generated by this instance.
*/
public PlanarFundamentalMatrixEstimator(final Transformation2D homography,
final PinholeCameraIntrinsicParameters leftIntrinsics,
final PinholeCameraIntrinsicParameters rightIntrinsics,
final PlanarFundamentalMatrixEstimatorListener listener) {
this(homography, leftIntrinsics, rightIntrinsics);
this.listener = listener;
}
/**
* Gets 2D transformation relating two views (left view to right view).
*
* @return 2D transformation relating two views.
*/
public Transformation2D getHomography() {
return homography;
}
/**
* Sets 2D transformation relating two views (left view to right view).
*
* @param homography 2D transformation relating two views.
* @throws LockedException if estimator is locked.
*/
public void setHomography(final Transformation2D homography) throws LockedException {
if (isLocked()) {
throw new LockedException();
}
this.homography = homography;
}
/**
* Gets intrinsic parameters to be used on left view.
*
* @return intrinsic parameters to be used on left view.
*/
public PinholeCameraIntrinsicParameters getLeftIntrinsics() {
return leftIntrinsics;
}
/**
* Sets intrinsic parameters to be used on left view.
*
* @param leftIntrinsics intrinsic parameters to be used on left view.
* @throws LockedException if estimator is locked.
*/
public void setLeftIntrinsics(final PinholeCameraIntrinsicParameters leftIntrinsics) throws LockedException {
if (isLocked()) {
throw new LockedException();
}
this.leftIntrinsics = leftIntrinsics;
}
/**
* Gets intrinsic parameters to be used on right view.
*
* @return intrinsic parameters to be used on right view.
*/
public PinholeCameraIntrinsicParameters getRightIntrinsics() {
return rightIntrinsics;
}
/**
* Sets intrinsic parameters to be used on right view.
*
* @param rightIntrinsics intrinsic parameters to be used on right view.
* @throws LockedException if estimator is locked.
*/
public void setRightIntrinsics(final PinholeCameraIntrinsicParameters rightIntrinsics) throws LockedException {
if (isLocked()) {
throw new LockedException();
}
this.rightIntrinsics = rightIntrinsics;
}
/**
* Gets listener to attend events generated by this instance.
*
* @return listener to attend events generated by this instance.
*/
public PlanarFundamentalMatrixEstimatorListener getListener() {
return listener;
}
/**
* Sets listener to attend events generated by this instance.
*
* @param listener listener to attend events generated by this instance.
*/
public void setListener(final PlanarFundamentalMatrixEstimatorListener listener) {
this.listener = listener;
}
/**
* Indicates whether estimator is locked while estimating fundamental
* matrix.
*
* @return true if estimator is locked, false otherwise.
*/
public boolean isLocked() {
return locked;
}
/**
* Indicates whether estimator is ready to start the estimation when all
* required data has been provided.
*
* @return true if estimator is ready, false otherwise.
*/
public boolean isReady() {
return homography != null && leftIntrinsics != null && rightIntrinsics != null;
}
/**
* Estimates fundamental matrices and returns the estimated result.
*
* @return estimated fundamental matrices.
* @throws LockedException if estimator is locked.
* @throws NotReadyException if estimator is not ready.
* @throws FundamentalMatrixEstimatorException if estimation fails for some
* reason.
*/
public List<FundamentalMatrix> estimate() throws LockedException, NotReadyException,
FundamentalMatrixEstimatorException {
final var result = new ArrayList<FundamentalMatrix>();
estimate(result);
return result;
}
/**
* Estimates fundamental matrices and stores result into provided instance.
*
* @param result instance where result will be stored.
* @throws LockedException if estimator is locked.
* @throws NotReadyException if estimator is not ready.
* @throws FundamentalMatrixEstimatorException if estimation fails for some
* reason.
*/
public void estimate(final List<FundamentalMatrix> result) throws LockedException, NotReadyException,
FundamentalMatrixEstimatorException {
if (isLocked()) {
throw new LockedException();
}
if (!isReady()) {
throw new NotReadyException();
}
try {
locked = true;
if (listener != null) {
listener.onEstimateStart(this);
}
final var decomposer = new HomographyDecomposer(homography, leftIntrinsics, rightIntrinsics);
final var decompositions = decomposer.decompose();
result.clear();
for (final var decomposition : decompositions) {
final var rotation = decomposition.getTransformation().getRotation();
final var translation = new HomogeneousPoint2D(decomposition.getTransformation().getTranslation());
final var essential = new EssentialMatrix(rotation, translation);
final var fundamentalMatrix = essential.toFundamentalMatrix(leftIntrinsics, rightIntrinsics);
result.add(fundamentalMatrix);
}
if (listener != null) {
listener.onEstimateEnd(this, result);
}
} catch (final GeometryException e) {
throw new FundamentalMatrixEstimatorException(e);
} finally {
locked = false;
}
}
}