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