Merge pull request #4975 from maxGimeno/Demo-Fix_mesh_plane_detection-maxGimeno

Polyhedron Demo: Fix Mesh_plane_detection_plugin
This commit is contained in:
Laurent Rineau 2020-09-24 16:43:07 +02:00
commit 62e8fcb4ce
5 changed files with 27 additions and 386 deletions

View File

@ -201,7 +201,7 @@ private:
const double max_distance_to_plane = const double max_distance_to_plane =
dialog.epsilon(); dialog.epsilon();
const double max_accepted_angle = const double max_accepted_angle =
(std::acos(dialog.normal_tolerance()) * 180.0) / CGAL_PI; dialog.normal_tolerance();
const std::size_t min_region_size = const std::size_t min_region_size =
dialog.min_points(); dialog.min_points();
@ -248,7 +248,19 @@ private:
64 + rnd.get_int(0, 192), 64 + rnd.get_int(0, 192),
64 + rnd.get_int(0, 192))); 64 + rnd.get_int(0, 192)));
} }
if(color_vector.empty())
{
for(const auto& f : faces(fg))
{
fg.property_map<face_descriptor, int>("f:patch_id").first[f] =
static_cast<int>(0);
}
CGAL::Random rnd(static_cast<unsigned int>(0));
color_vector.push_back(QColor(
64 + rnd.get_int(0, 192),
64 + rnd.get_int(0, 192),
64 + rnd.get_int(0, 192)));
}
colored_item->invalidateOpenGLBuffers(); colored_item->invalidateOpenGLBuffers();
scene->addItem(colored_item); scene->addItem(colored_item);
} }
@ -273,7 +285,7 @@ private:
const double max_distance_to_plane = const double max_distance_to_plane =
dialog.epsilon(); dialog.epsilon();
const double max_accepted_angle = const double max_accepted_angle =
(std::acos(dialog.normal_tolerance()) * 180.0) / CGAL_PI; dialog.normal_tolerance();
const std::size_t min_region_size = const std::size_t min_region_size =
dialog.min_points(); dialog.min_points();
@ -648,7 +660,7 @@ private:
CGAL::Shape_detection::Plane_map<Traits>(), CGAL::Shape_detection::Plane_map<Traits>(),
CGAL::Shape_detection::Point_to_shape_index_map<Traits>(*points, planes), CGAL::Shape_detection::Point_to_shape_index_map<Traits>(*points, planes),
true, true, true, true, true, true, true, true,
180 * std::acos (op.normal_threshold) / CGAL_PI, op.epsilon); op.normal_threshold, op.epsilon);
std::cerr << "done" << std::endl; std::cerr << "done" << std::endl;
} }
@ -927,8 +939,14 @@ void Polyhedron_demo_point_set_shape_detection_plugin::on_actionDetectShapesSM_t
dialog.label_4->setEnabled(false); dialog.label_4->setEnabled(false);
dialog.m_cluster_epsilon_field->setEnabled(false); dialog.m_cluster_epsilon_field->setEnabled(false);
dialog.groupBox_3->setEnabled(false); dialog.groupBox_3->setEnabled(false);
//todo: check default values
dialog.m_epsilon_field->setValue(0.01*sm_item->diagonalBbox());
std::size_t nb_faces = mesh->number_of_faces();
dialog.m_min_pts_field->setValue((std::max)(static_cast<int>(0.01*nb_faces), 1));
if(!dialog.exec()) return; if(!dialog.exec()) return;
if(dialog.min_points() > static_cast<unsigned int>(nb_faces))
dialog.m_min_pts_field->setValue(static_cast<unsigned int>(nb_faces));
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
if (dialog.region_growing()) { if (dialog.region_growing()) {
detect_shapes_with_region_growing_sm(sm_item, dialog); detect_shapes_with_region_growing_sm(sm_item, dialog);
@ -969,6 +987,8 @@ void Polyhedron_demo_point_set_shape_detection_plugin::on_actionDetect_triggered
Point_set_demo_point_set_shape_detection_dialog dialog; Point_set_demo_point_set_shape_detection_dialog dialog;
if(!dialog.exec()) if(!dialog.exec())
return; return;
if(dialog.min_points() > static_cast<unsigned int>(points->size()))
dialog.m_min_pts_field->setValue(static_cast<unsigned int>(points->size()));
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
if (dialog.region_growing()) if (dialog.region_growing())

View File

@ -152,7 +152,7 @@
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Normal Tolerance:</string> <string>Normal Tolerance (in degrees):</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -165,7 +165,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>0.90</string> <string>30</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -20,10 +20,6 @@ qt5_wrap_ui( segmentationUI_FILES Mesh_segmentation_widget.ui)
polyhedron_demo_plugin(mesh_segmentation_plugin Mesh_segmentation_plugin ${segmentationUI_FILES}) polyhedron_demo_plugin(mesh_segmentation_plugin Mesh_segmentation_plugin ${segmentationUI_FILES})
target_link_libraries(mesh_segmentation_plugin PUBLIC scene_surface_mesh_item) target_link_libraries(mesh_segmentation_plugin PUBLIC scene_surface_mesh_item)
qt5_wrap_ui( mesh_plane_detectionUI_FILES Mesh_plane_detection_dialog.ui)
polyhedron_demo_plugin(mesh_plane_detection_plugin Mesh_plane_detection_plugin ${mesh_plane_detectionUI_FILES})
target_link_libraries(mesh_plane_detection_plugin PUBLIC scene_surface_mesh_item)
qt5_wrap_ui( mesh_simplificationUI_FILES Mesh_simplification_dialog.ui) qt5_wrap_ui( mesh_simplificationUI_FILES Mesh_simplification_dialog.ui)
polyhedron_demo_plugin(mesh_simplification_plugin Mesh_simplification_plugin ${mesh_simplificationUI_FILES}) polyhedron_demo_plugin(mesh_simplification_plugin Mesh_simplification_plugin ${mesh_simplificationUI_FILES})
target_link_libraries(mesh_simplification_plugin PUBLIC scene_surface_mesh_item scene_selection_item) target_link_libraries(mesh_simplification_plugin PUBLIC scene_surface_mesh_item scene_selection_item)

View File

@ -1,125 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Mesh_plane_detection_dialog</class>
<widget class="QDialog" name="Mesh_plane_detection_dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>349</width>
<height>117</height>
</rect>
</property>
<property name="windowTitle">
<string>Mesh Plane Detection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="minimumAreaLabel">
<property name="text">
<string>Minimum area</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="maximumDeviationFromNormalLabel">
<property name="text">
<string>Maximum deviation from normal</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="maximumDeviationFromNormalSpinBox">
<property name="suffix">
<string>°</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>90</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="DoubleEdit" name="minimumAreaDoubleSpinBox">
<property name="text">
<string>0.01</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>DoubleEdit</class>
<extends>QLineEdit</extends>
<header>CGAL_double_edit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Mesh_plane_detection_dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Mesh_plane_detection_dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,250 +0,0 @@
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include "Scene_surface_mesh_item.h"
#include "Scene.h"
#include "Color_map.h"
#include <CGAL/mesh_segmentation.h>
#include <QApplication>
#include <QMainWindow>
#include <QInputDialog>
#include <QElapsedTimer>
#include <QAction>
#include <QDebug>
#include <QObject>
//#include <QtConcurrentRun>
#include <map>
#include <algorithm>
#include <vector>
#include <CGAL/property_map.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include "ui_Mesh_plane_detection_dialog.h"
namespace PMP = CGAL::Polygon_mesh_processing;
using namespace CGAL::Three;
class Polyhedron_demo_mesh_plane_detection_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
QList<QAction*> actions() const {
return QList<QAction*>() << actionPlaneDetection;
}
bool applicable(QAction*) const {
return
qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
}
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) {
this->scene = scene_interface;
this->mw = mainWindow;
actionPlaneDetection = new QAction("Mesh Plane Detection", mw);
actionPlaneDetection->setProperty("subMenuName", "Triangulated Surface Mesh Segmentation");
actionPlaneDetection->setObjectName("actionPlaneDetection");
// adding slot for itemAboutToBeDestroyed signal, aim is removing item from item-functor map.
if( Scene* scene = dynamic_cast<Scene*>(scene_interface) ) {
connect(scene, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)), this, SLOT(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)));
}
autoConnectActions();
}
virtual void closure()
{
}
template<class SegmentPropertyMap>
void colorize_segmentation(Scene_surface_mesh_item* item, SegmentPropertyMap& segment_ids, std::vector<QColor>& color_vector);
void check_and_set_ids(SMesh *sm);
public Q_SLOTS:
void on_actionPlaneDetection_triggered();
void itemAboutToBeDestroyed(CGAL::Three::Scene_item*);
private:
QAction* actionPlaneDetection;
template <typename OutputIterator>
void detect_planes_in_mesh (SMesh& mesh, const double area_min,
const double angle_max,
OutputIterator output)
{
typedef SMesh::size_type size_type;
std::cerr << "Detecting planes with:" << std::endl
<< " * Area min = " << area_min << std::endl
<< " * Min cos angle = " << angle_max << std::endl;
std::vector<int> label_region(mesh.number_of_faces(), 0);
int class_index = 0;
for (typename SMesh::Face_iterator f = mesh.faces_begin(); f != mesh.faces_end(); ++f)
{
if (label_region[*f] != 0)
continue;
class_index++;
label_region[*f] = class_index;
double area = PMP::face_area (*f, mesh, PMP::parameters::geom_traits(EPICK()));
//characteristics of the seed
EPICK::Vector_3 normal_seed = PMP::compute_face_normal (*f, mesh, PMP::parameters::geom_traits(EPICK()));
EPICK::Point_3 pt_seed = mesh.point(target(halfedge(*f, mesh), mesh));
EPICK::Plane_3 optimal_plane(pt_seed, normal_seed);
// Kernel::Plane_3 optimal_plane = f->plane();
//initialization containers
std::vector<size_type> index_container (1,*f);
std::vector<size_type> index_container_former_ring (1, *f);
std::list<size_type> index_container_current_ring;
//propagation
bool propagation = true;
do{
propagation = false;
for (size_type k = 0; k < index_container_former_ring.size(); k++)
{
typename SMesh::Halfedge_around_face_circulator
circ( mesh.halfedge(SMesh::Face_index(index_container_former_ring[k])), mesh)
, start = circ;
do
{
if (is_border(*circ, mesh))
continue;
typename SMesh::Face_index
neighbor = mesh.face(opposite(*circ, mesh));
size_type neighbor_index = neighbor;
if (label_region[neighbor_index] == 0)
{
EPICK::Vector_3 normal
= PMP::compute_face_normal (neighbor, mesh, PMP::parameters::geom_traits(EPICK()));
if (std::fabs(normal * optimal_plane.orthogonal_vector()) > angle_max)
{
label_region[neighbor_index] = class_index;
propagation = true;
index_container_current_ring.push_back(neighbor_index);
area += PMP::face_area (neighbor, mesh, PMP::parameters::geom_traits(EPICK()));
}
}
}
while (++ circ != start);
}
//update containers
index_container_former_ring.clear();
for (std::list<size_type>::iterator it = index_container_current_ring.begin();
it != index_container_current_ring.end(); ++it)
{
index_container_former_ring.push_back(*it);
index_container.push_back(*it);
}
index_container_current_ring.clear();
} while (propagation);
//Test the number of inliers -> reject if inferior to Nmin
if (area < area_min)
{
class_index--;
label_region[*f] = 0;
for (size_type k = 0; k < index_container.size(); k++)
label_region[index_container[k]] = 0;
}
}
std::cerr << class_index << " planes detected" << std::endl;
std::copy (label_region.begin(), label_region.end(), output);
}
};
void Polyhedron_demo_mesh_plane_detection_plugin::itemAboutToBeDestroyed(CGAL::Three::Scene_item*)
{
}
void Polyhedron_demo_mesh_plane_detection_plugin::on_actionPlaneDetection_triggered()
{
const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_surface_mesh_item* poly_item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
if (poly_item)
{
SMesh& pmesh =*poly_item->polyhedron();
QDialog dialog(mw);
Ui::Mesh_plane_detection_dialog ui;
ui.setupUi(&dialog);
ui.minimumAreaDoubleSpinBox->setMinimum(0.00001);
// check user cancellation
if(dialog.exec() == QDialog::Rejected)
return;
QElapsedTimer time;
time.start();
std::cerr << "Detecting planes... ";
QApplication::setOverrideCursor(Qt::WaitCursor);
QApplication::processEvents();
//check_and_set_ids (&pmesh);
std::vector<int> indices;
detect_planes_in_mesh (pmesh,
ui.minimumAreaDoubleSpinBox->value(),
std::fabs(std::cos (CGAL_PI * ui.maximumDeviationFromNormalSpinBox->value() / 180.)),
std::back_inserter (indices));
//poly_item->set_color_vector_read_only(true);
colorize_segmentation (poly_item, indices, poly_item->color_vector());
std::cerr << "ok (" << time.elapsed() << " ms, "
<< pmesh.number_of_halfedges() / 2 << " edges)" << std::endl;
poly_item->invalidateOpenGLBuffers();
scene->itemChanged(index);
QApplication::restoreOverrideCursor();
}
}
template<class SegmentPropertyMap>
void Polyhedron_demo_mesh_plane_detection_plugin::colorize_segmentation(
Scene_surface_mesh_item* item,
SegmentPropertyMap& segment_ids,
std::vector<QColor>& color_vector)
{
item->setItemIsMulticolor(true);
item->computeItemColorVectorAutomatically(true);
SMesh* sm = item->face_graph();
color_vector.clear();
std::size_t max_segment = 0;
for(SMesh::Face_iterator facet_it = sm->faces_begin();
facet_it != sm->faces_end(); ++facet_it)
{
std::size_t segment_id = segment_ids[static_cast<std::size_t>(*facet_it)];
sm->property_map<face_descriptor, int>("f:patch_id").first[*facet_it] = static_cast<int>(segment_id);
max_segment = (std::max)(max_segment, segment_id);
}
color_vector.push_back(QColor(0, 0, 0));
for(std::size_t i = 1; i <= max_segment; ++i)
color_vector.push_back (QColor (CGAL::get_default_random().get_int (41, 255),
CGAL::get_default_random().get_int (35, 238),
CGAL::get_default_random().get_int (35, 255)));
}
#include "Mesh_plane_detection_plugin.moc"