histogram_panel.cpp
Go to the documentation of this file.
2 
3 namespace graph_rviz_plugin
4 {
5 
6 HistogramPanel::HistogramPanel(QWidget *parent) : rviz::Panel(parent),
7  nh_(std::make_shared<ros::NodeHandle>()),
8  updating_(false),
9  bins_value_(256)
10 {
11  setName("Histogram");
12  setObjectName(getName());
13  qRegisterMetaType<QMessageBox::Icon>();
14  connect(this, &HistogramPanel::enable, this, &HistogramPanel::setEnabled);
17 
18  QVBoxLayout *layout = new QVBoxLayout();
19  setLayout(layout);
20 
21  graph_refresh_timer_ = new QTimer(this);
22  graph_refresh_timer_->setInterval(100); // milliseconds // 10 Hz
23  connect(graph_refresh_timer_, &QTimer::timeout, this, &HistogramPanel::updateChartSlot);
24 
25  QHBoxLayout *buttons(new QHBoxLayout);
26  start_stop_ = new QPushButton("Start");
27  start_stop_->setToolTip("Start the histogram");
28  start_stop_->setEnabled(false);
29  connect(start_stop_, &QPushButton::clicked, this, [=]()
30  {
31  if (updating_)
32  {
33  graph_refresh_timer_->start();
34  start_stop_->setText("Stop");
35  start_stop_->setToolTip("Stop the histogram");
38  updating_ = false;
39  }
40  else
41  {
42  graph_refresh_timer_->stop();
43  start_stop_->setText("Start");
44  start_stop_->setToolTip("Start the histogram");
46  updating_ = true;
47  }
48 
49  });
50 
51  graph_refresh_frequency_ = new QComboBox;
52  QLabel *refresh_label(new QLabel("Refresh frequency:"));
53  graph_refresh_frequency_->setToolTip("Change the histogram display frequency");
54  refresh_label->setToolTip(graph_refresh_frequency_->toolTip());
55 
56  graph_refresh_frequency_->addItem("1 Hz");
57  graph_refresh_frequency_->addItem("2 Hz");
58  graph_refresh_frequency_->addItem("5 Hz");
59  graph_refresh_frequency_->addItem("10 Hz");
60  graph_refresh_frequency_->addItem("20 Hz");
61  graph_refresh_frequency_->addItem("50 Hz");
62  graph_refresh_frequency_->setCurrentIndex(3); // Display by default "5 Hz"
63 
64  connect(graph_refresh_frequency_, qOverload<int>(&QComboBox::currentIndexChanged), this, &Panel::configChanged);
65  connect(graph_refresh_frequency_, qOverload<int>(&QComboBox::currentIndexChanged), this, [=](int index)
66  {
67  switch (index)
68  {
69  case 0:
70  {
71  graph_refresh_timer_->setInterval(1000 / 1);
72  break;
73  }
74  case 1:
75  {
76  graph_refresh_timer_->setInterval(1000 / 2);
77  break;
78  }
79  case 2:
80  {
81  graph_refresh_timer_->setInterval(1000 / 5);
82  break;
83  }
84  case 3:
85  {
86  graph_refresh_timer_->setInterval(1000 / 10);
87  break;
88  }
89  case 4:
90  {
91  graph_refresh_timer_->setInterval(1000 / 20);
92  break;
93  }
94  case 5:
95  {
96  graph_refresh_timer_->setInterval(1000 / 50);
97  break;
98  }
99  default:
100  {
101  graph_refresh_timer_->setInterval(1000 / 5); // Default to 5 Hz
102  break;
103  }
104  }
105  });
106 
108 
109  bars_ = new QCPBars(custom_plot_->xAxis, custom_plot_->yAxis);
110  bars_->setAntialiased(false);
111  bars_->setPen(QPen(QColor(0, 0, 0)));
112  bars_->setBrush(QColor(128, 128, 128));
113  bars_->setWidthType(QCPBars::WidthType::wtPlotCoords);
114  bars_->setWidth(1);
115  bars_->setVisible(true);
116 
117  bars_red_ = new QCPBars(custom_plot_->xAxis, custom_plot_->yAxis);
118  bars_red_->setAntialiased(false);
119  bars_red_->setPen(QPen(QColor(0, 0, 0)));
120  bars_red_->setBrush(QColor(255, 0, 0));
121  bars_red_->setWidthType(QCPBars::WidthType::wtPlotCoords);
122  bars_red_->setWidth(1);
123  bars_red_->setVisible(false);
124 
125  bars_green_ = new QCPBars(custom_plot_->xAxis, custom_plot_->yAxis);
126  bars_green_->setAntialiased(false);
127  bars_green_->setPen(QPen(QColor(0, 0, 0)));
128  bars_green_->setBrush(QColor(0, 255, 0));
129  bars_green_->setWidthType(QCPBars::WidthType::wtPlotCoords);
130  bars_green_->setWidth(1);
131  bars_green_->setVisible(false);
132 
133  bars_blue_ = new QCPBars(custom_plot_->xAxis, custom_plot_->yAxis);
134  bars_blue_->setAntialiased(false);
135  bars_blue_->setPen(QPen(QColor(0, 0, 0)));
136  bars_blue_->setBrush(QColor(0, 0, 255));
137  bars_blue_->setWidthType(QCPBars::WidthType::wtPlotCoords);
138  bars_blue_->setWidth(1);
139  bars_blue_->setVisible(false);
140 
141  for (int i(0); i < 255; ++i)
142  ticks_.push_back(i);
143 
144  custom_plot_->yAxis->rescale();
145  custom_plot_->xAxis->setRange(0, ticks_.back());
146  custom_plot_->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
147 
148  bins_selection_ = new QComboBox;
149  QLabel *bins_selection_label = new QLabel("Bins selection:");
150  bins_selection_->setToolTip("Change the bins number used to compute the histogram");
151  bins_selection_label->setToolTip(bins_selection_->toolTip());
152 
153  bins_selection_->addItem("16");
154  bins_selection_->addItem("32");
155  bins_selection_->addItem("64");
156  bins_selection_->addItem("128");
157  bins_selection_->addItem("256");
158  bins_selection_->addItem("512");
159  bins_selection_->addItem("1024");
160  bins_selection_->addItem("2048");
161  bins_selection_->addItem("4096");
162  bins_selection_->addItem("8192");
163  bins_selection_->addItem("16384");
164 
165  connect(bins_selection_, qOverload<int>(&QComboBox::currentIndexChanged), this, &Panel::configChanged);
166  connect(bins_selection_, qOverload<int>(&QComboBox::currentIndexChanged), this, [=](const int index)
167  {
168  switch (index)
169  {
170  case 0:
171  {
172  bins_value_ = 16;
173  break;
174  }
175  case 1:
176  {
177  bins_value_ = 32;
178 
179  break;
180  }
181  case 2:
182  {
183  bins_value_ = 64;
184  break;
185  }
186  case 3:
187  {
188  bins_value_ = 128;
189  break;
190  }
191  case 4:
192  {
193  bins_value_ = 256;
194  break;
195  }
196  case 5:
197  {
198  bins_value_ = 512;
199  break;
200  }
201  case 6:
202  {
203  bins_value_ = 1024;
204  break;
205  }
206  case 7:
207  {
208  bins_value_ = 2048;
209  break;
210  }
211  case 8:
212  {
213  bins_value_ = 4096;
214  break;
215  }
216  case 9:
217  {
218  bins_value_ = 8192;
219  break;
220  }
221  case 10:
222  {
223  bins_value_ = 16394;
224  break;
225  }
226  default:
227  {
228  bins_value_ = 256;
229  break;
230  }
231  }
232  });
233  bins_selection_->setCurrentIndex(4);
234 
235  QPushButton *topic(new QPushButton("Topic"));
236  topic->setToolTip("Select the image topic");
237  connect(topic, &QPushButton::clicked, this, &HistogramPanel::topicSelectionSlot);
238 
239  buttons->addWidget(topic);
240  buttons->addStretch(1);
241  buttons->addWidget(start_stop_);
242  buttons->addStretch(1);
243  buttons->addWidget(refresh_label);
244  buttons->addWidget(graph_refresh_frequency_);
245  buttons->addStretch(1);
246  buttons->addWidget(bins_selection_label);
247  buttons->addWidget(bins_selection_);
248 
249  layout->addLayout(buttons);
250  layout->addWidget(custom_plot_);
251 }
252 
254 {
255  graph_refresh_timer_->stop();
256 }
257 
259 {
260  std::lock_guard<std::mutex> lock(data_ticks_mutex_);
261 
262  if (grayscale_)
263  {
264  if (data_.size() != ticks_.size() || data_.empty())
265  return;
266  bars_->setData(ticks_, data_, true);
267  bars_->setVisible(true);
268  blue_channel_data_.clear();
269  green_channel_data_.clear();
270  red_channel_data_.clear();
271  bars_blue_->setVisible(false);
272  bars_green_->setVisible(false);
273  bars_red_->setVisible(false);
274  }
275  else
276  {
277  if ((blue_channel_data_.size() != ticks_.size() || blue_channel_data_.empty()) ||
278  (green_channel_data_.size() != ticks_.size() || green_channel_data_.empty()) ||
279  (red_channel_data_.size() != ticks_.size() || red_channel_data_.empty()))
280  return;
284  bars_->setVisible(false);
285  data_.clear();
286  bars_blue_->setVisible(true);
287  bars_green_->setVisible(true);
288  bars_red_->setVisible(true);
289  }
290 
294  custom_plot_->replot();
295 }
296 
298 {
299  std::vector<std::string> allowed_topics;
300  allowed_topics.emplace_back("sensor_msgs/Image");
301 
302  std::deque<std::shared_ptr<TopicData>> displayed_topics;
303  SelectionTopics *topic_window = new SelectionTopics(nh_, displayed_topics, allowed_topics, true);
304 
305  if (topic_window->supported_topics_.empty())
306  {
307  Q_EMIT displayMessageBox("No supported topic", "Error with topics, no supported topics found.", "",
308  QMessageBox::Icon::Warning);
309  return;
310  }
311 
312  if (!topic_window->exec() || topic_window->displayed_topics_.empty())
313  return;
314 
315  Q_EMIT subscribeToTopic(QString::fromStdString(topic_window->displayed_topics_.at(0)->topic_name_));
316  Q_EMIT configChanged();
317 }
318 
319 void HistogramPanel::subscribeToTopicSlot(const QString topic)
320 {
321  if (topic.isEmpty())
322  return;
323 
324  topic_ = topic;
325  sub_ = nh_->subscribe(topic.toStdString(), 1, &HistogramPanel::imageCallback, this);
326 
327  custom_plot_->legend->setVisible(false);
328  start_stop_->setEnabled(true);
329  updating_ = true;
330  start_stop_->click();
331 }
332 
333 void HistogramPanel::imageCallback(const sensor_msgs::ImageConstPtr &msg)
334 {
336  std::lock_guard<std::mutex> lock(data_ticks_mutex_);
337 
338  cv_bridge::CvImagePtr cv_image;
339  try
340  {
341  cv_image = cv_bridge::toCvCopy(msg, msg->encoding);
342  }
343  catch (const cv::Exception &e)
344  {
345  std::string error("Error converting the image: ");
346  error += e.what();
347  ROS_ERROR_STREAM_NAMED(getName().toStdString(), error);
348  return;
349  }
350 
351  grayscale_ = false;
352  if (cv_image->image.channels() == 1)
353  grayscale_ = true;
354 
355  std::vector<int> histogram_size = {bins_value_};
356 
357  ticks_.resize(bins_value_);
358  for (int i(0); i < ticks_.size(); ++i)
359  ticks_[i] = i;
360 
361  if (cv_image->image.channels() == 1) // Grayscale images
362  {
363  std::vector<cv::Mat> input_image = {cv_image->image};
364 
365  cv::Mat_<float> tmp_mat; // Only to initialize the output data structure histogram
366  cv::OutputArray histogram(tmp_mat);
367 
368  data_.clear();
369 
370  if (cv_image->image.depth() == CV_8U) // 8 bits images
371  {
372  std::vector<float> ranges = {0, 256};
373  cv::calcHist(input_image, {0}, cv::Mat(), histogram, histogram_size, ranges);
374  for (int i(0); i < ticks_.size(); ++i)
375  {
376  data_.push_back(histogram.getMatRef().at<float>(i));
377  if (histogram_max_counter_ < histogram.getMatRef().at<float>(i))
378  histogram_max_counter_ = histogram.getMatRef().at<float>(i);
379  }
380  }
381  else if (cv_image->image.depth() == CV_16U) // 16 bits images
382  {
383  std::vector<float> ranges = {0, 65536};
384  cv::calcHist(input_image, {0}, cv::Mat(), histogram, histogram_size, ranges);
385  for (int i(0); i < ticks_.size(); ++i)
386  {
387  data_.push_back(histogram.getMatRef().at<float>(i));
388  if (histogram_max_counter_ < histogram.getMatRef().at<float>(i))
389  histogram_max_counter_ = histogram.getMatRef().at<float>(i);
390  }
391  }
392  else
393  {
394  ROS_ERROR_STREAM_NAMED(getName().toStdString(),
395  "Image format is not supported: only CV_8U and CV_16U images are supported");
396  return;
397  }
398  }
399  else if (cv_image->image.channels() == 3) // Color images
400  {
401  cv::Mat_<float> bgr[3];
402  split(cv_image->image, bgr);
403  std::vector<cv::Mat> input_image_b = {bgr[0]};
404  std::vector<cv::Mat> input_image_g = {bgr[1]};
405  std::vector<cv::Mat> input_image_r = {bgr[2]};
406 
407  cv::Mat_<float> tmp_mat_b; // Only to initialize the output data structure blue channel histogram, blue_histogram
408  cv::Mat_<float> tmp_mat_g; // Only to initialize the output data structure green channel histogram, green_histogram
409  cv::Mat_<float> tmp_mat_r; // Only to initialize the output data structure red channel histogram, red_histogram
410  cv::OutputArray blue_histogram(tmp_mat_b);
411  cv::OutputArray green_histogram(tmp_mat_g);
412  cv::OutputArray red_histogram(tmp_mat_r);
413 
414  blue_channel_data_.clear();
415  green_channel_data_.clear();
416  red_channel_data_.clear();
417 
418  if (cv_image->image.depth() == CV_8U) //8 bits images
419  {
420  std::vector<float> ranges = {0, 256};
421  cv::calcHist(input_image_b, {0}, cv::Mat(), blue_histogram, histogram_size, ranges);
422  cv::calcHist(input_image_g, {0}, cv::Mat(), green_histogram, histogram_size, ranges);
423  cv::calcHist(input_image_r, {0}, cv::Mat(), red_histogram, histogram_size, ranges);
424 
425  for (int i(0); i < ticks_.size(); ++i)
426  {
427  blue_channel_data_.push_back(blue_histogram.getMatRef().at<float>(i));
428  if (histogram_max_counter_ < blue_histogram.getMatRef().at<float>(i))
429  histogram_max_counter_ = blue_histogram.getMatRef().at<float>(i);
430  green_channel_data_.push_back(green_histogram.getMatRef().at<float>(i));
431  if (histogram_max_counter_ < green_histogram.getMatRef().at<float>(i))
432  histogram_max_counter_ = green_histogram.getMatRef().at<float>(i);
433  red_channel_data_.push_back(red_histogram.getMatRef().at<float>(i));
434  if (histogram_max_counter_ < red_histogram.getMatRef().at<float>(i))
435  histogram_max_counter_ = red_histogram.getMatRef().at<float>(i);
436  }
437  }
438  else if (cv_image->image.depth() == CV_16U) //16 bits images
439  {
440  std::vector<float> ranges = {0, 65536};
441  cv::calcHist(input_image_b, {0}, cv::Mat(), blue_histogram, histogram_size, ranges);
442  cv::calcHist(input_image_g, {0}, cv::Mat(), green_histogram, histogram_size, ranges);
443  cv::calcHist(input_image_r, {0}, cv::Mat(), red_histogram, histogram_size, ranges);
444 
445  for (int i(0); i < ticks_.size(); ++i)
446  {
447  blue_channel_data_.push_back(blue_histogram.getMatRef().at<float>(i));
448  if (histogram_max_counter_ < blue_histogram.getMatRef().at<float>(i))
449  histogram_max_counter_ = blue_histogram.getMatRef().at<float>(i);
450  green_channel_data_.push_back(green_histogram.getMatRef().at<float>(i));
451  if (histogram_max_counter_ < green_histogram.getMatRef().at<float>(i))
452  histogram_max_counter_ = green_histogram.getMatRef().at<float>(i);
453  red_channel_data_.push_back(red_histogram.getMatRef().at<float>(i));
454  if (histogram_max_counter_ < red_histogram.getMatRef().at<float>(i))
455  histogram_max_counter_ = red_histogram.getMatRef().at<float>(i);
456  }
457  }
458  else
459  {
460  ROS_ERROR_STREAM_NAMED(getName().toStdString(),
461  "Image format is not supported: only CV_8U and CV_16U images are supported");
462  return;
463  }
464  }
465  else
466  {
467  ROS_ERROR_STREAM_NAMED(getName().toStdString(),
468  "Image depth is not 1 or 3 : this format is not supported. Only grayscale or color images are supported.");
469  return;
470  }
471 }
472 
474 {
475  rviz::Panel::load(config);
476 
477  int tmp_int;
478  QString tmp_str;
479 
480  if (config.mapGetString(objectName() + "_subscriber", &tmp_str))
481  subscribeToTopicSlot(tmp_str);
482 
483  if (config.mapGetInt(objectName() + "_graph_refresh_frequency", &tmp_int))
484  graph_refresh_frequency_->setCurrentIndex(tmp_int);
485 
486  if (config.mapGetInt(objectName() + "_bins_selection", &tmp_int))
487  bins_selection_->setCurrentIndex(tmp_int);
488 }
489 
491 {
492  rviz::Panel::save(config);
493  config.mapSetValue(objectName() + "_subscriber", QString::fromStdString(sub_.getTopic()));
494  config.mapSetValue(objectName() + "_graph_refresh_frequency", graph_refresh_frequency_->currentIndex());
495  config.mapSetValue(objectName() + "_bins_selection", bins_selection_->currentIndex());
496 }
497 
499  const QString message,
500  const QString info_msg,
501  const QMessageBox::Icon icon)
502 {
503  const bool old(isEnabled());
504  Q_EMIT setEnabled(false);
505  QMessageBox msg_box;
506  msg_box.setWindowTitle(title);
507  msg_box.setText(message);
508  msg_box.setInformativeText(info_msg);
509  msg_box.setIcon(icon);
510  msg_box.setStandardButtons(QMessageBox::Ok);
511  msg_box.exec();
512  Q_EMIT setEnabled(old);
513 }
514 
515 }
517 
519 
void setBrush(const QBrush &brush)
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes) ...
Definition: qcustomplot.h:256
void setWidth(double width)
std::shared_ptr< ros::NodeHandle > nh_
std::string getTopic() const
void setWidthType(WidthType widthType)
#define ROS_ERROR_STREAM_NAMED(name, args)
void setInteractions(const QCP::Interactions &interactions)
virtual QString getName() const
void subscribeToTopicSlot(const QString topic)
std::deque< std::shared_ptr< TopicData > > displayed_topics_
virtual void save(rviz::Config config) const
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
void setData(QSharedPointer< QCPBarsDataContainer > data)
Q_SLOT void setRange(const QCPRange &range)
void imageCallback(const sensor_msgs::ImageConstPtr &msg)
virtual void save(Config config) const
bool mapGetString(const QString &key, QString *value_out) const
void configChanged()
QCPAxis * yAxis
Definition: qcustomplot.h:3739
void mapSetValue(const QString &key, QVariant value)
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:3590
virtual void setName(const QString &name)
void setAntialiased(bool enabled)
void setPen(const QPen &pen)
void displayMessageBox(const QString, const QString, const QString, const QMessageBox::Icon)
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
QCPLegend * legend
Definition: qcustomplot.h:3740
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
void subscribeToTopic(const QString)
void setVisible(bool on)
HistogramPanel(QWidget *parent=nullptr)
ros::master::V_TopicInfo supported_topics_
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:257
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:5482
virtual void load(const Config &config)
bool mapGetInt(const QString &key, int *value_out) const
void displayMessageBoxHandler(const QString title, const QString text, const QString info="", const QMessageBox::Icon icon=QMessageBox::Icon::Information)
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
QCPAxis * xAxis
Definition: qcustomplot.h:3739
virtual void load(const rviz::Config &config)


graph_rviz_plugin
Author(s): Édouard Pronier, Victor Lamoine - Institut Maupertuis
autogenerated on Mon Feb 28 2022 22:27:30