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;
343 if (error)
return !error;
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.
ccMesh * cloneMesh(ccGenericPointCloud *vertices=nullptr, ccMaterialSet *clonedMaterials=nullptr, NormsIndexesTableType *clonedNormsTable=nullptr, TextureCoordsContainer *cloneTexCoords=nullptr)
Clones this entity.
bool merge(const ccMesh *mesh, bool createSubMesh)
Merges another mesh into this one.
cloudViewer::GenericTriangle * _getNextTriangle() override
Returns the next triangle (relatively to the global iterator position)
virtual unsigned size() const override
Returns the number of triangles.
void placeIteratorAtBeginning() override
Places the mesh iterator at the beginning.
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 setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void start() override
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)
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