Add Homogeneous vector parameterization.
Change-Id: I42f68a0665a62e2c7dcc7584e5581a05c9b849b0
diff --git a/docs/source/nnls_modeling.rst b/docs/source/nnls_modeling.rst
index 45ccd98..6772efe 100644
--- a/docs/source/nnls_modeling.rst
+++ b/docs/source/nnls_modeling.rst
@@ -1140,6 +1140,29 @@
product. :class:`QuaternionParameterization` is an implementation
of :eq:`quaternion`.
+.. class:: HomogeneousVectorParameterization
+
+ In computer vision, homogeneous vectors are commonly used to
+ represent entities in projective geometry such as points in
+ projective space. One example where it is useful to use this
+ over-parameterization is in representing points whose triangulation
+ is ill-conditioned. Here it is advantageous to use homogeneous
+ vectors, instead of an Euclidean vector, because it can represent
+ points at infinity.
+
+ When using homogeneous vectors it is useful to only make updates
+ orthogonal to that :math:`n`-vector defining the homogeneous
+ vector [HartleyZisserman]_. One way to do this is to let :math:`\Delta x`
+ be a :math:`n-1` dimensional vector and define :math:`\boxplus` to be
+
+ .. math:: \boxplus(x, \Delta x) = \left[ \frac{\sin\left(0.5 |\Delta x|\right)}{|\Delta x|} \Delta x, \cos(0.5 |\Delta x|) \right] * x
+
+ The multiplication between the two vectors on the right hand side
+ is defined as an operator which applies the update orthogonal to
+ :math:`x` to remain on the sphere. Note, it is assumed that
+ last element of :math:`x` is the scalar component of the homogeneous
+ vector.
+
:class:`AutoDiffLocalParameterization`
diff --git a/include/ceres/local_parameterization.h b/include/ceres/local_parameterization.h
index 87a2b76..4294677 100644
--- a/include/ceres/local_parameterization.h
+++ b/include/ceres/local_parameterization.h
@@ -210,6 +210,37 @@
virtual int LocalSize() const { return 3; }
};
+
+// This provides a parameterization for homogeneous vectors which are commonly
+// used in Structure for Motion problems. One example where they are used is
+// in representing points whose triangulation is ill-conditioned. Here
+// it is advantageous to use an over-parameterization since homogeneous vectors
+// can represent points at infinity.
+//
+// The plus operator is defined as
+// Plus(x, delta) =
+// [sin(0.5 * |delta|) * delta / |delta|, cos(0.5 * |delta|)] * x
+// with * defined as an operator which applies the update orthogonal to x to
+// remain on the sphere. We assume that the last element of x is the scalar
+// component. The size of the homogeneous vector is required to be greater than
+// 1.
+class CERES_EXPORT HomogeneousVectorParameterization :
+ public LocalParameterization {
+ public:
+ explicit HomogeneousVectorParameterization(int size);
+ virtual ~HomogeneousVectorParameterization() {}
+ virtual bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const;
+ virtual bool ComputeJacobian(const double* x,
+ double* jacobian) const;
+ virtual int GlobalSize() const { return size_; }
+ virtual int LocalSize() const { return size_ - 1; }
+
+ private:
+ const int size_;
+};
+
} // namespace ceres
#include "ceres/internal/reenable_warnings.h"
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 710a3f2..fedeafe 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -260,6 +260,7 @@
CERES_TEST(gradient_checking_cost_function)
CERES_TEST(graph)
CERES_TEST(graph_algorithms)
+ CERES_TEST(householder_vector)
CERES_TEST(implicit_schur_complement)
CERES_TEST(iterative_schur_complement_solver)
CERES_TEST(jet)
diff --git a/internal/ceres/householder_vector.h b/internal/ceres/householder_vector.h
new file mode 100644
index 0000000..f54feea
--- /dev/null
+++ b/internal/ceres/householder_vector.h
@@ -0,0 +1,85 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2015 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vitus@google.com (Michael Vitus)
+
+#ifndef CERES_PUBLIC_HOUSEHOLDER_VECTOR_H_
+#define CERES_PUBLIC_HOUSEHOLDER_VECTOR_H_
+
+#include "Eigen/Core"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+// Algorithm 5.1.1 from 'Matrix Computations' by Golub et al. (Johns Hopkins
+// Studies in Mathematical Sciences) but using the nth element of the input
+// vector as pivot instead of first. This computes the vector v with v(n) = 1
+// and beta such that H = I - beta * v * v^T is orthogonal and
+// H * x = ||x||_2 * e_n.
+template <typename Scalar>
+void ComputeHouseholderVector(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x,
+ Eigen::Matrix<Scalar, Eigen::Dynamic, 1>* v,
+ Scalar* beta) {
+ CHECK_NOTNULL(beta);
+ CHECK_NOTNULL(v);
+ CHECK_GT(x.rows(), 1);
+ CHECK_EQ(x.rows(), v->rows());
+
+ Scalar sigma = x.head(x.rows() - 1).squaredNorm();
+ *v = x;
+ (*v)(v->rows() - 1) = Scalar(1.0);
+
+ *beta = Scalar(0.0);
+ const Scalar& x_pivot = x(x.rows() - 1);
+
+ if (sigma <= Scalar(std::numeric_limits<double>::epsilon())) {
+ if (x_pivot < Scalar(0.0)) {
+ *beta = Scalar(2.0);
+ }
+ return;
+ }
+
+ const Scalar mu = sqrt(x_pivot * x_pivot + sigma);
+ Scalar v_pivot = Scalar(1.0);
+
+ if (x_pivot <= Scalar(0.0)) {
+ v_pivot = x_pivot - mu;
+ } else {
+ v_pivot = -sigma / (x_pivot + mu);
+ }
+
+ *beta = Scalar(2.0) * v_pivot * v_pivot / (sigma + v_pivot * v_pivot);
+
+ v->head(v->rows() - 1) /= v_pivot;
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_PUBLIC_HOUSEHOLDER_VECTOR_H_
diff --git a/internal/ceres/householder_vector_test.cc b/internal/ceres/householder_vector_test.cc
new file mode 100644
index 0000000..fca0360
--- /dev/null
+++ b/internal/ceres/householder_vector_test.cc
@@ -0,0 +1,115 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2015 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vitus@google.com (Michael Vitus)
+
+#include "ceres/householder_vector.h"
+#include "ceres/internal/eigen.h"
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+namespace ceres {
+namespace internal {
+
+void HouseholderTestHelper(const Vector& x) {
+ const double kTolerance = 1e-14;
+
+ // Check to ensure that H * x = ||x|| * [0 ... 0 1]'.
+ Vector v(x.rows());
+ double beta;
+ ComputeHouseholderVector(x, &v, &beta);
+ Vector result = x - beta * v * (v.transpose() * x);
+
+ Vector expected_result(x.rows());
+ expected_result.setZero();
+ expected_result(x.rows() - 1) = 1;
+ expected_result *= x.norm();
+
+ for (int i = 0; i < x.rows(); ++i) {
+ EXPECT_NEAR(expected_result[i], result[i], kTolerance);
+ }
+}
+
+TEST(HouseholderVector, ZeroPositive) {
+ Vector x(3);
+ x << 0.0, 0.0, 0.25;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, ZeroNegative) {
+ Vector x(3);
+ x << 0.0, 0.0, -0.25;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, NearZeroPositive) {
+ Vector x(3);
+ x << 1e-18, 1e-18, 0.25;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, NearZeroNegative) {
+ Vector x(3);
+ x << 1e-18, 1e-18, -0.25;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, NonZeroNegative) {
+ Vector x(3);
+ x << 1.0, 0.0, -3.0;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, NonZeroPositive) {
+ Vector x(3);
+ x << 1.0, 1.0, 1.0;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, NonZeroPositive_Size4) {
+ Vector x(4);
+ x << 1.0, 1.0, 0.0, 2.0;
+
+ HouseholderTestHelper(x);
+}
+
+TEST(HouseholderVector, LastElementZero) {
+ Vector x(4);
+ x << 1.0, 1.0, 0.0, 0.0;
+
+ HouseholderTestHelper(x);
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/internal/ceres/local_parameterization.cc b/internal/ceres/local_parameterization.cc
index 5e938c7..aa21e6b 100644
--- a/internal/ceres/local_parameterization.cc
+++ b/internal/ceres/local_parameterization.cc
@@ -30,6 +30,7 @@
#include "ceres/local_parameterization.h"
+#include "ceres/householder_vector.h"
#include "ceres/internal/eigen.h"
#include "ceres/rotation.h"
#include "glog/logging.h"
@@ -182,4 +183,68 @@
return true;
}
+HomogeneousVectorParameterization::HomogeneousVectorParameterization(int size)
+ : size_(size) {
+ CHECK_GT(size_, 1) << "The size of the homogeneous vector needs to be "
+ << "greater than 1.";
+}
+
+bool HomogeneousVectorParameterization::Plus(const double* x_ptr,
+ const double* delta_ptr,
+ double* x_plus_delta_ptr) const {
+ ConstVectorRef x(x_ptr, size_);
+ ConstVectorRef delta(delta_ptr, size_ - 1);
+ VectorRef x_plus_delta(x_plus_delta_ptr, size_);
+
+ const double norm_delta = delta.norm();
+
+ if (norm_delta == 0.0) {
+ x_plus_delta = x;
+ return true;
+ }
+
+ // Map the delta from the minimum representation to the over parameterized
+ // homogeneous vector. See section A6.9.2 on page 624 of Hartley & Zisserman
+ // (2nd Edition) for a detailed description. Note there is a typo on Page
+ // 625, line 4 so check the book errata.
+ const double norm_delta_div_2 = 0.5 * norm_delta;
+ const double sin_delta_by_delta = sin(norm_delta_div_2) /
+ norm_delta_div_2;
+
+ Vector y(size_);
+ y.head(size_ - 1) = 0.5 * sin_delta_by_delta * delta;
+ y(size_ - 1) = cos(norm_delta_div_2);
+
+ Vector v(size_);
+ double beta;
+ internal::ComputeHouseholderVector<double>(x, &v, &beta);
+
+ // Apply the delta update to remain on the unit sphere. See section A6.9.3
+ // on page 625 of Hartley & Zisserman (2nd Edition) for a detailed
+ // description.
+ x_plus_delta = x.norm() * (y - v * (beta *(v.transpose() * y)));
+
+ return true;
+}
+
+bool HomogeneousVectorParameterization::ComputeJacobian(
+ const double* x_ptr, double* jacobian_ptr) const {
+ ConstVectorRef x(x_ptr, size_);
+ MatrixRef jacobian(jacobian_ptr, size_, size_ - 1);
+
+ Vector v(size_);
+ double beta;
+ internal::ComputeHouseholderVector<double>(x, &v, &beta);
+
+ // The Jacobian is equal to J = 0.5 * H.leftCols(size_ - 1) where H is the
+ // Householder matrix (H = I - beta * v * v').
+ for (int i = 0; i < size_ - 1; ++i) {
+ jacobian.col(i) = -0.5 * beta * v(i) * v;
+ jacobian.col(i)(i) += 0.5;
+ }
+ jacobian *= x.norm();
+
+ return true;
+}
+
} // namespace ceres
diff --git a/internal/ceres/local_parameterization_test.cc b/internal/ceres/local_parameterization_test.cc
index c899046..46f131c 100644
--- a/internal/ceres/local_parameterization_test.cc
+++ b/internal/ceres/local_parameterization_test.cc
@@ -29,7 +29,9 @@
// Author: sameeragarwal@google.com (Sameer Agarwal)
#include <cmath>
+#include "ceres/autodiff_local_parameterization.h"
#include "ceres/fpclassify.h"
+#include "ceres/householder_vector.h"
#include "ceres/internal/autodiff.h"
#include "ceres/internal/eigen.h"
#include "ceres/local_parameterization.h"
@@ -244,6 +246,11 @@
EXPECT_EQ((local_matrix - expected_local_matrix).norm(), 0.0);
}
+template <int N>
+void Normalize(double* x) {
+ VectorRef(x, N).normalize();
+}
+
TEST(QuaternionParameterization, ZeroTest) {
double x[4] = {0.5, 0.5, 0.5, 0.5};
double delta[3] = {0.0, 0.0, 0.0};
@@ -251,16 +258,9 @@
QuaternionParameterizationTestHelper(x, delta, q_delta);
}
-
TEST(QuaternionParameterization, NearZeroTest) {
double x[4] = {0.52, 0.25, 0.15, 0.45};
- double norm_x = sqrt(x[0] * x[0] +
- x[1] * x[1] +
- x[2] * x[2] +
- x[3] * x[3]);
- for (int i = 0; i < 4; ++i) {
- x[i] = x[i] / norm_x;
- }
+ Normalize<4>(x);
double delta[3] = {0.24, 0.15, 0.10};
for (int i = 0; i < 3; ++i) {
@@ -278,14 +278,7 @@
TEST(QuaternionParameterization, AwayFromZeroTest) {
double x[4] = {0.52, 0.25, 0.15, 0.45};
- double norm_x = sqrt(x[0] * x[0] +
- x[1] * x[1] +
- x[2] * x[2] +
- x[3] * x[3]);
-
- for (int i = 0; i < 4; ++i) {
- x[i] = x[i] / norm_x;
- }
+ Normalize<4>(x);
double delta[3] = {0.24, 0.15, 0.10};
const double delta_norm = sqrt(delta[0] * delta[0] +
@@ -300,6 +293,154 @@
QuaternionParameterizationTestHelper(x, delta, q_delta);
}
+// Functor needed to implement automatically differentiated Plus for
+// homogeneous vectors. Note this explicitly defined for vectors of size 4.
+struct HomogeneousVectorParameterizationPlus {
+ template<typename Scalar>
+ bool operator()(const Scalar* p_x, const Scalar* p_delta,
+ Scalar* p_x_plus_delta) const {
+ Eigen::Map<const Eigen::Matrix<Scalar, 4, 1> > x(p_x);
+ Eigen::Map<const Eigen::Matrix<Scalar, 3, 1> > delta(p_delta);
+ Eigen::Map<Eigen::Matrix<Scalar, 4, 1> > x_plus_delta(p_x_plus_delta);
+
+ const Scalar squared_norm_delta =
+ delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];
+
+ Eigen::Matrix<Scalar, 4, 1> y;
+ Scalar one_half(0.5);
+ if (squared_norm_delta > Scalar(0.0)) {
+ Scalar norm_delta = sqrt(squared_norm_delta);
+ Scalar norm_delta_div_2 = 0.5 * norm_delta;
+ const Scalar sin_delta_by_delta = sin(norm_delta_div_2) /
+ norm_delta_div_2;
+ y[0] = sin_delta_by_delta * delta[0] * one_half;
+ y[1] = sin_delta_by_delta * delta[1] * one_half;
+ y[2] = sin_delta_by_delta * delta[2] * one_half;
+ y[3] = cos(norm_delta_div_2);
+
+ } else {
+ // We do not just use y = [0,0,0,1] here because that is a
+ // constant and when used for automatic differentiation will
+ // lead to a zero derivative. Instead we take a first order
+ // approximation and evaluate it at zero.
+ y[0] = delta[0] * one_half;
+ y[1] = delta[1] * one_half;
+ y[2] = delta[2] * one_half;
+ y[3] = Scalar(1.0);
+ }
+
+ Eigen::Matrix<Scalar, Eigen::Dynamic, 1> v(4);
+ Scalar beta;
+ internal::ComputeHouseholderVector<Scalar>(x, &v, &beta);
+
+ x_plus_delta = x.norm() * (y - v * (beta * v.dot(y)));
+
+ return true;
+ }
+};
+
+void HomogeneousVectorParameterizationHelper(const double* x,
+ const double* delta) {
+ const double kTolerance = 1e-14;
+
+ HomogeneousVectorParameterization homogeneous_vector_parameterization(4);
+
+ // Ensure the update maintains the norm.
+ double x_plus_delta[4] = {0.0, 0.0, 0.0, 0.0};
+ homogeneous_vector_parameterization.Plus(x, delta, x_plus_delta);
+
+ const double x_plus_delta_norm =
+ sqrt(x_plus_delta[0] * x_plus_delta[0] +
+ x_plus_delta[1] * x_plus_delta[1] +
+ x_plus_delta[2] * x_plus_delta[2] +
+ x_plus_delta[3] * x_plus_delta[3]);
+
+ const double x_norm = sqrt(x[0] * x[0] + x[1] * x[1] +
+ x[2] * x[2] + x[3] * x[3]);
+
+ EXPECT_NEAR(x_plus_delta_norm, x_norm, kTolerance);
+
+ // Autodiff jacobian at delta_x = 0.
+ AutoDiffLocalParameterization<HomogeneousVectorParameterizationPlus, 4, 3>
+ autodiff_jacobian;
+
+ double jacobian_autodiff[12];
+ double jacobian_analytic[12];
+
+ homogeneous_vector_parameterization.ComputeJacobian(x, jacobian_analytic);
+ autodiff_jacobian.ComputeJacobian(x, jacobian_autodiff);
+
+ for (int i = 0; i < 12; ++i) {
+ EXPECT_TRUE(ceres::IsFinite(jacobian_analytic[i]));
+ EXPECT_NEAR(jacobian_analytic[i], jacobian_autodiff[i], kTolerance)
+ << "Jacobian mismatch: i = " << i << ", " << jacobian_analytic[i] << " "
+ << jacobian_autodiff[i];
+ }
+}
+
+TEST(HomogeneousVectorParameterization, ZeroTest) {
+ double x[4] = {0.0, 0.0, 0.0, 1.0};
+ Normalize<4>(x);
+ double delta[3] = {0.0, 0.0, 0.0};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, NearZeroTest1) {
+ double x[4] = {1e-5, 1e-5, 1e-5, 1.0};
+ Normalize<4>(x);
+ double delta[3] = {0.0, 1.0, 0.0};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, NearZeroTest2) {
+ double x[4] = {0.001, 0.0, 0.0, 0.0};
+ double delta[3] = {0.0, 1.0, 0.0};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, AwayFromZeroTest1) {
+ double x[4] = {0.52, 0.25, 0.15, 0.45};
+ Normalize<4>(x);
+ double delta[3] = {0.0, 1.0, -0.5};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, AwayFromZeroTest2) {
+ double x[4] = {0.87, -0.25, -0.34, 0.45};
+ Normalize<4>(x);
+ double delta[3] = {0.0, 0.0, -0.5};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, AwayFromZeroTest3) {
+ double x[4] = {0.0, 0.0, 0.0, 2.0};
+ double delta[3] = {0.0, 0.0, 0};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, AwayFromZeroTest4) {
+ double x[4] = {0.2, -1.0, 0.0, 2.0};
+ double delta[3] = {1.4, 0.0, -0.5};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, AwayFromZeroTest5) {
+ double x[4] = {2.0, 0.0, 0.0, 0.0};
+ double delta[3] = {1.4, 0.0, -0.5};
+
+ HomogeneousVectorParameterizationHelper(x, delta);
+}
+
+TEST(HomogeneousVectorParameterization, DeathTests) {
+ EXPECT_DEATH_IF_SUPPORTED(HomogeneousVectorParameterization x(1), "size");
+}
} // namespace internal
} // namespace ceres