Add Tukey loss function. Change-Id: I7c76f13e01863440fc207e99b3fc7ad3fb6f7d1a
diff --git a/include/ceres/loss_function.h b/include/ceres/loss_function.h index 2c58500..70c981d 100644 --- a/include/ceres/loss_function.h +++ b/include/ceres/loss_function.h
@@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2010, 2011, 2012 Google Inc. All rights reserved. +// Copyright 2014 Google Inc. All rights reserved. // http://code.google.com/p/ceres-solver/ // // Redistribution and use in source and binary forms, with or without @@ -274,6 +274,25 @@ const double a_, b_, c_; }; +// This is the Tukey biweight loss function which aggressively +// attempts to suppress large errors. +// +// The term is computed as: +// +// rho(s) = a^2 / 6 * (1 - (1 - s / a^2)^3 ) for s <= a^2, +// rho(s) = a^2 / 6 for s > a^2. +// +// At s = 0: rho = [0, 0.5, -1 / a^2] +class CERES_EXPORT TukeyLoss : public ceres::LossFunction { + public: + explicit TukeyLoss(double a) : a_(a), a_squared_(a * a) { } + virtual void Evaluate(double, double*) const; + + private: + const double a_; + const double a_squared_; +}; + // Composition of two loss functions. The error is the result of first // evaluating g followed by f to yield the composition f(g(s)). // The loss functions must not be NULL.
diff --git a/internal/ceres/loss_function.cc b/internal/ceres/loss_function.cc index 62b545b..6500247 100644 --- a/internal/ceres/loss_function.cc +++ b/internal/ceres/loss_function.cc
@@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2010, 2011, 2012 Google Inc. All rights reserved. +// Copyright 2014 Google Inc. All rights reserved. // http://code.google.com/p/ceres-solver/ // // Redistribution and use in source and binary forms, with or without @@ -114,6 +114,22 @@ } } +void TukeyLoss::Evaluate(double s, double* rho) const { + if (s <= a_squared_) { + // Inlier region. + const double value = 1.0 - s / a_squared_; + const double value_sq = value * value; + rho[0] = a_squared_ / 6.0 * (1.0 - value_sq * value); + rho[1] = 0.5 * value_sq; + rho[2] = -1.0 / a_squared_ * value; + } else { + // Outlier region. + rho[0] = a_squared_ / 6.0; + rho[1] = 0.0; + rho[2] = 0.0; + } +} + ComposedLoss::ComposedLoss(const LossFunction* f, Ownership ownership_f, const LossFunction* g, Ownership ownership_g) : f_(CHECK_NOTNULL(f)),
diff --git a/internal/ceres/loss_function_test.cc b/internal/ceres/loss_function_test.cc index 0967406..04b6ce2 100644 --- a/internal/ceres/loss_function_test.cc +++ b/internal/ceres/loss_function_test.cc
@@ -130,6 +130,13 @@ AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 1000.0); } +TEST(LossFunction, TukeyLoss) { + AssertLossFunctionIsValid(TukeyLoss(0.7), 0.357); + AssertLossFunctionIsValid(TukeyLoss(0.7), 1.792); + AssertLossFunctionIsValid(TukeyLoss(1.3), 0.357); + AssertLossFunctionIsValid(TukeyLoss(1.3), 1.792); +} + TEST(LossFunction, ComposedLoss) { { HuberLoss f(0.7);