registration_info.hpp
Go to the documentation of this file.
00001 /****************************************************************
00002  *
00003  * Copyright (c) 2011
00004  *
00005  * Fraunhofer Institute for Manufacturing Engineering
00006  * and Automation (IPA)
00007  *
00008  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00009  *
00010  * Project name: care-o-bot
00011  * ROS stack name: cob_vision
00012  * ROS package name: registration
00013  * Description:
00014  *
00015  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00016  *
00017  * Author: Joshua Hampp
00018  * Supervised by: Georg Arbeiter, email:georg.arbeiter@ipa.fhg.de
00019  *
00020  * Date of creation: Nov 21, 2011
00021  * ToDo:
00022  *
00023  *
00024  *
00025  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00026  *
00027  * Redistribution and use in source and binary forms, with or without
00028  * modification, are permitted provided that the following conditions are met:
00029  *
00030  *     * Redistributions of source code must retain the above copyright
00031  *       notice, this list of conditions and the following disclaimer.
00032  *     * Redistributions in binary form must reproduce the above copyright
00033  *       notice, this list of conditions and the following disclaimer in the
00034  *       documentation and/or other materials provided with the distribution.
00035  *     * Neither the name of the Fraunhofer Institute for Manufacturing
00036  *       Engineering and Automation (IPA) nor the names of its
00037  *       contributors may be used to endorse or promote products derived from
00038  *       this software without specific prior written permission.
00039  *
00040  * This program is free software: you can redistribute it and/or modify
00041  * it under the terms of the GNU Lesser General Public License LGPL as
00042  * published by the Free Software Foundation, either version 3 of the
00043  * License, or (at your option) any later version.
00044  *
00045  * This program is distributed in the hope that it will be useful,
00046  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00047  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00048  * GNU Lesser General Public License LGPL for more details.
00049  *
00050  * You should have received a copy of the GNU Lesser General Public
00051  * License LGPL along with this program.
00052  * If not, see <http://www.gnu.org/licenses/>.
00053  *
00054  ****************************************************************/
00055 
00056 namespace cob_3d_registration {
00057 
00058   // organized access to pointcloud
00059 #define getInd(x, y) ((x)+(y)*pc.width)
00060 
00061   // evaluation mode uses every result, otherwise only if error is below 3%
00062 #define EVALUATION_MODE_ 0
00063 
00064   // debug mode outputs more information on console and creates pointclouds of HIRN points
00065 #define DEBUG_SWITCH_ 0
00066 
00067   // using odometry to determine keyframes -> set to one was workaround for "standing"-check
00068 #define USED_ODO_ 1
00069 
00070 
00071   template <typename Point>
00072   bool Registration_Infobased<Point>::compute_features()
00073   {
00074     if(!this->input_org_)
00075       return false;
00076 
00077     this->scene_changed_=false;
00078 
00079     indices_pos2.clear();
00080     indices_neg2.clear();
00081 
00082     // first frame -> frame is next frame to register against
00083     if(!this->last_input_) {
00084       this->last_input2_ = this->last_input_ = this->input_org_;
00085       register_ = *this->input_org_;
00086       return false;
00087     }
00088     if(kinect_f_==0) getKinectParameters();
00089 
00090     reproject();
00091     if(!this->last_input2_)
00092       return false;
00093 
00094     const pcl::PointCloud<Point> &pc_old  = *this->last_input2_;
00095     const pcl::PointCloud<Point> &pc      = *this->input_org_;
00096 
00097 #if DEBUG_SWITCH_
00098     assert(pc_old.size()==pc.size());
00099 #endif
00100 
00101     if(!depth_map)
00102       depth_map=new unsigned char[pc.height*pc.width];
00103 
00104     //diff
00105     std::vector<int> indices_pos, indices_neg;
00106 
00107     pcl::PointCloud<Point> _p;
00108     for(int y=0; y<(int)pc.height; y++) {
00109       for(int x=0; x<(int)pc.width; x++) {
00110 
00111         int ind = getInd(x,y);
00112         const Point &pn=pc.points[ind];
00113         const Point &po=pc_old.points[ind];
00114 
00115         if(std::min(pn.z,po.z)>4)
00116           continue;
00117 
00118         float d = pn.z-po.z;
00119 
00120         // threshold is calculated in consideration of quantization error
00121         const float di = std::max(pn.z,po.z) * threshold_diff_;
00122         if(d>di) {
00123           depth_map[ind]=1;
00124           indices_pos.push_back(ind);}
00125         else if(d<-di) {
00126           depth_map[ind]=1;
00127           indices_neg.push_back(ind);
00128         }
00129         else
00130           depth_map[ind]=0;
00131 
00132       }
00133     }
00134 
00135 #if DEBUG_SWITCH_
00136     ROS_INFO("found  %d %d", indices_pos.size(), indices_neg.size());
00137 #endif
00138 
00139     if((int)(indices_pos.size()+indices_neg.size())<min_changes_ /*|| std::min(indices_pos.size(), indices_neg.size())<100*/)
00140     {
00141       if(this->moved_)
00142       {
00143         standing_++;
00144         if(always_relevant_changes_ && standing_>15) {
00145           odo_is_good_=true;
00146           standing_=0;
00147           return true;
00148         }
00149       }
00150       return false;
00151     }
00152 
00153     //find HIRN points (instable points) which are lying on edges
00154     for(unsigned int i=0; i<indices_pos.size(); i++) {
00155       int mi;
00156       int info=getI(indices_pos[i], pc);
00157       if( info<max_info_ && info>min_info_) {
00158         if(getMaxDiff(pc_old, indices_pos[i])>threshold_step_)
00159           indices_pos2.push_back(indices_pos[i]);
00160         else if(getMaxDiff2(pc, indices_pos[i], pc_old, mi)>threshold_step_)
00161           indices_neg2.push_back(mi);
00162       }
00163     }
00164     for(unsigned int i=0; i<indices_neg.size(); i++) {
00165       int mi;
00166       int info=getI(indices_neg[i], pc_old);
00167       if( info<max_info_ && info>min_info_ ) {
00168         if(getMaxDiff(pc, indices_neg[i])>threshold_step_) {
00169           indices_neg2.push_back(indices_neg[i]);
00170         }
00171         else if(getMaxDiff2(pc_old, indices_neg[i], pc, mi)>threshold_step_)
00172           indices_pos2.push_back(mi);
00173       }
00174     }
00175 
00176 #if DEBUG_SWITCH_
00177     markers_.clear();
00178     for(int i=0; i<indices_pos2.size(); i++)
00179     {
00180       pcl::PointXYZRGB p;
00181       p.x=pc_old.points[indices_pos2[i]].x;
00182       p.y=pc_old.points[indices_pos2[i]].y;
00183       p.z=pc_old.points[indices_pos2[i]].z;
00184       p.g=p.b=0;
00185       p.r=255;
00186       markers_.points.push_back(p);
00187     }
00188     for(int i=0; i<indices_neg2.size(); i++)
00189     {
00190       pcl::PointXYZRGB p;
00191       p.x=pc.points[indices_neg2[i]].x;
00192       p.y=pc.points[indices_neg2[i]].y;
00193       p.z=pc.points[indices_neg2[i]].z;
00194       p.g=p.r=0;
00195       p.b=255;
00196       markers_.points.push_back(p);
00197     }
00198     markers_.width=markers_.size();
00199     markers_.height=1;
00200 
00201     ROS_INFO("found2 %d %d", indices_pos2.size(), indices_neg2.size());
00202 #endif
00203 
00204     this->scene_changed_=true;
00205 
00206 #if USED_ODO_
00207     return this->moved_;
00208 #elif USE_INFINITE_
00209     return std::min(indices_pos2.size(), indices_neg2.size())>400;
00210 #else
00211     return std::min(indices_pos2.size(), indices_neg2.size())>200;
00212 #endif
00213   }
00214 
00215   template <typename Point>
00216   bool Registration_Infobased<Point>::compute_corrospondences()
00217   {
00218     return true;
00219   }
00220 
00221   template <typename Point>
00222   bool Registration_Infobased<Point>::compute_transformation()
00223   {
00224     Eigen::Matrix4f T;
00225 
00226     this->failed_++;
00227     this->standing_=0;
00228 
00229     // if many points are HIRN points start from beginning with ICP as its faster in this case
00230     if(odo_is_good_){
00231       T=T.Identity();
00232       ROS_INFO("using odometry because it's good");
00233     }
00234     else if(use_icp_ || indices_pos2.size()+indices_neg2.size()>10000 ) {
00235       //do ICP
00236       ModifiedICP<Point> icp;
00237 
00238       const pcl::PointCloud<Point> &pc_old  = *this->last_input2_;
00239       const pcl::PointCloud<Point> &pc      = *this->input_org_;
00240 
00241       pcl::PointCloud<Point> tmp_pc_old, tmp_pc_new;
00242       for(int i=0; i<(int)indices_pos2.size(); i++)
00243         tmp_pc_old.points.push_back(pc_old.points[indices_pos2[i]]);
00244       for(int i=0; i<(int)indices_neg2.size(); i++)
00245         tmp_pc_new.points.push_back(pc.points[indices_neg2[i]]);
00246 
00247       icp.setInputCloud( tmp_pc_new.makeShared() );
00248       //icp.setIndices(boost::make_shared<pcl::PointIndices>(indices));
00249       icp.setInputTarget(tmp_pc_old.makeShared());
00250       icp.setMaximumIterations(40);
00251       icp.setRANSACOutlierRejectionThreshold(0.05);
00252       icp.setMaxCorrespondenceDistance(0.5);
00253       icp.setTransformationEpsilon (0.0001);
00254 
00255       pcl::PointCloud<Point> result;
00256       icp.align(result);
00257 
00258       T = icp.getFinalTransformation();
00259 
00260       //check if transformation ist within max.
00261       if(T.col(3).head<3>().squaredNorm()>tmax_*tmax_)
00262         T=T.Identity();
00263       if(Eigen::Quaternionf(T.topLeftCorner<3, 3> ()).angularDistance(Eigen::Quaternionf::Identity())>rmax_)
00264         T=T.Identity();
00265 
00266       if(check_samples_ && !checkSamples(T)) {
00267         if(!use_odometry_ || this->failed_<10)
00268           return false;
00269         else {
00270           T = T.Identity();
00271 #if DEBUG_SWITCH_
00272           ROS_INFO("using odometry");
00273 #endif
00274         }
00275       }
00276 
00277     }
00278     else {
00279       TransformationEstimationMultipleCorrespondences<Point> tf_est;
00280 
00281       tf_est.setMaxAngularDistance(rmax_);
00282       tf_est.setMaxTranslationDistance(tmax_);
00283 
00284       const pcl::PointCloud<Point> &pc_old  = *this->last_input2_;
00285       const pcl::PointCloud<Point> &pc      = *this->input_org_;
00286 
00287       pcl::PointCloud<Point> tmp_pc_old, tmp_pc_new;
00288       for(int i=0; i<(int)indices_pos2.size(); i++)
00289         tmp_pc_old.points.push_back(pc_old.points[indices_pos2[i]]);
00290       for(int i=0; i<(int)indices_neg2.size(); i++)
00291         tmp_pc_new.points.push_back(pc.points[indices_neg2[i]]);
00292 
00293       T = tf_est.compute(tmp_pc_old,tmp_pc_new).inverse();
00294 
00295       //check if transformation ist within max.
00296       if(T.col(3).head<3>().squaredNorm()>tmax_*tmax_)
00297         T=T.Identity();
00298       if(Eigen::Quaternionf(T.topLeftCorner<3, 3> ()).angularDistance(Eigen::Quaternionf::Identity())>rmax_)
00299         T=T.Identity();
00300 
00301       int bad=100;
00302       if(check_samples_) {
00303         bool b=checkSamples(T, &bad);
00304 
00305         if(bad>2) { // if error is ove 2% switch to ICP -> slower
00306           ModifiedICP<Point> icp;
00307           icp.setInputCloud( tmp_pc_new.makeShared() );
00308           //icp.setIndices(boost::make_shared<pcl::PointIndices>(indices));
00309           icp.setInputTarget(tmp_pc_old.makeShared());
00310           icp.setMaximumIterations(40);
00311           icp.setRANSACOutlierRejectionThreshold(0.05);
00312           icp.setMaxCorrespondenceDistance(0.2);
00313           icp.setTransformationEpsilon (0.0001);
00314 
00315           pcl::PointCloud<Point> result;
00316           if(b)
00317             icp.align(result,T);
00318           else
00319             icp.align(result);
00320           T = icp.getFinalTransformation();
00321         }
00322 
00323         checkSamples(T, &bad);
00324         if(bad>2) {
00325 #if EVALUATION_MODE_
00326           T=T.Identity();
00327 #else
00328           bad_counter_++;
00329           if(!use_odometry_ || this->failed_<10)
00330             return false;
00331         }
00332         else {
00333           T = T.Identity();
00334           ROS_INFO("using odometry");
00335         }
00336 #endif
00337       }
00338 
00339 
00340     }
00341 
00342     if(T.col(3).head<3>().squaredNorm()>9*tmax_*tmax_)
00343       T=T.Identity();
00344     if(Eigen::Quaternionf(T.topLeftCorner<3, 3> ()).angularDistance(Eigen::Quaternionf::Identity())>3*rmax_)
00345       T=T.Identity();
00346 
00347     this->transformation_ = this->transformation_*T;
00348 
00349     this->last_input_ = this->input_org_;
00350     this->odometry_last_ = this->odometry_;
00351     this->failed_ = 0;
00352 
00353     return true;
00354   }
00355 
00356 #define INFO_SEARCH_RADIUS_ 1
00357 
00358   template <typename Point>
00359   float Registration_Infobased<Point>::getMaxDiff(const pcl::PointCloud<Point> &pc,const int ind) {
00360     int x=ind%pc.width;
00361     int y=ind/pc.width;
00362     if(x<INFO_SEARCH_RADIUS_||y<INFO_SEARCH_RADIUS_||x>=(int)pc.width-INFO_SEARCH_RADIUS_||y>=(int)pc.height-INFO_SEARCH_RADIUS_)
00363       return 0.f;
00364 
00365     float z=pc.points[ind].z;
00366 
00367     float m=0.f;
00368     for(int dx=-INFO_SEARCH_RADIUS_; dx<=INFO_SEARCH_RADIUS_; dx++) {
00369       for(int dy=-INFO_SEARCH_RADIUS_; dy<=INFO_SEARCH_RADIUS_; dy++) {
00370         if(dx==0 && dy==0)
00371           continue;
00372         float t=pc.points[getInd(x+dx,y+dy)].z;
00373 #if USE_INFINITE_
00374         if(!pcl_isfinite(t)) t=100;
00375 #endif
00376         m=std::max(m,t-z);
00377       }
00378     }
00379 
00380     return m;
00381   }
00382 
00383   template <typename Point>
00384   float Registration_Infobased<Point>:: getMaxDiff2(const pcl::PointCloud<Point> &pc,const int ind,const pcl::PointCloud<Point> &pc2, int &mi) {
00385     int x=ind%pc.width;
00386     int y=ind/pc.width;
00387     if(x<INFO_SEARCH_RADIUS_||y<INFO_SEARCH_RADIUS_||x>=(int)pc.width-INFO_SEARCH_RADIUS_||y>=(int)pc.height-INFO_SEARCH_RADIUS_)
00388       return 0.f;
00389 
00390     float z=pc2.points[ind].z;
00391 
00392     float m=0.f, _mi=1000.f;
00393     for(int dx=-INFO_SEARCH_RADIUS_; dx<=INFO_SEARCH_RADIUS_; dx++) {
00394       for(int dy=-INFO_SEARCH_RADIUS_; dy<=INFO_SEARCH_RADIUS_; dy++) {
00395         if(dx==0 && dy==0)
00396           continue;
00397         float f=pc.points[getInd(x+dx,y+dy)].z-z;
00398 #if USE_INFINITE_
00399         if(!pcl_isfinite(f)) f=100;
00400 #endif
00401         m=std::max(m,f);
00402         f=std::abs(f);
00403         if(f<_mi) {
00404           _mi=f;
00405           mi = getInd(x+dx,y+dy);
00406         }
00407       }
00408     }
00409 
00410     if(_mi>threshold_diff_*z)
00411       return 0;
00412 
00413     return m;
00414   }
00415 
00416 
00417   template <typename Point>
00418   int Registration_Infobased<Point>::getI(const int ind, const pcl::PointCloud<Point> &pc) {
00419     int x=ind%pc.width;
00420     int y=ind/pc.width;
00421     if(x<2||y<2||x>=(int)pc.width-2||y>=(int)pc.height-2)
00422       return 25;
00423 
00424     int r=0;
00425     for(int dx=-2; dx<=2; dx++) {
00426       for(int dy=-2; dy<=2; dy++) {
00427         if(dx==0 && dy==0)
00428           continue;
00429         r+=depth_map[getInd(x+dx,y+dy)];
00430       }
00431     }
00432 
00433     return r;
00434   }
00435 
00436   template <typename Point>
00437   void Registration_Infobased<Point>::getKinectParameters()
00438   {
00439     const pcl::PointCloud<Point> &pc      = *this->input_org_;
00440 
00441     //get kinect paramters
00442     Point p1,p2;
00443     p1.x=p2.x=0.f;
00444     p1.y=p2.y=0.f;
00445     p1.z=p2.z=0.f;
00446     int i1=-1, i2=-1;
00447 
00448     for(int x=0; x<(int)pc.width; x+=8) {
00449       for(int y=0; y<(int)pc.height; y+=8) {
00450         int ind = getInd(x,y);
00451         if(pcl_isfinite(pc[ind].z)) {
00452           p1=pc[ind];
00453           i1=ind;
00454           x=pc.width;
00455           break;
00456         }
00457       }
00458     }
00459 
00460     for(int x=pc.width-1; x>=0; x-=8) {
00461       for(int y=pc.height-1; y>=0; y-=8) {
00462         int ind = getInd(x,y);
00463         if(pcl_isfinite(pc[ind].z)&&pc[ind].z!=p1.z&&pc[ind].z<10.f) {
00464           p2=pc[ind];
00465           i2=ind;
00466           x=-1;
00467           break;
00468         }
00469       }
00470     }
00471 
00472     if(i1==-1||i2==-1) {
00473       ROS_ERROR("no valid points found to detect kinect parameters, please RESTART!!!");
00474       return;
00475     }
00476 
00477     float ax1,ax2, bx1,bx2;
00478     float ay1,ay2, by1,by2;
00479 
00480     int x=i1%pc.width;
00481     int y=i1/pc.width;
00482     ax1=p1.z/p1.x*x;
00483     bx1=p1.z/p1.x;
00484     ay1=p1.z/p1.y*y;
00485     by1=p1.z/p1.y;
00486 
00487     x=i2%pc.width;
00488     y=i2/pc.width;
00489     ax2=p2.z/p2.x*x;
00490     bx2=p2.z/p2.x;
00491     ay2=p2.z/p2.y*y;
00492     by2=p2.z/p2.y;
00493 
00494     this->kinect_dx_ = (ax1-ax2)/(bx1-bx2);
00495     this->kinect_dy_ = (ay1-ay2)/(by1-by2);
00496     this->kinect_f_ = ax1 - bx1*this->kinect_dx_;
00497   }
00498 
00499   template <typename Point>
00500   bool Registration_Infobased<Point>::checkSamples(const Eigen::Matrix4f &T, int *bad_out)
00501   {
00502     const pcl::PointCloud<Point> &pc_old  = *this->last_input2_;
00503     const pcl::PointCloud<Point> &pc      = *this->input_org_;
00504 
00505     //raycast result to compare
00506     int found=0, bad=0;
00507     for(int xx=0; xx<(int)pc.width; xx+=8) {
00508       for(int yy=0; yy<(int)pc.height; yy+=8) {
00509         int ind = getInd(xx,yy);
00510         if(pcl_isfinite(pc[ind].z)) {
00511           Eigen::Vector4f v=T*pc[ind].getVector4fMap();
00512 
00513           if(v(2)>10.f) continue;
00514 
00515           int x=kinect_f_*v(0)/v(2)+kinect_dx_;
00516           int y=kinect_f_*v(1)/v(2)+kinect_dy_;
00517 
00518           if(x<0||x>=(int)pc_old.width || y<0||y>=(int)pc_old.height)
00519             continue;
00520 
00521           ++found;
00522           if( std::abs(pc_old[getInd(x,y)].z-v(2))>threshold_diff_*v(2)*0.5 )
00523             bad++;
00524         }
00525       }
00526     }
00527 
00528     if(found<10)
00529       return false;
00530 
00531 #if DEBUG_SWITCH_
00532     std::cout<<bad<<"/"<<found<<"\n";
00533     std::cout<<bad*100/found<<"\n";
00534 #endif
00535     if(bad_out) *bad_out = bad*100/found;
00536 
00537     return bad*17<found;
00538   }
00539 
00540   template <typename Point>
00541   void Registration_Infobased<Point>::getClouds(pcl::PointCloud<Point> &tmp_pc_old, pcl::PointCloud<Point> &tmp_pc_new)
00542   {
00543     source.clear();
00544     target.clear();
00545 
00546     if(!indices_pos2.size()||!indices_neg2.size())
00547       return;
00548 
00549     std::vector<int> _cor_inds;
00550     const pcl::PointCloud<Point> &pc_old  = *this->last_input2_;
00551     const pcl::PointCloud<Point> &pc      = *this->input_org_;
00552 
00553     for(size_t i=0; i<indices_pos2.size(); i++)
00554       tmp_pc_old.points.push_back(pc_old.points[indices_pos2[i]]);
00555     for(size_t i=0; i<indices_neg2.size(); i++)
00556       tmp_pc_new.points.push_back(pc.points[indices_neg2[i]]);
00557   }
00558 
00559   template <typename Point>
00560   void Registration_Infobased<Point>::reproject()
00561   {
00562     this->last_input2_ = this->last_input_;
00563     if(!use_odometry_ || this->kinect_f_==0)
00564       return;
00565 
00566     const Eigen::Matrix4f T = (this->odometry_last_.inverse()*this->odometry_).inverse();
00567     if( fabs ((T).sum ()) < 0.01 )
00568       return;
00569 
00570     pcl::PointCloud<Point> pc  = *this->last_input_;
00571     boost::shared_ptr<pcl::PointCloud<Point> > pc_old_new(pc.makeShared());
00572 
00573     for(int xx=0; xx<(int)pc.width; xx++)
00574       for(int yy=0; yy<(int)pc.height; yy++)
00575         (*pc_old_new)[getInd(xx,yy)].x = (*pc_old_new)[getInd(xx,yy)].y = (*pc_old_new)[getInd(xx,yy)].z = std::numeric_limits<float>::quiet_NaN();
00576 
00577     //raycast
00578     for(int xx=0; xx<(int)pc.width; xx++) {
00579       for(int yy=0; yy<(int)pc.height; yy++) {
00580         int ind = getInd(xx,yy);
00581         if(pcl_isfinite(pc[ind].z)) {
00582           Eigen::Vector4f v=T*pc[ind].getVector4fMap();
00583 
00584           int x=round(kinect_f_*v(0)/v(2)+kinect_dx_);
00585           int y=round(kinect_f_*v(1)/v(2)+kinect_dy_);
00586 
00587           if(x<0||x>=(int)pc.width || y<0||y>=(int)pc.height)
00588             continue;
00589 
00590           (*pc_old_new)[getInd(x,y)].x = v(0);
00591           (*pc_old_new)[getInd(x,y)].y = v(1);
00592           (*pc_old_new)[getInd(x,y)].z = v(2);
00593         }
00594       }
00595     }
00596 
00597     this->last_input2_ = pc_old_new;
00598   }
00599 
00600 #undef getInd
00601 
00602 }
00603 
00604 #define PCL_INSTANTIATE_Registration_Infobased(T) template class PCL_EXPORTS cob_3d_registration::Registration_Infobased<T>;


cob_3d_registration
Author(s): Joshua Hampp
autogenerated on Wed Aug 26 2015 11:02:36