All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.irurueta.ar.epipolar.EssentialMatrix Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
/*
 * Copyright (C) 2015 Alberto Irurueta Carro ([email protected])
 *
 * 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;

import com.irurueta.algebra.AlgebraException;
import com.irurueta.algebra.Matrix;
import com.irurueta.algebra.SingularValueDecomposer;
import com.irurueta.algebra.Utils;
import com.irurueta.geometry.*;

import java.io.Serializable;

/**
 * The essential matrix defines the relation between two views in a similar way
 * that the fundamental matrix does, but taking into account the intrinsic
 * parameters of the cameras associated to both views. That ways the relation
 * between their extrinsic parameters (rotation and translation) can be precisely
 * obtained.
 */
public class EssentialMatrix extends FundamentalMatrix implements Serializable {

    /**
     * Default threshold to determine that the two non-zero singular values are
     * equal.
     */
    public static final double DEFAULT_SINGULAR_VALUES_THRESHOLD = 1e-8;

    private Rotation3D mRotation1;
    private Rotation3D mRotation2;

    private Point2D mTranslation1;
    private Point2D mTranslation2;

    private boolean mPossibleRotationsAndTranslationsAvailable;

    /**
     * Constructor.
     */
    public EssentialMatrix() {
        super();
    }

    /**
     * Constructor.
     *
     * @param internalMatrix          matrix to be set internally.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values are equal.
     * @throws InvalidEssentialMatrixException if provided matrix is not 3x3,
     *                                         does not have rank 2 or its two non-zero singular values are not equal up
     *                                         to provided threshold.
     * @throws IllegalArgumentException        if provided threshold is negative.
     */
    public EssentialMatrix(final Matrix internalMatrix,
                           final double singularValuesThreshold)
            throws InvalidEssentialMatrixException {
        super();
        setInternalMatrix(internalMatrix, singularValuesThreshold);
    }

