diff --git a/ColorimetricSegmenter.cpp b/ColorimetricSegmenter.cpp index 63b940a..aa19590 100644 --- a/ColorimetricSegmenter.cpp +++ b/ColorimetricSegmenter.cpp @@ -38,9 +38,6 @@ #include "ColorimetricSegmenter.h" -#include "ccPointCloud.h" -#include "ccScalarField.h" - // Default constructor: // - pass the Qt resource path to the info.json file (from .qrc file) @@ -50,6 +47,7 @@ ColorimetricSegmenter::ColorimetricSegmenter( QObject *parent ) , ccStdPluginInterface( ":/CC/plugin/ColorimetricSegmenter/info.json" ) , m_action_filterScalar(nullptr ) , m_action_filterRgb( nullptr ) + , m_action_filterHSV( nullptr ) { } @@ -86,6 +84,10 @@ void ColorimetricSegmenter::onNewSelection( const ccHObject::Container &selected { return; } + if (m_action_filterHSV == nullptr) + { + return; + } // If you need to check for a specific type of object, you can use the methods // in ccHObjectCaster.h or loop and check the objects' classIDs like this: @@ -101,6 +103,7 @@ void ColorimetricSegmenter::onNewSelection( const ccHObject::Container &selected // For example - only enable our action if something is selected. m_action_filterScalar->setEnabled( !selectedEntities.empty() ); m_action_filterRgb->setEnabled(!selectedEntities.empty()); + m_action_filterHSV->setEnabled(!selectedEntities.empty()); } // This method returns all the 'actions' your plugin can perform. @@ -127,8 +130,6 @@ QList ColorimetricSegmenter::getActions() if (!m_action_filterRgb) { - // Here we use the default plugin name, description, and icon, - // but each action should have its own. 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->setIcon(getIcon()); @@ -142,7 +143,22 @@ QList ColorimetricSegmenter::getActions() } - return { m_action_filterScalar, m_action_filterRgb }; + 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->setIcon(getIcon()); + + // Connect appropriate signal + connect(m_action_filterHSV, &QAction::triggered, this, &ColorimetricSegmenter::filterHSV); + + connect(m_action_filterHSV, SIGNAL(newEntity(ccHObject*)), this, SLOT(handleNewEntity(ccHObject*))); + connect(m_action_filterHSV, SIGNAL(entityHasChanged(ccHObject*)), this, SLOT(handleEntityChange(ccHObject*))); + connect(m_action_filterHSV, SIGNAL(newErrorMessage(QString)), this, SLOT(handleErrorMessage(QString))); + + } + + return { m_action_filterScalar, m_action_filterRgb, m_action_filterHSV }; } @@ -224,12 +240,12 @@ void ColorimetricSegmenter::filterRgb() double marginError = static_cast(rgbDlg->margin->value()) / 100.0; - int redInf = rgbDlg->area_red->value() - (marginError * rgbDlg->area_red->value()); - int redSup = rgbDlg->area_red->value() + marginError * rgbDlg->area_red->value(); - int greenInf = rgbDlg->area_green->value() - marginError * rgbDlg->area_green->value(); - int greenSup = rgbDlg->area_green->value() + marginError * rgbDlg->area_green->value(); - int blueInf = rgbDlg->area_blue->value() - marginError * rgbDlg->area_blue->value(); - int blueSup = rgbDlg->area_blue->value() + marginError * rgbDlg->area_blue->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(); @@ -258,6 +274,8 @@ void ColorimetricSegmenter::filterRgb() } ccPointCloud* newCloud = cloud->partialClone(filteredCloud); + newCloud->setName(QString::fromStdString("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))); cloud->setEnabled(false); if (cloud->getParent()) { @@ -281,3 +299,132 @@ void ColorimetricSegmenter::filterRgb() ccLog::Print("Time to execute : " + s); } +void ColorimetricSegmenter::filterHSV() +{ + if (m_app == nullptr) + { + // m_app should have already been initialized by CC when plugin is loaded + Q_ASSERT(false); + + return; + } + + //check valid window + if (!m_app->getActiveGLWindow()) + { + m_app->dispToConsole("[ccCompass] Could not find valid 3D window.", ccMainAppInterface::ERR_CONSOLE_MESSAGE); + 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()) + return; + + // Start timer + auto start = std::chrono::high_resolution_clock::now(); + + 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()) + { + // Use only references for speed reasons + CCLib::ReferenceCloud* filteredCloud = new CCLib::ReferenceCloud(cloud); + + for (unsigned j = 0; j < cloud->size(); ++j) + { + const ccColor::Rgb& rgb = cloud->getPointColor(j); + hsv hsv_current = hsvDlg->rgb2hsv(rgb); + + if ( 0 < hsv_first.s && hsv_first.s <= 25 && 0 < hsv_current.s && hsv_current.s <= 25) // hue is useless here, so we will check value + { + if (0 < hsv_first.v && hsv_first.v <= 25 && 0 < hsv_current.v && hsv_current.v <= 25) { // black + addPoint(filteredCloud, j); + } + else if (hsv_first.v > 25 && hsv_first.v <= 75 && hsv_current.v > 25 && hsv_current.v <= 75) { // grey + addPoint(filteredCloud, j); + } + else if (hsv_first.v > 75 && hsv_first.v <= 100 && hsv_current.v > 75 && hsv_current.v <= 100) { // white + addPoint(filteredCloud, j); + } + } + else if (hsv_first.s > 25 && hsv_first.s <= 100 && hsv_current.s > 25 && hsv_current.s <= 100) { // we need to check value, then hue + if (0 < hsv_first.v && hsv_first.v <= 25 && 0 < hsv_current.v && hsv_current.v <= 25) { // black + addPoint(filteredCloud, j); + } + else if (hsv_first.v > 50 && hsv_first.v <= 100 && hsv_current.v > 50 && hsv_current.v <= 100) { // particular color + 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)) // red + { + addPoint(filteredCloud, j); + } + else if (hsv_first.h > 30 && hsv_first.h <= 90 && hsv_current.h > 30 && hsv_current.h <= 90) // yellow + { + addPoint(filteredCloud, j); + } + else if (hsv_first.h > 90 && hsv_first.h <= 150 && hsv_current.h > 90 && hsv_current.h <= 150) // green + { + addPoint(filteredCloud, j); + } + else if (hsv_first.h > 150 && hsv_first.h <= 210 && hsv_current.h > 150 && hsv_current.h <= 210) // cyan + { + addPoint(filteredCloud, j); + } + else if (hsv_first.h > 210 && hsv_first.h <= 270 && hsv_current.h > 210 && hsv_current.h <= 270) // blue + { + addPoint(filteredCloud, j); + } + else if (hsv_first.h > 270 && hsv_first.h <= 330 && hsv_current.h > 270 && hsv_current.h <= 330) // magenta + { + addPoint(filteredCloud, j); + } + } + } + + } + ccPointCloud* newCloud = cloud->partialClone(filteredCloud); + newCloud->setName(QString::fromStdString("h:" + std::to_string((int)hsv_first.h) + "/s:" + std::to_string((int)hsv_first.s) + "/v:" + std::to_string((int)hsv_first.v))); + + cloud->setEnabled(false); + if (cloud->getParent()) { + cloud->getParent()->addChild(newCloud); + } + + m_app->addToDB(newCloud, false, true, false, false); + + 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 + //std::cout << duration.count() << std::endl; + ccLog::Print("Time to execute : " + s); +} + +void ColorimetricSegmenter::addPoint(CCLib::ReferenceCloud* filteredCloud, unsigned int j) +{ + if (!filteredCloud->addPointIndex(j)) + { + //not enough memory + delete filteredCloud; + filteredCloud = nullptr; + m_app->dispToConsole("[ColorimetricSegmenter] Error, filter canceled."); + } +} \ No newline at end of file diff --git a/ColorimetricSegmenter.h b/ColorimetricSegmenter.h index 9216fa0..a116fa3 100644 --- a/ColorimetricSegmenter.h +++ b/ColorimetricSegmenter.h @@ -25,9 +25,11 @@ #include #include #include +#include "ccPointCloud.h" +#include "ccScalarField.h" #include "RgbDialog.h" - +#include "HSVDialog.h" //! Example qCC plugin /** Replace 'ExamplePlugin' by your own plugin class name throughout and then @@ -95,6 +97,10 @@ private: //! Filter a cloud with RGB color void filterRgb(); + void filterHSV(); + + void addPoint(CCLib::ReferenceCloud* filteredCloud, unsigned int j); + //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 @@ -106,11 +112,13 @@ private: **/ QAction* m_action_filterScalar; QAction* m_action_filterRgb; + QAction* m_action_filterHSV; //! Picking hub ccPickingHub* m_pickingHub = nullptr; RgbDialog* rgbDlg; + HSVDialog* hsvDlg; //link to application windows //ccGLWindow* m_window; diff --git a/HSVDialog.cpp b/HSVDialog.cpp new file mode 100644 index 0000000..6e5285c --- /dev/null +++ b/HSVDialog.cpp @@ -0,0 +1,154 @@ +//########################################################################## +//# # +//# 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 "HSVDialog.h" + +//local +#include "mainwindow.h" + +//Qt +#include +#include +#include +#include +#include +#include +#include + +//common +#include + +#include +#include + +//qCC_gl +#include +#include + +HSVDialog::HSVDialog(ccPickingHub* pickingHub, QWidget* parent) + : QDialog(parent) + , Ui::HSVDialog() + , m_pickingWin(0) + , m_pickingHub(pickingHub) +{ + assert(pickingHub); + + setModal(false); + setupUi(this); + + + //restore semi-persistent parameters + red_first->setValue(0); + green_first->setValue(0); + blue_first->setValue(0); + + //Link between Ui and actions + connect(pointPickingButton_first, &QCheckBox::toggled, this, &HSVDialog::pickPoint_first); + + //auto disable picking mode on quit + connect(this, &QDialog::finished, [&]() + { + if (pointPickingButton_first->isChecked()) pointPickingButton_first->setChecked(false); + } + ); +} + +void HSVDialog::pickPoint_first(bool state) +{ + if (!m_pickingHub) + { + return; + } + if (state) + { + if (!m_pickingHub->addListener(this, true)) + { + ccLog::Error("Can't start the picking process (another tool is using it)"); + state = false; + } + } + else + { + m_pickingHub->removeListener(this); + } + pointPickingButton_first->blockSignals(true); + pointPickingButton_first->setChecked(state); + pointPickingButton_first->blockSignals(false); +} + +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()) { + ccLog::Print("Point picked from first point picker"); + red_first->setValue(rgb.r); + green_first->setValue(rgb.g); + blue_first->setValue(rgb.b); + + hsv hsv_first = rgb2hsv(rgb); + hue_first->setValue(hsv_first.h); + sat_first->setValue(hsv_first.s); + val_first->setValue(hsv_first.v); + + pointPickingButton_first->setChecked(false); + } + } + +} + +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 new file mode 100644 index 0000000..2642e4d --- /dev/null +++ b/HSVDialog.h @@ -0,0 +1,65 @@ +//########################################################################## +//# # +//# 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 # +//# # +//########################################################################## + +#ifndef HSVDialog_H +#define HSVDialog_H + +#include +#include "ccPickingListener.h" + +//Qt +#include +#include +#include + +class ccGLWindow; +class ccPlane; +class ccHObject; +class ccPickingHub; + +typedef struct { + double h; + double s; + double v; +} hsv; + +class HSVDialog : public QDialog, public ccPickingListener, public Ui::HSVDialog +{ + Q_OBJECT +public: + explicit HSVDialog(ccPickingHub* pickingHub, QWidget* parent = 0); + + + //! Inherited from ccPickingListener + virtual void onItemPicked(const PickedItem& pi); + + hsv rgb2hsv(ccColor::Rgb rgb); + +public slots: + void pickPoint_first(bool); + +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 new file mode 100644 index 0000000..d3f59eb --- /dev/null +++ b/HSVDialog.ui @@ -0,0 +1,237 @@ + + + HSVDialog + + + + 0 + 0 + 401 + 215 + + + + + 0 + 0 + + + + HSV color setting + + + + + + + + 10 + 150 + 161 + 31 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + 10 + 10 + 361 + 131 + + + + + + + Select the reference point + + + + + 20 + 20 + 331 + 102 + + + + + + + Pick the plane center (click again to cancel) + + + + :/CC/images/ccPointPicking.png:/CC/images/ccPointPicking.png + + + true + + + + + + + + + RED : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + GREEN : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + BLUE : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + + + + + HUE : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + SAT : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + VAL : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + + + + + + + + + + + + + + + buttonBox + accepted() + HSVDialog + accept() + + + 218 + 114 + + + 157 + 118 + + + + + buttonBox + rejected() + HSVDialog + reject() + + + 260 + 120 + + + 280 + 118 + + + + + diff --git a/RgbDialog.cpp b/RgbDialog.cpp index 1f26d9d..41f69a2 100644 --- a/RgbDialog.cpp +++ b/RgbDialog.cpp @@ -38,9 +38,6 @@ #include #include -//semi-persistent parameters -static ccColor::Rgb rgb(0,0,0); - RgbDialog::RgbDialog(ccPickingHub* pickingHub, QWidget* parent) : QDialog(parent) , Ui::RgbDialog() @@ -52,23 +49,21 @@ RgbDialog::RgbDialog(ccPickingHub* pickingHub, QWidget* parent) setModal(false); setupUi(this); - - //restore semi-persistent parameters - area_red->setValue(rgb.r); - area_green->setValue(rgb.g); - area_blue->setValue(rgb.b); - //Link between Ui and actions - connect(pointPickingButton, &QCheckBox::toggled, this, &RgbDialog::pickPoint); + 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, [&]() { - if (pointPickingButton->isChecked()) pointPickingButton->setChecked(false); } + if (pointPickingButton_first->isChecked()) pointPickingButton_first->setChecked(false); + if (pointPickingButton_second->isChecked()) pointPickingButton_second->setChecked(false); + } ); } -void RgbDialog::pickPoint(bool state) +void RgbDialog::pickPoint_first(bool state) { if (!m_pickingHub) { @@ -86,27 +81,63 @@ void RgbDialog::pickPoint(bool state) { m_pickingHub->removeListener(this); } - pointPickingButton->blockSignals(true); - pointPickingButton->setChecked(state); - pointPickingButton->blockSignals(false); + pointPickingButton_first->blockSignals(true); + pointPickingButton_first->setChecked(state); + pointPickingButton_first->blockSignals(false); +} + +void RgbDialog::pickPoint_second(bool state) +{ + if (!m_pickingHub) + { + return; + } + if (state) + { + if (!m_pickingHub->addListener(this, true)) + { + ccLog::Error("Can't start the picking process (another tool is using it)"); + state = false; + } + } + else + { + m_pickingHub->removeListener(this); + } + pointPickingButton_second->blockSignals(true); + pointPickingButton_second->setChecked(state); + pointPickingButton_second->blockSignals(false); } void RgbDialog::onItemPicked(const PickedItem& pi) { - ccLog::Print("Point Picked"); 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()) { + ccLog::Print("Point picked from first point picker"); + red_first->setValue(rgb.r); + green_first->setValue(rgb.g); + blue_first->setValue(rgb.b); + + pointPickingButton_first->setChecked(false); + } + else { + ccLog::Print("Point picked from second point picker"); + red_second->setValue(rgb.r); + green_second->setValue(rgb.g); + blue_second->setValue(rgb.b); + + pointPickingButton_second->setChecked(false); + } - area_red->setValue(rgb.r); - area_green->setValue(rgb.g); - area_blue->setValue(rgb.b); } - pointPickingButton->setChecked(false); + } \ No newline at end of file diff --git a/RgbDialog.h b/RgbDialog.h index 0505742..7d2942e 100644 --- a/RgbDialog.h +++ b/RgbDialog.h @@ -41,7 +41,8 @@ public: virtual void onItemPicked(const PickedItem& pi); public slots: - void pickPoint(bool); + void pickPoint_first(bool); + void pickPoint_second(bool); protected: //members diff --git a/RgbDialog.ui b/RgbDialog.ui index 4611ca9..75a7e44 100644 --- a/RgbDialog.ui +++ b/RgbDialog.ui @@ -6,7 +6,7 @@ 0 0 - 361 + 484 171 @@ -27,19 +27,19 @@ 0 0 - 331 + 211 107 - Point Selected + Choose the darkest point true - + RED : @@ -55,7 +55,7 @@ - + Pick the plane center (click again to cancel) @@ -69,7 +69,7 @@ - + GREEN : @@ -85,7 +85,7 @@ - + BLUE : @@ -107,7 +107,7 @@ 0 110 - 332 + 441 31 @@ -115,7 +115,7 @@ - Margin of error (%) : + Deviation from RGB limits (%) : @@ -138,6 +138,86 @@ + + + + 240 + 0 + 201 + 107 + + + + Choose the clearest point + + + true + + + + + + RED : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + Pick the plane center (click again to cancel) + + + + :/CC/images/ccPointPicking.png:/CC/images/ccPointPicking.png + + + true + + + + + + + GREEN : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + + + + BLUE : + + + 0 + + + 0.000000000000000 + + + 255.000000000000000 + + + + +