$search
00001 #!/usr/bin/env python 00002 00003 # 00004 # Software License Agreement (BSD License) 00005 # 00006 # Copyright (c) 2010, Willow Garage, Inc. 00007 # All rights reserved. 00008 # 00009 # Redistribution and use in source and binary forms, with or without 00010 # modification, are permitted provided that the following conditions 00011 # are met: 00012 # 00013 # * Redistributions of source code must retain the above copyright 00014 # notice, this list of conditions and the following disclaimer. 00015 # * Redistributions in binary form must reproduce the above 00016 # copyright notice, this list of conditions and the following 00017 # disclaimer in the documentation and/or other materials provided 00018 # with the distribution. 00019 # * Neither the name of Willow Garage, Inc. nor the names of its 00020 # contributors may be used to endorse or promote products derived 00021 # from this software without specific prior written permission. 00022 # 00023 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00024 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00025 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00026 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00027 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00028 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00029 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00030 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00031 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00032 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00033 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00034 # POSSIBILITY OF SUCH DAMAGE. 00035 # 00036 # \author Aitor Aldoma 00037 # 00038 # Receives two parameters: 00039 # * ply_file path 00040 # * model_id 00041 # and computes stable planes, create views and the associated pose for each view 00042 # of the model and starts the normal computation + vfh descriptor for each one 00043 # of the views. 00044 00045 import roslib 00046 roslib.load_manifest('vfh_recognizer_db') 00047 import rospy 00048 import sys 00049 import vtk 00050 import os 00051 import tf.transformations as tfTransformations 00052 import numpy 00053 from geometry_msgs.msg import Pose 00054 from sensor_msgs.msg import PointCloud2, PointField 00055 from std_msgs.msg import Header, Int32 as stdMsgInt32 00056 from vfh_recognizer_db.srv import * 00057 00058 import roslib.packages 00059 sys.path.append(roslib.packages.get_pkg_dir('vfh_recognition') + "/nodes") 00060 from utils.tesselatedSphere import TesselatedSphere 00061 import views_generator.views_generator as views_generator 00062 from utils.visualize import Visualizer 00063 00064 #this will be later in a separate file 00065 import ctypes 00066 import math 00067 import struct 00068 00069 #sys.path.append("..") 00070 #from utils.tesselatedSphere import TesselatedSphere 00071 from utils.visualize import Visualizer 00072 00073 _DATATYPES = {} 00074 _DATATYPES[PointField.INT8] = ('b', 1) 00075 _DATATYPES[PointField.UINT8] = ('B', 1) 00076 _DATATYPES[PointField.INT16] = ('h', 2) 00077 _DATATYPES[PointField.UINT16] = ('H', 2) 00078 _DATATYPES[PointField.INT32] = ('i', 4) 00079 _DATATYPES[PointField.UINT32] = ('I', 4) 00080 _DATATYPES[PointField.FLOAT32] = ('f', 4) 00081 _DATATYPES[PointField.FLOAT64] = ('d', 8) 00082 00083 def create_cloud_xyz32(header,points): 00084 fields = [PointField('x', 0, PointField.FLOAT32, 1), 00085 PointField('y', 4, PointField.FLOAT32, 1), 00086 PointField('z', 8, PointField.FLOAT32, 1)] 00087 return create_cloud(header, fields, points) 00088 00089 def create_cloud(header, fields, points): 00090 cloud = PointCloud2() 00091 cloud.header = header 00092 cloud.height = 1 00093 cloud.width = len(points) 00094 cloud.is_dense = False 00095 cloud.is_bigendian = False 00096 cloud.fields = fields 00097 fmt = _get_struct_fmt(cloud) 00098 cloud_struct = struct.Struct(fmt) 00099 cloud.point_step = cloud_struct.size 00100 cloud.row_step = cloud_struct.size * cloud.width 00101 00102 buffer = ctypes.create_string_buffer(cloud_struct.size * cloud.width) 00103 00104 point_step, pack_into = cloud.point_step, cloud_struct.pack_into 00105 offset = 0 00106 for p in points: 00107 pack_into(buffer, offset, *p) 00108 offset += point_step 00109 00110 cloud.data = buffer.raw 00111 00112 return cloud 00113 00114 def _get_struct_fmt(cloud, field_names=None): 00115 fmt = '>' if cloud.is_bigendian else '<' 00116 00117 offset = 0 00118 for field in (f for f in sorted(cloud.fields, key=lambda f: f.offset) if field_names is None or f.name in field_names): 00119 if offset < field.offset: 00120 fmt += 'x' * (field.offset - offset) 00121 offset = field.offset 00122 if field.datatype not in _DATATYPES: 00123 print >> sys.stderr, 'Skipping unknown PointField datatype [%d]' % field.datatype 00124 else: 00125 datatype_fmt, datatype_length = _DATATYPES[field.datatype] 00126 fmt += field.count * datatype_fmt 00127 offset += field.count * datatype_length 00128 00129 return fmt 00130 00131 #check that the transformations are done correctly... 00132 def vtkToNumpy(vtk_matrix): 00133 np_matrix = numpy.zeros((4,4)) 00134 for i in xrange(4): 00135 for j in xrange(4): 00136 np_matrix[i,j] = float(vtk_matrix.GetElement(i,j)) 00137 00138 return np_matrix 00139 00140 def numPyToVTK(numpy_mat): 00141 vtk_matrix = vtk.vtkMatrix4x4() 00142 for i in xrange(4): 00143 for j in xrange(4): 00144 vtk_matrix.SetElement(i,j,numpy_mat[i,j]) 00145 00146 return vtk_matrix 00147 00148 #Transform a vtkMatrix4x4 to geometry_msgs/Pose 00149 #Quaternions ix+jy+kz+w are represented as [x, y, z, w]. 00150 00151 def vtkTransformToPose(transform): 00152 #print transform 00153 R = vtkToNumpy(transform) 00154 #print R #numpy matrix from vtkMatrix 00155 #print "R print" 00156 q = tfTransformations.quaternion_from_matrix(R) #get quaternion from rotation matrix 00157 #print q 00158 #rotFromQuaternion = tfTransformations.quaternion_matrix(q) #get rotation matrix from quaternion 00159 #print rotFromQuaternion 00160 #print "RotationMatrix from quatenion print" 00161 pose = Pose() 00162 pose.orientation.x = q[0] 00163 pose.orientation.y = q[1] 00164 pose.orientation.z = q[2] 00165 pose.orientation.w = q[3] 00166 pose.position.x = R[0,3] 00167 pose.position.y = R[1,3] 00168 pose.position.z = R[2,3] 00169 #print pose 00170 return pose 00171 00172 #Transform a pose to a vtkTransforom 00173 00174 def poseToVTKTransform(pose): 00175 quaternion = numpy.array((pose.orientation.x,pose.orientation.y,pose.orientation.z,pose.orientation.w)) 00176 R = tfTransformations.quaternion_matrix(quaternion) 00177 R[0,3] = pose.position.x 00178 R[1,3] = pose.position.y 00179 R[2,3] = pose.position.z 00180 matrixTrans = numPyToVTK(R) 00181 trans = vtk.vtkTransform() 00182 trans.SetMatrix(matrixTrans) 00183 return trans 00184 00185 def scaleModel(filename, factor): 00186 reader1 = vtk.vtkPLYReader() 00187 reader1.SetFileName(filename) 00188 00189 trans = vtk.vtkTransform() 00190 trans.Scale(factor,factor,factor) 00191 transFilter1 = vtk.vtkTransformFilter() 00192 transFilter1.SetTransform(trans) 00193 transFilter1.SetInputConnection(reader1.GetOutputPort()) 00194 00195 mapper1 = vtk.vtkPolyDataMapper() 00196 mapper1.SetInputConnection(transFilter1.GetOutputPort()) 00197 mapper1.Update() 00198 00199 (before, sep, after) = filename.rpartition("/") 00200 output_filename = '/tmp/' + after 00201 print "Writting scaled model in:", output_filename, " scale_factor:", scale_factor 00202 00203 writer = vtk.vtkPLYWriter() 00204 writer.SetFileName(output_filename) 00205 writer.SetInput(mapper1.GetInput()) 00206 writer.Write() 00207 00208 return output_filename 00209 00210 #sys.argv[1] filename of the original model (not scaled) 00211 #sys.argv[2] scaled_model_id 00212 #sys.argv[3] scale_factor 00213 00214 if __name__ == '__main__': 00215 if len(sys.argv) < 2: 00216 print "ERROR: ply file is missing..." 00217 else: 00218 filename = sys.argv[1] 00219 00220 if len(sys.argv) < 3: 00221 print "ERROR: scale_model_id is missing..." 00222 else: 00223 scale_model_id = int(sys.argv[2]) 00224 00225 if len(sys.argv) < 4: 00226 print "ERROR: scale_factor is missing..., using 1.0" 00227 scale_factor = 1.0 00228 else: 00229 scale_factor = float(sys.argv[3]) 00230 00231 iteration = int(sys.argv[4]) 00232 #scale the model and overwrite filename 00233 orig_file = filename 00234 output_filename = scaleModel(filename, scale_factor) 00235 filename = output_filename 00236 #quit() 00237 00238 #Generation of stable planes 00239 #the stable planes are stored in the same directory where the ply files are found 00240 #under tangent_planes 00241 00242 STABLE_PLANES = False 00243 if STABLE_PLANES: 00244 sphere = TesselatedSphere(1, False) 00245 sphere.construct() 00246 00247 if os.path.exists(filename): 00248 sphere.resetPolygons() 00249 groundPlaneFunction(filename,False, sphere) 00250 else: 00251 print "ERROR: File does not exist" 00252 00253 idMessage = stdMsgInt32() 00254 idMessage.data = int(sys.argv[2]) 00255 00256 iterationMessage = stdMsgInt32() 00257 iterationMessage.data = int(sys.argv[4]) 00258 00259 #if False: 00260 #Once the stable planes are generated, we can start generating the views 00261 #The views_generator returns a list of views (virtual camera coordinates) 00262 #and a transformation that transforms from model coordinates to the virtual 00263 #camera coordinates. Virtual camera coordinates map the REP 103 optical 00264 #frame coordinate system (see http://www.ros.org/reps/rep-0103.html) 00265 #[pcd_views, transforms] = views_generator.sample_views(filename) 00266 print "Going to generate views" 00267 [pcd_views, transforms] = views_generator.sample_views_tesselated_sphere(filename) 00268 00269 if len(pcd_views) != len(transforms): 00270 print "ERROR: Number of views does not match the number of transformations" 00271 00272 #Loop over views + pose 00273 #and call service that computes normals, VFH and store them in the database together with the pose 00274 for i in xrange(len(pcd_views)): 00275 view = pcd_views[i] 00276 trans = transforms[i] 00277 print "Number of points:", len(view) 00278 #transform to ROS messages to be sent to the service 00279 # # view = sensor_msgs/PointCloud2 00280 # # trans = geometry_msgs/Pose 00281 pc2_view = create_cloud_xyz32(Header(), view) 00282 pose_trans = vtkTransformToPose(trans) #transform vtk to pose message, cannot deal with scale... 00283 trans2 = poseToVTKTransform(pose_trans) 00284 00285 rospy.wait_for_service('normals_vfh_db_estimator') 00286 try: 00287 nvfhdb = rospy.ServiceProxy('normals_vfh_db_estimator', normals_vfh_db_estimator) 00288 resp = nvfhdb(pc2_view, pose_trans, idMessage, iterationMessage) 00289 except rospy.ServiceException, e: 00290 print "Service call failed: %s"%e 00291 00292 #check for duplicate views 00293 #rospy.wait_for_service('remove_duplicate_views') 00294 #try: 00295 # rdviews = rospy.ServiceProxy('remove_duplicate_views', remove_duplicate_views) 00296 # resp = rdviews(idMessage, iterationMessage) 00297 #except rospy.ServiceException, e: 00298 # print "remove_duplicate_views Service call failed: %s"%e 00299