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>
29 #include <QApplication>
30 #include <QMainWindow>
31 #include <QProgressDialog>
32 #include <QtConcurrentRun>
51 #if defined(CV_WINDOWS)
59 qRansacSD::qRansacSD(QObject* parent )
68 m_action->setEnabled(selectedEntities.size() == 1 &&
72 QList<QAction*> qRansacSD::getActions() {
94 static MiscLib::Vector<std::pair<MiscLib::RefCountPtr<PrimitiveShape>,
size_t>>*
98 static ransac::RansacPointCloud*
s_cloud =
nullptr;
126 size_t selNum = selectedEntities.size();
149 rsdDlg.epsilonDoubleSpinBox->setValue(
152 rsdDlg.bitmapEpsilonDoubleSpinBox->setValue(
160 rsdDlg.probaDoubleSpinBox->setValue(
s_proba);
167 if (!rsdDlg.exec())
return;
172 static_cast<float>(rsdDlg.epsilonDoubleSpinBox->value());
174 static_cast<float>(rsdDlg.bitmapEpsilonDoubleSpinBox->value());
176 static_cast<float>(rsdDlg.maxNormDevAngleSpinBox->value());
178 s_proba =
static_cast<float>(rsdDlg.probaDoubleSpinBox->value());
183 static_cast<unsigned>(rsdDlg.supportPointsSpinBox->value());
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();
191 s_minRadius =
static_cast<float>(rsdDlg.minRadiusSpinBox->value());
192 s_maxRadius =
static_cast<float>(rsdDlg.maxRadiusSpinBox->value());
205 const RansacParams&
params,
209 unsigned char primCount = 0;
210 for (
unsigned char k = 0; k < 5; ++k) {
211 primCount +=
static_cast<unsigned>(
params.primEnabled[k]);
213 if (primCount == 0) {
214 CVLog::Error(
"[qRansacSD] No primitive type selected!");
218 for (
unsigned char k = 0; k < 5; ++k) {
230 ransac::RansacPointCloud cloud;
233 cloud.reserve(
count);
236 "[qRansacSD] Could not create temporary cloud, Not enough "
242 ransac::RansacPoint Pt;
246 for (
unsigned i = 0; i <
count; ++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);
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);
257 #ifdef POINTSWITHINDEX
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);
274 RansacShapeDetector::Options ransacOptions;
276 ransacOptions.m_epsilon =
params.epsilon;
277 ransacOptions.m_bitmapEpsilon =
params.bitmapEpsilon;
278 ransacOptions.m_normalThresh =
static_cast<float>(
280 assert(ransacOptions.m_normalThresh >= 0);
281 ransacOptions.m_probability =
params.probability;
282 ransacOptions.m_minSupport =
params.supportPoints;
284 const float scale = cloud.getScale();
287 QProgressDialog* pDlg =
nullptr;
290 pDlg =
new QProgressDialog(
"Computing normals (please wait)",
294 pDlg =
new QProgressDialog(
"Computing normals (please wait)",
295 QString(), 0, 0,
nullptr);
297 pDlg->setWindowTitle(
"Ransac Shape Detection");
300 QApplication::processEvents();
302 cloud.calcNormals(.01f * scale);
305 for (
unsigned i = 0; i <
count; ++i) {
306 Vec3f& Nvi = cloud[i].normal;
317 CVLog::Error(
"[qRansacSD] Not enough memory to compute normals!");
330 RansacShapeDetector detector(ransacOptions);
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());
343 unsigned remaining =
count;
344 typedef std::pair<MiscLib::RefCountPtr<PrimitiveShape>,
size_t>
346 MiscLib::Vector<DetectedShape> shapes;
360 QProgressDialog* pDlg =
nullptr;
363 pDlg =
new QProgressDialog(
364 "Operation in progress (please wait)", QString(), 0, 0,
367 pDlg =
new QProgressDialog(
368 "Operation in progress (please wait)", QString(), 0, 0,
371 pDlg->setWindowTitle(
"Ransac Shape Detection");
379 QFuture<void> future = QtConcurrent::run(
doDetection);
381 while (!future.isFinished()) {
382 #if defined(CV_WINDOWS)
387 if (!silent && pDlg) {
388 pDlg->setValue(pDlg->value() + 1);
390 QApplication::processEvents();
394 QApplication::processEvents();
402 FILE* fp = fopen(
"RANS_SD_trace.txt",
"wt");
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);
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);
416 if (shapes.size() > 0)
418 fprintf(fp,
"\n[Shapes]\n");
419 for (
unsigned i = 0; i < shapes.size(); ++i)
421 PrimitiveShape* shape = shapes[i].first;
422 size_t shapePointsCount = shapes[i].second;
425 shape->Description(&desc);
426 fprintf(fp,
"#%i - %s - %i points\n", i + 1, desc.c_str(), shapePointsCount);
432 if (remaining ==
count) {
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;
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);
450 if (shapePointsCount >
count) {
455 if (shapePointsCount <
params.supportPoints) {
457 "[qRansacSD] Skipping shape, %d did not meet minimum "
460 count -= shapePointsCount;
465 shape->Description(&desc);
468 const auto shapeCloudIndex =
count - 1;
472 bool saveNormals =
true;
474 #ifdef POINTSWITHINDEX
477 if (!refPcShape.reserve(
478 static_cast<unsigned>(shapePointsCount))) {
483 for (
unsigned j = 0; j < shapePointsCount; ++j) {
484 refPcShape.addPointIndex(
static_cast<unsigned int>(
485 cloud[shapeCloudIndex - j].index));
499 static_cast<unsigned>(shapePointsCount))) {
506 for (
unsigned j = 0; j < shapePointsCount; ++j) {
508 cloud[shapeCloudIndex - j].pos));
511 cloud[shapeCloudIndex - j].
normal));
530 switch (shape->Identifier()) {
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();
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,
547 if (minX < param.first)
549 else if (maxX > param.first)
551 if (minY < param.second)
553 else if (maxY > param.second)
556 minX = maxX = param.first;
557 minY = maxY = param.second;
562 float dX = maxX - minX;
563 float dY = maxY - minY;
564 G +=
X * (minX + dX / 2);
565 G += Y * (minY + dY / 2);
582 QString dipAndDipDirStr =
585 prim->
setName(dipAndDipDirStr);
588 .arg(planeCount, 4, 10, QChar(
'0')));
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;
604 Vec3f CC = sphere->Internal().Center();
610 prim =
new ccSphere(radius, &glMat);
612 prim->
setName(QString(
"Sphere (r=%1)").arg(radius, 0,
'f'));
615 .arg(sphereCount, 4, 10, QChar(
'0')));
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;
631 Vec3f G = cyl->Internal().AxisPosition();
632 Vec3f N = cyl->Internal().AxisDirection();
633 Vec3f
X = cyl->Internal().AngularDirection();
634 Vec3f Y = N.cross(
X);
636 float hMin = cyl->MinHeight();
637 float hMax = cyl->MaxHeight();
638 float h = hMax - hMin;
639 G += N * (hMin + h / 2);
650 prim->
setName(QString(
"Cylinder (r=%1/h=%2)")
654 QString(
"Cylinder_%1")
655 .arg(cylinderCount, 4, 10, QChar(
'0')));
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();
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);
678 minP = cloud[shapeCloudIndex - j].pos;
679 }
else if (h > maxHeight) {
681 maxP = cloud[shapeCloudIndex - j].pos;
685 float minRadius = tan(alpha) * minHeight;
686 float maxRadius = tan(alpha) * maxHeight;
687 if (minRadius <
params.minRadius ||
688 maxRadius >
params.maxRadius) {
689 count -= shapePointsCount;
702 float midHeight = (minHeight + maxHeight) / 2;
704 (CC + CA * midHeight).getValue());
708 (maxP - (CC + maxHeight * CA)).getValue());
718 prim =
new ccCone(maxRadius, minRadius,
719 maxHeight - minHeight, 0, 0, &glMat);
722 QString(
"Cone (alpha=%1/h=%2)")
724 .arg(maxHeight - minHeight, 0,
'f'));
725 pcShape->
setName(QString(
"Cone_%1").arg(
726 coneCount, 4, 10, QChar(
'0')));
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!");
741 Vec3f CC = torus->Internal().Center();
742 Vec3f CA = torus->Internal().AxisDirection();
743 float minRadius = torus->Internal().MinorRadius();
744 float maxRadius = torus->Internal().MajorRadius();
746 if (minRadius <
params.minRadius ||
747 maxRadius >
params.maxRadius) {
748 count -= shapePointsCount;
763 prim =
new ccTorus(maxRadius - minRadius,
764 maxRadius + minRadius,
M_PI * 2.0,
767 prim->
setName(QString(
"Torus (r=%1/R=%2)")
768 .arg(minRadius, 0,
'f')
769 .arg(maxRadius, 0,
'f'));
772 .arg(torusCount, 4, 10, QChar(
'0')));
790 group =
new ccHObject(QString(
"Ransac Detected Shapes (%1)")
794 count -= shapePointsCount;
796 QApplication::processEvents();
805 "[qRansacSD] Input cloud has been automatically hidden!");
float PointCoordinateType
Type of the coordinates of a (N-D) point.
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
void normalize()
Sets vector norm to unity.
Vector3Tpl orthogonal() const
Returns a normalized vector which is orthogonal to this one.
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
virtual bool registerCommand(Command::Shared command)=0
Registers a new command.
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.
void enableStippling(bool state)
Enables polygon stippling.
Generic primitive interface.
virtual void setColor(const ecvColor::Rgb &col)
Sets primitive color (shortcut)
Hierarchical CLOUDVIEWER Object.
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
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.
void setRedrawFlagRecursive(bool redraw=false)
virtual void setSelectionBehavior(SelectionBehavior mode)
Sets selection behavior (when displayed)
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
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.
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.
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.
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.
Standard ECV plugin interface.
ecvMainAppInterface * m_app
Main application interface.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) override
unsigned size() const override
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.
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.
void doAction()
Slot called when associated ation is triggered.
__host__ __device__ int2 abs(int2 v)
void Sleep(int milliseconds)
float DegreesToRadians(int degrees)
Convert degrees to radians.
static MiscLib::Vector< std::pair< MiscLib::RefCountPtr< PrimitiveShape >, size_t > > * s_shapes
static unsigned s_supportPoints
static bool s_randomColor
static RansacShapeDetector * s_detector
static ecvMainAppInterface * s_app
static size_t s_remainingPoints
static double s_maxNormalDev_deg
static ransac::RansacPointCloud * s_cloud
static bool s_primEnabled[5]
QSharedPointer< Command > Shared
Shared type.