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