00001 #include <cstdlib>
00002 #include <fstream>
00003
00004 #include <boost/filesystem.hpp>
00005 #include <boost/filesystem/fstream.hpp>
00006 #include <boost/format.hpp>
00007 #include <boost/optional.hpp>
00008 #include <boost/version.hpp>
00009
00010 #include <ros/ros.h>
00011 #include <ros/param.h>
00012 #include <dynamic_reconfigure/server.h>
00013 #include <image_proc/advertisement_checker.h>
00014 #include <image_transport/image_transport.h>
00015 #include <visp_tracker/Init.h>
00016 #include <visp_tracker/MovingEdgeConfig.h>
00017
00018 #include <visp/vpMe.h>
00019 #include <visp/vpPixelMeterConversion.h>
00020 #include <visp/vpPose.h>
00021
00022 #define protected public
00023 #include <visp/vpMbEdgeTracker.h>
00024 #undef protected
00025
00026 #include <visp/vpDisplayX.h>
00027
00028 #include "conversion.hh"
00029 #include "callbacks.hh"
00030 #include "file.hh"
00031 #include "names.hh"
00032
00033 #include "tracker-client.hh"
00034
00035
00036 namespace visp_tracker
00037 {
00038 TrackerClient::TrackerClient(unsigned queueSize)
00039 : queueSize_(queueSize),
00040 nodeHandle_(),
00041 imageTransport_(nodeHandle_),
00042 image_(),
00043 modelPath_(),
00044 modelName_(),
00045 cameraPrefix_(),
00046 rectifiedImageTopic_(),
00047 cameraInfoTopic_(),
00048 vrmlPath_(),
00049 initPath_(),
00050 cameraSubscriber_(),
00051 reconfigureSrv_(),
00052 movingEdge_(),
00053 cameraParameters_(),
00054 tracker_(),
00055 startFromSavedPose_(),
00056 checkInputs_()
00057 {
00058 tracker_.resetTracker();
00059
00060
00061 ros::param::param<std::string>("~model_path", modelPath_,
00062 visp_tracker::default_model_path);
00063 ros::param::param<std::string>("~model_name", modelName_, "");
00064
00065 ros::param::param<bool>
00066 ("~start_from_saved_pose", startFromSavedPose_, false);
00067
00068 ros::param::param<bool>
00069 ("~confirm_init", confirmInit_, true);
00070
00071 if (modelName_.empty ())
00072 throw std::runtime_error
00073 ("empty model\n"
00074 "Relaunch the client while setting the model_name parameter, i.e.\n"
00075 "$ rosrun visp_tracker client _model_name:=my-model"
00076 );
00077
00078
00079
00080 ros::Rate rate (1);
00081 while (cameraPrefix_.empty ())
00082 {
00083 ros::param::get ("camera_prefix", cameraPrefix_);
00084 if (!ros::param::has ("camera_prefix"))
00085 {
00086 ROS_WARN
00087 ("the camera_prefix parameter does not exist.\n"
00088 "This may mean that:\n"
00089 "- the tracker is not launched,\n"
00090 "- the tracker and viewer are not running in the same namespace."
00091 );
00092 }
00093 else if (cameraPrefix_.empty ())
00094 {
00095 ROS_INFO
00096 ("tracker is not yet initialized, waiting...\n"
00097 "You may want to launch the client to initialize the tracker.");
00098 }
00099 if (!ros::ok ())
00100 return;
00101 rate.sleep ();
00102 }
00103
00104 rectifiedImageTopic_ =
00105 ros::names::resolve(cameraPrefix_ + "/image_rect");
00106 cameraInfoTopic_ =
00107 ros::names::resolve(cameraPrefix_ + "/camera_info");
00108
00109
00110 checkInputs();
00111
00112
00113 reconfigureCallback_t f =
00114 boost::bind(&reconfigureCallback, boost::ref(tracker_),
00115 boost::ref(image_), boost::ref(movingEdge_), _1, _2);
00116 reconfigureSrv_.setCallback(f);
00117
00118
00119 cameraSubscriber_ = imageTransport_.subscribeCamera
00120 (rectifiedImageTopic_, queueSize_,
00121 bindImageCallback(image_, header_, info_));
00122
00123
00124 vrmlPath_ = getModelFileFromModelName(modelName_, modelPath_);
00125 initPath_ = getInitFileFromModelName(modelName_, modelPath_);
00126
00127 ROS_INFO_STREAM("VRML file: " << vrmlPath_);
00128 ROS_INFO_STREAM("Init file: " << initPath_);
00129
00130
00131 if (!boost::filesystem::is_regular_file(vrmlPath_))
00132 {
00133 boost::format fmt("VRML model %1% is not a regular file");
00134 fmt % vrmlPath_;
00135 throw std::runtime_error(fmt.str());
00136 }
00137 if (!boost::filesystem::is_regular_file(initPath_))
00138 {
00139 boost::format fmt("Initialization file %1% is not a regular file");
00140 fmt % initPath_;
00141 throw std::runtime_error(fmt.str());
00142 }
00143
00144
00145 loadModel();
00146
00147
00148 waitForImage();
00149 if (!ros::ok())
00150 return;
00151 if (!image_.getWidth() || !image_.getHeight())
00152 throw std::runtime_error("failed to retrieve image");
00153
00154
00155
00156 initializeVpCameraFromCameraInfo(cameraParameters_, info_);
00157 tracker_.setCameraParameters(cameraParameters_);
00158 tracker_.setDisplayMovingEdges(true);
00159
00160
00161 movingEdge_.initMask();
00162 tracker_.setMovingEdge(movingEdge_);
00163
00164
00165 ROS_INFO_STREAM(cameraParameters_);
00166 movingEdge_.print();
00167 }
00168
00169 void
00170 TrackerClient::checkInputs()
00171 {
00172 ros::V_string topics;
00173 topics.push_back(rectifiedImageTopic_);
00174 topics.push_back(cameraInfoTopic_);
00175 checkInputs_.start(topics, 60.0);
00176 }
00177
00178 void
00179 TrackerClient::spin()
00180 {
00181 boost::format fmtWindowTitle ("ViSP MBT tracker initialization - [ns: %s]");
00182 fmtWindowTitle % ros::this_node::getNamespace ();
00183
00184 vpDisplayX d(image_, image_.getWidth(), image_.getHeight(),
00185 fmtWindowTitle.str().c_str());
00186
00187 ros::Rate loop_rate_tracking(200);
00188 bool ok = false;
00189 vpHomogeneousMatrix cMo;
00190 vpImagePoint point (10, 10);
00191 while (!ok && ros::ok())
00192 {
00193 try
00194 {
00195
00196 vpDisplay::display(image_);
00197 vpDisplay::flush(image_);
00198 if (!startFromSavedPose_)
00199 initClick();
00200 else
00201 {
00202 cMo = loadInitialPose();
00203 startFromSavedPose_ = false;
00204 tracker_.init(image_, cMo);
00205 }
00206 tracker_.getPose(cMo);
00207
00208 ROS_INFO_STREAM("initial pose [tx,ty,tz,tux,tuy,tuz]:\n"
00209 << vpPoseVector(cMo));
00210
00211
00212 if (confirmInit_)
00213 {
00214 vpImagePoint ip;
00215 vpMouseButton::vpMouseButtonType button =
00216 vpMouseButton::button1;
00217 do
00218 {
00219 vpDisplay::display(image_);
00220 tracker_.track(image_);
00221 tracker_.display(image_, cMo, cameraParameters_,
00222 vpColor::red, 2);
00223 vpDisplay::displayCharString
00224 (image_, point, "tracking, click to initialize tracker",
00225 vpColor::red);
00226 vpDisplay::flush(image_);
00227 tracker_.getPose(cMo);
00228
00229 ros::spinOnce();
00230 loop_rate_tracking.sleep();
00231 if (!ros::ok())
00232 return;
00233 }
00234 while(!vpDisplay::getClick(image_, ip, button, false));
00235 ok = true;
00236 }
00237 else
00238 ok = true;
00239 }
00240 catch(const std::string& str)
00241 {
00242 ROS_ERROR_STREAM("failed to initialize: "
00243 << str << ", retrying...");
00244 }
00245 catch(...)
00246 {
00247 ROS_ERROR("failed to initialize, retrying...");
00248 }
00249 }
00250
00251 ROS_INFO_STREAM("Initialization done, sending initial cMo:\n" << cMo);
00252 try
00253 {
00254 sendcMo(cMo);
00255 }
00256 catch(std::exception& e)
00257 {
00258 ROS_ERROR_STREAM("failed to send cMo: " << e.what ());
00259 }
00260 catch(...)
00261 {
00262 ROS_ERROR("unknown error happened while sending the cMo, retrying...");
00263 }
00264 }
00265
00266 void
00267 TrackerClient::sendcMo(const vpHomogeneousMatrix& cMo)
00268 {
00269 ros::ServiceClient client =
00270 nodeHandle_.serviceClient<visp_tracker::Init>(visp_tracker::init_service);
00271 visp_tracker::Init srv;
00272
00273
00274 boost::filesystem::path modelPath =
00275 getModelFileFromModelName (modelName_, modelPath_);
00276
00277 boost::filesystem::ifstream modelStream(modelPath);
00278 std::string modelDescription
00279 ((std::istreambuf_iterator<char>(modelStream)),
00280 std::istreambuf_iterator<char>());
00281
00282 ros::param::set (model_description_param, modelDescription);
00283
00284 vpHomogeneousMatrixToTransform(srv.request.initial_cMo, cMo);
00285
00286 convertVpMeToInitRequest(movingEdge_, tracker_, srv);
00287
00288 ros::Rate rate (1);
00289 while (!client.waitForExistence ())
00290 {
00291 ROS_INFO
00292 ("Waiting for the initialization service to become available.");
00293 rate.sleep ();
00294 }
00295
00296 if (client.call(srv))
00297 {
00298 if (srv.response.initialization_succeed)
00299 ROS_INFO("Tracker initialized with success.");
00300 else
00301 throw std::runtime_error("failed to initialize tracker.");
00302 }
00303 else
00304 throw std::runtime_error("failed to call service init");
00305 }
00306
00307 void
00308 TrackerClient::loadModel()
00309 {
00310 try
00311 {
00312 ROS_DEBUG_STREAM("Trying to load the model "
00313 << vrmlPath_.external_file_string());
00314 tracker_.loadModel(vrmlPath_.external_file_string().c_str());
00315 ROS_INFO("VRML model has been successfully loaded.");
00316
00317 ROS_DEBUG_STREAM("Nb hidden faces: "
00318 << tracker_.faces.getPolygon().size());
00319
00320 std::list<vpMbtDistanceLine *> linesList;
00321 tracker_.getLline(linesList);
00322 ROS_DEBUG_STREAM("Nb line: " << linesList.size());
00323 ROS_DEBUG_STREAM("nline: " << tracker_.nline);
00324 ROS_DEBUG_STREAM("Visible faces: " << tracker_.nbvisiblepolygone);
00325 }
00326 catch(...)
00327 {
00328 boost::format fmt("Failed to load the model %1%");
00329 fmt % vrmlPath_;
00330 throw std::runtime_error(fmt.str());
00331 }
00332 }
00333
00334 vpHomogeneousMatrix
00335 TrackerClient::loadInitialPose()
00336 {
00337 vpHomogeneousMatrix cMo;
00338 cMo.eye();
00339
00340 boost::filesystem::path initialPose =
00341 getInitialPoseFileFromModelName(modelName_, modelPath_);
00342 boost::filesystem::ifstream file(initialPose);
00343 if (!file.good())
00344 {
00345 ROS_WARN_STREAM("failed to load initial pose: " << initialPose << "\n"
00346 << "using identity as initial pose");
00347 return cMo;
00348 }
00349
00350 vpPoseVector pose;
00351 for (unsigned i = 0; i < 6; ++i)
00352 if (file.good())
00353 file >> pose[i];
00354 else
00355 {
00356 ROS_WARN("failed to parse initial pose file");
00357 return cMo;
00358 }
00359 cMo.buildFrom(pose);
00360 return cMo;
00361 }
00362
00363 void
00364 TrackerClient::saveInitialPose(const vpHomogeneousMatrix& cMo)
00365 {
00366 boost::filesystem::path initialPose =
00367 getInitialPoseFileFromModelName(modelName_, modelPath_);
00368 boost::filesystem::ofstream file(initialPose);
00369 if (!file.good())
00370 {
00371 ROS_WARN_STREAM("failed to save initial pose: " << initialPose);
00372 return;
00373 }
00374 vpPoseVector pose;
00375 pose.buildFrom(cMo);
00376 file << pose;
00377 }
00378
00379 TrackerClient::points_t
00380 TrackerClient::loadInitializationPoints()
00381 {
00382 points_t points;
00383
00384 boost::filesystem::path init =
00385 getInitFileFromModelName(modelName_, modelPath_);
00386 boost::filesystem::ifstream file(init);
00387 if (!file.good())
00388 {
00389 boost::format fmt("failed to load initialization points: %1");
00390 fmt % init;
00391 throw std::runtime_error(fmt.str());
00392 }
00393
00394 unsigned npoints = 0;
00395 file >> npoints;
00396 if (!file.good())
00397 throw std::runtime_error("failed to read initialization file");
00398
00399 double X = 0., Y = 0., Z = 0.;
00400 vpPoint point;
00401 for (unsigned i = 0; i < npoints; ++i)
00402 {
00403 if (!file.good())
00404 throw std::runtime_error("failed to read initialization file");
00405 file >> X >> Y >> Z;
00406 point.setWorldCoordinates(X,Y,Z);
00407 points.push_back(point);
00408 }
00409 return points;
00410 }
00411
00412 void
00413 TrackerClient::initPoint(unsigned& i,
00414 points_t& points,
00415 imagePoints_t& imagePoints,
00416 ros::Rate& rate,
00417 vpPose& pose)
00418 {
00419 vpImagePoint ip;
00420 double x = 0., y = 0.;
00421 boost::format fmt("click on point %u/%u");
00422 fmt % (i + 1) % points.size();
00423
00424
00425 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
00426 do
00427 {
00428 vpDisplay::display(image_);
00429 vpDisplay::displayCharString
00430 (image_, 15, 10,
00431 fmt.str().c_str(),
00432 vpColor::red);
00433
00434 for (unsigned j = 0; j < imagePoints.size(); ++j)
00435 vpDisplay::displayCross(image_, imagePoints[j], 5, vpColor::green);
00436
00437 vpDisplay::flush(image_);
00438 ros::spinOnce();
00439 rate.sleep();
00440 if (!ros::ok())
00441 return;
00442 }
00443 while(!vpDisplay::getClick(image_, ip, button, false));
00444
00445 imagePoints.push_back(ip);
00446 vpPixelMeterConversion::convertPoint(cameraParameters_, ip, x, y);
00447 points[i].set_x(x);
00448 points[i].set_y(y);
00449 pose.addPoint(points[i]);
00450 }
00451
00452 void
00453 TrackerClient::initClick()
00454 {
00455 ros::Rate loop_rate(200);
00456 vpHomogeneousMatrix cMo;
00457 vpImagePoint point (10, 10);
00458
00459 cMo = loadInitialPose();
00460
00461
00462 vpImagePoint ip;
00463 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
00464 do
00465 {
00466 vpDisplay::display(image_);
00467 tracker_.display(image_, cMo, cameraParameters_, vpColor::green);
00468 vpDisplay::displayFrame(image_, cMo, cameraParameters_,
00469 0.05, vpColor::green);
00470 vpDisplay::displayCharString
00471 (image_, 15, 10,
00472 "left click to validate, right click to modify initial pose",
00473 vpColor::red);
00474 vpDisplay::flush(image_);
00475 ros::spinOnce();
00476 loop_rate.sleep();
00477 if (!ros::ok())
00478 return;
00479 }
00480 while(!vpDisplay::getClick(image_, ip, button, false));
00481
00482 if(button == vpMouseButton::button1)
00483 {
00484 tracker_.init(image_, cMo);
00485 return;
00486 }
00487
00488 points_t points = loadInitializationPoints();
00489 imagePoints_t imagePoints;
00490
00491 vpPose pose;
00492 pose.clearPoint();
00493 bool done = false;
00494 while (!done)
00495 {
00496
00497 for(unsigned i = 0; i < points.size(); ++i)
00498 {
00499 initPoint(i, points, imagePoints, loop_rate, pose);
00500 if (!ros::ok())
00501 return;
00502 }
00503
00504
00505 vpHomogeneousMatrix cMo1, cMo2;
00506 pose.computePose(vpPose::LAGRANGE, cMo1);
00507 double d1 = pose.computeResidual(cMo1);
00508 pose.computePose(vpPose::DEMENTHON, cMo2);
00509 double d2 = pose.computeResidual(cMo2);
00510
00511 if(d1 < d2)
00512 cMo = cMo1;
00513 else
00514 cMo = cMo2;
00515 pose.computePose(vpPose::VIRTUAL_VS, cMo);
00516
00517
00518 do
00519 {
00520 vpDisplay::display(image_);
00521 tracker_.display(image_, cMo, cameraParameters_, vpColor::green);
00522 vpDisplay::displayCharString
00523 (image_, 15, 10,
00524 "left click to validate, right click to re initialize object",
00525 vpColor::red);
00526 vpDisplay::flush(image_);
00527 ros::spinOnce();
00528 loop_rate.sleep();
00529 if (!ros::ok())
00530 return;
00531 }
00532 while(!vpDisplay::getClick(image_, ip, button, false));
00533
00534 if(button != vpMouseButton::button1)
00535 {
00536 pose.clearPoint();
00537 imagePoints.clear();
00538 }
00539 else
00540 done = true;
00541 }
00542 tracker_.init(image_, cMo);
00543 saveInitialPose(cMo);
00544 }
00545
00546 void
00547 TrackerClient::waitForImage()
00548 {
00549 ros::Rate loop_rate(10);
00550 while (ros::ok()
00551 && (!image_.getWidth() || !image_.getHeight()))
00552 {
00553 ROS_INFO_THROTTLE(1, "waiting for a rectified image...");
00554 ros::spinOnce();
00555 loop_rate.sleep();
00556 }
00557 }
00558 }