ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvDeepSemanticSegmentationTool.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include "ecvEntityAction.h"
11 
12 // CV_CORE_LIB
13 #include <CVLog.h>
14 #include <CVTools.h>
15 
16 // CV_DB_LIB
17 #include <ecvDisplayTools.h>
18 #include <ecvHObjectCaster.h>
19 #include <ecvPointCloud.h>
20 
21 // Qt
22 #include <qtconcurrentrun.h>
23 
24 #include <QApplication>
25 #include <QFuture>
26 #include <QProgressDialog>
27 
28 // System
29 #if defined(CV_WINDOWS)
30 #include "windows.h"
31 #else
32 #include <time.h>
33 #include <unistd.h>
34 #endif
35 
36 #ifdef USE_PYTHON_MODULE
37 ecvDeepSemanticSegmentationTool::ecvDeepSemanticSegmentationTool(
38  QWidget* parent)
39  : ccOverlayDialog(parent),
40  Ui::DeepSemanticSegmentationDlg(),
41  m_show_progress(true) {
42  setupUi(this);
43  connect(detectToolButton, &QAbstractButton::clicked, this,
44  &ecvDeepSemanticSegmentationTool::detect);
45  connect(appyToolButton, &QAbstractButton::clicked, this,
46  &ecvDeepSemanticSegmentationTool::apply);
47  connect(cancelToolButton, &QAbstractButton::clicked, this,
48  &ecvDeepSemanticSegmentationTool::cancel);
49  connect(selectAllRadioButton, &QAbstractButton::clicked, this,
50  &ecvDeepSemanticSegmentationTool::selectAllClasses);
51  connect(unselectAllRadioButton, &QAbstractButton::clicked, this,
52  &ecvDeepSemanticSegmentationTool::selectAllClasses);
53 }
54 
55 ecvDeepSemanticSegmentationTool::~ecvDeepSemanticSegmentationTool() {}
56 
57 bool ecvDeepSemanticSegmentationTool::linkWith(QWidget* win) {
58  if (!ccOverlayDialog::linkWith(win)) {
59  return false;
60  }
61 
62  return true;
63 }
64 
65 bool ecvDeepSemanticSegmentationTool::addEntity(ccHObject* entity) {
66  if (!entity || !entity->isKindOf(CV_TYPES::POINT_CLOUD)) return false;
67 
68  int pointNumber =
69  static_cast<int>(ccHObjectCaster::ToPointCloud(entity)->size());
70  if (pointNumber < 65536) {
71  CVLog::Warning(QString("[ecvDeepSemanticSegmentationTool::addEntity] "
72  "Skip entity [%1] as the point number of it is "
73  "%2 lower than min limit 65536!")
74  .arg(entity->getName(), pointNumber));
75  return false;
76  }
77 
78  m_entity.addChild(entity, ccHObject::DP_NONE);
79 
80  return true;
81 }
82 
83 unsigned ecvDeepSemanticSegmentationTool::getNumberOfValidEntities() const {
84  return m_entity.getChildrenNumber();
85 }
86 
87 bool ecvDeepSemanticSegmentationTool::start() {
88  if (!ecvDisplayTools::GetCurrentScreen()) return false;
89 
90  // clear history in case
91  if (!m_clusters_map.empty()) {
92  m_clusters_map.clear();
93  }
94  if (!m_clusters.empty()) {
95  m_clusters.clear();
96  }
97 
98  if (m_selectedEntity.getChildrenNumber() != 0) {
99  m_selectedEntity.detachAllChildren();
100  }
101 
102  unsigned childNum = getNumberOfValidEntities();
103  if (childNum == 0) return false;
104 
105  selectedTreeWiget->headerItem()->setCheckState(0, Qt::Checked);
106  for (unsigned i = 0; i < getNumberOfValidEntities(); ++i) {
107  QTreeWidgetItem* item = new QTreeWidgetItem();
108  item->setCheckState(0, Qt::Checked);
109  item->setText(1, m_entity.getChild(i)->getName());
110  selectedTreeWiget->insertTopLevelItem(i, item);
111  }
112 
113 #ifdef USE_PYTHON_MODULE
114  return ccOverlayDialog::start();
115 #else
116  return false;
117 #endif
118 }
119 
120 void ecvDeepSemanticSegmentationTool::apply() {
121  if (m_clusters_map.empty()) // no preview and directly apply
122  {
123  performSegmentation();
124  }
125  stop(true);
126  clear();
127  return;
128 }
129 
130 void ecvDeepSemanticSegmentationTool::detect() {
131  if (performSegmentation() > 0) {
132  exportClustersToSF();
133  refreshSelectedClouds();
134  }
135 }
136 
137 void ecvDeepSemanticSegmentationTool::refreshSelectedClouds() {
139  for (unsigned i = 0; i < m_selectedEntity.getChildrenNumber(); ++i) {
140  m_selectedEntity.getChild(i)->setRedrawFlagRecursive(true);
141  }
143 }
144 
145 void ecvDeepSemanticSegmentationTool::cancel() {
146  stop(false);
147  clear();
148 }
149 
150 void ecvDeepSemanticSegmentationTool::stop(bool state) {
151  ccOverlayDialog::stop(state);
152 }
153 
154 void ecvDeepSemanticSegmentationTool::clear() {
155  m_entity.detachAllChildren();
156  for (int i = 0; i < selectedTreeWiget->topLevelItemCount(); ++i) {
157  delete selectedTreeWiget->takeTopLevelItem(i);
158  }
159 
160  m_clusters_map.clear();
161  m_clusters.clear();
162  m_selectedEntity.detachAllChildren();
163 }
164 
165 void ecvDeepSemanticSegmentationTool::getSegmentations(
167  if (exportModeComboBox->currentIndex() == 0) {
168  exportClustersToEntities(result);
169  } else if (exportModeComboBox->currentIndex() == 1) {
170  exportClustersToSF();
171  }
172 }
173 
174 void ecvDeepSemanticSegmentationTool::updateSelectedEntity() {
175  m_selectedEntity.detachAllChildren();
176  for (int i = 0; i < selectedTreeWiget->topLevelItemCount(); ++i) {
177  QTreeWidgetItem* item = selectedTreeWiget->topLevelItem(i);
178  if (item->checkState(0) == Qt::Checked) {
179  for (unsigned j = 0; j < getNumberOfValidEntities(); ++j) {
180  if (item->text(1) == m_entity.getChild(j)->getName()) {
181  m_selectedEntity.addChild(m_entity.getChild(j),
183  break;
184  }
185  }
186  }
187  }
188 }
189 
190 void ecvDeepSemanticSegmentationTool::exportClustersToSF() {
191  ccHObject::Container selectedClouds;
192  m_selectedEntity.filterChildren(selectedClouds, false,
194  if (m_clusters.size() != selectedClouds.size()) {
195  CVLog::Error(
196  "[ecvDeepSemanticSegmentationTool] dimensions do not match!");
197  return;
198  }
199 
200  std::vector<std::vector<ScalarType>> scalarsVector;
201  ccEntityAction::ConvertToScalarType<size_t>(m_clusters, scalarsVector);
202  if (!ccEntityAction::importToSF(selectedClouds, scalarsVector, "Clusters"))
203  CVLog::Error(
204  "[ecvDeepSemanticSegmentationTool::exportClustersToSF] import "
205  "sf failed!");
206 }
207 
208 void ecvDeepSemanticSegmentationTool::exportClustersToEntities(
210  bool needFresh = false;
211  for (unsigned int i = 0; i < m_selectedEntity.getChildrenNumber(); ++i) {
212  ccHObject* ent = m_selectedEntity.getChild(i);
213  if (!ent) {
214  continue;
215  }
216 
218  if (!cloud) {
219  continue;
220  }
221 
222  // remove preview scalar field if exist
223  if (cloud->hasDisplayedScalarField()) {
224  needFresh = true;
225  cloud->deleteScalarField(
228  cloud->showSF(false);
229  }
230 
231  // we create a new group to store all input CCs as 'clusters'
232  ccHObject* ecvGroup = new ccHObject(ent->getName() + "-recognition");
233  ecvGroup->setVisible(true);
234 
235  const ClassMap::ClusterMap& clusterMap = m_clusters_map[i];
236  for (ClassMap::ClusterMap::const_iterator it = clusterMap.begin();
237  it != clusterMap.end(); ++it) {
238  ccPointCloud* res = ccPointCloud::From(cloud, it->second);
239  if (res) {
240  res->setName(it->first.c_str());
241  ecvGroup->addChild(res);
242  }
243  }
244 
245  if (ecvGroup->getChildrenNumber() == 0) {
246  delete ecvGroup;
247  ecvGroup = nullptr;
248  } else {
249  if (ent->getParent()) ent->getParent()->addChild(ecvGroup);
250  result.push_back(ecvGroup);
251  }
252  }
253 
254  if (needFresh) {
255  refreshSelectedClouds();
256  }
257 
258  for (unsigned i = 0; i < m_selectedEntity.getChildrenNumber(); ++i) {
259  m_selectedEntity.getChild(i)->setEnabled(false);
260  }
261 }
262 
263 void ecvDeepSemanticSegmentationTool::getSelectedFilterClasses(
264  std::vector<size_t>& filteredClasses) {
265  if (!filteredClasses.empty()) {
266  filteredClasses.clear();
267  }
268 
269  if (unlabeledCheckBox->isChecked()) filteredClasses.push_back(0);
270  if (manMadeTerrainCheckBox->isChecked()) filteredClasses.push_back(1);
271  if (naturalTerrainCheckBox->isChecked()) filteredClasses.push_back(2);
272  if (highVegetationCheckBox->isChecked()) filteredClasses.push_back(3);
273  if (lowVegetationCheckBox->isChecked()) filteredClasses.push_back(4);
274  if (buildingsCheckBox->isChecked()) filteredClasses.push_back(5);
275  if (hardScapeCheckBox->isChecked()) filteredClasses.push_back(6);
276  if (scanningArtifactsCheckBox->isChecked()) filteredClasses.push_back(7);
277  if (carsCheckBox->isChecked()) filteredClasses.push_back(8);
278  if (utilityPoleCheckBox->isChecked()) filteredClasses.push_back(9);
279  if (insulatorCheckBox->isChecked()) filteredClasses.push_back(10);
280  if (electricalWireCheckBox->isChecked()) filteredClasses.push_back(11);
281  if (crossBarCheckBox->isChecked()) filteredClasses.push_back(12);
282  if (stickCheckBox->isChecked()) filteredClasses.push_back(13);
283  if (fuseCheckBox->isChecked()) filteredClasses.push_back(14);
284  if (wireClipCheckBox->isChecked()) filteredClasses.push_back(15);
285  if (linkerInsulatorCheckBox->isChecked()) filteredClasses.push_back(16);
286  if (personsCheckBox->isChecked()) filteredClasses.push_back(17);
287  if (trafficSignCheckBox->isChecked()) filteredClasses.push_back(18);
288  if (trafficLightCheckBox->isChecked()) filteredClasses.push_back(19);
289 }
290 
291 void ecvDeepSemanticSegmentationTool::selectAllClasses() {
292  bool state = selectAllRadioButton->isChecked();
293  QList<QCheckBox*> list = classGroupBox->findChildren<QCheckBox*>();
294 
295  foreach (QCheckBox* ncheckBox, list) {
296  if (ncheckBox) {
297  ncheckBox->setChecked(state);
298  }
299  }
300 }
301 
302 int ecvDeepSemanticSegmentationTool::performSegmentation() {
303  m_clusters_map.clear();
304  m_clusters.clear();
305  updateSelectedEntity();
306 
307  // check if selected entities are good
308  int check_result = checkSelected();
309  if (check_result != 1) {
310  return check_result;
311  }
312 
313  // if so go ahead with start()
314  int start_status = startDetection();
315  if (start_status != 1) {
316  return start_status;
317  }
318 
319  // verify segmentation result clusters
320  size_t entityNumber = m_selectedEntity.getChildrenNumber();
321  if (m_clusters_map.size() != entityNumber) {
322  CVLog::Error("dimensions do not match!");
323  return -1;
324  }
325 
326  return 1;
327 }
328 
329 int ecvDeepSemanticSegmentationTool::checkSelected() {
330  // In most of the cases we need at least 1 CC_POINT_CLOUD
331  if (m_selectedEntity.getChildrenNumber() == 0) {
332  CVLog::Warning("no selected point cloud, please select one again!");
333  return -1;
334  }
335 
336  std::vector<size_t> selectedFilters;
337  getSelectedFilterClasses(selectedFilters);
338  if (selectedFilters.empty()) {
340  "no selected class, please select at least one to continue!");
341  return -1;
342  }
343 
344  return 1;
345 }
346 
347 static int s_computeStatus = 0;
348 static bool s_computing = false;
349 int ecvDeepSemanticSegmentationTool::startDetection() {
351 
352  if (s_computing) {
353  return -1;
354  }
355 
356  QString tipInfo = "Operation in progress, please wait for a while";
357  if (useVotesCheckBox->isChecked()) {
358  tipInfo += "\n(disable votes to speed up!)";
359  }
360 
361  QProgressDialog progressCb(tipInfo, QString(), 0, 0);
362 
363  if (m_show_progress) {
364  progressCb.setWindowTitle(tr("Deep Semantic Segmentation"));
365  progressCb.show();
366  QApplication::processEvents();
367  }
368 
369  s_computeStatus = -1;
370  s_computing = true;
371  int progress = 0;
372 
373 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
374  QFuture<void> future = QtConcurrent::run([this]() { this->doCompute(); });
375 #else
376  QFuture<void> future = QtConcurrent::run(
378 #endif
379  while (!future.isFinished()) {
380 #if defined(CV_WINDOWS)
381  ::Sleep(500);
382 #else
383  usleep(500 * 1000);
384 #endif
385  if (m_show_progress) progressCb.setValue(++progress);
386  }
387 
388  int is_ok = s_computeStatus;
389  s_computing = false;
390 
391  if (m_show_progress) {
392  progressCb.close();
393  QApplication::processEvents();
394  }
395 
396  CVLog::Print(QString("Deep Semantic Segmentation: finish cost %1 s")
397  .arg(CVTools::TimeOff()));
398 
399  if (is_ok < 0) {
400  return is_ok;
401  }
402 
403  return 1;
404 }
405 
407 #ifdef USE_PYTHON_MODULE
408  try {
409  cloudViewer::utility::DeepSemanticSegmentation dss;
410  dss.setEnableSampling(samplingCheckBox->isChecked());
411  dss.setEnableVotes(useVotesCheckBox->isChecked());
412  dss.setInputCloud(&m_selectedEntity);
413  dss.compute(m_clusters, m_clusters_map);
414 
415  if (m_clusters_map.empty()) {
416  s_computeStatus = 1;
417  return;
418  }
419 
420  // filter to be segmented
421  std::vector<size_t> filteredClasses;
422  getSelectedFilterClasses(filteredClasses);
423  for (auto itMap = m_clusters_map.begin();
424  itMap != m_clusters_map.end();) {
425  auto& clusterMap = *itMap;
426  for (auto it = clusterMap.begin(); it != clusterMap.end();) {
427  int index = ClassMap::FindindexByValue(it->first);
428  if (index < 0) {
430  QString("ignore class index {%1}").arg(index));
431  ++it;
432  continue;
433  }
434 
435  auto ret = std::find(filteredClasses.begin(),
436  filteredClasses.end(), index);
437  if (ret == filteredClasses.end() ||
438  it->second.size() < clusterMinSizeSpinBox->value()) {
439  it = clusterMap.erase(it);
440  continue;
441  }
442 
443  ++it;
444  }
445 
446  if (clusterMap.empty()) {
447  itMap = m_clusters_map.erase(itMap);
448  } else {
449  ++itMap;
450  }
451  }
452 
453  s_computeStatus = m_clusters_map.empty() ? -1 : 1;
454  } catch (const std::exception&) {
455  s_computeStatus = -1;
456  }
457 
458 #else
459  CVLog::Warning("python interface library has not been compiled!");
460  return;
461 #endif // USE_PYTHON_MODULE
462 }
463 
464 #endif
static bool s_computing
static int s_computeStatus
static void doCompute()
core::Tensor result
Definition: VtkUtils.cpp:76
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
static QString TimeOff()
Definition: CVTools.cpp:39
static void TimeStart()
Definition: CVTools.cpp:37
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Definition: ecvHObject.cpp:534
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
Generic overlay dialog interface.
virtual void stop(bool accepted)
Stops process/dialog.
virtual bool start()
Starts process.
virtual bool linkWith(QWidget *win)
Links the overlay dialog with a MDI window.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
static ccPointCloud * From(const cloudViewer::GenericIndexedCloud *cloud, const ccGenericPointCloud *sourceCloud=nullptr)
Creates a new point cloud object from a GenericIndexedCloud.
bool hasDisplayedScalarField() const override
Returns whether an active scalar field is available or not.
int getCurrentDisplayedScalarFieldIndex() const
Returns the currently displayed scalar field index (or -1 if none)
void deleteScalarField(int index) override
Deletes a specific scalar field.
unsigned size() const override
Definition: PointCloudTpl.h:38
static void SetRedrawRecursive(bool redraw=false)
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
static QWidget * GetCurrentScreen()
@ POINT_CLOUD
Definition: CVTypes.h:104
std::map< std::string, std::vector< size_t > > ClusterMap
Definition: ClassMap.h:47
static int FindindexByValue(const std::string &value)
Definition: ClassMap.h:36
Definition: sfEditDlg.h:16
bool importToSF(const ccHObject::Container &selectedEntities, const std::vector< std::vector< ScalarType >> &scalarsVector, const std::string &name)
void Sleep(int milliseconds)
Definition: Helper.cpp:278