image_framework_ymj/include/open3d/t/pipelines/odometry/RGBDOdometry.h

313 lines
15 KiB
C
Raw Normal View History

2024-12-06 16:25:16 +08:00
// ----------------------------------------------------------------------------
// - Open3D: www.open3d.org -
// ----------------------------------------------------------------------------
// Copyright (c) 2018-2023 www.open3d.org
// SPDX-License-Identifier: MIT
// ----------------------------------------------------------------------------
/// \file RGBDOdometry.h
/// All the 4x4 transformation in this file, from params to returns, are
/// Float64. Only convert to Float32 in kernel calls.
#pragma once
#include "open3d/core/Tensor.h"
#include "open3d/t/geometry/Image.h"
#include "open3d/t/geometry/RGBDImage.h"
namespace open3d {
namespace t {
namespace pipelines {
namespace odometry {
enum class Method {
PointToPlane, // Implemented and commented in
// ComputeOdometryResultPointToPlane
Intensity, // Implemented and commented in ComputeOdometryResultIntensity
Hybrid, // Implemented and commented in ComputeOdometryResultHybrid
};
class OdometryConvergenceCriteria {
public:
/// \brief Constructor for the convergence criteria, where we stop
/// iterations once the criteria are met.
///
/// \param max_iteration Maximum iteration before iteration stops.
/// \param relative_rmse Relative rmse threshold where we stop iterations
/// when \f$ |rmse_{i+1} - rmse_i|/rmse_i < relative rmse\f$.
/// \param relative_fitness Relative fitness threshold where we stop
/// iterations when \f$ |fitness_{i+1} - fitness_i|/fitness_i < relative
/// fitness\f$
OdometryConvergenceCriteria(int max_iteration,
double relative_rmse = 1e-6,
double relative_fitness = 1e-6)
: max_iteration_(max_iteration),
relative_rmse_(relative_rmse),
relative_fitness_(relative_fitness) {}
public:
/// Maximum iteration before iteration stops.
int max_iteration_;
/// If relative change (difference) of inliner RMSE score is lower than
/// `relative_rmse`, the iteration stops.
double relative_rmse_;
/// If relative change (difference) of fitness score is lower than
/// `relative_fitness`, the iteration stops.
double relative_fitness_;
};
class OdometryResult {
public:
/// \brief Constructor for the odometry result.
///
/// \param transformation The estimated transformation matrix of dtype
/// Float64 on CPU device.
/// \param inlier_rmse RMSE of the inliers.
/// \param fitness Ratio between #inliers and #pixels.
OdometryResult(const core::Tensor& transformation = core::Tensor::Eye(
4, core::Float64, core::Device("CPU:0")),
double inlier_rmse = 0.0,
double fitness = 0.0)
: transformation_(transformation),
inlier_rmse_(inlier_rmse),
fitness_(fitness) {}
~OdometryResult() {}
public:
/// The estimated transformation matrix of dtype Float64 on CPU device.
core::Tensor transformation_;
/// RMSE of all inlier. Lower is better.
double inlier_rmse_;
/// The overlapping area (# of inlier correspondences / # of points
/// in target). Higher is better.
double fitness_;
};
class OdometryLossParams {
public:
/// \brief Constructor for the odometry loss function.
///
/// \param depth_outlier_trunc Threshold to filter outlier associations
/// where two depths differ significantly.
/// \param depth_huber_delta Huber norm parameter applied to depth loss (for
/// PointToPlane and Hybrid).
/// \param intensity_huber_delta Huber norm parameter applied to intensity
/// loss (for Intensity and Hybrid).
OdometryLossParams(float depth_outlier_trunc = 0.07,
float depth_huber_delta = 0.05,
float intensity_huber_delta = 0.1)
: depth_outlier_trunc_(depth_outlier_trunc),
depth_huber_delta_(depth_huber_delta),
intensity_huber_delta_(intensity_huber_delta) {
if (depth_outlier_trunc_ < 0) {
utility::LogWarning(
"Depth outlier truncation < 0, outliers will be counted!");
}
if (depth_huber_delta_ >= depth_outlier_trunc_) {
utility::LogWarning(
"Huber delta is greater than truncation, huber norm will "
"degenerate to L2 norm!");
}
}
public:
/// Depth difference threshold used to filter projective associations.
float depth_outlier_trunc_;
float depth_huber_delta_;
float intensity_huber_delta_;
};
/// \brief Create an RGBD image pyramid given the original source and target
/// RGBD images, and perform hierarchical odometry using specified \p
/// method.
/// Can be used for offline odometry where we do not expect to push performance
/// to the extreme and not reuse vertex/normal map computed before.
/// Input RGBD images hold a depth image (UInt16 or Float32) with a scale
/// factor and a color image (UInt8 x 3).
/// \param source Source RGBD image.
/// \param target Target RGBD image.
/// \param intrinsics (3, 3) intrinsic matrix for projection of core::Float64
/// on CPU.
/// \param init_source_to_target (4, 4) initial transformation matrix from
/// source to target of core::Float64 on CPU.
/// \param depth_scale Converts depth pixel values to meters by dividing the
/// scale factor.
/// \param depth_max Max depth to truncate depth image with noisy measurements.
/// \param criteria_list Criteria used to define and terminate iterations. In
/// multiscale odometry the order is from coarse to fine. Inputting a vector of
/// iterations by default triggers the implicit conversion.
/// \param method Method used to apply RGBD odometry.
/// \param params Parameters used in loss function, including outlier rejection
/// threshold and Huber norm parameters.
/// \return odometry result, with (4, 4) optimized transformation matrix from
/// source to target, inlier ratio, and fitness.
OdometryResult RGBDOdometryMultiScale(
const t::geometry::RGBDImage& source,
const t::geometry::RGBDImage& target,
const core::Tensor& intrinsics,
const core::Tensor& init_source_to_target =
core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")),
const float depth_scale = 1000.0f,
const float depth_max = 3.0f,
const std::vector<OdometryConvergenceCriteria>& criteria_list = {10, 5,
3},
const Method method = Method::Hybrid,
const OdometryLossParams& params = OdometryLossParams());
/// \brief Estimates the 4x4 rigid transformation T from source to target, with
/// inlier rmse and fitness.
/// Performs one iteration of RGBD odometry using loss function
/// \f$[(V_p - V_q)^T N_p]^2\f$, where
/// \f$ V_p \f$ denotes the vertex at pixel p in the source,
/// \f$ V_q \f$ denotes the vertex at pixel q in the target,
/// \f$ N_p \f$ denotes the normal at pixel p in the source.
/// q is obtained by transforming p with \p init_source_to_target then
/// projecting with \p intrinsics.
/// KinectFusion, ISMAR 2011
///
/// \param source_vertex_map (rows, cols, channels=3) Float32 source vertex
/// image obtained by CreateVertexMap before calling this function.
/// \param target_vertex_map (rows, cols, channels=3) Float32 target vertex
/// image obtained by CreateVertexMap before calling this function.
/// \param target_normal_map (rows, cols, channels=3) Float32 target normal
/// image obtained by CreateNormalMap before calling this function.
/// \param intrinsics (3, 3) intrinsic matrix for projection.
/// \param init_source_to_target (4, 4) initial transformation matrix from
/// source to target.
/// \param depth_outlier_trunc Depth difference threshold used to filter
/// projective associations.
/// \param depth_huber_delta Huber norm parameter used in depth loss.
/// \return odometry result, with (4, 4) optimized transformation matrix from
/// source to target, inlier ratio, and fitness.
OdometryResult ComputeOdometryResultPointToPlane(
const core::Tensor& source_vertex_map,
const core::Tensor& target_vertex_map,
const core::Tensor& target_normal_map,
const core::Tensor& intrinsics,
const core::Tensor& init_source_to_target,
const float depth_outlier_trunc,
const float depth_huber_delta);
/// \brief Estimates the 4x4 rigid transformation T from source to target, with
/// inlier rmse and fitness.
/// Performs one iteration of RGBD odometry using loss function
/// \f$(I_p - I_q)^2\f$, where
/// \f$ I_p \f$ denotes the intensity at pixel p in the source,
/// \f$ I_q \f$ denotes the intensity at pixel q in the target.
/// q is obtained by transforming p with \p init_source_to_target then
/// projecting with \p intrinsics.
/// Real-time visual odometry from dense RGB-D images, ICCV Workshops, 2011
///
/// \param source_depth (rows, cols, channels=1) Float32 source depth image
/// obtained by PreprocessDepth before calling this function.
/// \param target_depth (rows, cols, channels=1) Float32 target depth image
/// obtained by PreprocessDepth before calling this function.
/// \param source_intensity (rows, cols, channels=1) Float32 source intensity
/// image obtained by RGBToGray before calling this function.
/// \param target_intensity (rows, cols, channels=1) Float32 target intensity
/// image obtained by RGBToGray before calling this function.
/// \param target_intensity_dx (rows, cols, channels=1) Float32 target intensity
/// gradient image along x-axis obtained by FilterSobel before calling this
/// function.
/// \param target_intensity_dy (rows, cols, channels=1) Float32 target intensity
/// gradient image along y-axis obtained by FilterSobel before calling this
/// function.
/// \param source_vertex_map (rows, cols, channels=3) Float32 source vertex
/// image obtained by CreateVertexMap before calling this function.
/// \param intrinsics (3, 3) intrinsic matrix for projection.
/// \param init_source_to_target (4, 4) initial transformation matrix from
/// source to target.
/// \param depth_outlier_trunc Depth difference threshold used to filter
/// projective associations.
/// \param intensity_huber_delta Huber norm parameter used in intensity loss.
/// \return odometry result, with(4, 4) optimized transformation matrix
/// from source to target, inlier ratio, and fitness.
OdometryResult ComputeOdometryResultIntensity(
const core::Tensor& source_depth,
const core::Tensor& target_depth,
const core::Tensor& source_intensity,
const core::Tensor& target_intensity,
const core::Tensor& target_intensity_dx,
const core::Tensor& target_intensity_dy,
const core::Tensor& source_vertex_map,
const core::Tensor& intrinsics,
const core::Tensor& init_source_to_target,
const float depth_outlier_trunc,
const float intensity_huber_delta);
/// \brief Estimates the 4x4 rigid transformation T from source to target, with
/// inlier rmse and fitness.
/// Performs one iteration of RGBD odometry using loss function
/// \f$(I_p - I_q)^2 + \lambda(D_p - (D_q)')^2\f$, where
/// \f$ I_p \f$ denotes the intensity at pixel p in the source,
/// \f$ I_q \f$ denotes the intensity at pixel q in the target.
/// \f$ D_p \f$ denotes the depth pixel p in the source,
/// \f$ D_q \f$ denotes the depth pixel q in the target.
/// q is obtained by transforming p with \p init_source_to_target then
/// projecting with \p intrinsics.
/// Reference: J. Park, Q.Y. Zhou, and V. Koltun,
/// Colored Point Cloud Registration Revisited, ICCV, 2017.
///
/// \param source_depth (rows, cols, channels=1) Float32 source depth image
/// obtained by PreprocessDepth before calling this function.
/// \param target_depth (rows, cols, channels=1) Float32 target depth image
/// obtained by PreprocessDepth before calling this function.
/// \param source_intensity (rows, cols, channels=1) Float32 source intensity
/// image obtained by RGBToGray before calling this function.
/// \param target_intensity (rows, cols, channels=1) Float32 target intensity
/// image obtained by RGBToGray before calling this function.
/// \param target_depth_dx (rows, cols, channels=1) Float32 target depth
/// gradient image along x-axis obtained by FilterSobel before calling this
/// function.
/// \param target_depth_dy (rows, cols, channels=1) Float32 target depth
/// gradient image along y-axis obtained by FilterSobel before calling this
/// function.
/// \param target_intensity_dx (rows, cols, channels=1) Float32 target intensity
/// gradient image along x-axis obtained by FilterSobel before calling this
/// function.
/// \param target_intensity_dy (rows, cols, channels=1) Float32 target intensity
/// gradient image along y-axis obtained by FilterSobel before calling this
/// function.
/// \param source_vertex_map (rows, cols, channels=3) Float32 source vertex
/// image obtained by CreateVertexMap before calling this function.
/// \param intrinsics (3, 3) intrinsic matrix for projection.
/// \param init_source_to_target (4, 4) initial transformation matrix from
/// source to target.
/// \param depth_outlier_trunc Depth difference threshold used to filter
/// projective associations.
/// \param depth_huber_delta Huber norm parameter used in depth loss.
/// \param intensity_huber_delta Huber norm parameter used in intensity loss.
/// \return odometry result, with(4, 4) optimized transformation matrix
/// from source to target, inlier ratio, and fitness.
OdometryResult ComputeOdometryResultHybrid(
const core::Tensor& source_depth,
const core::Tensor& target_depth,
const core::Tensor& source_intensity,
const core::Tensor& target_intensity,
const core::Tensor& target_depth_dx,
const core::Tensor& target_depth_dy,
const core::Tensor& target_intensity_dx,
const core::Tensor& target_intensity_dy,
const core::Tensor& source_vertex_map,
const core::Tensor& intrinsics,
const core::Tensor& init_source_to_target,
const float depth_outlier_trunc,
const float depth_huber_delta,
const float intensity_huber_delta);
/// Estimates 6x6 information matrix from a pair of depth images.
/// The process is akin to information matrix creation for point clouds.
core::Tensor ComputeOdometryInformationMatrix(
const geometry::Image& source_depth,
const geometry::Image& target_depth,
const core::Tensor& intrinsic,
const core::Tensor& source_to_target,
const float dist_thr,
const float depth_scale = 1000.0f,
const float depth_max = 3.0f);
} // namespace odometry
} // namespace pipelines
} // namespace t
} // namespace open3d