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);