CPPxDIC
C++ Digital Image Correlation library for fingertip 3D reconstruction
Loading...
Searching...
No Matches
delaunay_triangulation.cpp
Go to the documentation of this file.
1
6#include "logging.h"
7#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
8#include <CGAL/Delaunay_triangulation_2.h>
9#include <CGAL/Triangulation_vertex_base_with_info_2.h>
10#include <iostream>
11#include <map>
12#include <cmath>
13
14namespace cppxdic {
15
16// CGAL type definitions
17typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
18typedef CGAL::Triangulation_vertex_base_with_info_2<size_t, K> Vb;
19typedef CGAL::Triangulation_data_structure_2<Vb> Tds;
20typedef CGAL::Delaunay_triangulation_2<K, Tds> Delaunay;
21typedef K::Point_2 Point_2;
22
23std::vector<int> DelaunayTriangulation::compute(const std::vector<cv::Point2f>& points) {
24 std::vector<int> faces;
25
26 if (points.size() < 3) {
27 LOG_ERROR << "Need at least 3 points for triangulation";
28 return faces;
29 }
30
31 // Create Delaunay triangulation with vertex info (for indexing)
32 Delaunay dt;
33
34 // Insert points with their original indices
35 std::vector<std::pair<Point_2, size_t>> cgal_points;
36 for (size_t i = 0; i < points.size(); ++i) {
37 cgal_points.push_back(std::make_pair(
38 Point_2(points[i].x, points[i].y), i
39 ));
40 }
41
42 dt.insert(cgal_points.begin(), cgal_points.end());
43
44 // Extract triangles (faces)
45 for (auto fit = dt.finite_faces_begin(); fit != dt.finite_faces_end(); ++fit) {
46 // Get vertex indices (stored as info)
47 size_t v0 = fit->vertex(0)->info();
48 size_t v1 = fit->vertex(1)->info();
49 size_t v2 = fit->vertex(2)->info();
50
51 // MATLAB uses 1-based indexing, but we'll use 0-based in C++
52 // and convert when writing to .mat files
53 faces.push_back(v0);
54 faces.push_back(v1);
55 faces.push_back(v2);
56 }
57
58 LOG_INFO << "Delaunay triangulation: " << points.size() << " points -> "
59 << (faces.size() / 3) << " triangles";
60
61 return faces;
62}
63
65 const std::vector<int>& faces,
66 const std::vector<cv::Point2f>& vertices,
67 double max_edge_threshold) {
68
69 std::vector<int> filtered_faces;
70
71 if (faces.size() % 3 != 0) {
72 LOG_ERROR << "Faces array size must be multiple of 3";
73 return filtered_faces;
74 }
75
76 size_t n_triangles = faces.size() / 3;
77
78 for (size_t i = 0; i < n_triangles; ++i) {
79 size_t idx = i * 3;
80 int v0 = faces[idx];
81 int v1 = faces[idx + 1];
82 int v2 = faces[idx + 2];
83
84 // Check bounds (explicit negative check before implicit unsigned conversion)
85 if (v0 < 0 || v1 < 0 || v2 < 0 ||
86 static_cast<size_t>(v0) >= vertices.size() || static_cast<size_t>(v1) >= vertices.size() || static_cast<size_t>(v2) >= vertices.size()) {
87 if (i < 5) {
88 LOG_WARN << "Invalid vertex index in triangle " << i
89 << " (v0=" << v0 << " v1=" << v1 << " v2=" << v2
90 << ", nVerts=" << vertices.size() << ")";
91 }
92 continue;
93 }
94
95 // Compute edge lengths
96 cv::Point2f p0 = vertices[v0];
97 cv::Point2f p1 = vertices[v1];
98 cv::Point2f p2 = vertices[v2];
99
100 double e01 = std::sqrt((p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p0.y) * (p1.y - p0.y));
101 double e12 = std::sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
102 double e20 = std::sqrt((p0.x - p2.x) * (p0.x - p2.x) + (p0.y - p2.y) * (p0.y - p2.y));
103
104 double max_edge = std::max({e01, e12, e20});
105
106 // Keep triangle if max edge is below threshold
107 if (max_edge <= max_edge_threshold) {
108 filtered_faces.push_back(v0);
109 filtered_faces.push_back(v1);
110 filtered_faces.push_back(v2);
111 }
112 }
113
114 size_t removed = n_triangles - (filtered_faces.size() / 3);
115 if (removed > 0) {
116 LOG_INFO << "Filtered " << removed << " irregular triangles (edge > "
117 << max_edge_threshold << ")";
118 }
119
120 return filtered_faces;
121}
122
124 const std::vector<int>& faces,
125 const std::vector<cv::Point2f>& vertices) {
126
127 std::vector<double> edge_lengths;
128
129 if (faces.size() % 3 != 0) {
130 LOG_ERROR << "Faces array size must be multiple of 3";
131 return edge_lengths;
132 }
133
134 size_t n_triangles = faces.size() / 3;
135 edge_lengths.reserve(n_triangles * 3);
136
137 for (size_t i = 0; i < n_triangles; ++i) {
138 size_t idx = i * 3;
139 int v0 = faces[idx];
140 int v1 = faces[idx + 1];
141 int v2 = faces[idx + 2];
142
143 if (v0 >= vertices.size() || v1 >= vertices.size() || v2 >= vertices.size()) {
144 // Invalid triangle, add NaN
145 edge_lengths.push_back(std::nan(""));
146 edge_lengths.push_back(std::nan(""));
147 edge_lengths.push_back(std::nan(""));
148 continue;
149 }
150
151 cv::Point2f p0 = vertices[v0];
152 cv::Point2f p1 = vertices[v1];
153 cv::Point2f p2 = vertices[v2];
154
155 double e01 = std::sqrt((p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p0.y) * (p1.y - p0.y));
156 double e12 = std::sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
157 double e20 = std::sqrt((p0.x - p2.x) * (p0.x - p2.x) + (p0.y - p2.y) * (p0.y - p2.y));
158
159 edge_lengths.push_back(e01);
160 edge_lengths.push_back(e12);
161 edge_lengths.push_back(e20);
162 }
163
164 return edge_lengths;
165}
166
167std::vector<int> DelaunayTriangulation::flipOrientation(const std::vector<int>& faces) {
168 std::vector<int> flipped;
169
170 if (faces.size() % 3 != 0) {
171 LOG_ERROR << "Faces array size must be multiple of 3";
172 return flipped;
173 }
174
175 flipped.reserve(faces.size());
176
177 // MATLAB: F(:, [1 3 2]) - swap 2nd and 3rd vertices
178 for (size_t i = 0; i < faces.size(); i += 3) {
179 flipped.push_back(faces[i]); // v0
180 flipped.push_back(faces[i + 2]); // v2 (swapped)
181 flipped.push_back(faces[i + 1]); // v1 (swapped)
182 }
183
184 return flipped;
185}
186
188 const std::vector<int>& faces,
189 const std::vector<cv::Point2f>& vertices) {
190
191 std::vector<cv::Point2f> centroids;
192
193 if (faces.size() % 3 != 0) {
194 LOG_ERROR << "Faces array size must be multiple of 3";
195 return centroids;
196 }
197
198 size_t n_triangles = faces.size() / 3;
199 centroids.reserve(n_triangles);
200
201 for (size_t i = 0; i < n_triangles; ++i) {
202 size_t idx = i * 3;
203 int v0 = faces[idx];
204 int v1 = faces[idx + 1];
205 int v2 = faces[idx + 2];
206
207 if (v0 >= vertices.size() || v1 >= vertices.size() || v2 >= vertices.size()) {
208 // Invalid triangle
209 centroids.push_back(cv::Point2f(std::nan(""), std::nan("")));
210 continue;
211 }
212
213 cv::Point2f p0 = vertices[v0];
214 cv::Point2f p1 = vertices[v1];
215 cv::Point2f p2 = vertices[v2];
216
217 float cx = (p0.x + p1.x + p2.x) / 3.0f;
218 float cy = (p0.y + p1.y + p2.y) / 3.0f;
219
220 centroids.push_back(cv::Point2f(cx, cy));
221 }
222
223 return centroids;
224}
225
227 const std::vector<int>& faces,
228 const std::vector<cv::Point2f>& vertices) {
229
230 std::vector<double> areas;
231
232 if (faces.size() % 3 != 0) {
233 LOG_ERROR << "Faces array size must be multiple of 3";
234 return areas;
235 }
236
237 size_t n_triangles = faces.size() / 3;
238 areas.reserve(n_triangles);
239
240 for (size_t i = 0; i < n_triangles; ++i) {
241 size_t idx = i * 3;
242 int v0 = faces[idx];
243 int v1 = faces[idx + 1];
244 int v2 = faces[idx + 2];
245
246 if (v0 >= vertices.size() || v1 >= vertices.size() || v2 >= vertices.size()) {
247 // Invalid triangle
248 areas.push_back(std::nan(""));
249 continue;
250 }
251
252 cv::Point2f p0 = vertices[v0];
253 cv::Point2f p1 = vertices[v1];
254 cv::Point2f p2 = vertices[v2];
255
256 // Triangle area using cross product: |AB × AC| / 2
257 double area = 0.5 * std::abs(
258 (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y)
259 );
260
261 areas.push_back(area);
262 }
263
264 return areas;
265}
266
267} // namespace cppxdic
static std::vector< int > flipOrientation(const std::vector< int > &faces)
Flip triangle orientation Matches MATLAB: F = F(:, [1 3 2])
static std::vector< double > computeEdgeLengths(const std::vector< int > &faces, const std::vector< cv::Point2f > &vertices)
Compute edge lengths for all triangles Matches MATLAB: patchEdgeLengths(F, V)
static std::vector< int > filterByEdgeLength(const std::vector< int > &faces, const std::vector< cv::Point2f > &vertices, double max_edge_threshold)
Filter irregular triangles by edge length Matches MATLAB: F(EdgeLengthsMax > threshold,...
static std::vector< cv::Point2f > computeCentroids(const std::vector< int > &faces, const std::vector< cv::Point2f > &vertices)
Compute face centroids.
static std::vector< double > computeAreas(const std::vector< int > &faces, const std::vector< cv::Point2f > &vertices)
Compute face areas.
static std::vector< int > compute(const std::vector< cv::Point2f > &points)
Compute Delaunay triangulation of 2D points Matches MATLAB: DT = delaunayTriangulation(P)
Lightweight, dependency-free logging facility for CPPxDIC.
#define LOG_ERROR
Definition logging.h:172
#define LOG_WARN
Definition logging.h:171
#define LOG_INFO
Definition logging.h:170
DataSerializer - Abstract base class for format-agnostic DIC data I/O.
CGAL::Delaunay_triangulation_2< K, Tds > Delaunay
CGAL::Triangulation_data_structure_2< Vb > Tds
CGAL::Exact_predicates_inexact_constructions_kernel K
CGAL::Triangulation_vertex_base_with_info_2< size_t, K > Vb