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