114 if (!checkCompatibility()) {
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();
127 constexpr int minimumWidth = 2;
129 auto workingTarget = levelSetTarget;
130 auto workingSample = levelSetSample;
132 if (levelSetTarget->getLevelSetWidth() < minimumWidth) {
133 workingTarget = SmartPointer<Domain<T, D>>::New(levelSetTarget);
136 "CompareChamfer: Expanded target level set to width " +
137 std::to_string(minimumWidth) +
" for surface extraction.");
140 if (levelSetSample->getLevelSetWidth() < minimumWidth) {
141 workingSample = SmartPointer<Domain<T, D>>::New(levelSetSample);
144 "CompareChamfer: Expanded sample level set to width " +
145 std::to_string(minimumWidth) +
" for surface extraction.");
149 auto targetSurfaceMesh = SmartPointer<Mesh<T>>::New();
150 auto sampleSurfaceMesh = SmartPointer<Mesh<T>>::New();
156 const auto &targetNodes = targetSurfaceMesh->getNodes();
157 const auto &sampleNodes = sampleSurfaceMesh->getNodes();
159 numTargetPoints = targetNodes.size();
160 numSamplePoints = sampleNodes.size();
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();
176 std::vector<std::array<T, D>> targetPoints(numTargetPoints);
177 std::vector<std::array<T, D>> samplePoints(numSamplePoints);
179 for (
unsigned i = 0; i < numTargetPoints; ++i) {
180 for (
unsigned d = 0; d <
D; ++d) {
181 targetPoints[i][d] = targetNodes[i][d];
185 for (
unsigned i = 0; i < numSamplePoints; ++i) {
186 for (
unsigned d = 0; d <
D; ++d) {
187 samplePoints[i][d] = sampleNodes[i][d];
192 KDTree<T, std::array<T, D>> targetTree(targetPoints);
195 KDTree<T, std::array<T, D>> sampleTree(samplePoints);
200 T sumSquaredForward = 0.0;
202 std::vector<T> targetDistances;
203 if (outputMeshTarget !=
nullptr) {
204 targetDistances.reserve(numTargetPoints);
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;
212 sumSquaredForward += dist * dist;
213 maxForward = std::max(maxForward, dist);
214 if (outputMeshTarget !=
nullptr) {
215 targetDistances.push_back(dist);
220 forwardDistance = sumForward / numTargetPoints;
224 T sumSquaredBackward = 0.0;
226 std::vector<T> sampleDistances;
227 if (outputMeshSample !=
nullptr) {
228 sampleDistances.reserve(numSamplePoints);
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;
236 sumSquaredBackward += dist * dist;
237 maxBackward = std::max(maxBackward, dist);
238 if (outputMeshSample !=
nullptr) {
239 sampleDistances.push_back(dist);
244 backwardDistance = sumBackward / numSamplePoints;
248 (sumForward + sumBackward) / (numTargetPoints + numSamplePoints);
249 rmsChamferDistance = std::sqrt((sumSquaredForward + sumSquaredBackward) /
250 (numTargetPoints + numSamplePoints));
251 maxDistance = std::max(maxForward, maxBackward);
254 if (outputMeshTarget !=
nullptr && !targetDistances.empty()) {
255 outputMeshTarget->clear();
256 outputMeshTarget->nodes = targetNodes;
259 outputMeshTarget->vertices.reserve(numTargetPoints);
260 for (
unsigned i = 0; i < numTargetPoints; ++i) {
261 outputMeshTarget->vertices.push_back({i});
265 outputMeshTarget->pointData.insertNextScalarData(
266 std::move(targetDistances),
"DistanceToSample");
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]);
281 if (outputMeshSample !=
nullptr && !sampleDistances.empty()) {
282 outputMeshSample->clear();
283 outputMeshSample->nodes = sampleNodes;
286 outputMeshSample->vertices.reserve(numSamplePoints);
287 for (
unsigned i = 0; i < numSamplePoints; ++i) {
288 outputMeshSample->vertices.push_back({i});
292 outputMeshSample->pointData.insertNextScalarData(
293 std::move(sampleDistances),
"DistanceToTarget");
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]);