    /**
     * Constructor.
     *
     * @param internalMatrix matrix to be set internally.
     * @throws InvalidEssentialMatrixException if provided matrix is not 3x3,
     *                                         does not have rank 2 or its two non-zero singular values are not equal.
     */
    public EssentialMatrix(final Matrix internalMatrix)
            throws InvalidEssentialMatrixException {
        this(internalMatrix, DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Constructor from pair of cameras.
     *
     * @param leftCamera              camera corresponding to left view.
     * @param rightCamera             camera corresponding to right view.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values of generated essential matrix are equal.
     * @throws InvalidPairOfCamerasException if provided cameras do not span a
     *                                       valid epipolar geometry (i.e. they are set in a degenerate configuration).
     * @throws IllegalArgumentException      if provided threshold is negative.
     */
    public EssentialMatrix(
            final PinholeCamera leftCamera,
            final PinholeCamera rightCamera,
            final double singularValuesThreshold)
            throws InvalidPairOfCamerasException {
        super();
        setFromPairOfCameras(leftCamera, rightCamera, singularValuesThreshold);
    }

    /**
     * Constructor from pair of cameras.
     *
     * @param leftCamera  camera corresponding to left view.
     * @param rightCamera camera corresponding to right view.
     * @throws InvalidPairOfCamerasException if provided cameras do not span a
     *                                       valid epipolar geometry (i.e. they are set in a degenerate configuration).
     */
    public EssentialMatrix(
            final PinholeCamera leftCamera,
            final PinholeCamera rightCamera)
            throws InvalidPairOfCamerasException {
        this(leftCamera, rightCamera, DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Constructor from rotation and translation of the image of world origin
     * relative to left view camera, which is assumed to be located at origin
     * of coordinates with no rotation.
     *
     * @param rotation                rotation of right camera relative to left camera.
     * @param translation             translation of the image of world origin on right
     *                                camera relative to left camera.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values of generated essential matrix are equal.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                translation yield a degenerate epipolar geometry.
     * @throws IllegalArgumentException               if provided threshold is negative.
     */
    public EssentialMatrix(
            final Rotation3D rotation,
            final Point2D translation,
            final double singularValuesThreshold)
            throws InvalidRotationAndTranslationException {
        super();
        setFromRotationAndTranslation(rotation, translation,
                singularValuesThreshold);
    }

    /**
     * Constructor from rotation and translation of the image of world origin
     * relative to left view camera, which is assumed to be located at origin
     * of coordinates with no rotation.
     *
     * @param rotation    rotation of right camera relative to left camera.
     * @param translation translation of the image of world origin on right
     *                    camera relative to left camera.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                translation yield a degenerate epipolar geometry.
     */
    public EssentialMatrix(
            final Rotation3D rotation,
            final Point2D translation)
            throws InvalidRotationAndTranslationException {
        this(rotation, translation, DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Constructor from rotation and translation of the camera center relative
     * to left view camera, which is assumed to be located at origin of
     * coordinates with no rotation.
     *
     * @param rotation                rotation of right camera relative to left camera.
     * @param cameraCenter            camera center of right camera relative to left camera.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values of generated essential matrix are equal.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                translation yield a degenerate epipolar geometry.
     * @throws IllegalArgumentException               if provided threshold is negative.
     */
    public EssentialMatrix(
            final Rotation3D rotation,
            final Point3D cameraCenter,
            final double singularValuesThreshold)
            throws InvalidRotationAndTranslationException {
        setFromRotationAndCameraCenter(rotation, cameraCenter,
                singularValuesThreshold);
    }

    /**
     * Constructor from rotation and translation of the camera center relative
     * to left view camera, which is assumed to be located at origin of
     * coordinates with no rotation.
     *
     * @param rotation     rotation of right camera relative to left camera.
     * @param cameraCenter camera center of right camera relative to left camera.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                translation yield a degenerate epipolar geometry.
     */
    public EssentialMatrix(
            final Rotation3D rotation,
            final Point3D cameraCenter)
            throws InvalidRotationAndTranslationException {
        this(rotation, cameraCenter, DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Constructor from fundamental matrix and intrinsic camera parameters.
     *
     * @param fundamentalMatrix        a fundamental matrix.
     * @param leftIntrinsicParameters  intrinsic camera parameters of left view.
     * @param rightIntrinsicParameters intrinsic camera parameters of right view.
     * @throws InvalidPairOfIntrinsicParametersException if provided intrinsic
     *                                                   parameters generate an invalid essential matrix.
     */
    public EssentialMatrix(
            final FundamentalMatrix fundamentalMatrix,
            final PinholeCameraIntrinsicParameters leftIntrinsicParameters,
            final PinholeCameraIntrinsicParameters rightIntrinsicParameters)
            throws InvalidPairOfIntrinsicParametersException {
        setFromFundamentalMatrixAndIntrinsics(fundamentalMatrix,
                leftIntrinsicParameters, rightIntrinsicParameters);
    }

    /**
     * Sets internal matrix associated to this instance.
     * This method makes a copy of provided matrix.
     *
     * @param internalMatrix matrix to be assigned to this instance.
     * @throws InvalidEssentialMatrixException if provided matrix is not 3x3,
     *                                         does not have rank 2 or its two non-zero singular values are not equal.
     */
    @Override
    public final void setInternalMatrix(final Matrix internalMatrix)
            throws InvalidEssentialMatrixException {
        setInternalMatrix(internalMatrix, DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Sets internal matrix associated to this instance.
     * This method makes a copy of provided matrix.
     *
     * @param internalMatrix          matrix to be assigned to this instance.
     * @param singularValuesThreshold threshold to determine that both
     *                                singular values are equal.
     * @throws IllegalArgumentException        if provided threshold is negative.
     * @throws InvalidEssentialMatrixException if provided matrix is not 3x3,
     *                                         does not have rank 2 or its two non-zero singular values are not equal up
     *                                         to provided threshold.
     */
    public final void setInternalMatrix(
            final Matrix internalMatrix,
            final double singularValuesThreshold)
            throws InvalidEssentialMatrixException {
        if (!isValidInternalMatrix(internalMatrix, singularValuesThreshold)) {
            throw new InvalidEssentialMatrixException();
        }

        // because provided matrix is valid, we proceed to setting it
        mInternalMatrix = new Matrix(internalMatrix);
        mNormalized = false;
        mLeftEpipole = mRightEpipole = null;
    }

    /**
     * Returns a boolean indicating whether provided matrix is a valid essential
     * matrix (i.e. has size 3x3, rank 2 and two non-zero and equal singular
     * values).
     *
     * @param internalMatrix matrix to be checked.
     * @return true if provided matrix is a valid essential matrix, false
     * otherwise.
     */
    public static boolean isValidInternalMatrix(final Matrix internalMatrix) {
        return isValidInternalMatrix(internalMatrix,
                DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Returns a boolean indicating whether provided matrix is a valid
     * essential matrix (i.e. has size 3x3, rank 2 and his two non-zero singular
     * values are equal up to provided threshold).
     *
     * @param internalMatrix          matrix to be checked.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values are equal.
     * @return true if provided matrix is a valid essential matrix, false
     * otherwise.
     * @throws IllegalArgumentException if provided threshold is negative.
     */
    public static boolean isValidInternalMatrix(
            final Matrix internalMatrix,
            final double singularValuesThreshold) {
        if (singularValuesThreshold < 0) {
            throw new IllegalArgumentException();
        }

        if (internalMatrix.getColumns() != FUNDAMENTAL_MATRIX_COLS ||
                internalMatrix.getRows() != FUNDAMENTAL_MATRIX_ROWS) {
            return false;
        }

        try {
            final SingularValueDecomposer decomposer = new SingularValueDecomposer(
                    internalMatrix);

            decomposer.decompose();

            final double rankEssential = decomposer.getRank();

            if (rankEssential != FUNDAMENTAL_MATRIX_RANK) {
                return false;
            }

            final double[] singularValues = decomposer.getSingularValues();

            return (Math.abs(singularValues[0] - singularValues[1]) <= singularValuesThreshold);
        } catch (final AlgebraException e) {
            return false;
        }
    }

    /**
     * Sets essential matrix from provided pair of cameras.
     *
     * @param leftCamera  camera corresponding to left view.
     * @param rightCamera camera corresponding to right view.
     * @throws InvalidPairOfCamerasException if provided cameras do not span a
     *                                       valid epipolar geometry (i.e. they are set in a degenerate
     *                                       configuration).
     */
    @Override
    public void setFromPairOfCameras(
            final PinholeCamera leftCamera,
            final PinholeCamera rightCamera) throws InvalidPairOfCamerasException {
        setFromPairOfCameras(leftCamera, rightCamera,
                DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Sets essential matrix from provided pair of cameras.
     *
     * @param leftCamera              camera corresponding to left view.
     * @param rightCamera             camera corresponding to right view.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values of generated essential matrix are equal.
     * @throws InvalidPairOfCamerasException if provided cameras do not span a
     *                                       valid epipolar geometry (i.e. they are set in a degenerate configuration).
     * @throws IllegalArgumentException      if provided threshold is negative.
     */
    public final void setFromPairOfCameras(
            final PinholeCamera leftCamera,
            final PinholeCamera rightCamera,
            final double singularValuesThreshold)
            throws InvalidPairOfCamerasException {

        if (singularValuesThreshold < 0) {
            throw new IllegalArgumentException();
        }

        try {
            // normalize cameras to increase accuracy of results and fix their signs
            // if needed
            leftCamera.normalize();
            rightCamera.normalize();

            if (!leftCamera.isCameraSignFixed()) {
                leftCamera.fixCameraSign();
            }
            if (!rightCamera.isCameraSignFixed()) {
                rightCamera.fixCameraSign();
            }

            // Obtain intrinsic parameters of cameras to obtain normalized pinhole
            // cameras where intrinsic parameters have been removed P1' = inv(K) * P1
            if (!leftCamera.areIntrinsicParametersAvailable()) {
                leftCamera.decompose(true, false);
            }
            if (!rightCamera.areIntrinsicParametersAvailable()) {
                rightCamera.decompose(true, false);
            }

            final PinholeCameraIntrinsicParameters leftIntrinsics =
                    leftCamera.getIntrinsicParameters();
            final PinholeCameraIntrinsicParameters rightIntrinsics =
                    rightCamera.getIntrinsicParameters();

            final Matrix leftIntrinsicsMatrix = leftIntrinsics.getInternalMatrix();
            final Matrix rightIntrinsicsMatrix = rightIntrinsics.getInternalMatrix();

            // get left and right internal matrices of cameras
            final Matrix leftCameraInternalMatrix = leftCamera.getInternalMatrix();
            final Matrix rightCameraInternalMatrix = rightCamera.getInternalMatrix();

            // normalize internal camera matrices using inverse intrinsic matrices
            final Matrix invLeftIntrinsicsMatrix = Utils.inverse(
                    leftIntrinsicsMatrix);
            final Matrix invRightIntrinsicsMatrix = Utils.inverse(
                    rightIntrinsicsMatrix);

            // normalize cameras
            // P1' = inv(K1) * P1
            invLeftIntrinsicsMatrix.multiply(leftCameraInternalMatrix);

            // P2' = inv(K2) * P2
            invRightIntrinsicsMatrix.multiply(rightCameraInternalMatrix);

            // instantiate normalized left camera to project right camera center
            // and obtain left eipole
            final PinholeCamera normLeftCamera = new PinholeCamera(invLeftIntrinsicsMatrix);

            // instantiate normalized right camera to decompose it and obtain its
            // center
            final PinholeCamera normRightCamera = new PinholeCamera(
                    invRightIntrinsicsMatrix);

            normRightCamera.decompose(false, true);

            final Point3D rightCameraCenter = normRightCamera.getCameraCenter();
            final Point2D normLeftEpipole = normLeftCamera.project(rightCameraCenter);
            // to increase accuracy
            normLeftEpipole.normalize();

            // compute skew matrix of left epipole
            final Matrix skewNormLeftEpipoleMatrix = Utils.skewMatrix(new double[]{
                    normLeftEpipole.getHomX(), normLeftEpipole.getHomY(),
                    normLeftEpipole.getHomW()});

            // compute transposed of internal normalized left pinhole camera
            final Matrix transNormLeftCameraMatrix =
                    invLeftIntrinsicsMatrix.transposeAndReturnNew();

            // compute transposed of internal normalized right pinhole camera
            final Matrix transNormRightCameraMatrix =
                    invRightIntrinsicsMatrix.transposeAndReturnNew();

            // compute pseudo-inverse of transposed normalized right pinhole camera
            final Matrix pseudoTransNormRightCameraMatrix = Utils.pseudoInverse(
                    transNormRightCameraMatrix);

            // obtain essential matrix as: inv(P2norm') * P1norm' * skew(e1)
            transNormLeftCameraMatrix.multiply(skewNormLeftEpipoleMatrix);
            pseudoTransNormRightCameraMatrix.multiply(transNormLeftCameraMatrix);

            setInternalMatrix(pseudoTransNormRightCameraMatrix, singularValuesThreshold);
        } catch (final GeometryException | AlgebraException e) {
            throw new InvalidPairOfCamerasException(e);
        }
    }

    /**
     * Sets essential matrix from provided rotation and translation of the image
     * of world origin relative to left view camera, which is assumed to be
     * located at origin of coordinates with no rotation.
     *
     * @param rotation    rotation of right camera relative to left camera.
     * @param translation translation of the image of world origin on right
     *                    camera relative to left camera.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                translation yield a degenerate epipolar geometry.
     */
    public void setFromRotationAndTranslation(
            final Rotation3D rotation,
            final Point2D translation) throws InvalidRotationAndTranslationException {
        setFromRotationAndTranslation(rotation, translation,
                DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Sets essential matrix from provided rotation and translation of the image
     * of world origin relative to left view camera, which is assumed to be
     * located at origin of coordinates with no rotation.
     *
     * @param rotation                rotation of right camera relative to left camera.
     * @param translation             translation of the image of world origin on right
     *                                camera relative to left camera.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values of generated essential matrix are equal.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                translation yield a degenerate epipolar geometry.
     * @throws IllegalArgumentException               if provided threshold is negative.
     */
    public final void setFromRotationAndTranslation(
            final Rotation3D rotation,
            final Point2D translation,
            final double singularValuesThreshold)
            throws InvalidRotationAndTranslationException {

        if (singularValuesThreshold < 0) {
            throw new IllegalArgumentException();
        }

        try {
            // to increase accuracy
            translation.normalize();
            final double[] translationArray = new double[]{
                    translation.getHomX(), translation.getHomY(), translation.getHomW()
            };

            final Matrix skewTranslationMatrix = Utils.skewMatrix(translationArray);

            final Matrix rotationMatrix = rotation.asInhomogeneousMatrix();

            // obtain essential matrix as: skew(translation) * rotation
            skewTranslationMatrix.multiply(rotationMatrix);

            setInternalMatrix(skewTranslationMatrix, singularValuesThreshold);
        } catch (final AlgebraException | InvalidEssentialMatrixException e) {
            throw new InvalidRotationAndTranslationException(e);
        }
    }

    /**
     * Sets essential matrix from provided rotation and translation of the
     * camera center relative to left view camera, which is assumed to be
     * located at origin of coordinates with no rotation.
     *
     * @param rotation     rotation of right camera relative to left camera.
     * @param cameraCenter camera center of right camera relative to left camera.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                camera center yield a degenerate epipolar geometry.
     */
    public void setFromRotationAndCameraCenter(
            final Rotation3D rotation,
            final Point3D cameraCenter) throws InvalidRotationAndTranslationException {
        setFromRotationAndCameraCenter(rotation, cameraCenter,
                DEFAULT_SINGULAR_VALUES_THRESHOLD);
    }

    /**
     * Sets essential matrix from provided rotation and translation of the
     * camera center relative to left view camera, which is assumed to be
     * located at origin of coordinates with no rotation.
     *
     * @param rotation                rotation of right camera relative to left camera.
     * @param cameraCenter            camera center of right camera relative to left camera.
     * @param singularValuesThreshold threshold to determine that both singular
     *                                values of generated essential matrix are equal.
     * @throws InvalidRotationAndTranslationException if provided rotation and
     *                                                camera center yield a degenerate epipolar geometry.
     * @throws IllegalArgumentException               if provided threshold is negative.
     */
    public final void setFromRotationAndCameraCenter(
            final Rotation3D rotation,
            final Point3D cameraCenter,
            final double singularValuesThreshold) throws InvalidRotationAndTranslationException {

        if (singularValuesThreshold < 0) {
            throw new IllegalArgumentException();
        }

        try {
            Matrix rotationMatrix = rotation.asInhomogeneousMatrix();

            // to increase accuracy
            cameraCenter.normalize();
            final Matrix inhomCenterMatrix = new Matrix(
                    Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH, 1);
            inhomCenterMatrix.setElementAtIndex(0, cameraCenter.getInhomX());
            inhomCenterMatrix.setElementAtIndex(1, cameraCenter.getInhomY());
            inhomCenterMatrix.setElementAtIndex(2, cameraCenter.getInhomZ());

            // translationMatrix = -rotationMatrix * inhomCenterMatrix
            rotationMatrix.multiplyByScalar(-1.0);
            rotationMatrix.multiply(inhomCenterMatrix);
            final Matrix translationMatrix = rotationMatrix;

            // essentialMatrix = skew(translationMatrix) * rotationMatrix
            final Matrix skewTranslationMatrix = Utils.skewMatrix(translationMatrix);
            rotationMatrix = rotation.asInhomogeneousMatrix();
            skewTranslationMatrix.multiply(rotationMatrix);

            setInternalMatrix(skewTranslationMatrix, singularValuesThreshold);

        } catch (final AlgebraException | InvalidEssentialMatrixException e) {
            throw new InvalidRotationAndTranslationException(e);
        }
    }

    /**
     * Sets essential matrix from provided fundamental matrix and intrinsic
     * camera parameters.
     *
     * @param fundamentalMatrix        a fundamental matrix.
     * @param leftIntrinsicParameters  intrinsic camera parameters of left view.
     * @param rightIntrinsicParameters intrinsic camera parameters of right view.
     * @throws InvalidPairOfIntrinsicParametersException if provided intrinsic
     *                                                   parameters generate an invalid essential matrix.
     */
    public final void setFromFundamentalMatrixAndIntrinsics(
            final FundamentalMatrix fundamentalMatrix,
            final PinholeCameraIntrinsicParameters leftIntrinsicParameters,
            final PinholeCameraIntrinsicParameters rightIntrinsicParameters)
            throws InvalidPairOfIntrinsicParametersException {

        try {
            final Matrix k1 = leftIntrinsicParameters.getInternalMatrix();
            final double normK1 = Utils.normF(k1);
            k1.multiplyByScalar(1.0 / normK1);

            final Matrix k2 = rightIntrinsicParameters.getInternalMatrix();
            final double normK2 = Utils.normF(k2);
            k2.multiplyByScalar(1.0 / normK2);

            // to increase accuracy
            fundamentalMatrix.normalize();
            final Matrix fundMatrix = fundamentalMatrix.getInternalMatrix();

            k2.transpose();

            // E = K2' * F * K1
            fundMatrix.multiply(k1);
            k2.multiply(fundMatrix);

            final double normEssential = Utils.normF(k2);
            k2.multiplyByScalar(1.0 / normEssential);

            mInternalMatrix = k2;
            mNormalized = false;
            mLeftEpipole = mRightEpipole = null;
        } catch (final AlgebraException | GeometryException e) {
            throw new InvalidPairOfIntrinsicParametersException(e);
        }
    }

    /**
     * Converts this essential matrix into a fundamental matrix by applying
     * provided intrinsic parameters on left and right views.
     * The essential matrix only contains information about rotation and
     * translation relating two views, while fundamental matrix also contains
     * information about the intrinsic parameters in both views.
     * NOTE: although essential matrix is a subclass of fundamental matrix, it
     * does not behave like a fundamental matrix.
     *
     * @param leftIntrinsicParameters  intrinsic parameters in left view.
     * @param rightIntrinsicParameters intrinsic parameters in right view.
     * @return a fundamental matrix.
     * @throws EpipolarException if something fails.
     */
    public FundamentalMatrix toFundamentalMatrix(
            final PinholeCameraIntrinsicParameters leftIntrinsicParameters,
            final PinholeCameraIntrinsicParameters rightIntrinsicParameters)
            throws EpipolarException {
        try {
            normalize();

            final Matrix essentialMatrix = getInternalMatrix();

            final Matrix k1 = leftIntrinsicParameters.getInternalMatrix();
            final Matrix invK1 = Utils.inverse(k1);
            final double normInvK1 = Utils.normF(invK1);
            invK1.multiplyByScalar(1.0 / normInvK1);

            final Matrix k2 = rightIntrinsicParameters.getInternalMatrix();
            final Matrix invK2 = Utils.inverse(k2);
            final double normInvK2 = Utils.normF(invK2);
            invK2.multiplyByScalar(1.0 / normInvK2);
            invK2.transpose();

            essentialMatrix.multiply(invK1);
            invK2.multiply(essentialMatrix);

            return new FundamentalMatrix(invK2);

        } catch (final AlgebraException | GeometryException e) {
            throw new EpipolarException(e);
        }
    }

    /**
     * Computes all possible camera rotations and translations that can generate
     * this essential matrix.
     *
     * @throws InvalidEssentialMatrixException if essential matrix contains
     *                                         numerically unstable values.
     */
    public void computePossibleRotationAndTranslations()
            throws InvalidEssentialMatrixException {
        try {
            final SingularValueDecomposer decomposer = new SingularValueDecomposer(
                    mInternalMatrix);

            decomposer.decompose();

            final Matrix u = decomposer.getU();
            final Matrix v = decomposer.getV();

            v.transpose();

            mTranslation1 = new HomogeneousPoint2D(u.getElementAt(0, 2),
                    u.getElementAt(1, 2), u.getElementAt(2, 2));
            mTranslation2 = new HomogeneousPoint2D(-u.getElementAt(0, 2),
                    -u.getElementAt(1, 2), -u.getElementAt(2, 2));

            // W is a skew-symmetric matrix that can be used to obtain two possible
            // rotations
            final Matrix w = new Matrix(FUNDAMENTAL_MATRIX_ROWS, FUNDAMENTAL_MATRIX_COLS);
            w.setElementAt(0, 1, -1.0);
            w.setElementAt(1, 0, 1.0);
            w.setElementAt(2, 2, 1.0);

            final Matrix transW = w.transposeAndReturnNew();

            // R1 = U * W * V'
            w.multiply(v);
            final Matrix rotationMatrix1 = u.multiplyAndReturnNew(w);

            // R2 = U * W' * V'
            transW.multiply(v);
            final Matrix rotationMatrix2 = u.multiplyAndReturnNew(transW);

            mRotation1 = new MatrixRotation3D(rotationMatrix1);
            mRotation2 = new MatrixRotation3D(rotationMatrix2);

            mPossibleRotationsAndTranslationsAvailable = true;
        } catch (final AlgebraException | InvalidRotationMatrixException e) {
            throw new InvalidEssentialMatrixException(e);
        }
    }

    /**
     * Indicates whether possible camera rotations and translations that can
     * generate this essential matrix have already been computed or not.
     *
     * @return true if possible camera rotations and translations have been
     * computed, false otherwise.
     */
    public boolean arePossibleRotationsAndTranslationsAvailable() {
        return mPossibleRotationsAndTranslationsAvailable;
    }

    /**
     * Gets first possible rotation that can generate this essential matrix.
     *
     * @return first possible rotation.
     * @throws NotAvailableException if possible rotation has not yet been
     *                               computed.
     */
    public Rotation3D getFirstPossibleRotation() throws NotAvailableException {
        if (!arePossibleRotationsAndTranslationsAvailable()) {
            throw new NotAvailableException();
        }

        return mRotation1;
    }

    /**
     * Gets second possible rotation that can generate this essential matrix.
     *
     * @return second possible rotation.
     * @throws NotAvailableException if possible rotation has not yet been
     *                               computed.
     */
    public Rotation3D getSecondPossibleRotation() throws NotAvailableException {
        if (!arePossibleRotationsAndTranslationsAvailable()) {
            throw new NotAvailableException();
        }

        return mRotation2;
    }

    /**
     * Gets first possible translation that can generate this essential matrix.
     *
     * @return first possible translation.
     * @throws NotAvailableException if possible translation has not yet been
     *                               computed.
     */
    public Point2D getFirstPossibleTranslation() throws NotAvailableException {
        if (!arePossibleRotationsAndTranslationsAvailable()) {
            throw new NotAvailableException();
        }

        return mTranslation1;
    }

    /**
     * Gets second possible translation that can generate this essential matrix.
     *
     * @return second possible translation.
     * @throws NotAvailableException if possible translation has not yet been
     *                               computed.
     */
    public Point2D getSecondPossibleTranslation() throws NotAvailableException {
        if (!arePossibleRotationsAndTranslationsAvailable()) {
            throw new NotAvailableException();
        }

        return mTranslation2;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy