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 #include <mapviz_plugins/image_plugin.h>
00031
00032
00033 #include <cstdio>
00034 #include <vector>
00035
00036
00037 #include <QDialog>
00038 #include <QGLWidget>
00039
00040
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
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
00073 QPalette p(config_widget_->palette());
00074 p.setColor(QPalette::Background, Qt::white);
00075 config_widget_->setPalette(p);
00076
00077
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
00240
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
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
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
00420 if (width != last_width_ || height != last_height_)
00421 {
00422 ScaleImage(width, height);
00423 }
00424
00425
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
00492
00493
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
00635
00636
00637
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
00648
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