image_plugin.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2014, Southwest Research Institute® (SwRI®)
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 //     * Redistributions of source code must retain the above copyright
00009 //       notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above copyright
00011 //       notice, this list of conditions and the following disclaimer in the
00012 //       documentation and/or other materials provided with the distribution.
00013 //     * Neither the name of Southwest Research Institute® (SwRI®) nor the
00014 //       names of its contributors may be used to endorse or promote products
00015 //       derived from this software without specific prior written permission.
00016 //
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //
00028 // *****************************************************************************
00029 
00030 #include <mapviz_plugins/image_plugin.h>
00031 
00032 // C++ standard libraries
00033 #include <cstdio>
00034 #include <vector>
00035 
00036 // QT libraries
00037 #include <QDialog>
00038 #include <QGLWidget>
00039 
00040 // ROS libraries
00041 #include <ros/master.h>
00042 #include <sensor_msgs/image_encodings.h>
00043 #include <opencv2/imgproc/imgproc.hpp>
00044 
00045 #include <mapviz/select_topic_dialog.h>
00046 
00047 // Declare plugin
00048 #include <pluginlib/class_list_macros.h>
00049 PLUGINLIB_DECLARE_CLASS(
00050     mapviz_plugins,
00051     image,
00052     mapviz_plugins::ImagePlugin,
00053     mapviz::MapvizPlugin)
00054 
00055 namespace mapviz_plugins
00056 {
00057   ImagePlugin::ImagePlugin() :
00058     config_widget_(new QWidget()),
00059     anchor_(TOP_LEFT),
00060     units_(PIXELS),
00061     offset_x_(0),
00062     offset_y_(0),
00063     width_(320),
00064     height_(240),
00065     transport_("default"),
00066     has_image_(false),
00067     last_width_(0),
00068     last_height_(0)
00069   {
00070     ui_.setupUi(config_widget_);
00071 
00072     // Set background white
00073     QPalette p(config_widget_->palette());
00074     p.setColor(QPalette::Background, Qt::white);
00075     config_widget_->setPalette(p);
00076 
00077     // Set status text red
00078     QPalette p3(ui_.status->palette());
00079     p3.setColor(QPalette::Text, Qt::red);
00080     ui_.status->setPalette(p3);
00081 
00082     QObject::connect(ui_.selecttopic, SIGNAL(clicked()), this, SLOT(SelectTopic()));
00083     QObject::connect(ui_.topic, SIGNAL(editingFinished()), this, SLOT(TopicEdited()));
00084     QObject::connect(ui_.anchor, SIGNAL(activated(QString)), this, SLOT(SetAnchor(QString)));
00085     QObject::connect(ui_.units, SIGNAL(activated(QString)), this, SLOT(SetUnits(QString)));
00086     QObject::connect(ui_.offsetx, SIGNAL(valueChanged(int)), this, SLOT(SetOffsetX(int)));
00087     QObject::connect(ui_.offsety, SIGNAL(valueChanged(int)), this, SLOT(SetOffsetY(int)));
00088     QObject::connect(ui_.width, SIGNAL(valueChanged(int)), this, SLOT(SetWidth(int)));
00089     QObject::connect(ui_.height, SIGNAL(valueChanged(int)), this, SLOT(SetHeight(int)));
00090     QObject::connect(this,SIGNAL(VisibleChanged(bool)),this,SLOT(SetSubscription(bool)));
00091     QObject::connect(ui_.transport_combo_box, SIGNAL(activated(const QString&)),
00092                      this, SLOT(SetTransport(const QString&)));
00093   }
00094 
00095   ImagePlugin::~ImagePlugin()
00096   {
00097   }
00098 
00099   void ImagePlugin::SetOffsetX(int offset)
00100   {
00101     offset_x_ = offset;
00102   }
00103 
00104   void ImagePlugin::SetOffsetY(int offset)
00105   {
00106     offset_y_ = offset;
00107   }
00108 
00109   void ImagePlugin::SetWidth(int width)
00110   {
00111     width_ = width;
00112   }
00113 
00114   void ImagePlugin::SetHeight(int height)
00115   {
00116     height_ = height;
00117   }
00118 
00119   void ImagePlugin::SetAnchor(QString anchor)
00120   {
00121     if (anchor == "top left")
00122     {
00123       anchor_ = TOP_LEFT;
00124     }
00125     else if (anchor == "top center")
00126     {
00127       anchor_ = TOP_CENTER;
00128     }
00129     else if (anchor == "top right")
00130     {
00131       anchor_ = TOP_RIGHT;
00132     }
00133     else if (anchor == "center left")
00134     {
00135       anchor_ = CENTER_LEFT;
00136     }
00137     else if (anchor == "center")
00138     {
00139       anchor_ = CENTER;
00140     }
00141     else if (anchor == "center right")
00142     {
00143       anchor_ = CENTER_RIGHT;
00144     }
00145     else if (anchor == "bottom left")
00146     {
00147       anchor_ = BOTTOM_LEFT;
00148     }
00149     else if (anchor == "bottom center")
00150     {
00151       anchor_ = BOTTOM_CENTER;
00152     }
00153     else if (anchor == "bottom right")
00154     {
00155       anchor_ = BOTTOM_RIGHT;
00156     }
00157   }
00158 
00159   void ImagePlugin::SetUnits(QString units)
00160   {
00161     if (units == "pixels")
00162     {
00163       units_ = PIXELS;
00164     }
00165     else if (units == "percent")
00166     {
00167       units_ = PERCENT;
00168     }
00169   }
00170   void ImagePlugin::SetSubscription(bool visible)
00171   {
00172     if(topic_.empty())
00173     {
00174       return;
00175     }
00176     else if(!visible)
00177     {
00178       image_sub_.shutdown();
00179       ROS_INFO("Dropped subscription to %s", topic_.c_str());
00180     }
00181     else
00182     {
00183       image_transport::ImageTransport it(local_node_);
00184       image_sub_ = it.subscribe(topic_, 1, &ImagePlugin::imageCallback, this);
00185 
00186       ROS_INFO("Subscribing to %s", topic_.c_str());
00187     }
00188   }
00189 
00190   void ImagePlugin::SetTransport(const QString& transport)
00191   {
00192     ROS_INFO("Changing image_transport to %s.", transport.toStdString().c_str());
00193     transport_ = transport;
00194     TopicEdited();
00195   }
00196 
00197   void ImagePlugin::Resubscribe()
00198   {
00199     if (transport_ == QString::fromStdString("default"))
00200     {
00201       force_resubscribe_ = true;
00202       TopicEdited();
00203     }
00204   }
00205 
00206   void ImagePlugin::SelectTopic()
00207   {
00208     ros::master::TopicInfo topic = mapviz::SelectTopicDialog::selectTopic(
00209       "sensor_msgs/Image");
00210 
00211     if(topic.name.empty())
00212     {
00213       topic.name.clear();
00214       TopicEdited();
00215 
00216     }
00217     if (!topic.name.empty())
00218     {
00219       ui_.topic->setText(QString::fromStdString(topic.name));
00220       TopicEdited();
00221     }
00222   }
00223 
00224   void ImagePlugin::TopicEdited()
00225   {
00226     std::string topic = ui_.topic->text().trimmed().toStdString();
00227     if(!this->Visible())
00228     {
00229       PrintWarning("Topic is Hidden");
00230       initialized_ = false;
00231       has_message_ = false;
00232       if (!topic.empty())
00233       {
00234         topic_ = topic;
00235       }
00236       image_sub_.shutdown();
00237       return;
00238     }
00239     // Re-subscribe if either the topic or the image transport
00240     // have changed.
00241     if (force_resubscribe_ ||
00242         topic != topic_ ||
00243         image_sub_.getTransport() != transport_.toStdString())
00244     {
00245       force_resubscribe_ = false;
00246       initialized_ = false;
00247       has_message_ = false;
00248       topic_ = topic;
00249       PrintWarning("No messages received.");
00250 
00251       image_sub_.shutdown();
00252 
00253       if (!topic_.empty())
00254       {
00255         boost::shared_ptr<image_transport::ImageTransport> it;
00256         if (transport_ == QString::fromStdString("default"))
00257         {
00258           ROS_DEBUG("Using default transport.");
00259           it = boost::make_shared<image_transport::ImageTransport>(node_);
00260           image_sub_ = it->subscribe(topic_, 1, &ImagePlugin::imageCallback, this);
00261         }
00262         else
00263         {
00264           ROS_DEBUG("Setting transport to %s on %s.",
00265                    transport_.toStdString().c_str(), local_node_.getNamespace().c_str());
00266 
00267           local_node_.setParam("image_transport", transport_.toStdString());
00268           it = boost::make_shared<image_transport::ImageTransport>(local_node_);
00269           image_sub_ = it->subscribe(topic_, 1, &ImagePlugin::imageCallback, this,
00270                                      image_transport::TransportHints(transport_.toStdString(),
00271                                                                      ros::TransportHints(),
00272                                                                      local_node_));
00273         }
00274 
00275         ROS_INFO("Subscribing to %s", topic_.c_str());
00276       }
00277     }
00278   }
00279 
00280   void ImagePlugin::imageCallback(const sensor_msgs::ImageConstPtr& image)
00281   {
00282     if (!has_message_)
00283     {
00284       initialized_ = true;
00285       has_message_ = true;
00286     }
00287 
00288     image_ = *image;
00289 
00290     try
00291     {
00292       cv_image_ = cv_bridge::toCvCopy(image, sensor_msgs::image_encodings::BGR8);
00293     }
00294     catch (const cv_bridge::Exception& e)
00295     {
00296       PrintError(e.what());
00297       return;
00298     }
00299 
00300     last_width_ = 0;
00301     last_height_ = 0;
00302 
00303     has_image_ = true;
00304   }
00305 
00306   void ImagePlugin::PrintError(const std::string& message)
00307   {
00308     if (message == ui_.status->text().toStdString())
00309     {
00310       return;
00311     }
00312 
00313     ROS_ERROR("Error: %s", message.c_str());
00314     QPalette p(ui_.status->palette());
00315     p.setColor(QPalette::Text, Qt::red);
00316     ui_.status->setPalette(p);
00317     ui_.status->setText(message.c_str());
00318   }
00319 
00320   void ImagePlugin::PrintInfo(const std::string& message)
00321   {
00322     if (message == ui_.status->text().toStdString())
00323     {
00324       return;
00325     }
00326 
00327     ROS_INFO("%s", message.c_str());
00328     QPalette p(ui_.status->palette());
00329     p.setColor(QPalette::Text, Qt::green);
00330     ui_.status->setPalette(p);
00331     ui_.status->setText(message.c_str());
00332   }
00333 
00334   void ImagePlugin::PrintWarning(const std::string& message)
00335   {
00336     if (message == ui_.status->text().toStdString())
00337     {
00338       return;
00339     }
00340 
00341     ROS_WARN("%s", message.c_str());
00342     QPalette p(ui_.status->palette());
00343     p.setColor(QPalette::Text, Qt::darkYellow);
00344     ui_.status->setPalette(p);
00345     ui_.status->setText(message.c_str());
00346   }
00347 
00348   QWidget* ImagePlugin::GetConfigWidget(QWidget* parent)
00349   {
00350     config_widget_->setParent(parent);
00351 
00352     return config_widget_;
00353   }
00354 
00355   bool ImagePlugin::Initialize(QGLWidget* canvas)
00356   {
00357     canvas_ = canvas;
00358 
00359     return true;
00360   }
00361 
00362   void ImagePlugin::ScaleImage(double width, double height)
00363   {
00364     if (!has_image_)
00365     {
00366       return;
00367     }
00368 
00369     cv::resize(cv_image_->image, scaled_image_, cvSize2D32f(width, height), 0, 0, CV_INTER_AREA);
00370   }
00371 
00372   void ImagePlugin::DrawIplImage(cv::Mat *image)
00373   {
00374     // TODO(malban) glTexture2D may be more efficient than glDrawPixels
00375 
00376     if (image == NULL || image->cols == 0 || image->rows == 0)
00377     {
00378       return;
00379     }
00380 
00381     GLenum format;
00382     switch (image->channels())
00383     {
00384       case 1:
00385         format = GL_LUMINANCE;
00386         break;
00387       case 2:
00388         format = GL_LUMINANCE_ALPHA;
00389         break;
00390       case 3:
00391         format = GL_BGR;
00392         break;
00393       default:
00394         return;
00395     }
00396 
00397     glPixelZoom(1.0f, -1.0f);
00398     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00399     glDrawPixels(image->cols, image->rows, format, GL_UNSIGNED_BYTE, image->ptr());
00400 
00401     PrintInfo("OK");
00402   }
00403 
00404   void ImagePlugin::Draw(double x, double y, double scale)
00405   {
00406     // Calculate the correct offsets and dimensions
00407     double x_offset = offset_x_;
00408     double y_offset = offset_y_;
00409     double width = width_;
00410     double height = height_;
00411     if (units_ == PERCENT)
00412     {
00413       x_offset = offset_x_ * canvas_->width() / 100.0;
00414       y_offset = offset_y_ * canvas_->height() / 100.0;
00415       width = width_ * canvas_->width() / 100.0;
00416       height = height_ * canvas_->height() / 100.0;
00417     }
00418 
00419     // Scale the source image if necessary
00420     if (width != last_width_ || height != last_height_)
00421     {
00422       ScaleImage(width, height);
00423     }
00424 
00425     // Calculate the correct render position
00426     double x_pos = 0;
00427     double y_pos = 0;
00428     if (anchor_ == TOP_LEFT)
00429     {
00430       x_pos = x_offset;
00431       y_pos = y_offset;
00432     }
00433     else if (anchor_ == TOP_CENTER)
00434     {
00435       x_pos = (canvas_->width() - width) / 2.0 + x_offset;
00436       y_pos = y_offset;
00437     }
00438     else if (anchor_ == TOP_RIGHT)
00439     {
00440       x_pos = canvas_->width() - width - x_offset;
00441       y_pos = y_offset;
00442     }
00443     else if (anchor_ == CENTER_LEFT)
00444     {
00445       x_pos = x_offset;
00446       y_pos = (canvas_->height() - height) / 2.0 + y_offset;
00447     }
00448     else if (anchor_ == CENTER)
00449     {
00450       x_pos = (canvas_->width() - width) / 2.0 + x_offset;
00451       y_pos = (canvas_->height() - height) / 2.0 + y_offset;
00452     }
00453     else if (anchor_ == CENTER_RIGHT)
00454     {
00455       x_pos = canvas_->width() - width - x_offset;
00456       y_pos = (canvas_->height() - height) / 2.0 + y_offset;
00457     }
00458     else if (anchor_ == BOTTOM_LEFT)
00459     {
00460       x_pos = x_offset;
00461       y_pos = canvas_->height() - height - y_offset;
00462     }
00463     else if (anchor_ == BOTTOM_CENTER)
00464     {
00465       x_pos = (canvas_->width() - width) / 2.0 + x_offset;
00466       y_pos = canvas_->height() - height - y_offset;
00467     }
00468     else if (anchor_ == BOTTOM_RIGHT)
00469     {
00470       x_pos = canvas_->width() - width - x_offset;
00471       y_pos = canvas_->height() - height - y_offset;
00472     }
00473 
00474     glMatrixMode(GL_PROJECTION);
00475     glPushMatrix();
00476     glLoadIdentity();
00477     glOrtho(0, canvas_->width(), canvas_->height(), 0, -0.5f, 0.5f);
00478 
00479     glRasterPos2d(x_pos, y_pos);
00480 
00481     DrawIplImage(&scaled_image_);
00482 
00483     glPopMatrix();
00484 
00485     last_width_ = width;
00486     last_height_ = height;
00487   }
00488 
00489   void ImagePlugin::LoadConfig(const YAML::Node& node, const std::string& path)
00490   {
00491     // Note that image_transport should be loaded before the
00492     // topic to make sure the transport is set appropriately before we
00493     // subscribe.
00494     if (node["image_transport"])
00495     {
00496       std::string transport;
00497       node["image_transport"] >> transport;
00498       transport_ = QString::fromStdString(transport);
00499       int index = ui_.transport_combo_box->findText(transport_);
00500       if (index != -1)
00501       {
00502         ui_.transport_combo_box->setCurrentIndex(index);
00503       }
00504       else
00505       {
00506         ROS_WARN("Saved image transport %s is unavailable.",
00507                  transport_.toStdString().c_str());
00508       }
00509     }
00510 
00511     if (node["topic"])
00512     {
00513       std::string topic;
00514       node["topic"] >> topic;
00515       ui_.topic->setText(topic.c_str());
00516       TopicEdited();
00517     }
00518 
00519     if (node["anchor"])
00520     {
00521       std::string anchor;
00522       node["anchor"] >> anchor;
00523       ui_.anchor->setCurrentIndex(ui_.anchor->findText(anchor.c_str()));
00524       SetAnchor(anchor.c_str());
00525     }
00526 
00527     if (node["units"])
00528     {
00529       std::string units;
00530       node["units"] >> units;
00531       ui_.units->setCurrentIndex(ui_.units->findText(units.c_str()));
00532       SetUnits(units.c_str());
00533     }
00534 
00535     if (node["offset_x"])
00536     {
00537       node["offset_x"] >> offset_x_;
00538       ui_.offsetx->setValue(offset_x_);
00539     }
00540 
00541     if (node["offset_y"])
00542     {
00543       node["offset_y"] >> offset_y_;
00544       ui_.offsety->setValue(offset_y_);
00545     }
00546 
00547     if (node["width"])
00548     {
00549       node["width"] >> width_;
00550       ui_.width->setValue(width_);
00551     }
00552 
00553     if (node["height"])
00554     {
00555       node["height"] >> height_;
00556       ui_.height->setValue(height_);
00557     }
00558   }
00559 
00560   void ImagePlugin::SaveConfig(YAML::Emitter& emitter, const std::string& path)
00561   {
00562     emitter << YAML::Key << "topic" << YAML::Value << ui_.topic->text().toStdString();
00563     emitter << YAML::Key << "anchor" << YAML::Value << AnchorToString(anchor_);
00564     emitter << YAML::Key << "units" << YAML::Value << UnitsToString(units_);
00565     emitter << YAML::Key << "offset_x" << YAML::Value << offset_x_;
00566     emitter << YAML::Key << "offset_y" << YAML::Value << offset_y_;
00567     emitter << YAML::Key << "width" << YAML::Value << width_;
00568     emitter << YAML::Key << "height" << YAML::Value << height_;
00569     emitter << YAML::Key << "image_transport" << YAML::Value << transport_.toStdString();
00570   }
00571 
00572   std::string ImagePlugin::AnchorToString(Anchor anchor)
00573   {
00574     std::string anchor_string = "top left";
00575 
00576     if (anchor == TOP_LEFT)
00577     {
00578       anchor_string = "top left";
00579     }
00580     else if (anchor == TOP_CENTER)
00581     {
00582       anchor_string = "top center";
00583     }
00584     else if (anchor == TOP_RIGHT)
00585     {
00586       anchor_string = "top right";
00587     }
00588     else if (anchor == CENTER_LEFT)
00589     {
00590       anchor_string = "center left";
00591     }
00592     else if (anchor == CENTER)
00593     {
00594       anchor_string = "center";
00595     }
00596     else if (anchor == CENTER_RIGHT)
00597     {
00598       anchor_string = "center right";
00599     }
00600     else if (anchor == BOTTOM_LEFT)
00601     {
00602       anchor_string = "bottom left";
00603     }
00604     else if (anchor == BOTTOM_CENTER)
00605     {
00606       anchor_string = "bottom center";
00607     }
00608     else if (anchor == BOTTOM_RIGHT)
00609     {
00610       anchor_string = "bottom right";
00611     }
00612 
00613     return anchor_string;
00614   }
00615 
00616   std::string ImagePlugin::UnitsToString(Units units)
00617   {
00618     std::string units_string = "pixels";
00619 
00620     if (units == PIXELS)
00621     {
00622       units_string = "pixels";
00623     }
00624     else if (units == PERCENT)
00625     {
00626       units_string = "percent";
00627     }
00628 
00629     return units_string;
00630   }
00631 
00632   void ImagePlugin::CreateLocalNode()
00633   {
00634     // This is the same way ROS generates anonymous node names.
00635     // See http://docs.ros.org/api/roscpp/html/this__node_8cpp_source.html
00636     // Giving each image plugin a unique node means that we can control
00637     // its image transport individually.
00638     char buf[200];
00639     snprintf(buf, sizeof(buf), "image_%llu", (unsigned long long)ros::WallTime::now().toNSec());
00640     local_node_ = ros::NodeHandle(node_, buf);
00641   }
00642 
00643   void ImagePlugin::SetNode(const ros::NodeHandle& node)
00644   {
00645     node_ = node;
00646 
00647     // As soon as we have a node, we can find the available image transports
00648     // and add them to our combo box.
00649     image_transport::ImageTransport it(node_);
00650     std::vector<std::string> transports = it.getLoadableTransports();
00651     Q_FOREACH (const std::string& transport, transports)
00652     {
00653       QString qtransport = QString::fromStdString(transport).replace("image_transport/", "");
00654       ui_.transport_combo_box->addItem(qtransport);
00655     }
00656 
00657     CreateLocalNode();
00658   }
00659 }
00660 


mapviz_plugins
Author(s): Marc Alban
autogenerated on Thu Aug 24 2017 02:46:09