28 #include <QApplication>
29 #include <QCoreApplication>
30 #include <QElapsedTimer>
31 #include <QMainWindow>
33 #include <QOpenGLShaderProgram>
34 #include <QPushButton>
35 #include <QThreadPool>
36 #include <QtConcurrent>
48 #include <Eigen/Geometry>
52 std::shared_ptr<G3PointAction> G3PointAction::s_g3PointAction;
54 QPointer<G3PointPlots> G3PointAction::s_g3PointPlots;
57 : m_app(app), m_dlg(nullptr), m_grainsAsEllipsoids(nullptr) {
63 if (s_g3PointAction) {
84 "[G3PointAction::GetG3PointAction] no g3point_label scalar "
85 "field, reset action");
89 "[G3PointAction::GetG3PointAction] use existing "
90 "g3point_label scalar field");
91 s_g3PointAction->setCloud(cloud);
94 s_g3PointAction->showDlg();
98 Q_ASSERT(randomColorsNumber > 1);
101 if (!randomColors.
reserveSafe(
static_cast<unsigned>(randomColorsNumber))) {
107 std::mt19937 gen(42);
108 std::uniform_int_distribution<uint16_t>
dist(
111 for (
int i = 0; i < randomColorsNumber; ++i) {
113 col.
r =
static_cast<unsigned char>(
dist(gen));
114 col.
g =
static_cast<unsigned char>(
dist(gen));
117 (
static_cast<double>(col.
r) +
118 static_cast<double>(col.
g)) /
127 bool G3PointAction::sfConvertToRandomRGB(
131 int s_randomColorsNumber = 256;
133 Q_ASSERT(s_randomColorsNumber > 1);
137 static_cast<unsigned>(s_randomColorsNumber))) {
145 std::mt19937 gen(42);
146 std::uniform_int_distribution<uint16_t>
dist(
149 for (
int i = 0; i < s_randomColorsNumber; ++i) {
151 col.
r =
static_cast<unsigned char>(
dist(gen));
152 col.
g =
static_cast<unsigned char>(
dist(gen));
153 col.
b =
static_cast<unsigned char>(
dist(gen));
162 for (
ccHObject* ent : selectedEntities) {
165 bool lockedVertices =
false;
167 if (lockedVertices) {
169 "[G3Point::sfConvertToRandomRGB] "
170 "DisplayLockedVerticesWarning");
173 if (cloud !=
nullptr)
183 ScalarType minSF = sf->
getMin();
184 ScalarType maxSF = sf->
getMax();
187 (maxSF - minSF) / (s_randomColorsNumber - 1);
188 if (step == 0) step =
static_cast<ScalarType
>(1.0);
190 for (
unsigned i = 0; i < pc->
size(); ++i) {
193 static_cast<unsigned>((val - minSF) / step);
194 if (colIndex == s_randomColorsNumber) --colIndex;
211 void G3PointAction::addToStack(
int index,
212 const Eigen::ArrayXi& n_donors,
213 const Eigen::ArrayXXi& donors,
214 std::vector<int>& stack) {
215 stack.push_back(index);
217 for (
int k = 0; k < n_donors(index); k++) {
218 addToStack(donors(index, k), n_donors, donors, stack);
222 int G3PointAction::segmentLabels(
bool useParallelStrategy) {
226 Eigen::ArrayXd min_slopes(m_neighborsSlopes.rowwise().minCoeff());
227 Eigen::ArrayXi index_of_min_slope = Eigen::ArrayXi::Zero(m_cloud->
size());
228 Eigen::ArrayXi receivers(m_cloud->
size());
230 for (
unsigned index = 0; index < m_cloud->
size(); index++) {
231 double min_slope = min_slopes(index);
233 for (
int k = 0; k < m_kNN; k++) {
234 if (m_neighborsSlopes(index, k) == min_slope) {
235 index_of_min_slope(index) = k;
238 std::cout <<
"[segment_labels] slope already seen, index "
242 receivers(index) = m_neighborsIndexes(index, index_of_min_slope(index));
246 int nb_maxima = (min_slopes > 0).
count();
247 Eigen::ArrayXi localMaximumIndexes = Eigen::ArrayXi::Zero(nb_maxima);
249 for (
unsigned int k = 0; k < m_cloud->
size(); k++) {
250 if (min_slopes(k) > 0) {
251 localMaximumIndexes(l) = k;
258 CVLog::Print(
"[segment_labels] identify the donors for each receiver");
259 Eigen::ArrayXi nDonors = Eigen::ArrayXi::Zero(m_cloud->
size());
260 Eigen::ArrayXXi donors = Eigen::ArrayXXi::Zero(m_cloud->
size(), m_kNN);
263 for (
unsigned int k = 0; k < m_cloud->
size(); k++) {
264 int receiver = receivers(k);
267 if (nDonors(receiver) < m_kNN - 1) {
268 nDonors(receiver) = nDonors(receiver) + 1;
269 donors(receiver, nDonors(receiver) - 1) = k;
272 "%1, receiver(k) %2, nDonors %3")
275 .arg(nDonors(receiver)));
282 Eigen::ArrayXi labels = Eigen::ArrayXi::Zero(m_cloud->
size());
283 Eigen::ArrayXi labelsnpoint = Eigen::ArrayXi::Zero(m_cloud->
size());
284 std::vector<std::vector<int>> stacks;
291 "[G3Point::segment_labels] impossible to create scalar "
292 "field g3point_label");
305 for (
int k = 0; k < localMaximumIndexes.size(); k++) {
306 int localMaximumIndex = localMaximumIndexes(k);
307 std::vector<int> stack;
308 addToStack(localMaximumIndex, nDonors, donors, stack);
310 for (
auto i : stack) {
312 labelsnpoint(i) =
static_cast<int>(stack.size());
318 stacks.push_back(stack);
334 int nLabels = localMaximumIndexes.size();
339 double G3PointAction::angleRot2VecMat(
const Eigen::Vector3d& a,
340 const Eigen::Vector3d& b) {
349 angle = atan2(c.norm(), d) * 180 /
M_PI;
354 Eigen::ArrayXXd G3PointAction::computeMeanAngleBetweenNormalsAtBorders() {
357 Eigen::ArrayXXi duplicated_labels(m_cloud->
size(), m_kNN);
358 for (
int n = 0; n < m_kNN; n++) {
359 duplicated_labels(Eigen::all, n) = m_labels;
361 Eigen::ArrayXXi labels_of_neighbors(m_cloud->
size(), m_kNN);
362 for (
int index = 0; index < static_cast<int>(m_cloud->
size()); index++) {
363 for (
int n = 0; n < m_kNN; n++) {
364 labels_of_neighbors(index, n) =
365 m_labels(m_neighborsIndexes(index, n));
369 Eigen::ArrayXi temp = m_kNN - (labels_of_neighbors == duplicated_labels)
373 auto condition = ((temp >= m_kNN / 4) && (m_ndon == 0));
374 Eigen::ArrayXi indborder(condition.count());
377 for (
int c = 0; c < condition.size(); c++) {
386 size_t nlabels = m_stacks.size();
387 Eigen::ArrayXXd A = Eigen::ArrayXXd::Zero(nlabels, nlabels);
388 Eigen::ArrayXXd Aangle(nlabels, nlabels);
390 Eigen::ArrayXXi N = Eigen::ArrayXXi::Zero(nlabels, nlabels);
392 for (
auto i : indborder) {
393 auto neighbors = m_neighborsIndexes(
395 Eigen::Vector3d N1(m_normals(i, Eigen::all));
396 for (
auto j : neighbors) {
398 Eigen::Vector3d N2(m_normals(j, Eigen::all));
399 double angle = angleRot2VecMat(N1, N2);
400 if ((m_labels(i) != -1) &&
404 if ((m_labels(i) > A.rows()) || (m_labels(j) > A.rows())) {
405 std::cout <<
"ERROR " << m_labels(i) <<
" " << m_labels(j)
406 <<
"(nlabels " << nlabels <<
")" <<
std::endl;
408 A(m_labels(i), m_labels(j)) =
409 A(m_labels(i), m_labels(j)) + angle;
410 N(m_labels(i), m_labels(j)) =
411 N(m_labels(i), m_labels(j)) + 1;
418 for (
int r = 0; r < nlabels; r++) {
419 for (
int c = 0; c < nlabels; c++) {
421 Aangle(r, c) = A(r, c) / N(r, c);
429 bool G3PointAction::checkStacks(
const std::vector<std::vector<int>>& stacks,
431 std::set<int> indexes;
436 for (
auto& stack : stacks) {
437 for (
int index : stack) {
438 if (indexes.count(index)) {
440 "[G3PointAction::check_stacks] index already in the "
442 QString::number(index));
446 indexes.insert(index);
451 CVLog::Warning(
"[G3PointAction::check_stacks] number of duplicates " +
452 QString::number(errorCount));
457 if (indexes.size() !=
count) {
459 QString::number(indexes.size()) +
", expected " +
460 QString::number(m_cloud->
size()) +
461 " (may be due to duplicates in the point cloud)");
468 bool G3PointAction::updateLocalMaximumIndexes() {
469 size_t nlabels = m_stacks.size();
471 Eigen::ArrayXi localMaximumIndexes = Eigen::ArrayXi::Ones(nlabels) * (-1);
473 for (
int k = 0; k < nlabels; k++) {
474 auto& stack = m_stacks[k];
475 size_t nPoints = stack.size();
476 Eigen::ArrayXd elevations(nPoints);
477 for (
int index = 0; index < nPoints; index++) {
479 elevations(index) =
point->z;
482 elevations.maxCoeff(&maxIndex);
483 localMaximumIndexes(k) = stack[maxIndex];
486 if ((localMaximumIndexes == -1).any()) {
488 "[G3PointAction::updateLocalMaximumIndexes] CANCEL value error "
489 "in the indexes of the local maximima");
493 m_localMaximumIndexes = localMaximumIndexes;
498 bool G3PointAction::updateLabelsAndColors() {
499 CVLog::Print(QString(
"[G3PointAction::update_labels_and_colors]"));
507 "[G3PointAction::update_labels_and_colors] impossible to "
508 "create scalar field g3point_label");
518 QObject::tr(
"[G3PointAction::update_labels_and_colors] Not "
523 for (
unsigned index = 0; index < m_cloud->
size();
531 for (
int k = 0; k < m_stacks.size(); k++) {
532 const std::vector<int>& stack = m_stacks[k];
534 for (
auto i : stack) {
536 m_labelsnpoint(i) =
static_cast<int>(stack.size());
563 bool G3PointAction::exportLocalMaximaAsCloud(
564 const Eigen::ArrayXi& localMaximumIndexes) {
567 QString cloudName =
"g3point_summits";
573 for (
auto index : localMaximumIndexes) {
579 for (
int index = 0; index < static_cast<int>(cloud->
size()); index++) {
587 std::vector<ccHObject*> toDelete;
588 for (
int k = 0; k < nbChildren; k++) {
591 if (child->getName() == cloudName) {
592 toDelete.push_back(child);
595 for (
auto& child : toDelete) {
611 if (!checkStacks(newStacks, pointCount)) {
613 "[G3PointAction::processNewStacks] newStacks is not valid");
618 m_stacks = newStacks;
620 updateLocalMaximumIndexes();
622 updateLabelsAndColors();
624 exportLocalMaximaAsCloud(m_localMaximumIndexes);
634 std::set<float> labels;
635 for (
int idx = 0; idx < g3PointLabel->size(); idx++) {
636 labels.insert(g3PointLabel->
getValue(idx));
640 for (
auto label : labels) {
641 std::vector<int> stack;
642 for (
int idx = 0; idx < static_cast<int>(m_cloud->
size()); idx++) {
643 if (g3PointLabel->
getValue(idx) == label) {
644 stack.push_back(idx);
647 m_stacks.push_back(stack);
658 std::vector<std::vector<int>> newStacks;
659 Eigen::ArrayXi newLabels = Eigen::ArrayXi::Ones(m_labels.size()) * (-1);
660 int countNewLabels = 0;
661 size_t nlabels = m_stacks.size();
663 if (condition.rows() != m_stacks.size())
665 CVLog::Error(
"[G3PointAction::merge] the shape of the condition (" +
666 QString::number(condition.rows()) +
", " +
667 QString::number(condition.cols()) +
668 ") is not coherent with the stacks size " +
669 QString::number(m_stacks.size()));
673 for (
int label = 0; label < nlabels; label++) {
674 if (newLabels(label) == -1)
676 newLabels(label) = countNewLabels;
683 for (
int otherLabel = 0; otherLabel < nlabels; otherLabel++) {
684 if (otherLabel == label) {
689 if (!condition(label, otherLabel)) {
690 std::vector<int>& labelStack = newStacks[newLabels(label)];
692 if (newLabels(otherLabel) !=
695 std::vector<int>& otherLabelStack =
696 newStacks[newLabels(otherLabel)];
697 if (newLabels(label) >
698 newLabels(otherLabel))
701 otherLabelStack.insert(
702 otherLabelStack.end(), labelStack.begin(),
708 newLabels(label) = newLabels(otherLabel);
710 if (newLabels(label) <
711 newLabels(otherLabel))
715 labelStack.end(), otherLabelStack.begin(),
716 otherLabelStack.end());
719 otherLabelStack.clear();
721 newLabels(otherLabel) = newLabels(label);
725 std::vector<int>& otherLabelStack = m_stacks[otherLabel];
728 labelStack.end(), otherLabelStack.begin(),
729 otherLabelStack.end());
732 newLabels(otherLabel) = newLabels(label);
739 std::vector<std::vector<int>> newStacksWithoutEmpty;
740 for (
auto& stack : newStacks) {
741 if (!stack.empty()) {
742 newStacksWithoutEmpty.push_back(stack);
746 newStacks = newStacksWithoutEmpty;
749 "[G3PointAction::merge] keep " + QString::number(newStacks.size()) +
750 "/" + QString::number(m_stacks.size()) +
" labels (" +
751 QString::number(m_stacks.size() - newStacks.size()) +
" removed)");
754 CVLog::Error(
"[G3PointAction::merge] processing newStacks failed");
762 std::vector<std::vector<int>> newStacks;
763 size_t pointCount = 0;
765 for (
int index = 0; index < condition.size(); index++) {
766 std::vector<int>& stack = m_stacks[index];
767 if (condition(index))
769 newStacks.push_back(stack);
770 pointCount = pointCount + stack.size();
775 "[G3PointAction::keep] keep " + QString::number(newStacks.size()) +
776 "/" + QString::number(m_stacks.size()) +
" gains (" +
777 QString::number(m_stacks.size() - newStacks.size()) +
" removed)");
780 CVLog::Error(
"[G3PointAction::keep] processing newStacks failed");
786 template <
typename T>
788 const Eigen::IOFormat CSVFormat(Eigen::StreamPrecision,
789 Eigen::DontAlignCols,
", ",
"\n");
790 std::ofstream file(
name.toLatin1());
791 file << array.format(CSVFormat);
797 size_t nlabels = m_stacks.size();
803 Eigen::ArrayXXd D1(nlabels, nlabels);
804 if (m_localMaximumIndexes.size() != nlabels) {
805 CVLog::Error(
"[G3PointAction::cluster] m_localMaximumIndexes size (" +
806 QString::number(m_localMaximumIndexes.size()) +
807 ") different from nlabels " + QString::number(nlabels));
810 for (
int i = 0; i < nlabels; i++) {
811 for (
int j = 0; j < nlabels; j++) {
812 D1(i, j) = (*m_cloud->
getPoint(m_localMaximumIndexes(i)) -
813 *m_cloud->
getPoint(m_localMaximumIndexes(j)))
820 Eigen::ArrayXXd D2 = Eigen::ArrayXXd::Zero(nlabels, nlabels);
821 Eigen::ArrayXd radius = Eigen::ArrayXd::Zero(nlabels);
822 for (
auto& stack : m_stacks)
825 radius(k) = sqrt(m_area(stack).sum() /
M_PI);
828 for (
int i = 0; i < nlabels;
831 for (
int j = 0; j < nlabels; j++) {
832 D2(i, j) = radius(i) + radius(j);
838 Eigen::ArrayXXi Dist = Eigen::ArrayXXi::Zero(nlabels, nlabels);
839 Dist = (m_radiusFactor * D2 > D1).select(1, Dist);
840 for (
int i = 0; i < nlabels; i++)
846 Eigen::ArrayXXi Nneigh = Eigen::ArrayXXi::Zero(nlabels, nlabels);
848 for (
auto& stack : m_stacks) {
849 Eigen::ArrayXXi labels(stack.size(), m_kNN);
850 for (
int index = 0; index < stack.size(); index++) {
851 for (
int n = 0; n < m_kNN; n++) {
853 m_labels(m_neighborsIndexes(stack[index], n));
856 auto reshaped = labels.reshaped();
857 std::set<int> unique_elements(reshaped.begin(), reshaped.end());
858 for (
auto unique : unique_elements) {
859 Nneigh(k, unique) = 1;
864 Eigen::ArrayXXd A = computeMeanAngleBetweenNormalsAtBorders();
872 if (!checkStacks(m_stacks, m_cloud->
size())) {
878 XXb condition = (Dist < 1) || (Nneigh < 1) || (A > m_maxAngle1) || (A != A);
879 XXb symmetrical_condition =
880 (condition == condition.transpose()).select(condition,
true);
881 symmetrical_condition.count();
882 condition = symmetrical_condition;
884 std::vector<std::vector<int>> newStacks;
885 Eigen::ArrayXi newLabels = Eigen::ArrayXi::Ones(m_labels.size()) * (-1);
886 int countNewLabels = 0;
888 for (
int label = 0; label < nlabels; label++) {
889 if (newLabels(label) == -1)
891 newLabels(label) = countNewLabels;
898 for (
int otherLabel = 0; otherLabel < nlabels; otherLabel++) {
899 if (otherLabel == label) {
904 if (!condition(label, otherLabel)) {
905 std::vector<int>& labelStack = newStacks[newLabels(label)];
907 if (newLabels(otherLabel) !=
910 std::vector<int>& otherLabelStack =
911 newStacks[newLabels(otherLabel)];
912 if (newLabels(label) >
913 newLabels(otherLabel))
916 otherLabelStack.insert(
917 otherLabelStack.end(), labelStack.begin(),
923 newLabels(label) = newLabels(otherLabel);
925 if (newLabels(label) <
926 newLabels(otherLabel))
930 labelStack.end(), otherLabelStack.begin(),
931 otherLabelStack.end());
934 otherLabelStack.clear();
936 newLabels(otherLabel) = newLabels(label);
940 std::vector<int>& otherLabelStack = m_stacks[otherLabel];
943 labelStack.end(), otherLabelStack.begin(),
944 otherLabelStack.end());
947 newLabels(otherLabel) = newLabels(label);
954 std::vector<std::vector<int>> newStacksWithoutEmpty;
955 for (
auto& stack : newStacks) {
956 if (!stack.empty()) {
957 newStacksWithoutEmpty.push_back(stack);
961 newStacks = newStacksWithoutEmpty;
964 "[G3PointAction::merge] keep " + QString::number(newStacks.size()) +
965 "/" + QString::number(m_stacks.size()) +
" labels (" +
966 QString::number(m_stacks.size() - newStacks.size()) +
" removed)");
969 CVLog::Error(
"[G3PointAction::cluster] new stacks are not valid");
977 if (m_stacks.empty()) {
979 "[G3PointAction::fit] stacks are empty, try to rebuild them "
980 "using g3point_label scalar field");
984 "[G3PointAction::fit] no existing g3point_label scalar "
991 "[G3PointAction::fit] not possible to build stacks from "
992 "existing g3point_scalar field");
1000 m_grainsAsEllipsoids =
1002 m_grainsAsEllipsoids->
setName(
"g3point_ellipsoids");
1027 m_cloud->
addChild(m_grainsAsEllipsoids);
1028 m_app->
addToDB(m_grainsAsEllipsoids);
1034 if (m_grainsAsEllipsoids) {
1039 Eigen::VectorXf
arange(
double start,
double stop,
double step) {
1041 double range = (stop - start);
1042 int n_steps =
floor(range / step) + 1;
1043 Eigen::VectorXf vector(n_steps);
1044 for (
int k = 0; k < n_steps; k++) {
1045 vector(k) = start + k * step;
1050 template <
typename T>
1052 std::cout <<
name.toStdString() <<
"\n" << array <<
std::endl;
1060 template <
typename T1,
typename T2>
1061 typename T1::value_type
quant(T1& x,
const T2& q) {
1062 assert(q >= 0.0 && q <= 1.0);
1064 std::sort(x.begin(), x.end());
1065 const auto n = x.size();
1066 const auto id = (n - 1) * q;
1067 const auto lo =
floor(
id);
1068 const auto hi =
ceil(
id);
1069 const auto qs = x[lo];
1070 const auto h = (
id - lo);
1072 return (1.0 - h) * qs + h * x[hi];
1075 template <
typename T>
1079 return std::sqrt((vec - vec.mean()).square().sum() / (vec.size() - 1));
1085 const Eigen::Array3d& dq_final,
1086 const Eigen::Array3d& edq) {
1089 s_g3PointPlots->addToTabWidget(
1091 s_g3PointPlots->show();
1097 if (!m_grainsAsEllipsoids) {
1099 "[G3PointAction::wolman] no ellipsoid, not possible to do "
1105 int nEllipsoids =
static_cast<int>(m_grainsAsEllipsoids->
m_center.size());
1106 Eigen::VectorXf b_axis(nEllipsoids);
1107 Eigen::VectorXi ellipsoidLabels(nEllipsoids);
1108 for (
int i = 0; i < nEllipsoids; i++) {
1109 b_axis(i) = 2 * m_grainsAsEllipsoids->
m_radii[i]
1111 ellipsoidLabels(i) = i;
1116 int n_points = m_grainsAsEllipsoids->
m_cloud->
size();
1117 Eigen::ArrayXf x(n_points);
1118 Eigen::ArrayXf y(n_points);
1119 Eigen::ArrayXf z(n_points);
1120 Eigen::ArrayXi pointsLabels(n_points);
1124 CVLog::Error(
"[G3PointAction::wolman] no g3point_label");
1128 for (
int i = 0; i < n_points; i++) {
1133 pointsLabels(i) = g3point_label->
getValue(i);
1136 float dx = 1.1 * b_axis.maxCoeff();
1138 std::vector<Eigen::ArrayXf> d;
1141 std::vector<int> iter_indices(n_iter);
1142 std::iota(iter_indices.begin(), iter_indices.end(), 0);
1146 pDlg.setMaximum(n_iter);
1150 std::atomic<int> progress_count(0);
1152 QtConcurrent::blockingMap(iter_indices, [&](
int k) {
1153 if (pDlg.wasCanceled())
return;
1156 Eigen::ArrayXf x_grid;
1157 Eigen::ArrayXf y_grid;
1158 Eigen::ArrayXf distances;
1162 static thread_local std::mt19937 urbg(std::random_device{}());
1163 std::uniform_real_distribution<float>
dist{0, 1};
1165 float r0 =
dist(urbg);
1166 float r1 =
dist(urbg);
1168 x_grid =
arange(x.minCoeff() - r0 * dx, x.maxCoeff(), dx);
1169 y_grid =
arange(y.minCoeff() - r1 * dx, y.maxCoeff(), dx);
1170 int nx = x_grid.size();
1171 int ny = y_grid.size();
1172 Eigen::ArrayXXf dist_grid(
1174 Eigen::ArrayXXf iWolman(nx, ny);
1175 for (
int ix = 0; ix < nx; ix++) {
1176 for (
int iy = 0; iy < ny; iy++) {
1177 distances = ((x - x_grid(ix)).pow(2) + (y - y_grid(iy)).pow(2))
1179 dist_grid(ix, iy) = distances.minCoeff(&minLoc);
1180 iWolman(ix, iy) = minLoc;
1184 XXb condition = (dist_grid < dx / 10);
1185 Eigen::ArrayXf iWolmanSelection(condition.count());
1186 int indexInWolmanSelection = 0;
1188 for (
int ix = 0; ix < nx; ix++) {
1189 for (
int iy = 0; iy < ny; iy++) {
1190 if (condition(ix, iy)) {
1191 iWolmanSelection(indexInWolmanSelection) = iWolman(ix, iy);
1192 indexInWolmanSelection++;
1196 Eigen::ArrayXi wolmanSelection;
1197 wolmanSelection = pointsLabels(iWolmanSelection);
1199 std::unordered_set<int> setOfA(wolmanSelection.begin(),
1200 wolmanSelection.end());
1201 std::vector<int> y_ind;
1202 for (
auto label : wolmanSelection) {
1203 if (m_grainsAsEllipsoids->
m_fitNotOK.count(label)) {
1206 y_ind.push_back(label);
1209 Eigen::ArrayXf d_item(y_ind.size());
1210 for (
int i = 0; i < y_ind.size(); i++) {
1211 d_item(i) = b_axis(y_ind[i]) * 1000;
1215 QMutexLocker locker(&mutex);
1216 d.push_back(d_item);
1220 Eigen::ArrayXXf dq(n_iter, 3);
1221 Eigen::ArrayXf d_sample = d[0];
1222 dq(0, Eigen::all) <<
quant(d[0], 0.1),
quant(d[0], 0.5),
quant(d[0], 0.9);
1223 for (
int i = 1; i < n_iter; i++) {
1224 Eigen::ArrayXf tmp(d_sample.size() + d[i].size());
1225 tmp << d_sample, d[i];
1227 dq(i, Eigen::all) <<
quant(d[i], 0.1),
quant(d[i], 0.5),
1234 Eigen::Array3d edq{
std_dev(dq(Eigen::all, 0)),
std_dev(dq(Eigen::all, 1)),
1236 Eigen::Array3d dq_final{
quant(d_sample, 0.1),
quant(d_sample, 0.5),
1237 quant(d_sample, 0.9)};
1241 QString(
"[G3PointAction::wolman] quantl 0.1 0.5 0.9 \n%1 %2 %3")
1247 QString(
"[G3PointAction::wolman] std_dev 0.1 0.5 0.9 \n%1 %2 %3")
1258 if (!m_grainsAsEllipsoids) {
1260 "[G3PointAction::angles] no ellipsoid, not possible to do "
1265 float delta =
static_cast<float>(1e32);
1268 QVector<double> granuloAngleMView(n_ellipsoids);
1269 QVector<double> granuloAngleXView(n_ellipsoids);
1271 for (
int i = 0; i < n_ellipsoids; i++) {
1279 Eigen::Vector3f p1{m_grainsAsEllipsoids->
m_center[i].x(),
1280 m_grainsAsEllipsoids->
m_center[i].y() + delta,
1281 m_grainsAsEllipsoids->
m_center[i].z()};
1282 float angle = atan2(p1.cross(p2).norm(), p1.dot(p2));
1285 if ((angle >
M_PI / 2) || (angle < -
M_PI / 2)) {
1289 granuloAngleMView[i] = (atan(v / u) +
M_PI / 2) * 180 /
M_PI;
1292 p1 << m_grainsAsEllipsoids->
m_center[i].x(),
1293 m_grainsAsEllipsoids->
m_center[i].y(),
1294 m_grainsAsEllipsoids->
m_center[i].z() + delta;
1295 angle = atan2(p1.cross(p2).norm(), p1.dot(p2));
1298 if ((angle >
M_PI / 2) || (angle < -
M_PI / 2)) {
1302 granuloAngleXView[i] = (atan(v / w) +
M_PI / 2) * 180 /
M_PI;
1308 s_g3PointPlots->addToTabWidget(
1310 s_g3PointPlots->addToTabWidget(
1312 s_g3PointPlots->show();
1327 "[cleanLabels] merge points considering the normals at the "
1329 Eigen::ArrayXXd A = computeMeanAngleBetweenNormalsAtBorders();
1330 XXb condition = (A > m_maxAngle2) ||
1333 XXb symmetrical_condition =
1334 (condition == condition.transpose()).select(condition,
true);
1336 merge(symmetrical_condition);
1338 QApplication::processEvents();
1344 Eigen::ArrayXi stackSize(m_stacks.size());
1345 for (
size_t k = 0; k < m_stacks.size(); k++) {
1346 stackSize(k) =
static_cast<int>(m_stacks[k].size());
1348 Xb condition = (stackSize > m_nMin);
1349 size_t numberOfGrainsToKeep = condition.count();
1350 if (numberOfGrainsToKeep == m_stacks.size()) {
1351 CVLog::Print(
"[cleanLabels] all grains are larger than " +
1352 QString::number(m_nMin) +
1353 " points, nothing to remove");
1354 }
else if (numberOfGrainsToKeep) {
1358 "[cleanLabels] no remaining grain after removing those "
1360 QString::number(m_nMin) +
" points");
1363 QApplication::processEvents();
1369 Eigen::ArrayX3d s(m_stacks.size(), 3);
1370 for (
size_t k = 0; k < m_stacks.size(); k++) {
1371 std::vector<int>& stack = m_stacks[k];
1373 size_t nPoints = stack.size();
1374 Eigen::MatrixX3d
points(nPoints, 3);
1375 for (
int index = 0; index < nPoints; index++) {
1382 Eigen::RowVector3d centroid =
points.colwise().mean();
1383 points.rowwise() -= centroid;
1385 s(k, Eigen::all) =
points.jacobiSvd().singularValues();
1390 (s(Eigen::all, 2) / s(Eigen::all, 0) > m_minFlatness) ||
1391 (s(Eigen::all, 1) / s(Eigen::all, 0) > 2. * m_minFlatness);
1392 size_t numberOfGrainsToKeep = condition.count();
1393 if (numberOfGrainsToKeep == m_stacks.size()) {
1394 CVLog::Print(
"[cleanLabels] no flattish grain, nothing to remove");
1395 }
else if (numberOfGrainsToKeep) {
1399 "[cleanLabels] no remaining grain after removing the "
1408 void G3PointAction::addToStackBraunWillett(
int index,
1409 const Eigen::ArrayXi& delta,
1410 const Eigen::ArrayXi& Di,
1411 std::vector<int>& stack,
1412 int local_maximum) {
1413 stack.push_back(index);
1415 for (
int k = delta[index]; k < delta[index + 1]; k++) {
1416 if (Di[k] != local_maximum)
1418 addToStackBraunWillett(Di[k], delta, Di, stack, local_maximum);
1423 int G3PointAction::segmentLabelsBraunWillett() {
1430 Eigen::ArrayXd extreme_slopes;
1431 if (steepestSlope) {
1433 "[segment_labels] classical steepest slope algorithm [Braun, "
1435 extreme_slopes = m_neighborsSlopes.rowwise().maxCoeff();
1438 "[segment_labels] reversed version of the steepest slope "
1439 "algorithm [Braun, Willett 2013]");
1440 extreme_slopes = m_neighborsSlopes.rowwise().minCoeff();
1442 Eigen::ArrayXi index_of_extreme_slope =
1443 Eigen::ArrayXi::Zero(m_cloud->
size());
1444 Eigen::ArrayXi receivers(m_cloud->
size());
1446 for (
unsigned index = 0; index < m_cloud->
size(); index++) {
1447 double extreme_slope = extreme_slopes(index);
1448 for (
int k = 0; k < m_kNN; k++) {
1449 if (m_neighborsSlopes(index, k) == extreme_slope) {
1450 index_of_extreme_slope(index) = k;
1455 m_neighborsIndexes(index, index_of_extreme_slope(index));
1460 if (steepestSlope) {
1461 nb_maxima = (extreme_slopes <= 0).
count();
1463 nb_maxima = (extreme_slopes >= 0).
count();
1465 m_initial_localMaximumIndexes = Eigen::ArrayXi::Zero(
1468 for (
unsigned int k = 0; k < m_cloud->
size(); k++) {
1469 if (steepestSlope) {
1470 if (extreme_slopes(k) <= 0) {
1471 m_initial_localMaximumIndexes(l) = k;
1476 if (extreme_slopes(k) >= 0) {
1477 m_initial_localMaximumIndexes(l) = k;
1487 "[segment_labels_braun_willett] identify the donors for each "
1490 Eigen::ArrayXi::Zero(m_cloud->
size());
1491 std::vector<std::vector<int>> Dij;
1492 for (
unsigned int k = 0; k < m_cloud->
size();
1495 std::vector<int> list_of_donors;
1496 Dij.push_back(list_of_donors);
1498 CVLog::Print(
"[segment_labels_braun_willett] create di and Dij");
1499 for (
unsigned int k = 0; k < m_cloud->
size(); k++) {
1500 int receiver = receivers(k);
1501 di[receiver] = di[receiver] +
1503 Dij[receiver].push_back(
1511 Eigen::ArrayXi::Zero(m_cloud->
size());
1513 for (
auto& list_ : Dij)
1515 for (
int point : list_) {
1522 Eigen::ArrayXi delta = Eigen::ArrayXi::Zero(m_cloud->
size() +
1524 delta[m_cloud->
size()] = m_cloud->
size();
1525 std::vector<int> delta_vec(m_cloud->
size());
1526 for (
int i = m_cloud->
size() - 1; i >= 0; i--) {
1527 delta[i] = delta[i + 1] - di[i];
1528 delta_vec[i] = delta[i];
1532 CVLog::Print(
"[segment_labels_braun_willett] build the stacks");
1540 "[G3Point::segment_labels] impossible to create scalar "
1541 "field g3point_initial_segmentation");
1554 for (
int k = 0; k < m_initial_localMaximumIndexes.size(); k++) {
1555 int localMaximumIndex = m_initial_localMaximumIndexes(k);
1556 std::vector<int> stack;
1557 addToStackBraunWillett(localMaximumIndex, delta, Di, stack,
1560 for (
auto i : stack) {
1561 m_initial_labels(i) = k;
1562 m_initial_labelsnpoint(i) =
static_cast<float>(stack.size());
1563 if (g3point_label) {
1568 m_initialStacks.push_back(stack);
1571 if (!checkStacks(m_initialStacks,
1578 if (g3point_label) {
1594 int nLabels = m_initial_localMaximumIndexes.size();
1599 void G3PointAction::getNeighborsDistancesSlopes(
unsigned index,
1600 std::vector<char>& duplicates) {
1603 m_nNSS.
level = m_bestOctreeLevel;
1604 double maxSquareDist = 0;
1605 int neighborhoodSize = 0;
1609 if (m_octree->findPointNeighbourhood(
1610 P, &Yk,
static_cast<unsigned>(m_kNN + 1), m_bestOctreeLevel,
1612 &neighborhoodSize) >=
static_cast<unsigned>(m_kNN)) {
1613 assert(m_kNN == m_neighborsIndexes.cols());
1614 for (
int k = 0; k < m_kNN; k++) {
1616 m_neighborsIndexes(index, k) = Yk.getPointGlobalIndex(k + 1);
1618 const CCVector3* neighbor = Yk.getPoint(k + 1);
1619 float distance = (*P - *neighbor).norm();
1620 m_neighborsDistances(index, k) = distance;
1622 if (distance != 0) {
1623 m_neighborsSlopes(index, k) = (P->
z - neighbor->
z) / distance;
1625 m_neighborsSlopes(index, k) =
1627 duplicates[index] = 1;
1633 void G3PointAction::computeNodeSurfaces() {
1634 m_area =
M_PI * m_neighborsDistances.rowwise().minCoeff().square();
1637 bool G3PointAction::computeNormalsAndOrientThem() {
1642 ccNormalVectors::Orientation::UNDEFINED;
1644 orientation = ccNormalVectors::Orientation::PLUS_Z;
1647 QScopedPointer<ecvProgressDialog> progressDialog(
nullptr);
1657 float thisCloudRadius = radius;
1658 if (std::isnan(thisCloudRadius)) {
1661 params.aimedPopulationPerCell = 16;
1662 params.aimedPopulationRange = 4;
1663 params.minCellPopulation = 6;
1664 params.minAboveMinRatio = 0.97;
1668 if (thisCloudRadius == 0) {
1670 "Failed to determine best normal radius for cloud " +
1674 " radius = " + QString::number(thisCloudRadius));
1679 model, orientation, thisCloudRadius, progressDialog.data());
1690 void G3PointAction::orientNormals(
const Eigen::Vector3d& sensorCenter) {
1691 int pointCloud = m_cloud->
size();
1693 for (
int i = 0; i < pointCloud; i++) {
1695 Eigen::Vector3d P1 =
1697 Eigen::Vector3d P2 = m_normals(i, Eigen::all);
1698 double angle = atan2(P1.cross(P2).norm(), P1.dot(P2));
1699 if ((angle < -
M_PI / 2) || (angle >
M_PI / 2)) {
1700 m_normals(i, 0) = -m_normals(i, 0);
1701 m_normals(i, 1) = -m_normals(i, 1);
1702 m_normals(i, 2) = -m_normals(i, 2);
1707 bool G3PointAction::findNearestNeighborsNanoFlann(
1708 const unsigned globalIndex,
1713 float query[3] = {Q->
x, Q->
y, Q->
z};
1715 std::vector<size_t> retIndexes(m_kNN);
1716 std::vector<float> outDistsSqr(m_kNN);
1719 nanoflann::KNNResultSet<float> resultSet(m_kNN);
1720 resultSet.init(&retIndexes[0], &outDistsSqr[0]);
1721 if (kdTree->findNeighbors(resultSet, &query[0])) {
1723 for (
int i = 0; i < m_kNN; ++i) {
1724 points->setPointIndex(i, retIndexes[i]);
1732 bool G3PointAction::computeNormWithFlann(
unsigned index,
1737 QScopedPointer<cloudViewer::ReferenceCloud>
points(
1739 if (findNearestNeighborsNanoFlann(index,
points.data(), kdTree)) {
1741 N = *neighbourhood.getLSPlaneNormal();
1751 bool G3PointAction::computeNormals() {
1755 msgBox.setInformativeText(
"Recompute normals?");
1756 msgBox.setText(
"There are existing normals, keep them or recompute.");
1757 QPushButton* keepButton =
1758 msgBox.addButton(tr(
"Keep"), QMessageBox::ActionRole);
1759 QPushButton* recomputeButton =
1760 msgBox.addButton(tr(
"Recompute"), QMessageBox::AcceptRole);
1761 QPushButton* cancelButton =
1762 msgBox.addButton(tr(
"Cancel"), QMessageBox::AcceptRole);
1766 if (msgBox.clickedButton() == keepButton) {
1768 }
else if (msgBox.clickedButton() == cancelButton) {
1773 unsigned pointCount = m_cloud->
size();
1775 if (!m_cloud || m_cloud->
size() == 0) {
1780 CloudAdaptor adaptor(m_cloud);
1784 size_t leaf_max_size = 10;
1785 nanoflann::KDTreeSingleIndexAdaptorFlags flags =
1787 unsigned int n_thread_build = 0;
1790 nanoflann::KDTreeSingleIndexAdaptorParams
params(leaf_max_size, flags,
1792 QSharedPointer<KDTree> m_kdTree(
new KDTree(3, adaptor,
params));
1793 m_kdTree->buildIndex();
1797 QScopedPointer<NormsIndexesTableType> normsIndexes(
1800 if (!theNorms->
resizeSafe(pointCount,
true, &blankN)) {
1801 normsIndexes->resize(0);
1808 for (
unsigned index = 0; index < pointCount; ++index) {
1809 computeNormWithFlann(index, theNorms.data(), m_kNN, m_kdTree.data(),
1813 std::vector<unsigned> pointsIndexes;
1814 pointsIndexes.resize(pointCount);
1815 for (
unsigned i = 0; i < pointCount; ++i) {
1816 pointsIndexes[i] = i;
1820 "[computeNormals] parallel strategy, thread "
1822 QString::number(threadCount));
1823 QThreadPool::globalInstance()->setMaxThreadCount(threadCount);
1824 QtConcurrent::blockingMap(pointsIndexes, [=](
int index) {
1825 computeNormWithFlann(index, theNorms.data(), m_kdTree.data());
1831 CVLog::Error(QString(
"Not enough memory to compute normals on "
1842 for (
unsigned i = 0; i < theNorms->
currentSize(); i++) {
1849 CVLog::Print(
"[computeNormals] orient normals, PLUS_Z ");
1856 bool G3PointAction::queryNeighbors(
ccPointCloud* cloud,
1858 bool useParallelStrategy) {
1863 progressDlg.setAutoClose(
false);
1870 CVLog::Print(QString(
"Computing octree of cloud %1 (%2 points)")
1872 .arg(m_cloud->
size()));
1873 if (progressCb) progressCb->
start();
1874 QCoreApplication::processEvents();
1877 errorStr =
"Failed to compute octree (not enough memory?)";
1882 m_bestOctreeLevel = m_octree->findBestLevelForAGivenPopulationPerCell(
1883 static_cast<unsigned>(
std::max(3, m_kNN)));
1885 size_t nPoints = m_cloud->
size();
1887 std::vector<unsigned> pointsIndexes(nPoints);
1888 std::vector<char> duplicates(nPoints, 0);
1890 if (useParallelStrategy) {
1891 for (
unsigned i = 0; i < nPoints; ++i) {
1892 pointsIndexes[i] = i;
1896 QString(
"[query_neighbor] parallel strategy, thread count %1")
1898 QThreadPool::globalInstance()->setMaxThreadCount(threadCount);
1899 QtConcurrent::blockingMap(pointsIndexes, [&](
int index) {
1900 getNeighborsDistancesSlopes(index, duplicates);
1904 for (
unsigned i = 0; i < nPoints; ++i) {
1905 getNeighborsDistancesSlopes(i, duplicates);
1909 auto trueCount =
std::count(duplicates.begin(), duplicates.end(), 1);
1912 QString::number(trueCount) +
1913 "), the algorithm will continue but you may think of "
1914 "cleaning your cloud.");
1924 bool useParallelStrategy;
1926 useParallelStrategy =
true;
1928 useParallelStrategy =
false;
1930 queryNeighbors(m_cloud, m_app, useParallelStrategy);
1932 computeNodeSurfaces();
1937 unsigned pointCount = m_cloud->
size();
1940 for (
unsigned i = 0; i < pointCount; ++i) {
1947 Eigen::Vector3d sensorCenter(G.
x, G.
y, 1000);
1948 orientNormals(sensorCenter);
1951 int nLabels = segmentLabelsBraunWillett();
1952 if (nLabels == -1) {
1954 "[G3Point::segment] initial segmentation failed, abort "
1959 exportLocalMaximaAsCloud(m_initial_localMaximumIndexes);
1962 QString::number(nLabels) +
" labels",
1969 m_labels = m_initial_labels;
1970 m_labelsnpoint = m_initial_labelsnpoint;
1971 m_localMaximumIndexes = m_initial_localMaximumIndexes;
1972 m_stacks = m_initialStacks;
1978 m_labels = m_initial_labels;
1979 m_labelsnpoint = m_initial_labelsnpoint;
1980 m_localMaximumIndexes = m_initial_localMaximumIndexes;
1981 m_stacks = m_initialStacks;
1986 "[G3PointAction::clusterAndOrClean] clustering failed");
1993 CVLog::Error(
"[G3PointAction::clusterAndOrClean] cleaning failed");
2002 Eigen::ArrayXXi duplicatedLabelsInColumns(m_cloud->
size(), m_kNN);
2003 for (
int n = 0; n < m_kNN; n++) {
2004 duplicatedLabelsInColumns(Eigen::all, n) = m_labels;
2006 Eigen::ArrayXXi labelsOfNeighbors(m_cloud->
size(), m_kNN);
2007 for (
int index = 0; index < static_cast<float>(m_cloud->
size()); index++) {
2008 for (
int n = 0; n < m_kNN; n++) {
2009 labelsOfNeighbors(index, n) =
2010 m_labels(m_neighborsIndexes(index, n));
2014 Eigen::ArrayXi temp =
2015 m_kNN - (labelsOfNeighbors == duplicatedLabelsInColumns)
2019 auto condition = ((temp >= m_kNN / 4) && (m_ndon == 0));
2023 for (
int index = 0; index < condition.size(); index++) {
2024 if (condition(index)) {
2038 void G3PointAction::init() {
2040 m_neighborsIndexes = Eigen::ArrayXXi::Zero(m_cloud->
size(), m_kNN);
2041 m_neighborsDistances = Eigen::ArrayXXd::Zero(m_cloud->
size(), m_kNN);
2042 m_neighborsSlopes = Eigen::ArrayXXd::Zero(m_cloud->
size(), m_kNN);
2043 m_normals = Eigen::ArrayXXd::Zero(m_cloud->
size(), 3);
2045 m_labels = Eigen::ArrayXi::Zero(m_cloud->
size());
2046 m_labelsnpoint = Eigen::ArrayXi::Zero(m_cloud->
size());
2047 m_initial_labels = Eigen::ArrayXi::Zero(m_cloud->
size());
2048 m_initial_labelsnpoint = Eigen::ArrayXi::Zero(m_cloud->
size());
2051 m_initialStacks.clear();
2054 void G3PointAction::showDlg() {
2059 s_g3PointAction.get(), &G3PointAction::setKNN);
2076 connect(m_dlg, &QDialog::finished, s_g3PointAction.get(),
2078 connect(m_dlg, &QDialog::finished, s_g3PointAction.get(),
2079 &G3Point::G3PointAction::resetDlg);
2088 void G3PointAction::resetDlg() {
2095 m_neighborsIndexes.resize(0, 0);
2096 m_neighborsDistances.resize(0, 0);
2097 m_neighborsSlopes.resize(0, 0);
2098 m_normals.resize(0, 0);
2101 m_labelsnpoint.resize(0);
2102 m_localMaximumIndexes.resize(0);
2124 if (sfIdxBackup == -1)
2128 if (sfIdxBackup == -1) {
2130 "[G3PointAction::setCloud] impossible to create scalar "
2131 "field g3point_label_backup");
2135 "[G3PointAction::setCloud] duplicate existing scalar "
2136 "field g3point_label => g3point_label_backup");
2146 std::set<ScalarType> labelsSet;
2147 for (
unsigned int index = 0; index < m_cloud->
size(); index++) {
2148 labelsSet.insert(g3PointLabelSF->
getValue(index));
2151 labelsSet.erase(-1);
2154 std::map<ScalarType, int> labelsMap;
2156 for (
auto label : labelsSet) {
2157 labelsMap[label] = index;
2162 int nPointsInGrains = 0;
2165 pDlg.setMethodTitle(
"Processing g3point_label");
2166 pDlg.setInfo(
"Classifying points...");
2167 pDlg.setMaximum(m_cloud->
size());
2169 QApplication::processEvents();
2171 std::vector<std::vector<int>> newStacks(labelsSet.size());
2175 unsigned updateStep = std::max<unsigned>(10000, m_cloud->
size() / 100);
2177 for (
unsigned int index = 0; index < m_cloud->
size(); index++) {
2178 ScalarType label = g3PointLabelSF->
getValue(index);
2184 newStacks[labelsMap[label]].push_back(index);
2188 if (index % updateStep == 0) {
2189 if (pDlg.wasCanceled()) {
2192 pDlg.setValue(index);
2197 "[G3PointAction::setCloud] g3point_label available, found " +
2198 QString::number(nPointsInGrains) +
"/" +
2199 QString::number(cloud->
size()) +
" points belonging to " +
2200 QString::number(newStacks.size()) +
" grains");
2208 void G3PointAction::setKNN() { m_kNN = m_dlg->
getkNN(); }
2211 if (appInterface ==
nullptr) {
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
void enableClusterAndOrClean(bool state)
void onlyOneClicked(bool state)
void drawPoints(bool state)
void kNNEditingFinished()
void onlyOneChanged(int idx)
void allClicked(bool state)
void setOnlyOneMax(int idx)
void drawLines(bool state)
void transparencyChanged(double transparency)
void glPointSize(int size)
void drawSurfaces(bool state)
static bool show(ecvMainAppInterface *app)
nanoflann::KDTreeSingleIndexAdaptor< nanoflann::L2_Simple_Adaptor< float, CloudAdaptor >, CloudAdaptor, 3 > KDTree
static bool EigenArrayToFile(QString name, T array)
bool buildStacksFromG3PointLabelSF(cloudViewer::ScalarField *g3PointLabel)
void showWolman(const Eigen::ArrayXf &d_sample, const Eigen::Array3d &dq_final, const Eigen::Array3d &edq)
bool merge(XXb &condition)
G3PointAction(ccPointCloud *cloud, ecvMainAppInterface *app=nullptr)
static void GetG3PointAction(ccPointCloud *cloud, ecvMainAppInterface *app=nullptr)
bool processNewStacks(std::vector< std::vector< int >> &newStacks, int pointCount)
static void createAction(ecvMainAppInterface *appInterface)
std::vector< Eigen::Array3f > m_center
std::vector< Eigen::Array3f > m_radii
void showOnlyOne(bool state)
void drawPoints(bool state)
void setGLPointSize(int size)
void drawSurfaces(bool state)
void drawLines(bool state)
std::set< int > m_fitNotOK
void setTransparency(double transparency)
bool exportResultsAsCloud()
std::vector< Eigen::Matrix3f > m_rotationMatrix
Array of compressed 3D normals (single index)
Array of (uncompressed) 3D normals (Nx,Ny,Nz)
Array of RGBA colors for each point.
Type & getValue(size_t index)
bool resizeSafe(size_t count, bool initNewElements=false, const Type *valueForNewElements=nullptr)
Resizes memory (no exception thrown)
bool reserveSafe(size_t count)
Reserves memory (no exception thrown)
void setValue(size_t index, const Type &value)
unsigned currentSize() const
void addElement(const Type &value)
virtual void showNormals(bool state)
Sets normals visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
unsigned getChildrenNumber() const
Returns the number of children.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
virtual void redrawDisplay(bool forceRedraw=true, bool only2D=false)
Redraws associated display.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
static bool UpdateNormalOrientations(ccGenericPointCloud *theCloud, NormsIndexesTableType &theNormsCodes, Orientation preferredOrientation)
Updates normals orientation based on a preferred orientation.
Orientation
'Default' orientations
@ PLUS_Z
N.z always positive.
static CompressedNormType GetNormIndex(const PointCoordinateType N[])
Returns the compressed index corresponding to a normal vector.
virtual QString getName() const
Returns object name.
bool isA(CV_CLASS_ENUM type) const
virtual void setName(const QString &name)
Sets object name.
virtual void setEnabled(bool state)
Sets the "enabled" property.
static PointCoordinateType GuessBestRadius(ccGenericPointCloud *cloud, const BestRadiusParams ¶ms, cloudViewer::DgmOctree *cloudOctree=nullptr, cloudViewer::GenericProgressCallback *progressCb=nullptr)
Tries to guess the best 'local radius' for octree-based computation.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
bool resizeTheNormsTable()
Resizes the compressed normals array.
void setPointNormalIndex(size_t pointIndex, CompressedNormType norm)
Sets a particular point compressed normal.
NormsIndexesTableType * normals() const
Returns pointer on compressed normals indexes table.
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool hasNormals() const override
Returns whether normals are enabled or not.
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
bool computeNormalsWithOctree(CV_LOCAL_MODEL_TYPES model, ccNormalVectors::Orientation preferredOrientation, PointCoordinateType defaultRadius, ecvProgressDialog *pDlg=nullptr)
Compute the normals by approximating the local surface around each point.
void deleteScalarField(int index) override
Deletes a specific scalar field.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
static int GetMaxThreadCount()
Returns the ideal number of threads/cores with Qt Concurrent.
A scalar field associated to display-related parameters.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
const CCVector3 * getPoint(unsigned index) const override
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
A simple scalar field (to be associated to a point cloud)
virtual void computeMinAndMax()
Determines the min and max values.
ScalarType getMin() const
Returns the minimum value.
ScalarType & getValue(std::size_t index)
void setValue(std::size_t index, ScalarType value)
unsigned currentSize() const
ScalarType getMax() const
Returns the maximum value.
Main application interface (for plugins)
virtual void updateUI()=0
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
virtual const ccHObject::Container & getSelectedEntities() const =0
Returns currently selected entities ("read only")
bool haveOneSelection() const
Checks if we have exactly one selection.
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
virtual void removeFromDB(ccHObject *obj, bool autoDelete=true)=0
Removes an entity from main db tree.
Graphical progress indicator (thread-safe)
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
unsigned int CompressedNormType
Compressed normals type.
unsigned char ColorCompType
Default color components type (R,G and B)
static double dist(double x1, double y1, double x2, double y2)
T1::value_type quant(T1 &x, const T2 &q)
double std_dev(const T &vec)
Eigen::VectorXf arange(double start, double stop, double step)
void myPrint(QString name, T array)
RGBAColorsTableType getRandomColors(size_t randomColorsNumber)
QTextStream & endl(QTextStream &stream)
constexpr utility::nullopt_t None
MiniVec< float, N > floor(const MiniVec< float, N > &a)
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
flann::Index< flann::L2< float > > KDTree
Parameters for the GuessBestRadius method.