ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qRANSAC_SD.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "qRANSAC_SD.h"
9 
10 #include "qRANSAC_SD_Commands.h"
11 
12 // PrimitiveShapes/MiscLib
13 #include <ConePrimitiveShape.h>
14 #include <ConePrimitiveShapeConstructor.h>
15 #include <CylinderPrimitiveShape.h>
16 #include <CylinderPrimitiveShapeConstructor.h>
17 #include <PlanePrimitiveShape.h>
18 #include <PlanePrimitiveShapeConstructor.h>
19 #include <RansacShapeDetector.h>
20 #include <SpherePrimitiveShape.h>
21 #include <SpherePrimitiveShapeConstructor.h>
22 #include <TorusPrimitiveShape.h>
23 #include <TorusPrimitiveShapeConstructor.h>
24 
25 // Dialog
26 #include "ccRansacSDDlg.h"
27 
28 // Qt
29 #include <QApplication>
30 #include <QMainWindow>
31 #include <QProgressDialog>
32 #include <QtConcurrentRun>
33 #include <QtGui>
34 
35 // CV_CORE_LIB
36 #include <CVPlatform.h>
37 #include <ScalarField.h>
38 
39 // CV_DB_LIB
40 #include <ecvCone.h>
41 #include <ecvCylinder.h>
42 #include <ecvGenericMesh.h>
43 #include <ecvGenericPointCloud.h>
44 #include <ecvPlane.h>
45 #include <ecvPointCloud.h>
46 #include <ecvSphere.h>
47 #include <ecvTorus.h>
48 
49 // System
50 #include <algorithm>
51 #if defined(CV_WINDOWS)
52 #include "windows.h"
53 #else
54 #include <time.h>
55 #endif
56 
57 static ecvMainAppInterface* s_app = nullptr;
58 
59 qRansacSD::qRansacSD(QObject* parent /*=nullptr*/)
60  : QObject(parent),
61  ccStdPluginInterface(":/CC/plugin/qRANSAC_SD/info.json"),
62  m_action(nullptr) {
63  s_app = m_app;
64 }
65 
66 void qRansacSD::onNewSelection(const ccHObject::Container& selectedEntities) {
67  if (m_action)
68  m_action->setEnabled(selectedEntities.size() == 1 &&
69  selectedEntities[0]->isA(CV_TYPES::POINT_CLOUD));
70 }
71 
72 QList<QAction*> qRansacSD::getActions() {
73  // default action
74  if (!m_action) {
75  m_action = new QAction(getName(), this);
76  m_action->setToolTip(getDescription());
77  m_action->setIcon(getIcon());
78  // connect signal
79  connect(m_action, &QAction::triggered, this, &qRansacSD::doAction);
80  }
81 
82  return QList<QAction*>{m_action};
83 }
84 
85 void qRansacSD::registerCommands(ccCommandLineInterface* cmd) {
86  if (!cmd) {
87  assert(false);
88  return;
89  }
90  cmd->registerCommand(
92 }
93 
94 static MiscLib::Vector<std::pair<MiscLib::RefCountPtr<PrimitiveShape>, size_t>>*
95  s_shapes; // stores the detected shapes
96 static size_t s_remainingPoints = 0;
97 static RansacShapeDetector* s_detector = nullptr;
98 static ransac::RansacPointCloud* s_cloud = nullptr;
99 void doDetection() {
100  if (!s_detector || !s_cloud || !s_shapes) return;
101 
103  s_detector->Detect(*s_cloud, 0, s_cloud->size(), s_shapes);
104 }
105 
106 // for parameters persistence
107 static unsigned s_supportPoints =
108  500; // this is the minimal numer of points required for a primitive
109 static double s_maxNormalDev_deg =
110  25.0; // maximal normal deviation from ideal shape (in degrees)
111 static double s_proba = 0.75; // default : 0.01, probability that no better
112  // candidate was overlooked during sampling
113 static bool s_primEnabled[5] = {true, true, true, false, false};
114 
115 static float s_minRadius =
116  0.001f; // minimum radius for sphere, cylinder, cone, torus
117 static float s_maxRadius =
118  0.5f; // maximum radius for sphere, cylinder, cone, torus
119 static bool s_randomColor = true;
120 
122  assert(m_app);
123  if (!m_app) return;
124 
125  const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
126  size_t selNum = selectedEntities.size();
127  if (selNum != 1) {
128  CVLog::Error("[qRansacSD] Select only one cloud!");
129  return;
130  }
131 
132  ccHObject* ent = selectedEntities[0];
133  assert(ent);
134  if (!ent || !ent->isA(CV_TYPES::POINT_CLOUD)) {
135  CVLog::Error("[qRansacSD] Select a real point cloud!");
136  return;
137  }
138 
139  ccPointCloud* pc = static_cast<ccPointCloud*>(ent);
140 
141  // input cloud
142  CCVector3 bbMin, bbMax;
143  pc->getBoundingBox(bbMin, bbMax);
144  CCVector3 diff = bbMax - bbMin;
145  float scale = std::max(std::max(diff[0], diff[1]), diff[2]);
146 
147  // init dialog with default values
149  rsdDlg.epsilonDoubleSpinBox->setValue(
150  .005f *
151  scale); // set distance threshold to 0.5% of bounding box width
152  rsdDlg.bitmapEpsilonDoubleSpinBox->setValue(
153  .01f * scale); // set bitmap resolution (= sampling resolution) to
154  // 1% of bounding box width
155  rsdDlg.supportPointsSpinBox->setValue(s_supportPoints);
156  rsdDlg.minRadiusSpinBox->setValue(s_minRadius);
157  rsdDlg.maxRadiusSpinBox->setValue(s_maxRadius);
158  rsdDlg.randomColorCheckBox->setChecked(s_randomColor);
159  rsdDlg.maxNormDevAngleSpinBox->setValue(s_maxNormalDev_deg);
160  rsdDlg.probaDoubleSpinBox->setValue(s_proba);
161  rsdDlg.planeCheckBox->setChecked(s_primEnabled[0]);
162  rsdDlg.sphereCheckBox->setChecked(s_primEnabled[1]);
163  rsdDlg.cylinderCheckBox->setChecked(s_primEnabled[2]);
164  rsdDlg.coneCheckBox->setChecked(s_primEnabled[3]);
165  rsdDlg.torusCheckBox->setChecked(s_primEnabled[4]);
166 
167  if (!rsdDlg.exec()) return;
168 
169  RansacParams params;
170  {
171  params.epsilon =
172  static_cast<float>(rsdDlg.epsilonDoubleSpinBox->value());
173  params.bitmapEpsilon =
174  static_cast<float>(rsdDlg.bitmapEpsilonDoubleSpinBox->value());
176  static_cast<float>(rsdDlg.maxNormDevAngleSpinBox->value());
177  params.maxNormalDev_deg = s_maxNormalDev_deg;
178  s_proba = static_cast<float>(rsdDlg.probaDoubleSpinBox->value());
179  params.probability = s_proba;
180  s_randomColor = rsdDlg.randomColorCheckBox->isChecked();
181  params.randomColor = s_randomColor;
183  static_cast<unsigned>(rsdDlg.supportPointsSpinBox->value());
184  params.supportPoints = s_supportPoints;
185  params.primEnabled[RPT_PLANE] = rsdDlg.planeCheckBox->isChecked();
186  params.primEnabled[RPT_SPHERE] = rsdDlg.sphereCheckBox->isChecked();
187  params.primEnabled[RPT_CYLINDER] = rsdDlg.cylinderCheckBox->isChecked();
188  params.primEnabled[RPT_CONE] = rsdDlg.coneCheckBox->isChecked();
189  params.primEnabled[RPT_TORUS] = rsdDlg.torusCheckBox->isChecked();
190 
191  s_minRadius = static_cast<float>(rsdDlg.minRadiusSpinBox->value());
192  s_maxRadius = static_cast<float>(rsdDlg.maxRadiusSpinBox->value());
193  params.minRadius = s_minRadius;
194  params.maxRadius = s_maxRadius;
195  }
196 
197  ccHObject* group = executeRANSAC(pc, params, false);
198 
199  if (group) {
200  m_app->addToDB(group);
201  }
202 }
203 
204 ccHObject* qRansacSD::executeRANSAC(ccPointCloud* ccPC,
205  const RansacParams& params,
206  bool silent) {
207  // consistency check
208  {
209  unsigned char primCount = 0;
210  for (unsigned char k = 0; k < 5; ++k) {
211  primCount += static_cast<unsigned>(params.primEnabled[k]);
212  }
213  if (primCount == 0) {
214  CVLog::Error("[qRansacSD] No primitive type selected!");
215  return nullptr;
216  }
217  }
218  for (unsigned char k = 0; k < 5; ++k) {
219  s_primEnabled[k] = params.primEnabled[k];
220  }
221  s_supportPoints = params.supportPoints;
222  s_maxNormalDev_deg = params.maxNormalDev_deg;
223  s_proba = params.probability;
224  unsigned count = ccPC->size();
225  bool hasNorms = ccPC->hasNormals();
226  CCVector3 bbMin, bbMax;
227  ccPC->getBoundingBox(bbMin, bbMax);
228  const CCVector3d& globalShift = ccPC->getGlobalShift();
229  double globalScale = ccPC->getGlobalScale();
230  ransac::RansacPointCloud cloud;
231  {
232  try {
233  cloud.reserve(count);
234  } catch (...) {
235  CVLog::Error(
236  "[qRansacSD] Could not create temporary cloud, Not enough "
237  "memory!");
238  return nullptr;
239  }
240 
241  // default point & normal
242  ransac::RansacPoint Pt;
243  Pt.normal[0] = 0.0;
244  Pt.normal[1] = 0.0;
245  Pt.normal[2] = 0.0;
246  for (unsigned i = 0; i < count; ++i) {
247  const CCVector3* P = ccPC->getPoint(i);
248  Pt.pos[0] = static_cast<float>(P->x);
249  Pt.pos[1] = static_cast<float>(P->y);
250  Pt.pos[2] = static_cast<float>(P->z);
251  if (hasNorms) {
252  const CCVector3& N = ccPC->getPointNormal(i);
253  Pt.normal[0] = static_cast<float>(N.x);
254  Pt.normal[1] = static_cast<float>(N.y);
255  Pt.normal[2] = static_cast<float>(N.z);
256  }
257 #ifdef POINTSWITHINDEX
258  Pt.index = i;
259 #endif
260  cloud.push_back(Pt);
261  }
262 
263  // manually set bounding box!
264  Vec3f cbbMin, cbbMax;
265  cbbMin[0] = static_cast<float>(bbMin.x);
266  cbbMin[1] = static_cast<float>(bbMin.y);
267  cbbMin[2] = static_cast<float>(bbMin.z);
268  cbbMax[0] = static_cast<float>(bbMax.x);
269  cbbMax[1] = static_cast<float>(bbMax.y);
270  cbbMax[2] = static_cast<float>(bbMax.z);
271  cloud.setBBox(cbbMin, cbbMax);
272  }
273 
274  RansacShapeDetector::Options ransacOptions;
275  {
276  ransacOptions.m_epsilon = params.epsilon;
277  ransacOptions.m_bitmapEpsilon = params.bitmapEpsilon;
278  ransacOptions.m_normalThresh = static_cast<float>(
279  cos(cloudViewer::DegreesToRadians(params.maxNormalDev_deg)));
280  assert(ransacOptions.m_normalThresh >= 0);
281  ransacOptions.m_probability = params.probability;
282  ransacOptions.m_minSupport = params.supportPoints;
283  }
284  const float scale = cloud.getScale();
285 
286  if (!hasNorms) {
287  QProgressDialog* pDlg = nullptr;
288  if (!silent) {
289  if (s_app) {
290  pDlg = new QProgressDialog("Computing normals (please wait)",
291  QString(), 0, 0,
292  s_app->getMainWindow());
293  } else {
294  pDlg = new QProgressDialog("Computing normals (please wait)",
295  QString(), 0, 0, nullptr);
296  }
297  pDlg->setWindowTitle("Ransac Shape Detection");
298  pDlg->show();
299  }
300  QApplication::processEvents();
301 
302  cloud.calcNormals(.01f * scale);
303 
304  if (ccPC->reserveTheNormsTable()) {
305  for (unsigned i = 0; i < count; ++i) {
306  Vec3f& Nvi = cloud[i].normal;
308  // normalize the vector in case of
309  Ni.normalize();
310  ccPC->addNorm(Ni);
311  }
312  ccPC->showNormals(true);
313 
314  // currently selected entities appearance may have changed!
315  ccPC->setRedrawFlagRecursive(true);
316  } else {
317  CVLog::Error("[qRansacSD] Not enough memory to compute normals!");
318  if (pDlg) {
319  pDlg->hide();
320  delete pDlg;
321  }
322  return nullptr;
323  }
324  if (pDlg) {
325  pDlg->hide();
326  delete pDlg;
327  }
328  }
329 
330  RansacShapeDetector detector(ransacOptions); // the detector object
331 
332  if (params.primEnabled[RPT_PLANE])
333  detector.Add(new PlanePrimitiveShapeConstructor());
334  if (params.primEnabled[RPT_SPHERE])
335  detector.Add(new SpherePrimitiveShapeConstructor());
336  if (params.primEnabled[RPT_CYLINDER])
337  detector.Add(new CylinderPrimitiveShapeConstructor());
338  if (params.primEnabled[RPT_CONE])
339  detector.Add(new ConePrimitiveShapeConstructor());
340  if (params.primEnabled[RPT_TORUS])
341  detector.Add(new TorusPrimitiveShapeConstructor());
342 
343  unsigned remaining = count;
344  typedef std::pair<MiscLib::RefCountPtr<PrimitiveShape>, size_t>
345  DetectedShape;
346  MiscLib::Vector<DetectedShape> shapes; // stores the detected shapes
347 
348  // run detection
349  // returns number of unassigned points
350  // the array shapes is filled with pointers to the detected shapes
351  // the second element per shapes gives the number of points assigned to that
352  // primitive (the support) the points belonging to the first shape
353  // (shapes[0]) have been sorted to the end of pc, i.e. into the range [
354  // pc.size() - shapes[0].second, pc.size() ) the points of shape i are found
355  // in the range [ pc.size() - \sum_{j=0..i} shapes[j].second, pc.size() -
356  // \sum_{j=0..i-1} shapes[j].second )
357 
358  {
359  // progress dialog (Qtconcurrent::run can't be canceled!)
360  QProgressDialog* pDlg = nullptr;
361  if (!silent) {
362  if (s_app) {
363  pDlg = new QProgressDialog(
364  "Operation in progress (please wait)", QString(), 0, 0,
365  s_app->getMainWindow());
366  } else {
367  pDlg = new QProgressDialog(
368  "Operation in progress (please wait)", QString(), 0, 0,
369  nullptr);
370  }
371  pDlg->setWindowTitle("Ransac Shape Detection");
372  pDlg->show();
373  }
374 
375  // run in a separate thread
376  s_detector = &detector;
377  s_shapes = &shapes;
378  s_cloud = &cloud;
379  QFuture<void> future = QtConcurrent::run(doDetection);
380 
381  while (!future.isFinished()) {
382 #if defined(CV_WINDOWS)
383  ::Sleep(500);
384 #else
385  usleep(500 * 1000);
386 #endif
387  if (!silent && pDlg) {
388  pDlg->setValue(pDlg->value() + 1);
389  }
390  QApplication::processEvents();
391  }
392  remaining = static_cast<unsigned>(s_remainingPoints);
393 
394  QApplication::processEvents();
395  if (pDlg) {
396  pDlg->hide();
397  delete pDlg;
398  }
399  }
400 
401 #if 0 // def _DEBUG
402  FILE* fp = fopen("RANS_SD_trace.txt", "wt");
403 
404  fprintf(fp, "[Options]\n");
405  fprintf(fp, "epsilon=%f\n", ransacOptions.m_epsilon);
406  fprintf(fp, "bitmap epsilon=%f\n", ransacOptions.m_bitmapEpsilon);
407  fprintf(fp, "normal thresh=%f\n", ransacOptions.m_normalThresh);
408  fprintf(fp, "min support=%i\n", ransacOptions.m_minSupport);
409  fprintf(fp, "probability=%f\n", ransacOptions.m_probability);
410 
411  fprintf(fp, "\n[Statistics]\n");
412  fprintf(fp, "input points=%i\n", count);
413  fprintf(fp, "segmented=%i\n", count - remaining);
414  fprintf(fp, "remaining=%i\n", remaining);
415 
416  if (shapes.size() > 0)
417  {
418  fprintf(fp, "\n[Shapes]\n");
419  for (unsigned i = 0; i < shapes.size(); ++i)
420  {
421  PrimitiveShape* shape = shapes[i].first;
422  size_t shapePointsCount = shapes[i].second;
423 
424  std::string desc;
425  shape->Description(&desc);
426  fprintf(fp, "#%i - %s - %i points\n", i + 1, desc.c_str(), shapePointsCount);
427  }
428  }
429  fclose(fp);
430 #endif
431 
432  if (remaining == count) {
433  CVLog::Error("[qRansacSD] Segmentation failed...");
434  return nullptr;
435  }
436 
437  if (shapes.size() > 0) {
438  unsigned planeCount = 1;
439  unsigned sphereCount = 1;
440  unsigned cylinderCount = 1;
441  unsigned coneCount = 1;
442  unsigned torusCount = 1;
443  ccHObject* group = nullptr;
444  for (MiscLib::Vector<DetectedShape>::const_iterator it = shapes.begin();
445  it != shapes.end(); ++it) {
446  const PrimitiveShape* shape = it->first;
447  unsigned shapePointsCount = static_cast<unsigned>(it->second);
448 
449  // too many points?!
450  if (shapePointsCount > count) {
451  CVLog::Error("[qRansacSD] Inconsistent result!");
452  break;
453  }
454 
455  if (shapePointsCount < params.supportPoints) {
457  "[qRansacSD] Skipping shape, %d did not meet minimum "
458  "point requirement",
459  shapePointsCount);
460  count -= shapePointsCount;
461  continue;
462  }
463 
464  std::string desc;
465  shape->Description(&desc);
466 
467  // points to current shapes last point in cloud
468  const auto shapeCloudIndex = count - 1;
469 
470  // new cloud for sub-part
471  ccPointCloud* pcShape = nullptr;
472  bool saveNormals = true;
473  {
474 #ifdef POINTSWITHINDEX
475  cloudViewer::ReferenceCloud refPcShape(ccPC);
476  // we fill cloud with sub-part points
477  if (!refPcShape.reserve(
478  static_cast<unsigned>(shapePointsCount))) {
479  CVLog::Error("[qRansacSD] Not enough memory!");
480  break;
481  }
482 
483  for (unsigned j = 0; j < shapePointsCount; ++j) {
484  refPcShape.addPointIndex(static_cast<unsigned int>(
485  cloud[shapeCloudIndex - j].index));
486  }
487  int warnings = 0;
488  pcShape = ccPC->partialClone(&refPcShape, &warnings);
489  if (warnings != 0) {
490  if ((warnings & ccPointCloud::WRN_OUT_OF_MEM_FOR_NORMALS) ==
492  saveNormals = false;
493  }
494  }
495 
496 #else
497  pcShape = new ccPointCloud(desc.c_str());
498  if (!pcShape->reserve(
499  static_cast<unsigned>(shapePointsCount))) {
500  CVLog::Error("[qRansacSD] Not enough memory!");
501  delete pcShape;
502  break;
503  }
504  saveNormals = pcShape->reserveTheNormsTable();
505 
506  for (unsigned j = 0; j < shapePointsCount; ++j) {
508  cloud[shapeCloudIndex - j].pos));
509  if (saveNormals) {
510  pcShape->addNorm(CCVector3::fromArray(
511  cloud[shapeCloudIndex - j].normal));
512  }
513  }
514  pcShape->setGlobalShift(globalShift);
515  pcShape->setGlobalScale(globalScale);
516 #endif
517  }
518  // random color
520  if (params.randomColor) {
521  pcShape->setRGBColor(col);
522  pcShape->showSF(false);
523  pcShape->showColors(true);
524  }
525  pcShape->showNormals(saveNormals);
526  pcShape->setVisible(true);
527 
528  // convert detected primitive into a CC primitive type
529  ccGenericPrimitive* prim = nullptr;
530  switch (shape->Identifier()) {
531  case RPT_PLANE: // plane
532  {
533  const PlanePrimitiveShape* plane =
534  static_cast<const PlanePrimitiveShape*>(shape);
535  Vec3f G = plane->Internal().getPosition();
536  Vec3f N = plane->Internal().getNormal();
537  Vec3f X = plane->getXDim();
538  Vec3f Y = plane->getYDim();
539 
540  // we look for real plane extents
541  float minX, maxX, minY, maxY;
542  for (unsigned j = 0; j < shapePointsCount; ++j) {
543  std::pair<float, float> param;
544  plane->Parameters(cloud[shapeCloudIndex - j].pos,
545  &param);
546  if (j != 0) {
547  if (minX < param.first)
548  minX = param.first;
549  else if (maxX > param.first)
550  maxX = param.first;
551  if (minY < param.second)
552  minY = param.second;
553  else if (maxY > param.second)
554  maxY = param.second;
555  } else {
556  minX = maxX = param.first;
557  minY = maxY = param.second;
558  }
559  }
560 
561  // we recenter plane (as it is not always the case!)
562  float dX = maxX - minX;
563  float dY = maxY - minY;
564  G += X * (minX + dX / 2);
565  G += Y * (minY + dY / 2);
566 
567  // we build matrix from these vectors
568  ccGLMatrix glMat(CCVector3::fromArray(X.getValue()),
569  CCVector3::fromArray(Y.getValue()),
570  CCVector3::fromArray(N.getValue()),
571  CCVector3::fromArray(G.getValue()));
572 
573  // plane primitive
574  // CVLog::Print(QString("dX: %1, dY: %2").arg(dX).arg(dY));
575  prim = new ccPlane(std::abs(dX), std::abs(dY), &glMat);
577  prim->enableStippling(true);
578  PointCoordinateType dip = 0.0f;
579  PointCoordinateType dipDir = 0.0f;
581  CCVector3::fromArray(N.getValue()), dip, dipDir);
582  QString dipAndDipDirStr =
584  dip, dipDir);
585  prim->setName(dipAndDipDirStr);
586  pcShape->setName(
587  QString("Plane_%1")
588  .arg(planeCount, 4, 10, QChar('0')));
589  planeCount++;
590  } break;
591 
592  case RPT_SPHERE: // sphere
593  {
594  const SpherePrimitiveShape* sphere =
595  static_cast<const SpherePrimitiveShape*>(shape);
596  float radius = sphere->Internal().Radius();
597  if (radius < params.minRadius ||
598  radius > params.maxRadius) {
599  count -= shapePointsCount;
600  delete pcShape;
601  continue;
602  }
603 
604  Vec3f CC = sphere->Internal().Center();
605 
606  // we build matrix from these vectors
607  ccGLMatrix glMat;
608  glMat.setTranslation(CC.getValue());
609  // sphere primitive
610  prim = new ccSphere(radius, &glMat);
611  prim->setEnabled(false);
612  prim->setName(QString("Sphere (r=%1)").arg(radius, 0, 'f'));
613  pcShape->setName(
614  QString("Sphere_%1")
615  .arg(sphereCount, 4, 10, QChar('0')));
616  sphereCount++;
617  } break;
618 
619  case RPT_CYLINDER: // cylinder
620  {
621  const CylinderPrimitiveShape* cyl =
622  static_cast<const CylinderPrimitiveShape*>(shape);
623  float radius = cyl->Internal().Radius();
624  if (radius < params.minRadius ||
625  radius > params.maxRadius) {
626  count -= shapePointsCount;
627  delete pcShape;
628  continue;
629  }
630 
631  Vec3f G = cyl->Internal().AxisPosition();
632  Vec3f N = cyl->Internal().AxisDirection();
633  Vec3f X = cyl->Internal().AngularDirection();
634  Vec3f Y = N.cross(X);
635 
636  float hMin = cyl->MinHeight();
637  float hMax = cyl->MaxHeight();
638  float h = hMax - hMin;
639  G += N * (hMin + h / 2);
640 
641  // we build matrix from these vectors
642  ccGLMatrix glMat(CCVector3::fromArray(X.getValue()),
643  CCVector3::fromArray(Y.getValue()),
644  CCVector3::fromArray(N.getValue()),
645  CCVector3::fromArray(G.getValue()));
646 
647  // cylinder primitive
648  prim = new ccCylinder(radius, h, &glMat);
649  prim->setEnabled(false);
650  prim->setName(QString("Cylinder (r=%1/h=%2)")
651  .arg(radius, 0, 'f')
652  .arg(h, 0, 'f'));
653  pcShape->setName(
654  QString("Cylinder_%1")
655  .arg(cylinderCount, 4, 10, QChar('0')));
656  cylinderCount++;
657  } break;
658 
659  case RPT_CONE: // cone
660  {
661  const ConePrimitiveShape* cone =
662  static_cast<const ConePrimitiveShape*>(shape);
663  Vec3f CC = cone->Internal().Center();
664  Vec3f CA = cone->Internal().AxisDirection();
665  float alpha = cone->Internal().Angle();
666 
667  // compute max height
668  Vec3f minP, maxP;
669  float minHeight, maxHeight;
670  minP = maxP = cloud[shapeCloudIndex].pos;
671  minHeight = maxHeight =
672  cone->Internal().Height(cloud[shapeCloudIndex].pos);
673  for (size_t j = 1; j < shapePointsCount; ++j) {
674  float h = cone->Internal().Height(
675  cloud[shapeCloudIndex - j].pos);
676  if (h < minHeight) {
677  minHeight = h;
678  minP = cloud[shapeCloudIndex - j].pos;
679  } else if (h > maxHeight) {
680  maxHeight = h;
681  maxP = cloud[shapeCloudIndex - j].pos;
682  }
683  }
684 
685  float minRadius = tan(alpha) * minHeight;
686  float maxRadius = tan(alpha) * maxHeight;
687  if (minRadius < params.minRadius ||
688  maxRadius > params.maxRadius) {
689  count -= shapePointsCount;
690  delete pcShape;
691  continue;
692  }
693 
694  // let's build the cone primitive
695  {
696  // the bottom should be the largest part so we inverse
697  // the axis direction
698  CCVector3 Z = -CCVector3::fromArray(CA.getValue());
699  Z.normalize();
700 
701  // the center is halfway between the min and max height
702  float midHeight = (minHeight + maxHeight) / 2;
704  (CC + CA * midHeight).getValue());
705 
706  // radial axis
708  (maxP - (CC + maxHeight * CA)).getValue());
709  X.normalize();
710 
711  // orthogonal radial axis
712  CCVector3 Y = Z * X;
713 
714  // we build the transformation matrix from these vectors
715  ccGLMatrix glMat(X, Y, Z, C);
716 
717  // eventually create the cone primitive
718  prim = new ccCone(maxRadius, minRadius,
719  maxHeight - minHeight, 0, 0, &glMat);
720  prim->setEnabled(false);
721  prim->setName(
722  QString("Cone (alpha=%1/h=%2)")
723  .arg(alpha, 0, 'f')
724  .arg(maxHeight - minHeight, 0, 'f'));
725  pcShape->setName(QString("Cone_%1").arg(
726  coneCount, 4, 10, QChar('0')));
727  coneCount++;
728  }
729 
730  } break;
731 
732  case RPT_TORUS: // torus
733  {
734  const TorusPrimitiveShape* torus =
735  static_cast<const TorusPrimitiveShape*>(shape);
736  if (torus->Internal().IsAppleShaped()) {
738  "[qRansacSD] Apple-shaped torus are not "
739  "handled by ACloudViewer!");
740  } else {
741  Vec3f CC = torus->Internal().Center();
742  Vec3f CA = torus->Internal().AxisDirection();
743  float minRadius = torus->Internal().MinorRadius();
744  float maxRadius = torus->Internal().MajorRadius();
745 
746  if (minRadius < params.minRadius ||
747  maxRadius > params.maxRadius) {
748  count -= shapePointsCount;
749  delete pcShape;
750  continue;
751  }
752 
753  CCVector3 Z = CCVector3::fromArray(CA.getValue());
754  CCVector3 C = CCVector3::fromArray(CC.getValue());
755  // construct remaining of base
756  CCVector3 X = Z.orthogonal();
757  CCVector3 Y = Z * X;
758 
759  // we build matrix from these vectors
760  ccGLMatrix glMat(X, Y, Z, C);
761 
762  // torus primitive
763  prim = new ccTorus(maxRadius - minRadius,
764  maxRadius + minRadius, M_PI * 2.0,
765  false, 0, &glMat);
766  prim->setEnabled(false);
767  prim->setName(QString("Torus (r=%1/R=%2)")
768  .arg(minRadius, 0, 'f')
769  .arg(maxRadius, 0, 'f'));
770  pcShape->setName(
771  QString("Torus_%1")
772  .arg(torusCount, 4, 10, QChar('0')));
773  torusCount++;
774  }
775 
776  } break;
777  }
778 
779  // is there a primitive to add to part cloud?
780  if (prim) {
782  pcShape->addChild(prim);
783  // prim->setDisplay(pcShape->getDisplay());
784  prim->setColor(col);
785  prim->showColors(true);
786  prim->setVisible(true);
787  }
788 
789  if (!group)
790  group = new ccHObject(QString("Ransac Detected Shapes (%1)")
791  .arg(ccPC->getName()));
792  group->addChild(pcShape);
793 
794  count -= shapePointsCount;
795 
796  QApplication::processEvents();
797  }
798 
799  if (group) {
800  assert(group->getChildrenNumber() != 0);
801 
802  // we hide input cloud
803  ccPC->setEnabled(false);
805  "[qRansacSD] Input cloud has been automatically hidden!");
806 
807  group->setVisible(true);
808  // group->setDisplay_recursive(ccPC->getDisplay());
809  return group;
810  }
811  }
812 
813  return nullptr;
814 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
double normal[3]
int count
void * X
Definition: SmallVector.cpp:45
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
Vector3Tpl orthogonal() const
Returns a normalized vector which is orthogonal to this one.
Definition: CVGeom.h:433
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
Command line interface.
virtual bool registerCommand(Command::Shared command)=0
Registers a new command.
Cone (primitive)
Definition: ecvCone.h:16
Cylinder (primitive)
Definition: ecvCylinder.h:16
virtual QString getName() const override
Returns (short) name (for menu entry, etc.)
virtual QString getDescription() const override
Returns long name/description (for tooltip, etc.)
virtual QIcon getIcon() const override
Returns icon.
virtual void setVisible(bool state)
Sets entity visibility.
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.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
void enableStippling(bool state)
Enables polygon stippling.
Generic primitive interface.
virtual void setColor(const ecvColor::Rgb &col)
Sets primitive color (shortcut)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
@ SELECTION_FIT_BBOX
Definition: ecvHObject.h:607
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
void setRedrawFlagRecursive(bool redraw=false)
virtual void setSelectionBehavior(SelectionBehavior mode)
Sets selection behavior (when displayed)
Definition: ecvHObject.h:616
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
static QString ConvertDipAndDipDirToString(PointCoordinateType dip_deg, PointCoordinateType dipDir_deg)
Converts geological 'dip direction & dip' parameters to a string.
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
Plane (primitive)
Definition: ecvPlane.h:18
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
bool setRGBColor(ColorCompType r, ColorCompType g, ColorCompType b)
Set a unique color for the whole cloud (shortcut)
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
@ WRN_OUT_OF_MEM_FOR_NORMALS
Dialog for qRansacSD plugin.
Definition: ccRansacSDDlg.h:13
virtual void setGlobalScale(double scale)
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
Sphere (primitive)
Definition: ecvSphere.h:16
Standard ECV plugin interface.
ecvMainAppInterface * m_app
Main application interface.
Torus (primitive)
Definition: ecvTorus.h:16
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) override
Definition: PointCloudTpl.h:57
unsigned size() const override
Definition: PointCloudTpl.h:38
const CCVector3 * getPoint(unsigned index) const override
A very simple point cloud (no point duplication)
static Rgb Random(bool lightOnly=true)
Generates a random color.
RGB color structure.
Definition: ecvColorTypes.h:49
Main application interface (for plugins)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual QWidget * getActiveWindow()=0
virtual const ccHObject::Container & getSelectedEntities() const =0
Returns currently selected entities ("read only")
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
QAction * m_action
Associated action.
Definition: qRANSAC_SD.h:99
void doAction()
Slot called when associated ation is triggered.
Definition: qRANSAC_SD.cpp:121
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
@ POINT_CLOUD
Definition: CVTypes.h:104
void Sleep(int milliseconds)
Definition: Helper.cpp:278
float DegreesToRadians(int degrees)
Convert degrees to radians.
Definition: CVMath.h:98
static MiscLib::Vector< std::pair< MiscLib::RefCountPtr< PrimitiveShape >, size_t > > * s_shapes
Definition: qRANSAC_SD.cpp:95
static unsigned s_supportPoints
Definition: qRANSAC_SD.cpp:107
static bool s_randomColor
Definition: qRANSAC_SD.cpp:119
static float s_maxRadius
Definition: qRANSAC_SD.cpp:117
static double s_proba
Definition: qRANSAC_SD.cpp:111
static RansacShapeDetector * s_detector
Definition: qRANSAC_SD.cpp:97
void doDetection()
Definition: qRANSAC_SD.cpp:99
static ecvMainAppInterface * s_app
Definition: qRANSAC_SD.cpp:57
static size_t s_remainingPoints
Definition: qRANSAC_SD.cpp:96
static double s_maxNormalDev_deg
Definition: qRANSAC_SD.cpp:109
static float s_minRadius
Definition: qRANSAC_SD.cpp:115
static ransac::RansacPointCloud * s_cloud
Definition: qRANSAC_SD.cpp:98
static bool s_primEnabled[5]
Definition: qRANSAC_SD.cpp:113
QSharedPointer< Command > Shared
Shared type.