10 #include <Eigen/Dense>
13 #include <unordered_map>
14 #include <unordered_set>
33 Quadric(
const Eigen::Vector4d& plane,
double weight = 1) {
34 Eigen::Vector3d n = plane.head<3>();
35 A_ = weight * n * n.transpose();
36 b_ = weight * plane(3) * n;
37 c_ = weight * plane(3) * plane(3);
49 res.
A_ = A_ + other.
A_;
50 res.
b_ = b_ + other.
b_;
51 res.
c_ = c_ + other.
c_;
55 double Eval(
const Eigen::Vector3d& v)
const {
56 Eigen::Vector3d Av = A_ * v;
57 double q = v.dot(Av) + 2 * b_.dot(v) + c_;
61 bool IsInvertible()
const {
return std::fabs(A_.determinant()) > 1
e-4; }
63 Eigen::Vector3d
Minimum()
const {
return -A_.ldlt().solve(b_); }
79 if (hasTriangleUvs()) {
81 "[SimplifyVertexClustering] This mesh contains triangle uvs "
82 "that are not handled in this function");
88 "[ccMesh::SimplifyVertexClustering] mesh"
89 "should set associated cloud before using!");
97 auto mesh = std::make_shared<ccMesh>(baseVertices);
98 mesh->addChild(baseVertices);
104 Eigen::Vector3d voxel_size3 =
106 Eigen::Vector3d voxel_min_bound = GetMinBound() - voxel_size3 * 0.5;
107 Eigen::Vector3d voxel_max_bound = GetMaxBound() + voxel_size3 * 0.5;
108 if (
voxel_size * std::numeric_limits<int>::max() <
109 (voxel_max_bound - voxel_min_bound).maxCoeff()) {
113 auto GetVoxelIdx = [&](
const Eigen::Vector3d& vert) {
114 Eigen::Vector3d ref_coord = (vert - voxel_min_bound) /
voxel_size;
116 int(
floor(ref_coord(2))));
120 std::unordered_map<Eigen::Vector3i, std::unordered_set<int>,
127 for (
size_t vidx = 0; vidx < cloud->
size(); ++vidx) {
129 voxel_vertices[vox_idx].insert(
int(vidx));
131 if (voxel_vert_ind.count(vox_idx) == 0) {
132 voxel_vert_ind[vox_idx] = new_vidx;
139 bool has_vert_color = cloud->
hasColors();
140 baseVertices->
resize(
static_cast<unsigned int>(voxel_vertices.size()));
141 if (has_vert_normal) {
145 if (has_vert_color) {
150 auto AvgVertex = [&](
const std::unordered_set<int> ind) {
151 Eigen::Vector3d aggr(0, 0, 0);
152 for (
int vidx : ind) {
155 aggr /= double(ind.size());
158 auto AvgNormal = [&](
const std::unordered_set<int> ind) {
159 Eigen::Vector3d aggr(0, 0, 0);
160 for (
int vidx : ind) {
163 aggr /= double(ind.size());
166 auto AvgColor = [&](
const std::unordered_set<int> ind) {
167 Eigen::Vector3d aggr(0, 0, 0);
168 for (
int vidx : ind) {
171 aggr /= double(ind.size());
175 if (contraction == SimplificationContraction::Average) {
176 for (
const auto& voxel : voxel_vertices) {
177 int vox_vidx = voxel_vert_ind[voxel.first];
178 baseVertices->
setPoint(
static_cast<size_t>(vox_vidx),
179 AvgVertex(voxel.second));
180 if (has_vert_normal) {
182 AvgNormal(voxel.second));
184 if (has_vert_color) {
186 AvgColor(voxel.second));
189 }
else if (contraction == SimplificationContraction::Quadric) {
191 std::unordered_map<int, std::unordered_set<int>> vert_to_triangles;
192 for (
size_t tidx = 0; tidx <
size(); ++tidx) {
194 getTriangleVertIndexes(
static_cast<unsigned>(tidx));
195 vert_to_triangles[tri->
i1].emplace(
int(tidx));
196 vert_to_triangles[tri->
i2].emplace(
int(tidx));
197 vert_to_triangles[tri->
i3].emplace(
int(tidx));
200 for (
const auto& voxel : voxel_vertices) {
201 size_t vox_vidx =
static_cast<size_t>(voxel_vert_ind[voxel.first]);
203 for (
int vidx : voxel.second) {
204 for (
int tidx : vert_to_triangles[vidx]) {
205 Eigen::Vector4d p = GetTrianglePlane(tidx);
206 double area = GetTriangleArea(tidx);
211 Eigen::Vector3d v = q.
Minimum();
212 baseVertices->
setPoint(vox_vidx, v);
214 baseVertices->
setPoint(vox_vidx, AvgVertex(voxel.second));
217 if (has_vert_normal) {
220 if (has_vert_color) {
221 baseVertices->
setPointColor(vox_vidx, AvgColor(voxel.second));
227 std::unordered_set<Eigen::Vector3i, utility::hash_eigen<Eigen::Vector3i>>
229 for (
unsigned triIndx = 0; triIndx <
size(); ++triIndx) {
230 Eigen::Vector3d v0, v1, v2;
231 getTriangleVertices(triIndx, v0.data(), v1.data(), v2.data());
232 int vidx0 = voxel_vert_ind[GetVoxelIdx(v0)];
233 int vidx1 = voxel_vert_ind[GetVoxelIdx(v1)];
234 int vidx2 = voxel_vert_ind[GetVoxelIdx(v2)];
237 if (vidx0 == vidx1 || vidx0 == vidx2 || vidx1 == vidx2) {
243 if (vidx1 < vidx0 && vidx1 < vidx2) {
248 }
else if (vidx2 < vidx0 && vidx2 < vidx1) {
258 mesh->resize(triangles.size());
259 unsigned int tidx = 0;
261 mesh->setTriangle(tidx, triangle);
265 if (hasTriNormals()) {
266 mesh->ComputeTriangleNormals();
273 int target_number_of_triangles,
274 double maximum_error ,
275 double boundary_weight )
const {
276 if (hasTriangleUvs()) {
278 "[SimplifyQuadricDecimation] This mesh contains triangle uvs "
279 "that are not handled in this function");
281 typedef std::tuple<double, int, int> CostEdge;
286 "[ccMesh::SimplifyVertexClustering] mesh"
287 "should set associated cloud before using!");
291 assert(baseVertices);
295 auto mesh = std::make_shared<ccMesh>(baseVertices);
296 mesh->addChild(baseVertices);
297 mesh->merge(
this,
false);
299 std::vector<bool> vertices_deleted(cloud->
size(),
false);
300 std::vector<bool> triangles_deleted(this->
size(),
false);
303 std::vector<std::unordered_set<int>> vert_to_triangles(cloud->
size());
304 std::vector<Eigen::Vector4d> triangle_planes(this->
size());
305 std::vector<double> triangle_areas(this->
size());
306 for (
size_t tidx = 0; tidx < this->
size(); ++tidx) {
308 getTriangleVertIndexes(
static_cast<unsigned>(tidx));
309 vert_to_triangles[tri->
i1].emplace(
static_cast<int>(tidx));
310 vert_to_triangles[tri->
i2].emplace(
static_cast<int>(tidx));
311 vert_to_triangles[tri->
i3].emplace(
static_cast<int>(tidx));
313 triangle_planes[tidx] = GetTrianglePlane(tidx);
314 triangle_areas[tidx] = GetTriangleArea(tidx);
318 std::vector<Quadric> Qs(cloud->
size());
319 for (
size_t vidx = 0; vidx < cloud->
size(); ++vidx) {
320 for (
int tidx : vert_to_triangles[vidx]) {
321 Qs[vidx] +=
Quadric(triangle_planes[tidx], triangle_areas[tidx]);
327 auto AddPerpPlaneQuadric = [&](
int vidx0,
int vidx1,
int vidx2,
329 int min = std::min(vidx0, vidx1);
330 int max = std::max(vidx0, vidx1);
332 if (edge_triangle_count[edge].
size() != 1) {
341 Eigen::Vector3d vert2p = (vert2 - vert0).cross(vert2 - vert1);
342 Eigen::Vector4d plane = ComputeTrianglePlane(vert0, vert1, vert2p);
343 Quadric quad(plane, area * boundary_weight);
347 for (
size_t tidx = 0; tidx < this->
size(); ++tidx) {
348 const auto& tria = getTriangle(tidx);
349 double area = triangle_areas[tidx];
350 AddPerpPlaneQuadric(tria(0), tria(1), tria(2), area);
351 AddPerpPlaneQuadric(tria(1), tria(2), tria(0), area);
352 AddPerpPlaneQuadric(tria(2), tria(0), tria(1), area);
363 auto CostEdgeComp = [](
const CostEdge&
a,
const CostEdge& b) {
364 return std::get<0>(
a) > std::get<0>(b);
366 std::priority_queue<CostEdge, std::vector<CostEdge>, decltype(CostEdgeComp)>
369 auto AddEdge = [&](
int vidx0,
int vidx1,
bool update) {
370 int min = std::min(vidx0, vidx1);
371 int max = std::max(vidx0, vidx1);
373 if (update || vbars.count(edge) == 0) {
378 Eigen::Vector3d vbar;
381 cost = Qbar.
Eval(vbar);
383 const Eigen::Vector3d& v0 =
385 const Eigen::Vector3d& v1 =
387 Eigen::Vector3d vmid = (v0 + v1) / 2;
388 double cost0 = Qbar.
Eval(v0);
389 double cost1 = Qbar.
Eval(v1);
390 double costmid = Qbar.
Eval(vbar);
391 cost = std::min(cost0, std::min(cost1, costmid));
392 if (cost == costmid) {
394 }
else if (cost == cost0) {
402 queue.push(CostEdge(cost, min, max));
407 for (
size_t trindx = 0; trindx < this->
size(); ++trindx) {
408 const auto& triangle = getTriangle(trindx);
409 AddEdge(triangle(0), triangle(1),
false);
410 AddEdge(triangle(1), triangle(2),
false);
411 AddEdge(triangle(2), triangle(0),
false);
416 bool has_vert_color = cloud->
hasColors();
417 int n_triangles = int(this->
size());
418 while (n_triangles > target_number_of_triangles && !queue.empty()) {
422 std::tie(cost, vidx0, vidx1) = queue.top();
425 if (cost > maximum_error) {
431 bool valid = !vertices_deleted[vidx0] && !vertices_deleted[vidx1] &&
438 bool flipped =
false;
439 for (
int tidx : vert_to_triangles[vidx1]) {
440 if (triangles_deleted[tidx]) {
445 mesh->getTriangle(
static_cast<size_t>(tidx));
447 vidx0 == tria(0) || vidx0 == tria(1) || vidx0 == tria(2);
449 vidx1 == tria(0) || vidx1 == tria(1) || vidx1 == tria(2);
450 if (has_vidx0 && has_vidx1) {
454 Eigen::Vector3d vert0 =
456 Eigen::Vector3d vert1 =
458 Eigen::Vector3d vert2 =
460 Eigen::Vector3d norm_before = (vert1 - vert0).cross(vert2 - vert0);
461 norm_before /= norm_before.norm();
463 if (vidx1 == tria(0)) {
465 }
else if (vidx1 == tria(1)) {
467 }
else if (vidx1 == tria(2)) {
471 Eigen::Vector3d norm_after = (vert1 - vert0).cross(vert2 - vert0);
472 norm_after /= norm_after.norm();
473 if (norm_before.dot(norm_after) < 0) {
483 for (
int tidx : vert_to_triangles[vidx1]) {
484 if (triangles_deleted[tidx]) {
489 mesh->getTriangleVertIndexes(
static_cast<unsigned>(tidx));
490 bool has_vidx0 = vidx0 ==
static_cast<int>(tria->
i[0]) ||
491 vidx0 ==
static_cast<int>(tria->
i[1]) ||
492 vidx0 ==
static_cast<int>(tria->
i[2]);
493 bool has_vidx1 = vidx1 ==
static_cast<int>(tria->
i[0]) ||
494 vidx1 ==
static_cast<int>(tria->
i[1]) ||
495 vidx1 ==
static_cast<int>(tria->
i[2]);
496 if (has_vidx0 && has_vidx1) {
497 triangles_deleted[tidx] =
true;
502 if (vidx1 ==
static_cast<int>(tria->
i[0])) {
503 tria->
i[0] =
static_cast<unsigned>(vidx0);
504 }
else if (vidx1 ==
static_cast<int>(tria->
i[1])) {
505 tria->
i[1] =
static_cast<unsigned>(vidx0);
506 }
else if (vidx1 ==
static_cast<int>(tria->
i[2])) {
507 tria->
i[2] =
static_cast<unsigned>(vidx0);
509 vert_to_triangles[vidx0].insert(tidx);
513 baseVertices->
setPoint(
static_cast<size_t>(vidx0), vbars[edge]);
514 Qs[vidx0] += Qs[vidx1];
515 if (has_vert_normal) {
517 static_cast<size_t>(vidx0),
519 static_cast<size_t>(vidx0)) +
521 static_cast<size_t>(vidx1))));
523 if (has_vert_color) {
525 static_cast<size_t>(vidx0),
527 static_cast<size_t>(vidx0)) +
529 static_cast<size_t>(vidx1))));
531 vertices_deleted[vidx1] =
true;
534 for (
const auto& tidx : vert_to_triangles[vidx0]) {
535 if (triangles_deleted[tidx]) {
539 mesh->getTriangle(
static_cast<size_t>(tidx));
540 if (tria(0) == vidx0 || tria(1) == vidx0) {
541 AddEdge(tria(0), tria(1),
true);
543 if (tria(1) == vidx0 || tria(2) == vidx0) {
544 AddEdge(tria(1), tria(2),
true);
546 if (tria(2) == vidx0 || tria(0) == vidx0) {
547 AddEdge(tria(2), tria(0),
true);
553 unsigned int next_free = 0;
554 std::unordered_map<int, int> vert_remapping;
555 for (
unsigned int idx = 0; idx < baseVertices->
size(); ++idx) {
556 if (!vertices_deleted[idx]) {
557 vert_remapping[int(idx)] = next_free;
559 if (has_vert_normal) {
563 if (has_vert_color) {
571 baseVertices->
resize(next_free);
572 if (has_vert_normal) {
575 if (has_vert_color) {
580 for (
size_t idx = 0; idx < mesh->size(); ++idx) {
581 if (!triangles_deleted[idx]) {
583 mesh->setTriangle(next_free,
585 vert_remapping[tria(1)],
586 vert_remapping[tria(2)]));
590 mesh->resize(next_free);
592 if (hasTriNormals()) {
593 mesh->ComputeTriangleNormals();
double c_
c_ = d . d, where d the non-normal component pf the plane parameters
Quadric operator+(const Quadric &other) const
bool IsInvertible() const
Eigen::Vector3d Minimum() const
Quadric(const Eigen::Vector4d &plane, double weight=1)
double Eval(const Eigen::Vector3d &v) const
Eigen::Matrix3d A_
A_ = n . n^T, where n is the plane normal.
Quadric & operator+=(const Quadric &other)
virtual void showNormals(bool state)
Sets normals visibility.
virtual void showColors(bool state)
Sets colors visibility.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
std::shared_ptr< ccMesh > SimplifyQuadricDecimation(int target_number_of_triangles, double maximum_error=std::numeric_limits< double >::infinity(), double boundary_weight=1.0) const
std::shared_ptr< ccMesh > SimplifyVertexClustering(double voxel_size, SimplificationContraction contraction=SimplificationContraction::Average) const
virtual void setLocked(bool state)
Sets the "enabled" property.
virtual void setEnabled(bool state)
Sets the "enabled" property.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setEigenColor(size_t index, const Eigen::Vector3d &color)
bool resizeTheNormsTable()
Resizes the compressed normals array.
void setEigenNormal(size_t index, const Eigen::Vector3d &normal)
Eigen::Vector3d getEigenNormal(size_t index) const
bool hasNormals() const override
Returns whether normals are enabled or not.
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
bool hasColors() const override
Returns whether colors are enabled or not.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
void setPointNormal(size_t pointIndex, const CCVector3 &N)
Sets a particular point normal (shortcut)
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
Eigen::Vector3d getEigenColor(size_t index) const
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
SimplificationContraction
Indicates the method that is used for mesh simplification if multiple vertices are combined to a sing...
unsigned size() const override
Eigen::Vector3d getEigenPoint(size_t index) const
void setPoint(size_t index, const CCVector3 &P)
const CCVector3 * getPoint(unsigned index) const override
static std::unordered_map< Edge< T >, std::vector< size_t >, utility::hash_tuple< Edge< T > > > GetEdgeToTrianglesMap(const core::Tensor &tris_cpu)
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, 2, 1 > Vector2i
Triangle described by the indexes of its 3 vertices.