00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 
00053 
00054 
00055 
00056 namespace cob_3d_registration {
00057 
00058   
00059 #define getInd(x, y) ((x)+(y)*pc.width)
00060 
00061   
00062 #define EVALUATION_MODE_ 0
00063 
00064   
00065 #define DEBUG_SWITCH_ 0
00066 
00067   
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     
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     
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         
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_ )
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     
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     
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       
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       
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       
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       
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) { 
00306           ModifiedICP<Point> icp;
00307           icp.setInputCloud( tmp_pc_new.makeShared() );
00308           
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     
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     
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     
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>;