00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <rqt_image_view/image_view.h>
00034
00035 #include <pluginlib/class_list_macros.h>
00036 #include <ros/master.h>
00037 #include <sensor_msgs/image_encodings.h>
00038
00039 #include <cv_bridge/cv_bridge.h>
00040 #include <opencv2/imgproc/imgproc.hpp>
00041
00042 #include <QMessageBox>
00043 #include <QPainter>
00044
00045 namespace rqt_image_view {
00046
00047 ImageView::ImageView()
00048 : rqt_gui_cpp::Plugin()
00049 , widget_(0)
00050 {
00051 setObjectName("ImageView");
00052 }
00053
00054 void ImageView::initPlugin(qt_gui_cpp::PluginContext& context)
00055 {
00056 widget_ = new QWidget();
00057 ui_.setupUi(widget_);
00058
00059 if (context.serialNumber() > 1)
00060 {
00061 widget_->setWindowTitle(widget_->windowTitle() + " (" + QString::number(context.serialNumber()) + ")");
00062 }
00063 context.addWidget(widget_);
00064
00065 ui_.image_frame->installEventFilter(this);
00066
00067 updateTopicList();
00068 ui_.topics_combo_box->setCurrentIndex(ui_.topics_combo_box->findText(""));
00069 connect(ui_.topics_combo_box, SIGNAL(currentIndexChanged(int)), this, SLOT(onTopicChanged(int)));
00070
00071 ui_.refresh_topics_push_button->setIcon(QIcon::fromTheme("view-refresh"));
00072 connect(ui_.refresh_topics_push_button, SIGNAL(pressed()), this, SLOT(updateTopicList()));
00073
00074 ui_.zoom_1_push_button->setIcon(QIcon::fromTheme("zoom-original"));
00075 connect(ui_.zoom_1_push_button, SIGNAL(toggled(bool)), this, SLOT(onZoom1(bool)));
00076
00077 connect(ui_.dynamic_range_check_box, SIGNAL(toggled(bool)), this, SLOT(onDynamicRange(bool)));
00078 }
00079
00080 bool ImageView::eventFilter(QObject* watched, QEvent* event)
00081 {
00082 if (watched == ui_.image_frame && event->type() == QEvent::Paint)
00083 {
00084 QPainter painter(ui_.image_frame);
00085 if (!qimage_.isNull())
00086 {
00087
00088
00089
00090 qimage_mutex_.lock();
00091 painter.drawImage(ui_.image_frame->contentsRect(), qimage_);
00092 qimage_mutex_.unlock();
00093 } else {
00094
00095 QLinearGradient gradient(0, 0, ui_.image_frame->frameRect().width(), ui_.image_frame->frameRect().height());
00096 gradient.setColorAt(0, Qt::white);
00097 gradient.setColorAt(1, Qt::black);
00098 painter.setBrush(gradient);
00099 painter.drawRect(0, 0, ui_.image_frame->frameRect().width() + 1, ui_.image_frame->frameRect().height() + 1);
00100 }
00101 return false;
00102 }
00103
00104 return QObject::eventFilter(watched, event);
00105 }
00106
00107 void ImageView::shutdownPlugin()
00108 {
00109 subscriber_.shutdown();
00110 }
00111
00112 void ImageView::saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const
00113 {
00114 QString topic = ui_.topics_combo_box->currentText();
00115
00116 instance_settings.setValue("topic", topic);
00117 instance_settings.setValue("zoom1", ui_.zoom_1_push_button->isChecked());
00118 instance_settings.setValue("dynamic_range", ui_.dynamic_range_check_box->isChecked());
00119 instance_settings.setValue("max_range", ui_.max_range_double_spin_box->value());
00120 }
00121
00122 void ImageView::restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings)
00123 {
00124 bool zoom1_checked = instance_settings.value("zoom1", false).toBool();
00125 ui_.zoom_1_push_button->setChecked(zoom1_checked);
00126
00127 bool dynamic_range_checked = instance_settings.value("dynamic_range", false).toBool();
00128 ui_.dynamic_range_check_box->setChecked(dynamic_range_checked);
00129
00130 double max_range = instance_settings.value("max_range", ui_.max_range_double_spin_box->value()).toDouble();
00131 ui_.max_range_double_spin_box->setValue(max_range);
00132
00133 QString topic = instance_settings.value("topic", "").toString();
00134
00135 selectTopic(topic);
00136 }
00137
00138 void ImageView::updateTopicList()
00139 {
00140 QSet<QString> message_types;
00141 message_types.insert("sensor_msgs/Image");
00142
00143
00144 QList<QString> transports;
00145 image_transport::ImageTransport it(getNodeHandle());
00146 std::vector<std::string> declared = it.getDeclaredTransports();
00147 for (std::vector<std::string>::const_iterator it = declared.begin(); it != declared.end(); it++)
00148 {
00149
00150 QString transport = it->c_str();
00151
00152
00153 QString prefix = "image_transport/";
00154 if (transport.startsWith(prefix))
00155 {
00156 transport = transport.mid(prefix.length());
00157 }
00158 transports.append(transport);
00159 }
00160
00161 QString selected = ui_.topics_combo_box->currentText();
00162
00163
00164 QList<QString> topics = getTopicList(message_types, transports);
00165 topics.append("");
00166 qSort(topics);
00167 ui_.topics_combo_box->clear();
00168 for (QList<QString>::const_iterator it = topics.begin(); it != topics.end(); it++)
00169 {
00170 QString label(*it);
00171 label.replace(" ", "/");
00172 ui_.topics_combo_box->addItem(label, QVariant(*it));
00173 }
00174
00175
00176 selectTopic(selected);
00177 }
00178
00179 QList<QString> ImageView::getTopicList(const QSet<QString>& message_types, const QList<QString>& transports)
00180 {
00181 ros::master::V_TopicInfo topic_info;
00182 ros::master::getTopics(topic_info);
00183
00184 QSet<QString> all_topics;
00185 for (ros::master::V_TopicInfo::const_iterator it = topic_info.begin(); it != topic_info.end(); it++)
00186 {
00187 all_topics.insert(it->name.c_str());
00188 }
00189
00190 QList<QString> topics;
00191 for (ros::master::V_TopicInfo::const_iterator it = topic_info.begin(); it != topic_info.end(); it++)
00192 {
00193 if (message_types.contains(it->datatype.c_str()))
00194 {
00195 QString topic = it->name.c_str();
00196
00197
00198 topics.append(topic);
00199
00200
00201
00202 for (QList<QString>::const_iterator jt = transports.begin(); jt != transports.end(); jt++)
00203 {
00204 if (all_topics.contains(topic + "/" + *jt))
00205 {
00206 QString sub = topic + " " + *jt;
00207 topics.append(sub);
00208
00209 }
00210 }
00211 }
00212 }
00213 return topics;
00214 }
00215
00216 void ImageView::selectTopic(const QString& topic)
00217 {
00218 int index = ui_.topics_combo_box->findText(topic);
00219 if (index == -1)
00220 {
00221 index = ui_.topics_combo_box->findText("");
00222 }
00223 ui_.topics_combo_box->setCurrentIndex(index);
00224 }
00225
00226 void ImageView::onTopicChanged(int index)
00227 {
00228 subscriber_.shutdown();
00229
00230
00231 qimage_ = QImage();
00232 ui_.image_frame->update();
00233
00234 QStringList parts = ui_.topics_combo_box->itemData(index).toString().split(" ");
00235 QString topic = parts.first();
00236 QString transport = parts.length() == 2 ? parts.last() : "raw";
00237
00238 if (!topic.isEmpty())
00239 {
00240 image_transport::ImageTransport it(getNodeHandle());
00241 image_transport::TransportHints hints(transport.toStdString());
00242 try {
00243 subscriber_ = it.subscribe(topic.toStdString(), 1, &ImageView::callbackImage, this, hints);
00244
00245 } catch (image_transport::TransportLoadException& e) {
00246 QMessageBox::warning(widget_, tr("Loading image transport plugin failed"), e.what());
00247 }
00248 }
00249 }
00250
00251 void ImageView::onZoom1(bool checked)
00252 {
00253 if (checked)
00254 {
00255 if (qimage_.isNull())
00256 {
00257 return;
00258 }
00259 widget_->resize(ui_.image_frame->size());
00260 widget_->setMinimumSize(widget_->sizeHint());
00261 widget_->setMaximumSize(widget_->sizeHint());
00262 } else {
00263 widget_->setMinimumSize(QSize(80, 60));
00264 widget_->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
00265 }
00266 }
00267
00268 void ImageView::onDynamicRange(bool checked)
00269 {
00270 ui_.max_range_double_spin_box->setEnabled(!checked);
00271 }
00272
00273 void ImageView::callbackImage(const sensor_msgs::Image::ConstPtr& msg)
00274 {
00275 try
00276 {
00277
00278 cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::RGB8);
00279 conversion_mat_ = cv_ptr->image;
00280 }
00281 catch (cv_bridge::Exception& e)
00282 {
00283
00284 cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg);
00285 if (msg->encoding == "CV_8UC3")
00286 {
00287
00288 conversion_mat_ = cv_ptr->image;
00289 } else if (msg->encoding == "8UC1") {
00290
00291 cv::cvtColor(cv_ptr->image, conversion_mat_, CV_GRAY2RGB);
00292 } else if (msg->encoding == "16UC1" || msg->encoding == "32FC1") {
00293
00294 double min = 0;
00295 double max = ui_.max_range_double_spin_box->value();
00296 if (msg->encoding == "16UC1") max *= 1000;
00297 if (ui_.dynamic_range_check_box->isChecked())
00298 {
00299
00300 cv::minMaxLoc(cv_ptr->image, &min, &max);
00301 if (min == max) {
00302
00303 min = 0;
00304 max = 2;
00305 }
00306 }
00307 cv::Mat img_scaled_8u;
00308 cv::Mat(cv_ptr->image-min).convertTo(img_scaled_8u, CV_8UC1, 255. / (max - min));
00309 cv::cvtColor(img_scaled_8u, conversion_mat_, CV_GRAY2RGB);
00310 } else {
00311 qWarning("ImageView.callback_image() could not convert image from '%s' to 'rgb8' (%s)", msg->encoding.c_str(), e.what());
00312 qimage_ = QImage();
00313 return;
00314 }
00315 }
00316
00317
00318 QImage image(conversion_mat_.data, conversion_mat_.cols, conversion_mat_.rows, QImage::Format_RGB888);
00319 qimage_mutex_.lock();
00320 qimage_ = image.copy();
00321 qimage_mutex_.unlock();
00322
00323 if (!ui_.zoom_1_push_button->isEnabled())
00324 {
00325 ui_.zoom_1_push_button->setEnabled(true);
00326 onZoom1(ui_.zoom_1_push_button->isChecked());
00327 }
00328 ui_.image_frame->update();
00329 }
00330
00331 }
00332
00333 PLUGINLIB_EXPORT_CLASS(rqt_image_view::ImageView, rqt_gui_cpp::Plugin)