1 /* 2 * Software License Agreement (BSD License) 3 * 4 * Copyright (c) 2011, Willow Garage, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 * * Neither the name of Willow Garage, Inc. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 * 34 * $Id$ 35 * 36 */ 37 38 #include "pcl/cuda/sample_consensus/sac_model_plane.h" 39 #include "pcl/cuda/cutil_math.h" 40 41 #include <vector_types.h> 42 #include <thrust/copy.h> 43 #include <thrust/count.h> 44 45 #include <stdio.h> 46 #include <limits> 47 48 49 namespace pcl 50 { 51 namespace cuda 52 { 53 ////////////////////////////////////////////////////////////////////////// 54 template <template <typename> class Storage> SampleConsensusModelPlane(const PointCloudConstPtr & cloud)55 SampleConsensusModelPlane<Storage>::SampleConsensusModelPlane ( 56 const PointCloudConstPtr &cloud) : 57 SampleConsensusModel<Storage> (cloud) 58 { 59 } 60 61 ////////////////////////////////////////////////////////////////////////// 62 template <template <typename> class Storage> void getSamples(int & iterations,Indices & samples)63 SampleConsensusModelPlane<Storage>::getSamples (int &iterations, Indices &samples) 64 { 65 samples.resize (3); 66 float trand = indices_->size () / (RAND_MAX + 1.0f); 67 for (int i = 0; i < 3; ++i) 68 { 69 int idx = (int)(rngl_ () * trand); 70 samples[i] = (*indices_)[idx]; 71 } 72 } 73 74 ////////////////////////////////////////////////////////////////////////// 75 template <template <typename> class Storage> bool computeModelCoefficients(const Indices & samples,Coefficients & model_coefficients)76 SampleConsensusModelPlane<Storage>::computeModelCoefficients ( 77 const Indices &samples, Coefficients &model_coefficients) 78 { 79 if (samples.size () != 3) 80 { 81 return (false); 82 } 83 84 // Compute the segment values (in 3d) between p1 and p0 85 float3 p1p0 = ((PointXYZRGB)input_->points[samples[1]]).xyz - ((PointXYZRGB)input_->points[samples[0]]).xyz; 86 // Compute the segment values (in 3d) between p2 and p0 87 float3 p2p0 = ((PointXYZRGB)input_->points[samples[2]]).xyz - ((PointXYZRGB)input_->points[samples[0]]).xyz; 88 89 // Avoid some crashes by checking for collinearity here 90 float3 dy1dy2 = p1p0 / p2p0; 91 if ( (dy1dy2.x == dy1dy2.y) && (dy1dy2.z == dy1dy2.y) ) // Check for collinearity 92 return (false); 93 94 // Compute the plane coefficients from the 3 given points in a straightforward manner 95 // calculate the plane normal n = (p2-p1) x (p3-p1) = cross (p2-p1, p3-p1) 96 float3 mc = normalize (cross (p1p0, p2p0)); 97 98 if (model_coefficients.size () != 4) 99 model_coefficients.resize (4); 100 model_coefficients[0] = mc.x; 101 model_coefficients[1] = mc.y; 102 model_coefficients[2] = mc.z; 103 // ... + d = 0 104 model_coefficients[3] = -1 * dot (mc, ((PointXYZRGB)input_->points[samples[0]]).xyz); 105 106 return (true); 107 } 108 109 ////////////////////////////////////////////////////////////////////////// 110 template <template <typename> class Storage> 111 //template <typename Tuple> 112 float4 operator ()(int t)113 CreatePlaneHypothesis<Storage>::operator () (int t) 114 { 115 float4 coeff; 116 coeff.x = coeff.y = coeff.z = coeff.w = bad_value; 117 118 int3 samples; 119 float trand = nr_indices / (RAND_MAX + 1.0f); 120 thrust::default_random_engine rng (t); 121 // rng.discard (10); 122 samples.x = indices[(int)(rng () * trand)]; 123 // rng.discard (20); 124 samples.y = indices[(int)(rng () * trand)]; 125 // rng.discard (30); 126 samples.z = indices[(int)(rng () * trand)]; 127 /* samples.x = indices[(int)(thrust::get<0>(t) * trand)]; 128 samples.y = indices[(int)(thrust::get<1>(t) * trand)]; 129 samples.z = indices[(int)(thrust::get<2>(t) * trand)];*/ 130 131 if (isnan (input[samples.x].x) || 132 isnan (input[samples.y].x) || 133 isnan (input[samples.z].x)) 134 return (coeff); 135 136 // Compute the segment values (in 3d) between p1 and p0 137 float3 p1p0 = input[samples.y].xyz - input[samples.x].xyz; 138 // Compute the segment values (in 3d) between p2 and p0 139 float3 p2p0 = input[samples.z].xyz - input[samples.x].xyz; 140 141 // Avoid some crashes by checking for collinearity here 142 float3 dy1dy2 = p1p0 / p2p0; 143 if ( (dy1dy2.x == dy1dy2.y) && (dy1dy2.z == dy1dy2.y) ) // Check for collinearity 144 return (coeff); 145 146 // Compute the plane coefficients from the 3 given points in a straightforward manner 147 // calculate the plane normal n = (p2-p1) x (p3-p1) = cross (p2-p1, p3-p1) 148 float3 mc = normalize (cross (p1p0, p2p0)); 149 150 coeff.x = mc.x; 151 coeff.y = mc.y; 152 coeff.z = mc.z; 153 // ... + d = 0 154 coeff.w = -1 * dot (mc, input[samples.x].xyz); 155 156 return (coeff); 157 } 158 159 ////////////////////////////////////////////////////////////////////////// 160 template <template <typename> class Storage> bool generateModelHypotheses(Hypotheses & h,int max_iterations)161 SampleConsensusModelPlane<Storage>::generateModelHypotheses ( 162 Hypotheses &h, int max_iterations) 163 { 164 using namespace thrust; 165 166 // Create a vector of how many samples/coefficients do we want to get 167 h.resize (max_iterations); 168 169 typename Storage<int>::type randoms (max_iterations); 170 // a sequence counting up from 0 171 thrust::counting_iterator<int> index_sequence_begin (0); 172 // transform the range [0,1,2,...N] 173 // to a range of random numbers 174 thrust::transform (index_sequence_begin, 175 index_sequence_begin + max_iterations, 176 randoms.begin (), 177 parallel_random_generator ((int) time (0))); 178 179 thrust::counting_iterator<int> first (0); 180 // Input: Point Cloud, Indices 181 // Output: Hypotheses 182 transform (//first, first + max_iterations, 183 randoms.begin (), randoms.begin () + max_iterations, 184 h.begin (), 185 CreatePlaneHypothesis<Storage> (thrust::raw_pointer_cast (&input_->points[0]), 186 thrust::raw_pointer_cast (&(*indices_)[0]), 187 (int) indices_->size (), std::numeric_limits<float>::quiet_NaN ())); 188 return (true); 189 } 190 191 ////////////////////////////////////////////////////////////////////////// 192 template <typename Tuple> bool operator ()(const Tuple & t)193 CountPlanarInlier::operator () (const Tuple &t) 194 { 195 if (!isfinite (thrust::raw_reference_cast(thrust::get<0>(t)).x)) 196 return (false); 197 198 return (std::abs (thrust::raw_reference_cast(thrust::get<0>(t)).x * coefficients.x + 199 thrust::raw_reference_cast(thrust::get<0>(t)).y * coefficients.y + 200 thrust::raw_reference_cast(thrust::get<0>(t)).z * coefficients.z + coefficients.w) < threshold); 201 } 202 203 ////////////////////////////////////////////////////////////////////////// 204 template <typename Tuple> int operator ()(const Tuple & t)205 CheckPlanarInlier::operator () (const Tuple &t) 206 { 207 if (isnan (thrust::get<0>(t).x)) 208 return (-1); 209 // Fill in XYZ (and copy NaNs with it) 210 float4 pt; 211 pt.x = thrust::get<0>(t).x; 212 pt.y = thrust::get<0>(t).y; 213 pt.z = thrust::get<0>(t).z; 214 pt.w = 1; 215 216 if (std::abs (dot (pt, coefficients)) < threshold) 217 // If inlier, return its position in the vector 218 return (thrust::get<1>(t)); 219 else 220 // If outlier, return -1 221 return (-1); 222 } 223 224 225 ////////////////////////////////////////////////////////////////////////// 226 template <template <typename> class Storage> int countWithinDistance(const Coefficients & model_coefficients,float threshold)227 SampleConsensusModelPlane<Storage>::countWithinDistance ( 228 const Coefficients &model_coefficients, float threshold) 229 { 230 using namespace thrust; 231 232 // Needs a valid set of model coefficients 233 if (model_coefficients.size () != 4) 234 { 235 fprintf (stderr, "[pcl::cuda::SampleConsensusModelPlane::countWithinDistance] Invalid number of model coefficients given (%lu)!\n", (unsigned long) model_coefficients.size ()); 236 return 0; 237 } 238 239 float4 coefficients; 240 coefficients.x = model_coefficients[0]; 241 coefficients.y = model_coefficients[1]; 242 coefficients.z = model_coefficients[2]; 243 coefficients.w = model_coefficients[3]; 244 245 return (int) count_if ( 246 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())), 247 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())) + 248 indices_->size (), 249 CountPlanarInlier (coefficients, threshold)); 250 } 251 252 ////////////////////////////////////////////////////////////////////////// 253 template <template <typename> class Storage> int countWithinDistance(const Hypotheses & h,int idx,float threshold)254 SampleConsensusModelPlane<Storage>::countWithinDistance ( 255 const Hypotheses &h, int idx, float threshold) 256 { 257 if (isnan (((float4)h[idx]).x)) 258 return (0); 259 260 return (int) 261 (thrust::count_if ( 262 thrust::make_zip_iterator (thrust::make_tuple (input_->points.begin (), indices_->begin ())), 263 thrust::make_zip_iterator (thrust::make_tuple (input_->points.begin (), indices_->begin ())) + 264 indices_->size (), 265 CountPlanarInlier (h[idx], threshold))); 266 } 267 268 ////////////////////////////////////////////////////////////////////////// 269 template <template <typename> class Storage> int selectWithinDistance(const Coefficients & model_coefficients,float threshold,IndicesPtr & inliers,IndicesPtr & inliers_stencil)270 SampleConsensusModelPlane<Storage>::selectWithinDistance ( 271 const Coefficients &model_coefficients, float threshold, IndicesPtr &inliers, IndicesPtr &inliers_stencil) 272 { 273 using namespace thrust; 274 275 // Needs a valid set of model coefficients 276 if (model_coefficients.size () != 4) 277 { 278 fprintf (stderr, "[pcl::cuda::SampleConsensusModelPlane::selectWithinDistance] Invalid number of model coefficients given (%lu)!\n", (unsigned long) model_coefficients.size ()); 279 return 0; 280 } 281 282 int nr_points = (int) indices_->size (); 283 if (!inliers_stencil) 284 inliers_stencil.reset (new Indices()); 285 286 inliers_stencil->resize (nr_points); 287 288 float4 coefficients; 289 coefficients.x = model_coefficients[0]; 290 coefficients.y = model_coefficients[1]; 291 coefficients.z = model_coefficients[2]; 292 coefficients.w = model_coefficients[3]; 293 294 // Send the data to the device 295 transform ( 296 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())), 297 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())) + 298 nr_points, 299 inliers_stencil->begin (), 300 CheckPlanarInlier (coefficients, threshold)); 301 302 if (!inliers) 303 inliers.reset (new Indices()); 304 inliers->resize (nr_points); 305 306 typename Indices::iterator it = copy_if (inliers_stencil->begin (), inliers_stencil->end (), inliers->begin (), isInlier ()); 307 // Copy data 308 //it = remove_copy (inliers_stencil->begin (), inliers_stencil->end (), inliers->begin (), -1); 309 310 inliers->resize (it - inliers->begin ()); 311 return (int) inliers->size(); 312 } 313 314 ////////////////////////////////////////////////////////////////////////// 315 template <template <typename> class Storage> int selectWithinDistance(const Hypotheses & h,int idx,float threshold,IndicesPtr & inliers,IndicesPtr & inliers_stencil)316 SampleConsensusModelPlane<Storage>::selectWithinDistance ( 317 const Hypotheses &h, int idx, float threshold, IndicesPtr &inliers, IndicesPtr &inliers_stencil) 318 { 319 using namespace thrust; 320 321 // Needs a valid set of model coefficients 322 /* if (model_coefficients.size () != 4) 323 { 324 fprintf (stderr, "[pcl::cuda::SampleConsensusModelPlane::selectWithinDistance] Invalid number of model coefficients given (%lu)!\n", (unsigned long) model_coefficients.size ()); 325 return; 326 }*/ 327 328 int nr_points = (int) indices_->size (); 329 330 if (!inliers_stencil) 331 inliers_stencil.reset (new Indices()); 332 333 inliers_stencil->resize (nr_points); 334 335 float4 coefficients; 336 coefficients.x = ((float4)h[idx]).x; 337 coefficients.y = ((float4)h[idx]).y; 338 coefficients.z = ((float4)h[idx]).z; 339 coefficients.w = ((float4)h[idx]).w; 340 341 // Send the data to the device 342 transform ( 343 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())), 344 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())) + 345 nr_points, 346 inliers_stencil->begin (), 347 CheckPlanarInlier (coefficients, threshold)); 348 349 if (!inliers) 350 inliers.reset (new Indices()); 351 inliers->resize (nr_points); 352 353 // Copy data 354 typename Indices::iterator it = copy_if (inliers_stencil->begin (), inliers_stencil->end (), inliers->begin (), isInlier ()); 355 inliers->resize (it - inliers->begin ()); 356 return (int) inliers->size (); 357 } 358 359 ////////////////////////////////////////////////////////////////////////// 360 template <template <typename> class Storage> int selectWithinDistance(Hypotheses & h,int idx,float threshold,IndicesPtr & inliers_stencil,float3 & centroid)361 SampleConsensusModelPlane<Storage>::selectWithinDistance ( 362 Hypotheses &h, int idx, float threshold, IndicesPtr &inliers_stencil, float3 & centroid) 363 { 364 using namespace thrust; 365 366 // Needs a valid set of model coefficients 367 /* if (model_coefficients.size () != 4) 368 { 369 fprintf (stderr, "[pcl::cuda::SampleConsensusModelPlane::selectWithinDistance] Invalid number of model coefficients given (%lu)!\n", (unsigned long) model_coefficients.size ()); 370 return; 371 }*/ 372 373 int nr_points = (int) indices_->size (); 374 375 if (!inliers_stencil) 376 inliers_stencil.reset (new Indices()); 377 inliers_stencil->resize (nr_points); 378 379 float4 coefficients; 380 coefficients.x = ((float4)h[idx]).x; 381 coefficients.y = ((float4)h[idx]).y; 382 coefficients.z = ((float4)h[idx]).z; 383 coefficients.w = ((float4)h[idx]).w; 384 385 transform ( 386 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())), 387 make_zip_iterator (make_tuple (input_->points.begin (), indices_->begin ())) + 388 nr_points, 389 inliers_stencil->begin (), 390 CheckPlanarInlier (coefficients, threshold)); 391 return nr_points - (int) thrust::count (inliers_stencil->begin (), inliers_stencil->end (), -1); 392 } 393 394 template class SampleConsensusModelPlane<Device>; 395 template class SampleConsensusModelPlane<Host>; 396 397 } // namespace 398 } // namespace 399 400