clean function complete but some work to do to handle the cases where several points can be local maxima (slope == 0), what arrives with laz format

This commit is contained in:
Paul Leroy
2024-03-01 16:48:09 +01:00
parent ed64b7d915
commit e8b3b37a82
2 changed files with 390 additions and 333 deletions
+5 -5
View File
@@ -32,9 +32,9 @@ public:
void segmentAndClusterAndClean();
void getBorders();
int cluster();
bool processNewStacks(std::vector<std::vector<int>>& stacks);
bool processNewStacks(std::vector<std::vector<int>>& stacks, int pointCount);
bool merge(XXb& condition);
bool keepLabels(Xb& condition);
bool keep(Xb& condition);
bool cleanLabels();
void clean();
@@ -43,7 +43,7 @@ private:
void add_to_stack(int index, const Eigen::ArrayXi& n_donors, const Eigen::ArrayXXi& donors, std::vector<int>& stack);
int segment_labels(bool useParallelStrategy=true);
double angle_rot_2_vec_mat(const Eigen::Vector3d &a, const Eigen::Vector3d &b);
Eigen::ArrayXXd computeMeanAngle();
Eigen::ArrayXXd computeMeanAngleBetweenNormalsAtBorders();
bool exportLocalMaximaAsCloud();
bool updateLocalMaximumIndexes();
bool updateLabelsAndColors();
@@ -72,8 +72,8 @@ private:
ccPointCloud* m_cloud;
ccMainAppInterface *m_app;
G3PointDialog* m_dlg;
Eigen::ArrayXXi m_neighbors_indexes;
Eigen::ArrayXXi m_neighborsIndexes;
Eigen::ArrayXXd m_neighbors_distances;
Eigen::ArrayXXd m_neighbors_slopes;
Eigen::ArrayXXd m_normals;
+385 -328
View File
@@ -215,7 +215,7 @@ int G3PointAction::segment_labels(bool useParallelStrategy)
std::cout << "[segment_labels] slope already seen, index " << index << ", k "<< k << std::endl;
}
}
receivers(index) = m_neighbors_indexes(index, index_of_min_slope(index));
receivers(index) = m_neighborsIndexes(index, index_of_min_slope(index));
}
// if the minimum slope is positive, the receiver is a local maximum
@@ -334,7 +334,7 @@ double G3PointAction::angle_rot_2_vec_mat(const Eigen::Vector3d& a, const Eigen:
return angle;
}
Eigen::ArrayXXd G3PointAction::computeMeanAngle()
Eigen::ArrayXXd G3PointAction::computeMeanAngleBetweenNormalsAtBorders()
{
// Find the indexborder nodes (no donor and many other labels in the neighbourhood)
Eigen::ArrayXXi duplicated_labels(m_cloud->size(), m_kNN);
@@ -347,7 +347,7 @@ Eigen::ArrayXXd G3PointAction::computeMeanAngle()
{
for (int n = 0; n < m_kNN; n++)
{
labels_of_neighbors(index, n) = m_labels(m_neighbors_indexes(index, n));
labels_of_neighbors(index, n) = m_labels(m_neighborsIndexes(index, n));
}
}
@@ -369,7 +369,7 @@ Eigen::ArrayXXd G3PointAction::computeMeanAngle()
// std::cout << indborder.block(0, 0, 10, 1) << std::endl;
// Compute the angle of the normal vector between the neighbours of each grain / label
int nlabels = m_stacks.size();
size_t nlabels = m_stacks.size();
Eigen::ArrayXXd A = Eigen::ArrayXXd::Zero(nlabels, nlabels);
Eigen::ArrayXXd Aangle(nlabels, nlabels);
Aangle.fill(NAN);
@@ -377,20 +377,30 @@ Eigen::ArrayXXd G3PointAction::computeMeanAngle()
for (auto i : indborder)
{
auto neighbors = m_neighbors_indexes(i, Eigen::all); // indexes of the neighbors of i
auto neighbors = m_neighborsIndexes(i, Eigen::all); // indexes of the neighbors of i
Eigen::Vector3d N1(m_normals(i, Eigen::all)); // normal at i
for (auto j : neighbors)
{
// Take the normals vector for i and j (duplicate the normal vector of i to have the same size as for j)
Eigen::Vector3d N1(m_normals(i, Eigen::all));
Eigen::Vector3d N2(m_normals(j, Eigen::all));
// Take the normals vector for i and j
Eigen::Vector3d N2(m_normals(j, Eigen::all)); // normal at j
double angle = angle_rot_2_vec_mat(N1, N2);
if (i < 27)
{
// std::cout << "INDBORDER i " << i << " j " "" << j << std::endl;
// std::cout << angle << std::endl;
}
A(m_labels(i), m_labels(j)) = A(m_labels(i), m_labels(j)) + angle;
N(m_labels(i), m_labels(j)) = N(m_labels(i), m_labels(j)) + 1;
if ((m_labels(i) != -1) && (m_labels(j) != -1)) // points which belong to the discarded grains have the -1 label
{
if ((m_labels(i) > A.rows()) || (m_labels(j) > A.rows()))
{
std::cout << "ERROR " << m_labels(i) << " " << m_labels(j) << "(nlabels " << nlabels << ")" << std::endl;
}
else
{
A(m_labels(i), m_labels(j)) = A(m_labels(i), m_labels(j)) + angle;
N(m_labels(i), m_labels(j)) = N(m_labels(i), m_labels(j)) + 1;
}
}
}
if (i < 31)
{
@@ -403,7 +413,7 @@ Eigen::ArrayXXd G3PointAction::computeMeanAngle()
// std::cout << A.block(0, 0, 10, 10) << std::endl;
/// compute the means
for (int r = 0; r < nlabels; r++)
for (int r = 0; r < nlabels; r++)
{
for(int c = 0; c < nlabels; c++)
{
@@ -420,52 +430,40 @@ Eigen::ArrayXXd G3PointAction::computeMeanAngle()
return Aangle;
}
bool G3PointAction::exportLocalMaximaAsCloud()
bool G3PointAction::checkStacks(const std::vector<std::vector<int>>& stacks, int count)
{
// create cloud
QString cloudName = m_cloud->getName() + "_g3point";
ccPointCloud *cloud = new ccPointCloud(cloudName);
std::set<int> indexes;
bool ret = true;
int errorCount = 0;
RGBAColorsTableType randomColors = getRandomColors(m_localMaximumIndexes.size());
for (auto index : m_localMaximumIndexes)
// the stacks shall contain each point, only one time
for (auto& stack : stacks)
{
cloud->addPoint(*m_cloud->getPoint(index));
}
//allocate colors if necessary
if (cloud->resizeTheRGBTable())
{
for (int index = 0; index < cloud->size(); index++)
for (int index : stack)
{
cloud->setPointColor(index, randomColors.getValue(index));
if (indexes.count(index))
{
ccLog::Warning("[G3PointAction::check_stacks] index already in the set " + QString::number(index));
ret = false;
errorCount++;
}
indexes.insert(index);
}
}
cloud->showColors(true);
cloud->setPointSize(m_dlg->getPointSize());
ccHObject* parent = m_cloud->getParent();
int nbChildren = parent->getChildrenNumber();
std::vector<ccHObject *> toDelete;
for (int k = 0; k < nbChildren; k++)
if (errorCount)
{
auto child = parent->getChild(k);
if (child->getName() == cloudName)
{
toDelete.push_back(child);
}
}
for (auto& child : toDelete)
{
parent->removeChild(child);
ccLog::Error("[G3PointAction::check_stacks] number of duplicates " + QString::number(errorCount));
}
parent->addChild(cloud, ccHObject::DP_PARENT_OF_OTHER, 0);
m_app->addToDB(cloud);
// the number of points in the stacks shall be equal to count
if(indexes.size() != count)
{
ccLog::Warning("[G3PointAction::check_stacks] size of indexes " + QString::number(indexes.size()) + ", point count " + QString::number(m_cloud->size()));
ret = false;
}
return true;
return ret;
}
bool G3PointAction::updateLocalMaximumIndexes()
@@ -524,6 +522,12 @@ bool G3PointAction::updateLabelsAndColors()
return false;
}
for (int index = 0; index < m_cloud->size(); index++) // points which are not in the stacks will have the label -1
{
g3point_label->setValue(index, -1);
m_labels = -1;
}
for (int k = 0; k < m_stacks.size(); k++)
{
const std::vector<int>& stack = m_stacks[k];
@@ -561,255 +565,63 @@ bool G3PointAction::updateLabelsAndColors()
return true;
}
bool G3PointAction::checkStacks(const std::vector<std::vector<int>>& stacks, int count)
bool G3PointAction::exportLocalMaximaAsCloud()
{
std::set<int> indexes;
bool ret = true;
int errorCount = 0;
// create cloud
// QString cloudName = m_cloud->getName() + "_g3point";
QString cloudName = "g3point_summits";
ccPointCloud *cloud = new ccPointCloud(cloudName);
// the stacks shall contain each point, only one time
for (auto& stack : stacks)
RGBAColorsTableType randomColors = getRandomColors(m_localMaximumIndexes.size());
for (auto index : m_localMaximumIndexes)
{
for (int index : stack)
cloud->addPoint(*m_cloud->getPoint(index));
}
//allocate colors if necessary
if (cloud->resizeTheRGBTable())
{
for (int index = 0; index < cloud->size(); index++)
{
if (indexes.count(index))
{
ccLog::Warning("[G3PointAction::check_stacks] index already in the set " + QString::number(index));
ret = false;
errorCount++;
}
indexes.insert(index);
cloud->setPointColor(index, randomColors.getValue(index));
}
}
if (errorCount)
cloud->showColors(true);
cloud->setPointSize(m_dlg->getPointSize());
ccHObject* parent = m_cloud->getParent();
int nbChildren = parent->getChildrenNumber();
std::vector<ccHObject *> toDelete;
for (int k = 0; k < nbChildren; k++)
{
ccLog::Error("[G3PointAction::check_stacks] number of duplicates " + QString::number(errorCount));
auto child = parent->getChild(k);
if (child->getName() == cloudName)
{
toDelete.push_back(child);
}
}
for (auto& child : toDelete)
{
m_app->removeFromDB(child, true);
}
// the number of points in the stacks shall be the number of point in m_labels
if(indexes.size() != m_labels.size())
{
ccLog::Warning("[G3PointAction::check_stacks] size of indexes " + QString::number(indexes.size()) + ", point count " + QString::number(m_cloud->size()));
ret = false;
}
parent->addChild(cloud, ccHObject::DP_PARENT_OF_OTHER, 0);
m_app->addToDB(cloud);
return ret;
return true;
}
int G3PointAction::cluster()
bool G3PointAction::processNewStacks(std::vector<std::vector<int>>& stacks, int pointCount)
{
ccLog::Print("[cluster_labels]");
size_t nlabels = m_stacks.size();
m_maxAngle1 = m_dlg->getMaxAngle1();
m_radiusFactor = m_dlg->getRadiusFactor();
// Compute the distances between the sinks associated to each label
Eigen::ArrayXXd D1(nlabels, nlabels);
for (int i = 0; i < nlabels; i++)
{
for (int j = 0; j < nlabels; j++)
{
D1(i, j) = (*m_cloud->getPoint(m_localMaximumIndexes(i)) - *m_cloud->getPoint(m_localMaximumIndexes(j))).norm();
}
}
// Estimate the distances between labels using the areas
int k = 0;
Eigen::ArrayXXd D2 = Eigen::ArrayXXd::Zero(nlabels, nlabels);
Eigen::ArrayXd radius = Eigen::ArrayXd::Zero(nlabels);
for (auto &stack : m_stacks) // Radius of each label (assuming the surface corresponds to a disk)
{
radius(k) = sqrt(m_area(stack).sum() / M_PI);
k++;
}
for(int i = 0; i < nlabels; i++) // Compute inter-distances by summing radius
{
for(int j = 0; j < nlabels; j++)
{
D2(i, j) = radius(i) + radius(j);
}
}
// If the radius of the sink is above the distance to the other sink (by a factor of rad_factor), set Dist to 1
Eigen::ArrayXXi Dist = Eigen::ArrayXXi::Zero(nlabels, nlabels);
Dist = (m_radiusFactor * D2 > D1).select(1, Dist);
std::cout << "Dist" << std::endl;
for (int i = 0; i < 10; i++) // set the values of the diagonal to 0
{
Dist(i, i) = 0;
}
// If labels are neighbours, set Nneigh to 1
Eigen::ArrayXXi Nneigh = Eigen::ArrayXXi::Zero(nlabels, nlabels);
k = 0;
for (auto& stack : m_stacks)
{
Eigen::ArrayXXi labels(stack.size(), m_kNN);
for (int index = 0; index < stack.size(); index++)
{
for (int n = 0; n < m_kNN; n++)
{
labels(index, n) = m_labels(m_neighbors_indexes(stack[index], n));
}
}
auto reshaped = labels.reshaped();
std::set<int> unique_elements(reshaped.begin(), reshaped.end());
for (auto unique : unique_elements)
{
Nneigh(k, unique) = 1;
}
k++;
}
Eigen::ArrayXXd A = computeMeanAngle();
// merge labels if sinks are
// => close to each other (Dist == 1)
// => neighbours (Nneigh == 1)
// => normals are similar
std::cout << "\n\nDist" << std::endl;
std::cout << Dist.block(0, 0, 10, 10) << std::endl;
std::cout << "\n\nNneigh" << std::endl;
std::cout << Nneigh.block(0, 0, 10, 10) << std::endl;
std::cout << "\n\nA" << std::endl;
std::cout << A.block(0, 0, 10, 10) << std::endl;
if (!checkStacks(m_stacks, m_cloud->size()))
{
ccLog::Error("m_stacks is not valid");
}
// create the condition matrix and force the symmetry of the matrix
XXb condition = (Dist < 1) || (Nneigh < 1) || (A > m_maxAngle1) || (A != A);
XXb symmetrical_condition = (condition == condition.transpose()).select(condition, true);
condition = symmetrical_condition;
std::cout << "\n\nsymmetrical_condition" << std::endl;
std::cout << symmetrical_condition.block(0, 0, 20, 20) << std::endl;
std::vector<std::vector<int>> newStacks;
Eigen::ArrayXi newLabels = Eigen::ArrayXi::Ones(m_labels.size()) * (-1);
int countNewLabels = 0;
for (int label = 0; label < nlabels; label++)
{
if (newLabels(label) == -1) // the label has not already been merged
{
newLabels(label) = countNewLabels;
newStacks.push_back(m_stacks[label]); // initialize a newStack with the stack of the current label
countNewLabels++;
}
for (int otherLabel = 0; otherLabel < nlabels; otherLabel++)
{
if (otherLabel == label)
{
continue; // do not try to merge a label with itself
}
// shall we merge otherLabel with label?
if (!condition(label, otherLabel))
// if ((Dist(label, otherLabel) == 1)
// && (Nneigh(label, otherLabel) == 1)
// && (A(label, otherLabel) <= m_maxAngle1)
// && (!isnan(A(label, otherLabel))))
{
std::vector<int>& labelStack = newStacks[newLabels(label)];
if (newLabels(otherLabel) != -1) // the other label has already been merged
{
std::vector<int>& otherLabelStack = newStacks[newLabels(otherLabel)];
if (newLabels(label) > newLabels(otherLabel)) // merge label in otherLabel
{
// add the label stack to the otherLabel stack
otherLabelStack.insert(otherLabelStack.end(), labelStack.begin(), labelStack.end()); // add the stack to the label stack
// empty the label stack
labelStack.clear();
// update the label
newLabels(label) = newLabels(otherLabel);
}
if (newLabels(label) < newLabels(otherLabel)) // merge otherLabel in label
{
// add the otherLabel stack to the label stack
labelStack.insert(labelStack.end(), otherLabelStack.begin(), otherLabelStack.end()); // add the stack to the label stack
// empty the otherLabel stack
otherLabelStack.clear();
// update the otherLabel
newLabels(otherLabel) = newLabels(label);
}
}
else // merge otherLabel and label
{
std::vector<int>& otherLabelStack = m_stacks[otherLabel];
// add the otherLabel stack to the label stack
labelStack.insert(labelStack.end(), otherLabelStack.begin(), otherLabelStack.end()); // add the stack to the label stack
// update the otherLabel
newLabels(otherLabel) = newLabels(label);
}
}
}
}
// remove empty stacks
std::vector<std::vector<int>> newStacksWithoutEmpty;
for (auto& stack : newStacks)
{
if (!stack.empty())
{
newStacksWithoutEmpty.push_back(stack);
}
}
std::cout << "m_stacks.size() " << m_stacks.size()
<< " newStacks.size() " << newStacks.size()
<< " newStacksWithoutEmpty.size() " << newStacksWithoutEmpty.size() << std::endl;
newStacks = newStacksWithoutEmpty;
std::cout << "(a) m_stacks.size() " << m_stacks.size() << std::endl;
std::cout << "(a) m_labels.size() " << m_labels.size() << std::endl;
for (int k = 0; k < 10; k++)
{
std::cout << m_stacks[k].size() << " " << newStacks[k].size() << std::endl;
}
if (!checkStacks(newStacks, m_cloud->size()))
{
ccLog::Error("newStacks is not valid");
}
m_stacks = newStacks;
updateLocalMaximumIndexes();
updateLabelsAndColors();
exportLocalMaximaAsCloud();
std::cout << "(b) m_stacks.size() " << m_stacks.size() << std::endl;
std::cout << "(b) m_labels.size() " << m_labels.size() << std::endl;
return 0;
}
bool G3PointAction::processNewStacks(std::vector<std::vector<int>>& stacks)
{
if (!checkStacks(stacks, m_cloud->size()))
if (!checkStacks(stacks, pointCount))
{
ccLog::Error("[G3PointAction::merge] newStacks is not valid");
return false;
}
ccLog::Print("[G3PointAction::processNewStacks] keep " + QString::number(stacks.size())
+ "/" + QString::number(m_stacks.size()) + " labels ("
+ QString::number(m_stacks.size() - stacks.size()) + " removed)");
QApplication::processEvents();
// new stacks are valid, set the class attribute
m_stacks = stacks;
@@ -907,19 +719,20 @@ bool G3PointAction::merge(XXb& condition)
newStacks = newStacksWithoutEmpty;
processNewStacks(newStacks);
ccLog::Print("[G3PointAction::merge] keep " + QString::number(newStacks.size())
+ "/" + QString::number(m_stacks.size()) + " labels ("
+ QString::number(m_stacks.size() - newStacks.size()) + " removed)");
processNewStacks(newStacks, m_cloud->size());
return true;
}
bool G3PointAction::keepLabels(Xb& condition)
bool G3PointAction::keep(Xb& condition)
{
std::vector<std::vector<int>> newStacks;
size_t pointCount = 0;
// the first stack will contain the removed points
newStacks.push_back(std::vector<int>());
for (int index = 0; index < condition.size(); index++)
{
std::vector<int>& stack = m_stacks[index];
@@ -928,14 +741,13 @@ bool G3PointAction::keepLabels(Xb& condition)
newStacks.push_back(stack);
pointCount = pointCount + stack.size();
}
else
{
// add the stack to the default stack
newStacks[0].insert(newStacks[0].end(), stack.begin(), stack.end());
}
}
if (!processNewStacks(newStacks))
ccLog::Print("[G3PointAction::keep] keep " + QString::number(newStacks.size())
+ "/" + QString::number(m_stacks.size()) + " gains ("
+ QString::number(m_stacks.size() - newStacks.size()) + " removed)");
if (!processNewStacks(newStacks, pointCount))
{
ccLog::Error("[G3PointAction::keepLabels] processing newStacks failed");
}
@@ -943,6 +755,206 @@ bool G3PointAction::keepLabels(Xb& condition)
return true;
}
int G3PointAction::cluster()
{
ccLog::Print("[cluster_labels]");
size_t nlabels = m_stacks.size();
m_maxAngle1 = m_dlg->getMaxAngle1();
m_radiusFactor = m_dlg->getRadiusFactor();
// Compute the distances between the sinks associated to each label
Eigen::ArrayXXd D1(nlabels, nlabels);
for (int i = 0; i < nlabels; i++)
{
for (int j = 0; j < nlabels; j++)
{
D1(i, j) = (*m_cloud->getPoint(m_localMaximumIndexes(i)) - *m_cloud->getPoint(m_localMaximumIndexes(j))).norm();
}
}
// Estimate the distances between labels using the areas
int k = 0;
Eigen::ArrayXXd D2 = Eigen::ArrayXXd::Zero(nlabels, nlabels);
Eigen::ArrayXd radius = Eigen::ArrayXd::Zero(nlabels);
for (auto &stack : m_stacks) // Radius of each label (assuming the surface corresponds to a disk)
{
radius(k) = sqrt(m_area(stack).sum() / M_PI);
k++;
}
for(int i = 0; i < nlabels; i++) // Compute inter-distances by summing radius
{
for(int j = 0; j < nlabels; j++)
{
D2(i, j) = radius(i) + radius(j);
}
}
// If the radius of the sink is above the distance to the other sink (by a factor of rad_factor), set Dist to 1
Eigen::ArrayXXi Dist = Eigen::ArrayXXi::Zero(nlabels, nlabels);
Dist = (m_radiusFactor * D2 > D1).select(1, Dist);
std::cout << "Dist" << std::endl;
for (int i = 0; i < 10; i++) // set the values of the diagonal to 0
{
Dist(i, i) = 0;
}
// If labels are neighbours, set Nneigh to 1
Eigen::ArrayXXi Nneigh = Eigen::ArrayXXi::Zero(nlabels, nlabels);
k = 0;
for (auto& stack : m_stacks)
{
Eigen::ArrayXXi labels(stack.size(), m_kNN);
for (int index = 0; index < stack.size(); index++)
{
for (int n = 0; n < m_kNN; n++)
{
labels(index, n) = m_labels(m_neighborsIndexes(stack[index], n));
}
}
auto reshaped = labels.reshaped();
std::set<int> unique_elements(reshaped.begin(), reshaped.end());
for (auto unique : unique_elements)
{
Nneigh(k, unique) = 1;
}
k++;
}
Eigen::ArrayXXd A = computeMeanAngleBetweenNormalsAtBorders();
// merge labels if sinks are
// => close to each other (Dist == 1)
// => neighbours (Nneigh == 1)
// => normals are similar
std::cout << "\n\nDist" << std::endl;
std::cout << Dist.block(0, 0, 10, 10) << std::endl;
std::cout << "\n\nNneigh" << std::endl;
std::cout << Nneigh.block(0, 0, 10, 10) << std::endl;
std::cout << "\n\nA" << std::endl;
std::cout << A.block(0, 0, 10, 10) << std::endl;
if (!checkStacks(m_stacks, m_cloud->size()))
{
ccLog::Error("m_stacks is not valid");
}
// create the condition matrix and force the symmetry of the matrix
XXb condition = (Dist < 1) || (Nneigh < 1) || (A > m_maxAngle1) || (A != A);
XXb symmetrical_condition = (condition == condition.transpose()).select(condition, true);
condition = symmetrical_condition;
std::cout << "\n\nsymmetrical_condition" << std::endl;
std::cout << symmetrical_condition.block(0, 0, 20, 20) << std::endl;
std::vector<std::vector<int>> newStacks;
Eigen::ArrayXi newLabels = Eigen::ArrayXi::Ones(m_labels.size()) * (-1);
int countNewLabels = 0;
for (int label = 0; label < nlabels; label++)
{
if (newLabels(label) == -1) // the label has not already been merged
{
newLabels(label) = countNewLabels;
newStacks.push_back(m_stacks[label]); // initialize a newStack with the stack of the current label
countNewLabels++;
}
for (int otherLabel = 0; otherLabel < nlabels; otherLabel++)
{
if (otherLabel == label)
{
continue; // do not try to merge a label with itself
}
// shall we merge otherLabel with label?
if (!condition(label, otherLabel))
{
std::vector<int>& labelStack = newStacks[newLabels(label)];
if (newLabels(otherLabel) != -1) // the other label has already been merged
{
std::vector<int>& otherLabelStack = newStacks[newLabels(otherLabel)];
if (newLabels(label) > newLabels(otherLabel)) // merge label in otherLabel
{
// add the label stack to the otherLabel stack
otherLabelStack.insert(otherLabelStack.end(), labelStack.begin(), labelStack.end()); // add the stack to the label stack
// empty the label stack
labelStack.clear();
// update the label
newLabels(label) = newLabels(otherLabel);
}
if (newLabels(label) < newLabels(otherLabel)) // merge otherLabel in label
{
// add the otherLabel stack to the label stack
labelStack.insert(labelStack.end(), otherLabelStack.begin(), otherLabelStack.end()); // add the stack to the label stack
// empty the otherLabel stack
otherLabelStack.clear();
// update the otherLabel
newLabels(otherLabel) = newLabels(label);
}
}
else // merge otherLabel and label
{
std::vector<int>& otherLabelStack = m_stacks[otherLabel];
// add the otherLabel stack to the label stack
labelStack.insert(labelStack.end(), otherLabelStack.begin(), otherLabelStack.end()); // add the stack to the label stack
// update the otherLabel
newLabels(otherLabel) = newLabels(label);
}
}
}
}
// remove empty stacks
std::vector<std::vector<int>> newStacksWithoutEmpty;
for (auto& stack : newStacks)
{
if (!stack.empty())
{
newStacksWithoutEmpty.push_back(stack);
}
}
std::cout << "m_stacks.size() " << m_stacks.size()
<< " newStacks.size() " << newStacks.size()
<< " newStacksWithoutEmpty.size() " << newStacksWithoutEmpty.size() << std::endl;
newStacks = newStacksWithoutEmpty;
std::cout << "(a) m_stacks.size() " << m_stacks.size() << std::endl;
std::cout << "(a) m_labels.size() " << m_labels.size() << std::endl;
for (int k = 0; k < 10; k++)
{
std::cout << m_stacks[k].size() << " " << newStacks[k].size() << std::endl;
}
ccLog::Print("[G3PointAction::merge] keep " + QString::number(newStacks.size())
+ "/" + QString::number(m_stacks.size()) + " labels ("
+ QString::number(m_stacks.size() - newStacks.size()) + " removed)");
if (!checkStacks(newStacks, m_cloud->size()))
{
ccLog::Error("newStacks is not valid");
}
m_stacks = newStacks;
updateLocalMaximumIndexes();
updateLabelsAndColors();
exportLocalMaximaAsCloud();
std::cout << "(b) m_stacks.size() " << m_stacks.size() << std::endl;
std::cout << "(b) m_labels.size() " << m_labels.size() << std::endl;
return 0;
}
bool G3PointAction::cleanLabels()
{
ccLog::Print("[cleanLabels]");
@@ -950,12 +962,12 @@ bool G3PointAction::cleanLabels()
m_maxAngle2 = m_dlg->getMaxAngle2();
m_nMin = m_dlg->getNMin();
m_minFlatness = m_dlg->getMinFlatness();
size_t nGrains = m_stacks.size();
// merge points considering the normals at the border
{
ccLog::Print("[cleanLabels] merge points considering the normals at the border");
Eigen::ArrayXXd A = computeMeanAngle();
Eigen::ArrayXXd A = computeMeanAngleBetweenNormalsAtBorders();
size_t nGrains = m_stacks.size();
XXb condition = (A > m_maxAngle2) || (A != A) || (Eigen::MatrixXi::Identity(nGrains, nGrains).array() == 1); // add true on the diagonal (important for the if hereafter)
XXb symmetrical_condition = (condition == condition.transpose()).select(condition, true);
if (condition.all())
@@ -964,7 +976,6 @@ bool G3PointAction::cleanLabels()
}
else
{
ccLog::Print("[cleanLabels] condition.count() " + QString::number(condition.count()) + " condition.size() " + QString::number(condition.size()));
merge(condition);
}
@@ -987,44 +998,57 @@ bool G3PointAction::cleanLabels()
}
else if (numberOfGrainsToKeep)
{
keepLabels(condition);
keep(condition);
}
else
{
ccLog::Error("[cleanLabels] CANCEL: no remaining grain after removing those with less than " + QString::number(m_nMin) + " points");
ccLog::Error("[cleanLabels] no remaining grain after removing those with less than " + QString::number(m_nMin) + " points");
return false;
}
QApplication::processEvents();
}
// remove flattish labels
// {
// ccLog::Print("[cleanLabels] remove flattish labels");
// Eigen::ArrayX3d s(m_stacks.size(), 3);
// for (size_t k = 0; k < m_stacks.size(); k++)
// {
// std::vector<int>& stack = m_stacks[k];
// // get the points of the label
// size_t nPoints = stack.size();
// Eigen::MatrixX3d points(nPoints, 3);
// for (int index = 0; index < nPoints; index++)
// {
// const CCVector3* point = m_cloud->getPoint(stack[index]);
// points(index, 0) = point->x;
// points(index, 1) = point->y;
// points(index, 2) = point->z;
// }
// // compute the centroid of the label
// Eigen::RowVector3d centroid = points.colwise().mean();
// points.rowwise() -= centroid;
// // SVD decomposition A = U S V
// s(k, Eigen::all) = points.jacobiSvd().singularValues();
// // filtering condition: (l2 / l0 > min_flatness) or (l1 / l0 > 2 * min_flatness)
// Xb condition = (s(Eigen::all, 2) / s(Eigen::all, 0) > m_minFlatness)
// || (s(Eigen::all, 1) / s(Eigen::all, 0) > 2. * m_minFlatness);
// keepLabels(condition);
// }
// }
{
ccLog::Print("[cleanLabels] remove flattish labels");
Eigen::ArrayX3d s(m_stacks.size(), 3);
for (size_t k = 0; k < m_stacks.size(); k++)
{
std::vector<int>& stack = m_stacks[k];
// get the points of the label
size_t nPoints = stack.size();
Eigen::MatrixX3d points(nPoints, 3);
for (int index = 0; index < nPoints; index++)
{
const CCVector3* point = m_cloud->getPoint(stack[index]);
points(index, 0) = point->x;
points(index, 1) = point->y;
points(index, 2) = point->z;
}
// compute the centroid of the label
Eigen::RowVector3d centroid = points.colwise().mean();
points.rowwise() -= centroid;
// SVD decomposition A = U S V
s(k, Eigen::all) = points.jacobiSvd().singularValues();
}
// filtering condition: (l2 / l0 > min_flatness) or (l1 / l0 > 2 * min_flatness)
Xb condition = (s(Eigen::all, 2) / s(Eigen::all, 0) > m_minFlatness)
|| (s(Eigen::all, 1) / s(Eigen::all, 0) > 2. * m_minFlatness);
size_t numberOfGrainsToKeep = condition.count();
if (numberOfGrainsToKeep == m_stacks.size())
{
ccLog::Print("[cleanLabels] no flattish grain, nothing to remove");
}
else if (numberOfGrainsToKeep)
{
keep(condition);
}
else
{
ccLog::Error("[cleanLabels] no remaining grain after removing the flattish ones");
return false;
}
}
return true;
}
@@ -1074,18 +1098,18 @@ int G3PointAction::segment_labels_braun_willett(bool useParallelStrategy)
break;
}
}
receivers(index) = m_neighbors_indexes(index, index_of_extreme_slope(index));
receivers(index) = m_neighborsIndexes(index, index_of_extreme_slope(index));
}
// if the minimum slope is positive, the receiver is a local maximum
int nb_maxima;
if (steepestSlope)
{
nb_maxima = (extreme_slopes < 0).count();
nb_maxima = (extreme_slopes <= 0).count();
}
else
{
nb_maxima = (extreme_slopes > 0).count();
nb_maxima = (extreme_slopes >= 0).count();
}
m_localMaximumIndexes = Eigen::ArrayXi::Zero(nb_maxima);
int l = 0;
@@ -1093,7 +1117,7 @@ int G3PointAction::segment_labels_braun_willett(bool useParallelStrategy)
{
if (steepestSlope)
{
if (extreme_slopes(k) < 0)
if (extreme_slopes(k) <= 0)
{
m_localMaximumIndexes(l) = k;
receivers(k) = k;
@@ -1102,7 +1126,7 @@ int G3PointAction::segment_labels_braun_willett(bool useParallelStrategy)
}
else
{
if (extreme_slopes(k) > 0)
if (extreme_slopes(k) >= 0)
{
m_localMaximumIndexes(l) = k;
receivers(k) = k;
@@ -1239,7 +1263,7 @@ int G3PointAction::segment_labels_steepest_slope(bool useParallelStrategy)
std::cout << "[segment_labels] slope already seen, index " << index << ", k "<< k << std::endl;
}
}
receivers(index) = m_neighbors_indexes(index, index_of_steepest_slope(index));
receivers(index) = m_neighborsIndexes(index, index_of_steepest_slope(index));
}
// if the minimum slope is positive, the receiver is a base level node
@@ -1369,7 +1393,7 @@ void G3PointAction::get_neighbors_distances_slopes(unsigned index)
for (int k = 0; k < m_kNN; k++)
{
// store the index of the neighbor
m_neighbors_indexes(index, k) = Yk.getPointGlobalIndex(k + 1);
m_neighborsIndexes(index, k) = Yk.getPointGlobalIndex(k + 1);
// compute the distance to the neighbor
const CCVector3* neighbor = Yk.getPoint(k + 1);
float distance = (*P - *neighbor).norm();
@@ -1636,7 +1660,40 @@ void G3PointAction::segmentAndClusterAndClean()
void G3PointAction::getBorders()
{
// Find the indexborder nodes (no donor and many other labels in the neighbourhood)
Eigen::ArrayXXi duplicatedLabelsInColumns(m_cloud->size(), m_kNN);
for (int n = 0; n < m_kNN; n++)
{
duplicatedLabelsInColumns(Eigen::all, n) = m_labels;
}
Eigen::ArrayXXi labelsOfNeighbors(m_cloud->size(), m_kNN);
for (int index = 0; index < m_cloud->size(); index++)
{
for (int n = 0; n < m_kNN; n++)
{
labelsOfNeighbors(index, n) = m_labels(m_neighborsIndexes(index, n));
}
}
Eigen::ArrayXi temp = m_kNN - (labelsOfNeighbors == duplicatedLabelsInColumns).cast<int>().rowwise().sum();
auto condition = ((temp >= m_kNN / 4) && (m_ndon == 0));
// create cloud
CCCoreLib::ReferenceCloud referenceCloud(m_cloud);
for (int index = 0; index < condition.size(); index++)
{
if (condition(index))
{
referenceCloud.addPointIndex(index);
}
}
ccPointCloud* borderCloud = m_cloud->partialClone(&referenceCloud);
// add cloud to the database
borderCloud->setName(m_cloud->getName() + "_borders");
m_cloud->getParent()->addChild(borderCloud, ccHObject::DP_PARENT_OF_OTHER, 0);
m_app->addToDB(borderCloud);
}
void G3PointAction::init()
@@ -1644,7 +1701,7 @@ void G3PointAction::init()
m_kNN = m_dlg->getkNN();
// initialize the matrices which will contain the results
m_neighbors_indexes = Eigen::ArrayXXi::Zero(m_cloud->size(), m_kNN);
m_neighborsIndexes = Eigen::ArrayXXi::Zero(m_cloud->size(), m_kNN);
m_neighbors_distances = Eigen::ArrayXXd::Zero(m_cloud->size(), m_kNN);
m_neighbors_slopes = Eigen::ArrayXXd::Zero(m_cloud->size(), m_kNN);
m_normals = Eigen::ArrayXXd::Zero(m_cloud->size(), 3);
@@ -1687,7 +1744,7 @@ void G3PointAction::resetDlg()
void G3PointAction::clean()
{
m_neighbors_indexes.resize(0, 0);
m_neighborsIndexes.resize(0, 0);
m_neighbors_distances.resize(0, 0);
m_neighbors_slopes.resize(0, 0);
m_normals.resize(0, 0);