mirror of https://github.com/CGAL/cgal
1441 lines
47 KiB
C++
1441 lines
47 KiB
C++
#include <QtCore/qglobal.h>
|
|
#include <QFileDialog>
|
|
#include <QColorDialog>
|
|
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
|
#include <CGAL/Qt/manipulatedFrame.h>
|
|
|
|
#include <fstream>
|
|
|
|
|
|
#include "Messages_interface.h"
|
|
#include "Scene_points_with_normal_item.h"
|
|
#include "Item_classification_base.h"
|
|
#include "Point_set_item_classification.h"
|
|
#include "Cluster_classification.h"
|
|
#include "Scene_surface_mesh_item.h"
|
|
#include "Surface_mesh_item_classification.h"
|
|
#include "Scene_polylines_item.h"
|
|
#include "Scene_polygon_soup_item.h"
|
|
|
|
#include <CGAL/Three/Scene_interface.h>
|
|
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
|
|
|
#include <CGAL/Random.h>
|
|
#include <CGAL/Real_timer.h>
|
|
|
|
#include <QMultipleInputDialog.h>
|
|
|
|
#include "ui_Classification_widget.h"
|
|
#include "ui_Classification_advanced_widget.h"
|
|
|
|
#include <QAction>
|
|
#include <QMainWindow>
|
|
#include <QApplication>
|
|
#include <QCheckBox>
|
|
#include <QInputDialog>
|
|
#include <QMessageBox>
|
|
#include <QSpinBox>
|
|
#include <QDoubleSpinBox>
|
|
#include <QSlider>
|
|
|
|
#include <map>
|
|
|
|
#include <boost/graph/adjacency_list.hpp>
|
|
#include <CGAL/boost/graph/split_graph_into_polylines.h>
|
|
|
|
using namespace CGAL::Three;
|
|
|
|
class Polyhedron_demo_classification_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")
|
|
|
|
struct LabelButton
|
|
{
|
|
QPushButton* color_button;
|
|
QMenu* menu;
|
|
char shortcut;
|
|
|
|
QColor color;
|
|
|
|
QLabel* label2;
|
|
QComboBox* effect;
|
|
|
|
LabelButton (QWidget* parent,
|
|
const char* name,
|
|
const QColor& color,
|
|
const char shortcut)
|
|
: shortcut (shortcut), color (color)
|
|
{
|
|
color_button = new QPushButton (tr("%1 (%2)").arg(name).arg((char)(std::toupper(shortcut))), parent);
|
|
|
|
menu = new QMenu("Label Menu", color_button);
|
|
|
|
QColor text_color (255, 255, 255);
|
|
if (color.red() * 0.299 + color.green() * 0.587 + color.blue() * 0.114 > 128)
|
|
text_color = QColor (0, 0, 0);
|
|
|
|
QString s("QPushButton { font-weight: bold; background: #"
|
|
+ QString(color.red() < 16? "0" : "") + QString::number(color.red(),16)
|
|
+ QString(color.green() < 16? "0" : "") + QString::number(color.green(),16)
|
|
+ QString(color.blue() < 16? "0" : "") + QString::number(color.blue(),16)
|
|
+ "; color: #"
|
|
+ QString(text_color.red() < 16? "0" : "") + QString::number(text_color.red(),16)
|
|
+ QString(text_color.green() < 16? "0" : "") + QString::number(text_color.green(),16)
|
|
+ QString(text_color.blue() < 16? "0" : "") + QString::number(text_color.blue(),16)
|
|
+ "; }");
|
|
|
|
color_button->setStyleSheet(s);
|
|
color_button->setMenu(menu);
|
|
|
|
|
|
label2 = new QLabel (name, parent);
|
|
effect = new QComboBox;
|
|
effect->addItem("Penalized");
|
|
effect->addItem("Neutral");
|
|
effect->addItem("Favored");
|
|
}
|
|
~LabelButton ()
|
|
{
|
|
}
|
|
void change_color (const QColor& color)
|
|
{
|
|
this->color = color;
|
|
QColor text_color (255, 255, 255);
|
|
if (color.red() * 0.299 + color.green() * 0.587 + color.blue() * 0.114 > 128)
|
|
text_color = QColor (0, 0, 0);
|
|
QString s("QPushButton { font-weight: bold; background: #"
|
|
+ QString(color.red() < 16? "0" : "") + QString::number(color.red(),16)
|
|
+ QString(color.green() < 16? "0" : "") + QString::number(color.green(),16)
|
|
+ QString(color.blue() < 16? "0" : "") + QString::number(color.blue(),16)
|
|
+ "; color: #"
|
|
+ QString(text_color.red() < 16? "0" : "") + QString::number(text_color.red(),16)
|
|
+ QString(text_color.green() < 16? "0" : "") + QString::number(text_color.green(),16)
|
|
+ QString(text_color.blue() < 16? "0" : "") + QString::number(text_color.blue(),16)
|
|
+ "; }");
|
|
|
|
color_button->setStyleSheet(s);
|
|
color_button->update();
|
|
}
|
|
};
|
|
|
|
|
|
public:
|
|
bool applicable(QAction*) const {
|
|
return
|
|
qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()))
|
|
|| qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
|
|
}
|
|
void print_message(QString message) { messages->information(message); }
|
|
QList<QAction*> actions() const { return QList<QAction*>() << actionClassification; }
|
|
|
|
using Polyhedron_demo_plugin_helper::init;
|
|
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface* m) {
|
|
|
|
mw = mainWindow;
|
|
scene = scene_interface;
|
|
messages = m;
|
|
actionClassification = new QAction(tr("Classification"), mw);
|
|
connect(actionClassification, SIGNAL(triggered()), this, SLOT(classification_action()));
|
|
|
|
dock_widget = new QDockWidget("Classification", mw);
|
|
dock_widget->setVisible(false);
|
|
dock_widget_adv = new QDockWidget("Classification (Advanced)", mw);
|
|
dock_widget_adv->setVisible(false);
|
|
|
|
label_button = new QPushButton(QIcon(QString(":/cgal/icons/plus")), "", dock_widget);
|
|
|
|
QMenu* label_menu = new QMenu("Label Menu", label_button);
|
|
label_button->setMenu (label_menu);
|
|
|
|
QAction* add_new_label = label_menu->addAction ("Add new label(s)");
|
|
connect(add_new_label, SIGNAL(triggered()), this,
|
|
SLOT(on_add_new_label_clicked()));
|
|
|
|
label_menu->addSeparator();
|
|
|
|
QAction* use_config_building = label_menu->addAction ("Use configuration ground/vegetation/building");
|
|
connect(use_config_building, SIGNAL(triggered()), this,
|
|
SLOT(on_use_config_building_clicked()));
|
|
QAction* use_config_roof = label_menu->addAction ("Use configuration ground/vegetation/roof/facade");
|
|
connect(use_config_roof, SIGNAL(triggered()), this,
|
|
SLOT(on_use_config_roof_clicked()));
|
|
QAction* use_config_las = label_menu->addAction ("Use LAS standard configuration");
|
|
connect(use_config_las, SIGNAL(triggered()), this,
|
|
SLOT(on_use_las_config_clicked()));
|
|
|
|
label_menu->addSeparator();
|
|
|
|
|
|
QAction* generate = label_menu->addAction ("Create one point set item per label");
|
|
connect(generate, SIGNAL(triggered()), this,
|
|
SLOT(on_generate_items_button_clicked()));
|
|
|
|
label_menu->addSeparator();
|
|
|
|
QAction* clear_labels = label_menu->addAction ("Clear labels");
|
|
connect(clear_labels, SIGNAL(triggered()), this,
|
|
SLOT(on_clear_labels_clicked()));
|
|
|
|
ui_widget.setupUi(dock_widget);
|
|
ui_widget_adv.setupUi(dock_widget_adv);
|
|
addDockWidget(dock_widget);
|
|
addDockWidget(dock_widget_adv);
|
|
|
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
|
ui_widget.classifier->addItem (tr("Random Forest (OpenCV %1.%2)")
|
|
.arg(CV_MAJOR_VERSION)
|
|
.arg(CV_MINOR_VERSION));
|
|
#endif
|
|
|
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
|
ui_widget.classifier->addItem (tr("Neural Network (TensorFlow)"));
|
|
#endif
|
|
|
|
color_att = QColor (75, 75, 77);
|
|
|
|
ui_widget.menu->setMenu (new QMenu("Classification Menu", ui_widget.menu));
|
|
|
|
connect(ui_widget.classifier, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(on_classifier_changed(int)));
|
|
|
|
QAction* compute_features = ui_widget.menu->menu()->addAction ("Compute features");
|
|
connect(compute_features, SIGNAL(triggered()), this,
|
|
SLOT(on_compute_features_button_clicked()));
|
|
|
|
ui_widget.menu->menu()->addSection ("Training");
|
|
|
|
action_train = ui_widget.menu->menu()->addAction ("Train classifier");
|
|
action_train->setShortcut(Qt::SHIFT | Qt::Key_T);
|
|
connect(action_train, SIGNAL(triggered()), this,
|
|
SLOT(on_train_clicked()));
|
|
|
|
action_reset_local = ui_widget.menu->menu()->addAction ("Reset training set of selection");
|
|
connect(action_reset_local, SIGNAL(triggered()), this,
|
|
SLOT(on_reset_training_set_of_selection_clicked()));
|
|
|
|
action_reset = ui_widget.menu->menu()->addAction ("Reset all training sets");
|
|
connect(action_reset, SIGNAL(triggered()), this,
|
|
SLOT(on_reset_training_sets_clicked()));
|
|
|
|
action_random_region = ui_widget.menu->menu()->addAction ("Select random region");
|
|
action_random_region->setShortcut(Qt::SHIFT | Qt::Key_S);
|
|
connect(action_random_region, SIGNAL(triggered()), this,
|
|
SLOT(on_select_random_region_clicked()));
|
|
|
|
action_validate = ui_widget.menu->menu()->addAction ("Validate labels of current selection as training sets");
|
|
connect(action_validate, SIGNAL(triggered()), this,
|
|
SLOT(on_validate_selection_clicked()));
|
|
|
|
action_save_config = ui_widget.menu->menu()->addAction ("Save classifier's current configuration");
|
|
action_load_config = ui_widget.menu->menu()->addAction ("Load configuration for classifier");
|
|
connect(action_save_config, SIGNAL(triggered()), this,
|
|
SLOT(on_save_config_button_clicked()));
|
|
connect(action_load_config, SIGNAL(triggered()), this,
|
|
SLOT(on_load_config_button_clicked()));
|
|
|
|
ui_widget.menu->menu()->addSection ("Algorithms");
|
|
|
|
action_run = ui_widget.menu->menu()->addAction ("Classification");
|
|
connect(action_run, SIGNAL(triggered()), this,
|
|
SLOT(on_run_button_clicked()));
|
|
|
|
action_run_smoothed = ui_widget.menu->menu()->addAction ("Classification with local smoothing");
|
|
connect(action_run_smoothed, SIGNAL(triggered()), this,
|
|
SLOT(on_run_smoothed_button_clicked()));
|
|
|
|
action_run_graphcut = ui_widget.menu->menu()->addAction ("Classification with Graph Cut");
|
|
connect(action_run_graphcut, SIGNAL(triggered()), this,
|
|
SLOT(on_run_graphcut_button_clicked()));
|
|
|
|
ui_widget.menu->menu()->addSeparator();
|
|
|
|
QAction* close = ui_widget.menu->menu()->addAction ("Close");
|
|
connect(close, SIGNAL(triggered()), this,
|
|
SLOT(ask_for_closing()));
|
|
|
|
connect(ui_widget.display, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(on_display_button_clicked(int)));
|
|
|
|
connect(ui_widget_adv.selected_feature, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(on_selected_feature_changed(int)));
|
|
connect(ui_widget_adv.feature_weight, SIGNAL(valueChanged(int)), this,
|
|
SLOT(on_feature_weight_changed(int)));
|
|
|
|
QObject* scene_obj = dynamic_cast<QObject*>(scene_interface);
|
|
if(scene_obj)
|
|
{
|
|
connect(scene_obj, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)), this,
|
|
SLOT(item_about_to_be_destroyed(CGAL::Three::Scene_item*)));
|
|
|
|
connect(scene_obj, SIGNAL(itemIndexSelected(int)), this,
|
|
SLOT(update_plugin(int)));
|
|
}
|
|
}
|
|
virtual void closure()
|
|
{
|
|
dock_widget->hide();
|
|
close_classification();
|
|
}
|
|
|
|
|
|
public Q_SLOTS:
|
|
|
|
void item_about_to_be_destroyed(CGAL::Three::Scene_item* scene_item) {
|
|
Item_map::iterator it = item_map.find(scene_item);
|
|
if (it != item_map.end())
|
|
{
|
|
Item_classification_base* classif = it->second;
|
|
item_map.erase(it); // first erase from map, because scene->erase will cause a call to this function
|
|
delete classif;
|
|
}
|
|
}
|
|
|
|
void classification_action()
|
|
{
|
|
dock_widget->show();
|
|
dock_widget->raise();
|
|
if (Scene_points_with_normal_item* points_item
|
|
= qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex())))
|
|
create_from_item(points_item);
|
|
else if (Scene_surface_mesh_item* mesh_item
|
|
= qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex())))
|
|
create_from_item(mesh_item);
|
|
}
|
|
|
|
|
|
void ask_for_closing()
|
|
{
|
|
QMessageBox oknotok;
|
|
oknotok.setWindowTitle("Closing classification");
|
|
oknotok.setText("All computed data structures will be discarded.\nColored display will be reinitialized.\nLabels and training information will remain in the classified items.\n\nAre you sure you want to close?");
|
|
oknotok.setStandardButtons(QMessageBox::Yes);
|
|
oknotok.addButton(QMessageBox::No);
|
|
oknotok.setDefaultButton(QMessageBox::Yes);
|
|
|
|
if (oknotok.exec() == QMessageBox::Yes)
|
|
close_classification();
|
|
}
|
|
|
|
void close_classification()
|
|
{
|
|
for (Item_map::iterator it = item_map.begin(); it != item_map.end(); ++ it)
|
|
{
|
|
Item_classification_base* classif = it->second;
|
|
item_changed (classif->item());
|
|
delete classif;
|
|
}
|
|
item_map.clear();
|
|
dock_widget->hide();
|
|
}
|
|
|
|
void item_changed (Scene_item* item)
|
|
{
|
|
scene->itemChanged(item);
|
|
item->invalidateOpenGLBuffers();
|
|
}
|
|
|
|
void update_plugin(int i)
|
|
{
|
|
if (dock_widget->isVisible())
|
|
update_plugin_from_item(get_classification(scene->item(i)));
|
|
}
|
|
|
|
void disable_everything ()
|
|
{
|
|
ui_widget.menu->setEnabled(false);
|
|
ui_widget.display->setEnabled(false);
|
|
ui_widget.classifier->setEnabled(false);
|
|
ui_widget.frame->setEnabled(false);
|
|
}
|
|
|
|
void enable_computation()
|
|
{
|
|
ui_widget.menu->setEnabled(true);
|
|
action_train->setEnabled(false);
|
|
action_reset_local->setEnabled(false);
|
|
action_reset->setEnabled(false);
|
|
action_random_region->setEnabled(false);
|
|
action_validate->setEnabled(false);
|
|
action_save_config->setEnabled(false);
|
|
action_load_config->setEnabled(false);
|
|
action_run->setEnabled(false);
|
|
action_run_smoothed->setEnabled(false);
|
|
action_run_graphcut->setEnabled(false);
|
|
ui_widget.display->setEnabled(true);
|
|
ui_widget.classifier->setEnabled(true);
|
|
}
|
|
|
|
void enable_classif()
|
|
{
|
|
action_train->setEnabled(true);
|
|
action_reset_local->setEnabled(true);
|
|
action_reset->setEnabled(true);
|
|
action_random_region->setEnabled(true);
|
|
action_validate->setEnabled(true);
|
|
action_save_config->setEnabled(true);
|
|
action_load_config->setEnabled(true);
|
|
action_run->setEnabled(true);
|
|
action_run_smoothed->setEnabled(true);
|
|
action_run_graphcut->setEnabled(true);
|
|
ui_widget.frame->setEnabled(true);
|
|
}
|
|
|
|
void update_plugin_from_item(Item_classification_base* classif)
|
|
{
|
|
disable_everything();
|
|
if (classif != NULL)
|
|
{
|
|
enable_computation();
|
|
|
|
// Clear class labels
|
|
for (std::size_t i = 0; i < label_buttons.size(); ++ i)
|
|
{
|
|
ui_widget.labelGrid->removeWidget (label_buttons[i].color_button);
|
|
label_buttons[i].color_button->deleteLater();
|
|
label_buttons[i].menu->deleteLater();
|
|
ui_widget_adv.gridLayout->removeWidget (label_buttons[i].label2);
|
|
delete label_buttons[i].label2;
|
|
ui_widget_adv.gridLayout->removeWidget (label_buttons[i].effect);
|
|
delete label_buttons[i].effect;
|
|
}
|
|
label_buttons.clear();
|
|
|
|
// Add labels
|
|
for (std::size_t i = 0; i < classif->number_of_labels(); ++ i)
|
|
add_new_label (LabelButton (dock_widget,
|
|
classif->label(i)->name().c_str(),
|
|
classif->label_color(i),
|
|
get_shortcut (i, classif->label(i)->name().c_str())));
|
|
add_label_button();
|
|
|
|
// Enabled classif if features computed
|
|
if (classif->features_computed())
|
|
enable_classif();
|
|
|
|
int index = ui_widget.display->currentIndex();
|
|
ui_widget.display->clear();
|
|
ui_widget.display->addItem("Real colors");
|
|
ui_widget.display->addItem("Classification");
|
|
ui_widget.display->addItem("Training sets");
|
|
ui_widget_adv.selected_feature->clear();
|
|
classif->fill_display_combo_box(ui_widget.display, ui_widget_adv.selected_feature);
|
|
if (index >= ui_widget.display->count())
|
|
ui_widget.display->setCurrentIndex(1);
|
|
else
|
|
ui_widget.display->setCurrentIndex(index);
|
|
ui_widget_adv.selected_feature->setCurrentIndex(0);
|
|
}
|
|
}
|
|
|
|
Item_classification_base* get_classification(Scene_item* item = NULL)
|
|
{
|
|
if (!item)
|
|
item = scene->item(scene->mainSelectionIndex());
|
|
|
|
if (!item)
|
|
return NULL;
|
|
|
|
Scene_polyhedron_selection_item* selection_item
|
|
= qobject_cast<Scene_polyhedron_selection_item*>(item);
|
|
if (selection_item)
|
|
item = selection_item->polyhedron_item();
|
|
|
|
Item_map::iterator it = item_map.find(item);
|
|
|
|
if (it != item_map.end())
|
|
{
|
|
if (selection_item)
|
|
dynamic_cast<Surface_mesh_item_classification*>(it->second)->set_selection_item(selection_item);
|
|
return it->second;
|
|
}
|
|
else if (Scene_points_with_normal_item* points_item
|
|
= qobject_cast<Scene_points_with_normal_item*>(item))
|
|
return create_from_item(points_item);
|
|
else if (Scene_surface_mesh_item* mesh_item
|
|
= qobject_cast<Scene_surface_mesh_item*>(item))
|
|
return create_from_item(mesh_item);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Item_classification_base* create_from_item(Scene_points_with_normal_item* points_item)
|
|
{
|
|
if (item_map.find(points_item) != item_map.end())
|
|
return item_map[points_item];
|
|
|
|
bool use_clusters = false;
|
|
|
|
if (points_item->point_set()->has_property_map<int> ("shape"))
|
|
{
|
|
QMessageBox::StandardButton reply
|
|
= QMessageBox::question(NULL, "Point Set Classification",
|
|
"This point set is divided in clusters. Do you want to classify clusters instead of points?",
|
|
QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
|
|
|
|
use_clusters = (reply == QMessageBox::Yes);
|
|
}
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
Item_classification_base* classif;
|
|
if (use_clusters)
|
|
classif = new Cluster_classification (points_item);
|
|
else
|
|
classif = new Point_set_item_classification (points_item);
|
|
|
|
item_map.insert (std::make_pair (points_item, classif));
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
update_plugin_from_item(classif);
|
|
return classif;
|
|
}
|
|
|
|
Item_classification_base* create_from_item(Scene_surface_mesh_item* mesh_item)
|
|
{
|
|
if (item_map.find(mesh_item) != item_map.end())
|
|
return item_map[mesh_item];
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
Item_classification_base* classif
|
|
= new Surface_mesh_item_classification (mesh_item);
|
|
item_map.insert (std::make_pair (mesh_item, classif));
|
|
QApplication::restoreOverrideCursor();
|
|
update_plugin_from_item(classif);
|
|
return classif;
|
|
}
|
|
|
|
void run (Item_classification_base* classif, int method,
|
|
std::size_t subdivisions = 1,
|
|
double smoothing = 0.5)
|
|
{
|
|
classif->run (method, ui_widget.classifier->currentIndex(), subdivisions, smoothing);
|
|
}
|
|
|
|
void on_classifier_changed (int index)
|
|
{
|
|
if (index == 0)
|
|
{
|
|
dock_widget_adv->show();
|
|
dock_widget_adv->raise();
|
|
}
|
|
else
|
|
dock_widget_adv->hide();
|
|
}
|
|
|
|
void on_compute_features_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
bool ok = false;
|
|
int nb_scales = QInputDialog::getInt((QWidget*)mw,
|
|
tr("Compute Features"), // dialog title
|
|
tr("Number of scales:"), // field label
|
|
5, 1, 99, 1, &ok);
|
|
if (!ok)
|
|
return;
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
classif->compute_features (std::size_t(nb_scales));
|
|
|
|
update_plugin_from_item(classif);
|
|
QApplication::restoreOverrideCursor();
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_save_config_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QString filename;
|
|
|
|
if (ui_widget.classifier->currentIndex() == 0)
|
|
filename = QFileDialog::getSaveFileName(mw,
|
|
tr("Save classification configuration"),
|
|
tr("%1 (CGAL classif config).xml").arg(classif->item()->name()),
|
|
"CGAL classification configuration (*.xml);;");
|
|
else if (ui_widget.classifier->currentIndex() == 1)
|
|
filename = QFileDialog::getSaveFileName(mw,
|
|
tr("Save classification configuration"),
|
|
tr("%1 (ETHZ random forest config).gz").arg(classif->item()->name()),
|
|
"Compressed ETHZ random forest configuration (*.gz);;");
|
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
|
else if (ui_widget.classifier->currentIndex() == 2)
|
|
filename = QFileDialog::getSaveFileName(mw,
|
|
tr("Save classification configuration"),
|
|
tr("%1 (OpenCV %2.%3 random forest config).xml")
|
|
.arg(classif->item()->name())
|
|
.arg(CV_MAJOR_VERSION)
|
|
.arg(CV_MINOR_VERSION),
|
|
tr("OpenCV %2.%3 random forest configuration (*.xml);;")
|
|
.arg(CV_MAJOR_VERSION)
|
|
.arg(CV_MINOR_VERSION));
|
|
#endif
|
|
|
|
if (filename == QString())
|
|
return;
|
|
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
classif->save_config (filename.toStdString().c_str(),
|
|
ui_widget.classifier->currentIndex());
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
}
|
|
|
|
void on_load_config_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
QString filename;
|
|
|
|
if (ui_widget.classifier->currentIndex() == 0)
|
|
filename = QFileDialog::getOpenFileName(mw,
|
|
tr("Open CGAL classification configuration"),
|
|
".",
|
|
"CGAL classification configuration (*.xml);;All Files (*)");
|
|
else if (ui_widget.classifier->currentIndex() == 1)
|
|
filename = QFileDialog::getOpenFileName(mw,
|
|
tr("Open ETHZ random forest configuration"),
|
|
".",
|
|
"Compressed ETHZ random forest configuration (*.gz);;All Files (*)");
|
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
|
else if (ui_widget.classifier->currentIndex() == 2)
|
|
filename = QFileDialog::getOpenFileName(mw,
|
|
tr("Open OpenCV %2.%3 random forest configuration")
|
|
.arg(CV_MAJOR_VERSION)
|
|
.arg(CV_MINOR_VERSION),
|
|
".",
|
|
tr("OpenCV %2.%3 random forest configuration (*.xml);;All Files (*)")
|
|
.arg(CV_MAJOR_VERSION)
|
|
.arg(CV_MINOR_VERSION));
|
|
#endif
|
|
|
|
if (filename == QString())
|
|
return;
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
classif->load_config (filename.toStdString().c_str(),
|
|
ui_widget.classifier->currentIndex());
|
|
update_plugin_from_item(classif);
|
|
run (classif, 0);
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
|
|
void on_display_button_clicked(int index)
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
return;
|
|
|
|
classif->change_color (index);
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_run_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
CGAL::Real_timer t;
|
|
t.start();
|
|
run (classif, 0);
|
|
t.stop();
|
|
std::cerr << "Raw classification computed in " << t.time() << " second(s)" << std::endl;
|
|
QApplication::restoreOverrideCursor();
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_run_smoothed_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
CGAL::Real_timer t;
|
|
t.start();
|
|
run (classif, 1);
|
|
t.stop();
|
|
std::cerr << "Smoothed classification computed in " << t.time() << " second(s)" << std::endl;
|
|
QApplication::restoreOverrideCursor();
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_run_graphcut_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QMultipleInputDialog dialog ("Classify with Graph Cut", mw);
|
|
QSpinBox* subdivisions = dialog.add<QSpinBox> ("Number of subdivisons: ");
|
|
subdivisions->setRange (1, 9999);
|
|
subdivisions->setValue (16);
|
|
|
|
QDoubleSpinBox* smoothing = dialog.add<QDoubleSpinBox> ("Regularization weight: ");
|
|
smoothing->setRange (0.0, 100.0);
|
|
smoothing->setValue (0.5);
|
|
smoothing->setSingleStep (0.1);
|
|
|
|
if (dialog.exec() != QDialog::Accepted)
|
|
return;
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
CGAL::Real_timer t;
|
|
t.start();
|
|
run (classif, 2, std::size_t(subdivisions->value()), smoothing->value());
|
|
t.stop();
|
|
std::cerr << "Graph Cut classification computed in " << t.time() << " second(s)" << std::endl;
|
|
QApplication::restoreOverrideCursor();
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_create_point_set_item()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QPushButton* label_clicked = qobject_cast<QPushButton*>(QObject::sender()->parent()->parent());
|
|
|
|
if (label_clicked == NULL)
|
|
std::cerr << "Error" << std::endl;
|
|
else
|
|
{
|
|
int index = ui_widget.labelGrid->indexOf(label_clicked);
|
|
int row_index, column_index, row_span, column_span;
|
|
ui_widget.labelGrid->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
|
|
|
int position = row_index * 3 + column_index;
|
|
|
|
Scene_item* item = classif->generate_one_item
|
|
(classif->item()->name().toStdString().c_str(), position);
|
|
|
|
Scene_points_with_normal_item* points_item
|
|
= qobject_cast<Scene_points_with_normal_item*>(item);
|
|
|
|
if (!points_item)
|
|
return;
|
|
|
|
if (points_item->point_set()->empty())
|
|
delete points_item;
|
|
else
|
|
scene->addItem (points_item);
|
|
}
|
|
}
|
|
|
|
void on_generate_items_button_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
std::vector<Scene_item*> new_items;
|
|
classif->generate_one_item_per_label
|
|
(new_items, classif->item()->name().toStdString().c_str());
|
|
|
|
for (std::size_t i = 0; i < new_items.size(); ++ i)
|
|
{
|
|
Scene_points_with_normal_item* points_item
|
|
= qobject_cast<Scene_points_with_normal_item*>(new_items[i]);
|
|
if (!points_item)
|
|
continue;
|
|
|
|
if (points_item->point_set()->empty())
|
|
delete points_item;
|
|
else
|
|
scene->addItem (points_item);
|
|
}
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
void clear_labels (Item_classification_base* classif)
|
|
{
|
|
classif->clear_labels();
|
|
}
|
|
|
|
void add_new_label (Item_classification_base* classif, const std::string& name)
|
|
{
|
|
add_new_label (LabelButton (dock_widget,
|
|
name.c_str(),
|
|
QColor (64 + rand() % 192,
|
|
64 + rand() % 192,
|
|
64 + rand() % 192),
|
|
get_shortcut (label_buttons.size(), name.c_str())));
|
|
QColor color = classif->add_new_label (name.c_str());
|
|
label_buttons.back().change_color (color);
|
|
}
|
|
|
|
void on_add_new_label_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
QString name =
|
|
QInputDialog::getText((QWidget*)mw,
|
|
tr("Add new labels"), // dialog title
|
|
tr("Names (separated by spaces):"), // field label
|
|
QLineEdit::Normal,
|
|
tr("label_%1").arg(label_buttons.size()),
|
|
&ok);
|
|
if (!ok)
|
|
return;
|
|
|
|
std::istringstream iss (name.toStdString());
|
|
|
|
std::string n;
|
|
while (iss >> n)
|
|
add_new_label (classif, n);
|
|
|
|
add_label_button();
|
|
update_plugin_from_item(classif);
|
|
}
|
|
|
|
void on_use_config_building_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
if (classif->number_of_labels() != 0)
|
|
{
|
|
QMessageBox::StandardButton reply
|
|
= QMessageBox::question(NULL, "Classification",
|
|
"Current labels will be discarded. Continue?",
|
|
QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
|
|
|
|
if (reply == QMessageBox::No)
|
|
return;
|
|
}
|
|
clear_labels (classif);
|
|
add_new_label (classif, "ground");
|
|
add_new_label (classif, "vegetation");
|
|
add_new_label (classif, "building");
|
|
update_plugin_from_item (classif);
|
|
}
|
|
|
|
void on_use_config_roof_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
if (classif->number_of_labels() != 0)
|
|
{
|
|
QMessageBox::StandardButton reply
|
|
= QMessageBox::question(NULL, "Classification",
|
|
"Current labels will be discarded. Continue?",
|
|
QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
|
|
|
|
if (reply == QMessageBox::No)
|
|
return;
|
|
}
|
|
clear_labels (classif);
|
|
add_new_label (classif, "ground");
|
|
add_new_label (classif, "vegetation");
|
|
add_new_label (classif, "roof");
|
|
add_new_label (classif, "facade");
|
|
update_plugin_from_item (classif);
|
|
}
|
|
|
|
void on_use_las_config_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
if (classif->number_of_labels() != 0)
|
|
{
|
|
QMessageBox::StandardButton reply
|
|
= QMessageBox::question(NULL, "Classification",
|
|
"Current labels will be discarded. Continue?",
|
|
QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
|
|
|
|
if (reply == QMessageBox::No)
|
|
return;
|
|
}
|
|
clear_labels (classif);
|
|
add_new_label (classif, "ground");
|
|
add_new_label (classif, "low_veget");
|
|
add_new_label (classif, "med_veget");
|
|
add_new_label (classif, "high_veget");
|
|
add_new_label (classif, "building");
|
|
add_new_label (classif, "noise");
|
|
add_new_label (classif, "reserved");
|
|
add_new_label (classif, "water");
|
|
add_new_label (classif, "rail");
|
|
add_new_label (classif, "road_surface");
|
|
add_new_label (classif, "reserved_2");
|
|
add_new_label (classif, "wire_guard");
|
|
add_new_label (classif, "wire_conduct");
|
|
add_new_label (classif, "trans_tower");
|
|
add_new_label (classif, "wire_connect");
|
|
add_new_label (classif, "bridge_deck");
|
|
add_new_label (classif, "high_noise");
|
|
update_plugin_from_item (classif);
|
|
}
|
|
|
|
void on_clear_labels_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
if (classif->number_of_labels() != 0)
|
|
{
|
|
QMessageBox::StandardButton reply
|
|
= QMessageBox::question(NULL, "Classification",
|
|
"Current labels will be discarded. Continue?",
|
|
QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
|
|
|
|
if (reply == QMessageBox::No)
|
|
return;
|
|
}
|
|
clear_labels (classif);
|
|
update_plugin_from_item (classif);
|
|
}
|
|
|
|
void on_reset_training_sets_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
classif->reset_training_sets();
|
|
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_reset_training_set_of_selection_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
classif->reset_training_set_of_selection();
|
|
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_reset_training_set_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QPushButton* label_clicked = qobject_cast<QPushButton*>(QObject::sender()->parent()->parent());
|
|
if (label_clicked == NULL)
|
|
std::cerr << "Error" << std::endl;
|
|
else
|
|
{
|
|
int index = ui_widget.labelGrid->indexOf(label_clicked);
|
|
int row_index, column_index, row_span, column_span;
|
|
ui_widget.labelGrid->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
|
|
|
int position = row_index * 3 + column_index;
|
|
|
|
classif->reset_training_set
|
|
(position);
|
|
}
|
|
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_validate_selection_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
classif->validate_selection();
|
|
}
|
|
|
|
void on_select_random_region_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
classif->select_random_region();
|
|
item_changed(classif->item());
|
|
|
|
CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin();
|
|
CGAL::Bbox_3 bbox = classif->bbox();
|
|
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(viewer)->offset();
|
|
|
|
viewer->camera()->fitBoundingBox(CGAL::qglviewer::Vec (bbox.xmin(), bbox.ymin(), bbox.zmin()) + offset,
|
|
CGAL::qglviewer::Vec (bbox.xmax(), bbox.ymax(), bbox.zmax()) + offset);
|
|
|
|
|
|
|
|
viewer->camera()->setPivotPoint (CGAL::qglviewer::Vec ((bbox.xmin() + bbox.xmax()) / 2.,
|
|
(bbox.ymin() + bbox.ymax()) / 2.,
|
|
(bbox.zmin() + bbox.zmax()) / 2.) + offset);
|
|
}
|
|
|
|
void on_train_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
int nb_trials = 0;
|
|
int num_trees = 0;
|
|
int max_depth = 0;
|
|
|
|
if (ui_widget.classifier->currentIndex() == 0 || ui_widget.classifier->currentIndex() == 3)
|
|
{
|
|
bool ok = false;
|
|
nb_trials = QInputDialog::getInt((QWidget*)mw,
|
|
tr("Train Classifier"), // dialog title
|
|
tr("Number of trials:"), // field label
|
|
800, 1, 99999, 50, &ok);
|
|
if (!ok)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
QMultipleInputDialog dialog ("Train Random Forest Classifier", mw);
|
|
QSpinBox* trees = dialog.add<QSpinBox> ("Number of trees: ");
|
|
trees->setRange (1, 9999);
|
|
trees->setValue (25);
|
|
QSpinBox* depth = dialog.add<QSpinBox> ("Maximum depth of tree: ");
|
|
depth->setRange (1, 9999);
|
|
depth->setValue (20);
|
|
|
|
if (dialog.exec() != QDialog::Accepted)
|
|
return;
|
|
num_trees = trees->value();
|
|
max_depth = depth->value();
|
|
}
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
CGAL::Real_timer t;
|
|
t.start();
|
|
classif->train(ui_widget.classifier->currentIndex(), nb_trials,
|
|
num_trees, max_depth);
|
|
t.stop();
|
|
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
|
QApplication::restoreOverrideCursor();
|
|
update_plugin_from_item(classif);
|
|
}
|
|
|
|
char get_shortcut (std::size_t position, const char* name)
|
|
{
|
|
std::set<char> used_letters;
|
|
|
|
used_letters.insert('t'); // used for "train"
|
|
used_letters.insert('s'); // used for "random select"
|
|
for (std::size_t i = 0; i < label_buttons.size(); ++ i)
|
|
if (i != position)
|
|
used_letters.insert (label_buttons[i].shortcut);
|
|
|
|
std::size_t idx = 0;
|
|
while (name[idx] != '\0')
|
|
{
|
|
if (std::isalpha(name[idx]) &&
|
|
used_letters.find (std::tolower(name[idx])) == used_letters.end())
|
|
return std::tolower(name[idx]);
|
|
++ idx;
|
|
}
|
|
|
|
char fallback = 'a';
|
|
while (used_letters.find (fallback) != used_letters.end())
|
|
++ fallback;
|
|
|
|
return fallback;
|
|
}
|
|
|
|
void add_new_label (const LabelButton& label_button)
|
|
{
|
|
label_buttons.push_back (label_button);
|
|
int position = static_cast<int>(label_buttons.size()) - 1;
|
|
|
|
int x = position / 3;
|
|
int y = position % 3;
|
|
|
|
ui_widget.labelGrid->addWidget (label_buttons.back().color_button, x, y);
|
|
|
|
QAction* add_selection = label_buttons.back().menu->addAction ("Add selection to training set");
|
|
|
|
add_selection->setShortcut(Qt::SHIFT | (Qt::Key_A + (label_button.shortcut - 'a')));
|
|
// add_selection->setShortcut(Qt::Key_0 + label_buttons.size() - 1);
|
|
|
|
connect(add_selection, SIGNAL(triggered()), this,
|
|
SLOT(on_add_selection_to_training_set_clicked()));
|
|
|
|
QAction* reset = label_buttons.back().menu->addAction ("Reset training set");
|
|
connect(reset, SIGNAL(triggered()), this,
|
|
SLOT(on_reset_training_set_clicked()));
|
|
|
|
label_buttons.back().menu->addSeparator();
|
|
|
|
QAction* change_color = label_buttons.back().menu->addAction ("Change color");
|
|
connect(change_color, SIGNAL(triggered()), this,
|
|
SLOT(on_color_changed_clicked()));
|
|
|
|
QAction* create = label_buttons.back().menu->addAction ("Create point set item from labeled points");
|
|
connect(create, SIGNAL(triggered()), this,
|
|
SLOT(on_create_point_set_item()));
|
|
|
|
label_buttons.back().menu->addSeparator();
|
|
|
|
QAction* remove_label = label_buttons.back().menu->addAction ("Remove label");
|
|
connect(remove_label, SIGNAL(triggered()), this,
|
|
SLOT(on_remove_label_clicked()));
|
|
|
|
ui_widget_adv.gridLayout->addWidget (label_buttons.back().label2, position + 1, 0);
|
|
ui_widget_adv.gridLayout->addWidget (label_buttons.back().effect, position + 1, 2);
|
|
|
|
connect(label_buttons.back().effect, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(on_effect_changed(int)));
|
|
}
|
|
|
|
void add_label_button()
|
|
{
|
|
int position = static_cast<int>(label_buttons.size());
|
|
int x = position / 3;
|
|
int y = position % 3;
|
|
|
|
label_button->setVisible (true);
|
|
ui_widget.labelGrid->addWidget (label_button, x, y);
|
|
}
|
|
|
|
void on_remove_label_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QPushButton* label_clicked = qobject_cast<QPushButton*>(QObject::sender()->parent()->parent());
|
|
if (label_clicked == NULL)
|
|
std::cerr << "Error" << std::endl;
|
|
else
|
|
{
|
|
int index = ui_widget.labelGrid->indexOf(label_clicked);
|
|
int row_index, column_index, row_span, column_span;
|
|
ui_widget.labelGrid->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
|
|
|
int position = row_index * 3 + column_index;
|
|
|
|
classif->remove_label (position);
|
|
|
|
ui_widget.labelGrid->removeWidget (label_buttons[position].color_button);
|
|
label_buttons[position].color_button->deleteLater();
|
|
label_buttons[position].menu->deleteLater();
|
|
|
|
ui_widget_adv.gridLayout->removeWidget (label_buttons[position].label2);
|
|
delete label_buttons[position].label2;
|
|
ui_widget_adv.gridLayout->removeWidget (label_buttons[position].effect);
|
|
delete label_buttons[position].effect;
|
|
|
|
if (label_buttons.size() > 1)
|
|
for (int i = position + 1; i < static_cast<int>(label_buttons.size()); ++ i)
|
|
{
|
|
int position = i - 1;
|
|
int x = position / 3;
|
|
int y = position % 3;
|
|
|
|
ui_widget.labelGrid->addWidget (label_buttons[i].color_button, x, y);
|
|
ui_widget_adv.gridLayout->addWidget (label_buttons[i].label2, (int)i, 0);
|
|
ui_widget_adv.gridLayout->addWidget (label_buttons[i].effect, (int)i, 2);
|
|
}
|
|
|
|
label_buttons.erase (label_buttons.begin() + position);
|
|
add_label_button();
|
|
}
|
|
update_plugin_from_item(classif);
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_color_changed_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QPushButton* label_clicked = qobject_cast<QPushButton*>(QObject::sender()->parent()->parent());
|
|
if (label_clicked == NULL)
|
|
std::cerr << "Error" << std::endl;
|
|
else
|
|
{
|
|
int index = ui_widget.labelGrid->indexOf(label_clicked);
|
|
int row_index, column_index, row_span, column_span;
|
|
ui_widget.labelGrid->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
|
|
|
int position = row_index * 3 + column_index;
|
|
|
|
QColor color = label_buttons[position].color;
|
|
color = QColorDialog::getColor(color, (QWidget*)mw, "Change of color of label");
|
|
label_buttons[position].change_color (color);
|
|
classif->change_label_color (position,
|
|
color);
|
|
}
|
|
classif->update_color ();
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_add_selection_to_training_set_clicked()
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
QPushButton* label_clicked = qobject_cast<QPushButton*>(QObject::sender()->parent()->parent());
|
|
if (label_clicked == NULL)
|
|
std::cerr << "Error" << std::endl;
|
|
else
|
|
{
|
|
int index = ui_widget.labelGrid->indexOf(label_clicked);
|
|
int row_index, column_index, row_span, column_span;
|
|
ui_widget.labelGrid->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
|
|
|
int position = row_index * 3 + column_index;
|
|
classif->add_selection_to_training_set
|
|
(position);
|
|
}
|
|
|
|
item_changed(classif->item());
|
|
}
|
|
|
|
void on_selected_feature_changed(int v)
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
|
|
if (classif->number_of_features() <= (std::size_t)v)
|
|
return;
|
|
|
|
Item_classification_base::Feature_handle
|
|
att = classif->feature(v);
|
|
|
|
if (att == Item_classification_base::Feature_handle())
|
|
return;
|
|
|
|
ui_widget_adv.feature_weight->setValue ((int)(1001. * 2. * std::atan(classif->weight(att)) / CGAL_PI));
|
|
|
|
for (std::size_t i = 0; i < classif->number_of_labels(); ++ i)
|
|
{
|
|
CGAL::Classification::Sum_of_weighted_features_classifier::Effect
|
|
eff = classif->effect (classif->label(i), att);
|
|
if (eff == CGAL::Classification::Sum_of_weighted_features_classifier::PENALIZING)
|
|
label_buttons[i].effect->setCurrentIndex(0);
|
|
else if (eff == CGAL::Classification::Sum_of_weighted_features_classifier::NEUTRAL)
|
|
label_buttons[i].effect->setCurrentIndex(1);
|
|
else
|
|
label_buttons[i].effect->setCurrentIndex(2);
|
|
}
|
|
}
|
|
|
|
|
|
void on_feature_weight_changed(int v)
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
Item_classification_base::Feature_handle
|
|
att = classif->feature(ui_widget_adv.selected_feature->currentIndex());
|
|
|
|
if (att == Item_classification_base::Feature_handle())
|
|
return;
|
|
|
|
classif->set_weight(att, std::tan ((CGAL_PI/2.) * v / 1001.));
|
|
|
|
for (std::size_t i = 0; i < label_buttons.size(); ++ i)
|
|
label_buttons[i].effect->setEnabled(classif->weight(att) != 0.);
|
|
}
|
|
|
|
void on_effect_changed (int v)
|
|
{
|
|
Item_classification_base* classif
|
|
= get_classification();
|
|
if(!classif)
|
|
{
|
|
print_message("Error: there is no point set classification item!");
|
|
return;
|
|
}
|
|
Item_classification_base::Feature_handle
|
|
att = classif->feature(ui_widget_adv.selected_feature->currentIndex());
|
|
|
|
if (att == Item_classification_base::Feature_handle())
|
|
return;
|
|
|
|
QComboBox* combo = qobject_cast<QComboBox*>(QObject::sender());
|
|
for (std::size_t i = 0;i < label_buttons.size(); ++ i)
|
|
if (label_buttons[i].effect == combo)
|
|
{
|
|
// std::cerr << att->id() << " is ";
|
|
if (v == 0)
|
|
{
|
|
classif->set_effect(classif->label(i),
|
|
att, CGAL::Classification::Sum_of_weighted_features_classifier::PENALIZING);
|
|
}
|
|
else if (v == 1)
|
|
{
|
|
classif->set_effect(classif->label(i),
|
|
att, CGAL::Classification::Sum_of_weighted_features_classifier::NEUTRAL);
|
|
}
|
|
else
|
|
{
|
|
classif->set_effect(classif->label(i),
|
|
att, CGAL::Classification::Sum_of_weighted_features_classifier::FAVORING);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private:
|
|
Messages_interface* messages;
|
|
QAction* actionClassification;
|
|
|
|
QDockWidget* dock_widget;
|
|
QDockWidget* dock_widget_adv;
|
|
QAction* action_train;
|
|
QAction* action_reset_local;
|
|
QAction* action_reset;
|
|
QAction* action_random_region;
|
|
QAction* action_validate;
|
|
QAction* action_save_config;
|
|
QAction* action_load_config;
|
|
QAction* action_run;
|
|
QAction* action_run_smoothed;
|
|
QAction* action_run_graphcut;
|
|
|
|
std::vector<LabelButton> label_buttons;
|
|
QPushButton* label_button;
|
|
|
|
Ui::Classification ui_widget;
|
|
Ui::ClassificationAdvanced ui_widget_adv;
|
|
|
|
QColor color_att;
|
|
|
|
typedef std::map<Scene_item*, Item_classification_base*> Item_map;
|
|
Item_map item_map;
|
|
|
|
|
|
}; // end Polyhedron_demo_classification_plugin
|
|
|
|
#include "Classification_plugin.moc"
|