00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00020 #include <sys/time.h>
00021 #include "scoped_timer.h"
00022 #include "graph_manager.h"
00023 #include "misc.h"
00024 #include "pcl_ros/transforms.h"
00025 #include "pcl/io/pcd_io.h"
00026 #include "pcl/io/ply_io.h"
00027
00028 #include <opencv2/features2d/features2d.hpp>
00029 #include <qtconcurrentrun.h>
00030 #include <QFile>
00031 #include <utility>
00032 #include <fstream>
00033 #include <boost/foreach.hpp>
00034 #include <rosbag/bag.h>
00035
00036 #include "g2o/types/slam3d/se3quat.h"
00037
00038 #include "g2o/types/slam3d/edge_se3.h"
00039 #include "g2o/core/optimizable_graph.h"
00040
00041 #include <pcl_ros/point_cloud.h>
00042 #include <pcl_ros/transforms.h>
00043
00044
00045 void GraphManager::sendAllClouds(bool threaded){
00046 if (ParameterServer::instance()->get<bool>("concurrent_io") && threaded) {
00047 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::sendAllCloudsImpl);
00048
00049 }
00050 else {
00051 sendAllCloudsImpl();
00052 }
00053 }
00054
00055 tf::StampedTransform GraphManager::computeFixedToBaseTransform(Node* node, bool invert)
00056 {
00057 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(node->vertex_id_));
00058 if(!v){
00059 ROS_FATAL("Nullpointer in graph at position %i!", node->vertex_id_);
00060 throw std::exception();
00061 }
00062
00063 tf::Transform computed_motion = eigenTransf2TF(v->estimate());
00064 tf::Transform base2points = node->getBase2PointsTransform();
00065 printTransform("base2points", base2points);
00066 printTransform("computed_motion", computed_motion);
00067 printTransform("init_base_pose_", init_base_pose_);
00068
00069 tf::Transform world2base = init_base_pose_*base2points*computed_motion*base2points.inverse();
00070 tf::Transform gt_world2base = node->getGroundTruthTransform();
00071 tf::Transform err = gt_world2base.inverseTimes(world2base);
00072
00073
00074
00075 ros::Time now = ros::Time::now();
00076
00077 printTransform("World->Base", world2base);
00078 std::string fixed_frame = ParameterServer::instance()->get<std::string>("fixed_frame_name");
00079 std::string base_frame = ParameterServer::instance()->get<std::string>("base_frame_name");
00080 if(base_frame.empty())
00081 {
00082 base_frame = graph_.begin()->second->header_.frame_id;
00083 }
00084 if(invert){
00085 return tf::StampedTransform(world2base.inverse(), now, base_frame, fixed_frame);
00086 } else {
00087 return tf::StampedTransform(world2base, now, fixed_frame, base_frame);
00088 }
00089 }
00090
00091
00092 void GraphManager::saveBagfileAsync(QString filename){
00093 if (ParameterServer::instance()->get<bool>("concurrent_io")) {
00094 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveBagfile, filename);
00095
00096 }
00097 else {
00098 saveBagfile(filename);
00099 }
00100 }
00101 void GraphManager::saveBagfile(QString filename)
00102 {
00103 ScopedTimer s(__FUNCTION__);
00104 if(filename.isEmpty()){
00105 ROS_ERROR("Filename empty. Cannot save to bagfile.");
00106 return;
00107 }
00108
00109 Q_EMIT iamBusy(0, "Saving Bagfile", graph_.size());
00110 rosbag::Bag bag;
00111 bag.open(qPrintable(filename), rosbag::bagmode::Write);
00112 if(ParameterServer::instance()->get<bool>("compress_output_bagfile"))
00113 {
00114 bag.setCompression(rosbag::compression::BZ2);
00115 }
00116 geometry_msgs::TransformStamped geom_msg;
00117
00118 for (graph_it it = graph_.begin(); it != graph_.end(); ++it)
00119 {
00120 Node* node = it->second;
00121 if(!node->valid_tf_estimate_) {
00122 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00123 continue;
00124 }
00125
00126 tf::tfMessage tfmsg;
00127
00128 tf::StampedTransform base_to_fixed = this->computeFixedToBaseTransform(node, true);
00129 base_to_fixed.stamp_ = node->header_.stamp;
00130 tf::transformStampedTFToMsg(base_to_fixed, geom_msg);
00131 tfmsg.transforms.push_back(geom_msg);
00132
00133 tf::StampedTransform base_to_points = node->getBase2PointsTransform();
00134 base_to_points.stamp_ = node->header_.stamp;
00135 tf::transformStampedTFToMsg(base_to_points, geom_msg);
00136 tfmsg.transforms.push_back(geom_msg);
00137
00138
00139
00140 bag.write("/tf", node->header_.stamp, tfmsg);
00141 bag.write("/rgbdslam/batch_clouds", node->header_.stamp, node->pc_col);
00142
00143 Q_EMIT progress(0, "Saving Bagfile", it->first+1);
00144 QString message;
00145 Q_EMIT setGUIInfo(message.sprintf("Writing pointcloud and map transform (%i/%i) to bagfile %s", it->first, (int)graph_.size(), qPrintable(filename)));
00146 }
00147 Q_EMIT progress(0, "Finished Saving Bagfile", 1e6);
00148 bag.close();
00149 }
00150
00151
00152 void GraphManager::sendAllCloudsImpl()
00153 {
00154 ScopedTimer s(__FUNCTION__);
00155
00156 if (batch_cloud_pub_.getNumSubscribers() == 0){
00157 ROS_WARN("No Subscribers: Sending of clouds cancelled");
00158 return;
00159 }
00160
00161 ROS_INFO("Sending out all clouds");
00162 batch_processing_runs_ = true;
00163 ros::Rate r(ParameterServer::instance()->get<double>("send_clouds_rate"));
00164 double delay_seconds = ParameterServer::instance()->get<double>("send_clouds_delay");
00165
00166 for (graph_it it = graph_.begin(); it != graph_.end(); ++it)
00167 {
00168
00169 Node* node = it->second;
00170 if(!node->valid_tf_estimate_) {
00171 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00172 continue;
00173 }
00174 while(node->header_.stamp.toSec() > (ros::Time::now().toSec() - delay_seconds)){
00175 ROS_INFO("Waiting for node becoming %f seconds old", delay_seconds);
00176 r.sleep();
00177 }
00178 tf::StampedTransform base_to_fixed = this->computeFixedToBaseTransform(node, true);
00179 br_.sendTransform(base_to_fixed);
00180 publishCloud(node, base_to_fixed.stamp_, batch_cloud_pub_);
00181
00182 QString message;
00183 Q_EMIT setGUIInfo(message.sprintf("Sending pointcloud and map transform (%i/%i) on topics %s and /tf", it->first, (int)graph_.size(), ParameterServer::instance()->get<std::string>("individual_cloud_out_topic").c_str()) );
00184 r.sleep();
00185 }
00186
00187 batch_processing_runs_ = false;
00188 Q_EMIT sendFinished();
00189 }
00190
00191
00192 void GraphManager::saveAllClouds(QString filename, bool threaded){
00193 if (ParameterServer::instance()->get<bool>("concurrent_io") && threaded) {
00194 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveAllCloudsToFile, filename);
00195
00196 }
00197 else {
00198 saveAllCloudsToFile(filename);
00199 }
00200 }
00201
00202 void GraphManager::saveIndividualClouds(QString filename, bool threaded){
00203 if (ParameterServer::instance()->get<bool>("concurrent_io") && threaded) {
00204 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveIndividualCloudsToFile, filename);
00205
00206 }
00207 else {
00208 saveIndividualCloudsToFile(filename);
00209 }
00210 }
00211
00212
00215 bool GraphManager::updateCloudOrigin(Node* node)
00216 {
00217 if(!node->valid_tf_estimate_) {
00218 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00219 return false;
00220 }
00221 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(node->vertex_id_));
00222 if(!v){
00223 ROS_ERROR("Nullpointer in graph at position %i!", node->id_);
00224 return false;
00225 }
00226 if(node->pc_col->size() == 0){
00227 ROS_INFO("Skipping Node %i, point cloud data is empty!", node->id_);
00228 return false;
00229 }
00230
00231 node->pc_col->sensor_origin_.head<3>() = v->estimate().translation().cast<float>();
00232 node->pc_col->sensor_orientation_ = v->estimate().rotation().cast<float>();
00233
00234 }
00235
00236 void GraphManager::saveOctomap(QString filename, bool threaded){
00237 if (ParameterServer::instance()->get<bool>("octomap_online_creation")) {
00238 this->writeOctomap(filename);
00239 }
00240 else{
00241 if (ParameterServer::instance()->get<bool>("concurrent_io") && threaded) {
00242
00243 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveOctomapImpl, filename);
00244
00245 }
00246 else {
00247 saveOctomapImpl(filename);
00248 }
00249 }
00250 }
00251
00252 void GraphManager::saveOctomapImpl(QString filename)
00253 {
00254 ScopedTimer s(__FUNCTION__);
00255
00256 batch_processing_runs_ = true;
00257 Q_EMIT iamBusy(1, "Saving Octomap", 0);
00258 std::list<Node*> nodes_for_octomapping;
00259 unsigned int points_to_render = 0;
00260 {
00261 QMutexLocker locker(&optimizer_mutex_);
00262 for (graph_it it = graph_.begin(); it != graph_.end(); ++it){
00263 Node* node = it->second;
00264 if(this->updateCloudOrigin(node)){
00265 nodes_for_octomapping.push_back(node);
00266 points_to_render += node->pc_col->size();
00267 }
00268 }
00269 }
00270
00271 int counter = 0;
00272 co_server_.reset();
00273 Q_EMIT iamBusy(1, "Saving Octomap", nodes_for_octomapping.size());
00274 unsigned int rendered_points = 0;
00275 double delay_seconds = ParameterServer::instance()->get<double>("save_octomap_delay");
00276 BOOST_FOREACH(Node* node, nodes_for_octomapping)
00277 {
00278
00279
00280
00281
00282
00283
00284
00285 QString message;
00286 Q_EMIT setGUIStatus(message.sprintf("Inserting Node %i/%i into octomap", ++counter, (int)nodes_for_octomapping.size()));
00287 this->renderToOctomap(node);
00288 rendered_points += node->pc_col->size();
00289 ROS_INFO("Rendered %u points of %u", rendered_points, points_to_render);
00290 Q_EMIT progress(1, "Saving Octomap", counter);
00291 if(counter % ParameterServer::instance()->get<int>("octomap_autosave_step") == 0){
00292 Q_EMIT setGUIStatus(QString("Autosaving preliminary octomap to ") + filename);
00293 this->writeOctomap(filename);
00294 }
00295 }
00296
00297
00298 Q_EMIT setGUIStatus(QString("Saving final octomap to ") + filename);
00299 co_server_.save(qPrintable(filename));
00300 Q_EMIT progress(1, "Finished Saving Octomap", 1e6);
00301 ROS_INFO ("Saved Octomap to %s", qPrintable(filename));
00302 if(ParameterServer::instance()->get<bool>("octomap_clear_after_save")){
00303 co_server_.reset();
00304 ROS_INFO ("Reset Octomap to free memory");
00305 }
00306
00307 Q_EMIT renderableOctomap(&co_server_);
00308 batch_processing_runs_ = false;
00309 }
00310
00311 void GraphManager::writeOctomap(QString filename) const
00312 {
00313 ScopedTimer s(__FUNCTION__);
00314 co_server_.save(qPrintable(filename));
00315 }
00316
00317 void GraphManager::renderToOctomap(Node* node)
00318 {
00319 ScopedTimer s(__FUNCTION__);
00320 ROS_INFO("Rendering Node %i with frame %s", node->id_, node->header_.frame_id.c_str());
00321 if(updateCloudOrigin(node)){
00322 co_server_.insertCloudCallback(node->pc_col, ParameterServer::instance()->get<double>("maximum_depth"));
00323 }
00324 if(ParameterServer::instance()->get<bool>("octomap_clear_raycasted_clouds")){
00325 node->clearPointCloud();
00326 ROS_INFO("Cleared pointcloud of Node %i", node->id_);
00327 }
00328 }
00329 void GraphManager::saveIndividualCloudsToFile(QString file_basename)
00330 {
00331 ScopedTimer s(__FUNCTION__);
00332
00333 ROS_INFO("Saving all clouds to %s_xxxx.pcd", qPrintable(file_basename));
00334 std::string gt = ParameterServer::instance()->get<std::string>("ground_truth_frame_name");
00335 ROS_INFO_COND(!gt.empty(), "Saving all clouds with ground truth sensor position to gt_%sxxxx.pcd", qPrintable(file_basename));
00336
00337 batch_processing_runs_ = true;
00338 tf::Transform world2base;
00339 QString message, filename;
00340 std::string fixed_frame_id = ParameterServer::instance()->get<std::string>("fixed_frame_name");
00341 for (graph_it it = graph_.begin(); it != graph_.end(); ++it){
00342
00343 Node* node = it->second;
00344 if(!node->valid_tf_estimate_) {
00345 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00346 continue;
00347 }
00348
00349 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(node->vertex_id_));
00350 if(!v){
00351 ROS_ERROR("Nullpointer in graph at position %i!", it->first);
00352 continue;
00353 }
00354 if(node->pc_col->size() == 0){
00355 ROS_INFO("Skipping Node %i, point cloud data is empty!", it->first);
00356 continue;
00357 }
00358
00359
00360
00361
00362
00363
00364
00365
00366 Eigen::Matrix3f export_rot;
00367 Eigen::Vector3f export_origin;
00368 QString transform_filename;
00369 if(ParameterServer::instance()->get<bool>("transform_individual_clouds")){
00370
00371 Eigen::Matrix4f m = v->estimate().matrix().cast<float>();
00372 pcl::transformPointCloud(*(node->pc_col), *(node->pc_col), m);
00373 Eigen::Vector4f sensor_origin(0,0,0,0);
00374 Eigen::Quaternionf sensor_orientation(0,0,0,1);
00375
00376 export_rot= sensor_orientation.toRotationMatrix();
00377 export_origin= sensor_origin.head<3>();
00378 node->pc_col->sensor_origin_ = sensor_origin;
00379 node->pc_col->sensor_orientation_ = sensor_orientation;
00380 node->header_.frame_id = fixed_frame_id;
00381 }
00382 else {
00383 tf::Transform pose = eigenTransf2TF(v->estimate());
00384 tf::StampedTransform base2points = node->getBase2PointsTransform();
00385 world2base = this->computeFixedToBaseTransform(node, false);
00386 tf::Transform world2points = world2base*base2points;
00387
00388 Eigen::Vector4f sensor_origin(world2points.getOrigin().x(),world2points.getOrigin().y(),world2points.getOrigin().z(),world2points.getOrigin().w());
00389 Eigen::Quaternionf sensor_orientation(world2points.getRotation().w(),world2points.getRotation().x(),world2points.getRotation().y(),world2points.getRotation().z());
00390
00391 export_rot= sensor_orientation.toRotationMatrix();
00392 export_origin= sensor_origin.head<3>();
00393 node->pc_col->sensor_origin_ = sensor_origin;
00394 node->pc_col->sensor_orientation_ = sensor_orientation;
00395 node->header_.frame_id = fixed_frame_id;
00396 }
00397
00398 filename.sprintf("%s_%04d.pcd", qPrintable(file_basename), it->first);
00399 transform_filename.sprintf("%s_%04d.txt", qPrintable(file_basename), it->first);
00400 ROS_INFO("Saving %s", qPrintable(filename));
00401 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Transformed Node %i/%i", qPrintable(filename), it->first, (int)camera_vertices.size()));
00402 pcl::io::savePCDFile(qPrintable(filename), *(node->pc_col), true);
00403
00404 std::ofstream trans_file;
00405 trans_file.open(qPrintable(transform_filename), std::ios::out);
00406 for (int i = 0; i < 3; ++i) {
00407 trans_file << export_rot(i,0) << " " << export_rot(i,1) << " " << export_rot(i,2) << " " << export_origin(i) << " ";
00408 }
00409 trans_file << 0 << " " << 0 << " " << 0 << " " << 1 << std::endl;
00410 if(!gt.empty()){
00411 tf::StampedTransform gt_world2base = node->getGroundTruthTransform();
00412 if( gt_world2base.frame_id_ == "/missing_ground_truth" ){
00413 ROS_WARN_STREAM("Skipping ground truth: " << gt_world2base.child_frame_id_ << " child/parent " << gt_world2base.frame_id_);
00414 continue;
00415 }
00416 Eigen::Vector4f sensor_origin(gt_world2base.getOrigin().x(),gt_world2base.getOrigin().y(),gt_world2base.getOrigin().z(),gt_world2base.getOrigin().w());
00417 Eigen::Quaternionf sensor_orientation(gt_world2base.getRotation().w(),gt_world2base.getRotation().x(),gt_world2base.getRotation().y(),gt_world2base.getRotation().z());
00418
00419 node->pc_col->sensor_origin_ = sensor_origin;
00420 node->pc_col->sensor_orientation_ = sensor_orientation;
00421 node->header_.frame_id = fixed_frame_id;
00422
00423 filename.sprintf("%s_%04d_gt.pcd", qPrintable(file_basename), it->first);
00424 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Transformed Node %i/%i", qPrintable(filename), it->first, (int)camera_vertices.size()));
00425 pcl::io::savePCDFile(qPrintable(filename), *(node->pc_col), true);
00426 }
00427
00428 }
00429 Q_EMIT setGUIStatus("Saved all point clouds");
00430 ROS_INFO ("Saved all points clouds to %sxxxx.pcd", qPrintable(file_basename));
00431 batch_processing_runs_ = false;
00432 }
00433
00434 void GraphManager::saveAllFeatures(QString filename, bool threaded)
00435 {
00436 if (ParameterServer::instance()->get<bool>("concurrent_io") && threaded) {
00437 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveAllFeaturesToFile, filename);
00438
00439 }
00440 else {
00441 saveAllFeaturesToFile(filename);
00442 }
00443 }
00444 void GraphManager::saveAllFeaturesToFile(QString filename)
00445 {
00446 ScopedTimer s(__FUNCTION__);
00447
00448 cv::FileStorage fs(qPrintable(filename), cv::FileStorage::WRITE);
00449 fs << "Feature_Locations" << "[";
00450
00451 ROS_INFO("Saving all features to %s transformed to a common coordinate frame.", qPrintable(filename));
00452 batch_processing_runs_ = true;
00453 tf::Transform world2rgb, cam2rgb;
00454 QString message;
00455 cam2rgb.setRotation(tf::createQuaternionFromRPY(-1.57,0,-1.57));
00456 cam2rgb.setOrigin(tf::Point(0,-0.04,0));
00457 int feat_count = 0;
00458 for (graph_it it = graph_.begin(); it != graph_.end(); ++it){
00459
00460 Node* node = it->second;
00461 if(!node->valid_tf_estimate_) {
00462 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00463 continue;
00464 }
00465
00466 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(node->vertex_id_));
00467 tf::Transform world2cam = eigenTransf2TF(v->estimate());
00468 world2rgb = cam2rgb*world2cam;
00469 Eigen::Matrix4f world2rgbMat;
00470 pcl_ros::transformAsMatrix(world2rgb, world2rgbMat);
00471 BOOST_FOREACH(Eigen::Vector4f loc, node->feature_locations_3d_)
00472 {
00473 loc.w() = 1.0;
00474 Eigen::Vector4f new_loc = world2rgbMat * loc;
00475 fs << "{:" << "x" << new_loc.x() << "y" << new_loc.y() << "z" << new_loc.z() << "}";
00476 feat_count++;
00477 }
00478 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Features of Node %i/%i", qPrintable(filename), it->first, (int)camera_vertices.size()));
00479 }
00480 fs << "]";
00481
00482 assert(graph_.size()>0);
00483 int descriptor_size = graph_[0]->feature_descriptors_.cols;
00484 int descriptor_type = graph_[0]->feature_descriptors_.type();
00485 cv::Mat alldescriptors(0, descriptor_size, descriptor_type);
00486 alldescriptors.reserve(feat_count);
00487 for (unsigned int i = 0; i < graph_.size(); ++i) {
00488 alldescriptors.push_back(graph_[i]->feature_descriptors_);
00489 }
00490 fs << "Feature_Descriptors" << alldescriptors;
00491 fs.release();
00492
00493 Q_EMIT setGUIStatus(message.sprintf("Saved %d features points to %s", feat_count, qPrintable(filename)));
00494 ROS_INFO ("Saved %d feature points to %s", feat_count, qPrintable(filename));
00495 batch_processing_runs_ = false;
00496 }
00497
00498
00499
00500
00501 void GraphManager::saveAllCloudsToFile(QString filename){
00502 ScopedTimer s(__FUNCTION__);
00503 if(graph_.size() < 1){
00504 ROS_WARN("Cannot save empty graph. Aborted");
00505 return;
00506 }
00507 bool export_normals =false;
00508 pcl::PointCloud<point_type> aggregate_cloud;
00509 pcl::PointCloud<pcl::PointXYZRGBNormal> aggregate_normal_cloud;
00510
00511 aggregate_cloud.reserve(graph_.size() * graph_.begin()->second->pc_col->size());
00512 ROS_INFO("Saving all clouds to %s, this may take a while as they need to be transformed to a common coordinate frame.", qPrintable(filename));
00513 batch_processing_runs_ = true;
00514
00515 std::string base_frame = ParameterServer::instance()->get<std::string>("base_frame_name");
00516 if(base_frame.empty()){
00517 base_frame = graph_.begin()->second->header_.frame_id;
00518 }
00519
00520 tf::Transform world2cam;
00521
00522
00523 QString message;
00525 tf::Transform cam2rgb;
00526 cam2rgb.setRotation(tf::createQuaternionFromRPY(-1.57,0,-1.57));
00527 cam2rgb.setOrigin(tf::Point(0,-0.04,0));
00528 for (graph_it it = graph_.begin(); it != graph_.end(); ++it){
00529 Node* node = it->second;
00530 if(!node->valid_tf_estimate_) {
00531 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00532 continue;
00533 }
00534 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(node->vertex_id_));
00535 if(!v){
00536 ROS_ERROR("Nullpointer in graph at position %i when saving clouds!", it->first);
00537 continue;
00538 }
00539 tf::Transform transform = eigenTransf2TF(v->estimate());
00540 world2cam = cam2rgb*transform;
00541 if (!export_normals){
00542 transformAndAppendPointCloud (*(node->pc_col), aggregate_cloud, world2cam, ParameterServer::instance()->get<double>("maximum_depth"), node->id_);
00543 } else {
00544 ROS_ERROR("Exporting normals is not implemented");
00545
00546 }
00547 if(ParameterServer::instance()->get<bool>("batch_processing")){
00548 node->clearPointCloud();
00549 }
00550 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Transformed Node %i/%i", qPrintable(filename), it->first, (int)camera_vertices.size()));
00551 }
00552 aggregate_cloud.header.frame_id = base_frame;
00553 aggregate_normal_cloud.header.frame_id = base_frame;
00554 if (filename.endsWith(".ply", Qt::CaseInsensitive) ){
00555 ROS_INFO("ply saving");
00556
00557 pcl::io::savePLYFileBinary(qPrintable(filename), aggregate_cloud);
00558 } else {
00559 ROS_INFO("pcd saving");
00560 if(!filename.endsWith(".pcd", Qt::CaseInsensitive)){
00561 filename.append(".pcd");
00562 }
00563 pcl::io::savePCDFile(qPrintable(filename), aggregate_cloud, true);
00564 }
00565
00566 Q_EMIT setGUIStatus(message.sprintf("Saved %d data points to %s", (int)aggregate_cloud.points.size(), qPrintable(filename)));
00567 ROS_INFO ("Saved %d data points to %s", (int)aggregate_cloud.points.size(), qPrintable(filename));
00568
00569 if (whole_cloud_pub_.getNumSubscribers() > 0){
00570
00571
00572
00573
00574
00575
00576
00577 whole_cloud_pub_.publish(aggregate_cloud.makeShared());
00578 ROS_INFO("Aggregate pointcloud sent");
00579 }
00580 batch_processing_runs_ = false;
00581 }
00582
00583 void GraphManager::pointCloud2MeshFile(QString filename, pointcloud_type full_cloud)
00584 {
00585 QFile file(filename);
00586 if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){
00587 ROS_ERROR("Could not open file %s", qPrintable(filename));
00588 return;
00589 }
00590 QTextStream out(&file);
00591 out << "ply\n";
00592 out << "format ascii 1.0\n";
00593 out << "element vertex " << (int)full_cloud.points.size() << "\n";
00594 out << "property float x\n";
00595 out << "property float y\n";
00596 out << "property float z\n";
00597 out << "property uchar red\n";
00598 out << "property uchar green\n";
00599 out << "property uchar blue\n";
00600 out << "end_header\n";
00601 unsigned char r,g,b;
00602 float x, y, z ;
00603 for(unsigned int i = 0; i < full_cloud.points.size() ; i++){
00604 getColor(full_cloud.points[i], r,g,b);
00605 x = full_cloud.points[i].x;
00606 y = full_cloud.points[i].y;
00607 z = full_cloud.points[i].z;
00608 out << qSetFieldWidth(8) << x << " " << y << " " << z << " ";
00609 out << qSetFieldWidth(3) << r << " " << g << " " << b << "\n";
00610 }
00611 }
00612
00613
00614 void GraphManager::saveTrajectory(QString filebasename, bool with_ground_truth)
00615 {
00616 ScopedTimer s(__FUNCTION__);
00617
00618 if(graph_.size() == 0){
00619 ROS_ERROR("Graph is empty, no trajectory can be saved");
00620 return;
00621 }
00622 QMutexLocker locker(&optimizer_mutex_);
00623
00624
00625 std::string gt = ParameterServer::instance()->get<std::string>("ground_truth_frame_name");
00626
00627 ROS_INFO("Comparison of relative motion with ground truth");
00628 QString gtt_fname("_ground_truth.txt");
00629 QFile gtt_file(gtt_fname.prepend(filebasename));
00630 if(!gtt_file.open(QIODevice::WriteOnly|QIODevice::Text)) return;
00631 QTextStream gtt_out(>t_file);
00632 tf::StampedTransform b2p = graph_[0]->getGroundTruthTransform();
00633 gtt_out.setRealNumberNotation(QTextStream::FixedNotation);
00634 gtt_out << "# TF Coordinate Frame ID: " << b2p.frame_id_.c_str() << "(data: " << b2p.child_frame_id_.c_str() << ")\n";
00635
00636
00637 QString et_fname("_estimate.txt");
00638 QFile et_file (et_fname.prepend(filebasename));
00639 if(!et_file.open(QIODevice::WriteOnly|QIODevice::Text)) return;
00640 ROS_INFO("Logging Trajectory to %s", qPrintable(et_fname));
00641 QTextStream et_out(&et_file);
00642 et_out.setRealNumberNotation(QTextStream::FixedNotation);
00643 b2p = graph_[0]->getBase2PointsTransform();
00644 et_out << "# TF Coordinate Frame ID: " << b2p.frame_id_.c_str() << "(data: " << b2p.child_frame_id_.c_str() << ")\n";
00645
00646 for (graph_it it = graph_.begin(); it != graph_.end(); ++it){
00647 Node* node = it->second;
00648 if(!node->valid_tf_estimate_) {
00649 ROS_INFO("Skipping node %i: No valid estimate", node->id_);
00650 continue;
00651 }
00652 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(node->vertex_id_));
00653
00654 ROS_ERROR_COND(!v, "Nullpointer in graph at position %i when saving trajectory!", it->first);
00655
00656 tf::Transform pose = eigenTransf2TF(v->estimate());
00657
00658 tf::StampedTransform base2points = node->getBase2PointsTransform();
00659 tf::Transform world2base = init_base_pose_*base2points*pose*base2points.inverse();
00660
00661 logTransform(et_out, world2base, node->header_.stamp.toSec());
00662
00663
00664 if(with_ground_truth && !gt.empty()){
00665 tf::StampedTransform gt_world2base = node->getGroundTruthTransform();
00666 if( gt_world2base.frame_id_ == "/missing_ground_truth" ){
00667 ROS_WARN_STREAM("Skipping ground truth: " << gt_world2base.child_frame_id_ << " child/parent " << gt_world2base.frame_id_);
00668 continue;
00669 }
00670 logTransform(gtt_out, gt_world2base, gt_world2base.stamp_.toSec());
00671
00672 }
00673 }
00674 ROS_INFO_COND(!gt.empty() && with_ground_truth, "Written logfiles ground_truth_trajectory.txt and estimated_trajectory.txt");
00675 ROS_INFO_COND(gt.empty(), "Written logfile estimated_trajectory.txt");
00676 }
00677
00682 #include <visualization_msgs/Marker.h>
00683 #include <geometry_msgs/Point.h>
00684 typedef std::set<g2o::HyperGraph::Edge*> EdgeSet;
00685
00686 void GraphManager::visualizeFeatureFlow3D(unsigned int marker_id, bool draw_outlier)
00687 {
00688 ScopedTimer s(__FUNCTION__);
00689
00690 if (ransac_marker_pub_.getNumSubscribers() > 0){
00691 if(curr_best_result_.edge.id1 < 0 || curr_best_result_.edge.id2 < 0) {
00692 ROS_WARN("Attempted to visualize invalid pairwise result");
00693 return;
00694 }
00695 visualization_msgs::Marker marker_lines;
00696
00697 marker_lines.header.frame_id = "/openni_rgb_optical_frame";
00698 marker_lines.ns = "ransac_markers";
00699 marker_lines.header.stamp = ros::Time::now();
00700 marker_lines.action = visualization_msgs::Marker::ADD;
00701 marker_lines.pose.orientation.w = 1.0;
00702 marker_lines.id = marker_id;
00703 marker_lines.type = visualization_msgs::Marker::LINE_LIST;
00704 marker_lines.scale.x = 0.002;
00705
00706 std_msgs::ColorRGBA color_red ;
00707 color_red.r = 1.0;
00708 color_red.a = 1.0;
00709 std_msgs::ColorRGBA color_green;
00710 color_green.g = 1.0;
00711 color_green.a = 1.0;
00712 std_msgs::ColorRGBA color_yellow;
00713 color_yellow.r = 1.0;
00714 color_yellow.g = 1.0;
00715 color_yellow.a = 1.0;
00716 std_msgs::ColorRGBA color_blue ;
00717 color_blue.b = 1.0;
00718 color_blue.a = 1.0;
00719
00720 marker_lines.color = color_green;
00721 const g2o::VertexSE3* earlier_v;
00722 const g2o::VertexSE3* newer_v;
00723
00724 ROS_DEBUG("Matches Visualization start: %zu Matches, %zu Inliers",
00725 curr_best_result_.all_matches.size(),
00726 curr_best_result_.inlier_matches.size());
00727
00728
00729 marker_lines.points.clear();
00730
00731 earlier_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(nodeId2VertexId(curr_best_result_.edge.id1)));
00732 newer_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(nodeId2VertexId(curr_best_result_.edge.id2)));
00733 Node* last = graph_.find(curr_best_result_.edge.id2)->second;
00734 Node* prev = graph_.find(curr_best_result_.edge.id1)->second;
00735
00736 if (draw_outlier)
00737 {
00738 BOOST_FOREACH(cv::DMatch match, curr_best_result_.all_matches)
00739 {
00740
00741 int newer_id = match.queryIdx;
00742 int earlier_id = match.trainIdx;
00743
00744
00745 marker_lines.colors.push_back(color_red);
00746 marker_lines.colors.push_back(color_blue);
00747
00748 marker_lines.points.push_back(
00749 pointInWorldFrame(last->feature_locations_3d_[newer_id], newer_v->estimate()));
00750 marker_lines.points.push_back(
00751 pointInWorldFrame(prev->feature_locations_3d_[earlier_id], earlier_v->estimate()));
00752 }
00753 }
00754
00755 BOOST_FOREACH(cv::DMatch match, curr_best_result_.inlier_matches)
00756 {
00757
00758 int newer_id = match.queryIdx;
00759 int earlier_id = match.trainIdx;
00760
00761
00762 marker_lines.colors.push_back(color_green);
00763 marker_lines.colors.push_back(color_blue);
00764
00765 marker_lines.points.push_back(
00766 pointInWorldFrame(last->feature_locations_3d_[newer_id], newer_v->estimate()));
00767 marker_lines.points.push_back(
00768 pointInWorldFrame(prev->feature_locations_3d_[earlier_id], earlier_v->estimate()));
00769 }
00770
00771 ransac_marker_pub_.publish(marker_lines);
00772 ROS_DEBUG_STREAM("Published " << marker_lines.points.size()/2 << " lines");
00773 }
00774 }
00775
00776
00777 void GraphManager::visualizeGraphEdges() const {
00778 ScopedTimer s(__FUNCTION__);
00779
00780 if (marker_pub_.getNumSubscribers() > 0){
00781 ROS_WARN("Sending RVIZ Marker");
00782 visualization_msgs::Marker edges_marker;
00783 edges_marker.header.frame_id = "/openni_rgb_optical_frame";
00784 edges_marker.header.stamp = ros::Time::now();
00785 edges_marker.ns = "camera_pose_graph";
00786 edges_marker.id = 0;
00787
00788 edges_marker.type = visualization_msgs::Marker::LINE_LIST;
00789 edges_marker.action = visualization_msgs::Marker::ADD;
00790 edges_marker.frame_locked = true;
00791
00792 edges_marker.scale.x = 0.005;
00793
00794 edges_marker.pose.position.x = 0;
00795 edges_marker.pose.position.y = 0;
00796 edges_marker.pose.position.z = 0;
00797 edges_marker.pose.orientation.x = 0.0;
00798 edges_marker.pose.orientation.y = 0.0;
00799 edges_marker.pose.orientation.z = 0.0;
00800 edges_marker.pose.orientation.w = 1.0;
00801
00802 edges_marker.color.r = 1.0f;
00803 edges_marker.color.g = 1.0f;
00804 edges_marker.color.b = 1.0f;
00805 edges_marker.color.a = 0.5f;
00806 geometry_msgs::Point point;
00807 g2o::VertexSE3* v1,* v2;
00808 EdgeSet::iterator edge_iter = cam_cam_edges_.begin();
00809 int counter = 0;
00810 for(;edge_iter != cam_cam_edges_.end(); edge_iter++, counter++) {
00811 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
00812 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
00813 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
00814 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
00815
00816 point.x = v1->estimate().translation().x();
00817 point.y = v1->estimate().translation().y();
00818 point.z = v1->estimate().translation().z();
00819 edges_marker.points.push_back(point);
00820
00821 point.x = v2->estimate().translation().x();
00822 point.y = v2->estimate().translation().y();
00823 point.z = v2->estimate().translation().z();
00824 edges_marker.points.push_back(point);
00825 }
00826
00827 marker_pub_.publish (edges_marker);
00828 ROS_INFO("published %d graph edges", counter);
00829 }
00830
00831 }
00832
00833 void GraphManager::visualizeGraphNodes() const {
00834 ScopedTimer s(__FUNCTION__);
00835
00836 if (marker_pub_.getNumSubscribers() > 0){
00837 visualization_msgs::Marker nodes_marker;
00838 nodes_marker.header.frame_id = "/openni_rgb_optical_frame";
00839 nodes_marker.header.stamp = ros::Time::now();
00840 nodes_marker.ns = "camera_pose_graph";
00841 nodes_marker.id = 1;
00842
00843
00844 nodes_marker.type = visualization_msgs::Marker::LINE_LIST;
00845 nodes_marker.action = visualization_msgs::Marker::ADD;
00846 nodes_marker.frame_locked = true;
00847
00848 nodes_marker.scale.x = 0.002;
00849
00850 nodes_marker.pose.position.x = 0;
00851 nodes_marker.pose.position.y = 0;
00852 nodes_marker.pose.position.z = 0;
00853 nodes_marker.pose.orientation.x = 0.0;
00854 nodes_marker.pose.orientation.y = 0.0;
00855 nodes_marker.pose.orientation.z = 0.0;
00856 nodes_marker.pose.orientation.w = 1.0;
00857
00858 nodes_marker.color.r = 1.0f;
00859 nodes_marker.color.g = 0.0f;
00860 nodes_marker.color.b = 0.0f;
00861 nodes_marker.color.a = 1.0f;
00862
00863
00864 geometry_msgs::Point tail;
00865 geometry_msgs::Point tip;
00866 std_msgs::ColorRGBA arrow_color_red ;
00867 arrow_color_red.r = 1.0;
00868 arrow_color_red.a = 1.0;
00869 std_msgs::ColorRGBA arrow_color_green;
00870 arrow_color_green.g = 1.0;
00871 arrow_color_green.a = 1.0;
00872 std_msgs::ColorRGBA arrow_color_blue ;
00873 arrow_color_blue.b = 1.0;
00874 arrow_color_blue.a = 1.0;
00875 Eigen::Vector3d origin(0.0,0.0,0.0);
00876 Eigen::Vector3d x_axis(0.2,0.0,0.0);
00877 Eigen::Vector3d y_axis(0.0,0.2,0.0);
00878 Eigen::Vector3d z_axis(0.0,0.0,0.2);
00879 Eigen::Vector3d tmp;
00880 int counter = 0;
00881 g2o::VertexSE3* v;
00882 for (g2o::HyperGraph::VertexSet::iterator it = camera_vertices.begin(); it != camera_vertices.end(); ++it){
00883
00884
00885
00886
00887 v = dynamic_cast<g2o::VertexSE3* >(*it);
00888
00889 tmp = v->estimate() * origin;
00890 tail.x = tmp.x();
00891 tail.y = tmp.y();
00892 tail.z = tmp.z();
00893
00894 nodes_marker.points.push_back(tail);
00895 nodes_marker.colors.push_back(arrow_color_red);
00896 tmp = v->estimate() * x_axis;
00897 tip.x = tmp.x();
00898 tip.y = tmp.y();
00899 tip.z = tmp.z();
00900 nodes_marker.points.push_back(tip);
00901 nodes_marker.colors.push_back(arrow_color_red);
00902
00903 nodes_marker.points.push_back(tail);
00904 nodes_marker.colors.push_back(arrow_color_green);
00905 tmp = v->estimate() * y_axis;
00906 tip.x = tmp.x();
00907 tip.y = tmp.y();
00908 tip.z = tmp.z();
00909 nodes_marker.points.push_back(tip);
00910 nodes_marker.colors.push_back(arrow_color_green);
00911
00912 nodes_marker.points.push_back(tail);
00913 nodes_marker.colors.push_back(arrow_color_blue);
00914 tmp = v->estimate() * z_axis;
00915 tip.x = tmp.x();
00916 tip.y = tmp.y();
00917 tip.z = tmp.z();
00918 nodes_marker.points.push_back(tip);
00919 nodes_marker.colors.push_back(arrow_color_blue);
00920
00921 x_axis.x() = 0.1;
00922 y_axis.y() = 0.1;
00923 z_axis.z() = 0.1;
00924 }
00925
00926 marker_pub_.publish (nodes_marker);
00927 ROS_INFO("published %d graph nodes", counter);
00928 }
00929
00930 }
00931
00932 void GraphManager::saveG2OGraph(QString filename)
00933 {
00934 ROS_INFO("Saving G2O graph to %s", qPrintable(filename));
00935 optimizer_->save(qPrintable(filename));
00936 }
00937
00938 tf::StampedTransform GraphManager::stampedTransformInWorldFrame(const Node* node, const tf::Transform& computed_motion) const
00939 {
00940 std::string fixed_frame = ParameterServer::instance()->get<std::string>("fixed_frame_name");
00941 std::string base_frame = ParameterServer::instance()->get<std::string>("base_frame_name");
00942 if(base_frame.empty()){
00943 base_frame = node->header_.frame_id;
00944 }
00945 const tf::StampedTransform& base2points = node->getBase2PointsTransform();
00946
00947 tf::Transform world2base = init_base_pose_*base2points*computed_motion*base2points.inverse();
00948
00949
00950 return tf::StampedTransform(world2base.inverse(), base2points.stamp_, base_frame, fixed_frame);
00951 }
00952
00953 void GraphManager::broadcastLatestTransform(const ros::TimerEvent& event) const
00954 {
00955
00956
00957
00958
00959
00960
00961
00962
00963 }
00964
00965 void GraphManager::broadcastTransform(const tf::StampedTransform& stamped_transform)
00966 {
00967 br_.sendTransform(stamped_transform);
00968 if(graph_.size() > 0){
00969 Node* current_node = graph_.at(graph_.size() - 1);
00970 if(current_node && current_node->header_.stamp.toSec() == stamped_transform.stamp_.toSec()){
00971 publishCloud(current_node, current_node->header_.stamp, online_cloud_pub_);
00972 } else {
00973 ROS_WARN("Timestamp of transform does not match node");
00974 }
00975 }
00976 }
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01006 void publishCloud(Node* node, ros::Time timestamp, ros::Publisher pub){
01007 if (pub.getNumSubscribers() != 0){
01008 myHeader backup_h(node->pc_col->header);
01009 myHeader newtime_h(node->pc_col->header);
01010 newtime_h.stamp = timestamp;
01011 node->pc_col->header = newtime_h;
01012 pub.publish(node->pc_col);
01013 ROS_INFO("Pointcloud with id %i sent with frame %s", node->id_, node->pc_col->header.frame_id.c_str());
01014 node->pc_col->header = backup_h;
01015 }
01016 }
01017
01018 void drawFeatureConnectors(cv::Mat& canvas, cv::Scalar line_color,
01019 const std::vector<cv::DMatch> matches,
01020 const std::vector<cv::KeyPoint>& newer_keypoints,
01021 const std::vector<cv::KeyPoint>& older_keypoints,
01022 int line_thickness = 2,
01023 int line_type = 16)
01024 {
01025 const double pi_fourth = 3.14159265358979323846 / 4.0;
01026 const int circle_radius = 6;
01027
01028 for(unsigned int mtch = 0; mtch < matches.size(); mtch++) {
01029 cv::Point2f p,q;
01030 unsigned int newer_idx = matches[mtch].queryIdx;
01031 unsigned int earlier_idx = matches[mtch].trainIdx;
01032 if(newer_idx > newer_keypoints.size()) break;
01033 q = newer_keypoints[newer_idx].pt;
01034 if(earlier_idx > older_keypoints.size()) break;
01035 p = older_keypoints[earlier_idx].pt;
01036
01037 double angle; angle = atan2( (double) p.y - q.y, (double) p.x - q.x );
01038 double hypotenuse = cv::norm(p-q);
01039 if(hypotenuse > 0.1){
01040 cv::line( canvas, p, q, line_color, line_thickness, line_type );
01041 } else {
01042 cv::circle(canvas, p, 1, line_color, line_thickness, line_type);
01043 }
01044 if(hypotenuse > 3.0){
01045
01046 p.x = (q.x + 4 * cos(angle + pi_fourth));
01047 p.y = (q.y + 4 * sin(angle + pi_fourth));
01048 cv::line( canvas, p, q, line_color, line_thickness, line_type );
01049 p.x = (q.x + 4 * cos(angle - pi_fourth));
01050 p.y = (q.y + 4 * sin(angle - pi_fourth));
01051 cv::line( canvas, p, q, line_color, line_thickness, line_type );
01052 }
01053 }
01054 }
01055 void GraphManager::drawFeatureFlow(cv::Mat& canvas, cv::Scalar line_color,
01056 cv::Scalar circle_color)
01057 {
01058 if(!ParameterServer::instance()->get<bool>("use_gui")){ return; }
01059 ROS_DEBUG("Number of features to draw: %d", (int)curr_best_result_.inlier_matches.size());
01060
01061 if(graph_.empty()) {
01062 ROS_WARN("Feature Flow for empty graph requested. Bug?");
01063 return;
01064 } else if(graph_.size() == 1 || curr_best_result_.edge.id1 == -1 ) {
01065 Node* newernode = graph_[graph_.size()-1];
01066 cv::drawKeypoints(canvas, newernode->feature_locations_2d_, canvas, cv::Scalar(255), 5);
01067 return;
01068 }
01069
01070 Node* earliernode = graph_[curr_best_result_.edge.id1];
01071 Node* newernode = graph_[curr_best_result_.edge.id2];
01072 if(earliernode == NULL){
01073 if(newernode == NULL ){ ROS_ERROR("Nullpointer for Node %u", (unsigned int)graph_.size()-1); }
01074 ROS_ERROR("Nullpointer for Node %d", curr_best_result_.edge.id1);
01075 curr_best_result_.edge.id1 = 0;
01076 return;
01077 } else if(newernode == NULL ){
01078 ROS_ERROR("Nullpointer for Node %u", (unsigned int)graph_.size()-1);
01079 return;
01080 }
01081
01082 cv::Mat tmpimage = cv::Mat::zeros(canvas.rows, canvas.cols, canvas.type());
01083 if(canvas.type() == CV_8UC1) circle_color = cv::Scalar(255);
01084
01085
01086 std::vector<cv::KeyPoint> with_depth, without_depth;
01087 for(int i = 0; i < newernode->feature_locations_2d_.size(); i++){
01088 if(isnan(newernode->feature_locations_3d_[i](2))){
01089 without_depth.push_back(newernode->feature_locations_2d_[i]);
01090 } else {
01091 with_depth.push_back(newernode->feature_locations_2d_[i]);
01092 }
01093 }
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104 drawFeatureConnectors(canvas, cv::Scalar(100.0), curr_best_result_.all_matches, newernode->feature_locations_2d_, earliernode->feature_locations_2d_, 1);
01105
01106 drawFeatureConnectors(canvas, line_color, curr_best_result_.inlier_matches, newernode->feature_locations_2d_, earliernode->feature_locations_2d_, 2);
01107
01108 }
01109
01110 void GraphManager::drawFeatureFlow(cv::Mat& canvas, cv::Mat& canvas_features, cv::Scalar line_color,
01111 cv::Scalar circle_color)
01112 {
01113 if(!ParameterServer::instance()->get<bool>("use_gui")){ return; }
01114 ROS_DEBUG("Number of features to draw: %d", (int)curr_best_result_.inlier_matches.size());
01115
01116 if(graph_.empty()) {
01117 ROS_WARN("Feature Flow for empty graph requested. Bug?");
01118 return;
01119 } else if(graph_.size() == 1 || curr_best_result_.edge.id1 == -1 ) {
01120 Node* newernode = graph_[graph_.size()-1];
01121 cv::drawKeypoints(canvas, newernode->feature_locations_2d_, canvas, cv::Scalar(255), 5);
01122 return;
01123 }
01124
01125 Node* earliernode = graph_[curr_best_result_.edge.id1];
01126 Node* newernode = graph_[curr_best_result_.edge.id2];
01127 if(earliernode == NULL){
01128 if(newernode == NULL ){ ROS_ERROR("Nullpointer for Node %u", (unsigned int)graph_.size()-1); }
01129 ROS_ERROR("Nullpointer for Node %d", curr_best_result_.edge.id1);
01130 curr_best_result_.edge.id1 = 0;
01131 return;
01132 } else if(newernode == NULL ){
01133 ROS_ERROR("Nullpointer for Node %u", (unsigned int)graph_.size()-1);
01134 return;
01135 }
01136
01137 cv::Mat tmpimage = cv::Mat::zeros(canvas.rows, canvas.cols, canvas.type());
01138 if(canvas.type() == CV_8UC1) circle_color = cv::Scalar(255);
01139
01140
01141 std::vector<cv::KeyPoint> with_depth, without_depth;
01142 for(int i = 0; i < newernode->feature_locations_2d_.size(); i++){
01143 if(isnan(newernode->feature_locations_3d_[i](2))){
01144 without_depth.push_back(newernode->feature_locations_2d_[i]);
01145 } else {
01146 with_depth.push_back(newernode->feature_locations_2d_[i]);
01147 }
01148 }
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158 drawFeatureConnectors(canvas, line_color, curr_best_result_.inlier_matches, newernode->feature_locations_2d_, earliernode->feature_locations_2d_);
01159
01160 }
01161 void GraphManager::savePlyFile(QString filename, pointcloud_normal_type& full_cloud){
01162
01163
01164 std::fstream file;
01165 file.open(filename.toStdString().c_str(), std::fstream::out);
01166
01167
01168 if (!file.is_open()) {
01169 std::cerr << "Could not write to file " << filename.toStdString() << std::endl;
01170 return;
01171 }
01172
01173 float x,y,z,nx,ny,nz;
01174
01175 unsigned int r,g,b;
01176 int n=0;
01177 std::stringstream data;
01178 data << std::setprecision(5);
01179
01180 for (int i = 0; i < full_cloud.points.size(); ++i) {
01181 point_normal_type p = full_cloud.points[i];
01182
01183 x=full_cloud.points[i].x;
01184 y=full_cloud.points[i].y;
01185 z=full_cloud.points[i].z;
01186 nx=full_cloud.points[i].normal_x;
01187 ny=full_cloud.points[i].normal_y;
01188 nz=full_cloud.points[i].normal_z;
01189 if (std::isnan(x)||std::isnan(y)||std::isnan(z)||std::isnan(nx)||std::isnan(ny)||std::isnan(nz)){
01190 continue;
01191 }
01192 data << x << " " << y << " " << z << " " << nx << " " << ny << " " << nz << " ";
01193
01194
01195 #ifdef RGB_IS_4TH_DIM
01196 b = *( (unsigned char*)(&p.data[3]));
01197 g = *(1+(unsigned char*)(&p.data[3]));
01198 r = *(2+(unsigned char*)(&p.data[3]));
01199 #else
01200 b = *( (unsigned char*)(&p.rgb));
01201 g = *(1+(unsigned char*)(&p.rgb));
01202 r = *(2+(unsigned char*)(&p.rgb));
01203 #endif
01204
01205 if (r>255) r=255;
01206 if (g>255) g=255;
01207 if (b>255) b=255;
01208 data << r << " " << g << " " << b << " " << r << " " << g << " " << b << std::endl;
01209 n++;
01210 }
01211
01212 file << "ply\n";
01213 file << "format ascii 1.0\n";
01214 file << "element vertex " << n << "\n";
01215 file << "property float x\n";
01216 file << "property float y\n";
01217 file << "property float z\n";
01218 file << "property float nx\n";
01219 file << "property float ny\n";
01220 file << "property float nz\n";
01221 file << "property uchar red\n";
01222 file << "property uchar green\n";
01223 file << "property uchar blue\n";
01224
01225 file << "property uchar diffuse_red\n";
01226 file << "property uchar diffuse_green\n";
01227 file << "property uchar diffuse_blue\n";
01228 file << "end_header\n";
01229
01230 file << data.str();
01231 file.close();
01232 }