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 
00034 
00035 
00036 
00037 
00038 
00039 from __future__ import with_statement
00040 
00041 PKG = 'approach_table_tools'
00042 NAME = 'image_cb_detector_node'
00043 import roslib; roslib.load_manifest(PKG)
00044 
00045 import rospy
00046 import cv
00047 import math
00048 import threading
00049 import numpy
00050 import tf
00051 
00052 from sensor_msgs.msg import Image, CameraInfo
00053 from geometry_msgs.msg import PoseStamped
00054 
00055 from cv_bridge import CvBridge, CvBridgeError
00056 from approach_table_tools.srv import GetCheckerboardPose, GetCheckerboardPoseResponse
00057 from image_geometry import PinholeCameraModel
00058 
00059 class ImageCbDetector:
00060   def get_board_corners(self, corners, corners_x, corners_y):
00061     return (corners[0], corners[corners_x  - 1], 
00062         corners[(corners_y - 1) * corners_x], corners[len(corners) - 1])
00063 
00064   def detect(self, image, corners_x, corners_y, spacing_x, spacing_y, width_scaling, height_scaling):
00065     
00066     scaled_width = int(.5 + image.width * width_scaling)
00067     scaled_height = int(.5 + image.height * height_scaling)
00068     
00069     
00070     image_scaled = cv.CreateMat(scaled_height, scaled_width, cv.GetElemType(image))
00071     cv.Resize(image, image_scaled, cv.CV_INTER_LINEAR)
00072 
00073     
00074     found, corners = cv.FindChessboardCorners(image_scaled, (corners_x, corners_y), cv.CV_CALIB_CB_ADAPTIVE_THRESH)
00075 
00076     if found:
00077       board_corners = self.get_board_corners(corners, corners_x, corners_y)
00078       
00079       
00080       perimeter = 0.0
00081       for i in range(len(board_corners)):
00082         next = (i + 1) % 4
00083         xdiff = board_corners[i][0] - board_corners[next][0]
00084         ydiff = board_corners[i][1] - board_corners[next][1]
00085         perimeter += math.sqrt(xdiff * xdiff + ydiff * ydiff)
00086 
00087       
00088       square_size = perimeter / ((corners_x - 1 + corners_y - 1) * 2)
00089       radius = int(square_size * 0.5 + 0.5)
00090 
00091       corners = cv.FindCornerSubPix(image_scaled, corners, (radius, radius), (-1, -1), (cv.CV_TERMCRIT_EPS | cv.CV_TERMCRIT_ITER, 30, 0.1))
00092 
00093       
00094       
00095       
00096       
00097       
00098 
00099       object_points = None
00100 
00101       
00102       if spacing_x != None and spacing_y != None:
00103         object_points = cv.CreateMat(3, corners_x * corners_y, cv.CV_32FC1)
00104 
00105         for y in range(corners_y):
00106           for x in range(corners_x):
00107             cv.SetReal2D(object_points, 0, y*corners_x + x, x * spacing_x)
00108             cv.SetReal2D(object_points, 1, y*corners_x + x, y * spacing_y)
00109             cv.SetReal2D(object_points, 2, y*corners_x + x, 0.0)
00110 
00111       
00112       corners_cv = cv.CreateMat(2, corners_x * corners_y, cv.CV_32FC1)
00113       for i in range(corners_x * corners_y):
00114         cv.SetReal2D(corners_cv, 0, i, corners[i][0])
00115         cv.SetReal2D(corners_cv, 1, i, corners[i][1])
00116 
00117       return (corners_cv, object_points)
00118 
00119     else:
00120       
00121       
00122       
00123       rospy.logwarn("Didn't find checkerboard")
00124       return (None, None)
00125 
00126 class ImageCbDetectorNode:
00127   def __init__(self):
00128     self.mutex = threading.RLock()
00129     self.corners_x = rospy.get_param('~corners_x', 6)
00130     self.corners_y = rospy.get_param('~corners_y', 6)
00131     self.spacing_x = rospy.get_param('~spacing_x', 0.022)
00132     self.spacing_y = rospy.get_param('~spacing_y', 0.022)
00133     self.publish_period = rospy.Duration(rospy.get_param('~publish_period', 0))
00134     self.last_publish_time = rospy.Time()
00135     self.width_scaling = rospy.get_param('~width_scaling', 1)
00136     self.height_scaling = rospy.get_param('~height_scaling', 1)
00137 
00138     self.im_cb_detector = ImageCbDetector()
00139 
00140     self.pose_srv = None
00141     self.bridge = CvBridge()
00142     self.cam_info = None
00143     self.ros_image = None
00144 
00145     self.image_sub = rospy.Subscriber("image_stream", Image, self.callback)
00146     self.caminfo_sub = rospy.Subscriber("camera_info", CameraInfo, self.cam_info_cb) 
00147     self.pose_pub = rospy.Publisher("board_pose", PoseStamped)
00148 
00149   def intrinsic_matrix_from_info(self, cam_info):
00150     intrinsic_matrix = cv.CreateMat(3, 3, cv.CV_32FC1)
00151 
00152     
00153     for i in range(0, 3):
00154       for j in range(0, 3):
00155         intrinsic_matrix[i, j] = cam_info.P[4*i+j]
00156 
00157     return intrinsic_matrix
00158 
00159   def attempt_to_advertise(self):
00160     
00161     if self.ros_image != None and self.cam_info != None:
00162       self.pose_srv = rospy.Service("get_checkerboard_pose", GetCheckerboardPose, self.find_checkerboard_service)
00163 
00164   def cam_info_cb(self, cam_info):
00165     with self.mutex:
00166       self.cam_info = cam_info
00167       if self.pose_srv == None:
00168         self.attempt_to_advertise()
00169 
00170   def callback(self, ros_image):
00171     
00172     with self.mutex:
00173       self.ros_image = ros_image
00174       if self.pose_srv == None:
00175         self.attempt_to_advertise()
00176       if rospy.Duration() != self.publish_period and rospy.rostime.get_rostime() - self.last_publish_time > self.publish_period:
00177         pose = self.find_checkerboard_pose(ros_image, self.corners_x, self.corners_y, self.spacing_x, self.spacing_y, self.width_scaling, self.height_scaling)
00178         if pose:
00179           self.pose_pub.publish(pose)    
00180           self.last_publish_time = rospy.rostime.get_rostime()
00181 
00182   def find_checkerboard_service(self, req):
00183     
00184     with self.mutex:
00185       image = self.ros_image
00186 
00187     pose = self.find_checkerboard_pose(image, req.corners_x, req.corners_y, req.spacing_x, req.spacing_y, self.width_scaling, self.height_scaling)
00188     return GetCheckerboardPoseResponse(pose)
00189 
00190   def find_checkerboard_pose(self, ros_image, corners_x, corners_y, spacing_x, spacing_y, width_scaling, height_scaling):
00191     
00192     try:
00193       image = self.bridge.imgmsg_to_cv(ros_image, "mono8")
00194     except CvBridgeError, e:
00195       rospy.logerror("Error importing image %s" % e)
00196       return
00197 
00198     corners, model = self.im_cb_detector.detect(image, corners_x, corners_y, spacing_x, spacing_y, width_scaling, height_scaling)
00199 
00200     if corners != None and model != None and self.cam_info != None:
00201       
00202       rot = cv.CreateMat(3, 1, cv.CV_32FC1)
00203       trans = cv.CreateMat(3, 1, cv.CV_32FC1)
00204       with self.mutex:
00205         kc = cv.CreateMat(1, 4, cv.CV_32FC1)
00206         cv.Set(kc, 0.0)
00207         intrinsic_matrix = self.intrinsic_matrix_from_info(self.cam_info)
00208 
00209         cv.FindExtrinsicCameraParams2(model, corners, intrinsic_matrix, kc, rot, trans)
00210 
00211       
00212       
00213       rot_mat = cv.CreateMat(3, 3, cv.CV_32FC1)
00214       cv.Rodrigues2(rot, rot_mat)
00215 
00216       
00217       
00218       numpy_mat = numpy.fromstring(rot_mat.tostring(), dtype = numpy.float32).reshape((3,3))
00219 
00220       
00221       full_pose = numpy.zeros((4, 4))
00222 
00223       
00224       full_pose[0:3, 0:3] = numpy_mat[0:3, 0:3]
00225 
00226       
00227       full_pose[0:3, 3] = [trans[i, 0] for i in range(3)]
00228 
00229       
00230       full_pose[3][3] = 1.0
00231 
00232       rospy.logdebug("%s" % numpy_mat)
00233       rospy.logdebug("%s" % full_pose)
00234 
00235       tf_trans = tf.transformations.translation_from_matrix(full_pose)
00236       tf_rot = tf.transformations.quaternion_from_matrix(full_pose)
00237 
00238       
00239       board_pose = PoseStamped()
00240       board_pose.header = ros_image.header
00241       board_pose.pose.position.x = tf_trans[0]
00242       board_pose.pose.position.y = tf_trans[1]
00243       board_pose.pose.position.z = tf_trans[2]
00244       board_pose.pose.orientation.x = tf_rot[0]
00245       board_pose.pose.orientation.y = tf_rot[1]
00246       board_pose.pose.orientation.z = tf_rot[2]
00247       board_pose.pose.orientation.w = tf_rot[3]
00248       rospy.logdebug("%s" % board_pose)
00249 
00250       
00251       self.pose_pub.publish(board_pose)
00252       return board_pose
00253 
00254 def cb_detector_main(argv=None):
00255   rospy.init_node(NAME, anonymous=False)
00256   cb_detector = ImageCbDetectorNode()
00257   rospy.spin()
00258 
00259 if __name__ == '__main__':
00260   cb_detector_main()
00261