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>;