Eigen functions of the day: normalize() and stableNormalize().
07 Jul 2022In computer vision, I find myself needing unit vectors, a lot. In other words, doing the following in C++,
Vector3d v;
// v's unnormalized value is computed.
v /= v.norm();
I also need image homogenous coordinates ‘normalized’, in other words, dividing the vector by the third element (scale):
Vector3d v;
// v's unnormalized value is computed. Something like v = P*X;
v /= v(2);
I looked up the Eigen function normalize()
, docs, and normalize()
will return a unit vector - the first case I mention. Here’s the Eigen code:
// from Dot.h
template<typename Derived>
inline void MatrixBase<Derived>::normalize()
{
RealScalar z = squaredNorm();
// NOTE: after extensive benchmarking, this conditional does not impact performance, at least on recent x86 CPU
if(z>RealScalar(0))
derived() /= numext::sqrt(z);
}
So one could write
Vector3d v;
// v's unnormalized value is computed.
v.normalize();
oohhh but then by poking around in the .h
files (which I really recommend), I came across stableNormalize()
, docs, which computes the unit vector (of a non-zero vector) whole reducing under- and over-flow risks:
/** Normalizes the vector while avoid underflow and overflow
*
* \only_for_vectors
*
* This method is analogue to the normalize() method, but it reduces the risk of
* underflow and overflow when computing the norm.
*
* \warning If the input vector is too small (i.e., this->norm()==0), then \c *this is left unchanged.
*
* \sa stableNorm(), stableNormalized(), normalize()
*/
template<typename Derived>
inline void MatrixBase<Derived>::stableNormalize()
{
RealScalar w = cwiseAbs().maxCoeff();
RealScalar z = (derived()/w).squaredNorm();
if(z>RealScalar(0))
derived() /= numext::sqrt(z)*w;
}
and the function can be used like this:
Vector3d v;
// v's unnormalized value is computed.
v.stableNormalize();