118 if (!checkCompatibility()) {
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();
131 constexpr int minimumWidth = 2;
133 auto workingTarget = levelSetTarget;
134 auto workingSample = levelSetSample;
136 if (levelSetTarget->getLevelSetWidth() < minimumWidth) {
137 workingTarget = SmartPointer<Domain<T, D>>::New(levelSetTarget);
139 Logger::getInstance()
140 .addInfo(
"CompareChamfer: Expanded target level set to width " +
141 std::to_string(minimumWidth) +
" for surface extraction.")
145 if (levelSetSample->getLevelSetWidth() < minimumWidth) {
146 workingSample = SmartPointer<Domain<T, D>>::New(levelSetSample);
148 Logger::getInstance()
149 .addInfo(
"CompareChamfer: Expanded sample level set to width " +
150 std::to_string(minimumWidth) +
" for surface extraction.")
155 auto targetSurfaceMesh = SmartPointer<Mesh<T>>::New();
156 auto sampleSurfaceMesh = SmartPointer<Mesh<T>>::New();
162 const auto &targetNodes = targetSurfaceMesh->getNodes();
163 const auto &sampleNodes = sampleSurfaceMesh->getNodes();
165 numTargetPoints = targetNodes.size();
166 numSamplePoints = sampleNodes.size();
168 if (numTargetPoints == 0 || numSamplePoints == 0) {
169 Logger::getInstance()
171 "CompareChamfer: One or both surfaces have no points. Cannot "
172 "compute Chamfer distance.")
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();
184 std::vector<std::array<T, D>> targetPoints(numTargetPoints);
185 std::vector<std::array<T, D>> samplePoints(numSamplePoints);
187 for (
unsigned i = 0; i < numTargetPoints; ++i) {
188 for (
unsigned d = 0; d <
D; ++d) {
189 targetPoints[i][d] = targetNodes[i][d];
193 for (
unsigned i = 0; i < numSamplePoints; ++i) {
194 for (
unsigned d = 0; d <
D; ++d) {
195 samplePoints[i][d] = sampleNodes[i][d];
200 KDTree<T, std::array<T, D>> targetTree(targetPoints);
203 KDTree<T, std::array<T, D>> sampleTree(samplePoints);
208 T sumSquaredForward = 0.0;
210 std::vector<T> targetDistances;
211 if (outputMeshTarget !=
nullptr) {
212 targetDistances.reserve(numTargetPoints);
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;
220 sumSquaredForward += dist * dist;
221 maxForward = std::max(maxForward, dist);
222 if (outputMeshTarget !=
nullptr) {
223 targetDistances.push_back(dist);
228 forwardDistance = sumForward / numTargetPoints;
232 T sumSquaredBackward = 0.0;
234 std::vector<T> sampleDistances;
235 if (outputMeshSample !=
nullptr) {
236 sampleDistances.reserve(numSamplePoints);
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;
244 sumSquaredBackward += dist * dist;
245 maxBackward = std::max(maxBackward, dist);
246 if (outputMeshSample !=
nullptr) {
247 sampleDistances.push_back(dist);
252 backwardDistance = sumBackward / numSamplePoints;
256 (sumForward + sumBackward) / (numTargetPoints + numSamplePoints);
257 rmsChamferDistance = std::sqrt((sumSquaredForward + sumSquaredBackward) /
258 (numTargetPoints + numSamplePoints));
259 maxDistance = std::max(maxForward, maxBackward);
262 if (outputMeshTarget !=
nullptr && !targetDistances.empty()) {
263 outputMeshTarget->clear();
264 outputMeshTarget->nodes = targetNodes;
267 outputMeshTarget->vertices.reserve(numTargetPoints);
268 for (
unsigned i = 0; i < numTargetPoints; ++i) {
269 outputMeshTarget->vertices.push_back({i});
273 outputMeshTarget->pointData.insertNextScalarData(
274 std::move(targetDistances),
"DistanceToSample");
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]);
289 if (outputMeshSample !=
nullptr && !sampleDistances.empty()) {
290 outputMeshSample->clear();
291 outputMeshSample->nodes = sampleNodes;
294 outputMeshSample->vertices.reserve(numSamplePoints);
295 for (
unsigned i = 0; i < numSamplePoints; ++i) {
296 outputMeshSample->vertices.push_back({i});
300 outputMeshSample->pointData.insertNextScalarData(
301 std::move(sampleDistances),
"DistanceToTarget");
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]);