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 #include <cstdio>
00027 #include <vector>
00028 #include <sstream>
00029 #include <ros/ros.h>
00030
00031 #include <boost/thread/mutex.hpp>
00032
00033 #include "opencv/cv.h"
00034 #include "opencv/highgui.h"
00035 #include "cv_bridge/cv_bridge.h"
00036 #include "sensor_msgs/image_encodings.h"
00037 #include "sensor_msgs/CameraInfo.h"
00038 #include "sensor_msgs/Image.h"
00039 #include "posedetection_msgs/ObjectDetection.h"
00040 #include "posedetection_msgs/Detect.h"
00041 #include "geometry_msgs/PointStamped.h"
00042 #include "math.h"
00043
00044 #include <sys/timeb.h>
00045 #include <sys/time.h>
00046
00047 using namespace std;
00048 using namespace ros;
00049
00050 class CheckerboardDetector
00051 {
00052 template <typename T>
00053 static vector<T> tokenizevector(const string& s)
00054 {
00055 stringstream ss(s);
00056 return vector<T>((istream_iterator<T>(ss)), istream_iterator<T>());
00057 }
00058
00059 public:
00060 struct CHECKERBOARD
00061 {
00062 CvSize griddims;
00063 vector<Vector> grid3d;
00064 vector<CvPoint2D32f> corners;
00065 TransformMatrix tlocaltrans;
00066 };
00067
00068 posedetection_msgs::ObjectDetection _objdetmsg;
00069 cv_bridge::CvImagePtr capture;
00070 sensor_msgs::CameraInfo _camInfoMsg;
00071
00072 ros::Subscriber camInfoSubscriber,camInfoSubscriber2;
00073 ros::Subscriber imageSubscriber,imageSubscriber2;
00074 ros::Publisher _pubDetection;
00075 ros::Publisher _pubCornerPoint;
00076 ros::ServiceServer _srvDetect;
00077
00078 string frame_id;
00079
00080 int display, verbose, maxboard;
00081 vector<CHECKERBOARD> vcheckers;
00082 vector< string > vstrtypes;
00083 map<string,int> maptypes;
00084 ros::Time lasttime;
00085 CvMat *intrinsic_matrix;
00086 boost::mutex mutexcalib;
00087 IplImage* frame;
00088
00089 ros::NodeHandle _node;
00090
00092
00093 CheckerboardDetector() : intrinsic_matrix(NULL), frame(NULL)
00094 {
00095 _node.param("display", display, 0);
00096 _node.param("verbose", verbose, 1);
00097 _node.param("maxboard", maxboard, -1);
00098
00099 char str[32];
00100 int index = 0;
00101
00102 while(1) {
00103 int dimx, dimy;
00104 double fRectSize[2];
00105 string type;
00106
00107 sprintf(str,"grid%d_size_x",index);
00108 if( !_node.getParam(str,dimx) )
00109 break;
00110 if (dimx < 3) {
00111 ROS_ERROR("Param: %s must be greater than 2",str);
00112 return;
00113 }
00114
00115 sprintf(str,"grid%d_size_y",index);
00116 if( !_node.getParam(str,dimy) )
00117 break;
00118 if (dimy < 3) {
00119 ROS_ERROR("Param: %s must be greater than 2",str);
00120 return;
00121 }
00122
00123 sprintf(str,"rect%d_size_x",index);
00124 if( !_node.getParam(str,fRectSize[0]) )
00125 break;
00126
00127 sprintf(str,"rect%d_size_y",index);
00128 if( !_node.getParam(str,fRectSize[1]) )
00129 break;
00130
00131 sprintf(str,"type%d",index);
00132 if( !_node.getParam(str,type) ) {
00133 sprintf(str,"checker%dx%d", dimx, dimy);
00134 type = str;
00135 }
00136
00137 string strtranslation,strrotation;
00138 sprintf(str,"translation%d",index);
00139 _node.param(str,strtranslation,string());
00140
00141 vector<float> vtranslation = tokenizevector<float>(strtranslation);
00142 sprintf(str,"rotation%d",index);
00143 _node.param(str,strrotation,string());
00144
00145 vector<float> vrotation = tokenizevector<float>(strrotation);
00146
00147 CHECKERBOARD cb;
00148 cb.griddims = cvSize(dimx,dimy);
00149
00150 cb.grid3d.resize(dimx*dimy);
00151 int j=0;
00152 for(int y=0; y<dimy; ++y)
00153 for(int x=0; x<dimx; ++x)
00154 cb.grid3d[j++] = Vector(x*fRectSize[0], y*fRectSize[1], 0);
00155
00156 if( vtranslation.size() == 3 )
00157 cb.tlocaltrans.trans =
00158 Vector(vtranslation[0],vtranslation[1],vtranslation[2]);
00159
00160 if( vrotation.size() == 9 ) {
00161 for(int k = 0; k < 3; ++k) {
00162 cb.tlocaltrans.m[4*k+0] = vrotation[3*k+0];
00163 cb.tlocaltrans.m[4*k+1] = vrotation[3*k+1];
00164 cb.tlocaltrans.m[4*k+2] = vrotation[3*k+2];
00165 }
00166 }
00167
00168 vcheckers.push_back(cb);
00169 vstrtypes.push_back(type);
00170 maptypes[vstrtypes.back()] = index;
00171 index++;
00172 }
00173
00174 _node.param("frame_id", frame_id,string(""));
00175
00176 if( maptypes.size() == 0 ) {
00177 ROS_ERROR("no checkerboards to detect");
00178 return;
00179 }
00180
00181 if( display ) {
00182
00183 cvNamedWindow("Checkerboard Detector", (display == 1? CV_WINDOW_AUTOSIZE : display));
00184 cvStartWindowThread();
00185 }
00186
00187 lasttime = ros::Time::now();
00188
00189 ros::SubscriberStatusCallback connect_cb = boost::bind( &CheckerboardDetector::connectCb, this);
00190 _pubDetection =
00191 _node.advertise<posedetection_msgs::ObjectDetection> ("ObjectDetection", 1,
00192 connect_cb, connect_cb);
00193 _pubCornerPoint = _node.advertise<geometry_msgs::PointStamped>("corner_point", 1, connect_cb, connect_cb);
00194
00195
00196
00197
00198 _srvDetect = _node.advertiseService("Detect",&CheckerboardDetector::detect_cb,this);
00199 }
00200
00202
00203 virtual ~CheckerboardDetector()
00204 {
00205 if( frame )
00206 cvReleaseImage(&frame);
00207 if( this->intrinsic_matrix )
00208 cvReleaseMat(&this->intrinsic_matrix);
00209 _srvDetect.shutdown();
00210 this->camInfoSubscriber.shutdown();
00211 this->imageSubscriber.shutdown();
00212 this->camInfoSubscriber2.shutdown();
00213 this->imageSubscriber2.shutdown();
00214 }
00215
00217
00218 void caminfo_cb(const sensor_msgs::CameraInfoConstPtr &msg)
00219 {
00220 boost::mutex::scoped_lock lock(this->mutexcalib);
00221
00222 this->_camInfoMsg = *msg;
00223
00224
00225
00226
00227 }
00228 void caminfo_cb2(const sensor_msgs::CameraInfoConstPtr &msg)
00229 {
00230 ROS_WARN("The topic CameraInfo has been deprecated. Please change your launch file to use camera_info instead.");
00231 caminfo_cb(msg);
00232 }
00233
00234 void image_cb2(const sensor_msgs::ImageConstPtr &msg)
00235 {
00236 ROS_WARN("The topic Image has been deprecated. Please change your launch file to use image instead.");
00237 boost::mutex::scoped_lock lock(this->mutexcalib);
00238 if( Detect(_objdetmsg,*msg,this->_camInfoMsg) )
00239 _pubDetection.publish(_objdetmsg);
00240 }
00241
00243
00244 void image_cb(const sensor_msgs::ImageConstPtr &msg)
00245 {
00246 boost::mutex::scoped_lock lock(this->mutexcalib);
00247 if( Detect(_objdetmsg,*msg,this->_camInfoMsg) )
00248 _pubDetection.publish(_objdetmsg);
00249 }
00250
00251 bool detect_cb(posedetection_msgs::Detect::Request& req, posedetection_msgs::Detect::Response& res)
00252 {
00253 return Detect(res.object_detection,req.image,req.camera_info);
00254 }
00255
00256
00257 void connectCb( )
00258 {
00259 boost::mutex::scoped_lock lock(this->mutexcalib);
00260 if (_pubDetection.getNumSubscribers() == 0 && _pubCornerPoint.getNumSubscribers() == 0)
00261 {
00262 camInfoSubscriber.shutdown();
00263 camInfoSubscriber2.shutdown();
00264 imageSubscriber.shutdown();
00265 imageSubscriber2.shutdown();
00266 }
00267 else
00268 {
00269 if ( camInfoSubscriber == NULL )
00270 camInfoSubscriber = _node.subscribe("camera_info", 1, &CheckerboardDetector::caminfo_cb, this);
00271 if ( imageSubscriber == NULL )
00272 imageSubscriber = _node.subscribe("image", 1, &CheckerboardDetector::image_cb, this);
00273 if ( camInfoSubscriber2 == NULL )
00274 camInfoSubscriber2 = _node.subscribe("CameraInfo", 1, &CheckerboardDetector::caminfo_cb2, this);
00275 if ( imageSubscriber2 == NULL )
00276 imageSubscriber2 = _node.subscribe("Image",1, &CheckerboardDetector::image_cb2, this);
00277 }
00278 }
00279
00280 bool Detect(posedetection_msgs::ObjectDetection& objdetmsg, const sensor_msgs::Image& imagemsg, const sensor_msgs::CameraInfo& camInfoMsg)
00281 {
00282 if( this->intrinsic_matrix == NULL )
00283 this->intrinsic_matrix = cvCreateMat(3,3,CV_32FC1);
00284
00285 for(int i = 0; i < 3; ++i)
00286 for(int j = 0; j < 3; ++j)
00287 this->intrinsic_matrix->data.fl[3*i+j] = camInfoMsg.P[4*i+j];
00288
00289 try {
00290 if (imagemsg.encoding == "32FC1") {
00291 cv_bridge::CvImagePtr float_capture
00292 = cv_bridge::toCvCopy(imagemsg,
00293 sensor_msgs::image_encodings::TYPE_32FC1);
00294 cv::Mat float_image = float_capture->image;
00295 cv::Mat mono_image;
00296 float_image.convertTo(mono_image, CV_8UC1);
00297 capture.reset(new cv_bridge::CvImage());
00298 capture->image = mono_image;
00299 }
00300 else {
00301 capture = cv_bridge::toCvCopy(imagemsg, sensor_msgs::image_encodings::MONO8);
00302 }
00303 } catch (cv_bridge::Exception &e) {
00304 ROS_ERROR("failed to get image %s", e.what());
00305 return false;
00306 }
00307
00308 IplImage imggray = capture->image;
00309 IplImage *pimggray = &imggray;
00310 if( display ) {
00311
00312 if( frame != NULL && (frame->width != (int)imagemsg.width || frame->height != (int)imagemsg.height) ) {
00313 cvReleaseImage(&frame);
00314 frame = NULL;
00315 }
00316 imggray = capture->image;
00317 pimggray = &imggray;
00318
00319 if( frame == NULL )
00320 frame = cvCreateImage(cvSize(imagemsg.width,imagemsg.height),IPL_DEPTH_8U, 3);
00321
00322 cvCvtColor(pimggray,frame,CV_GRAY2RGB);
00323 }
00324
00325 vector<posedetection_msgs::Object6DPose> vobjects;
00326
00327 #pragma omp parallel for schedule(dynamic,1)
00328 for(int i = 0; i < (int)vcheckers.size(); ++i) {
00329 CHECKERBOARD& cb = vcheckers[i];
00330 int ncorners, board=0;
00331 posedetection_msgs::Object6DPose objpose;
00332
00333
00334 while((maxboard==-1)?1:((++board)<=maxboard)) {
00335 cb.corners.resize(200);
00336 int allfound = cvFindChessboardCorners( pimggray, cb.griddims, &cb.corners[0], &ncorners,
00337 CV_CALIB_CB_ADAPTIVE_THRESH );
00338 cb.corners.resize(ncorners);
00339
00340
00341
00342
00343 if(!allfound || ncorners != (int)cb.grid3d.size())
00344 break;
00345
00346
00347 const int borderthresh = 30;
00348
00349 for(int j = 0; j < ncorners; ++j) {
00350 int x = cb.corners[j].x;
00351 int y = cb.corners[j].y;
00352 if( x < borderthresh || x > pimggray->width-borderthresh ||
00353 y < borderthresh || y > pimggray->height-borderthresh )
00354 {
00355 allfound = 0;
00356 break;
00357 }
00358 }
00359
00360
00361 CvPoint upperleft, lowerright;
00362 upperleft.x = lowerright.x = cb.corners[0].x;
00363 upperleft.y = lowerright.y = cb.corners[0].y;
00364 for(int j = 1; j < (int)cb.corners.size(); ++j) {
00365 if( upperleft.x > cb.corners[j].x ) upperleft.x = cb.corners[j].x;
00366 if( upperleft.y > cb.corners[j].y ) upperleft.y = cb.corners[j].y;
00367 if( lowerright.x < cb.corners[j].x ) lowerright.x = cb.corners[j].x;
00368 if( lowerright.y < cb.corners[j].y ) lowerright.y = cb.corners[j].y;
00369 }
00370
00371 float step_size =
00372 (double)( ((upperleft.x - lowerright.x) * (upperleft.x - lowerright.x)) +
00373 ((upperleft.y - lowerright.y) * (upperleft.y - lowerright.y)) )
00374 /
00375 ( ((cb.griddims.width - 1) * (cb.griddims.width - 1)) +
00376 ((cb.griddims.height - 1) * (cb.griddims.height - 1)) );
00377 #if 0
00378 ROS_INFO("(%d %d) - (%d %d) -> %f ",
00379 upperleft.x, upperleft.y, lowerright.x, lowerright.y, sqrt(step_size));
00380 #endif
00381 int size = (int)(0.5*sqrt(step_size) + 0.5);
00382
00383 if( allfound ) {
00384 cvFindCornerSubPix(pimggray, &cb.corners[0], cb.corners.size(), cvSize(size,size), cvSize(-1,-1),
00385 cvTermCriteria(CV_TERMCRIT_ITER, 50, 1e-2));
00386 objpose.pose = FindTransformation(cb.corners, cb.grid3d, cb.tlocaltrans);
00387 }
00388
00389 #pragma omp critical
00390 {
00391 if( allfound ) {
00392 vobjects.push_back(objpose);
00393 vobjects.back().type = vstrtypes[i];
00394 }
00395
00396 cvRectangle(pimggray, upperleft, lowerright, CV_RGB(0,0,0),CV_FILLED);
00397 }
00398 }
00399
00400
00401 }
00402
00403 objdetmsg.objects = vobjects;
00404 objdetmsg.header.stamp = imagemsg.header.stamp;
00405 if( frame_id.size() > 0 )
00406 objdetmsg.header.frame_id = frame_id;
00407 else
00408 objdetmsg.header.frame_id = imagemsg.header.frame_id;
00409
00410 if( verbose > 0 )
00411 ROS_INFO("checkerboard: image: %ux%u (size=%u), num: %u, total: %.3fs",imagemsg.width,imagemsg.height,
00412 (unsigned int)imagemsg.data.size(), (unsigned int)objdetmsg.objects.size(),
00413 (float)(ros::Time::now()-lasttime).toSec());
00414 lasttime = ros::Time::now();
00415
00416 if( display ) {
00417
00418 for(size_t i = 0; i < vobjects.size(); ++i) {
00419 int itype = maptypes[vobjects[i].type];
00420 CHECKERBOARD& cb = vcheckers[itype];
00421 Transform tglobal;
00422 tglobal.trans = Vector(vobjects[i].pose.position.x,vobjects[i].pose.position.y,vobjects[i].pose.position.z);
00423 tglobal.rot = Vector(vobjects[i].pose.orientation.w,vobjects[i].pose.orientation.x,vobjects[i].pose.orientation.y, vobjects[i].pose.orientation.z);
00424 Transform tlocal = tglobal * cb.tlocaltrans.inverse();
00425
00426 CvPoint X[4];
00427
00428 Vector vaxes[4];
00429 vaxes[0] = Vector(0,0,0);
00430 vaxes[1] = Vector(0.05f,0,0);
00431 vaxes[2] = Vector(0,0.05f,0);
00432 vaxes[3] = Vector(0,0,0.05f);
00433
00434 for(int i = 0; i < 4; ++i) {
00435 Vector p = tglobal*vaxes[i];
00436 dReal fx = p.x*camInfoMsg.P[0] + p.y*camInfoMsg.P[1] + p.z*camInfoMsg.P[2] + camInfoMsg.P[3];
00437 dReal fy = p.x*camInfoMsg.P[4] + p.y*camInfoMsg.P[5] + p.z*camInfoMsg.P[6] + camInfoMsg.P[7];
00438 dReal fz = p.x*camInfoMsg.P[8] + p.y*camInfoMsg.P[9] + p.z*camInfoMsg.P[10] + camInfoMsg.P[11];
00439 X[i].x = (int)(fx/fz);
00440 X[i].y = (int)(fy/fz);
00441 }
00442
00443
00444 CvScalar col0 = CV_RGB(255,0,(64*itype)%256);
00445 CvScalar col1 = CV_RGB(0,255,(64*itype)%256);
00446 CvScalar col2 = CV_RGB((64*itype)%256,(64*itype)%256,255);
00447 cvLine(frame, X[0], X[1], col0, 1);
00448 cvLine(frame, X[0], X[2], col1, 1);
00449 cvLine(frame, X[0], X[3], col2, 1);
00450
00451
00452 for(size_t i = 0; i < cb.grid3d.size(); ++i) {
00453 Vector p = tlocal * cb.grid3d[i];
00454 dReal fx = p.x*camInfoMsg.P[0] + p.y*camInfoMsg.P[1] + p.z*camInfoMsg.P[2] + camInfoMsg.P[3];
00455 dReal fy = p.x*camInfoMsg.P[4] + p.y*camInfoMsg.P[5] + p.z*camInfoMsg.P[6] + camInfoMsg.P[7];
00456 dReal fz = p.x*camInfoMsg.P[8] + p.y*camInfoMsg.P[9] + p.z*camInfoMsg.P[10] + camInfoMsg.P[11];
00457 int x = (int)(fx/fz);
00458 int y = (int)(fy/fz);
00459 cvCircle(frame, cvPoint(x,y), 6, CV_RGB(0,0,0), 2);
00460 cvCircle(frame, cvPoint(x,y), 2, CV_RGB(0,0,0), 2);
00461 cvCircle(frame, cvPoint(x,y), 4, CV_RGB(128,128,64*itype), 3);
00462 }
00463
00464 cvCircle(frame, X[0], 3, CV_RGB(255,255,128), 3);
00465
00466 geometry_msgs::PointStamped point_msg;
00467 point_msg.header = imagemsg.header;
00468 point_msg.point.x = X[0].x;
00469 point_msg.point.y = X[0].y;
00470 point_msg.point.z = vobjects[i].pose.position.z;
00471 _pubCornerPoint.publish(point_msg);
00472 }
00473
00474 cvShowImage("Checkerboard Detector",frame);
00475 }
00476
00477 return true;
00478 }
00479
00480
00482
00483 geometry_msgs::Pose FindTransformation(const vector<CvPoint2D32f> &imgpts, const vector<Vector> &objpts, const Transform& tlocal)
00484 {
00485 CvMat *objpoints = cvCreateMat(3,objpts.size(),CV_32FC1);
00486 for(size_t i=0; i<objpts.size(); ++i) {
00487 cvSetReal2D(objpoints, 0,i, objpts[i].x);
00488 cvSetReal2D(objpoints, 1,i, objpts[i].y);
00489 cvSetReal2D(objpoints, 2,i, objpts[i].z);
00490 }
00491
00492 geometry_msgs::Pose pose;
00493 Transform tchecker;
00494 assert(sizeof(tchecker.trans.x)==sizeof(float));
00495 float fR3[3];
00496 CvMat R3, T3;
00497 assert(sizeof(pose.position.x) == sizeof(double));
00498 cvInitMatHeader(&R3, 3, 1, CV_32FC1, fR3);
00499 cvInitMatHeader(&T3, 3, 1, CV_32FC1, &tchecker.trans.x);
00500
00501 float kc[4] = {0};
00502 CvMat kcmat;
00503 cvInitMatHeader(&kcmat,1,4,CV_32FC1,kc);
00504
00505 CvMat img_points;
00506 cvInitMatHeader(&img_points, 1,imgpts.size(), CV_32FC2, const_cast<CvPoint2D32f*>(&imgpts[0]));
00507
00508 cvFindExtrinsicCameraParams2(objpoints, &img_points, this->intrinsic_matrix, &kcmat, &R3, &T3);
00509 cvReleaseMat(&objpoints);
00510
00511 double fang = sqrt(fR3[0]*fR3[0] + fR3[1]*fR3[1] + fR3[2]*fR3[2]);
00512 if( fang >= 1e-6 ) {
00513 double fmult = sin(fang/2)/fang;
00514 tchecker.rot = Vector(cos(fang/2),fR3[0]*fmult, fR3[1]*fmult, fR3[2]*fmult);
00515 }
00516
00517 Transform tglobal = tchecker*tlocal;
00518 pose.position.x = tglobal.trans.x;
00519 pose.position.y = tglobal.trans.y;
00520 pose.position.z = tglobal.trans.z;
00521 pose.orientation.x = tglobal.rot.y;
00522 pose.orientation.y = tglobal.rot.z;
00523 pose.orientation.z = tglobal.rot.w;
00524 pose.orientation.w = tglobal.rot.x;
00525 return pose;
00526 }
00527 };
00528
00530
00531 int main(int argc, char **argv)
00532 {
00533 ros::init(argc,argv,"checkerboard_detector");
00534 if( !ros::master::check() )
00535 return 1;
00536
00537 CheckerboardDetector cd;
00538 ros::spin();
00539
00540 return 0;
00541 }