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 This program imports sensor data from the PR2.
00034 By default it will look for a cloud (/table_cloud), an image on wide_stereo
00035 left, and the necessary transformations, and camera_info.
00036 It also can map the laser points into the image frame, and color the cloud with the projected camera image.
00037
00038 This function runs well stand-alone, or can be imported and its functions called separately.
00039
00040 Python implementation by: Jason Okerman
00041 """
00042 import roslib; roslib.load_manifest('pr2_clutter_helper')
00043
00044
00045
00046
00047
00048 import rospy
00049 from sensor_msgs.msg import Image, PointCloud, CameraInfo, ChannelFloat32
00050 from geometry_msgs.msg import PointStamped, Point32
00051 from std_msgs.msg import Bool
00052 from image_geometry.cameramodels import PinholeCameraModel
00053 from cv_bridge import CvBridge
00054 import message_filters
00055 import tf
00056
00057 import cv as roscv
00058 from random import random, randint
00059 import numpy as np
00060
00061 class AcquireCloud:
00062 def __init__(self, return_empty=False):
00063 self.bool_pub = rospy.Publisher("table_bool", Bool)
00064 self.cloud_pub = rospy.Publisher("table_cloud_colored", PointCloud)
00065 self.img_pub = rospy.Publisher("table_image/color_rect_image", Image)
00066 self.cam_pub = rospy.Publisher("table_image/camera_info", CameraInfo)
00067 self.img_msg = None
00068 self.cam_msg = None
00069 self.cloud_msg = None
00070 self.tf_matrix = None
00071 self.tf_translation = None
00072 self.tf_quaternion = None
00073 self.camPts = []
00074 self.pts3d = None
00075 self.intensities = []
00076 self.camPts_idx = []
00077 self.img = np.array([])
00078 self.N = 0
00079
00080
00081 self.tfListener = tf.TransformListener()
00082 self.fixed_frame = '/base_footprint'
00083 self.target_frame = '/wide_stereo_optical_frame'
00084
00085 self.cam_topic = "/wide_stereo/left/camera_info"
00086 self.img_topic = "/wide_stereo/left/image_rect_color"
00087 self.cloud_topic = "/table_cloud"
00088 self.cvBridge = CvBridge()
00089 if not return_empty:
00090 self.subscribe()
00091 self.wait_for_data()
00092 self.unsubscribe()
00093 self.get_tf()
00094 self.print_test()
00095 self.pts3d, self.intensities = self.get_pts3d(self.cloud_msg)
00096 self.cam_model = self.get_cam_model(self.cam_msg)
00097 self.camPts, self.camPts_idx, self.colors, self.img_dotted = self.get_camPts()
00098 roscv.SaveImage('/home/jokerman/Desktop/most_recent_img_dotted.png', self.img_dotted)
00099
00100
00101
00102 ''' Not Internal. Passed a cloud_msg
00103 Does more than title: Sets interanal pts3d and also intensities
00104 If intensities is not part of cloud_msg, then intensities will be empty array.
00105 '''
00106 def get_pts3d(self, cloud_msg):
00107 N = len(cloud_msg.points)
00108 pts3d = np.zeros([3,N])
00109 for i in range(N):
00110 row = cloud_msg.points[i]
00111 pts3d[:,i] = (row.x, row.y, row.z);
00112 intensities = np.zeros(N)
00113 for ch in cloud_msg.channels:
00114 if ch.name == 'intensities':
00115 for i in range(N):
00116 intensities[i]=ch.values[i]
00117 break;
00118 return np.matrix(pts3d), intensities
00119
00120 def pub_cloud(self):
00121 publish_cloud(self.cloud_pub, self.fixed_frame, self.pts3d, intensities=self.intensities, colors=self.colors)
00122
00123 def pub_cloud_bound(self):
00124 crop = self.camPts_idx
00125 publish_cloud(self.cloud_pub, self.fixed_frame, self.pts3d[:,crop], intensities=self.intensities[crop], colors=self.colors[:,crop])
00126
00127 def pub_cam(self, pub=None, frame=None, cam_msg_template=None):
00128 if not pub: pub = self.cam_pub
00129
00130 if not cam_msg_template: c = self.cam_msg
00131 else: c = cam_msg_template
00132 msg = CameraInfo(D=c.D,K=c.K,R=c.R,P=c.P,height=c.height,width=c.width)
00133 if not frame: frame=self.target_frame
00134 msg.header.frame_id = frame;
00135 msg.header.stamp=rospy.Time.now()
00136 pub.publish(msg)
00137
00138 def pub_img(self, pub=None, frame=None, image_template=None):
00139 if not pub: pub=self.img_pub
00140
00141 if not image_template: a = self.img_msg
00142 else: a = image_template
00143 msg = Image(height=a.height,width=a.width,encoding=a.encoding,data=a.data,is_bigendian=a.is_bigendian)
00144 if not frame: frame=self.target_frame
00145 msg.header.frame_id = frame;
00146 msg.header.stamp=rospy.Time.now()
00147 pub.publish(msg)
00148
00149 def get_cam_model(self, cam_msg):
00150 ''' Not internal. Uses: camera_info message
00151 '''
00152 cam_model = PinholeCameraModel()
00153 cam_model.fromCameraInfo(cam_msg)
00154 return cam_model
00155
00156 def print_test(self):
00157 print '---'
00158 print '* tf_matrix =\n', self.tf_matrix
00159 if self.img_msg:
00160 print '* img_msg has size', self.img_msg.width,'x',self.img_msg.height, '\n'
00161 if self.cam_msg:
00162 print '* cam_msg has K = \n', np.round(self.cam_msg.K)
00163 if self.cloud_msg:
00164 print '* cloud_msg has size', len(self.cloud_msg.points)
00165 print '* Cloud frame id', self.cloud_msg.header.frame_id
00166 print '* cloud_msg has channels:',
00167 for ch in self.cloud_msg.channels: print "'"+ch.name+"'",
00168 print '\n---'
00169
00170 def wait_for_data(self):
00171 print 'wait_for_data'
00172 while not rospy.is_shutdown() and not (self.cam_msg and self.img_msg and self.cloud_msg):
00173 rospy.sleep(.5)
00174 print '\nWaiting for:',
00175 if not self.cam_msg: print 'cam_msg,',
00176 if not self.img_msg: print 'img_msg,',
00177 if not self.cloud_msg: print 'cloud_msg',
00178
00179 ''' Internal. Sets self.sub1, sub2, sub3 to specific callbacks.
00180 '''
00181 def subscribe(self):
00182 self.sub1 = rospy.Subscriber(self.img_topic, Image, self.image_callback )
00183 self.sub2 = rospy.Subscriber(self.cloud_topic, PointCloud , self.cloud_callback )
00184 self.sub3 = rospy.Subscriber(self.cam_topic, CameraInfo, self.camera_callback )
00185
00186 def unsubscribe(self):
00187 self.sub1.unregister()
00188 self.sub2.unregister()
00189 self.sub3.unregister()
00190 print 'unregistering self'
00191
00192 def image_callback(self, img_msg):
00193 if not self.img_msg: self.img_msg = img_msg; print '\n* img'
00194 else: print '..',
00195
00196 def camera_callback(self, cam_msg):
00197 if not self.cam_msg: self.cam_msg = cam_msg; print '\n* camera'
00198 else: print '.',
00199
00200 def cloud_callback(self, cloud_msg):
00201 if not self.cloud_msg: self.cloud_msg = cloud_msg; print '\n* cloud'
00202 else: print '...',
00203
00204 def get_tf(self):
00205 self.h = rospy.Header(frame_id=self.fixed_frame)
00206 self.tf_matrix = self.tfListener.asMatrix(self.target_frame, self.h)
00207
00208 self.tf_translation = tf.transformations.translation_from_matrix(self.tf_matrix)
00209 self.tf_quaternion = tf.transformations.quaternion_from_matrix(self.tf_matrix)
00210
00211 def callback(self, img_msg, cam_msg=None, cloud_msg=None):
00212 print 'callback starting'
00213
00214 def test_publish(self):
00215 self.bool_pub.publish(bool(randint(0,1)) )
00216 print 'publish complete'
00217
00218
00219 def return_data_for_segmentation(self):
00220 '''Returns: img, --> Numpy image #Formerly ROS CV 2.1 type image
00221 pts3d, --> 3xN numpy array of 3D points
00222 intensities, --> N length numpy array
00223 camPts, --> 2xN numpy array of camera points.
00224 Points are projection from 3D. May be outside frame of camera (need camPts_idx).
00225 colors, --> 3xN numpy array (0,1,2) = (r,g,b)
00226 camPts_idx (Formerly idx_list) --> boolean array of size N for cropping
00227 '''
00228 return self.img, self.pts3d, self.intensities, self.camPts, self.colors, self.camPts_idx
00229
00230
00231 ''' Internal. Uses: cloud , img_msg, camera_model, bounds=None:
00232 Does a lot more: camPts_idx -> boolean array, true if 3D point false in camera view
00233 self.img -> from img_msg (numpy array)
00234 img_dotted -> for calibration check
00235
00236 *** NOTE, in absence of working "transfromPointCloud" function, this only works if
00237 cloud is passed already in optical camera frame. :(
00238 '''
00239 def get_camPts_already_in_cam_frame(self):
00240 self.img = self.cvBridge.imgmsg_to_cv(self.img_msg)
00241 img_dotted = np.array(self.img)
00242
00243 height = self.img_msg.height; width = self.img_msg.width;
00244 N = len(self.cloud_msg.points)
00245 colors = np.zeros( (3,N) )
00246 camPts = np.zeros( (2,N) )
00247 camPts_idx = np.array([False for i in range(N)])
00248
00249
00250 i=0
00251 for row in self.cloud_msg.points:
00252 pt3d = (row.x, row.y, row.z);
00253 uv = self.cam_model.project3dToPixel(pt3d)
00254 camPts[:,i] = uv
00255 x = uv[0]; y = uv[1];
00256 if (x >=0 and y >=0 and x<width and y <height):
00257 roscv.Circle(img_dotted, uv, 0, (255,100,100) )
00258 row = int(y); col = int(x);
00259 r,g,b = (0,1,2)
00260 color = roscv.Get2D(self.img, row, col)
00261 colors[r,i] = color[r]
00262 colors[g,i] = color[g]
00263 colors[b,i] = color[b]
00264 camPts_idx[i] = True
00265
00266 i+=1;
00267 return camPts, camPts_idx, colors, img_dotted
00268
00269 ''' Internal. Uses: cloud , img_msg, camera_model, tf_matrix, bounds=None:
00270 Does a lot more: camPts_idx -> boolean array, true if 3D point false in camera view
00271 self.img -> from img_msg (numpy array)
00272 img_dotted -> for calibration check
00273 '''
00274 def get_camPts(self):
00275 self.img = self.cvBridge.imgmsg_to_cv(self.img_msg)
00276 img_dotted = np.array(self.img)
00277
00278 height = self.img_msg.height; width = self.img_msg.width;
00279 N = len(self.cloud_msg.points)
00280
00281 camPts = np.zeros( (2,N) )
00282 pts3d_camframe = self.get_pts3d_camframe()
00283 for i in range(N):
00284 pt3d = pts3d_camframe[0,i], pts3d_camframe[1,i], pts3d_camframe[2,i]
00285 uv = self.cam_model.project3dToPixel(pt3d)
00286 camPts[:,i] = uv
00287
00288 colors = np.zeros( (3,N) )
00289 camPts_idx = np.array([False for i in range(N)])
00290 for i in range(N):
00291 x = int(camPts[0,i]); y = int(camPts[1,i]);
00292 if (x >=0 and y >=0 and x<width and y <height):
00293 row = int(y); col = int(x);
00294 r,g,b = (0,1,2)
00295 color = roscv.Get2D(self.img, row, col)
00296 colors[r,i] = color[r]
00297 colors[g,i] = color[g]
00298 colors[b,i] = color[b]
00299 camPts_idx[i] = True
00300 roscv.Circle(img_dotted, (x,y), 0, (50,250,50) )
00301
00302 return camPts, camPts_idx, colors, img_dotted
00303
00304
00305 ''' Internal. Uses: tf_matrix, cloud_msg, img_msg, bridge,
00306 Notes: camTlaser is now called tf_matrix
00307 #focus_matrix is like "K" but with cx and cy offset removed.
00308 '''
00309 def get_camPts_by_matrix(self):
00310 self.pts3d,_ = self.get_pts3d(self.cloud_msg)
00311 self.get_tf()
00312 K = np.matrix(self.cam_msg.K).reshape(3,3)
00313 cam_centers = ( K[0,2], K[1,2] )
00314
00315 (fx, fy) = K[0,0], K[1,1]
00316 focus_matrix = np.matrix([[fx, 0, 0, 0],[0, fy, 0, 0],[0, 0, 1, 0]])
00317
00318 pts3d_camframe = apply_transform_matrix( self.tf_matrix, self.pts3d)
00319 camPts = focus_matrix* xyzToHomogenous(pts3d_camframe)
00320 (u,v,w) = (0,1,2)
00321 camPts[u] = camPts[u] / camPts[w] + cam_centers[u]
00322 camPts[v] = camPts[v] / camPts[w] + cam_centers[v]
00323 camPts = np.matrix( np.round(camPts[:2]), 'int')
00324
00325
00326 self.img = self.cvBridge.imgmsg_to_cv(self.img_msg)
00327 img_dotted = np.array(self.img)
00328 height = self.img_msg.height; width = self.img_msg.width;
00329 N = len(self.cloud_msg.points)
00330 colors = np.zeros( (3,N) )
00331 camPts_idx = np.array([False for i in range(N)])
00332 for i in range(N):
00333 x = camPts[0,i]; y = camPts[1,i];
00334 if (x >=0 and y >=0 and x < width and y < height):
00335 roscv.Circle(img_dotted, (x,y), 0, (0,100,255) )
00336 row = int(y); col = int(x);
00337 r,g,b = (0,1,2)
00338 color = roscv.Get2D(self.img, row, col)
00339 colors[r,i] = color[r]
00340 colors[g,i] = color[g]
00341 colors[b,i] = color[b]
00342 camPts_idx[i] = True
00343 return camPts, camPts_idx, colors, img_dotted
00344
00345 def get_pts3d_camframe(self):
00346 if self.tf_matrix == None: self.get_tf()
00347 if self.pts3d == None: self.pts3d, self.intensities = self.get_pts3d(self.cloud_msg)
00348
00349 pts3d_camframe = apply_transform_matrix( self.tf_matrix, self.pts3d )
00350 return pts3d_camframe
00351
00352
00353
00354
00355
00356
00357
00358 '''This appears multiply a Transform matrix by a 3xN-element point or point set.
00359 Note that xyzToHomogenous is made to convert 3XN matrix, to 4XN matrix in homog coords
00360 '''
00361 def apply_transform_matrix(T, p):
00362 pn = T * xyzToHomogenous(p)
00363 return pn[0:3] / pn[3]
00364
00365 """This is redundantly defined in hrl_lib.transforms.py, as part of gt-ros-pkg
00366 convert 3XN matrix, to 4XN matrix in homogenous coords
00367 """
00368 def xyzToHomogenous(v, floating_vector=False):
00369 if floating_vector == False:
00370 return np.row_stack((v, np.ones(v.shape[1])))
00371 else:
00372 return np.row_stack((v, np.zeros(v.shape[1])))
00373
00374 def publish_cloud(pub, frame, pts3d, intensities=None, labels=None, colors=None):
00375 cloud = convert_pointcloud_to_ROS(pts3d, intensities, labels, colors)
00376 if not frame: frame='/base_footprint'
00377 cloud.header.frame_id = frame
00378 cloud.header.stamp=rospy.Time.now()
00379 pub.publish(cloud)
00380
00381 def filter_example():
00382 rospy.init_node('rosSegmentCloud')
00383 pos_pub = rospy.Publisher("face_position", PointStamped)
00384 print 'Filter the image topic and point cloud topic to come in together'
00385 image_sub = message_filters.Subscriber("/wide_stereo/left/image_rect_color", Image)
00386 cloud_sub = message_filters.Subscriber("/tilt_scan_cloud", PointCloud )
00387 camera_sub = message_filters.Subscriber("/wide_stereo/left/camera_info", CameraInfo)
00388
00389 ts = message_filters.TimeSynchronizer([image_sub, camera_sub], 10)
00390 ts.registerCallback(cs.callback)
00391
00392 def show_image(img, window_name='img', wait=False):
00393 roscv.StartWindowThread(); roscv.NamedWindow(window_name)
00394 roscv.ShowImage(window_name, img)
00395 if wait: roscv.WaitKey()
00396
00397 def save_cloud_as_py(cloud, path = './saved_cloud.py'):
00398 f = open(path,'w')
00399 f.write('#number of points = %i \n' %len(cloud.points) )
00400 f.write('points = [')
00401 for row in cloud.points:
00402 f.write('\n [%.5f, %.5f, %.5f ],' %(row.x, row.y, row.z) )
00403 f.write(']')
00404
00405 for channel in cloud.channels:
00406 if channel.name == "intensities":
00407 f.write('\n\nintensities = [')
00408 for row in channel.values:
00409 f.write('%i, ' %int(row) )
00410 f.write(']\n\n')
00411 break;
00412 f.close()
00413
00414 ''' Expects pts3D and colors to be 3xN, intensities and labels to be N.
00415 '''
00416 def convert_pointcloud_to_ROS(pts3d, intensities = None, labels = None, colors=None, bgr=True):
00417 ROS_pointcloud = PointCloud()
00418 ROS_pointcloud.points = []
00419 ROS_pointcloud.channels = []
00420 (x,y,z) = (0,1,2)
00421 for pt in np.asarray(pts3d.T):
00422 ROS_pointcloud.points += [Point32(pt[x], pt[y], pt[z])]
00423
00424 intensity_channel = ChannelFloat32(name = 'intensities')
00425 if intensities != None:
00426 for value in intensities:
00427 intensity_channel.values += [value]
00428 ROS_pointcloud.channels += [intensity_channel]
00429
00430 label_channel = ChannelFloat32(name = 'labels')
00431 if labels != None:
00432 for value in labels:
00433 label_channel.values += [value]
00434 ROS_pointcloud.channels += [label_channel]
00435
00436 if colors != None:
00437 r_ch = ChannelFloat32(name='r')
00438 g_ch = ChannelFloat32(name='g')
00439 b_ch = ChannelFloat32(name='b')
00440 rgb_ch = ChannelFloat32(name='rgb')
00441 for (r,g,b) in np.asarray(colors.T):
00442
00443 r_ch.values += [b/256]
00444 g_ch.values += [g/256]
00445 b_ch.values += [r/256]
00446 rgb_color = int(b*0xFF*0xFF+ g*0xFF+r)
00447 rgb_ch.values += [rgb_color]
00448 ROS_pointcloud.channels += [r_ch, g_ch, b_ch]
00449 return ROS_pointcloud
00450
00451
00452
00453 def main():
00454 print 'Get Bridge'
00455 br = CvBridge()
00456
00457 print 'Setup the ros node'
00458 rospy.init_node('rosSegmentCloud')
00459
00460 print 'Setup the Acquire Object'
00461 ac = AcquireCloud()
00462
00463 ac.print_test()
00464
00465 show_image(ac.img, 'img')
00466 show_image(ac.img_dotted, 'dotted')
00467
00468
00469 print 'publish test'
00470 while not rospy.is_shutdown():
00471 now = rospy.Time.now()
00472
00473
00474 ac.pub_cloud_bound()
00475
00476 im_msg = br.cv_to_imgmsg(ac.img_dotted)
00477 im_msg.header.frame_id = ac.target_frame
00478
00479 im_msg.header.stamp = now
00480 ac.img_pub.publish(im_msg)
00481 cam_msg = ac.cam_msg
00482 cam_msg.header = rospy.Header(frame_id=ac.target_frame, stamp=now)
00483
00484 ac.cam_pub.publish(cam_msg)
00485
00486
00487 ac.test_publish()
00488 print 'pub'
00489 rospy.sleep(1)
00490
00491 if __name__ == '__main__':
00492 main()
00493
00494
00495