normal_descriptor_alg.cpp
Go to the documentation of this file.
00001 #include "normal_descriptor_alg.h"
00002 
00003 #define PI 3.1415926535
00004 
00005 
00006 //helper fun
00007 inline
00008 int get_bin(float x, float y, std::vector<float> &bin_boundsX, std::vector<float> &bin_boundsY)
00009 {
00010   int row=0;
00011   int col=0;
00012   int nb=bin_boundsX.size();
00013   //int nby=bin_boundsX.size();
00014   //if(x>=bin_boundsX[nb-1] || y >=bin_boundsY[nb-1])
00015   //   ROS_ERROR("x: %f y: %f",x,y);
00016   for(int i=0;i<nb;i++)//todo nb and nby assumed equal!
00017   {
00018     if(x<bin_boundsX[i] && y<bin_boundsY[i])
00019       break;
00020     if(x>bin_boundsX[i])
00021       col++;
00022     if(y>bin_boundsY[i])
00023       row+=nb;
00024   }
00025   return row+col;
00026 }
00027 
00028 //class definition
00029 NormalDescriptorAlgorithm::NormalDescriptorAlgorithm()
00030 {
00031   //default values
00032   this->desc_num_side_spatial_bins_ = 1;
00033   this->desc_num_orient_bins_ = 24;
00034   this->desc_patch_radius_=15;  
00035   
00036   //patch radius
00037   this->set_desc_patch_radius(15);
00038 
00039   //Spatial bins (side)
00040   this->set_num_side_spatial_bins(1);
00041 
00042   //Orientation bins
00043   this->set_num_orientation_bins(24);
00044 
00045   //Number of total bins computed automatically
00046 
00047   //Sample each (num) pixels
00048   this->sample_each_ = 1;
00049 }
00050 
00051 NormalDescriptorAlgorithm::~NormalDescriptorAlgorithm()
00052 {
00053 }
00054 
00055 void NormalDescriptorAlgorithm::config_update(const Config& new_cfg, uint32_t level)
00056 {
00057   this->lock();
00058 
00059   if(this->config_.num_spatial_bins != new_cfg.num_spatial_bins)
00060   {
00061     this->set_num_side_spatial_bins(new_cfg.num_spatial_bins);
00062   }
00063   if(this->config_.num_orientation_bins != new_cfg.num_orientation_bins)
00064   {
00065     this->set_num_orientation_bins(new_cfg.num_orientation_bins);
00066   }
00067   if(this->config_.desc_patch_radius != new_cfg.desc_patch_radius)
00068   {
00069     this->set_desc_patch_radius(new_cfg.desc_patch_radius);
00070   }
00071   if(this->config_.sample_each != new_cfg.sample_each)
00072   {
00073     this->set_sample_each(new_cfg.sample_each);
00074   }
00075 
00076   // save the current configuration
00077   this->config_=new_cfg;
00078   
00079   this->unlock();
00080 }
00081 
00082 // NormalDescriptorAlgorithm Public API
00083 
00084 void NormalDescriptorAlgorithm::compute_spherical_coords(pcl::PointCloud<pcl::Normal> &pcl_normal, pcl::PointCloud<pcl::PointXY> &pcl_spherical)
00085 {
00086   for (size_t i = 0; i < pcl_normal.points.size (); ++i)
00087   {
00088     pcl_spherical.points[i].x = acos(pcl_normal.points[i].normal[2]);
00089     pcl_spherical.points[i].y = atan2(pcl_normal.points[i].normal[1], pcl_normal.points[i].normal[0]);
00090     //std::cout<<"tangles: "<<pcl_spherical.points[i].x<<" "<<pcl_spherical.points[i].y<<std::endl;
00091   }
00092 }
00093 
00094 class II
00095 {
00096 public:
00097   std::vector<unsigned int*> ii_;
00098   unsigned int bins_;
00099   unsigned int width_;
00100   unsigned int height_;
00101 
00102 
00103   II(unsigned int bins, unsigned int width, unsigned int height):bins_(bins),width_(width),height_(height){
00104     ii_.resize(bins);
00105     for(unsigned int i=0;i<bins;i++)
00106       ii_[i]=new unsigned int[width*height];
00107   }
00108   ~II(){
00109     for(unsigned int i=0;i<this->bins_;i++)
00110       delete [] ii_[i];
00111   }
00112   void construct(std::vector<unsigned int> &im){
00113     //0,0
00114     unsigned int u=0;
00115     unsigned int v=0;
00116     for(unsigned int b=0;b<this->bins_;b++)
00117     {
00118       //printf("%d %d %d\n",u,v,b);fflush(0);
00119       ii_[b][this->width_*v+u] =                     \
00120         (im[this->width_*v+u]==b ? 1:0);
00121     }
00122 
00123     //n,0
00124     v=0;
00125     for(u=1;u<this->width_;u++)
00126     {
00127       for(unsigned int b=0;b<this->bins_;b++)
00128       {
00129         //printf("%d %d %d\n",u,v,b);fflush(0);
00130         ii_[b][this->width_*v+u] =                   \
00131           ii_[b][this->width_*v+(u-1)] +             \
00132           (im[this->width_*v+u]==b ? 1:0);
00133         //printf("%d ",im[this->width_*v+u]);
00134       }
00135     }
00136     //printf("0,n\n");fflush(0);
00137     //0,n
00138     u=0;
00139     for(v=1;v<this->height_;v++)
00140     {
00141       for(unsigned int b=0;b<this->bins_;b++)
00142       {
00143         //printf("%d %d %d\n",u,v,b);fflush(0);
00144         ii_[b][this->width_*v+u] =                   \
00145           ii_[b][this->width_*(v-1)+u] +             \
00146           (im[this->width_*v+u]==b ? 1:0);
00147         //printf("%d ",im[this->width_*v+u]);
00148       }
00149     }
00150     //n,n
00151     for(v=1;v<this->height_;v++)
00152     {
00153       for(u=1;u<this->width_;u++)
00154       {
00155         for(unsigned int b=0;b<this->bins_;b++)
00156         {
00157           //printf("%d %d %d\n",u,v,b);fflush(0);
00158           ii_[b][this->width_*v+u] =                 \
00159             ii_[b][this->width_*(v-1)+u] +           \
00160             ii_[b][this->width_*v+(u-1)] -           \
00161             ii_[b][this->width_*(v-1)+(u-1)] +       \
00162             (im[this->width_*v+u]==b ? 1:0);
00163           //printf("%d ",im[this->width_*v+u]);
00164         }
00165       }
00166     }
00167   }
00168   std::vector<unsigned int> get_sub_rect(unsigned int tu, unsigned int tv, unsigned int bu, unsigned int bv)
00169   {
00170     std::vector<unsigned int> ret(this->bins_);
00171     for(unsigned int b=0;b<this->bins_;b++)
00172     {
00173       ret[b] = ii_[b][this->width_*tv+tu] + ii_[b][this->width_*bv+bu] - ii_[b][this->width_*tv+bu] - ii_[b][this->width_*bv+tu];
00174     }
00175     return ret;
00176   }
00177 };
00178 
00179 
00180 //void NormalDescriptorAlgorithm::canonical_orientation(const )
00181 //{
00182 //}
00183 
00184 
00185 void NormalDescriptorAlgorithm::compute_ndescs_integral_spatial(pcl::PointCloud<pcl::PointXYZ>& cloud, normal_descriptor_node::ndesc_pc &ndesc_pc_msg)
00186 {
00187 
00188   ROS_INFO("PointCloud received");
00189 
00190   //TODO check if optimize with voxel-grid or subsampling http://pointclouds.org/documentation/tutorials/voxel_grid.php#voxelgrid 
00191   //compute normal
00192   pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;
00193   pcl::PointCloud<pcl::Normal> pcl_normals;
00194   normal_estimator.setInputCloud(boost::make_shared<pcl::PointCloud<pcl::PointXYZ> > (cloud));
00195   normal_estimator.setKSearch (20);
00196   pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
00197 
00198   normal_estimator.setSearchMethod (tree);
00199   normal_estimator.compute (pcl_normals);
00200 
00201   //get spherical coordinates
00202   pcl::PointCloud<pcl::PointXY> pcl_spherical; //(ab)using PointXY for holding spherical angles
00203   pcl_spherical.width  = cloud.width;
00204   pcl_spherical.height = cloud.height;
00205   pcl_spherical.points.resize (pcl_spherical.width * pcl_spherical.height);
00206   this->compute_spherical_coords(pcl_normals, pcl_spherical);
00207 
00208   //compute descriptors
00209   ndesc_pc_msg.descriptors.clear();
00210   ndesc_pc_msg.len    = this->desc_num_total_bins_;
00211   ndesc_pc_msg.width  = pcl_spherical.width;
00212   ndesc_pc_msg.height = pcl_spherical.height;
00213   ndesc_pc_msg.num_orient_bins = this->desc_num_orient_bins_;
00214   ndesc_pc_msg.num_spa_bins = this->desc_num_side_spatial_bins_;
00215 
00216   std::vector<unsigned int> binned(cloud.width*cloud.height);
00217 
00218   for(uint v=0;v<cloud.height;v++)
00219   {
00220     for(uint u=0;u<cloud.width;u++)
00221     {
00222       if(isnan(pcl_spherical(u,v).x) || isnan(pcl_spherical(u,v).y))
00223         binned[u+cloud.width*v]=-1;
00224       else
00225         binned[u+cloud.width*v]=get_bin(pcl_spherical(u,v).x, pcl_spherical(u,v).y, this->orient_bin_boundsX_, this->orient_bin_boundsY_);
00226     }
00227   }
00228 
00229   II intim(this->desc_num_orient_bins_*this->desc_num_orient_bins_, cloud.width, cloud.height);
00230   intim.construct(binned);
00231 
00232   unsigned int spatial_bin_size    = (2*this->desc_patch_radius_+1)/this->desc_num_side_spatial_bins_;
00233   unsigned int spatial_bin_orients = this->desc_num_orient_bins_*this->desc_num_orient_bins_;
00234 
00235   for(unsigned int v=this->desc_patch_radius_;v<cloud.height-this->desc_patch_radius_-1;v+=this->sample_each_)
00236   {
00237     for(unsigned int u=this->desc_patch_radius_;u<cloud.width-this->desc_patch_radius_-1;u+=this->sample_each_)
00238     {      
00239       //ROS_INFO("v:%d u:%d, spbin size=%d",v,u,spatial_bin_size);
00240 
00241       //ROS_INFO("Computing base descriptor");
00242       normal_descriptor_node::ndesc desc;
00243       //insert point3D
00244       desc.point3d.x=cloud(u,v).x;
00245       desc.point3d.y=cloud(u,v).y;
00246       desc.point3d.z=cloud(u,v).z;
00247       
00248       //insert point2D
00249       desc.u=u;
00250       desc.v=v;
00251       
00252       desc.descriptor.resize(this->desc_num_total_bins_);
00253 
00254       float total_votes=0; // for normalization
00255 
00256       unsigned int absolute_top  = v-this->desc_patch_radius_;
00257       //int absolute_bottom = v+this->desc_patch_radius_;
00258       unsigned int absolute_left = u-this->desc_patch_radius_;
00259       //int absolute_right = u+this->desc_patch_radius_;
00260       //ROS_INFO("atop aleft %d %d",absolute_top,absolute_left);
00261 
00262       for(unsigned int k=0;k<this->desc_num_side_spatial_bins_; k++)
00263       {
00264         unsigned int bound_left  = absolute_left + k*spatial_bin_size;
00265         unsigned int bound_right = absolute_left + (k+1)*spatial_bin_size;
00266         for(unsigned int l=0; l<this->desc_num_side_spatial_bins_; l++)
00267         {
00268           unsigned int bound_top    = absolute_top + l*spatial_bin_size;
00269           unsigned int bound_bottom = absolute_top + (l+1)*spatial_bin_size;
00270 
00271           //ROS_INFO("(l,t,r,b)(%d,%d,%d,%d)",bound_left, bound_top, bound_right, bound_bottom);
00272           std::vector<unsigned int> tmp = intim.get_sub_rect(bound_left, bound_top, bound_right, bound_bottom);
00273           for(unsigned int i=0;i<spatial_bin_orients;i++)
00274           {
00275             unsigned int offset_full_rows = l*this->desc_num_side_spatial_bins_*spatial_bin_orients;
00276             unsigned int offset_this_row  = k*spatial_bin_orients;//swaped k and l, Arnau 4/4/12
00277             desc.descriptor[offset_full_rows + offset_this_row + i]=tmp[i];
00278             total_votes+=tmp[i];
00279           }
00280           //normalize per bin?
00281         //increment bin  (TODO soft voting)
00282         //norm histogram/s (L1 norm)
00283         //TODO: Test L2 norm 
00284         }
00285       }
00286 
00287       //L1 norm //aRNAU 4/4/12 DISABLED NORM
00288       //if(total_votes!=0){
00289       //   for(unsigned int i=0; i<this->desc_num_total_bins_; i++)
00290       //    desc.descriptor[i]/=total_votes;
00291       //}
00292       //sqrt
00293       /*if(0 && total_votes!=0){
00294         for(unsigned int i=0; i<this->desc_num_total_bins_; i++)
00295           desc.descriptor[i]=sqrt(desc.descriptor[i]);
00296       }*/
00297 
00298       ndesc_pc_msg.descriptors.push_back(desc);
00299       ndesc_pc_msg.num++;
00300       //ROS_INFO("loop %d", desc.descriptor.size());
00301     }
00302   }
00303   ROS_INFO("Computed %d descriptors", ndesc_pc_msg.num);
00304 
00305   //save for debugging
00306   // FILE* pf=fopen("/tmp/dasdescs.txt","w");
00307   // for(int ii=0;ii<ndesc_pc_msg.num;ii++)
00308   // {
00309   //   fprintf(pf,"%d %d %f %f %f ",ndesc_pc_msg.descriptors[ii].u,
00310   //        ndesc_pc_msg.descriptors[ii].v,
00311   //        ndesc_pc_msg.descriptors[ii].point3d.x,
00312   //        ndesc_pc_msg.descriptors[ii].point3d.y,
00313   //        ndesc_pc_msg.descriptors[ii].point3d.z);
00314   //   for(int j=0;j<ndesc_pc_msg.descriptors[ii].descriptor.size();j++)
00315   //   fprintf(pf,"%f ",ndesc_pc_msg.descriptors[ii].descriptor[j]);
00316   //   fprintf(pf,"\n");
00317   //   //debug off
00318   // }
00319   // fclose(pf);
00320 
00321   //return ndesc_pc_msg; //data copying... do with shared pointer? done
00322 }
00323 
00324 
00325 // ARNAU 1/12/11
00326 //Untested!
00327 
00328 void NormalDescriptorAlgorithm::compute_descriptor_spatial_rot_inv(pcl::PointCloud<pcl::PointXYZ> &cloud, normal_descriptor_node::ndesc_pc &ndesc_pc_msg)
00329 {
00330 
00331   //TODO check if optimize with voxel-grid or subsampling http://pointclouds.org/documentation/tutorials/voxel_grid.php#voxelgrid   <--- aixo s'hauria d'unificar amb la versio integral image!
00332   //compute normal
00333   pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;
00334   pcl::PointCloud<pcl::Normal> pcl_normals;
00335   normal_estimator.setInputCloud(boost::make_shared<pcl::PointCloud<pcl::PointXYZ> > (cloud));
00336   normal_estimator.setKSearch (20);
00337 
00338   pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
00339 
00340   normal_estimator.setSearchMethod (tree);
00341   normal_estimator.compute (pcl_normals);
00342 
00343   //get spherical coordinates
00344   pcl::PointCloud<pcl::PointXY> pcl_spherical; //(ab)using PointXY for holding spherical angles
00345   pcl_spherical.width  = cloud.width;
00346   pcl_spherical.height = cloud.height;
00347   pcl_spherical.points.resize (pcl_spherical.width * pcl_spherical.height);
00348   this->compute_spherical_coords(pcl_normals, pcl_spherical);
00349 
00350   for(unsigned int v=this->desc_patch_radius_;v<cloud.height-this->desc_patch_radius_;v+=this->sample_each_)
00351   {
00352     for(unsigned int u=this->desc_patch_radius_;u<cloud.width-this->desc_patch_radius_;u+=this->sample_each_)
00353     {      
00354       ndesc_pc_msg.descriptors.push_back(this->compute_descriptor_one_spatial_rot_inv(cloud, pcl_spherical, u, v));
00355       ndesc_pc_msg.num++;
00356     }
00357   }  
00358   ROS_INFO("Computed %d descriptors", ndesc_pc_msg.num);
00359 }
00360 
00361 
00362 normal_descriptor_node::ndesc NormalDescriptorAlgorithm::compute_descriptor_one_spatial_rot_inv(pcl::PointCloud<pcl::PointXYZ> &cloud, pcl::PointCloud<pcl::PointXY> &pcl_spherical, uint u, uint v)
00363 {
00364   //ROS_INFO("Computing spatial descriptor");
00365   normal_descriptor_node::ndesc desc;
00366   //insert point3D
00367   desc.point3d.x = cloud(u,v).x;
00368   desc.point3d.y = cloud(u,v).y;
00369   desc.point3d.z = cloud(u,v).z;
00370 
00371   //insert point2D
00372   desc.u = u;
00373   desc.v = v;
00374 
00375   //ROS_INFO("onpetaa1");
00376 
00377   //rotation invariance - determine dominant orientation in theta (spehrical.Y) angle
00378   //allow for 36 orientations to rotate to
00379   int nbins = 36;
00380   float orientation[36];
00381   //init hist
00382   memset(orientation, 0, sizeof(float)*36);
00383 
00384   int absolute_top  = v-this->desc_patch_radius_;
00385   int absolute_bottom = v+this->desc_patch_radius_;
00386   int absolute_left = u-this->desc_patch_radius_;
00387   int absolute_right = u+this->desc_patch_radius_;
00388   for(int j=absolute_top;j<absolute_bottom;j++)
00389   {
00390     for(int i=absolute_left;i<absolute_right;i++)
00391     {
00392       if(isnan(pcl_spherical(i,j).y))
00393         continue;
00394       int bin = floor(nbins*(PI+pcl_spherical(i,j).y)/(2*PI));
00395       //ROS_INFO("the-- %f -> bin = %i",pcl_spherical(i,j).y, bin);
00396       orientation[bin]+=1;
00397     }  
00398   }
00399 
00400   //TODO smooth histogram
00401   int max_ori=0;
00402   float max_val=0;
00403   for(int ii=0;ii<nbins;ii++)
00404   {
00405     //ROS_INFO("%f ", orientation[ii]);
00406     if(max_val<orientation[ii])
00407     {
00408       //ROS_INFO("!!!!");
00409       max_val=orientation[ii];
00410       max_ori=ii;
00411     }
00412   }
00413   
00414   
00415   float ori=float(max_ori)/float(nbins)*2*PI-PI;
00416   desc.ori=ori;
00417   
00418   // for (int uuu=0;uuu<36;uuu++)
00419   //   printf("%f ", orientation[uuu]);
00420   // printf("\n");
00421   //ROS_INFO("%f %i %f ori",ori,max_ori, max_val);
00422 
00423   desc.descriptor.resize(this->desc_num_total_bins_);
00424   //int absolute_top  = u+this->desc_patch_radius_;
00425   //int absolute_bottom = v+this->desc_patch_radius_;
00426   //int absolute_left = u-this->desc_patch_radius_;
00427   //int absolute_right = u+this->desc_patch_radius_;
00428   int size_bin      = (2*this->desc_patch_radius_+1)/this->desc_num_side_spatial_bins_;
00429 
00430   for(uint k=0; k<this->desc_num_side_spatial_bins_; k++)
00431   {
00432     uint bound_left  = absolute_left + k*size_bin;
00433     uint bound_right = absolute_left + (k+1)*size_bin;
00434     for(uint l=0; l<this->desc_num_side_spatial_bins_; l++)
00435     {
00436       uint bound_top    = absolute_top + l*size_bin;
00437       uint bound_bottom = absolute_top + (l+1)*size_bin;
00438       for(uint i=bound_left; i<=bound_right; i++)
00439       {
00440         for (uint j=bound_top; j<=bound_bottom; j++)
00441         {
00442           //Determine new origin after rotation (rotation center is u,v)
00443           int i_s = (int)round( (cos(ori)*(i-u) - sin(ori)*(j-v)) + u);
00444           int j_s = (int)round( (sin(ori)*(i-u) + cos(ori)*(j-v)) + v);
00445 
00446           //filter points outside
00447           if((i_s<0) || (j_s<0) || (i_s>=cloud.width) || (j_s>=cloud.height))
00448             continue;
00449 
00450           //filter nans
00451           if(isnan(pcl_spherical(i_s,j_s).x) || isnan(pcl_spherical(i_s,j_s).y))
00452             continue;
00453           
00454           //ROS_INFO("%i %i - (%i %i / %i %i @ %i - %f) -> (%i %i)",k,l,i,j,u,v,max_ori,ori,i_s,j_s);
00455       
00456           //determine bin
00457           uint offset = (l*this->desc_num_side_spatial_bins_+k) * this->desc_num_orient_bins_;
00458           float anglemod2pi=(pcl_spherical(i_s,j_s).y+PI)-(ori+PI); //in range 2PI
00459           while(anglemod2pi<0){
00460             //ROS_INFO("%f",anglemod2pi);
00461             anglemod2pi+=2*PI;
00462           }
00463           anglemod2pi-=PI; //back to -PI:PI
00464           uint bin    = get_bin(pcl_spherical(i_s,j_s).x, anglemod2pi, this->orient_bin_boundsX_,this->orient_bin_boundsY_);
00465           //increment bin  (TODO soft voting)
00466           
00467           //ROS_INFO("vote (%i, %i) here %i+%i (max %i) ori=%f %f (%f)",i_s, j_s, bin,offset,ori,this->desc_num_total_bins_,pcl_spherical(i_s,j_s).y-ori,anglemod2pi);
00468           //if(bin>=this->desc_num_total_bins_ || bin<0)
00469           //{
00470           //  ROS_INFO("kaput");
00471           //  exit(10);
00472           //}
00473           desc.descriptor[bin+offset]++;
00474         }
00475       }
00476     }
00477   }
00478   //norm histogram/s (L1 norm)
00479   //TODO: Test L2 norm 
00480 
00481   //ROS_INFO("aaa1");
00482 
00483   //float total_votes=0;
00484   //for(uint i=0; i<this->desc_num_total_bins_; i++)
00485   //  total_votes+=desc.descriptor[i];
00486   //
00487   //for(uint i=0; i<this->desc_num_total_bins_; i++)
00488   //  desc.descriptor[i]/=total_votes;
00489 
00490   return desc;
00491 }


normal_descriptor_node
Author(s): Arnau Ramisa
autogenerated on Fri Dec 6 2013 20:19:55