00001 #include <cstdlib>
00002 #include <fstream>
00003 #include <sstream>
00004 #include <stdexcept>
00005
00006 #include <boost/filesystem.hpp>
00007 #include <boost/filesystem/fstream.hpp>
00008 #include <boost/format.hpp>
00009 #include <boost/optional.hpp>
00010 #include <boost/version.hpp>
00011
00012 #include <ros/ros.h>
00013 #include <ros/param.h>
00014 #include <dynamic_reconfigure/server.h>
00015 #include <geometry_msgs/PoseWithCovarianceStamped.h>
00016 #include <image_proc/advertisement_checker.h>
00017 #include <image_transport/image_transport.h>
00018 #include <visp_tracker/Init.h>
00019 #include <visp_tracker/ModelBasedSettingsConfig.h>
00020
00021 #include <visp/vpMe.h>
00022 #include <visp/vpPixelMeterConversion.h>
00023 #include <visp/vpPose.h>
00024
00025 #define protected public
00026 #include <visp/vpMbEdgeTracker.h>
00027 #include <visp/vpMbKltTracker.h>
00028 #include <visp/vpMbEdgeKltTracker.h>
00029 #undef protected
00030
00031 #include <visp/vpDisplayX.h>
00032
00033 #include "conversion.hh"
00034 #include "callbacks.hh"
00035 #include "file.hh"
00036 #include "names.hh"
00037
00038 #include "tracker-client.hh"
00039
00040
00041 namespace visp_tracker
00042 {
00043 TrackerClient::TrackerClient(ros::NodeHandle& nh,
00044 ros::NodeHandle& privateNh,
00045 volatile bool& exiting,
00046 unsigned queueSize)
00047 : exiting_ (exiting),
00048 queueSize_(queueSize),
00049 nodeHandle_(nh),
00050 nodeHandlePrivate_(privateNh),
00051 imageTransport_(nodeHandle_),
00052 image_(),
00053 modelPath_(),
00054 modelName_(),
00055 cameraPrefix_(),
00056 rectifiedImageTopic_(),
00057 cameraInfoTopic_(),
00058 vrmlPath_(),
00059 initPath_(),
00060 cameraSubscriber_(),
00061 mutex_(),
00062 reconfigureSrv_(mutex_, nodeHandlePrivate_),
00063 movingEdge_(),
00064 kltTracker_(),
00065 cameraParameters_(),
00066 tracker_(),
00067 startFromSavedPose_(),
00068 checkInputs_(),
00069 resourceRetriever_()
00070 {
00071
00072 nodeHandlePrivate_.param<std::string>("model_path", modelPath_,
00073 visp_tracker::default_model_path);
00074 nodeHandlePrivate_.param<std::string>("model_name", modelName_, "");
00075
00076 nodeHandlePrivate_.param<bool>
00077 ("start_from_saved_pose", startFromSavedPose_, false);
00078
00079 nodeHandlePrivate_.param<bool>
00080 ("confirm_init", confirmInit_, true);
00081
00082 nodeHandlePrivate_.param<std::string>("tracker_type", trackerType_, "mbt");
00083 if(trackerType_=="mbt")
00084 tracker_ = new vpMbEdgeTracker();
00085 else if(trackerType_=="klt")
00086 tracker_ = new vpMbKltTracker();
00087 else
00088 tracker_ = new vpMbEdgeKltTracker();
00089
00090
00091
00092 if (modelName_.empty ())
00093 throw std::runtime_error
00094 ("empty model\n"
00095 "Relaunch the client while setting the model_name parameter, i.e.\n"
00096 "$ rosrun visp_tracker client _model_name:=my-model"
00097 );
00098
00099
00100
00101 ros::Rate rate (1);
00102 while (cameraPrefix_.empty ())
00103 {
00104 if (!nodeHandle_.getParam ("camera_prefix", cameraPrefix_))
00105 {
00106 ROS_WARN
00107 ("the camera_prefix parameter does not exist.\n"
00108 "This may mean that:\n"
00109 "- the tracker is not launched,\n"
00110 "- the tracker and viewer are not running in the same namespace."
00111 );
00112 }
00113 else if (cameraPrefix_.empty ())
00114 {
00115 ROS_INFO
00116 ("tracker is not yet initialized, waiting...\n"
00117 "You may want to launch the client to initialize the tracker.");
00118 }
00119 if (this->exiting())
00120 return;
00121 rate.sleep ();
00122 }
00123
00124 rectifiedImageTopic_ =
00125 ros::names::resolve(cameraPrefix_ + "/image_rect");
00126 cameraInfoTopic_ =
00127 ros::names::resolve(cameraPrefix_ + "/camera_info");
00128
00129
00130 checkInputs();
00131
00132
00133 if(trackerType_!="klt"){
00134 vpMbEdgeTracker* t = dynamic_cast<vpMbEdgeTracker*>(tracker_);
00135 t->setMovingEdge(movingEdge_);
00136 }
00137
00138 if(trackerType_!="mbt"){
00139 vpMbKltTracker* t = dynamic_cast<vpMbKltTracker*>(tracker_);
00140 t->setKltOpencv(kltTracker_);
00141 }
00142
00143
00144 reconfigureSrv_t::CallbackType f =
00145 boost::bind(&reconfigureCallback, boost::ref(tracker_),
00146 boost::ref(image_), boost::ref(movingEdge_), boost::ref(kltTracker_),
00147 boost::ref(trackerType_), boost::ref(mutex_), _1, _2);
00148 reconfigureSrv_.setCallback(f);
00149
00150
00151 cameraSubscriber_ = imageTransport_.subscribeCamera
00152 (rectifiedImageTopic_, queueSize_,
00153 bindImageCallback(image_, header_, info_));
00154
00155
00156 vrmlPath_ = getModelFileFromModelName(modelName_, modelPath_);
00157 initPath_ = getInitFileFromModelName(modelName_, modelPath_);
00158
00159 ROS_INFO_STREAM("VRML file: " << vrmlPath_);
00160 ROS_INFO_STREAM("Init file: " << initPath_);
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177 loadModel();
00178
00179
00180 waitForImage();
00181 if (this->exiting())
00182 return;
00183 if (!image_.getWidth() || !image_.getHeight())
00184 throw std::runtime_error("failed to retrieve image");
00185
00186
00187
00188 initializeVpCameraFromCameraInfo(cameraParameters_, info_);
00189 tracker_->setCameraParameters(cameraParameters_);
00190 tracker_->setDisplayFeatures(true);
00191
00192
00193 movingEdge_.initMask();
00194 if(trackerType_!="klt"){
00195 vpMbEdgeTracker* t = dynamic_cast<vpMbEdgeTracker*>(tracker_);
00196 t->setMovingEdge(movingEdge_);
00197 }
00198
00199 if(trackerType_!="mbt"){
00200 vpMbKltTracker* t = dynamic_cast<vpMbKltTracker*>(tracker_);
00201 t->setKltOpencv(kltTracker_);
00202 }
00203
00204
00205 ROS_INFO_STREAM(cameraParameters_);
00206 movingEdge_.print();
00207 }
00208
00209 void
00210 TrackerClient::checkInputs()
00211 {
00212 ros::V_string topics;
00213 topics.push_back(rectifiedImageTopic_);
00214 topics.push_back(cameraInfoTopic_);
00215 checkInputs_.start(topics, 60.0);
00216 }
00217
00218 void
00219 TrackerClient::spin()
00220 {
00221 boost::format fmtWindowTitle ("ViSP MBT tracker initialization - [ns: %s]");
00222 fmtWindowTitle % ros::this_node::getNamespace ();
00223
00224 vpDisplayX d(image_, image_.getWidth(), image_.getHeight(),
00225 fmtWindowTitle.str().c_str());
00226
00227 ros::Rate loop_rate_tracking(200);
00228 bool ok = false;
00229 vpHomogeneousMatrix cMo;
00230 vpImagePoint point (10, 10);
00231 while (!ok && !exiting())
00232 {
00233 try
00234 {
00235
00236 vpDisplay::display(image_);
00237 vpDisplay::flush(image_);
00238 if (!startFromSavedPose_)
00239 init();
00240 else
00241 {
00242 cMo = loadInitialPose();
00243 startFromSavedPose_ = false;
00244 tracker_->initFromPose(image_, cMo);
00245 }
00246 tracker_->getPose(cMo);
00247
00248 ROS_INFO_STREAM("initial pose [tx,ty,tz,tux,tuy,tuz]:\n"
00249 << vpPoseVector(cMo));
00250
00251
00252 if (confirmInit_)
00253 {
00254 vpImagePoint ip;
00255 vpMouseButton::vpMouseButtonType button =
00256 vpMouseButton::button1;
00257 do
00258 {
00259 vpDisplay::display(image_);
00260 tracker_->track(image_);
00261 tracker_->display(image_, cMo, cameraParameters_,
00262 vpColor::red, 2);
00263 vpDisplay::displayCharString
00264 (image_, point, "tracking, click to initialize tracker",
00265 vpColor::red);
00266 vpDisplay::flush(image_);
00267 tracker_->getPose(cMo);
00268
00269 ros::spinOnce();
00270 loop_rate_tracking.sleep();
00271 if (exiting())
00272 return;
00273 }
00274 while(!vpDisplay::getClick(image_, ip, button, false));
00275 ok = true;
00276 }
00277 else
00278 ok = true;
00279 }
00280 catch(const std::runtime_error& e)
00281 {
00282 ROS_ERROR_STREAM("failed to initialize: "
00283 << e.what() << ", retrying...");
00284 }
00285 catch(const std::string& str)
00286 {
00287 ROS_ERROR_STREAM("failed to initialize: "
00288 << str << ", retrying...");
00289 }
00290 catch(...)
00291 {
00292 ROS_ERROR("failed to initialize, retrying...");
00293 }
00294 }
00295
00296 ROS_INFO_STREAM("Initialization done, sending initial cMo:\n" << cMo);
00297 try
00298 {
00299 sendcMo(cMo);
00300 }
00301 catch(std::exception& e)
00302 {
00303 ROS_ERROR_STREAM("failed to send cMo: " << e.what ());
00304 }
00305 catch(...)
00306 {
00307 ROS_ERROR("unknown error happened while sending the cMo, retrying...");
00308 }
00309 }
00310
00311 TrackerClient::~TrackerClient()
00312 {
00313 delete tracker_;
00314 }
00315
00316 void
00317 TrackerClient::sendcMo(const vpHomogeneousMatrix& cMo)
00318 {
00319 ros::ServiceClient client =
00320 nodeHandle_.serviceClient<visp_tracker::Init>(visp_tracker::init_service);
00321 visp_tracker::Init srv;
00322
00323
00324 std::string modelDescription = fetchResource
00325 (getModelFileFromModelName (modelName_, modelPath_));
00326 nodeHandle_.setParam (model_description_param, modelDescription);
00327
00328 vpHomogeneousMatrixToTransform(srv.request.initial_cMo, cMo);
00329
00330 if(trackerType_!="klt"){
00331 convertVpMeToInitRequest(movingEdge_, tracker_, srv);
00332 }
00333
00334 if(trackerType_!="mbt"){
00335 convertVpKltOpencvToInitRequest(kltTracker_, tracker_, srv);
00336 }
00337
00338 ros::Rate rate (1);
00339 while (!client.waitForExistence ())
00340 {
00341 ROS_INFO
00342 ("Waiting for the initialization service to become available.");
00343 rate.sleep ();
00344 }
00345
00346 if (client.call(srv))
00347 {
00348 if (srv.response.initialization_succeed)
00349 ROS_INFO("Tracker initialized with success.");
00350 else
00351 throw std::runtime_error("failed to initialize tracker.");
00352 }
00353 else
00354 throw std::runtime_error("failed to call service init");
00355 }
00356
00357 void
00358 TrackerClient::loadModel()
00359 {
00360 try
00361 {
00362 ROS_DEBUG_STREAM("Trying to load the model "
00363 << vrmlPath_.native());
00364
00365 std::string modelPath;
00366 boost::filesystem::ofstream modelStream;
00367 if (!makeModelFile(modelStream,
00368 vrmlPath_.native(),
00369 modelPath))
00370 throw std::runtime_error ("failed to retrieve model");
00371
00372 tracker_->loadModel(modelPath.c_str());
00373 ROS_INFO("VRML model has been successfully loaded.");
00374
00375 if(trackerType_=="mbt"){
00376 vpMbEdgeTracker* t = dynamic_cast<vpMbEdgeTracker*>(tracker_);
00377 ROS_DEBUG_STREAM("Nb faces: "
00378 << t->getFaces().getPolygon().size());
00379 ROS_DEBUG_STREAM("Nb visible faces: " << t->getFaces().getNbVisiblePolygon());
00380
00381 std::list<vpMbtDistanceLine *> linesList;
00382 t->getLline(linesList);
00383 ROS_DEBUG_STREAM("Nb line: " << linesList.size());
00384 ROS_DEBUG_STREAM("nline: " << t->nline);
00385 }
00386 else if(trackerType_=="klt"){
00387 vpMbKltTracker* t = dynamic_cast<vpMbKltTracker*>(tracker_);
00388 ROS_DEBUG_STREAM("Nb faces: "
00389 << t->getFaces().getPolygon().size());
00390 ROS_DEBUG_STREAM("Nb visible faces: " << t->getFaces().getNbVisiblePolygon());
00391 ROS_DEBUG_STREAM("Nb KLT points: " << t->getNbKltPoints());
00392 }
00393 else {
00394 vpMbEdgeKltTracker* t = dynamic_cast<vpMbEdgeKltTracker*>(tracker_);
00395 ROS_DEBUG_STREAM("Nb hidden faces: "
00396 << t->getFaces().getPolygon().size());
00397 ROS_DEBUG_STREAM("Nb visible faces: " << t->getFaces().getNbVisiblePolygon());
00398 ROS_DEBUG_STREAM("Nb KLT points: " << t->getNbKltPoints());
00399
00400 std::list<vpMbtDistanceLine *> linesList;
00401 t->getLline(linesList);
00402 ROS_DEBUG_STREAM("Nb line: " << linesList.size());
00403 ROS_DEBUG_STREAM("nline: " << t->nline);
00404 }
00405 }
00406 catch(...)
00407 {
00408 boost::format fmt
00409 ("Failed to load the model %1%\n"
00410 "Do you use resource_retriever syntax?\n"
00411 "I.e. replace /my/model/path by file:///my/model/path");
00412 fmt % vrmlPath_;
00413 throw std::runtime_error(fmt.str());
00414 }
00415 }
00416
00417 vpHomogeneousMatrix
00418 TrackerClient::loadInitialPose()
00419 {
00420 vpHomogeneousMatrix cMo;
00421 cMo.eye();
00422
00423 std::string initialPose =
00424 getInitialPoseFileFromModelName (modelName_, modelPath_);
00425 std::string resource;
00426 try
00427 {
00428 resource = fetchResource (initialPose);
00429 }
00430 catch (...)
00431 {
00432 ROS_WARN_STREAM
00433 ("failed to retrieve initial pose: " << initialPose << "\n"
00434 << "using identity as initial pose");
00435 return cMo;
00436 }
00437 std::stringstream file;
00438 file << resource;
00439
00440 if (!file.good())
00441 {
00442 ROS_WARN_STREAM("failed to load initial pose: " << initialPose << "\n"
00443 << "using identity as initial pose");
00444 return cMo;
00445 }
00446
00447 vpPoseVector pose;
00448 for (unsigned i = 0; i < 6; ++i)
00449 if (file.good())
00450 file >> pose[i];
00451 else
00452 {
00453 ROS_WARN("failed to parse initial pose file");
00454 return cMo;
00455 }
00456 cMo.buildFrom(pose);
00457 return cMo;
00458 }
00459
00460 void
00461 TrackerClient::saveInitialPose(const vpHomogeneousMatrix& cMo)
00462 {
00463 boost::filesystem::path initialPose =
00464 getInitialPoseFileFromModelName(modelName_, modelPath_);
00465 boost::filesystem::ofstream file(initialPose);
00466 if (!file.good())
00467 {
00468 ROS_WARN_STREAM
00469 ("failed to save initial pose: " << initialPose
00470 <<"\n"
00471 <<"Note: this is normal is you use a remote resource."
00472 <<"\n"
00473 <<"I.e. your model path starts with http://, package://, etc.");
00474 return;
00475 }
00476 vpPoseVector pose;
00477 pose.buildFrom(cMo);
00478 file << pose;
00479 }
00480
00481 TrackerClient::points_t
00482 TrackerClient::loadInitializationPoints()
00483 {
00484 points_t points;
00485
00486 std::string init =
00487 getInitFileFromModelName(modelName_, modelPath_);
00488 std::string resource = fetchResource(init);
00489 std::stringstream file;
00490 file << resource;
00491
00492 if (!file.good())
00493 {
00494 boost::format fmt("failed to load initialization points: %1");
00495 fmt % init;
00496 throw std::runtime_error(fmt.str());
00497 }
00498
00499 unsigned npoints = 0;
00500 file >> npoints;
00501 if (!file.good())
00502 throw std::runtime_error("failed to read initialization file");
00503
00504 double X = 0., Y = 0., Z = 0.;
00505 vpPoint point;
00506 for (unsigned i = 0; i < npoints; ++i)
00507 {
00508 if (!file.good())
00509 throw std::runtime_error("failed to read initialization file");
00510 file >> X >> Y >> Z;
00511 point.setWorldCoordinates(X,Y,Z);
00512 points.push_back(point);
00513 }
00514 return points;
00515 }
00516
00517 bool
00518 TrackerClient::validatePose(const vpHomogeneousMatrix &cMo){
00519 ros::Rate loop_rate(200);
00520 vpImagePoint ip;
00521 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
00522
00523 vpDisplay::display(image_);
00524 tracker_->display(image_, cMo, cameraParameters_, vpColor::green);
00525 vpDisplay::displayFrame(image_, cMo, cameraParameters_,0.05, vpColor::green);
00526 vpDisplay::displayCharString(image_, 15, 10,
00527 "left click to validate, right click to modify initial pose",
00528 vpColor::red);
00529 vpDisplay::flush(image_);
00530
00531 do
00532 {
00533 ros::spinOnce();
00534 loop_rate.sleep();
00535 if (!ros::ok())
00536 return false;
00537 }
00538 while(ros::ok() && !vpDisplay::getClick(image_, ip, button, false));
00539
00540 if(button == vpMouseButton::button1)
00541 return true;
00542
00543 return false;
00544 }
00545
00546 void
00547 TrackerClient::init()
00548 {
00549 ros::Rate loop_rate(200);
00550 vpHomogeneousMatrix cMo;
00551 vpImagePoint point (10, 10);
00552
00553 cMo = loadInitialPose();
00554
00555
00556 vpImagePoint ip;
00557 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
00558
00559 if(validatePose(cMo))
00560 {
00561 tracker_->initFromPose(image_, cMo);
00562 return;
00563 }
00564
00565 points_t points = loadInitializationPoints();
00566 imagePoints_t imagePoints;
00567
00568 bool done = false;
00569 while(!done){
00570 vpDisplay::display(image_);
00571 vpDisplay::flush(image_);
00572
00573 imagePoints.clear();
00574 for(unsigned i = 0; i < points.size(); ++i)
00575 {
00576 do
00577 {
00578 ros::spinOnce();
00579 loop_rate.sleep();
00580 if (!ros::ok())
00581 return;
00582 }
00583 while(ros::ok() && !vpDisplay::getClick(image_, ip, button, false));
00584
00585 imagePoints.push_back(ip);
00586 vpDisplay::displayCross(image_, imagePoints.back(), 5, vpColor::green);
00587 vpDisplay::flush(image_);
00588 }
00589
00590 tracker_->initFromPoints(image_,imagePoints,points);
00591 tracker_->getPose(cMo);
00592 if(validatePose(cMo))
00593 done = true;
00594 }
00595 tracker_->initFromPose(image_, cMo);
00596 saveInitialPose(cMo);
00597 }
00598
00599 void
00600 TrackerClient::initPoint(unsigned& i,
00601 points_t& points,
00602 imagePoints_t& imagePoints,
00603 ros::Rate& rate,
00604 vpPose& pose)
00605 {
00606 vpImagePoint ip;
00607 double x = 0., y = 0.;
00608 boost::format fmt("click on point %u/%u");
00609 fmt % (i + 1) % points.size();
00610
00611
00612 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
00613 do
00614 {
00615 vpDisplay::display(image_);
00616 vpDisplay::displayCharString
00617 (image_, 15, 10,
00618 fmt.str().c_str(),
00619 vpColor::red);
00620
00621 for (unsigned j = 0; j < imagePoints.size(); ++j)
00622 vpDisplay::displayCross(image_, imagePoints[j], 5, vpColor::green);
00623
00624 vpDisplay::flush(image_);
00625 ros::spinOnce();
00626 rate.sleep();
00627 if (exiting())
00628 return;
00629 }
00630 while(!vpDisplay::getClick(image_, ip, button, false));
00631
00632 imagePoints.push_back(ip);
00633 vpPixelMeterConversion::convertPoint(cameraParameters_, ip, x, y);
00634 points[i].set_x(x);
00635 points[i].set_y(y);
00636 pose.addPoint(points[i]);
00637 }
00638
00639 void
00640 TrackerClient::waitForImage()
00641 {
00642 ros::Rate loop_rate(10);
00643 while (!exiting()
00644 && (!image_.getWidth() || !image_.getHeight()))
00645 {
00646 ROS_INFO_THROTTLE(1, "waiting for a rectified image...");
00647 ros::spinOnce();
00648 loop_rate.sleep();
00649 }
00650 }
00651
00652 std::string
00653 TrackerClient::fetchResource(const std::string& s)
00654 {
00655 resource_retriever::MemoryResource resource =
00656 resourceRetriever_.get(s);
00657 std::string result;
00658 result.resize(resource.size);
00659 unsigned i = 0;
00660 for (; i < resource.size; ++i)
00661 result[i] = resource.data.get()[i];
00662 return result;
00663 }
00664
00665 bool
00666 TrackerClient::makeModelFile(boost::filesystem::ofstream& modelStream,
00667 const std::string& resourcePath,
00668 std::string& fullModelPath)
00669 {
00670 resource_retriever::MemoryResource resource =
00671 resourceRetriever_.get(resourcePath);
00672 std::string result;
00673 result.resize(resource.size);
00674 unsigned i = 0;
00675 for (; i < resource.size; ++i)
00676 result[i] = resource.data.get()[i];
00677 result[resource.size];
00678
00679 char* tmpname = strdup("/tmp/tmpXXXXXX");
00680 if (mkdtemp(tmpname) == NULL)
00681 {
00682 ROS_ERROR_STREAM
00683 ("Failed to create the temporary directory: " << strerror(errno));
00684 return false;
00685 }
00686 boost::filesystem::path path(tmpname);
00687 path /= "model.wrl";
00688 free(tmpname);
00689
00690 fullModelPath = path.native();
00691
00692 modelStream.open(path);
00693 if (!modelStream.good())
00694 {
00695 ROS_ERROR_STREAM
00696 ("Failed to create the temporary file: " << path);
00697 return false;
00698 }
00699 modelStream << result;
00700 modelStream.flush();
00701 return true;
00702 }
00703 }