00001 from distutils.version import LooseVersion
00002 import math
00003 import os
00004 import sys
00005 from threading import Lock, Thread
00006 import time
00007
00008 import cv2
00009 import numpy as np
00010 import python_qt_binding
00011 from python_qt_binding.QtCore import pyqtSignal
00012 from python_qt_binding.QtCore import pyqtSlot
00013 from python_qt_binding.QtCore import QEvent
00014 from python_qt_binding.QtCore import QSize
00015 from python_qt_binding.QtCore import Qt
00016 import python_qt_binding.QtCore as QtCore
00017 from python_qt_binding.QtCore import QTimer
00018 from python_qt_binding.QtCore import qWarning
00019 from python_qt_binding.QtCore import Slot
00020 from python_qt_binding.QtGui import QBrush
00021 from python_qt_binding.QtGui import QColor
00022 from python_qt_binding.QtGui import QFont
00023 from python_qt_binding.QtGui import QIcon
00024 from python_qt_binding.QtGui import QImage
00025 from python_qt_binding.QtGui import QPainter
00026 from python_qt_binding.QtGui import QPen
00027 from python_qt_binding.QtGui import QPixmap
00028 from python_qt_binding.QtGui import qRgb
00029 import python_qt_binding.QtGui as QtGui
00030 import yaml
00031
00032 from cv_bridge import CvBridge
00033 from cv_bridge import CvBridgeError
00034 from image_view2.msg import MouseEvent
00035 from resource_retriever import get_filename
00036 import rospy
00037 from rqt_gui_py.plugin import Plugin
00038 from sensor_msgs.msg import Image
00039 from std_msgs.msg import Bool
00040 from std_msgs.msg import Time
00041
00042 if LooseVersion(python_qt_binding.QT_BINDING_VERSION).version[0] >= 5:
00043 from python_qt_binding.QtWidgets import QAction
00044 from python_qt_binding.QtWidgets import QComboBox
00045 from python_qt_binding.QtWidgets import QDialog
00046 from python_qt_binding.QtWidgets import QMenu
00047 from python_qt_binding.QtWidgets import QMessageBox
00048 from python_qt_binding.QtWidgets import QLabel
00049 from python_qt_binding.QtWidgets import QPushButton
00050 from python_qt_binding.QtWidgets import QSizePolicy
00051 from python_qt_binding.QtWidgets import QVBoxLayout
00052 from python_qt_binding.QtWidgets import QWidget
00053
00054 else:
00055 from python_qt_binding.QtGui import QAction
00056 from python_qt_binding.QtGui import QComboBox
00057 from python_qt_binding.QtGui import QDialog
00058 from python_qt_binding.QtGui import QMenu
00059 from python_qt_binding.QtGui import QMessageBox
00060 from python_qt_binding.QtGui import QLabel
00061 from python_qt_binding.QtGui import QPushButton
00062 from python_qt_binding.QtGui import QSizePolicy
00063 from python_qt_binding.QtGui import QVBoxLayout
00064 from python_qt_binding.QtGui import QWidget
00065
00066
00067 class ComboBoxDialog(QDialog):
00068 def __init__(self, parent=None):
00069 super(ComboBoxDialog, self).__init__()
00070 self.number = 0
00071 vbox = QVBoxLayout(self)
00072 self.combo_box = QComboBox(self)
00073 self.combo_box.activated.connect(self.onActivated)
00074 vbox.addWidget(self.combo_box)
00075 button = QPushButton()
00076 button.setText("Done")
00077 button.clicked.connect(self.buttonCallback)
00078 vbox.addWidget(button)
00079 self.setLayout(vbox)
00080
00081 def buttonCallback(self, event):
00082 self.close()
00083
00084 def onActivated(self, number):
00085 self.number = number
00086
00087
00088 class ImageView2Plugin(Plugin):
00089 """
00090 rqt wrapper for image_view2
00091 """
00092 def __init__(self, context):
00093 super(ImageView2Plugin, self).__init__(context)
00094 self.setObjectName("ImageView2Plugin")
00095 self._widget = ImageView2Widget()
00096 context.add_widget(self._widget)
00097
00098 def save_settings(self, plugin_settings, instance_settings):
00099 self._widget.save_settings(plugin_settings, instance_settings)
00100
00101 def restore_settings(self, plugin_settings, instance_settings):
00102 self._widget.restore_settings(plugin_settings, instance_settings)
00103
00104 def trigger_configuration(self):
00105 self._widget.trigger_configuration()
00106
00107
00108 class ScaledLabel(QLabel):
00109 def __init__(self, *args, **kwargs):
00110 QLabel.__init__(self)
00111 self._pixmap = QPixmap(self.pixmap())
00112
00113 def resizeEvent(self, event):
00114 self.setPixmap(self._pixmap.scaled(
00115 self.width(), self.height(),
00116 QtCore.Qt.KeepAspectRatio))
00117
00118
00119 class ImageView2Widget(QWidget):
00120 """
00121 Qt widget to communicate with image_view2
00122 """
00123 cv_image = None
00124 pixmap = None
00125 repaint_trigger = pyqtSignal()
00126
00127 def __init__(self):
00128 super(ImageView2Widget, self).__init__()
00129 self.left_button_clicked = False
00130
00131 self.repaint_trigger.connect(self.redraw)
00132 self.lock = Lock()
00133 self.need_to_rewrite = False
00134 self.bridge = CvBridge()
00135 self.image_sub = None
00136 self.event_pub = None
00137 self.label = ScaledLabel()
00138 self.label.setAlignment(Qt.AlignCenter)
00139 self.label.setSizePolicy(
00140 QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored))
00141
00142 vbox = QVBoxLayout(self)
00143 vbox.addWidget(self.label)
00144 self.setLayout(vbox)
00145
00146 self._image_topics = []
00147 self._update_topic_thread = Thread(target=self.updateTopics)
00148 self._update_topic_thread.start()
00149
00150 self._active_topic = None
00151 self.setMouseTracking(True)
00152 self.label.setMouseTracking(True)
00153 self._dialog = ComboBoxDialog()
00154 self.show()
00155
00156 def trigger_configuration(self):
00157 self._dialog.exec_()
00158 self.setupSubscriber(self._image_topics[self._dialog.number])
00159
00160 def setupSubscriber(self, topic):
00161 if self.image_sub:
00162 self.image_sub.unregister()
00163 rospy.loginfo("Subscribing %s" % (topic + "/marked"))
00164 self.image_sub = rospy.Subscriber(topic + "/marked",
00165 Image,
00166 self.imageCallback)
00167 self.event_pub = rospy.Publisher(
00168 topic + "/event", MouseEvent, queue_size=1)
00169 self._active_topic = topic
00170
00171 def onActivated(self, number):
00172 self.setupSubscriber(self._image_topics[number])
00173
00174 def imageCallback(self, msg):
00175 with self.lock:
00176 if msg.width == 0 or msg.height == 0:
00177 rospy.logdebug("Looks input images is invalid")
00178 return
00179 cv_image = self.bridge.imgmsg_to_cv2(msg, msg.encoding)
00180 if msg.encoding == "bgr8":
00181 self.cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
00182 elif msg.encoding == "rgb8":
00183 self.cv_image = cv_image
00184 self.numpy_image = np.asarray(self.cv_image)
00185 self.need_to_rewrite = True
00186 self.repaint_trigger.emit()
00187
00188 def updateTopics(self):
00189 need_to_update = False
00190 for (topic, topic_type) in rospy.get_published_topics():
00191 if topic_type == "sensor_msgs/Image":
00192 with self.lock:
00193 if not topic in self._image_topics:
00194 self._image_topics.append(topic)
00195 need_to_update = True
00196 if need_to_update:
00197 with self.lock:
00198 self._image_topics = sorted(self._image_topics)
00199 self._dialog.combo_box.clear()
00200 for topic in self._image_topics:
00201 self._dialog.combo_box.addItem(topic)
00202 if self._active_topic:
00203 self._dialog.combo_box.setCurrentIndex(
00204 self._image_topics.index(self._active_topic))
00205 time.sleep(1)
00206
00207 @pyqtSlot()
00208 def redraw(self):
00209 with self.lock:
00210 if not self.need_to_rewrite:
00211 return
00212 if np.all(self.cv_image) is not None:
00213 size = self.cv_image.shape
00214 img = QImage(self.cv_image.data,
00215 size[1], size[0], size[2] * size[1],
00216 QImage.Format_RGB888)
00217
00218 self.pixmap = QPixmap(size[1], size[0])
00219 self.pixmap.convertFromImage(img)
00220 self.label.setPixmap(self.pixmap.scaled(
00221 self.label.width(), self.label.height(),
00222 QtCore.Qt.KeepAspectRatio))
00223
00224
00225 def mousePosition(self, e):
00226 label_x = self.label.x()
00227 label_y = self.label.y()
00228 label_width = self.label.width()
00229 label_height = self.label.height()
00230 pixmap_width = self.label.pixmap().width()
00231 pixmap_height = self.label.pixmap().height()
00232 x_offset = (label_width - pixmap_width) / 2.0 + label_x
00233 y_offset = (label_height - pixmap_height) / 2.0 + label_y
00234 return (e.x() - x_offset, e.y() - y_offset)
00235
00236 def mouseMoveEvent(self, e):
00237 msg = MouseEvent()
00238 msg.header.stamp = rospy.Time.now()
00239 msg.type = MouseEvent.MOUSE_MOVE
00240 msg.x, msg.y = self.mousePosition(e)
00241 msg.width = self.label.pixmap().width()
00242 msg.height = self.label.pixmap().height()
00243 if self.event_pub:
00244 self.event_pub.publish(msg)
00245
00246 def mousePressEvent(self, e):
00247 msg = MouseEvent()
00248 msg.header.stamp = rospy.Time.now()
00249 if e.button() == Qt.LeftButton:
00250 msg.type = MouseEvent.MOUSE_LEFT_DOWN
00251 self.left_button_clicked = True
00252 elif e.button() == Qt.RightButton:
00253 msg.type = MouseEvent.MOUSE_RIGHT_DOWN
00254 msg.width = self.label.pixmap().width()
00255 msg.height = self.label.pixmap().height()
00256 msg.x, msg.y = self.mousePosition(e)
00257 if self.event_pub:
00258 self.event_pub.publish(msg)
00259
00260 def mouseReleaseEvent(self, e):
00261 if e.button() == Qt.LeftButton:
00262 self.left_button_clicked = False
00263 msg = MouseEvent()
00264 msg.header.stamp = rospy.Time.now()
00265 msg.width = self.label.pixmap().width()
00266 msg.height = self.label.pixmap().height()
00267 msg.type = MouseEvent.MOUSE_LEFT_UP
00268 msg.x, msg.y = self.mousePosition(e)
00269 if self.event_pub:
00270 self.event_pub.publish(msg)
00271
00272 def save_settings(self, plugin_settings, instance_settings):
00273 if self._active_topic:
00274 instance_settings.set_value("active_topic", self._active_topic)
00275
00276 def restore_settings(self, plugin_settings, instance_settings):
00277 if instance_settings.value("active_topic"):
00278 topic = instance_settings.value("active_topic")
00279 self._dialog.combo_box.addItem(topic)
00280 self.setupSubscriber(topic)