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