00001 //#include <pcl_ias_sample_consensus/pcl_sac_model_orientation.h> 00002 // 00003 //namespace 00004 //{ 00005 // void 00006 // SACModelOrientation::init (sensor_msgs::PointCloud *cloud) 00007 // { 00008 // // Locate channels with normals 00009 // for (unsigned int d = 0; d < cloud->channels.size (); d++) 00010 // if (cloud->channels[d].name == "nx") 00011 // { 00012 // nx_idx_ = d; 00013 // break; 00014 // } 00015 // if (nx_idx_ == -1) 00016 // { 00017 // ROS_ERROR ("[SACModelOrientation] Provided point cloud does not have normals!"); 00018 // return; 00019 // } 00020 // 00021 // // Copy normals of points into a PCD -- not the most optimal way of creating the kd-tree, but the only one easily available as of this writing :) 00022 // //normals_ = new sensor_msgs::PointCloud; 00023 // normals_.points.resize (indices_.size ()); // we don't care about header and channels 00024 // for (unsigned i = 0; i < normals_.points.size (); i++) 00025 // { 00026 // normals_.points[i].x = cloud->channels[nx_idx_+0].values[indices_[i]]; 00027 // normals_.points[i].y = cloud->channels[nx_idx_+1].values[indices_[i]]; 00028 // normals_.points[i].z = cloud->channels[nx_idx_+2].values[indices_[i]]; 00029 // } 00030 // 00031 // // Create kd-tree 00032 // kdtree_ = new cloud_kdtree::KdTreeANN (normals_); 00033 // } 00034 00035 //template<typename NormalT> void 00036 // pcl::SACModelOrientation<NormalT>::getMinAndMax (boost::shared_ptr<pcl::PointCloud<PointT> > cloud, Eigen::VectorXf *model_coefficients, std::vector<int> *inliers, std::vector<int> &min_max_indices, std::vector<float> &min_max_distances) 00037 // { 00038 // // Initialize result vectors 00039 // min_max_indices.resize (6); 00040 // min_max_distances.resize (6); 00041 // min_max_distances[0] = min_max_distances[2] = min_max_distances[4] = +DBL_MAX; 00042 // min_max_distances[1] = min_max_distances[3] = min_max_distances[5] = -DBL_MAX; 00043 // 00044 // // The 3 coordinate axes are nm, nc and axis_ 00045 // Eigen::Vector3f nm = Eigen::Vector3d::Map(&(*model_coefficients)[0]).cast<float> (); 00046 // Eigen::Vector3f nc = axis_.cross (nm); 00047 // 00048 // // Find minimum and maximum distances from origin along the three axes 00049 // for (std::vector<int>::iterator it = inliers->begin (); it != inliers->end (); it++) 00050 // //for (unsigned i = 0; i < inliers.size (); i++) 00051 // { 00052 // /// @NOTE inliers is a list of indices of the indices_ array! 00053 // Eigen::Vector3f point (input_->points[(*indices_)[*it]].x, input_->points[(*indices_)[*it]].y, input_->points[(*indices_)[*it]].z); 00054 // //Eigen::Vector3f point (cloud_->points[indices_[*it]].x - center.x, cloud_->points[indices_[*it]].y - center.y, cloud_->points[indices_[*it]].z - center.z); 00055 // //Eigen::Vector3f point (cloud_->points[indices_[inliers[i]]].x, cloud_->points[indices_[inliers[i]]].y, cloud_->points[indices_[inliers[i]]].z); 00056 // double dists[3]; 00057 // dists[0] = nm.dot(point); 00058 // dists[1] = nc.dot(point); 00059 // dists[2] = axis_.dot(point); 00060 // for (int d=0; d<3; d++) 00061 // { 00062 // if (min_max_distances[2*d+0] > dists[d]) { min_max_distances[2*d+0] = dists[d]; min_max_indices[2*d+0] = *it; } 00063 // if (min_max_distances[2*d+1] < dists[d]) { min_max_distances[2*d+1] = dists[d]; min_max_indices[2*d+1] = *it; } 00064 // } 00065 // } 00066 // } 00067 00068 template <typename NormalT> void 00069 pcl::SACModelOrientation<NormalT>::getSamples (int &iterations, std::vector<int> &samples) 00070 { 00071 // We're assuming that indices_ have already been set in the constructor 00072 ROS_ASSERT (indices_->size () != 0); 00073 00075 00076 // Get a random number between 0 and indices_.size () 00077 samples.resize (1); 00078 00079 // I don't care, but: http://www.thinkage.ca/english/gcos/expl/c/lib/rand.html 00080 while (indices_->size () <= (unsigned)(samples[0] = rand () / (RAND_MAX/indices_->size ()))); 00081 //unsigned idx;// = (int)(rand () * indices_.size () / (RAND_MAX + 1.0)); 00082 //while (indices_.size () <= (idx = rand () / (RAND_MAX/indices_.size ()))); 00083 // Get the index 00084 //samples[0] = indices_.at (idx); 00085 00086 // TODO: repeat this and test coefficients until ok? increase iterations? 00087 } 00088 00089 template <typename NormalT> bool 00090 pcl::SACModelOrientation<NormalT>::computeModelCoefficients (const std::vector<int> &samples, Eigen::VectorXf &model_coefficients) 00091 { 00092 // Check whether the given index samples can form a valid model 00093 // compute the model coefficients from these samples 00094 // and store them internally in model_coefficients_ 00095 model_coefficients.resize (4); 00096 //model_coefficients_[0] = cloud_->channels[nx_idx_+0].values[samples[0]]; 00097 //model_coefficients_[1] = cloud_->channels[nx_idx_+1].values[samples[0]]; 00098 //model_coefficients_[2] = cloud_->channels[nx_idx_+2].values[samples[0]]; 00099 //model_coefficients_[0] = normals_.points[samples[0]].x; 00100 //model_coefficients_[1] = normals_.points[samples[0]].y; 00101 //model_coefficients_[2] = normals_.points[samples[0]].z; 00102 model_coefficients[0] = input_->points[samples[0]].normal[0]; 00103 model_coefficients[1] = input_->points[samples[0]].normal[1]; 00104 model_coefficients[2] = input_->points[samples[0]].normal[2]; 00105 model_coefficients[3] = samples[0]; 00106 return true; // return value not used anyways 00107 } 00108 00109 template <typename NormalT> void 00110 pcl::SACModelOrientation<NormalT>::refitModel (const std::vector<int> &inliers, std::vector<double> &refit_coefficients) 00111 { 00112 // Compute average direction 00113 refit_coefficients.resize (4); 00114 00115 // Coefficient[3] will hold the the source point's index 00116 if (inliers.size () == 0) 00117 { 00118 ROS_ERROR ("[SACModelOrientation] Can not re-fit 0 points! Returned -1 as source index (in refit_coefficients[3])."); 00119 refit_coefficients[3] = -1; 00120 return; 00121 } 00122 refit_coefficients[3] = inliers.at (0); 00123 00124 //* 00125 00126 // Get source and acceptable distance from it 00127 Eigen::Vector3f nm (input_->points[refit_coefficients[3]].normal[0], input_->points[refit_coefficients[3]].normal[1], input_.points[refit_coefficients[3]].normal[2]); 00128 double eps_angle = M_PI/6; // or use a user defined threshold (eps_angle_)? 00129 double radius = 2 * sin (eps_angle/2); 00130 double sqr_radius = radius*radius; 00131 00132 // get local coordinate system from the axis 00133 //Eigen::Vector3f v = axis_.unitOrthogonal (); 00134 //Eigen::Vector3f u = axis_.cross (v); 00135 00136 //Eigen::MatrixXf rotated_normals(inliers.size (), 3); 00137 Eigen::Vector3f sum_rotated_normals = Eigen::VectorXf::Zero(3); 00138 00139 // Rotate all inliers onto the first direction and sum them up 00140 for (unsigned it = 0; it < inliers.size (); it++) 00141 //for (std::vector<int>::iterator it = inliers.begin (); it != inliers.end (); it++) 00142 { 00145 Eigen::Vector3f ni (input_->points[inliers[it]].normal[0], input_->points[inliers[it]].normal[1], input_->points[inliers[it]].normal[2]); 00146 //std::cerr << "inlier " << it << "/" << inliers[it] << ": " << ni.transpose () << std::endl; 00147 //Eigen::Vector3f ni2 = ni.cwise.square (); 00148 for (int i=0; i<4; i++) 00149 { 00150 // Find the orientation that is pointing in the same direction as the model 00151 if ((nm-ni).squaredNorm () < sqr_radius) 00152 break; 00153 ni = rotateAroundAxis (ni, axis_, M_PI/2); 00154 //std::cerr << i << ": " << ni.transpose () << std::endl; 00155 } 00156 sum_rotated_normals += ni; 00157 } 00158 00159 // Compute average and normalize to get the final model 00160 sum_rotated_normals /= inliers.size (); 00161 sum_rotated_normals.normalize(); 00162 refit_coefficients[0] = sum_rotated_normals[0]; 00163 refit_coefficients[1] = sum_rotated_normals[1]; 00164 refit_coefficients[2] = sum_rotated_normals[2]; 00165 00166 //*/ 00167 } 00168 00169 template <typename NormalT> void 00170 pcl::SACModelOrientation<NormalT>::selectWithinDistance (const Eigen::VectorXf &model_coefficients, double threshold, std::vector<int> &inliers) 00171 { 00172 //std::cerr << model_coefficients[3] << ": " << model_coefficients[0] << " " << model_coefficients[1] << " " << model_coefficients[2] << " "; 00173 00174 // accept only if model direction is perpendicular to axis_ 00175 double cosine = axis_[0]*model_coefficients[0] + axis_[1]*model_coefficients[1] + axis_[2]*model_coefficients[2]; 00176 if (cosine > 1) cosine = 1; 00177 if (cosine < -1) cosine = -1; 00178 //std::cerr << "angle difference = " << acos (cosine); 00179 if (fabs (acos (cosine) - M_PI/2) > threshold) 00180 { 00181 // this will make sure the model is not accepted 00182 inliers.resize (0); 00183 //std::cerr << " DISMISSED" << std::endl; 00184 return; 00185 // TODO: this will increase the iteration count, thus reduces robustness if many points are on the top! do it in getSamples ()! 00186 } 00187 //std::cerr << " ACCEPTED" << std::endl; 00188 00189 00190 // Distance in normal space - TODO: make setter for eps_angle_ and set these there 00191 double radius = 2 * sin (threshold/2); 00192 //double sqr_radius = radius*radius; 00193 00194 // TODO: radius search should return number of points found 00195 00196 // List of points matching the model directly (parallel normal to the sample) 00197 kdtree_->radiusSearch (model_coefficients[3], radius, front_indices_, points_sqr_distances_, input_->points.size ()); 00198 00199 // TODO: pre-check? 00200 00201 // List of points matching the model's back side (opposing normal to the sample) 00202 NormalT tmp; 00203 tmp.normal[0] = -input_->points[model_coefficients[3]].normal[0]; 00204 tmp.normal[1] = -input_->points[model_coefficients[3]].normal[1]; 00205 tmp.normal[2] = -input_->points[model_coefficients[3]].normal[2]; 00206 kdtree_->radiusSearch (tmp, radius, back_indices_, points_sqr_distances_, input_->points.size ()); 00207 00208 // Number of points matching the model's left side (perpendicular normal to the sample) 00209 NormalT zXn; // cross product of axis and original normal (==-tmp) 00210 zXn.normal[0] = axis_[1]*(-tmp.normal[2]) - axis_[2]*(-tmp.normal[1]); 00211 zXn.normal[1] = axis_[2]*(-tmp.normal[0]) - axis_[0]*(-tmp.normal[2]); 00212 zXn.normal[2] = axis_[0]*(-tmp.normal[1]) - axis_[1]*(-tmp.normal[0]); 00213 kdtree_->radiusSearch (zXn, radius, left_indices_, points_sqr_distances_, input_->points.size ()); 00214 00215 // Number of points matching the model's right side (perpendicular normal to the sample) 00216 tmp.normal[0] = -zXn.normal[0]; 00217 tmp.normal[1] = -zXn.normal[1]; 00218 tmp.normal[2] = -zXn.normal[2]; 00219 kdtree_->radiusSearch (tmp, radius, right_indices_, points_sqr_distances_, input_->points.size ()); 00220 00221 // Concatenate results 00222 inliers = front_indices_; 00223 inliers.insert (inliers.end (), back_indices_.begin (), back_indices_.end ()); 00224 inliers.insert (inliers.end (), left_indices_.begin (), left_indices_.end ()); 00225 inliers.insert (inliers.end (), right_indices_.begin (), right_indices_.end ()); 00226 00227 // TODO if inlier threshold is too large, there could be overlaps 00228 //sort (inliers.begin (), inliers.end ()); 00229 //inliers.erase (unique (inliers.begin (), inliers.end ()), inliers.end ()); 00230 00232 } 00233 00235 template <typename PointT> bool 00236 pcl::SACModelOrientation<PointT>::isSampleGood(const std::vector<int> &samples) const 00237 { 00238 return true; 00239 } 00240