11 #include <Eigen/Dense>
12 #include <Eigen/Eigenvalues>
18 #include "RGBDImage.h"
19 #include "VoxelGrid.h"
33 using namespace geometry;
35 int CountValidDepthPixels(
const Image &depth,
int stride) {
36 int num_valid_pixels = 0;
39 const float *p = depth.
PointerAt<
float>(j, i);
40 if (*p > 0) num_valid_pixels += 1;
43 return num_valid_pixels;
46 std::shared_ptr<ccPointCloud> CreatePointCloudFromFloatDepthImage(
49 const Eigen::Matrix4d &extrinsic,
51 bool project_valid_depth_only) {
52 auto pointcloud = std::make_shared<ccPointCloud>();
53 Eigen::Matrix4d camera_pose = extrinsic.inverse();
57 if (!project_valid_depth_only) {
61 num_valid_pixels = CountValidDepthPixels(depth,
stride);
63 pointcloud->resize(num_valid_pixels);
67 const float *p = depth.
PointerAt<
float>(j, i);
69 double z = (double)(*p);
70 double x = (j - principal_point.first) *
z / focal_length.first;
72 (i - principal_point.second) *
z / focal_length.second;
73 Eigen::Vector4d point =
74 camera_pose * Eigen::Vector4d(
x,
y,
z, 1.0);
76 pointcloud->setEigenPoint(
static_cast<size_t>(cnt++),
77 point.block<3, 1>(0, 0));
78 }
else if (!project_valid_depth_only) {
79 double z = std::numeric_limits<float>::quiet_NaN();
80 double x = std::numeric_limits<float>::quiet_NaN();
81 double y = std::numeric_limits<float>::quiet_NaN();
82 pointcloud->setEigenPoint(
static_cast<size_t>(cnt++),
83 Eigen::Vector3d(
x,
y,
z));
90 template <
typename TC,
int NC>
91 std::shared_ptr<ccPointCloud> CreatePointCloudFromRGBDImageT(
94 const Eigen::Matrix4d &extrinsic,
95 bool project_valid_depth_only) {
96 auto pointcloud = std::make_shared<ccPointCloud>();
97 Eigen::Matrix4d camera_pose = extrinsic.inverse();
100 double scale = (
sizeof(TC) == 1) ? 255.0 : 1.0;
101 int num_valid_pixels;
102 if (!project_valid_depth_only) {
103 num_valid_pixels =
image.depth_.height_ *
image.depth_.width_;
105 num_valid_pixels = CountValidDepthPixels(
image.depth_, 1);
107 pointcloud->resize(num_valid_pixels);
108 pointcloud->resizeTheRGBTable();
110 for (
int i = 0; i <
image.depth_.height_; i++) {
111 float *p = (
float *)(
image.depth_.data_.data() +
112 i *
image.depth_.BytesPerLine());
113 TC *pc = (TC *)(
image.color_.data_.data() +
114 i *
image.color_.BytesPerLine());
115 for (
int j = 0; j <
image.depth_.width_; j++, p++, pc += NC) {
117 double z = (double)(*p);
118 double x = (j - principal_point.first) *
z / focal_length.first;
120 (i - principal_point.second) *
z / focal_length.second;
121 Eigen::Vector4d point =
122 camera_pose * Eigen::Vector4d(
x,
y,
z, 1.0);
123 pointcloud->setEigenPoint(
static_cast<size_t>(cnt),
124 point.block<3, 1>(0, 0));
125 pointcloud->setEigenColor(
126 static_cast<size_t>(cnt++),
127 Eigen::Vector3d(pc[0], pc[(NC - 1) / 2], pc[NC - 1]) /
129 }
else if (!project_valid_depth_only) {
130 double z = std::numeric_limits<float>::quiet_NaN();
131 double x = std::numeric_limits<float>::quiet_NaN();
132 double y = std::numeric_limits<float>::quiet_NaN();
133 pointcloud->setEigenPoint(
static_cast<size_t>(cnt),
134 Eigen::Vector3d(
x,
y,
z));
135 pointcloud->setEigenColor(
136 static_cast<size_t>(cnt++),
137 Eigen::Vector3d(std::numeric_limits<TC>::quiet_NaN(),
138 std::numeric_limits<TC>::quiet_NaN(),
139 std::numeric_limits<TC>::quiet_NaN()));
150 for (
size_t idx = 0; idx <
size; idx++) {
158 size_t Find(
size_t x) {
159 if (
x != parent_[
x]) {
160 parent_[
x] = Find(parent_[
x]);
166 void Union(
size_t x,
size_t y) {
181 std::vector<size_t> parent_;
182 std::vector<size_t>
size_;
185 struct WeightedEdge {
186 WeightedEdge(
size_t v0,
size_t v1,
double weight)
194 std::vector<WeightedEdge> Kruskal(std::vector<WeightedEdge> &edges,
196 std::sort(edges.begin(), edges.end(),
197 [](WeightedEdge &e0, WeightedEdge &e1) {
198 return e0.weight_ < e1.weight_;
200 DisjointSet disjoint_set(n_vertices);
201 std::vector<WeightedEdge> mst;
202 for (
size_t eidx = 0; eidx < edges.size(); ++eidx) {
203 size_t set0 = disjoint_set.Find(edges[eidx].
v0_);
204 size_t set1 = disjoint_set.Find(edges[eidx].
v1_);
206 mst.push_back(edges[eidx]);
207 disjoint_set.Union(set0, set1);
219 const Eigen::Matrix4d &extrinsic ,
223 bool project_valid_depth_only ) {
228 return cloudViewer::CreatePointCloudFromFloatDepthImage(
229 *float_depth, intrinsic, extrinsic,
stride,
230 project_valid_depth_only);
232 return cloudViewer::CreatePointCloudFromFloatDepthImage(
233 depth, intrinsic, extrinsic,
stride,
234 project_valid_depth_only);
238 "[CreatePointCloudFromDepthImage] Unsupported image format.");
239 return std::make_shared<ccPointCloud>();
245 const Eigen::Matrix4d &extrinsic ,
246 bool project_valid_depth_only ) {
247 if (
image.depth_.num_of_channels_ == 1 &&
248 image.depth_.bytes_per_channel_ == 4) {
249 if (
image.color_.bytes_per_channel_ == 1 &&
250 image.color_.num_of_channels_ == 3) {
251 return cloudViewer::CreatePointCloudFromRGBDImageT<uint8_t, 3>(
252 image, intrinsic, extrinsic, project_valid_depth_only);
253 }
else if (
image.color_.bytes_per_channel_ == 1 &&
254 image.color_.num_of_channels_ == 4) {
255 return cloudViewer::CreatePointCloudFromRGBDImageT<uint8_t, 4>(
256 image, intrinsic, extrinsic, project_valid_depth_only);
257 }
else if (
image.color_.bytes_per_channel_ == 4 &&
258 image.color_.num_of_channels_ == 1) {
259 return cloudViewer::CreatePointCloudFromRGBDImageT<float, 1>(
260 image, intrinsic, extrinsic, project_valid_depth_only);
264 "[CreatePointCloudFromRGBDImage] Unsupported image format.");
265 return std::make_shared<ccPointCloud>();
270 auto output = std::make_shared<ccPointCloud>();
271 output->resize(
static_cast<unsigned int>(voxel_grid.
voxels_.size()));
274 output->resizeTheRGBTable();
277 for (
auto &it : voxel_grid.
voxels_) {
282 output->setPointColor(
static_cast<unsigned int>(vidx),
292 for (
size_t i = 0; i < m_normals->size(); ++i) {
301 std::vector<double> distances(
size());
306 #pragma omp parallel for schedule(static)
308 for (
int i = 0; i < static_cast<int>(
size()); i++) {
309 std::vector<int> indices(1);
310 std::vector<double> dists(1);
312 *getPoint(
static_cast<unsigned int>(i))),
313 1, indices, dists) == 0) {
315 "[ccPointCloud::ComputePointCloudDistance] Found a point "
316 "without neighbors.");
319 distances[i] = std::sqrt(dists[0]);
326 bool remove_infinite) {
327 bool has_normal = hasNormals();
328 bool has_color = hasColors();
329 bool has_covariance = HasCovariances();
330 bool has_fwf = hasFWF();
332 unsigned sfCount = getNumberOfScalarFields();
334 size_t old_point_num = m_points.size();
336 for (
size_t i = 0; i < old_point_num; i++) {
338 bool is_nan = remove_nan && (std::isnan(m_points[i][0]) ||
339 std::isnan(m_points[i][1]) ||
340 std::isnan(m_points[i][2]));
342 bool is_infinite = remove_infinite && (std::isinf(m_points[i][0]) ||
343 std::isinf(m_points[i][1]) ||
344 std::isinf(m_points[i][2]));
346 if (!is_nan && !is_infinite) {
347 m_points[k] = m_points[i];
348 if (has_normal) m_points[k] = m_points[i];
349 if (has_color) m_points[k] = m_points[i];
350 if (has_covariance) covariances_[k] = covariances_[i];
352 if (fwfDescriptors().contains(
353 m_fwfWaveforms[k].descriptorID())) {
355 fwfDescriptors().remove(m_fwfWaveforms[k].descriptorID());
357 m_fwfWaveforms[k] = m_fwfWaveforms[i];
362 for (
unsigned j = 0; j < sfCount; ++j) {
366 if (currentScalarField) {
368 j, currentScalarField->
getValue(i));
372 "[RemoveNonFinitePoints] Not enough memory to "
373 "copy scalar field!");
378 deleteAllScalarFields();
386 if (!resize(
static_cast<unsigned int>(k))) {
390 sfCount = getNumberOfScalarFields();
392 for (
unsigned j = 0; j < sfCount; ++j) {
401 if (getCurrentDisplayedScalarField()) {
402 int sfIdx = getScalarFieldIndexByName(
403 getCurrentDisplayedScalarField()->getName());
405 setCurrentDisplayedScalarField(sfIdx);
407 setCurrentDisplayedScalarField(
static_cast<int>(sfCount) - 1);
411 CVLog::Print(
"[RemoveNonFinitePoints] {:d} nan points have been removed.",
412 (
int)(old_point_num - k));
414 "[RemoveNonFinitePoints] {:d} nan points have been removed.",
415 (
int)(old_point_num - k));
422 class AccumulatedPoint {
424 void AddPoint(
const ccPointCloud &cloud,
unsigned int index) {
442 Eigen::Vector3d GetAveragePoint()
const {
443 return point_ / double(num_of_points_);
446 Eigen::Vector3d GetAverageNormal()
const {
return normal_.normalized(); }
448 Eigen::Vector3d GetAverageColor()
const {
449 return color_ / double(num_of_points_);
452 Eigen::Matrix3d GetAverageCovariance()
const {
453 return covariance_ / double(num_of_points_);
457 int num_of_points_ = 0;
458 Eigen::Vector3d point_ = Eigen::Vector3d::Zero();
459 Eigen::Vector3d normal_ = Eigen::Vector3d::Zero();
460 Eigen::Vector3d color_ = Eigen::Vector3d::Zero();
461 Eigen::Matrix3d covariance_ = Eigen::Matrix3d::Zero();
464 class point_cubic_id {
470 class AccumulatedPointForTrace :
public AccumulatedPoint {
475 bool approximate_class) {
487 if (approximate_class) {
488 auto got = classes.find(
int(fcolor[0]));
489 if (got == classes.end())
490 classes[int(fcolor[0])] = 1;
492 classes[int(fcolor[0])] += 1;
502 point_cubic_id new_id;
503 new_id.point_id = index;
504 new_id.cubic_id = cubic_index;
505 original_id.push_back(new_id);
509 Eigen::Vector3d GetMaxClass() {
512 for (
auto it = classes.begin(); it != classes.end(); it++) {
513 if (it->second > max_count) {
514 max_count = it->second;
515 max_class = it->first;
518 return Eigen::Vector3d(max_class, max_class, max_class);
521 std::vector<point_cubic_id> GetOriginalID() {
return original_id; }
525 std::vector<point_cubic_id> original_id;
526 std::unordered_map<int, int> classes;
531 auto output = std::make_shared<ccPointCloud>(
"pointCloud");
533 output->setVisible(isVisible());
534 output->setEnabled(isEnabled());
537 output->importParametersFrom(
this);
543 Eigen::Vector3d voxel_size3 =
546 Eigen::Vector3d voxel_min_bound = GetMinBound() - voxel_size3 * 0.5;
547 Eigen::Vector3d voxel_max_bound = GetMaxBound() + voxel_size3 * 0.5;
548 if (
voxel_size * std::numeric_limits<int>::max() <
549 (voxel_max_bound - voxel_min_bound).maxCoeff()) {
551 "[ccPointCloud::VoxelDownSample] voxel_size is too small.");
555 voxelindex_to_accpoint;
557 Eigen::Vector3d ref_coord;
559 for (
int i = 0; i < (int)
size(); i++) {
560 Eigen::Vector3d p = getEigenPoint(i);
561 ref_coord = (p - voxel_min_bound) /
voxel_size;
563 int(
floor(ref_coord(2)));
564 voxelindex_to_accpoint[
voxel_index].AddPoint(*
this, i);
569 bool has_covariances = HasCovariances();
571 if (!output->reserveThePointsTable(
572 static_cast<unsigned int>(voxelindex_to_accpoint.size()))) {
574 "[ccPointCloud::VoxelDownSample] Not enough memory to "
581 if (output->reserveTheRGBTable()) {
582 output->showColors(colorsShown());
585 "[ccPointCloud::VoxelDownSample] Not enough memory to copy "
593 if (output->reserveTheNormsTable()) {
594 output->showNormals(normalsShown());
597 "[ccPointCloud::VoxelDownSample] Not enough memory to copy "
604 for (
auto accpoint : voxelindex_to_accpoint) {
606 output->addPoint(accpoint.second.GetAveragePoint());
610 output->addEigenColor(accpoint.second.GetAverageColor());
615 output->addEigenNorm(accpoint.second.GetAverageNormal());
619 if (has_covariances) {
620 output->covariances_.emplace_back(
621 accpoint.second.GetAverageCovariance());
626 "ccPointCloud down sampled from {:d} points to {:d} points.",
627 (
int)
size(), (
int)output->size());
631 std::tuple<std::shared_ptr<ccPointCloud>,
633 std::vector<std::vector<int>>>
635 const Eigen::Vector3d &min_bound,
636 const Eigen::Vector3d &max_bound,
637 bool approximate_class)
const {
644 auto voxel_min_bound = min_bound;
645 auto voxel_max_bound = max_bound;
646 if (
voxel_size * std::numeric_limits<int>::max() <
647 (voxel_max_bound - voxel_min_bound).maxCoeff()) {
651 Eigen::MatrixXi cubic_id;
652 auto output = std::make_shared<ccPointCloud>(
"pointCloud");
654 output->setVisible(isVisible());
655 output->setEnabled(isEnabled());
658 output->importParametersFrom(
this);
662 voxelindex_to_accpoint;
664 int cid_temp[3] = {1, 2, 4};
665 for (
size_t i = 0; i < this->
size(); i++) {
666 Eigen::Vector3d p = getEigenPoint(i);
667 auto ref_coord = (p - voxel_min_bound) /
voxel_size;
669 int(
floor(ref_coord(1))),
670 int(
floor(ref_coord(2))));
672 for (
int c = 0; c < 3; c++) {
677 voxelindex_to_accpoint[
voxel_index].AddPoint(*
this, i, cid,
683 bool has_covariances = HasCovariances();
685 cubic_id.resize(voxelindex_to_accpoint.size(), 8);
686 cubic_id.setConstant(-1);
687 std::vector<std::vector<int>> original_indices(
688 voxelindex_to_accpoint.size());
690 if (!output->reserveThePointsTable(
691 static_cast<unsigned int>(voxelindex_to_accpoint.size()))) {
693 "[ccPointCloud::VoxelDownSampleAndTrace] Not enough memory to "
699 if (output->reserveTheRGBTable()) {
700 output->showColors(colorsShown());
703 "[ccPointCloud::VoxelDownSampleAndTrace] Not enough memory "
704 "to copy RGB colors!");
711 if (output->reserveTheNormsTable()) {
712 output->showNormals(normalsShown());
715 "[ccPointCloud::VoxelDownSampleAndTrace] Not enough memory "
721 for (
auto accpoint : voxelindex_to_accpoint) {
723 output->addEigenPoint(accpoint.second.GetAveragePoint());
727 if (approximate_class) {
728 output->addEigenColor(accpoint.second.GetMaxClass());
730 output->addEigenColor(accpoint.second.GetAverageColor());
736 output->addEigenNorm(accpoint.second.GetAverageNormal());
740 if (has_covariances) {
741 output->covariances_.emplace_back(
742 accpoint.second.GetAverageCovariance());
745 auto original_id = accpoint.second.GetOriginalID();
746 for (
int i = 0; i < (int)original_id.size(); i++) {
747 size_t pid = original_id[i].point_id;
748 int cid = original_id[i].cubic_id;
749 cubic_id(cnt, cid) = int(pid);
750 original_indices[cnt].push_back(
int(pid));
756 "ccPointCloud down sampled from {:d} points to {:d} points.",
757 (
int)
size(), (
int)output->size());
759 return std::make_tuple(output, cubic_id, original_indices);
763 size_t every_k_points)
const {
764 if (every_k_points == 0) {
766 "[ccPointCloud::UniformDownSample] Illegal sample rate.");
770 std::vector<size_t> indices;
771 for (
size_t i = 0; i <
size(); i += every_k_points) {
772 indices.push_back(i);
779 double sampling_ratio)
const {
780 if (sampling_ratio < 0 || sampling_ratio > 1) {
782 "[RandomDownSample] Illegal sampling_ratio {}, sampling_ratio "
783 "must be between 0 and 1.");
785 std::vector<size_t> indices(this->
size());
786 std::iota(std::begin(indices), std::end(indices), (
size_t)0);
787 std::random_device rd;
788 std::mt19937 prng(rd());
789 std::shuffle(indices.begin(), indices.end(), prng);
790 indices.resize((
int)(sampling_ratio * this->
size()));
795 const size_t num_samples,
const size_t start_index)
const {
796 if (num_samples == 0) {
797 return std::make_shared<ccPointCloud>();
798 }
else if (num_samples == this->
size()) {
799 return std::make_shared<ccPointCloud>(*
this);
800 }
else if (num_samples > this->
size()) {
802 "Illegal number of samples: {}, must <= point size: {}",
803 num_samples, this->
size());
804 }
else if (start_index >= this->
size()) {
806 start_index, this->
size());
810 std::vector<size_t> selected_indices;
811 selected_indices.reserve(num_samples);
812 const size_t num_points = this->
size();
813 std::vector<double> distances(num_points,
814 std::numeric_limits<double>::infinity());
815 size_t farthest_index = start_index;
816 for (
size_t i = 0; i < num_samples; i++) {
817 selected_indices.push_back(farthest_index);
818 const Eigen::Vector3d &selected = getEigenPoint(farthest_index);
820 for (
size_t j = 0; j < num_points; j++) {
821 double dist = (getEigenPoint(j) - selected).squaredNorm();
822 distances[j] = std::min(distances[j], dist);
823 if (distances[j] > max_dist) {
824 max_dist = distances[j];
832 std::tuple<std::shared_ptr<ccPointCloud>, std::vector<size_t>>
834 double search_radius)
const {
835 if (nb_points < 1 || search_radius <= 0) {
837 "[RemoveRadiusOutliers] Illegal input parameters,"
838 "number of points and radius must be positive");
839 return std::make_tuple(std::make_shared<ccPointCloud>(
"pointCloud"),
840 std::vector<size_t>());
844 std::vector<bool> mask = std::vector<bool>(
size());
846 #pragma omp parallel for schedule(static)
848 for (
int i = 0; i < int(
size()); i++) {
849 std::vector<int> tmp_indices;
850 std::vector<double> dist;
853 search_radius, tmp_indices, dist);
854 mask[i] = (nb_neighbors > nb_points);
856 std::vector<size_t> indices;
857 for (
size_t i = 0; i < mask.size(); i++) {
859 indices.push_back(i);
865 std::tuple<std::shared_ptr<ccPointCloud>, std::vector<size_t>>
867 double std_ratio)
const {
868 if (nb_neighbors < 1 || std_ratio <= 0) {
870 "[RemoveStatisticalOutliers] Illegal input parameters, number "
871 "of neighbors and standard deviation ratio must be positive");
872 return std::make_tuple(std::make_shared<ccPointCloud>(
"pointCloud"),
873 std::vector<size_t>());
877 return std::make_tuple(std::make_shared<ccPointCloud>(
"pointCloud"),
878 std::vector<size_t>());
883 std::vector<double> avg_distances = std::vector<double>(
size());
884 std::vector<size_t> indices;
885 size_t valid_distances = 0;
887 #pragma omp parallel for schedule(static)
889 for (
int i = 0; i < int(
size()); i++) {
890 std::vector<int> tmp_indices;
891 std::vector<double> dist;
894 int(nb_neighbors), tmp_indices, dist);
896 if (dist.size() > 0u) {
898 std::for_each(dist.begin(), dist.end(),
899 [](
double &d) { d = std::sqrt(d); });
900 mean = std::accumulate(dist.begin(), dist.end(), 0.0) / dist.size();
902 avg_distances[i] = mean;
904 if (valid_distances == 0) {
905 return std::make_tuple(std::make_shared<ccPointCloud>(
"pointCloud"),
906 std::vector<size_t>());
908 double cloud_mean = std::accumulate(
909 avg_distances.begin(), avg_distances.end(), 0.0,
910 [](
double const &
x,
double const &
y) { return y > 0 ? x + y : x; });
911 cloud_mean /= valid_distances;
912 double sq_sum = std::inner_product(
913 avg_distances.begin(), avg_distances.end(), avg_distances.begin(),
914 0.0, [](
double const &
x,
double const &
y) { return x + y; },
915 [cloud_mean](
double const &
x,
double const &
y) {
916 return x > 0 ? (x - cloud_mean) * (y - cloud_mean) : 0;
919 double std_dev = std::sqrt(sq_sum / (valid_distances - 1));
920 double distance_threshold = cloud_mean + std_ratio *
std_dev;
921 for (
size_t i = 0; i < avg_distances.size(); i++) {
922 if (avg_distances[i] > 0 && avg_distances[i] < distance_threshold) {
923 indices.push_back(i);
932 using namespace geometry;
935 Eigen::Vector3d row0(A(0, 0) - eval0, A(0, 1), A(0, 2));
936 Eigen::Vector3d row1(A(0, 1), A(1, 1) - eval0, A(1, 2));
937 Eigen::Vector3d row2(A(0, 2), A(1, 2), A(2, 2) - eval0);
938 Eigen::Vector3d r0xr1 = row0.cross(row1);
939 Eigen::Vector3d r0xr2 = row0.cross(row2);
940 Eigen::Vector3d r1xr2 = row1.cross(row2);
941 double d0 = r0xr1.dot(r0xr1);
942 double d1 = r0xr2.dot(r0xr2);
943 double d2 = r1xr2.dot(r1xr2);
956 return r0xr1 / std::sqrt(d0);
957 }
else if (imax == 1) {
958 return r0xr2 / std::sqrt(d1);
960 return r1xr2 / std::sqrt(d2);
965 const Eigen::Vector3d &evec0,
967 Eigen::Vector3d U, V;
968 if (std::abs(evec0(0)) > std::abs(evec0(1))) {
970 1 / std::sqrt(evec0(0) * evec0(0) + evec0(2) * evec0(2));
971 U << -evec0(2) * inv_length, 0, evec0(0) * inv_length;
974 1 / std::sqrt(evec0(1) * evec0(1) + evec0(2) * evec0(2));
975 U << 0, evec0(2) * inv_length, -evec0(1) * inv_length;
979 Eigen::Vector3d AU(A(0, 0) * U(0) + A(0, 1) * U(1) + A(0, 2) * U(2),
980 A(0, 1) * U(0) + A(1, 1) * U(1) + A(1, 2) * U(2),
981 A(0, 2) * U(0) + A(1, 2) * U(1) + A(2, 2) * U(2));
983 Eigen::Vector3d AV = {A(0, 0) * V(0) + A(0, 1) * V(1) + A(0, 2) * V(2),
984 A(0, 1) * V(0) + A(1, 1) * V(1) + A(1, 2) * V(2),
985 A(0, 2) * V(0) + A(1, 2) * V(1) + A(2, 2) * V(2)};
987 double m00 = U(0) * AU(0) + U(1) * AU(1) + U(2) * AU(2) - eval1;
988 double m01 = U(0) * AV(0) + U(1) * AV(1) + U(2) * AV(2);
989 double m11 = V(0) * AV(0) + V(1) * AV(1) + V(2) * AV(2) - eval1;
991 double absM00 = std::abs(m00);
992 double absM01 = std::abs(m01);
993 double absM11 = std::abs(m11);
995 if (absM00 >= absM11) {
996 max_abs_comp = std::max(absM00, absM01);
997 if (max_abs_comp > 0) {
998 if (absM00 >= absM01) {
1000 m00 = 1 / std::sqrt(1 + m01 * m01);
1004 m01 = 1 / std::sqrt(1 + m00 * m00);
1007 return m01 * U - m00 * V;
1012 max_abs_comp = std::max(absM11, absM01);
1013 if (max_abs_comp > 0) {
1014 if (absM11 >= absM01) {
1016 m11 = 1 / std::sqrt(1 + m01 * m01);
1020 m01 = 1 / std::sqrt(1 + m11 * m11);
1023 return m11 * U - m01 * V;
1030 Eigen::Vector3d FastEigen3x3(
const Eigen::Matrix3d &covariance) {
1037 Eigen::Matrix3d A = covariance;
1038 double max_coeff = A.maxCoeff();
1039 if (max_coeff == 0) {
1040 return Eigen::Vector3d::Zero();
1044 double norm = A(0, 1) * A(0, 1) + A(0, 2) * A(0, 2) + A(1, 2) * A(1, 2);
1046 Eigen::Vector3d eval;
1047 Eigen::Vector3d evec0;
1048 Eigen::Vector3d evec1;
1049 Eigen::Vector3d evec2;
1051 double q = (A(0, 0) + A(1, 1) + A(2, 2)) / 3;
1053 double b00 = A(0, 0) - q;
1054 double b11 = A(1, 1) - q;
1055 double b22 = A(2, 2) - q;
1058 std::sqrt((b00 * b00 + b11 * b11 + b22 * b22 + norm * 2) / 6);
1060 double c00 = b11 * b22 - A(1, 2) * A(1, 2);
1061 double c01 = A(0, 1) * b22 - A(1, 2) * A(0, 2);
1062 double c02 = A(0, 1) * A(1, 2) - b11 * A(0, 2);
1063 double det = (b00 * c00 - A(0, 1) * c01 + A(0, 2) * c02) / (p * p * p);
1065 double half_det = det * 0.5;
1066 half_det = std::min(std::max(half_det, -1.0), 1.0);
1068 double angle = std::acos(half_det) / (double)3;
1069 double const two_thirds_pi = 2.09439510239319549;
1070 double beta2 = std::cos(angle) * 2;
1071 double beta0 = std::cos(angle + two_thirds_pi) * 2;
1072 double beta1 = -(beta0 + beta2);
1074 eval(0) = q + p * beta0;
1075 eval(1) = q + p * beta1;
1076 eval(2) = q + p * beta2;
1078 if (half_det >= 0) {
1080 if (eval(2) < eval(0) && eval(2) < eval(1)) {
1086 if (eval(1) < eval(0) && eval(1) < eval(2)) {
1089 evec0 = evec1.cross(evec2);
1093 if (eval(0) < eval(1) && eval(0) < eval(2)) {
1099 if (eval(1) < eval(0) && eval(1) < eval(2)) {
1102 evec2 = evec0.cross(evec1);
1107 if (A(0, 0) < A(1, 1) && A(0, 0) < A(2, 2)) {
1108 return Eigen::Vector3d(1, 0, 0);
1109 }
else if (A(1, 1) < A(0, 0) && A(1, 1) < A(2, 2)) {
1110 return Eigen::Vector3d(0, 1, 0);
1112 return Eigen::Vector3d(0, 0, 1);
1117 Eigen::Vector3d ComputeNormal(
const Eigen::Matrix3d &covariance,
1118 bool fast_normal_computation) {
1119 if (fast_normal_computation) {
1120 return FastEigen3x3(covariance);
1122 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver;
1123 solver.compute(covariance, Eigen::ComputeEigenvectors);
1124 return solver.eigenvectors().col(0);
1135 std::vector<Eigen::Matrix3d> covariances;
1136 covariances.resize(
points.size());
1141 #pragma omp parallel for schedule(static) \
1142 num_threads(utility::EstimateMaxThreads())
1144 for (
int i = 0; i < (int)
points.size(); i++) {
1145 std::vector<int> indices;
1146 std::vector<double> distance2;
1147 if (kdtree.
Search(
points[i], search_param, indices, distance2) >= 3) {
1152 covariances[i] = covariance;
1155 covariances[i] = Eigen::Matrix3d::Identity();
1163 this->covariances_ = EstimatePerPointCovariances(*
this, search_param);
1169 bool fast_normal_computation ) {
1170 bool has_normal = hasNormals();
1171 if (!hasNormals()) {
1172 resizeTheNormsTable();
1175 std::vector<Eigen::Matrix3d> covariances;
1176 if (!HasCovariances()) {
1177 covariances = EstimatePerPointCovariances(*
this, search_param);
1179 covariances = covariances_;
1183 #pragma omp parallel for schedule(static) \
1184 num_threads(utility::EstimateMaxThreads())
1186 for (
int i = 0; i < (int)covariances.size(); i++) {
1187 auto normal = ComputeNormal(covariances[i], fast_normal_computation);
1188 if (
normal.norm() == 0.0) {
1191 getPointNormal(
static_cast<unsigned int>(i)));
1193 normal = Eigen::Vector3d(0.0, 0.0, 1.0);
1197 static_cast<unsigned int>(i)))) < 0.0) {
1200 setPointNormal(
static_cast<unsigned int>(i),
normal);
1207 const Eigen::Vector3d &orientation_reference
1209 if (!hasNormals()) {
1211 "[OrientNormalsToAlignWithDirection] No normals in the "
1212 "ccPointCloud. Call EstimateNormals() first.");
1215 #pragma omp parallel for schedule(static) \
1216 num_threads(utility::EstimateMaxThreads())
1218 for (
int i = 0; i < (int)this->
size(); i++) {
1219 auto &
normal = getPointNormalPtr(
static_cast<size_t>(i));
1220 if (
normal.norm() == 0.0f) {
1231 const Eigen::Vector3d &camera_location ) {
1232 if (hasNormals() ==
false) {
1234 "[OrientNormalsTowardsCameraLocation] No normals in the "
1235 "ccPointCloud. Call EstimateNormals() first.");
1238 #pragma omp parallel for schedule(static) \
1239 num_threads(utility::EstimateMaxThreads())
1241 for (
int i = 0; i < (int)this->
size(); i++) {
1242 Eigen::Vector3d orientation_reference =
1243 camera_location - getEigenPoint(
static_cast<size_t>(i));
1244 auto &
normal = getPointNormalPtr(
static_cast<size_t>(i));
1245 if (
normal.norm() == 0.0f) {
1246 normal = orientation_reference;
1247 if (
normal.norm() == 0.0f) {
1252 }
else if (
normal.dot(orientation_reference) < 0.0f) {
1260 if (!hasNormals()) {
1262 "[OrientNormalsConsistentTangentPlane] No normals in the "
1263 "ccPointCloud. Call EstimateNormals() first.");
1268 std::shared_ptr<cloudViewer::geometry::TetraMesh> delaunay_mesh;
1269 std::vector<size_t> pt_map;
1270 std::tie(delaunay_mesh, pt_map) =
1272 std::vector<cloudViewer::WeightedEdge> delaunay_graph;
1273 std::unordered_set<size_t> graph_edges;
1274 auto EdgeIndex = [&](
size_t v0,
size_t v1) ->
size_t {
1275 return std::min(v0, v1) * this->
size() + std::max(v0, v1);
1277 auto AddEdgeToDelaunayGraph = [&](
size_t v0,
size_t v1) {
1281 if (graph_edges.count(edge) == 0) {
1282 double dist = (getEigenPoint(v0) - getEigenPoint(v1)).squaredNorm();
1283 delaunay_graph.push_back(cloudViewer::WeightedEdge(v0, v1, dist));
1284 graph_edges.insert(edge);
1288 AddEdgeToDelaunayGraph(tetra[0], tetra[1]);
1289 AddEdgeToDelaunayGraph(tetra[0], tetra[2]);
1290 AddEdgeToDelaunayGraph(tetra[0], tetra[3]);
1291 AddEdgeToDelaunayGraph(tetra[1], tetra[2]);
1292 AddEdgeToDelaunayGraph(tetra[1], tetra[3]);
1293 AddEdgeToDelaunayGraph(tetra[2], tetra[3]);
1296 std::vector<cloudViewer::WeightedEdge> mst =
1297 cloudViewer::Kruskal(delaunay_graph, this->
size());
1299 auto NormalWeight = [&](
size_t v0,
size_t v1) ->
double {
1300 return 1.0 - std::abs(getEigenNormal(v0).dot(getEigenNormal(v1)));
1302 for (
auto &edge : mst) {
1303 edge.weight_ = NormalWeight(edge.v0_, edge.v1_);
1308 for (
size_t v0 = 0; v0 < this->
size(); ++v0) {
1309 std::vector<int> neighbors;
1310 std::vector<double> dists2;
1311 kdtree.
SearchKNN(getEigenPoint(v0), int(k), neighbors, dists2);
1312 for (
size_t vidx1 = 0; vidx1 < neighbors.size(); ++vidx1) {
1313 size_t v1 = size_t(neighbors[vidx1]);
1318 if (graph_edges.count(edge) == 0) {
1319 double weight = NormalWeight(v0, v1);
1320 mst.push_back(cloudViewer::WeightedEdge(v0, v1, weight));
1321 graph_edges.insert(edge);
1327 mst = cloudViewer::Kruskal(mst, this->
size());
1330 std::vector<std::unordered_set<size_t>> mst_graph(this->
size());
1331 for (
const auto &edge : mst) {
1332 size_t v0 = edge.v0_;
1333 size_t v1 = edge.v1_;
1334 mst_graph[v0].insert(v1);
1335 mst_graph[v1].insert(v0);
1340 double max_z = std::numeric_limits<double>::lowest();
1342 for (
size_t vidx = 0; vidx < this->
size(); ++vidx) {
1343 const Eigen::Vector3d &v = getEigenPoint(vidx);
1351 std::queue<size_t> traversal_queue;
1352 std::vector<bool> visited(this->
size(),
false);
1353 traversal_queue.push(v0);
1355 if (n0.
dot(n1) < 0) {
1359 TestAndOrientNormal(
CCVector3(0.0f, 0.0f, 1.0f), getPointNormalPtr(v0));
1360 while (!traversal_queue.empty()) {
1361 v0 = traversal_queue.front();
1362 traversal_queue.pop();
1364 for (
size_t v1 : mst_graph[v0]) {
1366 traversal_queue.push(v1);
1367 TestAndOrientNormal(getPointNormalPtr(v0),
1368 getPointNormalPtr(v1));
1375 std::vector<double> mahalanobis(
size());
1376 Eigen::Vector3d mean;
1377 Eigen::Matrix3d covariance;
1378 std::tie(mean, covariance) = computeMeanAndCovariance();
1380 Eigen::Matrix3d cov_inv = covariance.inverse();
1382 #pragma omp parallel for schedule(static) \
1383 num_threads(utility::EstimateMaxThreads())
1385 for (
int i = 0; i < (int)
size(); i++) {
1389 mahalanobis[i] = std::sqrt(p.transpose() * cov_inv * p);
1395 std::vector<double> nn_dis(
size());
1398 #pragma omp parallel for schedule(static) \
1399 num_threads(utility::EstimateMaxThreads())
1401 for (
int i = 0; i < (int)
size(); i++) {
1402 std::vector<int> indices(2);
1403 std::vector<double> dists(2);
1405 *getPoint(
static_cast<unsigned int>(i))),
1406 2, indices, dists) <= 1) {
1408 "[ComputePointCloudNearestNeighborDistance] Found a point "
1409 "without neighbors.");
1412 nn_dis[i] = std::sqrt(dists[1]);
1419 std::vector<double> nn_dis = ComputeNearestNeighborDistance();
1420 return std::accumulate(std::begin(nn_dis), std::end(nn_dis), 0.0) /
1424 std::tuple<std::shared_ptr<ccMesh>, std::vector<size_t>>
1429 std::tuple<std::shared_ptr<ccMesh>, std::vector<size_t>>
1431 const double radius)
const {
1434 "[ccPointCloud::HiddenPointRemoval] radius must be larger than "
1436 return std::make_tuple(std::make_shared<ccMesh>(
nullptr),
1437 std::vector<size_t>());
1441 std::vector<CCVector3> spherical_projection;
1442 for (
unsigned int pidx = 0; pidx <
size(); ++pidx) {
1443 CCVector3 projected_point = *getPoint(pidx) - camera_location;
1444 double norm = projected_point.
norm();
1445 spherical_projection.push_back(
1448 projected_point / norm);
1452 size_t origin_pidx = spherical_projection.size();
1453 spherical_projection.push_back(
CCVector3(0, 0, 0));
1456 std::shared_ptr<ccMesh> visible_mesh;
1457 std::vector<size_t> pt_map;
1458 std::tie(visible_mesh, pt_map) =
1460 spherical_projection);
1463 size_t origin_vidx = pt_map.size();
1464 for (
size_t vidx = 0; vidx < pt_map.size(); vidx++) {
1465 size_t pidx = pt_map[vidx];
1467 if (pidx <
size()) {
1468 visible_mesh->setVertice(vidx, getEigenPoint(pidx));
1471 if (pidx == origin_pidx) {
1473 visible_mesh->setVertice(vidx, camera_location);
1478 if (origin_vidx < visible_mesh->getVerticeSize()) {
1479 visible_mesh->getAssociatedCloud()->removePoints(origin_vidx);
1480 pt_map.erase(pt_map.begin() + origin_vidx);
1481 for (
size_t tidx = visible_mesh->size(); tidx-- > 0;) {
1482 auto &tsi = visible_mesh->getTrianglesPtr()->getValue(tidx);
1483 if (tsi.i1 == (
unsigned int)origin_vidx ||
1484 tsi.i2 == (
unsigned int)origin_vidx ||
1485 tsi.i3 == (
unsigned int)origin_vidx) {
1486 visible_mesh->removeTriangles(tidx);
1488 if (tsi.i1 > (
int)origin_vidx) tsi.i1 -= 1;
1489 if (tsi.i2 > (
int)origin_vidx) tsi.i2 -= 1;
1490 if (tsi.i3 > (
int)origin_vidx) tsi.i3 -= 1;
1494 return std::make_tuple(visible_mesh, pt_map);
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
std::shared_ptr< core::Tensor > image
static bool Print(const char *format,...)
Prints out a formatted message in console.
void normalize()
Sets vector norm to unity.
Type dot(const Vector3Tpl &v) const
Dot product.
Type norm() const
Returns vector norm.
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
static CCVector3 & GetNormalPtr(unsigned normIndex)
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
std::tuple< std::shared_ptr< ccMesh >, std::vector< size_t > > HiddenPointRemoval(const Eigen::Vector3d &camera_location, const double radius) const
This is an implementation of the Hidden Point Removal operator described in Katz et....
std::vector< double > ComputePointCloudDistance(const ccPointCloud &target)
Function to compute the point to point distances between point clouds.
bool EstimateNormals(const cloudViewer::geometry::KDTreeSearchParam &search_param=cloudViewer::geometry::KDTreeSearchParamKNN(), bool fast_normal_computation=true)
Function to compute the normals of a point cloud.
std::tuple< std::shared_ptr< ccPointCloud >, std::vector< size_t > > RemoveStatisticalOutliers(size_t nb_neighbors, double std_ratio) const
Function to remove points that are further away from their nb_neighbor neighbors in average.
static std::vector< Eigen::Matrix3d > EstimatePerPointCovariances(const ccPointCloud &input, const cloudViewer::geometry::KDTreeSearchParam &search_param=cloudViewer::geometry::KDTreeSearchParamKNN())
Static function to compute the covariance matrix for each point of a point cloud. Doesn't change the ...
std::tuple< std::shared_ptr< ccPointCloud >, Eigen::MatrixXi, std::vector< std::vector< int > > > VoxelDownSampleAndTrace(double voxel_size, const Eigen::Vector3d &min_bound, const Eigen::Vector3d &max_bound, bool approximate_class=false) const
Function to downsample using geometry.ccPointCloud.VoxelDownSample.
bool HasCovariances() const
Returns 'true' if the point cloud contains per-point covariance matrix.
bool OrientNormalsTowardsCameraLocation(const Eigen::Vector3d &camera_location=Eigen::Vector3d::Zero())
Function to orient the normals of a point cloud.
std::vector< double > ComputeMahalanobisDistance() const
Function to compute the Mahalanobis distance for points in an input point cloud.
Eigen::Vector3d getEigenNormal(size_t index) const
double ComputeResolution() const
std::shared_ptr< ccPointCloud > RandomDownSample(double sampling_ratio) const
Function to downsample input pointcloud into output pointcloud randomly.
std::tuple< std::shared_ptr< ccPointCloud >, std::vector< size_t > > RemoveRadiusOutliers(size_t nb_points, double search_radius) const
Function to remove points that have less than nb_points in a sphere of a given radius.
bool hasNormals() const override
Returns whether normals are enabled or not.
std::shared_ptr< ccPointCloud > FarthestPointDownSample(const size_t num_samples, const size_t start_index=0) const
Function to downsample input pointcloud into output pointcloud with a set of points has farthest dist...
void OrientNormalsConsistentTangentPlane(size_t k)
Function to consistently orient estimated normals based on consistent tangent planes as described in ...
bool OrientNormalsToAlignWithDirection(const Eigen::Vector3d &orientation_reference=Eigen::Vector3d(0.0, 0.0, 1.0))
Function to orient the normals of a point cloud.
std::tuple< std::shared_ptr< ccMesh >, std::vector< size_t > > ComputeConvexHull() const
Function that computes the convex hull of the point cloud using qhull.
std::shared_ptr< ccPointCloud > CreateFromVoxelGrid(const cloudViewer::geometry::VoxelGrid &voxel_grid)
Function to create a PointCloud from a VoxelGrid.
std::vector< double > ComputeNearestNeighborDistance() const
bool hasColors() const override
Returns whether colors are enabled or not.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
std::vector< Eigen::Matrix3d > covariances_
Covariance Matrix for each point.
Eigen::Vector3d getEigenColor(size_t index) const
std::shared_ptr< ccPointCloud > UniformDownSample(size_t every_k_points) const
Function to downsample input ccPointCloud into output ccPointCloud uniformly.
ccPointCloud & PaintUniformColor(const Eigen::Vector3d &color)
Assigns each vertex in the ccMesh the same color.
ccPointCloud & NormalizeNormals()
Normalize point normals to length 1.`.
std::shared_ptr< ccPointCloud > VoxelDownSample(double voxel_size)
Function to downsample input ccPointCloud into output ccPointCloud with a voxel.
void EstimateCovariances(const cloudViewer::geometry::KDTreeSearchParam &search_param=cloudViewer::geometry::KDTreeSearchParamKNN())
Function to compute the covariance matrix for each point of a point cloud.
ccPointCloud & RemoveNonFinitePoints(bool remove_nan=true, bool remove_infinite=true)
Remove all points fromt he point cloud that have a nan entry, or infinite entries.
static std::shared_ptr< ccPointCloud > CreateFromDepthImage(const cloudViewer::geometry::Image &depth, const cloudViewer::camera::PinholeCameraIntrinsic &intrinsic, const Eigen::Matrix4d &extrinsic=Eigen::Matrix4d::Identity(), double depth_scale=1000.0, double depth_trunc=1000.0, int stride=1, bool project_valid_depth_only=true)
Factory function to create a pointcloud from a depth image and a camera model.
static std::shared_ptr< ccPointCloud > CreateFromRGBDImage(const cloudViewer::geometry::RGBDImage &image, const cloudViewer::camera::PinholeCameraIntrinsic &intrinsic, const Eigen::Matrix4d &extrinsic=Eigen::Matrix4d::Identity(), bool project_valid_depth_only=true)
Factory function to create a pointcloud from an RGB-D image and a camera model.
A scalar field associated to display-related parameters.
void computeMinAndMax() override
Determines the min and max values.
std::vector< Eigen::Vector3d > getEigenPoints() const
Eigen::Vector3d getEigenPoint(size_t index) const
ScalarType & getValue(std::size_t index)
void setValue(std::size_t index, ScalarType value)
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
Contains the pinhole camera intrinsic parameters.
std::pair< double, double > GetFocalLength() const
Returns the focal length in a tuple of X-axis and Y-axis focal lengths.
std::pair< double, double > GetPrincipalPoint() const
The Image class stores image with customizable width, height, num of channels and bytes per channel.
int num_of_channels_
Number of chanels in the image.
int height_
Height of the image.
int bytes_per_channel_
Number of bytes per channel.
std::shared_ptr< Image > ConvertDepthToFloatImage(double depth_scale=1000.0, double depth_trunc=3.0) const
int width_
Width of the image.
T * PointerAt(int u, int v) const
Function to access the raw data of a single-channel Image.
KDTree with FLANN for nearest neighbor search.
int SearchRadius(const T &query, double radius, std::vector< int > &indices, std::vector< double > &distance2) const
int SearchKNN(const T &query, int knn, std::vector< int > &indices, std::vector< double > &distance2) const
int Search(const T &query, const KDTreeSearchParam ¶m, std::vector< int > &indices, std::vector< double > &distance2) const
bool SetGeometry(const ccHObject &geometry)
Base class for KDTree search parameters.
static std::tuple< std::shared_ptr< ccMesh >, std::vector< size_t > > ComputeConvexHull(const std::vector< Eigen::Vector3d > &points)
RGBDImage is for a pair of registered color and depth images,.
static std::tuple< std::shared_ptr< TetraMesh >, std::vector< size_t > > CreateFromPointCloud(const ccPointCloud &point_cloud)
Function that creates a tetrahedral mesh (TetraMeshFactory.cpp). from a point cloud.
VoxelGrid is a collection of voxels which are aligned in grid.
std::unordered_map< Eigen::Vector3i, Voxel, cloudViewer::utility::hash_eigen< Eigen::Vector3i > > voxels_
Voxels contained in voxel grid.
Eigen::Vector3d GetVoxelCenterCoordinate(const Eigen::Vector3i &idx) const
Function that returns the 3d coordinates of the queried voxel center.
bool HasColors() const
Returns true if the voxel grid contains voxel colors.
Base Voxel class, containing grid id and color.
Eigen::Vector3i grid_index_
Grid coordinate index of the voxel.
Eigen::Vector3d color_
Color of the voxel.
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
typename boost::graph_traits< CP::Graph< T > >::edges_size_type EdgeIndex
double std_dev(const T &vec)
CLOUDVIEWER_HOST_DEVICE void ComputeEigenvector0(const scalar_t *A, const scalar_t eval0, scalar_t *eigen_vector0)
CLOUDVIEWER_HOST_DEVICE void ComputeEigenvector1(const scalar_t *A, const scalar_t *evec0, const scalar_t eval1, scalar_t *eigen_vector1)
void SelectByIndex(benchmark::State &state, bool remove_duplicates, const core::Device &device)
Eigen::Matrix3d ComputeCovariance(const std::vector< Eigen::Vector3d > &points, const std::vector< IdxType > &indices)
Function to compute the covariance matrix of a set of points.
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Eigen::Matrix< Index, 4, 1 > Vector4i