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
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
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.
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)
static ccPointCloud * From(const cloudViewer::GenericIndexedCloud *cloud, const ccGenericPointCloud *sourceCloud=nullptr)
Creates a new point cloud object from a GenericIndexedCloud.
void deleteScalarField(int index) override
Deletes a specific scalar field.
unsigned size() const override
Definition: PointCloudTpl.h:38
static QWidget * GetCurrentScreen()
static void SetRedrawRecursive(bool redraw=false)
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
@ 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
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