From f067d8beacd7301c98b3e5e23bb7ccc6c47d49b7 Mon Sep 17 00:00:00 2001 From: Paul Leroy Date: Wed, 22 Oct 2025 15:34:01 +0200 Subject: [PATCH] multithreading is OK --- include/G3PointAction.h | 9 +-- src/G3PointAction.cpp | 143 +++++++++++++--------------------------- 2 files changed, 46 insertions(+), 106 deletions(-) diff --git a/include/G3PointAction.h b/include/G3PointAction.h index 87a1bb8..755c952 100644 --- a/include/G3PointAction.h +++ b/include/G3PointAction.h @@ -105,11 +105,8 @@ private: bool computeNormalsAndOrientThemWithCloudCompare(); void orientNormals(const Eigen::Vector3d &sensorCenter); bool computeNormalsWithOpen3D(); - static bool FindNearestNeighborsNanoFlann(ccPointCloud* cloud, unsigned globalIndex, int k, - CCCoreLib::ReferenceCloud *points, KDTree *kdTree); - static bool ComputeNormsAtLevel(const CCCoreLib::DgmOctree::octreeCell& cell, - void** additionalParameters, - CCCoreLib::NormalizedProgress* nProgress=nullptr); + bool findNearestNeighborsNanoFlann(const unsigned int globalIndex, CCCoreLib::ReferenceCloud *points, const KDTree *kdTree); + bool computeNormWithFlann(unsigned int index, NormsTableType* theNorms, const KDTree *kdTree); bool computeNormalsWithCloudCompare(); bool computeNormals(); bool queryNeighbors(ccPointCloud* cloud, ccMainAppInterface* appInterface, bool useParallelStrategy=true); @@ -160,7 +157,5 @@ private: GrainsAsEllipsoids* m_grainsAsEllipsoids; int m_currentNumberOfSteps; - - QSharedPointer m_kdTree; }; } diff --git a/src/G3PointAction.cpp b/src/G3PointAction.cpp index 7e77adb..2cc8175 100644 --- a/src/G3PointAction.cpp +++ b/src/G3PointAction.cpp @@ -1817,26 +1817,24 @@ bool G3PointAction::computeNormalsWithOpen3D() #endif } -bool G3PointAction::FindNearestNeighborsNanoFlann(ccPointCloud* cloud, - unsigned globalIndex, - int kNN, +bool G3PointAction::findNearestNeighborsNanoFlann(const unsigned globalIndex, CCCoreLib::ReferenceCloud* points, - KDTree* kdTree) + const KDTree* kdTree) { // Prepare query - const CCVector3* Q = cloud->getPoint(globalIndex); + const CCVector3* Q = m_cloud->getPoint(globalIndex); float query[3] = { Q->x, Q->y, Q->z }; - std::vector retIndexes(kNN); - std::vector outDistsSqr(kNN); + std::vector retIndexes(m_kNN); + std::vector outDistsSqr(m_kNN); // Perform search - nanoflann::KNNResultSet resultSet(kNN); + nanoflann::KNNResultSet resultSet(m_kNN); resultSet.init(&retIndexes[0], &outDistsSqr[0]); if(kdTree->findNeighbors(resultSet, &query[0])) { - points->resize(kNN); - for (int i = 0; i < kNN; ++i) + points->resize(m_kNN); + for (int i = 0; i < m_kNN; ++i) { points->setPointIndex(i, retIndexes[i]); } @@ -1848,59 +1846,25 @@ bool G3PointAction::FindNearestNeighborsNanoFlann(ccPointCloud* cloud, } } -bool G3PointAction::ComputeNormsAtLevel(const CCCoreLib::DgmOctree::octreeCell& cell, - void** additionalParameters, - CCCoreLib::NormalizedProgress* nProgress /*=nullptr*/) +bool G3PointAction::computeNormWithFlann(unsigned index, + NormsTableType* theNorms, + const G3PointAction::KDTree* kdTree) { - // additional parameters - NormsTableType* theNorms = static_cast(additionalParameters[0]); - int* kNN = static_cast(additionalParameters[1]); - KDTree* kdTree = static_cast(additionalParameters[2]); - ccPointCloud* cloud = static_cast(additionalParameters[3]); + CCVector3 N; - CCCoreLib::DgmOctree::NearestNeighboursSearchStruct nNSS; - nNSS.level = cell.level; - nNSS.minNumberOfNeighbors = *kNN; - cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos, true); - cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level, nNSS.cellCenter); - - // we already know which points are lying in the current cell - unsigned pointCount = cell.points->size(); - nNSS.pointsInNeighbourhood.resize(pointCount); - CCCoreLib::DgmOctree::NeighboursSet::iterator it = nNSS.pointsInNeighbourhood.begin(); + QScopedPointer points(new CCCoreLib::ReferenceCloud(m_cloud)); + if(findNearestNeighborsNanoFlann(index, points.data(), kdTree)) { - for (unsigned j = 0; j < pointCount; ++j, ++it) - { - it->point = cell.points->getPointPersistentPtr(j); - it->pointIndex = cell.points->getPointGlobalIndex(j); - } + CCCoreLib::Neighbourhood neighbourhood(points.data()); + N = *neighbourhood.getLSPlaneNormal(); } - nNSS.alreadyVisitedNeighbourhoodSize = 1; - - for (unsigned i = 0; i < pointCount; ++i) + else { - cell.points->getPoint(i, nNSS.queryPoint); - unsigned int globalIndex = cell.points->getPointGlobalIndex(i); - - CCVector3 N; - - QScopedPointer points(new CCCoreLib::ReferenceCloud(cloud)); - if(FindNearestNeighborsNanoFlann(cloud, globalIndex, *kNN, points.data(), kdTree)) - { - CCCoreLib::Neighbourhood neighbourhood(points.data()); - N = *neighbourhood.getLSPlaneNormal(); - } - else - { - return false; - } - - theNorms->setValue(globalIndex, N); - - if (nProgress && !nProgress->oneStep()) - return false; + return false; } + theNorms->setValue(index, N); + return true; } @@ -1910,71 +1874,51 @@ bool G3PointAction::computeNormalsWithCloudCompare() if (!m_cloud || m_cloud->size() == 0) { - std::cerr << "Invalid cloud.\n"; + ccLog::Error("Invalid cloud."); return false; } CloudAdaptor adaptor(m_cloud); - // Build KD-tree (parameter: number of leaf nodes to inspect per query) + // Build KD-tree (parameter: number of leaf nodes to inspect per query) size_t leaf_max_size = 10; nanoflann::KDTreeSingleIndexAdaptorFlags flags = nanoflann::KDTreeSingleIndexAdaptorFlags::None; unsigned int n_thread_build = 0; // 0 => nanoflann automatically determines the number of threads to use nanoflann::KDTreeSingleIndexAdaptorParams params(leaf_max_size, flags, n_thread_build); - m_kdTree.reset(new KDTree(3, adaptor, params)); + QSharedPointer m_kdTree(new KDTree(3, adaptor, params)); m_kdTree->buildIndex(); - ccOctree::Shared octree = m_cloud->getOctree(); - if (octree.isNull()) - { - octree.reset(new ccOctree(m_cloud)); - if (octree->build() <= 0) - { - octree.clear(); - return false; - } - } - unsigned char level = octree->findBestLevelForAGivenPopulationPerCell(m_kNN); - - // we instantiate 3D normal vectors - QScopedPointer theNorms(new NormsTableType); + // we instantiate 3D normal vectors + QSharedPointer theNorms(new NormsTableType); QScopedPointer normsIndexes(new NormsIndexesTableType); static const CCVector3 blankN(0, 0, 0); if (!theNorms->resizeSafe(pointCount, true, &blankN)) { normsIndexes->resize(0); - if (nullptr == octree) - { - octree.clear(); - } return false; } - // theNorms->fill(0); - void* additionalParameters[4] = {reinterpret_cast(theNorms.data()), - reinterpret_cast(&m_kNN), - reinterpret_cast(m_kdTree.data()), - reinterpret_cast(m_cloud)}; - - unsigned processedCells = 0; - QScopedPointer progressCb; - processedCells = octree->executeFunctionForAllCellsStartingAtLevel(level, - &(ComputeNormsAtLevel), - additionalParameters, - m_kNN / 2, - m_kNN * 3, - true, - progressCb.data(), - "Normals Computation [G3Point]"); - - // error or canceled by user? - if (processedCells == 0 || (progressCb && progressCb->isCancelRequested())) + ccLog::Print("[computeNormalsWithCloudCompare]"); +#ifdef QT_DEBUG + //manually call the static per-point method! + for (unsigned index = 0; index < pointCount; ++index) { - normsIndexes->resize(0); - return false; + computeNormWithFlann(index, theNorms.data(), m_kNN, m_kdTree.data(), m_cloud); } +#else + std::vector pointsIndexes; + pointsIndexes.resize(pointCount); + for (unsigned i = 0; i < pointCount; ++i) + { + pointsIndexes[i] = i; + } + int threadCount = std::max(1, ccQtHelpers::GetMaxThreadCount() - 2); + ccLog::Print("[computeNormalsWithCloudCompare] parallel strategy, thread count " + QString::number(threadCount)); + QThreadPool::globalInstance()->setMaxThreadCount(threadCount); + QtConcurrent::blockingMap(pointsIndexes, [=](int index){computeNormWithFlann(index, theNorms.data(), m_kdTree.data());}); +#endif if (!m_cloud->hasNormals()) { @@ -1997,6 +1941,7 @@ bool G3PointAction::computeNormalsWithCloudCompare() } // preferred orientation + ccLog::Print("[computeNormalsWithCloudCompare] orient normals, PLUS_Z "); ccNormalVectors::UpdateNormalOrientations(m_cloud, *m_cloud->normals(), ccNormalVectors::PLUS_Z); return true; @@ -2011,7 +1956,7 @@ bool G3PointAction::computeNormals() msgBox.setInformativeText("Recompute normals?"); msgBox.setText("There are existing normals, keep them or recompute."); QPushButton *keepButton = msgBox.addButton(tr("Keep"), QMessageBox::ActionRole); - QPushButton *recomputeButton = msgBox.addButton(tr("Recompute"), QMessageBox::AcceptRole); + msgBox.addButton(tr("Recompute"), QMessageBox::AcceptRole); QPushButton *cancelButton = msgBox.addButton(tr("Cancel"), QMessageBox::AcceptRole); msgBox.exec();