UserAssistedSegmenterGuiWidgetController.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * \package SceneUnderstanding::gui-plugins::UserAssistedSegmenterGuiWidgetController
17  * \author Rainer Kartmann ( rainer dot kartmann at student dot kit dot edu )
18  * \date 2018
19  * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
24 
25 #include <boost/lexical_cast.hpp>
26 
27 #include <pcl/common/colors.h>
28 
30 
33 
36 
37 
38 using namespace armarx;
39 using namespace Eigen;
40 
41 template <class T>
42 std::string
43 str(const T& t)
44 {
45  return boost::lexical_cast<std::string>(t);
46 }
47 
48 template <class T>
49 QString
50 qstr(const T& t)
51 {
52  return QString::fromStdString(boost::lexical_cast<std::string>(t));
53 }
54 
55 namespace visionx
56 {
57 
58  QString
59  UserAssistedSegmenterGuiWidgetController::GetWidgetName()
60  {
61  return "VisionX.UserAssistedSegmenterGui";
62  }
63 
64  UserAssistedSegmenterGuiWidgetController::UserAssistedSegmenterGuiWidgetController()
65  {
66  widget.setupUi(getWidget());
67 
68 
69  this->segmentsOverviewTable = new PointCloudSegmentsTable();
70  widget.tableSegmentsOverviewLayout->addWidget(segmentsOverviewTable);
71  widget.tableSegmentsOverviewPlaceholder->deleteLater();
72 
73 
74  // Setup tableUserSegmentation.
75  widget.tableUserGrouping->setColumnCount(3);
76  widget.tableUserGrouping->setRowCount(0);
77 
78  QStringList header;
79  header << "C"
80  << "ID"
81  << "Space seperated segment IDs";
82  widget.tableUserGrouping->setHorizontalHeaderLabels(header);
83  widget.tableUserGrouping->setColumnWidth(0, 30);
84  widget.tableUserGrouping->setColumnWidth(1, 40);
85  // last column is stretched
86  }
87 
88  UserAssistedSegmenterGuiWidgetController::~UserAssistedSegmenterGuiWidgetController()
89  {
90  }
91 
92  void
93  UserAssistedSegmenterGuiWidgetController::loadSettings(QSettings* settings)
94  {
95  (void)settings; // unused
96  }
97 
98  void
99  UserAssistedSegmenterGuiWidgetController::saveSettings(QSettings* settings)
100  {
101  (void)settings; // unused
102  }
103 
104  QPointer<QDialog>
105  UserAssistedSegmenterGuiWidgetController::getConfigDialog(QWidget* parent)
106  {
107  if (!configDialog)
108  {
109  configDialog = new UserAssistedSegmenterConfigDialog(parent);
110  }
111  return qobject_cast<UserAssistedSegmenterConfigDialog*>(configDialog);
112  }
113 
114  void
115  UserAssistedSegmenterGuiWidgetController::configured()
116  {
117  userAssistedSegmenterProxyName = configDialog->getUserAssistedSegmenterProxyName();
118  userAssistedSegmenterTopicName = configDialog->getUserAssistedSegmenterTopicName();
119  }
120 
121  void
122  UserAssistedSegmenterGuiWidgetController::onInitComponent()
123  {
124  usingTopic(userAssistedSegmenterTopicName);
125  drawer.offeringTopic(*this);
126 
127  usingProxy(userAssistedSegmenterProxyName);
128  }
129 
130  void
131  UserAssistedSegmenterGuiWidgetController::onConnectComponent()
132  {
133  drawer.getTopic(*this);
134 
135  segmenterProxy =
136  getProxy<visionx::UserAssistedSegmenterInterfacePrx>(userAssistedSegmenterProxyName);
137  if (!segmenterProxy)
138  {
139  ARMARX_ERROR << "Could not get proxy '" << userAssistedSegmenterProxyName << "'.";
140  }
141 
142  connect(this, SIGNAL(newSourcePointCloud()), this, SLOT(onNewSourcePointCloud()));
143 
144  connect(widget.spinBoxFilterSmallSegmentsNum,
145  SIGNAL(editingFinished()),
146  this,
147  SLOT(onFilterSmallSegmentsNumEdited()));
148 
149  connect(widget.checkBoxFilterSegment0, SIGNAL(toggled(bool)), this, SLOT(updateFilters()));
150  connect(
151  widget.checkBoxFilterSmallSegments, SIGNAL(toggled(bool)), this, SLOT(updateFilters()));
152 
153  connect(widget.checkBoxShowIDs, SIGNAL(toggled(bool)), this, SLOT(onShowIDsToggled(bool)));
154  connect(widget.radioButtonShowInput,
155  SIGNAL(toggled(bool)),
156  this,
157  SLOT(onRadioButtonShowInputToggled(bool)));
158  connect(widget.radioButtonShowResult,
159  SIGNAL(toggled(bool)),
160  this,
161  SLOT(onRadioButtonShowResultToggle(bool)));
162 
163  connect(widget.spinBoxNumObjects,
164  SIGNAL(valueChanged(int)),
165  this,
166  SLOT(updateUserGroupingRows(int)));
167  connect(widget.tableUserGrouping,
168  SIGNAL(cellChanged(int, int)),
169  this,
170  SLOT(onUserGroupingChanged(int, int)));
171 
172  connect(widget.buttonClearVisu, SIGNAL(released()), this, SLOT(clearVisualization()));
173  connect(widget.buttonPublish, SIGNAL(released()), this, SLOT(publishSegmentation()));
174 
175  connect(widget.tableUserGrouping,
176  SIGNAL(cellChanged(int, int)),
177  this,
178  SLOT(onTableGroupingCellChanged(int, int)));
179 
180 
181  for (auto& spinBox : {widget.spinTextSize, widget.spinSphereSize})
182  {
183  connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(updateSegmentIDs()));
184  }
185  }
186 
187  void
188  UserAssistedSegmenterGuiWidgetController::reportSegmentation(
189  const visionx::ColoredLabeledPointCloud& receivedPointCloud,
190  const Ice::Current&)
191  {
192  if (!widget.checkBoxListening->isChecked())
193  {
194  return;
195  }
196 
197  ARMARX_IMPORTANT << "Received new point cloud segmentation";
198 
199  ARMARX_INFO << "Setting source point cloud";
200  visionx::tools::convertToPCL(receivedPointCloud, sourcePointCloud);
201 
202  ARMARX_INFO << "Emittinging signal receivedSegmentation().";
203  emit newSourcePointCloud();
204  }
205 
206  void
207  UserAssistedSegmenterGuiWidgetController::onNewSourcePointCloud()
208  {
209  ARMARX_INFO << "onNewSourcePointCloud()";
210 
211  widget.labelPcStatus->setText(QString("Processing..."));
212 
213  this->pointCloud = sourcePointCloud;
214 
215  computeSegmentIndex(pointCloud);
216 
217  applyFilters(pointCloud, segmentIndex);
218 
219  updatePointCloud(pointCloud, segmentIndex);
220 
221  // updateUserGroupingRows();
222 
223  widget.labelPcStatus->setText(QString("Current point cloud:"));
224  }
225 
226  void
227  UserAssistedSegmenterGuiWidgetController::onFilterSmallSegmentsNumEdited()
228  {
229  widget.checkBoxFilterSmallSegments->setChecked(
230  widget.spinBoxFilterSmallSegmentsNum->value() > 0);
231  }
232 
233  void
234  UserAssistedSegmenterGuiWidgetController::updateFilters()
235  {
236  applyFilters(pointCloud, segmentIndex);
237  updatePointCloud(pointCloud, segmentIndex);
238  }
239 
240  void
241  UserAssistedSegmenterGuiWidgetController::onRadioButtonShowInputToggled(bool checked)
242  {
243  if (checked)
244  {
245  drawInputPointCloud(false);
246  }
247  }
248 
249  void
250  UserAssistedSegmenterGuiWidgetController::onRadioButtonShowResultToggle(bool checked)
251  {
252  if (checked)
253  {
254  drawResultPointCloud(false);
255  }
256  }
257 
258  void
259  UserAssistedSegmenterGuiWidgetController::onShowIDsToggled(bool checked)
260  {
261  if (checked)
262  {
263  drawSegmentIDs(false);
264  }
265  else
266  {
267  drawer.clearLayer(layerSegmentIDs);
268  }
269  }
270 
271  void
272  UserAssistedSegmenterGuiWidgetController::updateSegmentIDs()
273  {
274  drawSegmentIDs();
275  }
276 
277  void
278  UserAssistedSegmenterGuiWidgetController::applyFilters(
279  PointCloudT& pointCloud,
280  std::map<uint32_t, pcl::PointIndices>& segmentIndex)
281  {
282  ARMARX_INFO << "Applying filters";
283 
284  if (widget.checkBoxFilterSegment0)
285  {
286  auto ifLabelZero = [](const PointT& p) { return p.label == 0; };
287  pointCloud.erase(std::remove_if(pointCloud.begin(), pointCloud.end(), ifLabelZero),
288  pointCloud.end());
289  // Recompute index.
290  computeSegmentIndex(pointCloud);
291  }
292 
293  if (widget.checkBoxFilterSmallSegments->isChecked())
294  {
295  const int minPoints = std::max(0, widget.spinBoxFilterSmallSegmentsNum->value());
296  ARMARX_CHECK_NONNEGATIVE(minPoints);
297 
298  if (minPoints > 1)
299  {
300  auto ifLessPoints = [&segmentIndex, minPoints](const PointT& p) {
301  return segmentIndex.at(p.label).indices.size() <
302  static_cast<std::size_t>(minPoints);
303  };
304  pointCloud.erase(std::remove_if(pointCloud.begin(), pointCloud.end(), ifLessPoints),
305  pointCloud.end());
306  // Recompute index.
307  computeSegmentIndex(pointCloud);
308  }
309  }
310  }
311 
312  void
313  UserAssistedSegmenterGuiWidgetController::updatePointCloud(
314  const PointCloudT& pointCloud,
315  const std::map<uint32_t, pcl::PointIndices>& segmentIndex)
316  {
317  ARMARX_INFO << "Updating point cloud.";
318  computeCenters(pointCloud, segmentIndex);
319 
320  widget.labelPcPointsNum->setText(qstr(pointCloud.size()));
321  widget.labelPcSegmentsNum->setText(qstr(segmentIndex.size()));
322 
323  updateTableOverview();
324  drawInputPointCloud();
325  drawSegmentIDs(true);
326  }
327 
328  void
329  UserAssistedSegmenterGuiWidgetController::computeSegmentIndex(const PointCloudT& pointCloud)
330  {
331  segmentIndex.clear();
332  const bool excludeZero = false;
333  visionx::tools::fillLabelMap(pointCloud, segmentIndex, excludeZero);
334  }
335 
336  void
337  UserAssistedSegmenterGuiWidgetController::computeCenters(
338  const PointCloudT& pointCloud,
339  const std::map<uint32_t, pcl::PointIndices>& segmentIndex)
340  {
341  ARMARX_INFO << "Computing centers";
342  centers.clear();
343  for (const auto& [segmentID, indices] : segmentIndex)
344  {
345  Vector3f center(0, 0, 0);
346  for (int i : indices.indices)
347  {
348  const PointT& p = pointCloud[static_cast<std::size_t>(i)];
349  center += Vector3f(p.x, p.y, p.z);
350  }
351  center /= segmentIndex.at(segmentID).indices.size();
352  centers[segmentID] = center;
353  }
354  }
355 
356  void
357  UserAssistedSegmenterGuiWidgetController::updateTableOverview()
358  {
359  segmentsOverviewTable->setData(segmentIndex);
360  }
361 
362  void
363  UserAssistedSegmenterGuiWidgetController::drawSegmentIDs(bool onlyIfChecked)
364  {
365  if (onlyIfChecked && !widget.checkBoxShowIDs->isChecked())
366  {
367  return;
368  }
369 
370  drawer.clearLayer(layerSegmentIDs, true);
371 
372  for (const auto& [segmentID, _] : segmentIndex)
373  {
374  const int textSize = widget.spinTextSize->value();
375  const int sphereSize = widget.spinSphereSize->value();
376 
377  const DrawColor segColor = dcolor(segmentID);
378  const DrawColor textColor = (segColor.r + segColor.g + segColor.b) > 1.f
379  ? DrawColor{0, 0, 0, 1}
380  : DrawColor{1, 1, 1, 1};
381 
382  drawer.drawText({layerSegmentIDs, "id" + str(segmentID)},
383  centers[segmentID],
384  str(segmentID),
385  textSize,
386  textColor);
387  drawer.drawSphere({layerSegmentIDs, "sph" + str(segmentID)},
388  centers[segmentID],
389  sphereSize,
390  segColor);
391  }
392  }
393 
394  void
395  UserAssistedSegmenterGuiWidgetController::drawInputPointCloud(bool onlyIfChecked)
396  {
397  if (onlyIfChecked && !widget.radioButtonShowInput->isChecked())
398  {
399  return;
400  }
401 
402  ARMARX_INFO << "Drawing input point cloud";
403 
404  // Draw point cloud.
405  drawer.drawPointCloud({layerPointCloud, "PointCloud"},
406  pointCloud,
407  [this](const PointT& p) { return dcolor(p.label); });
408 
409  // drawSegmentIDs(true);
410  }
411 
412  void
413  UserAssistedSegmenterGuiWidgetController::drawResultPointCloud(bool onlyIfChecked)
414  {
415  if (onlyIfChecked && !widget.radioButtonShowResult->isChecked())
416  {
417  return;
418  }
419 
420  ARMARX_INFO << "Drawing result point cloud";
421 
422  DebugDrawerColoredPointCloud debugPointCloud;
423 
424  for (std::size_t group = 0; group < userGrouping.size(); ++group)
425  {
426  for (uint32_t segment : userGrouping[group])
427  {
428  for (int i : segmentIndex.at(segment).indices)
429  {
430  PointT& p = pointCloud[static_cast<std::size_t>(i)];
431  debugPointCloud.points.push_back(
432  DebugDrawerColoredPointCloudElement{p.x, p.y, p.z, dcolor(group)});
433  }
434  }
435  }
436 
437  drawer.drawPointCloud({layerPointCloud, "PointCloud"}, debugPointCloud);
438 
439  // drawSegmentIDs(true);
440  }
441 
442  DrawColor
443  UserAssistedSegmenterGuiWidgetController::dcolor(std::size_t id) const
444  {
445  return armarx::GlasbeyLUT::at(id);
446  }
447 
448  int
449  toByte(float f)
450  {
451  return static_cast<int>(f * 255);
452  }
453 
454  QColor
455  UserAssistedSegmenterGuiWidgetController::qcolor(std::size_t id) const
456  {
457  const DrawColor c = dcolor(id);
458  return QColor(toByte(c.r), toByte(c.g), toByte(c.b), toByte(c.a));
459  }
460 
461  void
462  UserAssistedSegmenterGuiWidgetController::updateUserGroupingRows(int numRows)
463  {
464  int previousNumRows = widget.tableUserGrouping->rowCount();
465  widget.tableUserGrouping->setRowCount(numRows);
466 
467  std::set<uint32_t> segmentIDs;
468  for (const auto& [segmentID, _] : segmentIndex)
469  {
470  segmentIDs.insert(segmentID);
471  }
472 
473  // Initialize new rows (does nothing if previousNumRows >= numRows).
474  for (int id = previousNumRows; id < numRows; ++id)
475  {
476  QTableWidgetItem* itemColor = new QTableWidgetItem(QString());
477  itemColor->setBackgroundColor(qcolor(static_cast<std::size_t>(id)));
478  widget.tableUserGrouping->setItem(id, 0, itemColor);
479 
480  QTableWidgetItem* itemID = new QTableWidgetItem(qstr(id));
481  itemID->setTextAlignment(Qt::AlignCenter);
482  widget.tableUserGrouping->setItem(id, 1, itemID);
483 
484  UserGroupingLineEdit* itemList =
485  new UserGroupingLineEdit({segmentIDs}, static_cast<uint32_t>(id));
486  connect(itemList,
487  SIGNAL(groupingChanged(uint32_t, std::vector<uint32_t>)),
488  this,
489  SLOT(onUserGroupingChanged(uint32_t, std::vector<uint32_t>)));
490  widget.tableUserGrouping->setCellWidget(id, 2, itemList);
491  }
492 
493  userGrouping.resize(static_cast<std::size_t>(numRows));
494  }
495 
496  void
497  UserAssistedSegmenterGuiWidgetController::onUserGroupingChanged(
498  uint32_t groupID,
499  std::vector<uint32_t> segmentIDs)
500  {
501  if (groupID < userGrouping.size())
502  {
503  userGrouping[groupID] = segmentIDs;
504 
505  ARMARX_INFO << "Updated user grouping " << groupID << ": \n" << userGrouping[groupID];
506  drawResultPointCloud(true);
507  }
508  }
509 
510  void
511  UserAssistedSegmenterGuiWidgetController::onTableGroupingCellChanged(int row, int col)
512  {
513  if (col == 0)
514  {
515  widget.tableUserGrouping->item(row, col)->setText(QString());
516  }
517  else if (col == 1)
518  {
519  widget.tableUserGrouping->item(row, col)->setText(qstr(row));
520  }
521  }
522 
523  void
524  UserAssistedSegmenterGuiWidgetController::publishSegmentation()
525  {
526  if (!segmenterProxy)
527  {
528  ARMARX_ERROR << "Segmenter proxy not set.";
529  return;
530  }
531 
532  visionx::ColoredLabeledPointCloud resultCloud;
533 
534  for (std::size_t groupID = 0; groupID < userGrouping.size(); ++groupID)
535  {
536  for (uint32_t segment : userGrouping[groupID])
537  {
538  for (int i : segmentIndex.at(segment).indices)
539  {
540  PointT& p = pointCloud.at(static_cast<std::size_t>(i));
541  resultCloud.push_back(
542  visionx::ColoredLabeledPoint3D{visionx::Point3D{p.x, p.y, p.z},
543  visionx::RGBA{p.r, p.g, p.b, p.a},
544  static_cast<int>(groupID)});
545  }
546  }
547  }
548  segmenterProxy->publishSegmentation(resultCloud);
549  }
550 
551  void
552  UserAssistedSegmenterGuiWidgetController::clearVisualization()
553  {
554  drawer.clearLayer(layerSegmentIDs);
555  drawer.clearLayer(layerPointCloud);
556  drawer.clearColoredPointCloud({layerPointCloud, "PointCloud"});
557  }
558 
559 } // namespace visionx
Eigen
Definition: Elements.h:32
ARMARX_CHECK_NONNEGATIVE
#define ARMARX_CHECK_NONNEGATIVE(number)
Check whether number is nonnegative (>= 0). If it is not, throw an ExpressionException with the expre...
Definition: ExpressionException.h:152
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:43
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:190
visionx
ArmarX headers.
Definition: OpenPoseStressTest.h:38
armarx::GlasbeyLUT::at
static DrawColor at(std::size_t id, float alpha=1.f)
Definition: GlasbeyLUT.cpp:27
qstr
QString qstr(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:50
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:46
toByte
int toByte(float f)
Definition: PointCloudSegmentsTable.cpp:11
pcl::graph::indices
pcl::PointIndices::Ptr indices(const PCG &g)
Retrieve the indices of the points of the point cloud stored in a point cloud graph that actually bel...
Definition: point_cloud_graph.h:717
PCLUtilities.h
visionx::UserAssistedSegmenterConfigDialog
Definition: UserAssistedSegmenterConfigDialog.h:17
UserAssistedSegmenterGuiWidgetController.h
armarx::PointT
pcl::PointXYZRGBL PointT
Definition: Common.h:30
max
T max(T t1, T t2)
Definition: gdiam.h:51
visionx::PointCloudSegmentsTable
Definition: PointCloudSegmentsTable.h:21
armarx::PointCloudT
pcl::PointCloud< PointT > PointCloudT
Definition: Common.h:32
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:196
GlasbeyLUT.h
visionx::tools::convertToPCL
void convertToPCL(void **source, visionx::MetaPointCloudFormatPtr pointCloudFormat, pcl::PointCloud< pcl::PointXYZRGBA >::Ptr targetPtr)
Definition: PointCloudConversions.cpp:169
PointCloudConversions.h
UserGroupingLineEdit.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
armarx::view_selection::skills::direction::state::center
state::Type center(state::Type previous)
Definition: LookDirection.cpp:233
armarx::UserGroupingLineEdit
Definition: UserGroupingLineEdit.h:10
T
float T
Definition: UnscentedKalmanFilterTest.cpp:38
UserAssistedSegmenterConfigDialog.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
visionx::PointT
pcl::PointXYZRGBA PointT
Definition: MaskRCNNPointCloudObjectLocalizer.h:77
visionx::tools::fillLabelMap
std::enable_if< is_shared_ptr< LabeledPointCloudPtrT >::value, void >::type fillLabelMap(LabeledPointCloudPtrT labeledCloudPtr, std::map< uint32_t, pcl::PointIndices > &labelIndicesMap, bool excludeZero=true)
Definition: PCLUtilities.h:140