LinePlaneCorrespondencePinholeCameraEstimator.java
/*
* Copyright (C) 2013 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.geometry.estimators;
import com.irurueta.geometry.Line2D;
import com.irurueta.geometry.NotAvailableException;
import com.irurueta.geometry.PinholeCamera;
import com.irurueta.geometry.Plane;
import com.irurueta.geometry.refiners.DecomposedLinePlaneCorrespondencePinholeCameraRefiner;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
/**
* This file contains abstract implementation for pinhole camera estimators
* based on line/plane correspondences.
*/
public abstract class LinePlaneCorrespondencePinholeCameraEstimator extends PinholeCameraEstimator {
/**
* Minimum number of required line/plane correspondences to estimate a
* camera.
*/
public static final int MIN_NUMBER_OF_LINE_PLANE_CORRESPONDENCES = 4;
/**
* List of corresponding 3D planes.
*/
protected List<Plane> planes;
/**
* List of corresponding 2D lines.
*/
protected List<Line2D> lines2D;
/**
* Constructor.
*/
protected LinePlaneCorrespondencePinholeCameraEstimator() {
super();
}
/**
* Constructor.
*
* @param listener listener to be notified of events such as when estimation
* starts, ends or estimation progress changes.
*/
protected LinePlaneCorrespondencePinholeCameraEstimator(final PinholeCameraEstimatorListener listener) {
super(listener);
}
/**
* Constructor.
*
* @param planes list of corresponding 3D planes.
* @param lines2D list of corresponding 2D lines.
* @throws IllegalArgumentException if any of the lists are null.
* @throws WrongListSizesException if provided lists of points don't have
* the same size and enough correspondences.
*/
protected LinePlaneCorrespondencePinholeCameraEstimator(final List<Plane> planes, final List<Line2D> lines2D)
throws WrongListSizesException {
super();
internalSetLists(planes, lines2D);
}
/**
* Constructor.
*
* @param planes list of corresponding 3D planes.
* @param lines2D list of corresponding 2D lines.
* @param listener listener to be notified of events such as when estimation
* starts, ends or estimation progress changes.
* @throws IllegalArgumentException if any of the lists are null.
* @throws WrongListSizesException if provided lists of points don't have
* the same size and enough correspondences.
*/
protected LinePlaneCorrespondencePinholeCameraEstimator(
final List<Plane> planes, final List<Line2D> lines2D, final PinholeCameraEstimatorListener listener)
throws WrongListSizesException {
super(listener);
internalSetLists(planes, lines2D);
}
/**
* Internal method to set list of corresponding lines/planes (it does not
* check if estimator is locked).
*
* @param planes list of corresponding 3D planes.
* @param lines2D list of corresponding 2D lines.
* @throws IllegalArgumentException if any of the lists are null.
* @throws WrongListSizesException if provided lists of points don't have
* the same size and enough correspondences.
*/
private void internalSetLists(final List<Plane> planes, final List<Line2D> lines2D) throws WrongListSizesException {
if (planes == null || lines2D == null) {
throw new IllegalArgumentException();
}
if (!areValidLists(planes, lines2D)) {
throw new WrongListSizesException();
}
this.planes = planes;
this.lines2D = lines2D;
}
/**
* Set list of corresponding lines/planes.
*
* @param planes list of corresponding 3D planes.
* @param lines2D list of corresponding 2D lines.
* @throws LockedException if estimator is locked.
* @throws IllegalArgumentException if any of the lists are null.
* @throws WrongListSizesException if provided lists of points don't have
* the same size and enough correspondences.
*/
public void setLists(final List<Plane> planes, final List<Line2D> lines2D) throws LockedException,
WrongListSizesException {
if (isLocked()) {
throw new LockedException();
}
internalSetLists(planes, lines2D);
}
/**
* Returns list of corresponding lines/planes.
* Notice that this method returns an unmodifiable list of planes to avoid
* undesired modifications.
*
* @return list of corresponding 3D planes.
* @throws NotAvailableException if list of planes is not yet available.
*/
public List<Plane> getPlanes() throws NotAvailableException {
if (planes == null) {
throw new NotAvailableException();
}
// to avoid undesired modifications
return Collections.unmodifiableList(planes);
}
/**
* Returns list of corresponding 2D lines.
* Notice that this method returns an unmodifiable list of 2D lines to avoid
* undesired modifications.
*
* @return list of corresponding 2D lines.
* @throws NotAvailableException if list of 2D lines is not yet available.
*/
public List<Line2D> getLines2D() throws NotAvailableException {
if (lines2D == null) {
throw new NotAvailableException();
}
// to avoid undesired modifications
return Collections.unmodifiableList(lines2D);
}
/**
* Indicates if lists of corresponding 2D lines and planes are valid.
* Lists are considered valid if they have the same number of points and
* both have more than the required minimum of correspondences (which is 4).
*
* @param planes list of corresponding 3D planes.
* @param lines2D list of corresponding 2D lines.
* @return true if corresponding 2D lines and planes are valid, false
* otherwise.
*/
public static boolean areValidLists(final List<Plane> planes, final List<Line2D> lines2D) {
if (planes == null || lines2D == null) {
return false;
}
return planes.size() == lines2D.size() && lines2D.size() >= MIN_NUMBER_OF_LINE_PLANE_CORRESPONDENCES;
}
/**
* Indicates if lists have already been provided and are available for
* retrieval.
*
* @return true if available, false otherwise.
*/
public boolean areListsAvailable() {
return planes != null && lines2D != null;
}
/**
* Attempts to refine provided camera using requested suggestions.
* If no suggestions are requested or if refinement fails, provided
* camera is returned instead.
*
* @param pinholeCamera camera to be refined.
* @return refined camera.
*/
@SuppressWarnings("DuplicatedCode")
@Override
protected PinholeCamera attemptRefine(PinholeCamera pinholeCamera) {
if (hasSuggestions()) {
final var numPoints = planes.size();
final var inliers = new BitSet(numPoints);
inliers.set(0, numPoints, true);
final var residuals = new double[numPoints];
final var refiner = new DecomposedLinePlaneCorrespondencePinholeCameraRefiner(pinholeCamera,
false, inliers, residuals, numPoints, planes, lines2D, 0.0);
try {
refiner.setMinSuggestionWeight(minSuggestionWeight);
refiner.setMaxSuggestionWeight(maxSuggestionWeight);
refiner.setSuggestionWeightStep(suggestionWeightStep);
refiner.setSuggestSkewnessValueEnabled(suggestSkewnessValueEnabled);
refiner.setSuggestedSkewnessValue(suggestedSkewnessValue);
refiner.setSuggestHorizontalFocalLengthEnabled(suggestHorizontalFocalLengthEnabled);
refiner.setSuggestedHorizontalFocalLengthValue(suggestedHorizontalFocalLengthValue);
refiner.setSuggestVerticalFocalLengthEnabled(suggestVerticalFocalLengthEnabled);
refiner.setSuggestedVerticalFocalLengthValue(suggestedVerticalFocalLengthValue);
refiner.setSuggestAspectRatioEnabled(suggestAspectRatioEnabled);
refiner.setSuggestedAspectRatioValue(suggestedAspectRatioValue);
refiner.setSuggestPrincipalPointEnabled(suggestPrincipalPointEnabled);
refiner.setSuggestedPrincipalPointValue(suggestedPrincipalPointValue);
refiner.setSuggestRotationEnabled(suggestRotationEnabled);
refiner.setSuggestedRotationValue(suggestedRotationValue);
refiner.setSuggestCenterEnabled(suggestCenterEnabled);
refiner.setSuggestedCenterValue(suggestedCenterValue);
final var result = new PinholeCamera();
final var improved = refiner.refine(result);
return improved ? result : pinholeCamera;
} catch (final Exception e) {
return pinholeCamera;
}
} else {
return pinholeCamera;
}
}
}