diff --git a/CMakeLists.txt b/CMakeLists.txt index 6196098..f56d5d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ if ( PLUGIN_STANDARD_QCOLORIMETRIC_SEGMENTER ) ${CMAKE_CURRENT_SOURCE_DIR}/qColorimetricSegmenter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/qColorimetricSegmenter.h ${CMAKE_CURRENT_SOURCE_DIR}/qColorimetricSegmenter.qrc + ${CMAKE_CURRENT_SOURCE_DIR}/HSV.h ${CMAKE_CURRENT_SOURCE_DIR}/HSVDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HSVDialog.h ${CMAKE_CURRENT_SOURCE_DIR}/HSVDialog.ui diff --git a/HSV.h b/HSV.h new file mode 100644 index 0000000..d483937 --- /dev/null +++ b/HSV.h @@ -0,0 +1,73 @@ +#pragma once + +//########################################################################## +//# # +//# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # +//# # +//# This program is free software; you can redistribute it and/or modify # +//# it under the terms of the GNU General Public License as published by # +//# the Free Software Foundation; version 2 of the License. # +//# # +//# This program is distributed in the hope that it will be useful, # +//# but WITHOUT ANY WARRANTY; without even the implied warranty of # +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +//# GNU General Public License for more details. # +//# # +//# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # +//# # +//########################################################################## + +//qCC_db +#include + +//! HSV color +struct Hsv +{ + //! Default constrctor + Hsv() + : h(0) + , s(0) + , v(0) + { + } + + //! Constrctor from a RGB color + Hsv(const ccColor::Rgb& rgb) + { + float r = rgb.r / 255.0f; + float g = rgb.g / 255.0f; + float b = rgb.b / 255.0f; + float maxComp = std::max(std::max(r, g), b); + float minComp = std::min(std::min(r, g), b); + float deltaComp = maxComp - minComp; + + h = 0; + if (deltaComp != 0) + { + if (r == maxComp) + { + h = (g - b) / deltaComp; + } + else + { + if (g == maxComp) + { + h = 2 + (b - r) / deltaComp; + } + else + { + h = 4 + (r - g) / deltaComp; + } + } + h *= 60; + if (h < 0) + h += 360; + } + + s = (maxComp == 0 ? 0 : (deltaComp / maxComp) * 100); + v = maxComp * 100; + } + + // HSV components + float h, s, v; +}; diff --git a/HSVDialog.cpp b/HSVDialog.cpp index d35ab41..427ba53 100644 --- a/HSVDialog.cpp +++ b/HSVDialog.cpp @@ -14,29 +14,20 @@ //# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # //# # //########################################################################## + #include "HSVDialog.h" -//local -#include "mainwindow.h" +//Local +#include "HSV.h" -//Qt -#include -#include -#include -#include -#include -#include -#include - -//common +//qCC #include +//qCC_db #include -#include -//qCC_gl -#include -#include +//Qt +#include /* Constructor @@ -44,7 +35,6 @@ HSVDialog::HSVDialog(ccPickingHub* pickingHub, QWidget* parent) : QDialog(parent) , Ui::HSVDialog() - , m_pickingWin(0) , m_pickingHub(pickingHub) { assert(pickingHub); @@ -52,12 +42,11 @@ HSVDialog::HSVDialog(ccPickingHub* pickingHub, QWidget* parent) setModal(false); setupUi(this); - //restore semi-persistent parameters red->setValue(0); green->setValue(0); blue->setValue(0); - //Link between Ui and actions + //link between Ui and actions connect(pointPickingButton_first, &QCheckBox::toggled, this, &HSVDialog::pickPoint); connect(red, static_cast(&QDoubleSpinBox::valueChanged), this, &HSVDialog::updateValues); connect(green, static_cast(&QDoubleSpinBox::valueChanged), this, &HSVDialog::updateValues); @@ -66,7 +55,9 @@ HSVDialog::HSVDialog(ccPickingHub* pickingHub, QWidget* parent) //auto disable picking mode on quit connect(this, &QDialog::finished, [&]() { - if (pointPickingButton_first->isChecked()) pointPickingButton_first->setChecked(false); + //if (pointPickingButton_first->isChecked()) pointPickingButton_first->setChecked(false); + if (m_pickingHub) + m_pickingHub->removeListener(this); } ); } @@ -80,6 +71,7 @@ void HSVDialog::pickPoint(bool state) { return; } + if (state) { if (!m_pickingHub->addListener(this, true)) @@ -92,9 +84,10 @@ void HSVDialog::pickPoint(bool state) { m_pickingHub->removeListener(this); } - pointPickingButton_first->blockSignals(true); - pointPickingButton_first->setChecked(state); - pointPickingButton_first->blockSignals(false); + + pointPickingButton_first->blockSignals(true); + pointPickingButton_first->setChecked(state); + pointPickingButton_first->blockSignals(false); } /* @@ -103,14 +96,14 @@ void HSVDialog::pickPoint(bool state) void HSVDialog::onItemPicked(const PickedItem& pi) { assert(pi.entity); - m_pickingWin = m_pickingHub->activeWindow(); if (pi.entity->isKindOf(CC_TYPES::POINT_CLOUD)) { //Get RGB values of the picked point ccGenericPointCloud* cloud = static_cast(pi.entity); - const ccColor::Rgb& rgb = cloud->getPointColor(pi.itemIndex); - if (pointPickingButton_first->isChecked()) { + const ccColor::Rgba& rgb = cloud->getPointColor(pi.itemIndex); + if (pointPickingButton_first->isChecked()) + { ccLog::Print("Point picked"); //blocking signals to avoid updating 2 times hsv values for nothing @@ -127,7 +120,6 @@ void HSVDialog::onItemPicked(const PickedItem& pi) pointPickingButton_first->setChecked(false); } } - } /* @@ -137,48 +129,8 @@ void HSVDialog::updateValues() { ccColor::Rgb rgb(red->value(), green->value(), blue->value()); - hsv hsv_values = rgb2hsv(rgb); + Hsv hsv_values(rgb); hue_first->setValue(hsv_values.h); sat_first->setValue(hsv_values.s); val_first->setValue(hsv_values.v); } - -/* - Method to convert from rgb values to hsv values - return : hsv struct -*/ -hsv HSVDialog::rgb2hsv(ccColor::Rgb rgb) { - hsv res; - float r = rgb.r / 255.0f; - float g = rgb.g / 255.0f; - float b = rgb.b / 255.0f; - float max = std::max(std::max(r, g), b); - float min = std::min(std::min(r, g), b); - float delta = max - min; - - res.v = max; - if (delta != 0) { - float hue; - if (r == max) { - hue = (g - b) / delta; - } - else { - if (g == max) { - hue = 2 + (b - r) / delta; - } - else { - hue = 4 + (r - g) / delta; - } - } - hue *= 60; - if (hue < 0) hue += 360; - res.h = hue; - } - else { - res.h = 0; - } - res.s = max == 0 ? 0 : ((max - min) / max) * 100; - res.v = max * 100; - - return res; -} \ No newline at end of file diff --git a/HSVDialog.h b/HSVDialog.h index be5d0f9..d8911d1 100644 --- a/HSVDialog.h +++ b/HSVDialog.h @@ -1,3 +1,5 @@ +#pragma once + //########################################################################## //# # //# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # @@ -15,31 +17,14 @@ //# # //########################################################################## -#ifndef HSVDialog_H -#define HSVDialog_H - #include -#include "ccPickingListener.h" +#include //Qt -#include #include -#include -class ccGLWindow; -class ccPlane; -class ccHObject; class ccPickingHub; -/* - Struct for HSV -*/ -typedef struct { - double h; - double s; - double v; -} hsv; - /* Get the values of the HSV interface, and interactions */ @@ -47,26 +32,19 @@ class HSVDialog : public QDialog, public ccPickingListener, public Ui::HSVDialog { Q_OBJECT public: - explicit HSVDialog(ccPickingHub* pickingHub, QWidget* parent = 0); + explicit HSVDialog(ccPickingHub* pickingHub, QWidget* parent = nullptr); //! Inherited from ccPickingListener virtual void onItemPicked(const PickedItem& pi); - hsv rgb2hsv(ccColor::Rgb rgb); - public slots: void pickPoint(bool); void updateValues(); protected: //members - //! Picking window (if any) - ccGLWindow* m_pickingWin; - //! Picking hub ccPickingHub* m_pickingHub; }; - -#endif // HSVDialog_H diff --git a/HSVDialog.ui b/HSVDialog.ui index f3c484f..ec33d80 100644 --- a/HSVDialog.ui +++ b/HSVDialog.ui @@ -233,7 +233,7 @@ Pick the plane center (click again to cancel) - + :/CC/images/ccPointPicking.png:/CC/images/ccPointPicking.png @@ -314,7 +314,7 @@ - + diff --git a/KmeansDlg.cpp b/KmeansDlg.cpp index 9e38ae4..6b24324 100644 --- a/KmeansDlg.cpp +++ b/KmeansDlg.cpp @@ -1,12 +1,25 @@ #include "KmeansDlg.h" +//########################################################################## +//# # +//# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # +//# # +//# This program is free software; you can redistribute it and/or modify # +//# it under the terms of the GNU General Public License as published by # +//# the Free Software Foundation; version 2 of the License. # +//# # +//# This program is distributed in the hope that it will be useful, # +//# but WITHOUT ANY WARRANTY; without even the implied warranty of # +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +//# GNU General Public License for more details. # +//# # +//# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # +//# # +//########################################################################## -#include KmeansDlg::KmeansDlg(QWidget* parent) : QDialog(parent) , Ui::KmeansDialog() { - setupUi(this); - } diff --git a/KmeansDlg.h b/KmeansDlg.h index 020ad34..bc2f16b 100644 --- a/KmeansDlg.h +++ b/KmeansDlg.h @@ -1,6 +1,21 @@ -#ifndef KMEANSDIALOG_H -#define KMEANSDIALOG_H +#pragma once +//########################################################################## +//# # +//# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # +//# # +//# This program is free software; you can redistribute it and/or modify # +//# it under the terms of the GNU General Public License as published by # +//# the Free Software Foundation; version 2 of the License. # +//# # +//# This program is distributed in the hope that it will be useful, # +//# but WITHOUT ANY WARRANTY; without even the implied warranty of # +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +//# GNU General Public License for more details. # +//# # +//# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # +//# # +//########################################################################## #include @@ -9,10 +24,9 @@ class KmeansDlg : public QDialog, public Ui::KmeansDialog { -Q_OBJECT + Q_OBJECT + public: - explicit KmeansDlg(QWidget* parent = 0); + explicit KmeansDlg(QWidget* parent = nullptr); }; - -#endif \ No newline at end of file diff --git a/KmeansDlg.ui b/KmeansDlg.ui index 1bc77ec..0d2148b 100644 --- a/KmeansDlg.ui +++ b/KmeansDlg.ui @@ -42,7 +42,10 @@ 1 - 255 + 65536 + + + 16 @@ -66,7 +69,7 @@ - Number of iterations + Max number of iterations @@ -89,7 +92,10 @@ 1 - 255 + 1000 + + + 10 diff --git a/QuantiDialog.cpp b/QuantiDialog.cpp index f780cbf..d3c5272 100644 --- a/QuantiDialog.cpp +++ b/QuantiDialog.cpp @@ -1,25 +1,35 @@ #include "QuantiDialog.h" -#include -#include +//########################################################################## +//# # +//# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # +//# # +//# This program is free software; you can redistribute it and/or modify # +//# it under the terms of the GNU General Public License as published by # +//# the Free Software Foundation; version 2 of the License. # +//# # +//# This program is distributed in the hope that it will be useful, # +//# but WITHOUT ANY WARRANTY; without even the implied warranty of # +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +//# GNU General Public License for more details. # +//# # +//# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # +//# # +//########################################################################## + QuantiDialog::QuantiDialog(QWidget* parent) : QDialog(parent) , Ui::QuantiDialog() { - setupUi(this); - //connect(area_quanti, static_cast(&QSpinBox::valueChanged), this, SLOT(QuantiDialog::updateLabe())); - - //connect(area_quanti, SIGNAL(valueChanged(int)), this, &QuantiDialog::updateLabelValue); - - connect(area_quanti, static_cast(&QDoubleSpinBox::valueChanged), this, &QuantiDialog::updateLabelValues); - + connect(area_quanti, static_cast(&QSpinBox::valueChanged), this, &QuantiDialog::updateLabelValues); } /* Method applied after entering a value in RGB text fields */ void QuantiDialog::updateLabelValues() { - nb_color_label->setText(QString::fromStdString(std::to_string(static_cast(pow(area_quanti->value(), 3))))); + int value = area_quanti->value(); + nb_color_label->setText(QString::number(value*value*value)); } diff --git a/QuantiDialog.h b/QuantiDialog.h index 30a1030..187ac5a 100644 --- a/QuantiDialog.h +++ b/QuantiDialog.h @@ -1,6 +1,21 @@ -#ifndef QUANTIDIALOG_H -#define QUANTIDIALOG_H +#pragma once +//########################################################################## +//# # +//# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # +//# # +//# This program is free software; you can redistribute it and/or modify # +//# it under the terms of the GNU General Public License as published by # +//# the Free Software Foundation; version 2 of the License. # +//# # +//# This program is distributed in the hope that it will be useful, # +//# but WITHOUT ANY WARRANTY; without even the implied warranty of # +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +//# GNU General Public License for more details. # +//# # +//# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # +//# # +//########################################################################## #include @@ -11,10 +26,9 @@ class QuantiDialog : public QDialog, public Ui::QuantiDialog { Q_OBJECT public: - explicit QuantiDialog(QWidget* parent = 0); + explicit QuantiDialog(QWidget* parent = nullptr); public slots: void updateLabelValues(); }; -#endif \ No newline at end of file diff --git a/QuantiDialog.ui b/QuantiDialog.ui index b8551a7..d756d56 100644 --- a/QuantiDialog.ui +++ b/QuantiDialog.ui @@ -40,15 +40,15 @@ - - - 0 - + - 1.000000000000000 + 1 - 255.000000000000000 + 1000 + + + 4 diff --git a/RgbDialog.cpp b/RgbDialog.cpp index 3cc3519..4deca18 100644 --- a/RgbDialog.cpp +++ b/RgbDialog.cpp @@ -14,37 +14,27 @@ //# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # //# # //########################################################################## + #include "RgbDialog.h" -//local -#include "mainwindow.h" - -//Qt -#include -#include -#include -#include -#include -#include -#include - //common #include - #include -#include //qCC_gl #include #include +//Qt +#include + /* Constructor */ RgbDialog::RgbDialog(ccPickingHub* pickingHub, QWidget* parent) : QDialog(parent) , Ui::RgbDialog() - , m_pickingWin(0) + , m_pickingWin(nullptr) , m_pickingHub(pickingHub) { assert(pickingHub); @@ -55,8 +45,7 @@ RgbDialog::RgbDialog(ccPickingHub* pickingHub, QWidget* parent) //Link between Ui and actions connect(pointPickingButton_first, &QCheckBox::toggled, this, &RgbDialog::pickPoint_first); connect(pointPickingButton_second, &QCheckBox::toggled, this, &RgbDialog::pickPoint_second); - - + //auto disable picking mode on quit connect(this, &QDialog::finished, [&]() { @@ -155,4 +144,4 @@ void RgbDialog::onItemPicked(const PickedItem& pi) } } -} \ No newline at end of file +} diff --git a/RgbDialog.h b/RgbDialog.h index 8515e63..9689e9b 100644 --- a/RgbDialog.h +++ b/RgbDialog.h @@ -1,3 +1,5 @@ +#pragma once + //########################################################################## //# # //# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # @@ -15,22 +17,14 @@ //# # //########################################################################## -#ifndef RgbDialog_H -#define RgbDialog_H - #include #include "ccPickingListener.h" //Qt -#include #include -#include -class ccGLWindow; -class ccPlane; -class ccHObject; class ccPickingHub; - +class ccGLWindow; /* Get the values of the RGB interface, and interactions */ @@ -38,7 +32,7 @@ class RgbDialog : public QDialog, public ccPickingListener, public Ui::RgbDialog { Q_OBJECT public: - explicit RgbDialog(ccPickingHub* pickingHub, QWidget* parent = 0); + explicit RgbDialog(ccPickingHub* pickingHub, QWidget* parent = nullptr); //! Inherited from ccPickingListener virtual void onItemPicked(const PickedItem& pi); @@ -54,9 +48,4 @@ protected: //members //! Picking hub ccPickingHub* m_pickingHub; -private: - static const int NULL_VALUE = 0; - }; - -#endif // RgbDialog_H diff --git a/ScalarDialog.cpp b/ScalarDialog.cpp index 1480c79..1b192f4 100644 --- a/ScalarDialog.cpp +++ b/ScalarDialog.cpp @@ -14,30 +14,20 @@ //# COPYRIGHT: Tri-Thien TRUONG, Ronan COLLIER, Mathieu LETRONE # //# # //########################################################################## + #include "ScalarDialog.h" -//local -#include "mainwindow.h" - -//Qt -#include -#include -#include -#include -#include -#include -#include - //common #include - #include -#include //qCC_gl #include #include +//Qt +#include + /* Constructor */ diff --git a/ScalarDialog.h b/ScalarDialog.h index 87af043..142e8fd 100644 --- a/ScalarDialog.h +++ b/ScalarDialog.h @@ -1,3 +1,5 @@ +#pragma once + //########################################################################## //# # //# CLOUDCOMPARE PLUGIN: ColorimetricSegmenter # @@ -15,20 +17,13 @@ //# # //########################################################################## -#ifndef ScalarDialog_H -#define ScalarDialog_H - #include #include "ccPickingListener.h" //Qt -#include #include -#include class ccGLWindow; -class ccPlane; -class ccHObject; class ccPickingHub; /* @@ -38,7 +33,7 @@ class ScalarDialog : public QDialog, public ccPickingListener, public Ui::Scalar { Q_OBJECT public: - explicit ScalarDialog(ccPickingHub* pickingHub, QWidget* parent = 0); + explicit ScalarDialog(ccPickingHub* pickingHub, QWidget* parent = nullptr); //! Inherited from ccPickingListener virtual void onItemPicked(const PickedItem& pi); @@ -54,9 +49,4 @@ protected: //members //! Picking hub ccPickingHub* m_pickingHub; -private: - static const int NULL_VALUE = 0; - }; - -#endif // ScalarDialog_H diff --git a/qColorimetricSegmenter.cpp b/qColorimetricSegmenter.cpp index 7db044d..c2e5983 100644 --- a/qColorimetricSegmenter.cpp +++ b/qColorimetricSegmenter.cpp @@ -15,23 +15,37 @@ //# # //########################################################################## -//Qt -#include - -//System -#include -#include -#include +//Local +#include "qColorimetricSegmenter.h" +#include "HSV.h" +#include "RgbDialog.h" +#include "HSVDialog.h" +#include "ScalarDialog.h" +#include "QuantiDialog.h" +#include "KmeansDlg.h" //CloudCompare #include #include -#include //CCCoreLib #include -#include "qColorimetricSegmenter.h" +//System +#include +#include + +//Qt +#include + +static void ShowDurationNow(const std::chrono::high_resolution_clock::time_point& startTime) +{ + auto stopTime = std::chrono::high_resolution_clock::now(); + auto duration_ms = std::chrono::duration_cast(stopTime - startTime).count(); + + //Print duration of execution + ccLog::Print("Time to execute: " + QString::number(duration_ms) + " milliseconds"); +} ColorimetricSegmenter::ColorimetricSegmenter(QObject* parent) : QObject(parent) @@ -40,20 +54,21 @@ ColorimetricSegmenter::ColorimetricSegmenter(QObject* parent) /*, m_action_filterRgbWithSegmentation(nullptr)*/ , m_action_filterHSV(nullptr) , m_action_filterScalar(nullptr) - , m_action_ToonMapping_Hist(nullptr) - , m_action_ToonMapping_KMeans(nullptr) + , m_action_histogramClustering(nullptr) + , m_action_kMeansClustering(nullptr) + , m_addPointError(false) { } void ColorimetricSegmenter::handleNewEntity(ccHObject* entity) { - assert(entity && m_app); + Q_ASSERT(entity && m_app); m_app->addToDB(entity); } void ColorimetricSegmenter::handleEntityChange(ccHObject* entity) { - assert(entity && m_app); + Q_ASSERT(entity && m_app); entity->prepareDisplayForRefresh_recursive(); m_app->refreshAll(); m_app->updateUI(); @@ -65,40 +80,8 @@ void ColorimetricSegmenter::handleErrorMessage(QString message) m_app->dispToConsole(message, ccMainAppInterface::ERR_CONSOLE_MESSAGE); } -// This method should enable or disable your plugin actions -// depending on the currently selected entities ('selectedEntities'). void ColorimetricSegmenter::onNewSelection(const ccHObject::Container& selectedEntities) { - if (m_action_filterRgb == nullptr) - { - return; - } - - if (m_action_filterHSV == nullptr) - { - return; - } - - /*if (m_action_filterRgbWithSegmentation == nullptr) - { - return; - }*/ - - if (m_action_filterScalar == nullptr) - { - return; - } - if (m_action_ToonMapping_Hist == nullptr) - { - return; - } - if (m_action_ToonMapping_KMeans == nullptr) - { - return; - } - - - // For example - only enable our action if something is selected. // Only enable our action if something is selected. bool activateColorFilters = false; bool activateScalarFilter = false; @@ -106,34 +89,35 @@ void ColorimetricSegmenter::onNewSelection(const ccHObject::Container& selectedE { if (entity->isKindOf(CC_TYPES::POINT_CLOUD)) { - if (entity->hasColors()) { + if (entity->hasColors()) + { activateColorFilters = true; } - else if (entity->hasDisplayedScalarField()) { + else if (entity->hasDisplayedScalarField()) + { activateScalarFilter = true; } } } - m_action_filterRgb->setEnabled(false); - m_action_filterHSV->setEnabled(false); - //m_action_filterRgbWithSegmentation->setEnabled(false); - m_action_filterScalar->setEnabled(false); - m_action_ToonMapping_Hist->setEnabled(false); - m_action_ToonMapping_KMeans->setEnabled(false); - - //Activate only if only one of them is activated - if ((activateColorFilters != activateScalarFilter) && !selectedEntities.empty()) { - m_action_filterRgb->setEnabled(activateColorFilters); - m_action_filterHSV->setEnabled(activateColorFilters); - //m_action_filterRgbWithSegmentation->setEnabled(activateColorFilters); - m_action_filterScalar->setEnabled(activateScalarFilter); - m_action_ToonMapping_Hist->setEnabled(activateColorFilters); - m_action_ToonMapping_KMeans->setEnabled(activateColorFilters); - + if (activateColorFilters && activateScalarFilter) + { + activateColorFilters = activateScalarFilter = false; } + if (m_action_filterRgb) + m_action_filterRgb->setEnabled(activateColorFilters); + if (m_action_filterHSV) + m_action_filterHSV->setEnabled(activateColorFilters); + //if (m_action_filterRgbWithSegmentation) + // m_action_filterRgbWithSegmentation->setEnabled(activateColorFilters); + if (m_action_filterScalar) + m_action_filterScalar->setEnabled(activateScalarFilter); + if (m_action_histogramClustering) + m_action_histogramClustering->setEnabled(activateColorFilters); + if (m_action_kMeansClustering) + m_action_kMeansClustering->setEnabled(activateColorFilters); } QList ColorimetricSegmenter::getActions() @@ -142,7 +126,7 @@ QList ColorimetricSegmenter::getActions() if (!m_action_filterRgb) { m_action_filterRgb = new QAction("Filter RGB", this); - m_action_filterRgb->setToolTip("Filter the points on the selected cloud by RGB color"); + m_action_filterRgb->setToolTip("Filter the points of the selected cloud by RGB color"); m_action_filterRgb->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_rgb.png")); // Connect appropriate signal @@ -154,7 +138,6 @@ QList ColorimetricSegmenter::getActions() } - /*if (!m_action_filterRgbWithSegmentation) { // Here we use the default plugin name, description, and icon, @@ -175,7 +158,7 @@ QList ColorimetricSegmenter::getActions() if (!m_action_filterHSV) { m_action_filterHSV = new QAction("Filter HSV", this); - m_action_filterHSV->setToolTip("Filter the points on the selected cloud by HSV color"); + m_action_filterHSV->setToolTip("Filter the points of the selected cloud by HSV color"); m_action_filterHSV->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_hsv.png")); // Connect appropriate signal @@ -191,7 +174,7 @@ QList ColorimetricSegmenter::getActions() if (!m_action_filterScalar) { m_action_filterScalar = new QAction("Filter scalar", this); - m_action_filterScalar->setToolTip("Filter the points on the selected cloud using scalar field"); + m_action_filterScalar->setToolTip("Filter the points of the selected cloud using scalar field"); m_action_filterScalar->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_scalar.png")); // Connect appropriate signal @@ -202,41 +185,45 @@ QList ColorimetricSegmenter::getActions() connect(m_action_filterScalar, SIGNAL(newErrorMessage(QString)), this, SLOT(handleErrorMessage(QString))); } - if (!m_action_ToonMapping_Hist) + + if (!m_action_histogramClustering) + { + m_action_histogramClustering = new QAction("Histogram Clustering", this); + m_action_histogramClustering->setToolTip("Quantify the number of colors using Histogram Clustering"); + m_action_histogramClustering->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_quantif_h.png")); + + // Connect appropriate signal + connect(m_action_histogramClustering, &QAction::triggered, this, &ColorimetricSegmenter::HistogramClustering); + + connect(m_action_histogramClustering, SIGNAL(newEntity(ccHObject*)), this, SLOT(handleNewEntity(ccHObject*))); + connect(m_action_histogramClustering, SIGNAL(entityHasChanged(ccHObject*)), this, SLOT(handleEntityChange(ccHObject*))); + connect(m_action_histogramClustering, SIGNAL(newErrorMessage(QString)), this, SLOT(handleErrorMessage(QString))); + + } + + if (!m_action_kMeansClustering) { // Here we use the default plugin name, description, and icon, // but each action should have its own. - m_action_ToonMapping_Hist = new QAction("Histogram Clustering", this); - m_action_ToonMapping_Hist->setToolTip("Quantify the number of colors using Histogram Clustering"); - m_action_ToonMapping_Hist->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_quantif_h.png")); + m_action_kMeansClustering = new QAction("Kmeans Clustering", this); + m_action_kMeansClustering->setToolTip("Quantify the number of colors using Kmeans Clustering"); + m_action_kMeansClustering->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_quantif_k.png")); // Connect appropriate signal - connect(m_action_ToonMapping_Hist, &QAction::triggered, this, &ColorimetricSegmenter::HistogramClustering); - - connect(m_action_ToonMapping_Hist, SIGNAL(newEntity(ccHObject*)), this, SLOT(handleNewEntity(ccHObject*))); - connect(m_action_ToonMapping_Hist, SIGNAL(entityHasChanged(ccHObject*)), this, SLOT(handleEntityChange(ccHObject*))); - connect(m_action_ToonMapping_Hist, SIGNAL(newErrorMessage(QString)), this, SLOT(handleErrorMessage(QString))); - - } - if (!m_action_ToonMapping_KMeans) - { - // Here we use the default plugin name, description, and icon, - // but each action should have its own. - m_action_ToonMapping_KMeans = new QAction("Kmeans Clustering", this); - m_action_ToonMapping_KMeans->setToolTip("Quantify the number of colors using Kmeans Clustering"); - m_action_ToonMapping_KMeans->setIcon(QIcon(":/CC/plugin/ColorimetricSegmenter/images/icon_quantif_k.png")); - - // Connect appropriate signal - connect(m_action_ToonMapping_KMeans, &QAction::triggered, this, &ColorimetricSegmenter::KmeansClustering); - - connect(m_action_ToonMapping_KMeans, SIGNAL(newEntity(ccHObject*)), this, SLOT(handleNewEntity(ccHObject*))); - connect(m_action_ToonMapping_KMeans, SIGNAL(entityHasChanged(ccHObject*)), this, SLOT(handleEntityChange(ccHObject*))); - connect(m_action_ToonMapping_KMeans, SIGNAL(newErrorMessage(QString)), this, SLOT(handleErrorMessage(QString))); + connect(m_action_kMeansClustering, &QAction::triggered, this, &ColorimetricSegmenter::KmeansClustering); + connect(m_action_kMeansClustering, SIGNAL(newEntity(ccHObject*)), this, SLOT(handleNewEntity(ccHObject*))); + connect(m_action_kMeansClustering, SIGNAL(entityHasChanged(ccHObject*)), this, SLOT(handleEntityChange(ccHObject*))); + connect(m_action_kMeansClustering, SIGNAL(newErrorMessage(QString)), this, SLOT(handleErrorMessage(QString))); } - - return { m_action_filterRgb, m_action_filterHSV, /*m_action_filterRgbWithSegmentation,*/ m_action_filterScalar,m_action_ToonMapping_Hist, m_action_ToonMapping_KMeans }; + return { m_action_filterRgb, + m_action_filterHSV, + //m_action_filterRgbWithSegmentation, + m_action_filterScalar, + m_action_histogramClustering, + m_action_kMeansClustering + }; } // Get all point clouds that are selected in CC @@ -254,15 +241,15 @@ std::vector ColorimetricSegmenter::getSelectedPointClouds() std::vector clouds; for (size_t i = 0; i < selectedEntities.size(); ++i) { - if (selectedEntities[i]->isKindOf(CC_TYPES::POINT_CLOUD)) { - clouds.push_back(static_cast (selectedEntities[i])); + if (selectedEntities[i]->isKindOf(CC_TYPES::POINT_CLOUD)) + { + clouds.push_back(static_cast(selectedEntities[i])); } } return clouds; } - // Algorithm for the RGB filter // It uses a color range with RGB values, and keeps the points with a color within that range. void ColorimetricSegmenter::filterRgb() @@ -278,35 +265,40 @@ void ColorimetricSegmenter::filterRgb() //check valid window if (!m_app->getActiveGLWindow()) { - m_app->dispToConsole("[ccCompass] Could not find valid 3D window.", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + m_app->dispToConsole("[ColorimetricSegmenter] No active 3D view", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + return; + } + + std::vector clouds = getSelectedPointClouds(); + if (clouds.empty()) + { + Q_ASSERT(false); return; } // Retrieve parameters from dialog - if (m_app->pickingHub()) { - m_pickingHub = m_app->pickingHub(); - } - - rgbDlg = new RgbDialog(m_pickingHub, (QWidget*)m_app->getMainWindow()); - rgbDlg->show(); - - if (!rgbDlg->exec()) + RgbDialog rgbDlg(m_app->pickingHub(), m_app->getMainWindow()); + + rgbDlg.show(); //necessary for setModal to be retained + + if (!rgbDlg.exec()) return; // Start timer - auto start = std::chrono::high_resolution_clock::now(); + auto startTime = std::chrono::high_resolution_clock::now(); // Get all values to make the color range with RGB values - int redInf = std::min(rgbDlg->red_first->value(), rgbDlg->red_second->value()); - int redSup = std::max(rgbDlg->red_first->value(), rgbDlg->red_second->value()); - int greenInf = std::min(rgbDlg->green_first->value(), rgbDlg->green_second->value()); - int greenSup = std::max(rgbDlg->green_first->value(), rgbDlg->green_second->value()); - int blueInf = std::min(rgbDlg->blue_first->value(), rgbDlg->blue_second->value()); - int blueSup = std::max(rgbDlg->blue_first->value(), rgbDlg->blue_second->value()); + int redInf = std::min( rgbDlg.red_first->value(), rgbDlg.red_second->value() ); + int redSup = std::max( rgbDlg.red_first->value(), rgbDlg.red_second->value() ); + int greenInf = std::min( rgbDlg.green_first->value(), rgbDlg.green_second->value() ); + int greenSup = std::max( rgbDlg.green_first->value(), rgbDlg.green_second->value() ); + int blueInf = std::min( rgbDlg.blue_first->value(), rgbDlg.blue_second->value() ); + int blueSup = std::max( rgbDlg.blue_first->value(), rgbDlg.blue_second->value() ); - if (rgbDlg->margin->value() > 0) { + if (rgbDlg.margin->value() > 0) + { // Get margin value (percent) - double marginError = static_cast(rgbDlg->margin->value()) / 100.0; + double marginError = rgbDlg.margin->value() / 100.0; redInf -= marginError * redInf; redSup += marginError * redSup; @@ -317,48 +309,57 @@ void ColorimetricSegmenter::filterRgb() } // Set to min or max value (0-255) - redInf = (redInf < MIN_VALUE ? MIN_VALUE : redInf); - greenInf = (greenInf < MIN_VALUE ? MIN_VALUE : greenInf); - blueInf = (blueInf < MIN_VALUE ? MIN_VALUE : blueInf); + { + const int MIN_VALUE = 0; + redInf = std::max(redInf, MIN_VALUE); + greenInf = std::max(greenInf, MIN_VALUE); + blueInf = std::max(blueInf, MIN_VALUE); - redSup = (redSup > MAX_VALUE ? MAX_VALUE : redSup); - greenSup = (greenSup > MAX_VALUE ? MAX_VALUE : greenSup); - blueSup = (blueSup > MAX_VALUE ? MAX_VALUE : blueSup); + const int MAX_VALUE = 255; + redSup = std::min(redSup, MAX_VALUE); + greenSup = std::min(greenSup, MAX_VALUE); + blueSup = std::min(blueSup, MAX_VALUE); + } - std::vector clouds = getSelectedPointClouds(); - - for (ccPointCloud* cloud : clouds) { - if (cloud->hasColors()) + for (ccPointCloud* cloud : clouds) + { + if (cloud && cloud->hasColors()) { // Use only references for speed reasons - CCCoreLib::ReferenceCloud* filteredCloudInside = new CCCoreLib::ReferenceCloud(cloud); - CCCoreLib::ReferenceCloud* filteredCloudOutside = new CCCoreLib::ReferenceCloud(cloud); + CCCoreLib::ReferenceCloud filteredCloudInside(cloud); + CCCoreLib::ReferenceCloud filteredCloudOutside(cloud); for (unsigned j = 0; j < cloud->size(); ++j) { - const ccColor::Rgb& rgb = cloud->getPointColor(j); - (rgb.r >= redInf&& rgb.r <= redSup && - rgb.g >= greenInf&& rgb.g <= greenSup && - rgb.b >= blueInf&& rgb.b <= blueSup) ? addPoint(filteredCloudInside, j) : addPoint(filteredCloudOutside, j); - } - std::string name = "Rmin:" + std::to_string(redInf) + "/Gmin:" + std::to_string(greenInf) + "/Bmin:" + std::to_string(blueInf) + - "/Rmax:" + std::to_string(redSup) + "/Gmax:" + std::to_string(greenSup) + "/Bmax:" + std::to_string(blueSup); + const ccColor::Rgba& rgb = cloud->getPointColor(j); + if ( rgb.r >= redInf && rgb.r <= redSup + && rgb.g >= greenInf && rgb.g <= greenSup + && rgb.b >= blueInf && rgb.b <= blueSup) + { + addPoint(filteredCloudInside, j); + } + else + { + addPoint(filteredCloudOutside, j); + } - createClouds(rgbDlg, cloud, filteredCloudInside, filteredCloudOutside, name); + if (m_addPointError) + { + return; + } + } + QString name = "Rmin:" + QString::number(redInf) + "/Gmin:" + QString::number(greenInf) + "/Bmin:" + QString::number(blueInf) + + "/Rmax:" + QString::number(redSup) + "/Gmax:" + QString::number(greenSup) + "/Bmax:" + QString::number(blueSup); + + createClouds(rgbDlg, cloud, filteredCloudInside, filteredCloudOutside, name); m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully filtered ! ", ccMainAppInterface::STD_CONSOLE_MESSAGE); } } - // Stop timer - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start).count(); - QString s = QString::number(duration); - //Print time of execution - ccLog::Print("Time to execute : " + s + " milliseconds."); + ShowDurationNow(startTime); } - void ColorimetricSegmenter::filterScalar() { if (m_app == nullptr) @@ -372,55 +373,53 @@ void ColorimetricSegmenter::filterScalar() //check valid window if (!m_app->getActiveGLWindow()) { - m_app->dispToConsole("[ccCompass] Could not find valid 3D window.", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + m_app->dispToConsole("[ColorimetricSegmenter] No active 3D view", ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } // Retrieve parameters from dialog - if (m_app->pickingHub()) { - m_pickingHub = m_app->pickingHub(); - } + ScalarDialog scalarDlg(m_app->pickingHub(), m_app->getMainWindow()); - scalarDlg = new ScalarDialog(m_pickingHub, (QWidget*)m_app->getMainWindow()); - scalarDlg->show(); + scalarDlg.show(); //necessary for setModal to be retained - if (!scalarDlg->exec()) + if (!scalarDlg.exec()) return; - auto start = std::chrono::high_resolution_clock::now(); + // Start timer + auto startTime = std::chrono::high_resolution_clock::now(); - double marginError = static_cast(scalarDlg->margin->value()) / 100.0; - ScalarType min = std::min(scalarDlg->first->value(), scalarDlg->second->value()); - ScalarType max = std::max(scalarDlg->first->value(), scalarDlg->second->value()); + double marginError = static_cast(scalarDlg.margin->value()) / 100.0; + ScalarType min = std::min(scalarDlg.first->value(), scalarDlg.second->value()); + ScalarType max = std::max(scalarDlg.first->value(), scalarDlg.second->value()); min -= (marginError * min); max += (marginError * max); std::vector clouds = getSelectedPointClouds(); - for (ccPointCloud* cloud : clouds) { + for (ccPointCloud* cloud : clouds) + { // Use only references for speed reasons - CCCoreLib::ReferenceCloud* filteredCloudInside = new CCCoreLib::ReferenceCloud(cloud); - CCCoreLib::ReferenceCloud* filteredCloudOutside = new CCCoreLib::ReferenceCloud(cloud); + CCCoreLib::ReferenceCloud filteredCloudInside(cloud); + CCCoreLib::ReferenceCloud filteredCloudOutside(cloud); for (unsigned j = 0; j < cloud->size(); ++j) { const ScalarType val = cloud->getPointScalarValue(j); - (val > min&& val < max) - ? addPoint(filteredCloudInside, j) : addPoint(filteredCloudOutside, j); - } - std::string name = "min:" + std::to_string(min) + "/max:" + std::to_string(max); + addPoint(val > min && val < max ? filteredCloudInside : filteredCloudOutside, j); - createClouds(scalarDlg, cloud, filteredCloudInside, filteredCloudOutside, name); + if (m_addPointError) + { + return; + } + } + QString name = "min:" + QString::number(min) + "/max:" + QString::number(max); + + createClouds(scalarDlg, cloud, filteredCloudInside, filteredCloudOutside, name); m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully filtered ! ", ccMainAppInterface::STD_CONSOLE_MESSAGE); } - // Stop timer - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start).count(); - QString s = QString::number(duration); - //Print time of execution - ccLog::Print("Time to execute : " + s + " milliseconds."); + ShowDurationNow(startTime); } /** @@ -432,7 +431,13 @@ void ColorimetricSegmenter::filterScalar() * @param neighbours The resulting nearest regions. * @param thresholdDistance The maximum distance to search for neighbors. */ -void knnRegions(ccPointCloud* basePointCloud, std::vector* regions, const CCCoreLib::ReferenceCloud* region, unsigned k, std::vector* neighbours, unsigned thresholdDistance) { +void knnRegions(ccPointCloud* basePointCloud, + std::vector* regions, + const CCCoreLib::ReferenceCloud* region, + unsigned k, + std::vector* neighbours, + unsigned thresholdDistance) +{ ccPointCloud* computedRegion = basePointCloud->partialClone(region); // compute distances CCCoreLib::DistanceComputationTools::Cloud2CloudDistanceComputationParams params = CCCoreLib::DistanceComputationTools::Cloud2CloudDistanceComputationParams(); @@ -672,6 +677,12 @@ std::vector* ColorimetricSegmenter::regionMergingAnd return mergedRegionsRef; } +// filterRgbWithSegmentation parameters +static const unsigned TNN = 1; +static const double TPP = 2.0; +static const double TD = 2.0; +static const double TRR = 2.0; +static const unsigned Min = 2; void ColorimetricSegmenter::filterRgbWithSegmentation() { @@ -683,32 +694,31 @@ void ColorimetricSegmenter::filterRgbWithSegmentation() return; } - // Retrieve parameters from dialog - if (m_app->pickingHub()) { - m_pickingHub = m_app->pickingHub(); - } - // Retrieve parameters from dialog - rgbDlg = new RgbDialog(m_pickingHub, (QWidget*)m_app->getMainWindow()); - rgbDlg->show(); + RgbDialog rgbDlg(m_app->pickingHub(), m_app->getMainWindow()); - auto start = std::chrono::high_resolution_clock::now(); + rgbDlg.show(); //necessary for setModal to be retained - if (!rgbDlg->exec()) + if (!rgbDlg.exec()) return; + + // Start timer + auto startTime = std::chrono::high_resolution_clock::now(); + // Get margin value (percent) - double marginError = static_cast(rgbDlg->margin->value()) / 100.0; + double marginError = rgbDlg.margin->value() / 100.0; // Get all values to make the color range with RGB values - int redInf = rgbDlg->red_first->value() - (marginError * rgbDlg->red_first->value()); - int redSup = rgbDlg->red_second->value() + marginError * rgbDlg->red_second->value(); - int greenInf = rgbDlg->green_first->value() - marginError * rgbDlg->green_first->value(); - int greenSup = rgbDlg->green_second->value() + marginError * rgbDlg->green_second->value(); - int blueInf = rgbDlg->blue_first->value() - marginError * rgbDlg->blue_first->value(); - int blueSup = rgbDlg->blue_second->value() + marginError * rgbDlg->blue_second->value(); + int redInf = rgbDlg.red_first->value() - (marginError * rgbDlg.red_first->value()); + int redSup = rgbDlg.red_second->value() + marginError * rgbDlg.red_second->value(); + int greenInf = rgbDlg.green_first->value() - marginError * rgbDlg.green_first->value(); + int greenSup = rgbDlg.green_second->value() + marginError * rgbDlg.green_second->value(); + int blueInf = rgbDlg.blue_first->value() - marginError * rgbDlg.blue_first->value(); + int blueSup = rgbDlg.blue_second->value() + marginError * rgbDlg.blue_second->value(); std::vector clouds = getSelectedPointClouds(); - for (ccPointCloud* cloud : clouds) { + for (ccPointCloud* cloud : clouds) + { if (cloud->hasColors()) { std::vector* regions = regionGrowing(cloud, TNN, TPP, TD); @@ -726,7 +736,8 @@ void ColorimetricSegmenter::filterRgbWithSegmentation() ccPointCloud* newCloud = cloud->partialClone(r); cloud->setEnabled(false); - if (cloud->getParent()) { + if (cloud->getParent()) + { cloud->getParent()->addChild(newCloud); } @@ -734,19 +745,11 @@ void ColorimetricSegmenter::filterRgbWithSegmentation() m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully filtered with segmentation ! ", ccMainAppInterface::STD_CONSOLE_MESSAGE); } - } - - } } - // Stop timer - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start).count(); - QString s = QString::number(duration); - //Print time of execution - ccLog::Print("Time to execute : " + s + " milliseconds."); + ShowDurationNow(startTime); } // Algorithm for the HSV filter @@ -760,137 +763,181 @@ void ColorimetricSegmenter::filterHSV() return; } - //check valid window + // Check valid window if (!m_app->getActiveGLWindow()) { - m_app->dispToConsole("[ccCompass] Could not find valid 3D window.", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + m_app->dispToConsole("[ColorimetricSegmenter] No active 3D view", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + return; + } + + std::vector clouds = getSelectedPointClouds(); + if (clouds.empty()) + { + Q_ASSERT(false); return; } // Retrieve parameters from dialog - if (m_app->pickingHub()) { - m_pickingHub = m_app->pickingHub(); - } - - hsvDlg = new HSVDialog(m_pickingHub, (QWidget*)m_app->getMainWindow()); - hsvDlg->show(); - - if (!hsvDlg->exec()) + HSVDialog hsvDlg(m_app->pickingHub(), m_app->getMainWindow()); + + hsvDlg.show(); //necessary for setModal to be retained + + if (!hsvDlg.exec()) return; // Start timer - auto start = std::chrono::high_resolution_clock::now(); + auto startTime = std::chrono::high_resolution_clock::now(); // Get HSV values - hsv hsv_first; - hsv_first.h = hsvDlg->hue_first->value(); - hsv_first.s = hsvDlg->sat_first->value(); - hsv_first.v = hsvDlg->val_first->value(); + Hsv hsv_first; + hsv_first.h = hsvDlg.hue_first->value(); + hsv_first.s = hsvDlg.sat_first->value(); + hsv_first.v = hsvDlg.val_first->value(); - std::vector clouds = getSelectedPointClouds(); - - for (ccPointCloud* cloud : clouds) { - if (cloud->hasColors()) { + for (ccPointCloud* cloud : clouds) + { + if (cloud->hasColors()) + { // Use only references for speed reasons - CCCoreLib::ReferenceCloud* filteredCloudInside = new CCCoreLib::ReferenceCloud(cloud); - CCCoreLib::ReferenceCloud* filteredCloudOutside = new CCCoreLib::ReferenceCloud(cloud); + CCCoreLib::ReferenceCloud filteredCloudInside(cloud); + CCCoreLib::ReferenceCloud filteredCloudOutside(cloud); // We manually add color ranges with HSV values for (unsigned j = 0; j < cloud->size(); ++j) { const ccColor::Rgb& rgb = cloud->getPointColor(j); - hsv hsv_current = hsvDlg->rgb2hsv(rgb); + Hsv hsv_current(rgb); - // Hue is useless here because the saturation is not high enough + // If Saturation is too small, considering Hue is useless if (0 <= hsv_first.s && hsv_first.s <= 25 && 0 <= hsv_current.s && hsv_current.s <= 25) { - // We only check value - if (hsv_first.v >= 0 && hsv_first.v <= 25 && 0 <= hsv_current.v && hsv_current.v <= 25) addPoint(filteredCloudInside, j); // black - else if (hsv_first.v > 25 && hsv_first.v <= 60 && hsv_current.v > 25 && hsv_current.v <= 60) addPoint(filteredCloudInside, j); // grey - else if (hsv_first.v > 60 && hsv_first.v <= 100 && hsv_current.v > 60 && hsv_current.v <= 100) addPoint(filteredCloudInside, j); // white - else addPoint(filteredCloudOutside, j); + // We only check Value + if ( (hsv_first.v >= 0 && hsv_first.v <= 25 && hsv_current.v >= 0 && hsv_current.v <= 25) //black + || (hsv_first.v > 25 && hsv_first.v <= 60 && hsv_current.v > 25 && hsv_current.v <= 60) //grey + || (hsv_first.v > 60 && hsv_first.v <= 100 && hsv_current.v > 60 && hsv_current.v <= 100) //white + ) + { + addPoint(filteredCloudInside, j); + } + else + { + addPoint(filteredCloudOutside, j); + } } - else if (hsv_first.s > 25 && hsv_first.s <= 100 && hsv_current.s > 25 && hsv_current.s <= 100) { - // We need to check value first - if (0 <= hsv_first.v && hsv_first.v <= 25 && 0 <= hsv_current.v && hsv_current.v <= 25) addPoint(filteredCloudInside, j); // black - // Then, we can check value + else if (hsv_first.s > 25 && hsv_first.s <= 100 && hsv_current.s > 25 && hsv_current.s <= 100) + { + if (0 <= hsv_first.v && hsv_first.v <= 25 && 0 <= hsv_current.v && hsv_current.v <= 25) + { + addPoint(filteredCloudInside, j); // black + } else if (hsv_first.v > 25 && hsv_first.v <= 100 && hsv_current.v > 25 && hsv_current.v <= 100) { - if (((hsv_first.h >= 0 && hsv_first.h <= 30) || (hsv_first.h >= 330 && hsv_first.h <= 360)) && - ((hsv_current.h >= 0 && hsv_current.h <= 30) || (hsv_current.h >= 330 && hsv_current.h <= 360))) addPoint(filteredCloudInside, j); // red - else if (hsv_first.h > 30 && hsv_first.h <= 90 && hsv_current.h > 30 && hsv_current.h <= 90) addPoint(filteredCloudInside, j); // yellow - else if (hsv_first.h > 90 && hsv_first.h <= 150 && hsv_current.h > 90 && hsv_current.h <= 150) addPoint(filteredCloudInside, j); // green - else if (hsv_first.h > 150 && hsv_first.h <= 210 && hsv_current.h > 150 && hsv_current.h <= 210) addPoint(filteredCloudInside, j); // cyan - else if (hsv_first.h > 210 && hsv_first.h <= 270 && hsv_current.h > 210 && hsv_current.h <= 270) addPoint(filteredCloudInside, j); // blue - else if (hsv_first.h > 270 && hsv_first.h <= 330 && hsv_current.h > 270 && hsv_current.h <= 330) addPoint(filteredCloudInside, j); // magenta - else addPoint(filteredCloudOutside, j); + if (((hsv_first.h >= 0 && hsv_first.h <= 30) || (hsv_first.h >= 330 && hsv_first.h <= 360)) && + ((hsv_current.h >= 0 && hsv_current.h <= 30) || (hsv_current.h >= 330 && hsv_current.h <= 360)) + ) + { + addPoint(filteredCloudInside, j); // red + } + else if ( (hsv_first.h > 30 && hsv_first.h <= 90 && hsv_current.h > 30 && hsv_current.h <= 90) // yellow + || (hsv_first.h > 90 && hsv_first.h <= 150 && hsv_current.h > 90 && hsv_current.h <= 150) // green + || (hsv_first.h > 150 && hsv_first.h <= 210 && hsv_current.h > 150 && hsv_current.h <= 210) // cyan + || (hsv_first.h > 210 && hsv_first.h <= 270 && hsv_current.h > 210 && hsv_current.h <= 270) // blue + || (hsv_first.h > 270 && hsv_first.h <= 330 && hsv_current.h > 270 && hsv_current.h <= 330) // magenta + ) + { + addPoint(filteredCloudInside, j); + } + else + { + addPoint(filteredCloudOutside, j); + } + } + else + { + addPoint(filteredCloudOutside, j); } - else addPoint(filteredCloudOutside, j); } - else addPoint(filteredCloudOutside, j); + else + { + addPoint(filteredCloudOutside, j); + } + + if (m_addPointError) + { + return; + } } - std::string name = "h:" + std::to_string((int)hsv_first.h) + "/s:" + std::to_string((int)hsv_first.s) + "/v:" + std::to_string((int)hsv_first.v); - createClouds(hsvDlg, cloud, filteredCloudInside, filteredCloudOutside, name); + QString name = "h:" + QString::number(hsv_first.h, 'f', 0) + "/s:" + QString::number(hsv_first.s, 'f', 0) + "/v:" + QString::number(hsv_first.v, 'f', 0); + createClouds(hsvDlg, cloud, filteredCloudInside, filteredCloudOutside, name); m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully filtered ! ", ccMainAppInterface::STD_CONSOLE_MESSAGE); - - } } - // Stop timer - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start).count(); - QString s = QString::number(duration); - - //Print time of execution - ccLog::Print("Time to execute : " + s + " milliseconds"); + ShowDurationNow(startTime); } // Method to add point to a ReferenceCloud* -void ColorimetricSegmenter::addPoint(CCCoreLib::ReferenceCloud* filteredCloud, unsigned int j) +bool ColorimetricSegmenter::addPoint(CCCoreLib::ReferenceCloud& filteredCloud, unsigned int j) { - if (!filteredCloud->addPointIndex(j)) + m_addPointError = !filteredCloud.addPointIndex(j); + + if (m_addPointError) { //not enough memory - delete filteredCloud; - filteredCloud = nullptr; m_app->dispToConsole("[ColorimetricSegmenter] Error, filter canceled."); } + + return m_addPointError; } // Method to interact with the component "Which points to keep" template -void ColorimetricSegmenter::createClouds(T& dlg, ccPointCloud* cloud, CCCoreLib::ReferenceCloud* filteredCloudInside, CCCoreLib::ReferenceCloud* filteredCloudOutside, std::string name) +void ColorimetricSegmenter::createClouds( T& dlg, + ccPointCloud* cloud, + const CCCoreLib::ReferenceCloud& filteredCloudInside, + const CCCoreLib::ReferenceCloud& filteredCloudOutside, + QString name ) { - - if (dlg->retain->isChecked()) { - createCloud(cloud, filteredCloudInside, name, true); + if (dlg.retain->isChecked()) + { + createCloud(cloud, filteredCloudInside, name + ".inside"); } - else if (dlg->exclude->isChecked()) { - createCloud(cloud, filteredCloudOutside, name, false); + else if (dlg.exclude->isChecked()) + { + createCloud(cloud, filteredCloudOutside, name + ".outside"); } - else if (dlg->both->isChecked()) { - createCloud(cloud, filteredCloudInside, name, true); - createCloud(cloud, filteredCloudOutside, name, false); + else if (dlg.both->isChecked()) + { + createCloud(cloud, filteredCloudInside, name + ".inside"); + createCloud(cloud, filteredCloudOutside, name + ".outside"); } } // Method to create a new cloud -void ColorimetricSegmenter::createCloud(ccPointCloud* cloud, CCCoreLib::ReferenceCloud* referenceCloud, std::string name, bool inside) { - ccPointCloud* newCloud = cloud->partialClone(referenceCloud); - if (inside) { - name += ".inside"; +void ColorimetricSegmenter::createCloud(ccPointCloud* cloud, + const CCCoreLib::ReferenceCloud& referenceCloud, + QString name) +{ + if (!cloud) + { + Q_ASSERT(false); + return; } - else { - name += ".outside"; + + ccPointCloud* newCloud = cloud->partialClone(&referenceCloud); + if (!newCloud) + { + m_app->dispToConsole("Not enough memory"); + return; } - - newCloud->setName(QString::fromStdString(name)); + + newCloud->setName(name); cloud->setEnabled(false); - if (cloud->getParent()) { + if (cloud->getParent()) + { cloud->getParent()->addChild(newCloud); } @@ -903,39 +950,33 @@ Generate nxnxn clusters of points according to their color value (RGB) @param clusterPerDim : coefficient uses to split each RGB component Returns a map of nxnxn keys, for each key a vector of the points index in the partition */ -std::map> getKeyCluster(const ccPointCloud& cloud, int clusterPerDim) { +typedef std::map< size_t, std::vector > ClusterMap; +static bool GetKeyCluster(const ccPointCloud& cloud, size_t clusterPerDim, ClusterMap& clusterMap) +{ + Q_ASSERT(ccColor::MAX == 255); - float clusterSize = 256 / clusterPerDim; + try + { + for (unsigned i = 0; i < cloud.size(); i++) + { + const ccColor::Rgb& rgb = cloud.getPointColor(i); - std::map> keyMap; - std::map>::iterator it; + size_t redCluster = (static_cast(rgb.r) * clusterPerDim) >> 8; // shift 8 bits (= division by 256) + size_t greenCluster = (static_cast(rgb.g) * clusterPerDim) >> 8; // shift 8 bits (= division by 256) + size_t blueCluster = (static_cast(rgb.b) * clusterPerDim) >> 8; // shift 8 bits (= division by 256) + size_t index = redCluster + (greenCluster + blueCluster * clusterPerDim) * clusterPerDim; - for (unsigned i = 0; i < cloud.size(); i++) { - - const ccColor::Rgb& rgb = cloud.getPointColor(i); - - int redCluster = rgb.r / clusterSize; - int greenCluster = rgb.g / clusterSize; - int blueCluster = rgb.b / clusterSize; - - int index = redCluster + greenCluster * 10 + blueCluster * 100; - it = keyMap.find(index); - //check if the entry with this index already exists - if (it == keyMap.end()) { - //if no, we create it - std::vector points = { i }; - keyMap.insert(std::pair>(index, points)); + //we add the point to the right container + clusterMap[index].push_back(i); } - else { - //else we add the point in the container - it->second.push_back(i); - } - - + } + catch (const std::bad_alloc&) + { + return false; } - return keyMap; + return true; } /** Compute the average color (RGB) @@ -943,27 +984,33 @@ Compute the average color (RGB) @param bucket : vector of indexes of points Returns average color (RGB) */ -ccColor::Rgb computeAverageColor(const ccPointCloud& cloud, const std::vector& bucket) +static ccColor::Rgba ComputeAverageColor(const ccPointCloud& cloud, const std::vector& bucket) { size_t count = bucket.size(); if (count == 0) { - return ccColor::whiteRGB; + return ccColor::white; + } + else if (count == 1) + { + return cloud.getPointColor(bucket.front()); } //other formula to compute the average can be used - size_t red = 0, green = 0, blue = 0; - for (unsigned point : bucket) + size_t redSum = 0, greenSum = 0, blueSum = 0, alphaSum = 0; + for (unsigned pointIndex : bucket) { - const ccColor::Rgb rgb = cloud.getPointColor(point); - red += rgb.r; - green += rgb.g; - blue += rgb.b; + const ccColor::Rgba& rgba = cloud.getPointColor(pointIndex); + redSum += rgba.r; + greenSum += rgba.g; + blueSum += rgba.b; + alphaSum += rgba.a; } - ccColor::Rgb res( static_cast(std::min(red / count, static_cast(ccColor::MAX))), - static_cast(std::min(green / count, static_cast(ccColor::MAX))), - static_cast(std::min(blue / count, static_cast(ccColor::MAX)))); + ccColor::Rgba res( static_cast(std::min(redSum / count, static_cast(ccColor::MAX))), + static_cast(std::min(greenSum / count, static_cast(ccColor::MAX))), + static_cast(std::min(blueSum / count, static_cast(ccColor::MAX))), + static_cast(std::min(alphaSum / count, static_cast(ccColor::MAX)))); return res; } @@ -972,15 +1019,17 @@ ccColor::Rgb computeAverageColor(const ccPointCloud& cloud, const std::vector(c1.r) - c2.r) + (static_cast(c1.b) - c2.b) + (static_cast(c1.g) - c2.g); } + /** Generate a pointcloud quantified using an histogram clustering The purpose is to counter luminance variation due to the merge of different scans */ -void ColorimetricSegmenter::HistogramClustering() { - +void ColorimetricSegmenter::HistogramClustering() +{ if (m_app == nullptr) { // m_app should have already been initialized by CC when plugin is loaded @@ -988,94 +1037,194 @@ void ColorimetricSegmenter::HistogramClustering() { return; } + + std::vector clouds = ColorimetricSegmenter::getSelectedPointClouds(); + if (clouds.empty()) + { + Q_ASSERT(false); + return; + } + // creation of the window - quantiDlg = new QuantiDialog((QWidget*)m_app->getMainWindow()); - if (!quantiDlg->exec()) + QuantiDialog quantiDlg(m_app->getMainWindow()); + if (!quantiDlg.exec()) return; // Start timer - auto start = std::chrono::high_resolution_clock::now(); + auto startTime = std::chrono::high_resolution_clock::now(); - int nbClusterByComponent = quantiDlg->area_quanti->value(); + int nbClusterByComponent = quantiDlg.area_quanti->value(); + if (nbClusterByComponent < 0) + { + Q_ASSERT(false); + return; + } - - - std::vector clouds = ColorimetricSegmenter::getSelectedPointClouds(); - - for (ccPointCloud* cloud : clouds) { - - if (cloud->hasColors()) { + for (ccPointCloud* cloud : clouds) + { + if (cloud->hasColors()) + { + ClusterMap clusterMap; + if (!GetKeyCluster(*cloud, static_cast(nbClusterByComponent), clusterMap)) + { + m_app->dispToConsole("Not enough memory", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + break; + } ccPointCloud* histCloud = cloud->cloneThis(); - histCloud->setName(QString::fromStdString("HistogramClustering : Indice Q : " + std::to_string(nbClusterByComponent) + " //Couleurs : " + std::to_string(nbClusterByComponent * nbClusterByComponent * nbClusterByComponent))); - - std::map> clusterMap; - - clusterMap = getKeyCluster(*histCloud, nbClusterByComponent); - - for (std::map>::iterator it = clusterMap.begin(), end = clusterMap.end(); it != end; it++) + if (!histCloud) { + m_app->dispToConsole("Not enough memory", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + break; + } - ccColor::Rgb averageColor = computeAverageColor(*histCloud, it->second); + histCloud->setName(QString("HistogramClustering: Indice Q = %1 // colors = %2").arg(nbClusterByComponent).arg(nbClusterByComponent * nbClusterByComponent * nbClusterByComponent)); - for (auto point : it->second) { - (*histCloud).setPointColor(point, averageColor); + for (auto it = clusterMap.begin(); it != clusterMap.end(); it++) + { + ccColor::Rgba averageColor = ComputeAverageColor(*histCloud, it->second); + + for (unsigned pointIndex : it->second) + { + (*histCloud).setPointColor(pointIndex, averageColor); } - - } cloud->setEnabled(false); - if (cloud->getParent()) { + if (cloud->getParent()) + { cloud->getParent()->addChild(histCloud); } m_app->addToDB(histCloud, false, true, false, false); - m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully clustering ! ", ccMainAppInterface::STD_CONSOLE_MESSAGE); + m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully clustered!", ccMainAppInterface::STD_CONSOLE_MESSAGE); } } - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start).count(); - QString s = QString::number(duration); - - //Print time of execution - ccLog::Print("Time to execute : " + s + " milliseconds."); + ShowDurationNow(startTime); } + /** K-means algorithm @param k : k clusters @param it : limit of iterations before returns a result Returns a cloud quantified */ -ccPointCloud* computeKmeansClustering(ccPointCloud* theCloud, unsigned char K, int it) +static ccPointCloud* ComputeKmeansClustering(ccPointCloud* theCloud, unsigned K, int maxIterationCount) { //valid parameters? if (!theCloud || K == 0) { - assert(false); + Q_ASSERT(false); return nullptr; } - unsigned n = theCloud->size(); - if (n == 0) + unsigned pointCount = theCloud->size(); + if (pointCount == 0) return nullptr; - //on a besoin de memoire ici ! - std::vector theKMeans; //K clusters centers - std::vector belongings; //index of the cluster the point belongs to - std::vector minDistsToMean; //distance to the nearest cluster center - std::vector theKNums; //number of points per clusters - std::vector theOldKNums; //number of points per clusters (prior to iteration) + if (K >= pointCount) + { + ccLog::Warning("Cloud %1 has less point than the expected number of classes."); + return nullptr; + } + + ccPointCloud* KCloud = nullptr; + try { - theKMeans.resize(n); - belongings.resize(n); - minDistsToMean.resize(n); - theKNums.resize(K); - theOldKNums.resize(K); + std::vector clusterCenters; //K clusters centers + std::vector clusterIndex; //index of the cluster the point belongs to + + clusterIndex.resize(pointCount); + clusterCenters.resize(K); + + //init (regularly sampled) classes centers + double step = static_cast(pointCount) / K; + for (unsigned j = 0; j < K; ++j) + { + //TODO: this initialization is pretty biased... To be improved? + clusterCenters[j] = theCloud->getPointColor(static_cast(std::ceil(step * j))); + } + + //let's start + int iteration = 0; + for (; iteration < maxIterationCount; ++iteration) + { + bool meansHaveMoved = false; + + // assign each point (color) to the nearest cluster + for (unsigned i = 0; i < pointCount; ++i) + { + const ccColor::Rgba& color = theCloud->getPointColor(i); + + int minK = 0; + int minDistsToMean = std::abs(ColorDistance(color, clusterCenters[minK])); + + //we look for the nearest cluster center + for (unsigned j = 1; j < K; ++j) + { + int distToMean = std::abs(ColorDistance(color, clusterCenters[j])); + if (distToMean < minDistsToMean) + { + minDistsToMean = distToMean; + minK = j; + } + } + + clusterIndex[i] = minK; + } + + //update the clusters centers + std::vector< std::vector > clusters; + clusters.resize(K); + for (unsigned i = 0; i < pointCount; ++i) + { + unsigned index = clusterIndex[i]; + clusters[index].push_back(i); + } + + ccLog::Print("Iteration " + QString::number(iteration)); + for (unsigned j = 0; j < K; ++j) + { + const std::vector& cluster = clusters[j]; + if (cluster.empty()) + { + continue; + } + + ccColor::Rgba newMean = ComputeAverageColor(*theCloud, cluster); + + if (!meansHaveMoved && ColorDistance(clusterCenters[j], newMean) != 0) + { + meansHaveMoved = true; + } + + clusterCenters[j] = newMean; + } + + if (!meansHaveMoved) + { + break; + } + } + + KCloud = theCloud->cloneThis(); + if (!KCloud) + { + //not enough memory + return nullptr; + } + KCloud->setName("Kmeans clustering: K = " + QString::number(K) + " / it = " + QString::number(iteration)); + + //set color for each cluster + for (unsigned i = 0; i < pointCount; i++) + { + KCloud->setPointColor(i, clusterCenters[clusterIndex[i]]); + } + } catch (const std::bad_alloc&) { @@ -1083,110 +1232,40 @@ ccPointCloud* computeKmeansClustering(ccPointCloud* theCloud, unsigned char K, i return nullptr; } - //init classes centers (regularly sampled - unsigned step = n / K; - for (unsigned char j = 0; j < K; ++j) - theKMeans[j] = theCloud->getPointColor(step * j); - - - //let's start - bool meansHaveMoved = false; - int iteration = 0; - do - { - meansHaveMoved = false; - ++iteration; - // - std::map> KGroups; - { - for (unsigned i = 0; i < n; ++i) - { - unsigned char minK = 0; - - ccColor::Rgb color = theCloud->getPointColor(i); - minDistsToMean[i] = std::abs(ColorDistance(color, theKMeans[minK])); - - //we look for the nearest cluster center - for (unsigned char j = 1; j < K; ++j) - { - double distToMean = std::abs(ColorDistance(color, theKMeans[j])); - if (distToMean < minDistsToMean[i]) - { - minDistsToMean[i] = distToMean; - minK = j; - } - } - - - belongings[i] = minK; - //minDistsToMean[i] = V; - } - } - - //compute the clusters centers - - theOldKNums = theKNums; - std::fill(theKNums.begin(), theKNums.end(), static_cast(0)); - for (unsigned i = 0; i < n; ++i) - { - auto it = KGroups.find(belongings[i]); - if (it == KGroups.end()) { - std::vector points = { i }; - KGroups.insert(std::pair>(belongings[i], points)); - } - else { - it->second.push_back(i); - } - ++theKNums[belongings[i]]; - } - - - - for (unsigned char j = 0; j < K; ++j) - { - ccColor::Rgb newMean = (KGroups[j].size() > 0 ? computeAverageColor(*theCloud, KGroups[j]) : theKMeans[j]); - - if (theOldKNums[j] != theKNums[j]) - { - meansHaveMoved = true; - } - - theKMeans[j] = newMean; - } - - - - } while (iteration < it); - - ccPointCloud* KCloud = theCloud->cloneThis(); - KCloud->setName(QString::fromStdString("Kmeans clustering : K : " + std::to_string(K))); - - //set color for each cluster - for (unsigned i = 0; i < n; i++) { - (*KCloud).setPointColor(i, theKMeans[belongings[i]]); - } - return KCloud; } /** Algorithm based on k-means for clustering points cloud by its colors */ -void ColorimetricSegmenter::KmeansClustering() { +void ColorimetricSegmenter::KmeansClustering() +{ + std::vector clouds = ColorimetricSegmenter::getSelectedPointClouds(); + if (clouds.empty()) + { + Q_ASSERT(false); + return; + } - kmeansDlg = new KmeansDlg((QWidget*)m_app->getMainWindow()); - if (!kmeansDlg->exec()) + KmeansDlg kmeansDlg(m_app->getMainWindow()); + if (!kmeansDlg.exec()) return; - // Start timer - auto start = std::chrono::high_resolution_clock::now(); + assert(kmeansDlg.spinBox_k->value() >= 0); + unsigned K = static_cast(kmeansDlg.spinBox_k->value()); + int iterationCount = kmeansDlg.spinBox_it->value(); - std::vector clouds = ColorimetricSegmenter::getSelectedPointClouds(); + // Start timer + auto startTime = std::chrono::high_resolution_clock::now(); for (ccPointCloud* cloud : clouds) { - - ccPointCloud* kcloud = computeKmeansClustering(cloud, kmeansDlg->spinBox_k->value(), kmeansDlg->spinBox_it->value()); + ccPointCloud* kcloud = ComputeKmeansClustering(cloud, K, iterationCount); + if (!kcloud) + { + m_app->dispToConsole(QString("[ColorimetricSegmenter] Failed to cluster cloud %1").arg(cloud->getName()), ccMainAppInterface::WRN_CONSOLE_MESSAGE); + continue; + } cloud->setEnabled(false); if (cloud->getParent()) @@ -1195,14 +1274,9 @@ void ColorimetricSegmenter::KmeansClustering() { } m_app->addToDB(kcloud, false, true, false, false); - m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully clustering ! ", ccMainAppInterface::STD_CONSOLE_MESSAGE); + m_app->dispToConsole("[ColorimetricSegmenter] Cloud successfully clustered!", ccMainAppInterface::STD_CONSOLE_MESSAGE); } - // Stop timer - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start).count(); - QString s = QString::number(duration); - //Print time of execution - ccLog::Print("Time to execute : " + s + " milliseconds"); + ShowDurationNow(startTime); } diff --git a/qColorimetricSegmenter.h b/qColorimetricSegmenter.h index 85eb2eb..386887a 100644 --- a/qColorimetricSegmenter.h +++ b/qColorimetricSegmenter.h @@ -18,24 +18,15 @@ //########################################################################## #include "ccStdPluginInterface.h" -#include "ccPointCloud.h" + +//CCCoreLib +#include + +//Qt #include #include -#include -#include -#include -#include "ccPointCloud.h" -#include "ccScalarField.h" - -#include "RgbDialog.h" -#include "HSVDialog.h" -#include "ScalarDialog.h" -#include "QuantiDialog.h" -#include "KmeansDlg.h" - -const int MIN_VALUE = 0; -const int MAX_VALUE = 255; +class ccPointCloud; class ColorimetricSegmenter : public QObject, public ccStdPluginInterface { @@ -86,16 +77,18 @@ private: void KmeansClustering(); - void addPoint(CCCoreLib::ReferenceCloud* filteredCloud, unsigned int j); + bool addPoint(CCCoreLib::ReferenceCloud& filteredCloud, unsigned int j); template - void createClouds(T& dlg, ccPointCloud* cloud, CCCoreLib::ReferenceCloud* filteredCloudInside, CCCoreLib::ReferenceCloud* filteredCloudOutside, std::string name); + void createClouds( T& dlg, + ccPointCloud* cloud, + const CCCoreLib::ReferenceCloud& filteredCloudInside, + const CCCoreLib::ReferenceCloud& filteredCloudOutside, + QString name); - void createCloud(ccPointCloud* cloud, CCCoreLib::ReferenceCloud* referenceCloud, std::string name, bool inside); - - //picked point callbacks - //void pointPicked(ccHObject* entity, unsigned itemIdx, int x, int y, const CCVector3& P); - //virtual void onItemPicked(const ccPickingListener::PickedItem& pi); //inherited from ccPickingListener + void createCloud( ccPointCloud* cloud, + const CCCoreLib::ReferenceCloud& referenceCloud, + QString name); //! Segment a cloud with RGB color void filterRgbWithSegmentation(); @@ -123,37 +116,15 @@ private: */ std::vector* regionMergingAndRefinement(ccPointCloud* basePointCloud, std::vector* regions, const unsigned TNN, const double TRR, const double TD, const unsigned Min); +private: //members - //! Default action - /** You can add as many actions as you want in a plugin. - Each action will correspond to an icon in the dedicated - toolbar and an entry in the plugin menu. - **/ QAction* m_action_filterRgb; //QAction* m_action_filterRgbWithSegmentation; QAction* m_action_filterHSV; QAction* m_action_filterScalar; - QAction* m_action_ToonMapping_Hist; - QAction* m_action_ToonMapping_KMeans; + QAction* m_action_histogramClustering; + QAction* m_action_kMeansClustering; - - //! Picking hub - ccPickingHub* m_pickingHub = nullptr; - - RgbDialog* rgbDlg; - HSVDialog* hsvDlg; - ScalarDialog* scalarDlg; - QuantiDialog* quantiDlg; - KmeansDlg* kmeansDlg; - - - //link to application windows - //ccGLWindow* m_window; - //QMainWindow* m_main_window; - - const unsigned TNN = 1; - const double TPP = 2.0; - const double TD = 2.0; - const double TRR = 2.0; - const unsigned Min = 2; + //! Error state after the last call to addPoint + bool m_addPointError; };