Conversion
parent
76175feb95
commit
32980f3e09
|
@ -62,9 +62,58 @@ class GeneralFundamentalMatrix : public FundamentalMatrix {
|
||||||
GeneralFundamentalMatrix(const Rot3& U, double s, const Rot3& V)
|
GeneralFundamentalMatrix(const Rot3& U, double s, const Rot3& V)
|
||||||
: U_(U), s_(s), V_(V) {}
|
: U_(U), s_(s), V_(V) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct from a 3x3 matrix using SVD
|
||||||
|
*
|
||||||
|
* Initializes the GeneralFundamentalMatrix by performing SVD on the given
|
||||||
|
* matrix and ensuring U and V are not reflections.
|
||||||
|
*
|
||||||
|
* @param F A 3x3 matrix representing the fundamental matrix
|
||||||
|
*/
|
||||||
|
GeneralFundamentalMatrix(const Matrix3& F) {
|
||||||
|
// Perform SVD
|
||||||
|
Eigen::JacobiSVD<Matrix3> svd(F, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||||
|
|
||||||
|
// Extract U and V
|
||||||
|
Matrix3 U = svd.matrixU();
|
||||||
|
Matrix3 V = svd.matrixV();
|
||||||
|
Vector3 singularValues = svd.singularValues();
|
||||||
|
|
||||||
|
// Scale the singular values
|
||||||
|
double scale = singularValues(0);
|
||||||
|
if (scale != 0) {
|
||||||
|
singularValues /= scale; // Normalize the first singular value to 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the third singular value is close to zero (valid F condition)
|
||||||
|
if (std::abs(singularValues(2)) > 1e-9) {
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"The input matrix does not represent a valid fundamental matrix.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the second singular value is recorded as s
|
||||||
|
s_ = singularValues(1);
|
||||||
|
|
||||||
|
// Check if U is a reflection
|
||||||
|
if (U.determinant() < 0) {
|
||||||
|
U = -U;
|
||||||
|
s_ = -s_; // Change sign of scalar if U is a reflection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if V is a reflection
|
||||||
|
if (V.determinant() < 0) {
|
||||||
|
V = -V;
|
||||||
|
s_ = -s_; // Change sign of scalar if U is a reflection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the rotations
|
||||||
|
U_ = Rot3(U);
|
||||||
|
V_ = Rot3(V);
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the fundamental matrix representation
|
/// Return the fundamental matrix representation
|
||||||
Matrix3 matrix() const override {
|
Matrix3 matrix() const override {
|
||||||
return U_.matrix() * Vector3(1, s_, 1).asDiagonal() *
|
return U_.matrix() * Vector3(1, s_, 0).asDiagonal() *
|
||||||
V_.transpose().matrix();
|
V_.transpose().matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,14 +190,24 @@ class SimpleFundamentalMatrix : FundamentalMatrix {
|
||||||
const Point2& cb = Point2(0.0, 0.0))
|
const Point2& cb = Point2(0.0, 0.0))
|
||||||
: E_(E), fa_(fa), fb_(fb), ca_(ca), cb_(cb) {}
|
: E_(E), fa_(fa), fb_(fb), ca_(ca), cb_(cb) {}
|
||||||
|
|
||||||
/// Return the fundamental matrix representation
|
/// Return the left calibration matrix
|
||||||
Matrix3 matrix() const override {
|
Matrix3 leftK() const {
|
||||||
Matrix3 Ka, Kb;
|
Matrix3 K;
|
||||||
Ka << fa_, 0, ca_.x(), 0, fa_, ca_.y(), 0, 0, 1; // Left calibration
|
K << fa_, 0, ca_.x(), 0, fa_, ca_.y(), 0, 0, 1;
|
||||||
Kb << fb_, 0, cb_.x(), 0, fb_, cb_.y(), 0, 0, 1; // Right calibration
|
return K;
|
||||||
return Ka * E_.matrix() * Kb.inverse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the right calibration matrix
|
||||||
|
Matrix3 rightK() const {
|
||||||
|
Matrix3 K;
|
||||||
|
K << fb_, 0, cb_.x(), 0, fb_, cb_.y(), 0, 0, 1;
|
||||||
|
return K;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the fundamental matrix representation
|
||||||
|
Matrix3 matrix() const override {
|
||||||
|
return leftK().transpose().inverse() * E_.matrix() * rightK().inverse();
|
||||||
|
}
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
|
|
@ -49,37 +49,122 @@ TEST(GeneralFundamentalMatrix, RoundTrip) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// Create essential matrix and focal lengths for
|
// Create the simplest SimpleFundamentalMatrix, a stereo pair
|
||||||
// SimpleFundamentalMatrix
|
EssentialMatrix defaultE(Rot3(), Unit3(1, 0, 0));
|
||||||
EssentialMatrix trueE; // Assuming a default constructor is available
|
Point2 zero(0.0, 0.0);
|
||||||
double trueFa = 1.0;
|
SimpleFundamentalMatrix stereoF(defaultE, 1.0, 1.0, zero, zero);
|
||||||
double trueFb = 1.0;
|
|
||||||
Point2 trueCa(0.0, 0.0);
|
|
||||||
Point2 trueCb(0.0, 0.0);
|
|
||||||
SimpleFundamentalMatrix trueSimpleF(trueE, trueFa, trueFb, trueCa, trueCb);
|
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(SimpleFundamentalMatrix, localCoordinates) {
|
TEST(SimpleStereo, Conversion) {
|
||||||
|
GeneralFundamentalMatrix convertedF(stereoF.matrix());
|
||||||
|
EXPECT(assert_equal(stereoF.matrix(), convertedF.matrix(), 1e-8));
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(SimpleStereo, localCoordinates) {
|
||||||
Vector expected = Z_7x1;
|
Vector expected = Z_7x1;
|
||||||
Vector actual = trueSimpleF.localCoordinates(trueSimpleF);
|
Vector actual = stereoF.localCoordinates(stereoF);
|
||||||
EXPECT(assert_equal(expected, actual, 1e-8));
|
EXPECT(assert_equal(expected, actual, 1e-8));
|
||||||
}
|
}
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(SimpleFundamentalMatrix, retract) {
|
TEST(SimpleStereo, retract) {
|
||||||
SimpleFundamentalMatrix actual = trueSimpleF.retract(Z_9x1);
|
SimpleFundamentalMatrix actual = stereoF.retract(Z_9x1);
|
||||||
EXPECT(assert_equal(trueSimpleF, actual));
|
EXPECT(assert_equal(stereoF, actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(SimpleFundamentalMatrix, RoundTrip) {
|
TEST(SimpleStereo, RoundTrip) {
|
||||||
Vector7 d;
|
Vector7 d;
|
||||||
d << 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7;
|
d << 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7;
|
||||||
SimpleFundamentalMatrix hx = trueSimpleF.retract(d);
|
SimpleFundamentalMatrix hx = stereoF.retract(d);
|
||||||
Vector actual = trueSimpleF.localCoordinates(hx);
|
Vector actual = stereoF.localCoordinates(hx);
|
||||||
EXPECT(assert_equal(d, actual, 1e-8));
|
EXPECT(assert_equal(d, actual, 1e-8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(SimpleStereo, EpipolarLine) {
|
||||||
|
// Create a point in b
|
||||||
|
Point3 p_b(0, 2, 1);
|
||||||
|
// Convert the point to a horizontal line in a
|
||||||
|
Vector3 l_a = stereoF.matrix() * p_b;
|
||||||
|
// Check if the line is horizontal at height 2
|
||||||
|
EXPECT(assert_equal(Vector3(0, -1, 2), l_a));
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
// Create a stereo pair, but in pixels not normalized coordinates.
|
||||||
|
// We're still using zero principal points here.
|
||||||
|
double fa = 1000;
|
||||||
|
double fb = 1000;
|
||||||
|
SimpleFundamentalMatrix pixelStereo(defaultE, fa, fb, zero, zero);
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(PixelStereo, Conversion) {
|
||||||
|
auto expected = pixelStereo.matrix();
|
||||||
|
|
||||||
|
GeneralFundamentalMatrix convertedF(pixelStereo.matrix());
|
||||||
|
|
||||||
|
// Check equality of F-matrices up to a scale
|
||||||
|
auto actual = convertedF.matrix();
|
||||||
|
actual *= expected(1, 2) / actual(1, 2);
|
||||||
|
EXPECT(assert_equal(expected, actual, 1e-5));
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(PixelStereo, PointInBToHorizontalLineInA) {
|
||||||
|
// Create a point in b
|
||||||
|
Point3 p_b = Point3(0, 300, 1);
|
||||||
|
// Convert the point to a horizontal line in a
|
||||||
|
Vector3 l_a = pixelStereo.matrix() * p_b;
|
||||||
|
// Check if the line is horizontal at height 2
|
||||||
|
EXPECT(assert_equal(Vector3(0, -0.001, 0.3), l_a));
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
// Create a stereo pair with the right camera rotated 90 degrees
|
||||||
|
Rot3 aRb = Rot3::Rz(M_PI_2); // Rotate 90 degrees around the Z-axis
|
||||||
|
EssentialMatrix rotatedE(aRb, Unit3(1, 0, 0));
|
||||||
|
SimpleFundamentalMatrix rotatedPixelStereo(rotatedE, fa, fb, zero, zero);
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(RotatedPixelStereo, Conversion) {
|
||||||
|
auto expected = rotatedPixelStereo.matrix();
|
||||||
|
|
||||||
|
GeneralFundamentalMatrix convertedF(rotatedPixelStereo.matrix());
|
||||||
|
|
||||||
|
// Check equality of F-matrices up to a scale
|
||||||
|
auto actual = convertedF.matrix();
|
||||||
|
actual *= expected(1, 2) / actual(1, 2);
|
||||||
|
EXPECT(assert_equal(expected, actual, 1e-4));
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(RotatedPixelStereo, PointInBToHorizontalLineInA) {
|
||||||
|
// Create a point in b
|
||||||
|
Point3 p_b = Point3(300, 0, 1);
|
||||||
|
// Convert the point to a horizontal line in a
|
||||||
|
Vector3 l_a = rotatedPixelStereo.matrix() * p_b;
|
||||||
|
// Check if the line is horizontal at height 2
|
||||||
|
EXPECT(assert_equal(Vector3(0, -0.001, 0.3), l_a));
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
// Now check that principal points also survive conversion
|
||||||
|
SimpleFundamentalMatrix stereoWithPrincipalPoints(rotatedE, fa, fb, zero, zero);
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(stereoWithPrincipalPoints, Conversion) {
|
||||||
|
auto expected = stereoWithPrincipalPoints.matrix();
|
||||||
|
|
||||||
|
GeneralFundamentalMatrix convertedF(stereoWithPrincipalPoints.matrix());
|
||||||
|
|
||||||
|
// Check equality of F-matrices up to a scale
|
||||||
|
auto actual = convertedF.matrix();
|
||||||
|
actual *= expected(1, 2) / actual(1, 2);
|
||||||
|
EXPECT(assert_equal(expected, actual, 1e-4));
|
||||||
|
}
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
int main() {
|
int main() {
|
||||||
TestResult tr;
|
TestResult tr;
|
||||||
|
|
Loading…
Reference in New Issue