PointCloud.cpp
Go to the documentation of this file.
1#include "PointCloud.h"
2
3#include <algorithm>
4#include <fstream>
5#include <iostream>
6#include <iterator>
7#include <limits>
8
9#include <MiscLib/Vector.h>
10#ifndef _USE_MATH_DEFINES
11#define _USE_MATH_DEFINES
12#endif
13#include <cmath>
14
15#include "Plane.h"
25#include <GfxTL/KdTree.h>
26#include <GfxTL/L2Norm.h>
29#include <GfxTL/Plane.h>
30#include <GfxTL/VectorKernel.h>
31#include <GfxTL/VectorXD.h>
34
35typedef GfxTL::KdTree<
46
47//#define LMS_NORMALS
48#define PCA_NORMALS
49
50using namespace std;
51
52/*
53 * * This Quickselect routine is based on the algorithm described in
54 * * "Numerical recipes in C", Second Edition,
55 * * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
56 * * This code by Nicolas Devillard - 1998. Public domain.
57 * */
58#define ELEM_SWAP(a, b) \
59 { \
60 float t = (a); \
61 (a) = (b); \
62 (b) = t; \
63 }
64
65float
66quick_select(float arr[], int n)
67{
68 int low, high;
69 int median;
70 int middle, ll, hh;
71 low = 0;
72 high = n - 1;
73 median = (low + high) / 2;
74 for (;;)
75 {
76 if (high <= low) /* One element only */
77 {
78 return arr[median];
79 }
80 if (high == low + 1) /* Two elements only */
81 {
82 if (arr[low] > arr[high])
83 {
84 ELEM_SWAP(arr[low], arr[high]);
85 }
86 return arr[median];
87 }
88 /* Find median of low, middle and high items; swap into position low */
89 middle = (low + high) / 2;
90 if (arr[middle] > arr[high])
91 {
92 ELEM_SWAP(arr[middle], arr[high]);
93 }
94 if (arr[low] > arr[high])
95 {
96 ELEM_SWAP(arr[low], arr[high]);
97 }
98 if (arr[middle] > arr[low])
99 {
100 ELEM_SWAP(arr[middle], arr[low]);
101 }
102 /* Swap low item (now in position middle) into position (low+1) */
103 ELEM_SWAP(arr[middle], arr[low + 1]);
104 /* Nibble from each end towards middle, swapping items when stuck */
105 ll = low + 1;
106 hh = high;
107 for (;;)
108 {
109 do
110 {
111 ll++;
112 } while (arr[low] > arr[ll]);
113 do
114 {
115 hh--;
116 } while (arr[hh] > arr[low]);
117 if (hh < ll)
118 {
119 break;
120 }
121 ELEM_SWAP(arr[ll], arr[hh]);
122 }
123 /* Swap middle item (in position low) back into correct position */
124 ELEM_SWAP(arr[low], arr[hh]);
125 /* Re-set active partition */
126 if (hh <= median)
127 {
128 low = ll;
129 }
130 if (hh >= median)
131 {
132 high = hh - 1;
133 }
134 }
135}
136
137#undef ELEM_SWAP
138
140{
141 float fmax = numeric_limits<float>::max();
142 float fmin = -fmax;
143 m_min = Vec3f(fmax, fmax, fmax);
144 m_max = Vec3f(fmin, fmin, fmin);
145}
146
147PointCloud::PointCloud(Point* points, unsigned int s)
148{
149 float fmax = numeric_limits<float>::max();
150 float fmin = -fmax;
151 m_min = Vec3f(fmax, fmax, fmax);
152 m_max = Vec3f(fmin, fmin, fmin);
153 std::copy(points, points + s, std::back_inserter(*this));
154}
155
156void
158{
159 resize(s);
160 float fmax = numeric_limits<float>::max();
161 float fmin = -fmax;
162 m_min = Vec3f(fmax, fmax, fmax);
163 m_max = Vec3f(fmin, fmin, fmin);
164}
165
166float*
168{
169 float* bbox = new float[6];
170 m_min.getValue(bbox[0], bbox[2], bbox[4]);
171 m_max.getValue(bbox[1], bbox[3], bbox[5]);
172
173 return bbox;
174}
175
176void
178{
179 *min = m_min;
180 *max = m_max;
181}
182
183void
185{
186 for (size_t i = 0; i < size(); ++i)
187 {
188 at(i).pos += trans;
189 }
190 m_min += trans;
191 m_max += trans;
192}
193
194void
195PointCloud::calcNormals(float radius, unsigned int kNN, unsigned int maxTries)
196{
197 // cerr << "Begin calcNormals " << endl << flush;
198
199 KdTree3Df kd;
200 kd.IndexedData(begin(), end());
201 kd.Build();
202
204 KdTree3Df::NearestNeighborsAuxData<value_type> nnAux;
205 //GfxTL::AssumeUniqueLimitedHeap< GfxTL::NN< float > > nn;
207
208 vector<int> stats(91, 0);
209#ifdef PCA_NORMALS
211#endif
212 for (unsigned int i = 0; i < size(); i++)
213 {
214 //kd.PointsInBall(at(i), radius, &nn);
215 //if(nn.size() > kNN)
216 //{
217 // std::sort(nn.begin(), nn.end());
218 // nn.resize(kNN);
219 //}
220 kd.NearestNeighbors(at(i), kNN, &nn, &nnAux);
221 unsigned int num = (unsigned int)nn.size();
222
223 //if(i%1000 == 0)
224 // cerr << num << " ";
225
226 if (num > kNN)
227 {
228 num = kNN;
229 }
230
231 at(i).normal = Vec3f(0, 0, 0);
232 if (num < 8)
233 {
234
235 continue;
236 }
237
238#ifdef PCA_NORMALS
239 weights.resize(nn.size());
240 if (nn.front().sqrDist > 0)
241 {
242 float h = nn.front().sqrDist / 2;
243 for (unsigned int i = 0; i < nn.size(); ++i)
244 {
245 weights[i] = std::exp(-nn[i].sqrDist / h);
246 }
247 }
248 else
249 {
250 std::fill(weights.begin(), weights.end(), 1.f);
251 }
252 plane.Fit(GfxTL::IndexIterate(nn.begin(), begin()),
254 weights.begin());
255 at(i).normal = Vec3f(plane.Normal().Data());
256#endif
257
258#ifdef LMS_NORMALS
259 float score, bestScore = -1.f;
260 for (unsigned int tries = 0; tries < maxTries; tries++)
261 {
262 //choose candidate
263 int i0, i1, i2;
264 i0 = rand() % num;
265 do
266 {
267 i1 = rand() % num;
268 } while (i1 == i0);
269 do
270 {
271 i2 = rand() % num;
272 } while (i2 == i0 || i2 == i1);
273
274 Plane plane;
275 if (!plane.Init(at(nn[i0]), at(nn[i1]), at(nn[i2])))
276 {
277 continue;
278 }
279
280 //evaluate metric
281 float* dist = new float[num];
282 for (unsigned int j = 0; j < num; j++)
283 {
284 dist[j] = plane.getDistance(at(nn[j]));
285 }
286 // sort(dist, dist+num);
287 // score = dist[num/2]; // evaluate median
288 score = quick_select(dist, num); // evaluate median
289 delete[] dist;
290
291 if (score < bestScore || bestScore < 0.f)
292 {
293 if (tries > maxTries / 2)
294 {
295 // let us see how good the first half of candidates are...
296 int index = std::floor(
297 180 / M_PI *
298 std::acos(std::min(1.f, abs(plane.getNormal().dot(at(i).normal)))));
299 stats[index]++;
300 }
301 at(i).normal = plane.getNormal();
302 bestScore = score;
303 }
304 }
305
306#endif
307 }
308
309 //cerr << "End calcNormals " << endl << flush;
310 //copy(stats.begin(), stats.end(), ostream_iterator<int>(cerr, " "));
311}
uint8_t index
#define M_PI
Definition MathTools.h:17
#define ELEM_SWAP(a, b)
GfxTL::KdTree< GfxTL::IncrementalDistanceKdTreeStrategy< GfxTL::MaxIntervalSplittingKdTreeStrategy< GfxTL::CellBBoxBuildInformationKdTreeStrategy< GfxTL::BBoxBuildInformationTreeStrategy< GfxTL::BucketSizeMaxLevelSubdivisionTreeStrategy< GfxTL::CellLevelTreeStrategy< GfxTL::BaseKdTreeStrategy< GfxTL::CellRangeDataTreeStrategy< GfxTL::NullTreeStrategy, GfxTL::IndexedIteratorTreeDataKernel< PointCloud::const_iterator, MiscLib::Vector< size_t > > > > > > > > > >, GfxTL::L2Norm, GfxTL::VectorKernelD< 3 >::VectorKernelType > KdTree3Df
float quick_select(float arr[], int n)
bool Fit(const PointType &origin, PointsForwardIt begin, PointsForwardIt end, WeightsForwardIt weights)
Definition Plane.h:71
void Normal(PointType *normal) const
Definition Plane.hpp:48
void resize(size_type s, const value_type &v)
void resize(size_type s, const value_type &v)
Definition Vector.h:227
const Point * const_iterator
Definition Vector.h:25
Point & at(size_type i)
Definition Vector.h:314
size_type size() const
Definition Vector.h:215
Definition Plane.h:19
float getDistance(const Vec3f &pos) const
Definition Plane.h:41
const Vec3f & getNormal() const
Definition Plane.h:72
bool Init(Vec3f p1, Vec3f p2, Vec3f p3)
Definition Plane.cpp:30
void GetCurrentBBox(Vec3f *min, Vec3f *max) const
void Translate(const Vec3f &trans)
void calcNormals(float radius, unsigned int kNN=20, unsigned int maxTries=100)
void reset(size_t s=0)
float * getBbox() const
Definition basic.h:18
float dot(const Vec3f &v) const
Definition basic.h:104
T min(T t1, T t2)
Definition gdiam.h:44
T max(T t1, T t2)
Definition gdiam.h:51
IndexedIterator< IndexIteratorT, IteratorT > IndexIterate(IndexIteratorT idxIt, IteratorT it)
Vec3f pos
Definition PointCloud.h:30
Vec3f normal
Definition PointCloud.h:31