source/rig/RigAligner.h (142 lines of code) (raw):
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <ceres/ceres.h>
#include "source/util/Camera.h"
namespace fb360_dep {
const Eigen::Transform<double, 3, Eigen::Affine> generateTransform(
const Camera::Vector3& rotation,
const Camera::Vector3& translation,
const Eigen::UniformScaling<double>& scale,
const bool applyInReverse = false) {
const Eigen::AngleAxis<Camera::Real> x(rotation.x(), Camera::Vector3::UnitX());
const Eigen::AngleAxis<Camera::Real> y(rotation.y(), Camera::Vector3::UnitY());
const Eigen::AngleAxis<Camera::Real> z(rotation.z(), Camera::Vector3::UnitZ());
const Eigen::Affine3d r = Eigen::Affine3d(z * y * x);
const Eigen::Translation3d t(translation);
// Create transform from rotation, position and scale
Eigen::Transform<double, 3, Eigen::Affine> xform;
if (applyInReverse) {
xform = r * t * scale;
} else {
xform = scale * t * r;
}
return xform;
}
const Eigen::Transform<double, 3, Eigen::Affine> generateTransform(
double const* const rotation,
double const* const translation,
double const* const scale,
const bool applyInReverse = false) {
return generateTransform(
Camera::Vector3(rotation),
Camera::Vector3(translation),
Eigen::UniformScaling<double>(scale[0]),
applyInReverse);
}
void solve(ceres::Problem& problem) {
ceres::Solver::Options options;
options.use_inner_iterations = true;
options.max_num_iterations = 500;
options.minimizer_progress_to_stdout = false;
ceres::Solver::Summary summary;
Solve(options, &problem, &summary);
LOG(INFO) << summary.BriefReport();
}
struct TransformationFunctor {
static ceres::CostFunction* addResidual(
ceres::Problem& problem,
Camera::Vector3& rotation,
Camera::Vector3& translation,
Eigen::UniformScaling<double>& scale,
const Camera& camera,
const Camera& referenceCamera,
const bool robust = false) {
auto* cost = new CostFunction(new TransformationFunctor(camera, referenceCamera));
auto* loss = robust ? new ceres::HuberLoss(1.0) : nullptr;
problem.AddResidualBlock(cost, loss, rotation.data(), translation.data(), &scale.factor());
return cost;
}
bool operator()(
double const* const rotation,
double const* const translation,
double const* const scale,
double* residuals) const {
const Eigen::Transform<double, 3, Eigen::Affine> xform =
generateTransform(rotation, translation, scale);
const Camera::Vector3 newPosition = xform * camera.position;
Eigen::Map<Camera::Vector3> r(residuals);
r = referenceCamera.position - newPosition;
return true;
}
private:
using CostFunction = ceres::NumericDiffCostFunction<
TransformationFunctor,
ceres::CENTRAL,
3, // residuals
3, // rotation
3, // translation
1>; // scale
TransformationFunctor(const Camera& camera, const Camera& referenceCamera)
: camera(camera), referenceCamera(referenceCamera) {}
const Camera& camera;
const Camera& referenceCamera;
};
Camera::Rig transformRig(
const Camera::Rig& rig,
const Camera::Vector3& rotation,
const Camera::Vector3& translation,
const Eigen::UniformScaling<double>& scale,
const bool applyInReverse = false) {
Camera::Rig result;
// Generate a transform with rotation only (no translation or scaling) for the forward, up and
// right vectors
const Eigen::Transform<double, 3, Eigen::Affine> rot = generateTransform(
rotation, Camera::Vector3(0, 0, 0), Eigen::UniformScaling<double>(1), applyInReverse);
const Eigen::Transform<double, 3, Eigen::Affine> xform =
generateTransform(rotation, translation, scale, applyInReverse);
for (Camera camera : rig) {
const Camera::Vector3 forward = camera.forward();
const Camera::Vector3 up = camera.up();
const Camera::Vector3 right = camera.right();
camera.setRotation(rot * forward, rot * up, rot * right);
camera.position = xform * camera.position;
result.push_back(camera);
}
return result;
}
Camera::Rig alignRig(
const Camera::Rig& rig,
const Camera::Rig& referenceRig,
bool lockRotation = false,
bool lockTranslation = false,
bool lockScale = false) {
ceres::Problem problem;
Camera::Vector3 rotation(0, 0, 0);
Camera::Vector3 translation(0, 0, 0);
Eigen::UniformScaling<double> scale(1);
for (int i = 0; i < int(rig.size()); ++i) {
const Camera& referenceCamera = Camera::findCameraById(rig[i].id, referenceRig);
TransformationFunctor::addResidual(
problem, rotation, translation, scale, rig[i], referenceCamera);
}
problem.SetParameterLowerBound(&scale.factor(), 0, 0.25);
problem.SetParameterLowerBound(rotation.data(), 0, -M_PI);
problem.SetParameterLowerBound(rotation.data(), 1, -M_PI);
problem.SetParameterLowerBound(rotation.data(), 2, -M_PI / 2);
problem.SetParameterUpperBound(rotation.data(), 0, M_PI);
problem.SetParameterUpperBound(rotation.data(), 1, M_PI);
problem.SetParameterUpperBound(rotation.data(), 2, M_PI / 2);
if (lockRotation) {
problem.SetParameterBlockConstant(rotation.data());
}
if (lockTranslation) {
problem.SetParameterBlockConstant(translation.data());
}
if (lockScale) {
problem.SetParameterBlockConstant(&scale.factor());
}
solve(problem);
LOG(INFO) << folly::sformat(
"New rotation values: {} {} {}", rotation[0], rotation[1], rotation[2]);
LOG(INFO) << folly::sformat(
"New translation values: {} {} {}", translation[0], translation[1], translation[2]);
LOG(INFO) << folly::sformat("New scale: {}", scale.factor());
const Camera::Rig transformedRig = transformRig(rig, rotation, translation, scale);
return transformedRig;
}
} // namespace fb360_dep