"Efficient Second-order Minimization" for the planar tracker
This implements the "Efficient Second-order Minimization" scheme, as supported by the existing translation tracker. This increases the amount of per-iteration work, but decreases the number of iterations required to converge and also increases the size of the basin of attraction for the optimization.
This commit is contained in:
@@ -102,6 +102,7 @@ bool EsmRegionTracker::Track(const FloatImage &image1,
|
||||
options.num_samples_y = 2 * half_window_size + 1;
|
||||
options.max_iterations = 20;
|
||||
options.sigma = sigma;
|
||||
options.use_esm = true;
|
||||
|
||||
TrackRegionResult result;
|
||||
TrackRegion(image1, image2, xx1, yy1, options, xx2, yy2, &result);
|
||||
|
||||
99
extern/libmv/libmv/tracking/track_region.cc
vendored
99
extern/libmv/libmv/tracking/track_region.cc
vendored
@@ -63,33 +63,32 @@ bool AllInBounds(const FloatImage &image,
|
||||
// supported, the sample function must be inside a template-specialized class
|
||||
// with a non-templated static member.
|
||||
|
||||
// The "AutoDiffImage::Sample()" function allows sampling an image at an x, y
|
||||
// The "AutoDiff::Sample()" function allows sampling an image at an x, y
|
||||
// position such that if x and y are jets, then the derivative information is
|
||||
// correctly propagated.
|
||||
|
||||
// Empty default template.
|
||||
template<typename T>
|
||||
struct AutoDiffImage {
|
||||
struct AutoDiff {
|
||||
// Sample only the image when the coordinates are scalars.
|
||||
static T Sample(const FloatImage &image_and_gradient,
|
||||
const T &x, const T &y) {
|
||||
return 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
// Sample only the image when the coordinates are scalars.
|
||||
template<>
|
||||
struct AutoDiffImage<double> {
|
||||
static double Sample(const FloatImage &image_and_gradient,
|
||||
const double& x, const double& y) {
|
||||
return SampleLinear(image_and_gradient, y, x, 0);
|
||||
}
|
||||
|
||||
static void SetScalarPart(double scalar, T *value) {
|
||||
*value = scalar;
|
||||
}
|
||||
static void ScaleDerivative(double scale_by, T *value) {
|
||||
// For double, there is no derivative to scale.
|
||||
}
|
||||
};
|
||||
|
||||
// Sample the image and gradient when the coordinates are jets, applying the
|
||||
// jacobian appropriately to propagate the derivatives from the coordinates.
|
||||
template<>
|
||||
template<typename T, int N>
|
||||
struct AutoDiffImage<ceres::Jet<T, N> > {
|
||||
struct AutoDiff<ceres::Jet<T, N> > {
|
||||
static ceres::Jet<T, N> Sample(const FloatImage &image_and_gradient,
|
||||
const ceres::Jet<T, N> &x,
|
||||
const ceres::Jet<T, N> &y) {
|
||||
@@ -116,6 +115,13 @@ struct AutoDiffImage<ceres::Jet<T, N> > {
|
||||
jet_s.v = Matrix<T, 1, 2>(dsdx, dsdy) * dxydz;
|
||||
return jet_s;
|
||||
}
|
||||
|
||||
static void SetScalarPart(double scalar, ceres::Jet<T, N> *value) {
|
||||
value->a = scalar;
|
||||
}
|
||||
static void ScaleDerivative(double scale_by, ceres::Jet<T, N> *value) {
|
||||
value->v *= scale_by;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Warp>
|
||||
@@ -151,18 +157,16 @@ class BoundaryCheckingCallback : public ceres::IterationCallback {
|
||||
template<typename Warp>
|
||||
class WarpCostFunctor {
|
||||
public:
|
||||
WarpCostFunctor(const FloatImage &image_and_gradient1,
|
||||
WarpCostFunctor(const TrackRegionOptions &options,
|
||||
const FloatImage &image_and_gradient1,
|
||||
const FloatImage &image_and_gradient2,
|
||||
const Mat3 &canonical_to_image1,
|
||||
const Warp &warp,
|
||||
int num_samples_x,
|
||||
int num_samples_y)
|
||||
: image_and_gradient1_(image_and_gradient1),
|
||||
const Warp &warp)
|
||||
: options_(options),
|
||||
image_and_gradient1_(image_and_gradient1),
|
||||
image_and_gradient2_(image_and_gradient2),
|
||||
canonical_to_image1_(canonical_to_image1),
|
||||
warp_(warp),
|
||||
num_samples_x_(num_samples_x),
|
||||
num_samples_y_(num_samples_y) {}
|
||||
warp_(warp) {}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(const T *warp_parameters, T *residuals) const {
|
||||
@@ -171,8 +175,8 @@ class WarpCostFunctor {
|
||||
}
|
||||
|
||||
int cursor = 0;
|
||||
for (int r = 0; r < num_samples_y_; ++r) {
|
||||
for (int c = 0; c < num_samples_x_; ++c) {
|
||||
for (int r = 0; r < options_.num_samples_y; ++r) {
|
||||
for (int c = 0; c < options_.num_samples_x; ++c) {
|
||||
// Compute the location of the source pixel (via homography).
|
||||
Vec3 image1_position = canonical_to_image1_ * Vec3(c, r, 1);
|
||||
image1_position /= image1_position(2);
|
||||
@@ -185,28 +189,52 @@ class WarpCostFunctor {
|
||||
&image2_position[0],
|
||||
&image2_position[1]);
|
||||
|
||||
// Sample the source and destination.
|
||||
double src_sample = AutoDiffImage<double>::Sample(image_and_gradient1_,
|
||||
image1_position[0],
|
||||
image1_position[1]);
|
||||
T dst_sample = AutoDiffImage<T>::Sample(image_and_gradient2_,
|
||||
image2_position[0],
|
||||
image2_position[1]);
|
||||
|
||||
// Sample the destination, propagating derivatives.
|
||||
T dst_sample = AutoDiff<T>::Sample(image_and_gradient2_,
|
||||
image2_position[0],
|
||||
image2_position[1]);
|
||||
|
||||
// Sample the source. This is made complicated by ESM mode.
|
||||
T src_sample;
|
||||
if (options_.use_esm) {
|
||||
// In ESM mode, the derivative of the source is also taken into
|
||||
// account. This changes the linearization in a way that causes
|
||||
// better convergence. Copy the derivative of the warp parameters
|
||||
// onto the jets for the image1 position. This is the ESM hack.
|
||||
T image1_position_x = image2_position[0];
|
||||
T image1_position_y = image2_position[1];
|
||||
AutoDiff<T>::SetScalarPart(image1_position[0], &image1_position_x);
|
||||
AutoDiff<T>::SetScalarPart(image1_position[1], &image1_position_y);
|
||||
src_sample = AutoDiff<T>::Sample(image_and_gradient1_,
|
||||
image1_position_x,
|
||||
image1_position_y);
|
||||
|
||||
// The jacobians for these should be averaged. Due to the subtraction
|
||||
// below, flip the sign of the src derivative so that the effect
|
||||
// after subtraction of the jets is that they are averaged.
|
||||
AutoDiff<T>::ScaleDerivative(-0.5, &src_sample);
|
||||
AutoDiff<T>::ScaleDerivative(0.5, &dst_sample);
|
||||
} else {
|
||||
// This is the traditional, forward-mode KLT solution.
|
||||
src_sample = T(AutoDiff<double>::Sample(image_and_gradient1_,
|
||||
image1_position[0],
|
||||
image1_position[1]));
|
||||
}
|
||||
|
||||
// The difference is the error.
|
||||
residuals[cursor++] = T(src_sample) - dst_sample;
|
||||
residuals[cursor++] = src_sample - dst_sample;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const TrackRegionOptions &options_;
|
||||
const FloatImage &image_and_gradient1_;
|
||||
const FloatImage &image_and_gradient2_;
|
||||
const Mat3 &canonical_to_image1_;
|
||||
const Warp &warp_;
|
||||
int num_samples_x_;
|
||||
int num_samples_y_;
|
||||
};
|
||||
|
||||
// Compute the warp from rectangular coordinates, where one corner is the
|
||||
@@ -570,12 +598,11 @@ void TemplatedTrackRegion(const FloatImage &image1,
|
||||
|
||||
// Construct the warp cost function. AutoDiffCostFunction takes ownership.
|
||||
WarpCostFunctor<Warp> *cost_function =
|
||||
new WarpCostFunctor<Warp>(image_and_gradient1,
|
||||
new WarpCostFunctor<Warp>(options,
|
||||
image_and_gradient1,
|
||||
image_and_gradient2,
|
||||
canonical_homography,
|
||||
warp,
|
||||
options.num_samples_x,
|
||||
options.num_samples_y);
|
||||
warp);
|
||||
|
||||
// Construct the problem with a single residual.
|
||||
ceres::Problem::Options problem_options;
|
||||
|
||||
3
extern/libmv/libmv/tracking/track_region.h
vendored
3
extern/libmv/libmv/tracking/track_region.h
vendored
@@ -47,6 +47,9 @@ struct TrackRegionOptions {
|
||||
|
||||
double minimum_correlation;
|
||||
int max_iterations;
|
||||
|
||||
// Use the "Efficient Second-order Minimization" scheme. This increases
|
||||
// convergence speed at the cost of more per-iteration work.
|
||||
bool use_esm;
|
||||
|
||||
double sigma;
|
||||
|
||||
Reference in New Issue
Block a user