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
78 CompareChamfer(SmartPointer<Domain<T, D>> passedLevelSetTarget,
79 SmartPointer<Domain<T, D>> passedLevelSetSample)
80 : levelSetTarget(passedLevelSetTarget),
81 levelSetSample(passedLevelSetSample) {}
82
84 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
85 levelSetTarget = passedLevelSet;
86 }
87
89 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
90 levelSetSample = passedLevelSet;
91 }
92
94 void setOutputMeshTarget(SmartPointer<Mesh<T>> passedMesh) {
95 outputMeshTarget = passedMesh;
96 }
97
99 void setOutputMeshSample(SmartPointer<Mesh<T>> passedMesh) {
100 outputMeshSample = passedMesh;
101 }
102
104 void apply() {
105 // Check compatibility
106 if (!checkCompatibility()) {
107 // Set NaN results to indicate failure
108 forwardDistance = std::numeric_limits<T>::quiet_NaN();
109 backwardDistance = std::numeric_limits<T>::quiet_NaN();
110 chamferDistance = std::numeric_limits<T>::quiet_NaN();
111 rmsChamferDistance = std::numeric_limits<T>::quiet_NaN();
112 maxDistance = std::numeric_limits<T>::quiet_NaN();
113 numTargetPoints = 0;
114 numSamplePoints = 0;
115 return;
116 }
117
118 // Ensure both level sets have sufficient width for surface extraction
119 constexpr int minimumWidth = 2;
120
121 auto workingTarget = levelSetTarget;
122 auto workingSample = levelSetSample;
123
124 if (levelSetTarget->getLevelSetWidth() < minimumWidth) {
125 workingTarget = SmartPointer<Domain<T, D>>::New(levelSetTarget);
126 Expand<T, D>(workingTarget, minimumWidth).apply();
127 VIENNACORE_LOG_INFO(
128 "CompareChamfer: Expanded target level set to width " +
129 std::to_string(minimumWidth) + " for surface extraction.");
130 }
131
132 if (levelSetSample->getLevelSetWidth() < minimumWidth) {
133 workingSample = SmartPointer<Domain<T, D>>::New(levelSetSample);
134 Expand<T, D>(workingSample, minimumWidth).apply();
135 VIENNACORE_LOG_INFO(
136 "CompareChamfer: Expanded sample level set to width " +
137 std::to_string(minimumWidth) + " for surface extraction.");
138 }
139
140 // Extract surface meshes
141 auto targetSurfaceMesh = SmartPointer<Mesh<T>>::New();
142 auto sampleSurfaceMesh = SmartPointer<Mesh<T>>::New();
143
144 ToSurfaceMesh<T, D>(workingTarget, targetSurfaceMesh).apply();
145 ToSurfaceMesh<T, D>(workingSample, sampleSurfaceMesh).apply();
146
147 // Get surface points
148 const auto &targetNodes = targetSurfaceMesh->getNodes();
149 const auto &sampleNodes = sampleSurfaceMesh->getNodes();
150
151 numTargetPoints = targetNodes.size();
152 numSamplePoints = sampleNodes.size();
153
154 if (numTargetPoints == 0 || numSamplePoints == 0) {
155 VIENNACORE_LOG_WARNING(
156 "CompareChamfer: One or both surfaces have no points. "
157 "Cannot compute Chamfer distance.");
158 forwardDistance = std::numeric_limits<T>::infinity();
159 backwardDistance = std::numeric_limits<T>::infinity();
160 chamferDistance = std::numeric_limits<T>::infinity();
161 rmsChamferDistance = std::numeric_limits<T>::infinity();
162 maxDistance = std::numeric_limits<T>::infinity();
163 return;
164 }
165
166 // Convert nodes to format suitable for KDTree
167 std::vector<std::array<T, D>> targetPoints(numTargetPoints);
168 std::vector<std::array<T, D>> samplePoints(numSamplePoints);
169
170 for (unsigned i = 0; i < numTargetPoints; ++i) {
171 for (unsigned d = 0; d < D; ++d) {
172 targetPoints[i][d] = targetNodes[i][d];
173 }
174 }
175
176 for (unsigned i = 0; i < numSamplePoints; ++i) {
177 for (unsigned d = 0; d < D; ++d) {
178 samplePoints[i][d] = sampleNodes[i][d];
179 }
180 }
181
182 // Build KD-trees
183 KDTree<T, std::array<T, D>> targetTree(targetPoints);
184 targetTree.build();
185
186 KDTree<T, std::array<T, D>> sampleTree(samplePoints);
187 sampleTree.build();
188
189 // Compute forward distance (target → sample)
190 T sumForward = 0.0;
191 T sumSquaredForward = 0.0;
192 T maxForward = 0.0;
193 std::vector<T> targetDistances;
194 if (outputMeshTarget != nullptr) {
195 targetDistances.reserve(numTargetPoints);
196 }
197
198 for (unsigned i = 0; i < numTargetPoints; ++i) {
199 auto nearest = sampleTree.findNearest(targetPoints[i]);
200 if (nearest.has_value()) {
201 T dist = nearest.value().second;
202 sumForward += dist;
203 sumSquaredForward += dist * dist;
204 maxForward = std::max(maxForward, dist);
205 if (outputMeshTarget != nullptr) {
206 targetDistances.push_back(dist);
207 }
208 }
209 }
210
211 forwardDistance = sumForward / numTargetPoints;
212
213 // Compute backward distance (sample → target)
214 T sumBackward = 0.0;
215 T sumSquaredBackward = 0.0;
216 T maxBackward = 0.0;
217 std::vector<T> sampleDistances;
218 if (outputMeshSample != nullptr) {
219 sampleDistances.reserve(numSamplePoints);
220 }
221
222 for (unsigned i = 0; i < numSamplePoints; ++i) {
223 auto nearest = targetTree.findNearest(samplePoints[i]);
224 if (nearest.has_value()) {
225 T dist = nearest.value().second;
226 sumBackward += dist;
227 sumSquaredBackward += dist * dist;
228 maxBackward = std::max(maxBackward, dist);
229 if (outputMeshSample != nullptr) {
230 sampleDistances.push_back(dist);
231 }
232 }
233 }
234
235 backwardDistance = sumBackward / numSamplePoints;
236
237 // Compute overall metrics
238 chamferDistance =
239 (sumForward + sumBackward) / (numTargetPoints + numSamplePoints);
240 rmsChamferDistance = std::sqrt((sumSquaredForward + sumSquaredBackward) /
241 (numTargetPoints + numSamplePoints));
242 maxDistance = std::max(maxForward, maxBackward);
243
244 // Generate output meshes if requested
245 if (outputMeshTarget != nullptr && !targetDistances.empty()) {
246 outputMeshTarget->clear();
247 outputMeshTarget->nodes = targetNodes;
248
249 // Copy topology for visualization
250 if constexpr (D == 2) {
251 outputMeshTarget->lines = targetSurfaceMesh->lines;
252 } else {
253 outputMeshTarget->triangles = targetSurfaceMesh->triangles;
254 }
255
256 // Add distance data
257 outputMeshTarget->pointData.insertNextScalarData(
258 std::move(targetDistances), "DistanceToSample");
259
260 // Set mesh extent
261 for (unsigned d = 0; d < 3; ++d) {
262 outputMeshTarget->minimumExtent[d] =
263 (d < D) ? std::numeric_limits<T>::max() : 0.0;
264 outputMeshTarget->maximumExtent[d] =
265 (d < D) ? std::numeric_limits<T>::lowest() : 0.0;
266 }
267 for (unsigned d = 0; d < D; ++d) {
268 for (const auto &node : targetNodes) {
269 outputMeshTarget->minimumExtent[d] =
270 std::min(outputMeshTarget->minimumExtent[d], node[d]);
271 outputMeshTarget->maximumExtent[d] =
272 std::max(outputMeshTarget->maximumExtent[d], node[d]);
273 }
274 }
275 }
276
277 if (outputMeshSample != nullptr && !sampleDistances.empty()) {
278 outputMeshSample->clear();
279 outputMeshSample->nodes = sampleNodes;
280
281 // Copy topology for visualization
282 if constexpr (D == 2) {
283 outputMeshSample->lines = sampleSurfaceMesh->lines;
284 } else {
285 outputMeshSample->triangles = sampleSurfaceMesh->triangles;
286 }
287
288 // Add distance data
289 outputMeshSample->pointData.insertNextScalarData(
290 std::move(sampleDistances), "DistanceToTarget");
291
292 // Set mesh extent
293 for (unsigned d = 0; d < 3; ++d) {
294 outputMeshSample->minimumExtent[d] =
295 (d < D) ? std::numeric_limits<T>::max() : 0.0;
296 outputMeshSample->maximumExtent[d] =
297 (d < D) ? std::numeric_limits<T>::lowest() : 0.0;
298 }
299 for (unsigned d = 0; d < D; ++d) {
300 for (const auto &node : sampleNodes) {
301 outputMeshSample->minimumExtent[d] =
302 std::min(outputMeshSample->minimumExtent[d], node[d]);
303 outputMeshSample->maximumExtent[d] =
304 std::max(outputMeshSample->maximumExtent[d], node[d]);
305 }
306 }
307 }
308 }
309
311 T getForwardDistance() const { return forwardDistance; }
312
314 T getBackwardDistance() const { return backwardDistance; }
315
317 T getChamferDistance() const { return chamferDistance; }
318
320 T getRMSChamferDistance() const { return rmsChamferDistance; }
321
323 T getMaxDistance() const { return maxDistance; }
324
326 unsigned getNumTargetPoints() const { return numTargetPoints; }
327
329 unsigned getNumSamplePoints() const { return numSamplePoints; }
330};
331
332// Add template specializations for this class
333PRECOMPILE_PRECISION_DIMENSION(CompareChamfer)
334
335} // 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:329
void apply()
Apply the Chamfer distance calculation.
Definition lsCompareChamfer.hpp:104
CompareChamfer(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedLevelSetSample)
Definition lsCompareChamfer.hpp:78
T getMaxDistance() const
Get the maximum nearest-neighbor distance.
Definition lsCompareChamfer.hpp:323
T getBackwardDistance() const
Get the backward distance (average distance from sample to target).
Definition lsCompareChamfer.hpp:314
CompareChamfer()
Definition lsCompareChamfer.hpp:76
void setOutputMeshSample(SmartPointer< Mesh< T > > passedMesh)
Set output mesh for sample surface points with distance data.
Definition lsCompareChamfer.hpp:99
T getRMSChamferDistance() const
Get the RMS Chamfer distance.
Definition lsCompareChamfer.hpp:320
T getChamferDistance() const
Get the Chamfer distance (average of forward and backward).
Definition lsCompareChamfer.hpp:317
T getForwardDistance() const
Get the forward distance (average distance from target to sample).
Definition lsCompareChamfer.hpp:311
void setOutputMeshTarget(SmartPointer< Mesh< T > > passedMesh)
Set output mesh for target surface points with distance data.
Definition lsCompareChamfer.hpp:94
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Set the target level set.
Definition lsCompareChamfer.hpp:84
unsigned getNumTargetPoints() const
Get the number of target surface points.
Definition lsCompareChamfer.hpp:326
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Set the sample level set.
Definition lsCompareChamfer.hpp:89
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
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:41