ViennaLS
Loading...
Searching...
No Matches
lsCompareChamfer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <lsDomain.hpp>
4#include <lsExpand.hpp>
5#include <lsMesh.hpp>
7#include <lsToSurfaceMesh.hpp>
8
9#include <vcKDTree.hpp>
10
11#include <cmath>
12#include <limits>
13
14namespace viennals {
15
16using namespace viennacore;
17
38
39template <class T, int D = 2> class CompareChamfer {
40 SmartPointer<Domain<T, D>> levelSetTarget = nullptr;
41 SmartPointer<Domain<T, D>> levelSetSample = nullptr;
42
43 // Results
44 T forwardDistance = 0.0; // Target → Sample
45 T backwardDistance = 0.0; // Sample → Target
46 T chamferDistance = 0.0; // Average of forward and backward
47 T rmsChamferDistance = 0.0; // RMS of all nearest-neighbor distances
48 T maxDistance = 0.0; // Maximum nearest-neighbor distance
49 unsigned numTargetPoints = 0;
50 unsigned numSamplePoints = 0;
51
52 // Optional mesh output
53 SmartPointer<Mesh<T>> outputMeshTarget = nullptr;
54 SmartPointer<Mesh<T>> outputMeshSample = nullptr;
55
56 bool checkCompatibility() {
57 if (levelSetTarget == nullptr || levelSetSample == nullptr) {
58 VIENNACORE_LOG_WARNING("Missing level set in CompareChamfer.");
59 return false;
60 }
61
62 // Check if the grids are compatible
63 const auto &gridTarget = levelSetTarget->getGrid();
64 const auto &gridSample = levelSetSample->getGrid();
65
66 if (gridTarget.getGridDelta() != gridSample.getGridDelta()) {
67 VIENNACORE_LOG_WARNING("Grid delta mismatch in CompareChamfer. The grid "
68 "deltas of the two level sets must be equal.");
69 return false;
70 }
71
72 return true;
73 }
74
75public:
77 static_assert(
78 D == 2,
79 "CompareChamfer is currently only implemented for 2D level sets.");
80 }
81
82 CompareChamfer(SmartPointer<Domain<T, D>> passedLevelSetTarget,
83 SmartPointer<Domain<T, D>> passedLevelSetSample)
84 : levelSetTarget(passedLevelSetTarget),
85 levelSetSample(passedLevelSetSample) {
86 static_assert(
87 D == 2,
88 "CompareChamfer is currently only implemented for 2D level sets.");
89 }
90
92 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
93 levelSetTarget = passedLevelSet;
94 }
95
97 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
98 levelSetSample = passedLevelSet;
99 }
100
102 void setOutputMeshTarget(SmartPointer<Mesh<T>> passedMesh) {
103 outputMeshTarget = passedMesh;
104 }
105
107 void setOutputMeshSample(SmartPointer<Mesh<T>> passedMesh) {
108 outputMeshSample = passedMesh;
109 }
110
112 void apply() {
113 // Check compatibility
114 if (!checkCompatibility()) {
115 // Set NaN results to indicate failure
116 forwardDistance = std::numeric_limits<T>::quiet_NaN();
117 backwardDistance = std::numeric_limits<T>::quiet_NaN();
118 chamferDistance = std::numeric_limits<T>::quiet_NaN();
119 rmsChamferDistance = std::numeric_limits<T>::quiet_NaN();
120 maxDistance = std::numeric_limits<T>::quiet_NaN();
121 numTargetPoints = 0;
122 numSamplePoints = 0;
123 return;
124 }
125
126 // Ensure both level sets have sufficient width for surface extraction
127 constexpr int minimumWidth = 2;
128
129 auto workingTarget = levelSetTarget;
130 auto workingSample = levelSetSample;
131
132 if (levelSetTarget->getLevelSetWidth() < minimumWidth) {
133 workingTarget = SmartPointer<Domain<T, D>>::New(levelSetTarget);
134 Expand<T, D>(workingTarget, minimumWidth).apply();
135 VIENNACORE_LOG_INFO(
136 "CompareChamfer: Expanded target level set to width " +
137 std::to_string(minimumWidth) + " for surface extraction.");
138 }
139
140 if (levelSetSample->getLevelSetWidth() < minimumWidth) {
141 workingSample = SmartPointer<Domain<T, D>>::New(levelSetSample);
142 Expand<T, D>(workingSample, minimumWidth).apply();
143 VIENNACORE_LOG_INFO(
144 "CompareChamfer: Expanded sample level set to width " +
145 std::to_string(minimumWidth) + " for surface extraction.");
146 }
147
148 // Extract surface meshes
149 auto targetSurfaceMesh = SmartPointer<Mesh<T>>::New();
150 auto sampleSurfaceMesh = SmartPointer<Mesh<T>>::New();
151
152 ToSurfaceMesh<T, D>(workingTarget, targetSurfaceMesh).apply();
153 ToSurfaceMesh<T, D>(workingSample, sampleSurfaceMesh).apply();
154
155 // Get surface points
156 const auto &targetNodes = targetSurfaceMesh->getNodes();
157 const auto &sampleNodes = sampleSurfaceMesh->getNodes();
158
159 numTargetPoints = targetNodes.size();
160 numSamplePoints = sampleNodes.size();
161
162 if (numTargetPoints == 0 || numSamplePoints == 0) {
163 VIENNACORE_LOG_WARNING(
164 "CompareChamfer: One or both surfaces have no points. "
165 "Cannot compute Chamfer distance.");
166 forwardDistance = std::numeric_limits<T>::infinity();
167 backwardDistance = std::numeric_limits<T>::infinity();
168 chamferDistance = std::numeric_limits<T>::infinity();
169 rmsChamferDistance = std::numeric_limits<T>::infinity();
170 maxDistance = std::numeric_limits<T>::infinity();
171 return;
172 }
173
174 // Convert nodes to format suitable for KDTree (only using x, y
175 // coordinates)
176 std::vector<std::array<T, D>> targetPoints(numTargetPoints);
177 std::vector<std::array<T, D>> samplePoints(numSamplePoints);
178
179 for (unsigned i = 0; i < numTargetPoints; ++i) {
180 for (unsigned d = 0; d < D; ++d) {
181 targetPoints[i][d] = targetNodes[i][d];
182 }
183 }
184
185 for (unsigned i = 0; i < numSamplePoints; ++i) {
186 for (unsigned d = 0; d < D; ++d) {
187 samplePoints[i][d] = sampleNodes[i][d];
188 }
189 }
190
191 // Build KD-trees
192 KDTree<T, std::array<T, D>> targetTree(targetPoints);
193 targetTree.build();
194
195 KDTree<T, std::array<T, D>> sampleTree(samplePoints);
196 sampleTree.build();
197
198 // Compute forward distance (target → sample)
199 T sumForward = 0.0;
200 T sumSquaredForward = 0.0;
201 T maxForward = 0.0;
202 std::vector<T> targetDistances;
203 if (outputMeshTarget != nullptr) {
204 targetDistances.reserve(numTargetPoints);
205 }
206
207 for (unsigned i = 0; i < numTargetPoints; ++i) {
208 auto nearest = sampleTree.findNearest(targetPoints[i]);
209 if (nearest.has_value()) {
210 T dist = nearest.value().second;
211 sumForward += dist;
212 sumSquaredForward += dist * dist;
213 maxForward = std::max(maxForward, dist);
214 if (outputMeshTarget != nullptr) {
215 targetDistances.push_back(dist);
216 }
217 }
218 }
219
220 forwardDistance = sumForward / numTargetPoints;
221
222 // Compute backward distance (sample → target)
223 T sumBackward = 0.0;
224 T sumSquaredBackward = 0.0;
225 T maxBackward = 0.0;
226 std::vector<T> sampleDistances;
227 if (outputMeshSample != nullptr) {
228 sampleDistances.reserve(numSamplePoints);
229 }
230
231 for (unsigned i = 0; i < numSamplePoints; ++i) {
232 auto nearest = targetTree.findNearest(samplePoints[i]);
233 if (nearest.has_value()) {
234 T dist = nearest.value().second;
235 sumBackward += dist;
236 sumSquaredBackward += dist * dist;
237 maxBackward = std::max(maxBackward, dist);
238 if (outputMeshSample != nullptr) {
239 sampleDistances.push_back(dist);
240 }
241 }
242 }
243
244 backwardDistance = sumBackward / numSamplePoints;
245
246 // Compute overall metrics
247 chamferDistance =
248 (sumForward + sumBackward) / (numTargetPoints + numSamplePoints);
249 rmsChamferDistance = std::sqrt((sumSquaredForward + sumSquaredBackward) /
250 (numTargetPoints + numSamplePoints));
251 maxDistance = std::max(maxForward, maxBackward);
252
253 // Generate output meshes if requested
254 if (outputMeshTarget != nullptr && !targetDistances.empty()) {
255 outputMeshTarget->clear();
256 outputMeshTarget->nodes = targetNodes;
257
258 // Create vertices for visualization
259 outputMeshTarget->vertices.reserve(numTargetPoints);
260 for (unsigned i = 0; i < numTargetPoints; ++i) {
261 outputMeshTarget->vertices.push_back({i});
262 }
263
264 // Add distance data
265 outputMeshTarget->pointData.insertNextScalarData(
266 std::move(targetDistances), "DistanceToSample");
267
268 // Set mesh extent
269 for (unsigned d = 0; d < D; ++d) {
270 outputMeshTarget->minimumExtent[d] = std::numeric_limits<T>::max();
271 outputMeshTarget->maximumExtent[d] = std::numeric_limits<T>::lowest();
272 for (const auto &node : targetNodes) {
273 outputMeshTarget->minimumExtent[d] =
274 std::min(outputMeshTarget->minimumExtent[d], node[d]);
275 outputMeshTarget->maximumExtent[d] =
276 std::max(outputMeshTarget->maximumExtent[d], node[d]);
277 }
278 }
279 }
280
281 if (outputMeshSample != nullptr && !sampleDistances.empty()) {
282 outputMeshSample->clear();
283 outputMeshSample->nodes = sampleNodes;
284
285 // Create vertices for visualization
286 outputMeshSample->vertices.reserve(numSamplePoints);
287 for (unsigned i = 0; i < numSamplePoints; ++i) {
288 outputMeshSample->vertices.push_back({i});
289 }
290
291 // Add distance data
292 outputMeshSample->pointData.insertNextScalarData(
293 std::move(sampleDistances), "DistanceToTarget");
294
295 // Set mesh extent
296 for (unsigned d = 0; d < D; ++d) {
297 outputMeshSample->minimumExtent[d] = std::numeric_limits<T>::max();
298 outputMeshSample->maximumExtent[d] = std::numeric_limits<T>::lowest();
299 for (const auto &node : sampleNodes) {
300 outputMeshSample->minimumExtent[d] =
301 std::min(outputMeshSample->minimumExtent[d], node[d]);
302 outputMeshSample->maximumExtent[d] =
303 std::max(outputMeshSample->maximumExtent[d], node[d]);
304 }
305 }
306 }
307 }
308
310 T getForwardDistance() const { return forwardDistance; }
311
313 T getBackwardDistance() const { return backwardDistance; }
314
316 T getChamferDistance() const { return chamferDistance; }
317
319 T getRMSChamferDistance() const { return rmsChamferDistance; }
320
322 T getMaxDistance() const { return maxDistance; }
323
325 unsigned getNumTargetPoints() const { return numTargetPoints; }
326
328 unsigned getNumSamplePoints() const { return numSamplePoints; }
329};
330
331// Add template specializations for this class
332PRECOMPILE_PRECISION_DIMENSION(CompareChamfer)
333
334} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:11
double T
Definition Epitaxy.cpp:12
unsigned getNumSamplePoints() const
Get the number of sample surface points.
Definition lsCompareChamfer.hpp:328
void apply()
Apply the Chamfer distance calculation.
Definition lsCompareChamfer.hpp:112
CompareChamfer(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedLevelSetSample)
Definition lsCompareChamfer.hpp:82
T getMaxDistance() const
Get the maximum nearest-neighbor distance.
Definition lsCompareChamfer.hpp:322
T getBackwardDistance() const
Get the backward distance (average distance from sample to target).
Definition lsCompareChamfer.hpp:313
CompareChamfer()
Definition lsCompareChamfer.hpp:76
void setOutputMeshSample(SmartPointer< Mesh< T > > passedMesh)
Set output mesh for sample surface points with distance data.
Definition lsCompareChamfer.hpp:107
T getRMSChamferDistance() const
Get the RMS Chamfer distance.
Definition lsCompareChamfer.hpp:319
T getChamferDistance() const
Get the Chamfer distance (average of forward and backward).
Definition lsCompareChamfer.hpp:316
T getForwardDistance() const
Get the forward distance (average distance from target to sample).
Definition lsCompareChamfer.hpp:310
void setOutputMeshTarget(SmartPointer< Mesh< T > > passedMesh)
Set output mesh for target surface points with distance data.
Definition lsCompareChamfer.hpp:102
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Set the target level set.
Definition lsCompareChamfer.hpp:92
unsigned getNumTargetPoints() const
Get the number of target surface points.
Definition lsCompareChamfer.hpp:325
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Set the sample level set.
Definition lsCompareChamfer.hpp:97
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27
Expands the levelSet to the specified number of layers. The largest value in the levelset is thus wid...
Definition lsExpand.hpp:17
void apply()
Apply the expansion to the specified width.
Definition lsExpand.hpp:44
This class holds an explicit mesh, which is always given in 3 dimensions. If it describes a 2D mesh,...
Definition lsMesh.hpp:22
Extract an explicit Mesh<> instance from an lsDomain. The interface is then described by explicit sur...
Definition lsToSurfaceMesh.hpp:21
void apply()
Definition lsToSurfaceMesh.hpp:45
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:37