12 #include <unordered_set>
38 #include <QApplication>
39 #include <QElapsedTimer>
40 #include <QMessageBox>
41 #include <QtConcurrentMap>
65 bool exportBlocksAsMeshes =
false;
66 bool exportLossGain =
false;
70 std::vector<std::vector<int>>
nbs;
75 int emptyVoxelCount = 0;
94 bool processFailed =
false;
103 if (progressCb->textCanBeEdited()) {
104 progressCb->setInfo(
"Initialization");
105 progressCb->setMethodTitle(
"VoxFall Detection");
107 progressCb->update(0);
114 for (
int index = 0; index < voxelCount; ++index) {
147 if (progressCb->textCanBeEdited()) {
149 snprintf(buffer, 64,
"Clustering empty space \n Voxels: %u",
151 progressCb->setInfo(buffer);
152 progressCb->setMethodTitle(
"VoxFall Detection");
154 progressCb->update(0);
161 #pragma omp parallel for schedule(static) num_threads(maxThreads)
163 for (
int index = 0; index < voxelCount; ++index) {
166 for (
auto const& n :
NN) {
171 #pragma omp critical(ClusterEmptySpace)
175 for (
int index = 0; index < voxelCount; ++index) {
192 std::unordered_set<unsigned int> nbs_next(
195 std::unordered_set<unsigned int> visited;
196 visited.insert(index);
209 while (!nbs_next.empty()) {
210 unsigned nb = *nbs_next.begin();
211 nbs_next.erase(nbs_next.begin());
244 if (visited.count(qnb) == 0) {
245 nbs_next.insert(qnb);
259 std::atomic<bool>
error(
false);
267 #pragma omp parallel for schedule(static) num_threads(maxThreads)
269 for (
int i = 0; i < clusterCount; i++) {
276 std::unordered_set<unsigned int> nbs_next(
279 while (!nbs_next.empty()) {
280 unsigned nb = *nbs_next.begin();
281 nbs_next.erase(nbs_next.begin());
304 if (voxel.
x > maxBound.
x)
306 if (voxel.
y > maxBound.
y)
308 if (voxel.
z > maxBound.
z)
311 if (voxel.
x < minBound.
x)
313 if (voxel.
y < minBound.
y)
315 if (voxel.
z < minBound.
z)
331 float ymin = minBound.
y;
332 float ymax = maxBound.
y;
334 CCVector3 center = minBound + extent / 2;
337 maxBound.
y = ymax + (ymax - ymin) / 2.0;
348 QString& errorMessage,
350 QWidget* parentWidget ,
352 errorMessage.clear();
358 if (!mesh1 || !mesh2) {
371 QString(
"[VoxFall] Will use %1 threads")
372 .arg(maxThreadCount == 0
373 ?
"the max number of"
374 : QString::number(maxThreadCount)),
381 QElapsedTimer initTimer;
385 mesh->
merge(mesh2,
false);
388 mesh->applyGLTransformation_recursive(&transform.matrix);
407 QString(
" [VoxFall grid] (voxel %1 m)")
417 errorMessage =
"Failed to initialize voxel grid!";
434 static_cast<ScalarType
>(-1.0))) {
435 errorMessage =
"Failed to allocate memory for cluster ID values!";
444 voxelGrid.innerCellCount(),
true,
NAN_VALUE)) {
445 errorMessage =
"Failed to allocate memory for change type values!";
454 errorMessage =
"Failed to allocate memory for volume values!";
463 "Failed to allocate memory for volume uncertainty values!";
469 errorMessage =
"Failed to initialize output data!";
473 qint64 initTime_ms = initTimer.elapsed();
477 .arg(initTime_ms / 1000.0, 0,
'f', 3),
484 QElapsedTimer detectTimer;
490 errorMessage =
"Failed to compute grid occupancy!";
498 errorMessage =
"Failed to compute grid occupancy!";
505 errorMessage =
"Failed to compute grid occupancy!";
509 qint64 detectTime_ms = detectTimer.elapsed();
512 app->
dispToConsole(QString(
"[VoxFall] Block detection: %1 s")
513 .arg(detectTime_ms / 1000.0, 0,
'f', 3),
523 QElapsedTimer volumeTimer;
530 snprintf(buffer, 64,
"VoxFall clusters: %u \n Empty voxels: %u",
546 static_cast<ScalarType
>(label)) {
557 errorMessage =
"Failed to compute cluster volume!";
564 for (
unsigned n = 0; n < mesh1->
size(); n++) {
584 ScalarType changeType =
586 ScalarType uncertainty =
static_cast<ScalarType
>(
589 ScalarType volume =
static_cast<ScalarType
>(
604 volume / uncertainty / 100);
609 qint64 volumeTime_ms = volumeTimer.elapsed();
612 app->
dispToConsole(QString(
"[VoxFall] Volume computation: %1 s")
613 .arg(volumeTime_ms / 1000.0, 0,
'f', 3),
621 QElapsedTimer meshTimer;
638 QString(
" [VoxFall clusters] (voxel %1 m)")
642 std::vector<unsigned int> indices;
651 QString(
"Cluster#%1 - (v: %2 m3)")
656 for (
int i = 0; i < indices.size(); i++) {
674 qint64 meshTime_ms = meshTimer.elapsed();
677 app->
dispToConsole(QString(
"[VoxFall] Block as mesh export: %1 s")
678 .arg(meshTime_ms / 1000.0, 0,
'f', 3),
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
virtual void link()
Increase counter.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
Hierarchical CLOUDVIEWER Object.
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
bool merge(const ccMesh *mesh, bool createSubMesh)
Merges another mesh into this one.
ccMesh * cloneMesh(ccGenericPointCloud *vertices=nullptr, ccMaterialSet *clonedMaterials=nullptr, NormsIndexesTableType *clonedNormsTable=nullptr, TextureCoordsContainer *cloneTexCoords=nullptr)
Clones this entity.
void placeIteratorAtBeginning() override
Places the mesh iterator at the beginning.
virtual unsigned size() const override
Returns the number of triangles.
cloudViewer::GenericTriangle * _getNextTriangle() override
Returns the next triangle (relatively to the global iterator position)
virtual QString getName() const
Returns object name.
virtual void setEnabled(bool state)
Sets the "enabled" property.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
A scalar field associated to display-related parameters.
void computeMinAndMax() override
Determines the min and max values.
A generic triangle interface.
virtual const CCVector3 * _getA() const =0
Returns the first vertex (A)
virtual const CCVector3 * _getC() const =0
Returns the third vertex (C)
virtual const CCVector3 * _getB() const =0
Returns the second vertex (B)
Simple 3D grid structure.
bool oneStep()
Increments total progress value of a single unit.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPoint(unsigned index) const override
ScalarType & getValue(std::size_t index)
void setValue(std::size_t index, ScalarType value)
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
Main application interface (for plugins)
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
Graphical progress indicator (thread-safe)
virtual void update(float percent) override
Notifies the algorithm progress.
virtual void start() override
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
VOXFALL plugin's main dialog.
ccMesh * getMesh1() const
Returns mesh #1.
bool getExportMeshesActivation() const
Returns whether the blocks will be exported as meshes.
bool getLossGainActivation() const
Labels the blocks as loss or gain clusters.
double getVoxelSize() const
Returns voxel size.
ccMesh * getMesh2() const
Returns mesh #2.
int getMaxThreadCount() const
Returns the max number of threads to use.
double getAzimuth() const
Returns slope azimuth.
static bool Compute(const qVoxFallDialog &dlg, QString &errorMessage, bool allowDialogs, QWidget *parentWidget=nullptr, ecvMainAppInterface *app=nullptr)
static void error(char *msg)
Generic file read and write utility for python interface.
void GetVoxelOccupancy(const Tuple3i &cellPos, unsigned n)
static const char CLUSTER_SF_NAME[]
void GetVoxelOccupancyBefore(const Tuple3i &cellPos, unsigned n)
bool ComputeClusterVolume(int maxThreads, int clusterCount, ccHObject *clusterGroup=nullptr)
bool InitializeOutputCloud(int voxelCount, GenericProgressCallback *progressCb=nullptr)
bool ClusterEmptySpace(int maxThreads, int voxelCount, GenericProgressCallback *progressCb=nullptr)
static const char CHANGE_TYPE_SF_NAME[]
static const char VOLUME_SF_NAME[]
static const char OCCUPANCY_SF_NAME[]
Default name for VoxFall scalar fields.
static const char UNCERTAINTY_SF_NAME[]
static VoxFallParams s_VoxFallParams
ccScalarField * uncertaintySF
std::vector< float > volumes
std::vector< bool > isEmptyBefore
std::vector< bool > nonEmptyVoxelsVisited
ccScalarField * clusterSF
std::vector< std::vector< int > > nbs
std::vector< bool > isEmpty
ccScalarField * changeTypeSF
cloudViewer::NormalizedProgress * nProgress
std::vector< int > clusters
int clusterOutterVoxelCount
bool exportBlocksAsMeshes
std::vector< unsigned int > clusterIndices