27 const std::vector<std::string>& fnames,
29 std::string subdir_name =
params.GetSubfolderName();
30 if (!subdir_name.empty()) {
34 std::vector<std::string> fnames_processed;
36 for (
auto& fname : fnames) {
40 fnames_processed.emplace_back(fname_processed);
49 if (
params.voxel_size_ > 0) {
50 pcd = pcd->VoxelDownSample(
params.voxel_size_);
51 pcd->RemoveStatisticalOutliers(20, 2.0);
52 pcd->EstimateNormals();
54 pcd->RemoveStatisticalOutliers(20, 2.0);
55 if (!pcd->hasNormals()) {
56 pcd->EstimateNormals();
64 return fnames_processed;
89 int64_t N = target_correspondences.
GetLength();
92 target_correspondences.
Ne(-1).
Reshape({-1});
96 target_correspondences.
IndexGet({valid_correspondences});
118 return correspondence_set;
137 float distance_threshold,
138 float fitness_threshold,
155 bool check = tpcd_j_nns.
HybridIndex(distance_threshold);
159 core::Tensor target_indices, residual_distances_Tij, neighbour_counts;
160 std::tie(target_indices, residual_distances_Tij, neighbour_counts) =
161 tpcd_j_nns.
HybridSearch(tpcd_i_transformed_Tij.GetPointPositions(),
162 distance_threshold, 1);
183 core::Tensor square_residual = (residual * residual).Sum({1});
185 square_residual.
Le(distance_threshold * distance_threshold);
189 float inlier_ratio =
static_cast<float>(num_inliers) /
195 if (j != i + 1 && inlier_ratio < fitness_threshold) {
198 tpcd_i, tpcd_j, correspondence_set,
204 return correspondence_set;
210 const std::vector<std::string>& fnames_processed,
215 for (
auto& edge : pose_graph.
edges_) {
216 int i = edge.source_node_id_;
217 int j = edge.target_node_id_;
222 "{}/{:03d}_{:03d}.npy",
params.GetSubfolderName(), i, j);
232 pose_graph.
nodes_[i].pose_);
235 pose_graph.
nodes_[j].pose_);
238 edge.transformation_);
242 i, j, tpcd_i, tpcd_j, T_i, T_j, T_ij,
246 if (correspondence_set.
GetLength() > 0) {
247 correspondence_set.
Save(correspondences_fname);
255 const std::vector<std::string>& fnames) {
257 for (
auto& fname : fnames) {
261 ctr_grid.
Touch(tpcd);
269 if (delta_poses.
GetLength() != int64_t(fragment_pose_graph.
nodes_.size())) {
272 for (int64_t i = 0; i < delta_poses.
GetLength(); ++i) {
276 fragment_pose_graph.
nodes_[i].pose_)
279 Eigen::Matrix<float, -1, -1, Eigen::RowMajor> pose_eigen =
281 Eigen::Matrix<double, -1, -1, Eigen::RowMajor> pose_eigen_d =
282 pose_eigen.cast<
double>().eval();
283 Eigen::Ref<Eigen::Matrix<double, 4, 4, Eigen::RowMajor>> pose_eigen_ref(
285 fragment_pose_graph.
nodes_[i].pose_ = pose_eigen_ref;
299 const std::vector<std::string>& fnames,
304 if (!
params.slac_folder_.empty()) {
326 int64_t num_params = fnames_down.size() * 6 + ctr_grid.
Size() * 3;
330 for (
int itr = 0; itr <
params.max_iterations_; ++itr) {
339 AtA.
IndexSet({indices_eye0, indices_eye0},
345 pose_graph_update,
params, debug_option);
355 residual_reg[0].Item<float>());
360 delta.
Slice(0, 0, 6 * pose_graph_update.
nodes_.size());
375 if (!
params.slac_folder_.empty()) {
381 std::vector<std::string> fnames_down =
389 int64_t num_params = fnames_down.size() * 6;
393 for (
int itr = 0; itr <
params.max_iterations_; ++itr) {
403 AtA.
IndexSet({indices_eye0, indices_eye0},
407 pose_graph_update,
params, debug_option);
414 return pose_graph_update;
filament::Texture::InternalFormat format
cmdLineReadable * params[]
#define AssertTensorDevice(tensor,...)
#define AssertTensorDtype(tensor,...)
#define AssertTensorShape(tensor,...)
static TensorKey Slice(utility::optional< int64_t > start, utility::optional< int64_t > stop, utility::optional< int64_t > step)
Tensor Solve(const Tensor &rhs) const
Tensor Matmul(const Tensor &rhs) const
void IndexSet(const std::vector< Tensor > &index_tensors, const Tensor &src_tensor)
Advanced indexing getter.
Tensor Sum(const SizeVector &dims, bool keepdim=false) const
void Save(const std::string &file_name) const
Save tensor to numpy's npy format.
static Tensor Arange(const Scalar start, const Scalar stop, const Scalar step=1, const Dtype dtype=core::Int64, const Device &device=core::Device("CPU:0"))
Create a 1D tensor with evenly spaced values in the given interval.
int64_t GetLength() const
Tensor Ne(const Tensor &value) const
Element-wise not-equals-to of tensors, returning a new boolean tensor.
Tensor SetItem(const Tensor &value)
Set all items. Equivalent to tensor[:] = value in Python.
Tensor IndexGet(const std::vector< Tensor > &index_tensors) const
Advanced indexing getter. This will always allocate a new Tensor.
Tensor Le(const Tensor &value) const
static Tensor Zeros(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with zeros.
Tensor View(const SizeVector &dst_shape) const
Device GetDevice() const override
Tensor Reshape(const SizeVector &dst_shape) const
static Tensor Ones(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with ones.
Tensor Slice(int64_t dim, int64_t start, int64_t stop, int64_t step=1) const
Tensor To(Dtype dtype, bool copy=false) const
A Class for nearest neighbor search.
bool HybridIndex(utility::optional< double > radius={})
std::tuple< Tensor, Tensor, Tensor > HybridSearch(const Tensor &query_points, const double radius, const int max_knn) const
Data structure defining the pose graph.
std::vector< PoseGraphNode > nodes_
List of PoseGraphNode.
std::vector< PoseGraphEdge > edges_
List of PoseGraphEdge.
A point cloud contains a list of 3D points.
core::Tensor & GetPointPositions()
Get the value of the "positions" attribute. Convenience function.
core::Device GetDevice() const override
Returns the device attribute of this PointCloud.
PointCloud Clone() const
Returns copy of the point cloud on the same device.
PointCloud & Transform(const core::Tensor &transformation)
Transforms the PointPositions and PointNormals (if exist) of the PointCloud.
void Touch(const geometry::PointCloud &pcd)
Allocate control grids in the shared camera space.
core::Tensor GetCurrPositions()
core::Tensor EigenMatrixToTensor(const Eigen::MatrixBase< Derived > &matrix)
Converts a eigen matrix of shape (M, N) with alignment A and type T to a tensor.
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > TensorToEigenMatrixXf(const core::Tensor &tensor)
Converts a 2D tensor to Eigen::MatrixXf of same shape. Regardless of the tensor dtype,...
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
::ccPointCloud PointCloud
void To(const core::Tensor &src, core::Tensor &dst, double scale, double offset)
bool WritePointCloud(const std::string &filename, const geometry::PointCloud &pointcloud, const cloudViewer::io::WritePointCloudOption ¶ms)
std::shared_ptr< geometry::PointCloud > CreatePointCloudFromFile(const std::string &filename, const std::string &format, bool print_progress)
core::Tensor PoseToTransformation(const core::Tensor &pose)
Convert pose to the transformation matrix.
void SaveCorrespondencesForPointClouds(const std::vector< std::string > &fnames_processed, const PoseGraph &pose_graph, const SLACOptimizerParams ¶ms, const SLACDebugOption &debug_option)
Read pose graph containing loop closures and odometry to compute putative correspondences between pai...
static void InitializeControlGrid(ControlGrid &ctr_grid, const std::vector< std::string > &fnames)
PoseGraph RunRigidOptimizerForFragments(const std::vector< std::string > &fnames, const PoseGraph &pose_graph, const SLACOptimizerParams ¶ms, const SLACDebugOption &debug_option)
Extended ICP to simultaneously align multiple point clouds with dense pairwise point-to-plane distanc...
static PointCloud CreateTPCDFromFile(const std::string &fname, const core::Device &device=core::Device("CPU:0"))
std::pair< PoseGraph, ControlGrid > RunSLACOptimizerForFragments(const std::vector< std::string > &fnames, const PoseGraph &pose_graph, const SLACOptimizerParams ¶ms, const SLACDebugOption &debug_option)
Simultaneous Localization and Calibration: Self-Calibration of Consumer Depth Cameras,...
void VisualizePointCloudCorrespondences(const t::geometry::PointCloud &tpcd_i, const t::geometry::PointCloud &tpcd_j, const core::Tensor correspondences, const core::Tensor &T_ij)
Visualize pairs with correspondences.
static void UpdateControlGrid(ControlGrid &ctr_grid, core::Tensor &delta)
static void FillInSLACAlignmentTerm(Tensor &AtA, Tensor &Atb, Tensor &residual, ControlGrid &ctr_grid, const PointCloud &tpcd_param_i, const PointCloud &tpcd_param_j, const Tensor &Ti, const Tensor &Tj, const int i, const int j, const int n_fragments, const float threshold)
static std::vector< std::string > PreprocessPointClouds(const std::vector< std::string > &fnames, const SLACOptimizerParams ¶ms)
static core::Tensor GetCorrespondenceSetForPointCloudPair(int i, int j, PointCloud &tpcd_i, PointCloud &tpcd_j, const core::Tensor &T_i, const core::Tensor &T_j, const core::Tensor &T_ij, float distance_threshold, float fitness_threshold, bool debug)
static core::Tensor ConvertCorrespondencesTargetIndexedToCx2Form(const core::Tensor &target_correspondences)
void FillInSLACRegularizerTerm(Tensor &AtA, Tensor &Atb, Tensor &residual, ControlGrid &ctr_grid, int n_frags, const SLACOptimizerParams ¶ms, const SLACDebugOption &debug_option)
static void FillInRigidAlignmentTerm(Tensor &AtA, Tensor &Atb, Tensor &residual, PointCloud &tpcd_i, PointCloud &tpcd_j, const Tensor &Ti, const Tensor &Tj, const int i, const int j, const float threshold)
static void UpdatePoses(PoseGraph &fragment_pose_graph, core::Tensor &delta)
bool FileExists(const std::string &filename)
bool MakeDirectory(const std::string &directory)
std::string GetFileNameWithoutDirectory(const std::string &filename)
Generic file read and write utility for python interface.