initial segmentation
This commit is contained in:
@@ -9,8 +9,10 @@ Granulometry from 3D Point clouds
|
|||||||
|
|
||||||
**G3Point** is a tool which aims at automatically measuring the size, shape, and orientation of a large
|
**G3Point** is a tool which aims at automatically measuring the size, shape, and orientation of a large
|
||||||
number of individual grains as detected from any type of 3D point clouds describing the topography of surfaces covered by sediments.
|
number of individual grains as detected from any type of 3D point clouds describing the topography of surfaces covered by sediments.
|
||||||
|
|
||||||
The tool has been developped initially in *Matlab* https://github.com/philippesteer/G3Point
|
The tool has been developped initially in *Matlab* https://github.com/philippesteer/G3Point
|
||||||
This repository aims at making a plugin for CloudCompare from the original Matlab tool, also including later developments of the tool, either in *Matlab* (https://github.com/philippesteer/G3Point_dev) or in *Python* (https://github.com/p-leroy/g3point_python).
|
This repository aims at making a plugin for CloudCompare from the original Matlab tool, also including later developments of the tool, either in *Matlab* (https://github.com/philippesteer/G3Point_dev) or in *Python* (https://github.com/p-leroy/g3point_python).
|
||||||
|
|
||||||
This algorithm relies on 3 main phases:
|
This algorithm relies on 3 main phases:
|
||||||
1. Grain **segmentation** using a waterhsed algorithm
|
1. Grain **segmentation** using a waterhsed algorithm
|
||||||
2. Grain **merging and cleaning**
|
2. Grain **merging and cleaning**
|
||||||
|
|||||||
+5
-2
@@ -2,14 +2,17 @@
|
|||||||
|
|
||||||
#include "ccPointCloud.h"
|
#include "ccPointCloud.h"
|
||||||
|
|
||||||
|
#include "Eigen/Dense"
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class ccMainAppInterface;
|
class ccMainAppInterface;
|
||||||
|
|
||||||
namespace G3Point
|
namespace G3Point
|
||||||
{
|
{
|
||||||
int getBestOctreeLevel();
|
void add_to_stack(int index, const Eigen::ArrayXi& n_donors, const Eigen::ArrayXXi& donors, std::vector<int>& stack);
|
||||||
void get_neighbors(unsigned index);
|
int segment_labels(bool useParallelStrategy=true);
|
||||||
|
void get_neighbors_distances_slopes(unsigned index);
|
||||||
bool query_neighbors(ccPointCloud* cloud, ccMainAppInterface* appInterface, bool useParallelStrategy=true);
|
bool query_neighbors(ccPointCloud* cloud, ccMainAppInterface* appInterface, bool useParallelStrategy=true);
|
||||||
void performActionA( ccMainAppInterface *appInterface );
|
void performActionA( ccMainAppInterface *appInterface );
|
||||||
}
|
}
|
||||||
|
|||||||
+122
-22
@@ -6,37 +6,138 @@
|
|||||||
#include "ccOctree.h"
|
#include "ccOctree.h"
|
||||||
#include "ccProgressDialog.h"
|
#include "ccProgressDialog.h"
|
||||||
#include "ccQtHelpers.h"
|
#include "ccQtHelpers.h"
|
||||||
|
#include "CCGeom.h"
|
||||||
|
|
||||||
#include "QMainWindow"
|
#include "QMainWindow"
|
||||||
#include "QCoreApplication"
|
#include "QCoreApplication"
|
||||||
#include "QThreadPool"
|
#include "QThreadPool"
|
||||||
#include "QtConcurrent"
|
#include "QtConcurrent"
|
||||||
|
|
||||||
#include "Eigen/Dense"
|
|
||||||
|
|
||||||
#include "algorithm"
|
#include "algorithm"
|
||||||
|
#include "iostream"
|
||||||
|
|
||||||
namespace G3Point
|
namespace G3Point
|
||||||
{
|
{
|
||||||
|
|
||||||
Eigen::MatrixXi neighbors_indexes;
|
Eigen::ArrayXXi neighbors_indexes;
|
||||||
|
Eigen::ArrayXXf neighbors_distances;
|
||||||
|
Eigen::ArrayXXf neighbors_slopes;
|
||||||
int kNN = 20;
|
int kNN = 20;
|
||||||
ccOctree::Shared octree;
|
ccOctree::Shared octree;
|
||||||
ccPointCloud* cloud;
|
ccPointCloud* cloud;
|
||||||
unsigned char bestOctreeLevel = 0;
|
unsigned char bestOctreeLevel = 0;
|
||||||
CCCoreLib::DgmOctree::NearestNeighboursSearchStruct nNSS;
|
CCCoreLib::DgmOctree::NearestNeighboursSearchStruct nNSS;
|
||||||
|
|
||||||
void get_neighbors(unsigned index)
|
void add_to_stack(int index, const Eigen::ArrayXi& n_donors, const Eigen::ArrayXXi& donors, std::vector<int>& stack)
|
||||||
|
{
|
||||||
|
stack.push_back(index);
|
||||||
|
|
||||||
|
for (int k = 0; k < n_donors(index); k++)
|
||||||
|
{
|
||||||
|
add_to_stack(donors(index, k), n_donors, donors, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int segment_labels(bool useParallelStrategy)
|
||||||
|
{
|
||||||
|
// for each point, find in the neighborhood the point with the minimum slope (the receiver)
|
||||||
|
Eigen::ArrayXf min_slopes(neighbors_slopes.rowwise().minCoeff());
|
||||||
|
Eigen::ArrayXi index_of_min_slope = Eigen::ArrayXi::Zero(cloud->size());
|
||||||
|
Eigen::ArrayXi receivers(cloud->size());
|
||||||
|
|
||||||
|
for (unsigned index = 0; index < cloud->size(); index++)
|
||||||
|
{
|
||||||
|
float min_slope = min_slopes(index);
|
||||||
|
for (int k = 0; k < kNN; k++)
|
||||||
|
{
|
||||||
|
if (neighbors_slopes(index, k) == min_slope)
|
||||||
|
{
|
||||||
|
index_of_min_slope(index) = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receivers(index) = neighbors_indexes(index, index_of_min_slope(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the minimum slope is positive, the receiver is a local maximum
|
||||||
|
int nb_maxima = (min_slopes > 0).count();
|
||||||
|
Eigen::ArrayXi localMaximumIndexes = Eigen::ArrayXi::Zero(nb_maxima);
|
||||||
|
int l = 0;
|
||||||
|
for (unsigned int k = 0; k < cloud->size(); k++)
|
||||||
|
{
|
||||||
|
if (min_slopes(k) > 0)
|
||||||
|
{
|
||||||
|
localMaximumIndexes(l) = k;
|
||||||
|
receivers(k) = k;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// identify the donors for each receiver
|
||||||
|
Eigen::ArrayXi nDonors = Eigen::ArrayXi::Zero(cloud->size());
|
||||||
|
Eigen::ArrayXXi donors = Eigen::ArrayXXi::Zero(cloud->size(), kNN);
|
||||||
|
|
||||||
|
for (unsigned int k = 0; k < cloud->size(); k++)
|
||||||
|
{
|
||||||
|
int receiver = receivers(k);
|
||||||
|
if (receiver != k) // this receiver is not a local maximum
|
||||||
|
{
|
||||||
|
nDonors(receiver) = nDonors(receiver) + 1;
|
||||||
|
donors(receiver, nDonors(receiver) - 1) = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the stacks
|
||||||
|
Eigen::ArrayXi labels = Eigen::ArrayXi::Zero(cloud->size());
|
||||||
|
Eigen::ArrayXi labelsk = Eigen::ArrayXi::Zero(cloud->size());
|
||||||
|
Eigen::ArrayXi labelsnpoint = Eigen::ArrayXi::Zero(cloud->size());
|
||||||
|
std::vector<std::vector<int>> stacks;
|
||||||
|
|
||||||
|
for (int k = 0; k < localMaximumIndexes.size(); k++)
|
||||||
|
{
|
||||||
|
int localMaximumIndex = localMaximumIndexes(k);
|
||||||
|
std::vector<int> stack;
|
||||||
|
add_to_stack(localMaximumIndex, nDonors, donors, stack);
|
||||||
|
stacks.push_back(stack);
|
||||||
|
// labels
|
||||||
|
for (auto i : stack)
|
||||||
|
{
|
||||||
|
labels(i) = k;
|
||||||
|
labelsnpoint(i) = stack.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nLabels = localMaximumIndexes.size();
|
||||||
|
|
||||||
|
return nLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_neighbors_distances_slopes(unsigned index)
|
||||||
{
|
{
|
||||||
const CCVector3* P = cloud->getPoint(index);
|
const CCVector3* P = cloud->getPoint(index);
|
||||||
|
|
||||||
|
|
||||||
nNSS.level = bestOctreeLevel;
|
nNSS.level = bestOctreeLevel;
|
||||||
|
double maxSquareDist = 0;
|
||||||
|
int neighborhoodSize = 0;
|
||||||
|
CCCoreLib::ReferenceCloud Yk(cloud);
|
||||||
|
|
||||||
octree->findNearestNeighborsStartingFromCell();
|
// get the nearest neighbors
|
||||||
|
if (octree->findPointNeighbourhood(P, &Yk, static_cast<unsigned>(kNN + 1), bestOctreeLevel, maxSquareDist, 0, &neighborhoodSize) >= static_cast<unsigned>(kNN))
|
||||||
|
{
|
||||||
|
for (int k = 0; k < kNN; k++)
|
||||||
|
{
|
||||||
|
// store the index of the neighbor
|
||||||
|
neighbors_indexes(index, k) = Yk.getPointGlobalIndex(k + 1);
|
||||||
|
// compute the distance to the neighbor
|
||||||
|
const CCVector3* neighbor = Yk.getPoint(k + 1);
|
||||||
|
float distance = sqrt((*P - *neighbor).norm2());
|
||||||
|
neighbors_distances(index, k) = distance;
|
||||||
|
// compute the slope to the neighbor
|
||||||
|
neighbors_slopes(index, k) = (P->z - neighbor->z) / distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool query_neighbors(ccMainAppInterface* appInterface, bool useParallelStrategy)
|
bool query_neighbors(ccPointCloud* cloud, ccMainAppInterface* appInterface, bool useParallelStrategy)
|
||||||
{
|
{
|
||||||
QString errorStr;
|
QString errorStr;
|
||||||
|
|
||||||
@@ -67,6 +168,7 @@ bool query_neighbors(ccMainAppInterface* appInterface, bool useParallelStrategy)
|
|||||||
int maxThreadCount = 0;
|
int maxThreadCount = 0;
|
||||||
CCCoreLib::DgmOctree::NearestNeighboursSearchStruct nNSS;
|
CCCoreLib::DgmOctree::NearestNeighboursSearchStruct nNSS;
|
||||||
std::vector<unsigned> pointsIndexes;
|
std::vector<unsigned> pointsIndexes;
|
||||||
|
pointsIndexes.resize(nPoints);
|
||||||
|
|
||||||
if (useParallelStrategy)
|
if (useParallelStrategy)
|
||||||
{
|
{
|
||||||
@@ -79,14 +181,14 @@ bool query_neighbors(ccMainAppInterface* appInterface, bool useParallelStrategy)
|
|||||||
maxThreadCount = ccQtHelpers::GetMaxThreadCount();
|
maxThreadCount = ccQtHelpers::GetMaxThreadCount();
|
||||||
}
|
}
|
||||||
QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
|
QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
|
||||||
QtConcurrent::blockingMap(pointsIndexes, get_neighbors);
|
QtConcurrent::blockingMap(pointsIndexes, get_neighbors_distances_slopes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//manually call the static per-point method!
|
//manually call the static per-point method!
|
||||||
for (unsigned i = 0; i < nPoints; ++i)
|
for (unsigned i = 0; i < nPoints; ++i)
|
||||||
{
|
{
|
||||||
get_neighbors(i);
|
get_neighbors_distances_slopes(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,8 +205,6 @@ void performActionA( ccMainAppInterface *appInterface )
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** HERE STARTS THE ACTION ***/
|
|
||||||
|
|
||||||
//we need one point cloud
|
//we need one point cloud
|
||||||
if (!appInterface->haveOneSelection())
|
if (!appInterface->haveOneSelection())
|
||||||
{
|
{
|
||||||
@@ -124,21 +224,21 @@ void performActionA( ccMainAppInterface *appInterface )
|
|||||||
|
|
||||||
cloud = ccHObjectCaster::ToPointCloud(ent);
|
cloud = ccHObjectCaster::ToPointCloud(ent);
|
||||||
|
|
||||||
|
// initialize the matrix which will contain the results
|
||||||
|
neighbors_indexes.resize(cloud->size(), kNN);
|
||||||
|
neighbors_distances.resize(cloud->size(), kNN);
|
||||||
|
neighbors_slopes.resize(cloud->size(), kNN);
|
||||||
|
|
||||||
// Find neighbors of each point of the cloud
|
// Find neighbors of each point of the cloud
|
||||||
query_neighbors(cloud, appInterface);
|
query_neighbors(cloud, appInterface, true);
|
||||||
|
|
||||||
// Perform initial segmentation
|
// Perform initial segmentation
|
||||||
|
int nLabels = segment_labels();
|
||||||
|
|
||||||
// This is how you can output messages
|
appInterface->dispToConsole( "[G3Point] initial segmentation: " + QString::number(nLabels) + " labels", ccMainAppInterface::STD_CONSOLE_MESSAGE );
|
||||||
// Display a standard message in the console
|
|
||||||
appInterface->dispToConsole( "[ExamplePlugin] Hello world!", ccMainAppInterface::STD_CONSOLE_MESSAGE );
|
|
||||||
|
|
||||||
// Display a warning message in the console
|
neighbors_indexes.resize(0, 0);
|
||||||
appInterface->dispToConsole( "[ExamplePlugin] Warning: example plugin shouldn't be used as is", ccMainAppInterface::WRN_CONSOLE_MESSAGE );
|
neighbors_distances.resize(0, 0);
|
||||||
|
neighbors_slopes.resize(0, 0);
|
||||||
// Display an error message in the console AND pop-up an error box
|
|
||||||
appInterface->dispToConsole( "Example plugin shouldn't be used - it doesn't do anything!", ccMainAppInterface::ERR_CONSOLE_MESSAGE );
|
|
||||||
|
|
||||||
/*** HERE ENDS THE ACTION ***/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user