image_view.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011, Dirk Thomas, TU Darmstadt
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  *   * Redistributions of source code must retain the above copyright
00010  *     notice, this list of conditions and the following disclaimer.
00011  *   * Redistributions in binary form must reproduce the above
00012  *     copyright notice, this list of conditions and the following
00013  *     disclaimer in the documentation and/or other materials provided
00014  *     with the distribution.
00015  *   * Neither the name of the TU Darmstadt nor the names of its
00016  *     contributors may be used to endorse or promote products derived
00017  *     from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00027  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00028  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00029  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030  * POSSIBILITY OF SUCH DAMAGE.
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 <QFileDialog>
00043 #include <QMessageBox>
00044 #include <QPainter>
00045 
00046 namespace rqt_image_view {
00047 
00048 ImageView::ImageView()
00049   : rqt_gui_cpp::Plugin()
00050   , widget_(0)
00051   , num_gridlines_(0)
00052   , rotate_state_(ROTATE_0)
00053 {
00054   setObjectName("ImageView");
00055 }
00056 
00057 void ImageView::initPlugin(qt_gui_cpp::PluginContext& context)
00058 {
00059   widget_ = new QWidget();
00060   ui_.setupUi(widget_);
00061 
00062   if (context.serialNumber() > 1)
00063   {
00064     widget_->setWindowTitle(widget_->windowTitle() + " (" + QString::number(context.serialNumber()) + ")");
00065   }
00066   context.addWidget(widget_);
00067 
00068   updateTopicList();
00069   ui_.topics_combo_box->setCurrentIndex(ui_.topics_combo_box->findText(""));
00070   connect(ui_.topics_combo_box, SIGNAL(currentIndexChanged(int)), this, SLOT(onTopicChanged(int)));
00071 
00072   ui_.refresh_topics_push_button->setIcon(QIcon::fromTheme("view-refresh"));
00073   connect(ui_.refresh_topics_push_button, SIGNAL(pressed()), this, SLOT(updateTopicList()));
00074 
00075   ui_.zoom_1_push_button->setIcon(QIcon::fromTheme("zoom-original"));
00076   connect(ui_.zoom_1_push_button, SIGNAL(toggled(bool)), this, SLOT(onZoom1(bool)));
00077 
00078   connect(ui_.dynamic_range_check_box, SIGNAL(toggled(bool)), this, SLOT(onDynamicRange(bool)));
00079 
00080   ui_.save_as_image_push_button->setIcon(QIcon::fromTheme("document-save-as"));
00081   connect(ui_.save_as_image_push_button, SIGNAL(pressed()), this, SLOT(saveImage()));
00082 
00083   connect(ui_.num_gridlines_spin_box, SIGNAL(valueChanged(int)), this, SLOT(updateNumGridlines()));
00084 
00085   // set topic name if passed in as argument
00086   const QStringList& argv = context.argv();
00087   if (!argv.empty()) {
00088     arg_topic_name = argv[0];
00089     selectTopic(arg_topic_name);
00090   }
00091   pub_topic_custom_ = false;
00092 
00093   ui_.image_frame->setOuterLayout(ui_.image_layout);
00094 
00095   QRegExp rx("([a-zA-Z/][a-zA-Z0-9_/]*)?"); //see http://www.ros.org/wiki/ROS/Concepts#Names.Valid_Names (but also accept an empty field)
00096   ui_.publish_click_location_topic_line_edit->setValidator(new QRegExpValidator(rx, this));
00097   connect(ui_.publish_click_location_check_box, SIGNAL(toggled(bool)), this, SLOT(onMousePublish(bool)));
00098   connect(ui_.image_frame, SIGNAL(mouseLeft(int, int)), this, SLOT(onMouseLeft(int, int)));
00099   connect(ui_.publish_click_location_topic_line_edit, SIGNAL(editingFinished()), this, SLOT(onPubTopicChanged()));
00100 
00101   connect(ui_.smooth_image_check_box, SIGNAL(toggled(bool)), ui_.image_frame, SLOT(onSmoothImageChanged(bool)));
00102 
00103   connect(ui_.rotate_left_push_button, SIGNAL(clicked(bool)), this, SLOT(onRotateLeft()));
00104   connect(ui_.rotate_right_push_button, SIGNAL(clicked(bool)), this, SLOT(onRotateRight()));
00105 
00106   // Make sure we have enough space for "XXX °"
00107   ui_.rotate_label->setMinimumWidth(
00108     ui_.rotate_label->fontMetrics().width("XXX°")
00109   );
00110 
00111   hide_toolbar_action_ = new QAction(tr("Hide toolbar"), this);
00112   hide_toolbar_action_->setCheckable(true);
00113   ui_.image_frame->addAction(hide_toolbar_action_);
00114   connect(hide_toolbar_action_, SIGNAL(toggled(bool)), this, SLOT(onHideToolbarChanged(bool)));
00115 }
00116 
00117 void ImageView::shutdownPlugin()
00118 {
00119   subscriber_.shutdown();
00120   pub_mouse_left_.shutdown();
00121 }
00122 
00123 void ImageView::saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const
00124 {
00125   QString topic = ui_.topics_combo_box->currentText();
00126   //qDebug("ImageView::saveSettings() topic '%s'", topic.toStdString().c_str());
00127   instance_settings.setValue("topic", topic);
00128   instance_settings.setValue("zoom1", ui_.zoom_1_push_button->isChecked());
00129   instance_settings.setValue("dynamic_range", ui_.dynamic_range_check_box->isChecked());
00130   instance_settings.setValue("max_range", ui_.max_range_double_spin_box->value());
00131   instance_settings.setValue("publish_click_location", ui_.publish_click_location_check_box->isChecked());
00132   instance_settings.setValue("mouse_pub_topic", ui_.publish_click_location_topic_line_edit->text());
00133   instance_settings.setValue("toolbar_hidden", hide_toolbar_action_->isChecked());
00134   instance_settings.setValue("num_gridlines", ui_.num_gridlines_spin_box->value());
00135   instance_settings.setValue("smooth_image", ui_.smooth_image_check_box->isChecked());
00136   instance_settings.setValue("rotate", rotate_state_);
00137 }
00138 
00139 void ImageView::restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings)
00140 {
00141   bool zoom1_checked = instance_settings.value("zoom1", false).toBool();
00142   ui_.zoom_1_push_button->setChecked(zoom1_checked);
00143 
00144   bool dynamic_range_checked = instance_settings.value("dynamic_range", false).toBool();
00145   ui_.dynamic_range_check_box->setChecked(dynamic_range_checked);
00146 
00147   double max_range = instance_settings.value("max_range", ui_.max_range_double_spin_box->value()).toDouble();
00148   ui_.max_range_double_spin_box->setValue(max_range);
00149 
00150   num_gridlines_ = instance_settings.value("num_gridlines", ui_.num_gridlines_spin_box->value()).toInt();
00151   ui_.num_gridlines_spin_box->setValue(num_gridlines_);
00152 
00153   QString topic = instance_settings.value("topic", "").toString();
00154   // don't overwrite topic name passed as command line argument
00155   if (!arg_topic_name.isEmpty())
00156   {
00157     arg_topic_name = "";
00158   }
00159   else
00160   {
00161     //qDebug("ImageView::restoreSettings() topic '%s'", topic.toStdString().c_str());
00162     selectTopic(topic);
00163   }
00164 
00165   bool publish_click_location = instance_settings.value("publish_click_location", false).toBool();
00166   ui_.publish_click_location_check_box->setChecked(publish_click_location);
00167 
00168   QString pub_topic = instance_settings.value("mouse_pub_topic", "").toString();
00169   ui_.publish_click_location_topic_line_edit->setText(pub_topic);
00170 
00171   bool toolbar_hidden = instance_settings.value("toolbar_hidden", false).toBool();
00172   hide_toolbar_action_->setChecked(toolbar_hidden);
00173 
00174   bool smooth_image_checked = instance_settings.value("smooth_image", false).toBool();
00175   ui_.smooth_image_check_box->setChecked(smooth_image_checked);
00176 
00177   rotate_state_ = static_cast<RotateState>(instance_settings.value("rotate", 0).toInt());
00178   if(rotate_state_ >= ROTATE_STATE_COUNT)
00179     rotate_state_ = ROTATE_0;
00180   syncRotateLabel();
00181 }
00182 
00183 void ImageView::updateTopicList()
00184 {
00185   QSet<QString> message_types;
00186   message_types.insert("sensor_msgs/Image");
00187   QSet<QString> message_sub_types;
00188   message_sub_types.insert("sensor_msgs/CompressedImage");
00189 
00190   // get declared transports
00191   QList<QString> transports;
00192   image_transport::ImageTransport it(getNodeHandle());
00193   std::vector<std::string> declared = it.getDeclaredTransports();
00194   for (std::vector<std::string>::const_iterator it = declared.begin(); it != declared.end(); it++)
00195   {
00196     //qDebug("ImageView::updateTopicList() declared transport '%s'", it->c_str());
00197     QString transport = it->c_str();
00198 
00199     // strip prefix from transport name
00200     QString prefix = "image_transport/";
00201     if (transport.startsWith(prefix))
00202     {
00203       transport = transport.mid(prefix.length());
00204     }
00205     transports.append(transport);
00206   }
00207 
00208   QString selected = ui_.topics_combo_box->currentText();
00209 
00210   // fill combo box
00211   QList<QString> topics = getTopics(message_types, message_sub_types, transports).values();
00212   topics.append("");
00213   qSort(topics);
00214   ui_.topics_combo_box->clear();
00215   for (QList<QString>::const_iterator it = topics.begin(); it != topics.end(); it++)
00216   {
00217     QString label(*it);
00218     label.replace(" ", "/");
00219     ui_.topics_combo_box->addItem(label, QVariant(*it));
00220   }
00221 
00222   // restore previous selection
00223   selectTopic(selected);
00224 }
00225 
00226 QList<QString> ImageView::getTopicList(const QSet<QString>& message_types, const QList<QString>& transports)
00227 {
00228   QSet<QString> message_sub_types;
00229   return getTopics(message_types, message_sub_types, transports).values();
00230 }
00231 
00232 QSet<QString> ImageView::getTopics(const QSet<QString>& message_types, const QSet<QString>& message_sub_types, const QList<QString>& transports)
00233 {
00234   ros::master::V_TopicInfo topic_info;
00235   ros::master::getTopics(topic_info);
00236 
00237   QSet<QString> all_topics;
00238   for (ros::master::V_TopicInfo::const_iterator it = topic_info.begin(); it != topic_info.end(); it++)
00239   {
00240     all_topics.insert(it->name.c_str());
00241   }
00242 
00243   QSet<QString> topics;
00244   for (ros::master::V_TopicInfo::const_iterator it = topic_info.begin(); it != topic_info.end(); it++)
00245   {
00246     if (message_types.contains(it->datatype.c_str()))
00247     {
00248       QString topic = it->name.c_str();
00249 
00250       // add raw topic
00251       topics.insert(topic);
00252       //qDebug("ImageView::getTopics() raw topic '%s'", topic.toStdString().c_str());
00253 
00254       // add transport specific sub-topics
00255       for (QList<QString>::const_iterator jt = transports.begin(); jt != transports.end(); jt++)
00256       {
00257         if (all_topics.contains(topic + "/" + *jt))
00258         {
00259           QString sub = topic + " " + *jt;
00260           topics.insert(sub);
00261           //qDebug("ImageView::getTopics() transport specific sub-topic '%s'", sub.toStdString().c_str());
00262         }
00263       }
00264     }
00265     if (message_sub_types.contains(it->datatype.c_str()))
00266     {
00267       QString topic = it->name.c_str();
00268       int index = topic.lastIndexOf("/");
00269       if (index != -1)
00270       {
00271         topic.replace(index, 1, " ");
00272         topics.insert(topic);
00273         //qDebug("ImageView::getTopics() transport specific sub-topic '%s'", topic.toStdString().c_str());
00274       }
00275     }
00276   }
00277   return topics;
00278 }
00279 
00280 void ImageView::selectTopic(const QString& topic)
00281 {
00282   int index = ui_.topics_combo_box->findText(topic);
00283   if (index == -1)
00284   {
00285     // add topic name to list if not yet in
00286     QString label(topic);
00287     label.replace(" ", "/");
00288     ui_.topics_combo_box->addItem(label, QVariant(topic));
00289     index = ui_.topics_combo_box->findText(topic);
00290   }
00291   ui_.topics_combo_box->setCurrentIndex(index);
00292 }
00293 
00294 void ImageView::onTopicChanged(int index)
00295 {
00296   subscriber_.shutdown();
00297 
00298   // reset image on topic change
00299   ui_.image_frame->setImage(QImage());
00300 
00301   QStringList parts = ui_.topics_combo_box->itemData(index).toString().split(" ");
00302   QString topic = parts.first();
00303   QString transport = parts.length() == 2 ? parts.last() : "raw";
00304 
00305   if (!topic.isEmpty())
00306   {
00307     image_transport::ImageTransport it(getNodeHandle());
00308     image_transport::TransportHints hints(transport.toStdString());
00309     try {
00310       subscriber_ = it.subscribe(topic.toStdString(), 1, &ImageView::callbackImage, this, hints);
00311       //qDebug("ImageView::onTopicChanged() to topic '%s' with transport '%s'", topic.toStdString().c_str(), subscriber_.getTransport().c_str());
00312     } catch (image_transport::TransportLoadException& e) {
00313       QMessageBox::warning(widget_, tr("Loading image transport plugin failed"), e.what());
00314     }
00315   }
00316 
00317   onMousePublish(ui_.publish_click_location_check_box->isChecked());
00318 }
00319 
00320 void ImageView::onZoom1(bool checked)
00321 {
00322   if (checked)
00323   {
00324     if (ui_.image_frame->getImage().isNull())
00325     {
00326       return;
00327     }
00328     ui_.image_frame->setInnerFrameFixedSize(ui_.image_frame->getImage().size());
00329   } else {
00330     ui_.image_frame->setInnerFrameMinimumSize(QSize(80, 60));
00331     ui_.image_frame->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
00332     widget_->setMinimumSize(QSize(80, 60));
00333     widget_->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
00334   }
00335 }
00336 
00337 void ImageView::onDynamicRange(bool checked)
00338 {
00339   ui_.max_range_double_spin_box->setEnabled(!checked);
00340 }
00341 
00342 void ImageView::updateNumGridlines()
00343 {
00344   num_gridlines_ = ui_.num_gridlines_spin_box->value();
00345 }
00346 
00347 void ImageView::saveImage()
00348 {
00349   // take a snapshot before asking for the filename
00350   QImage img = ui_.image_frame->getImageCopy();
00351 
00352   QString file_name = QFileDialog::getSaveFileName(widget_, tr("Save as image"), "image.png", tr("Image (*.bmp *.jpg *.png *.tiff)"));
00353   if (file_name.isEmpty())
00354   {
00355     return;
00356   }
00357 
00358   img.save(file_name);
00359 }
00360 
00361 void ImageView::onMousePublish(bool checked)
00362 {
00363   std::string topicName;
00364   if(pub_topic_custom_)
00365   {
00366     topicName = ui_.publish_click_location_topic_line_edit->text().toStdString();
00367   } else {
00368     if(!subscriber_.getTopic().empty())
00369     {
00370       topicName = subscriber_.getTopic()+"_mouse_left";
00371     } else {
00372       topicName = "mouse_left";
00373     }
00374     ui_.publish_click_location_topic_line_edit->setText(QString::fromStdString(topicName));
00375   }
00376 
00377   if(checked)
00378   {
00379     pub_mouse_left_ = getNodeHandle().advertise<geometry_msgs::Point>(topicName, 1000);
00380   } else {
00381     pub_mouse_left_.shutdown();
00382   }
00383 }
00384 
00385 void ImageView::onMouseLeft(int x, int y)
00386 {
00387   if(ui_.publish_click_location_check_box->isChecked() && !ui_.image_frame->getImage().isNull())
00388   {
00389     geometry_msgs::Point clickCanvasLocation;
00390     // Publish click location in pixel coordinates
00391     clickCanvasLocation.x = round((double)x/(double)ui_.image_frame->width()*(double)ui_.image_frame->getImage().width());
00392     clickCanvasLocation.y = round((double)y/(double)ui_.image_frame->height()*(double)ui_.image_frame->getImage().height());
00393     clickCanvasLocation.z = 0;
00394 
00395     geometry_msgs::Point clickLocation = clickCanvasLocation;
00396 
00397     switch(rotate_state_)
00398     {
00399       case ROTATE_90:
00400         clickLocation.x = clickCanvasLocation.y;
00401         clickLocation.y = ui_.image_frame->getImage().width() - clickCanvasLocation.x;
00402         break;
00403       case ROTATE_180:
00404         clickLocation.x = ui_.image_frame->getImage().width() - clickCanvasLocation.x;
00405         clickLocation.y = ui_.image_frame->getImage().height() - clickCanvasLocation.y;
00406         break;
00407       case ROTATE_270:
00408         clickLocation.x = ui_.image_frame->getImage().height() - clickCanvasLocation.y;
00409         clickLocation.y = clickCanvasLocation.x;
00410         break;
00411       default:
00412         break;
00413     }
00414 
00415     pub_mouse_left_.publish(clickLocation);
00416   }
00417 }
00418 
00419 void ImageView::onPubTopicChanged()
00420 {
00421   pub_topic_custom_ = !(ui_.publish_click_location_topic_line_edit->text().isEmpty());
00422   onMousePublish(ui_.publish_click_location_check_box->isChecked());
00423 }
00424 
00425 void ImageView::onHideToolbarChanged(bool hide)
00426 {
00427   ui_.toolbar_widget->setVisible(!hide);
00428 }
00429 
00430 void ImageView::onRotateLeft()
00431 {
00432   int m = rotate_state_ - 1;
00433   if(m < 0)
00434     m = ROTATE_STATE_COUNT-1;
00435 
00436   rotate_state_ = static_cast<RotateState>(m);
00437   syncRotateLabel();
00438 }
00439 
00440 void ImageView::onRotateRight()
00441 {
00442   rotate_state_ = static_cast<RotateState>((rotate_state_ + 1) % ROTATE_STATE_COUNT);
00443   syncRotateLabel();
00444 }
00445 
00446 void ImageView::syncRotateLabel()
00447 {
00448   switch(rotate_state_)
00449   {
00450     default:
00451     case ROTATE_0:   ui_.rotate_label->setText("0°"); break;
00452     case ROTATE_90:  ui_.rotate_label->setText("90°"); break;
00453     case ROTATE_180: ui_.rotate_label->setText("180°"); break;
00454     case ROTATE_270: ui_.rotate_label->setText("270°"); break;
00455   }
00456 }
00457 
00458 void ImageView::invertPixels(int x, int y)
00459 {
00460   // Could do 255-conversion_mat_.at<cv::Vec3b>(cv::Point(x,y))[i], but that doesn't work well on gray
00461   cv::Vec3b & pixel = conversion_mat_.at<cv::Vec3b>(cv::Point(x, y));
00462   if (pixel[0] + pixel[1] + pixel[2] > 3 * 127)
00463     pixel = cv::Vec3b(0,0,0);
00464   else
00465     pixel = cv::Vec3b(255,255,255);
00466 }
00467 
00468 QList<int> ImageView::getGridIndices(int size) const
00469 {
00470   QList<int> indices;
00471 
00472   // the spacing between adjacent grid lines
00473   float grid_width = 1.0f * size / (num_gridlines_ + 1);
00474 
00475   // select grid line(s) closest to the center
00476   float index;
00477   if (num_gridlines_ % 2)  // odd
00478   {
00479     indices.append(size / 2);
00480     // make the center line 2px wide in case of an even resolution
00481     if (size % 2 == 0)  // even
00482       indices.append(size / 2 - 1);
00483     index = 1.0f * (size - 1) / 2;
00484   }
00485   else  // even
00486   {
00487     index = grid_width * (num_gridlines_ / 2);
00488     // one grid line before the center
00489     indices.append(round(index));
00490     // one grid line after the center
00491     indices.append(size - 1 - round(index));
00492   }
00493 
00494   // add additional grid lines from the center to the border of the image
00495   int lines = (num_gridlines_ - 1) / 2;
00496   while (lines > 0)
00497   {
00498     index -= grid_width;
00499     indices.append(round(index));
00500     indices.append(size - 1 - round(index));
00501     lines--;
00502   }
00503 
00504   return indices;
00505 }
00506 
00507 void ImageView::overlayGrid()
00508 {
00509   // vertical gridlines
00510   QList<int> columns = getGridIndices(conversion_mat_.cols);
00511   for (QList<int>::const_iterator x = columns.begin(); x != columns.end(); ++x)
00512   {
00513     for (int y = 0; y < conversion_mat_.rows; ++y)
00514     {
00515       invertPixels(*x, y);
00516     }
00517   }
00518 
00519   // horizontal gridlines
00520   QList<int> rows = getGridIndices(conversion_mat_.rows);
00521   for (QList<int>::const_iterator y = rows.begin(); y != rows.end(); ++y)
00522   {
00523     for (int x = 0; x < conversion_mat_.cols; ++x)
00524     {
00525       invertPixels(x, *y);
00526     }
00527   }
00528 }
00529 
00530 void ImageView::callbackImage(const sensor_msgs::Image::ConstPtr& msg)
00531 {
00532   try
00533   {
00534     // First let cv_bridge do its magic
00535     cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::RGB8);
00536     conversion_mat_ = cv_ptr->image;
00537 
00538     if (num_gridlines_ > 0)
00539       overlayGrid();
00540   }
00541   catch (cv_bridge::Exception& e)
00542   {
00543     try
00544     {
00545       // If we're here, there is no conversion that makes sense, but let's try to imagine a few first
00546       cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg);
00547       if (msg->encoding == "CV_8UC3")
00548       {
00549         // assuming it is rgb
00550         conversion_mat_ = cv_ptr->image;
00551       } else if (msg->encoding == "8UC1") {
00552         // convert gray to rgb
00553         cv::cvtColor(cv_ptr->image, conversion_mat_, CV_GRAY2RGB);
00554       } else if (msg->encoding == "16UC1" || msg->encoding == "32FC1") {
00555         // scale / quantify
00556         double min = 0;
00557         double max = ui_.max_range_double_spin_box->value();
00558         if (msg->encoding == "16UC1") max *= 1000;
00559         if (ui_.dynamic_range_check_box->isChecked())
00560         {
00561           // dynamically adjust range based on min/max in image
00562           cv::minMaxLoc(cv_ptr->image, &min, &max);
00563           if (min == max) {
00564             // completely homogeneous images are displayed in gray
00565             min = 0;
00566             max = 2;
00567           }
00568         }
00569         cv::Mat img_scaled_8u;
00570         cv::Mat(cv_ptr->image-min).convertTo(img_scaled_8u, CV_8UC1, 255. / (max - min));
00571         cv::cvtColor(img_scaled_8u, conversion_mat_, CV_GRAY2RGB);
00572       } else {
00573         qWarning("ImageView.callback_image() could not convert image from '%s' to 'rgb8' (%s)", msg->encoding.c_str(), e.what());
00574         ui_.image_frame->setImage(QImage());
00575         return;
00576       }
00577     }
00578     catch (cv_bridge::Exception& e)
00579     {
00580       qWarning("ImageView.callback_image() while trying to convert image from '%s' to 'rgb8' an exception was thrown (%s)", msg->encoding.c_str(), e.what());
00581       ui_.image_frame->setImage(QImage());
00582       return;
00583     }
00584   }
00585 
00586   // Handle rotation
00587   switch(rotate_state_)
00588   {
00589     case ROTATE_90:
00590     {
00591       cv::Mat tmp;
00592       cv::transpose(conversion_mat_, tmp);
00593       cv::flip(tmp, conversion_mat_, 1);
00594       break;
00595     }
00596     case ROTATE_180:
00597     {
00598       cv::Mat tmp;
00599       cv::flip(conversion_mat_, tmp, -1);
00600       conversion_mat_ = tmp;
00601       break;
00602     }
00603     case ROTATE_270:
00604     {
00605       cv::Mat tmp;
00606       cv::transpose(conversion_mat_, tmp);
00607       cv::flip(tmp, conversion_mat_, 0);
00608       break;
00609     }
00610     default:
00611       break;
00612   }
00613 
00614   // image must be copied since it uses the conversion_mat_ for storage which is asynchronously overwritten in the next callback invocation
00615   QImage image(conversion_mat_.data, conversion_mat_.cols, conversion_mat_.rows, conversion_mat_.step[0], QImage::Format_RGB888);
00616   ui_.image_frame->setImage(image);
00617 
00618   if (!ui_.zoom_1_push_button->isEnabled())
00619   {
00620     ui_.zoom_1_push_button->setEnabled(true);
00621   }
00622   // Need to update the zoom 1 every new image in case the image aspect ratio changed,
00623   // though could check and see if the aspect ratio changed or not.
00624   onZoom1(ui_.zoom_1_push_button->isChecked());
00625 }
00626 }
00627 
00628 PLUGINLIB_EXPORT_CLASS(rqt_image_view::ImageView, rqt_gui_cpp::Plugin)


rqt_image_view
Author(s): Dirk Thomas
autogenerated on Thu Jun 6 2019 19:02:34