SegmentedPointCloudFusion.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
25
26#include "ColorICP.h"
27#include "OLPTools.h"
28#include <omp.h>
29
30// system
31#include <sys/time.h>
32
34
36{
37 void
38 FusePointClouds(std::vector<CObjectHypothesis*> aHypotheses,
39 CObjectHypothesis*& pFusedHypothesis)
40 {
41 Transformation3d tTrafo;
42 tTrafo.rotation = Math3d::unit_mat;
43 tTrafo.translation = Math3d::zero_vec;
44 std::vector<Transformation3d> aTransformations;
45 aTransformations.resize(aHypotheses.size());
46
47 for (size_t i = 0; i < aTransformations.size(); i++)
48 {
49 aTransformations.at(i) = tTrafo;
50 }
51
52 FusePointClouds(aHypotheses, aTransformations, pFusedHypothesis);
53 }
54
55 void
56 FusePointClouds(std::vector<CObjectHypothesis*> aHypotheses,
57 std::vector<Transformation3d> aEstimatedTransformations,
58 CObjectHypothesis*& pFusedHypothesis)
59 {
60 std::vector<std::vector<CHypothesisPoint*>> aPointClouds;
61 aPointClouds.resize(aHypotheses.size());
62
63 for (size_t i = 0; i < aHypotheses.size(); i++)
64 {
65 aPointClouds.at(i).resize(aHypotheses.at(i)->aConfirmedPoints.size());
66
67 for (size_t j = 0; j < aHypotheses.at(i)->aConfirmedPoints.size(); j++)
68 {
69 aPointClouds.at(i).at(j) = aHypotheses.at(i)->aConfirmedPoints.at(j);
70 }
71 }
72
73 FusePointClouds(aPointClouds, aEstimatedTransformations, pFusedHypothesis);
74 }
75
76 void
77 FusePointClouds(std::vector<std::vector<CHypothesisPoint*>> aHypothesisPoints,
78 CObjectHypothesis*& pFusedHypothesis)
79 {
80 Transformation3d tTrafo;
81 tTrafo.rotation = Math3d::unit_mat;
82 tTrafo.translation = Math3d::zero_vec;
83 std::vector<Transformation3d> aTransformations;
84 aTransformations.resize(aHypothesisPoints.size());
85
86 for (size_t i = 0; i < aTransformations.size(); i++)
87 {
88 aTransformations.at(i) = tTrafo;
89 }
90
91 FusePointClouds(aHypothesisPoints, aTransformations, pFusedHypothesis);
92 }
93
94 // this one does the real work
95 void
96 FusePointClouds(std::vector<std::vector<CHypothesisPoint*>> aHypothesisPoints,
97 std::vector<Transformation3d> aEstimatedTransformations,
98 CObjectHypothesis*& pFusedHypothesis)
99 {
100 bool bPairwiseFusion = true;
101
102 if (aHypothesisPoints.size() < 2)
103 {
104 ARMARX_WARNING_S << "FusePointClouds: At least two point clouds required!";
105 return;
106 }
107
108 timeval tStart, tEnd;
109 gettimeofday(&tStart, 0);
110
111 // transform points with estimated transformations
112 Transformation3d tAccumulatedTransform, tInverseTransform;
113 tAccumulatedTransform.rotation = Math3d::unit_mat;
114 tAccumulatedTransform.translation = Math3d::zero_vec;
115
116 for (size_t i = 0; i < aHypothesisPoints.size() - 1; i++)
117 {
118 Math3d::MulMatMat(aEstimatedTransformations.at(i).rotation,
119 tAccumulatedTransform.rotation,
120 tAccumulatedTransform.rotation);
121 Math3d::MulMatVec(aEstimatedTransformations.at(i).rotation,
122 tAccumulatedTransform.translation,
123 aEstimatedTransformations.at(i).translation,
124 tAccumulatedTransform.translation);
125 Math3d::Transpose(tAccumulatedTransform.rotation, tInverseTransform.rotation);
126 Math3d::MulMatVec(tInverseTransform.rotation,
127 tAccumulatedTransform.translation,
128 tInverseTransform.translation);
129 Math3d::MulVecScalar(tInverseTransform.translation, -1, tInverseTransform.translation);
130
131 for (size_t j = 0; j < aHypothesisPoints.at(i).size(); j++)
132 {
133 Math3d::MulMatVec(tInverseTransform.rotation,
134 aHypothesisPoints.at(i).at(j)->vPosition,
135 tInverseTransform.translation,
136 aHypothesisPoints.at(i).at(j)->vPosition);
137 }
138 }
139
140 // convert the points to simple format for ICP
141 std::vector<std::vector<CColorICP::CPointXYZRGBI>> aPointClouds;
142 aPointClouds.resize(aHypothesisPoints.size());
143
144 for (size_t i = 0; i < aHypothesisPoints.size(); i++)
145 {
146 aPointClouds.at(i).resize(aHypothesisPoints.at(i).size());
147
148 for (size_t j = 0; j < aHypothesisPoints.at(i).size(); j++)
149 {
150 aPointClouds.at(i).at(j) =
151 CColorICP::ConvertToXYZRGBI(aHypothesisPoints.at(i).at(j));
152 }
153 }
154
155 // different orientations
156 const int nNumDifferentInitializations = 7;
157 std::vector<Mat3d> aRotations;
158 aRotations.resize(nNumDifferentInitializations);
159 const float fAngle = M_PI / 6.0f;
160 Math3d::SetMat(aRotations.at(0), Math3d::unit_mat);
161 Math3d::SetRotationMatX(aRotations.at(1), fAngle);
162 Math3d::SetRotationMatX(aRotations.at(2), -fAngle);
163 Math3d::SetRotationMatY(aRotations.at(3), fAngle);
164 Math3d::SetRotationMatY(aRotations.at(4), -fAngle);
165 Math3d::SetRotationMatZ(aRotations.at(5), fAngle);
166 Math3d::SetRotationMatZ(aRotations.at(6), -fAngle);
167
168
169 // create parallel ICP instances
170 const int nParallelityFactor = omp_get_num_procs();
171 omp_set_num_threads(nParallelityFactor);
172 CColorICP** pColorICPInstances = new CColorICP*[nParallelityFactor];
173
174 for (int i = 0; i < nParallelityFactor; i++)
175 {
176 pColorICPInstances[i] = new CColorICP();
177 pColorICPInstances[i]->SetParameters(OLP_ICP_COLOR_DISTANCE_WEIGHT,
179 }
180
181
182 int nFusionResultIndex = -1;
183
184 ARMARX_VERBOSE_S << "Fusing " << aPointClouds.size() << " segmentations";
185
186 if (bPairwiseFusion)
187 {
188 nFusionResultIndex = 0;
189 std::vector<Transformation3d> aPairwiseTransformations;
190 aPairwiseTransformations.resize(aPointClouds.size() - 1);
191 std::vector<float> aMinDistances;
192 aMinDistances.resize(aPointClouds.size() - 1);
193
194 for (size_t i = 0; i < aPointClouds.size() - 1; i++)
195 {
196 aPairwiseTransformations.at(i).rotation = Math3d::unit_mat;
197 aPairwiseTransformations.at(i).translation = Math3d::zero_vec;
198 aMinDistances.at(i) = FLT_MAX;
199 }
200
201
202// determine the best transformation, starting from different initial transformations
203#pragma omp parallel for schedule(static, 1)
204 for (int k = 0; k < nNumDifferentInitializations * ((int)aPointClouds.size() - 1); k++)
205 {
206 const int n = k / nNumDifferentInitializations;
207 const int i = k % nNumDifferentInitializations;
208 const int nThreadNumber = omp_get_thread_num();
209
210 // set second point cloud as scene point cloud (because it is probably bigger than the one before)
211 pColorICPInstances[nThreadNumber]->SetScenePointcloud(aPointClouds.at(n + 1));
212
213 // rotate points
214 std::vector<CColorICP::CPointXYZRGBI> aRotatedPointCloud;
215 aRotatedPointCloud.resize(aPointClouds.at(n).size());
216
217 for (size_t j = 0; j < aPointClouds.at(n).size(); j++)
218 {
219 aRotatedPointCloud.at(j) = aPointClouds.at(n).at(j);
221 aRotatedPointCloud.at(j), aRotations.at(i), Math3d::zero_vec);
222 }
223
224 Mat3d mRotation;
225 Vec3d vTranslation;
226 float fDist = pColorICPInstances[nThreadNumber]->SearchObject(
227 aRotatedPointCloud, mRotation, vTranslation);
228
229#pragma omp critical
230 if (fDist < aMinDistances.at(n))
231 {
232 Math3d::MulMatMat(
233 mRotation, aRotations.at(i), aPairwiseTransformations.at(n).rotation);
234 Math3d::SetVec(aPairwiseTransformations.at(n).translation, vTranslation);
235 aMinDistances.at(n) = fDist;
236 }
237 }
238
239 // combine all transformed point clouds
240 int nOverallNumberOfPoints = 0;
241 int nIndexOffset = aPointClouds.at(0).size();
242
243 for (size_t i = 0; i < aPointClouds.size(); i++)
244 {
245 nOverallNumberOfPoints += aPointClouds.at(i).size();
246 }
247
248 aPointClouds.at(nFusionResultIndex).resize(nOverallNumberOfPoints);
249 tAccumulatedTransform.rotation = Math3d::unit_mat;
250 tAccumulatedTransform.translation = Math3d::zero_vec;
251
252 for (size_t i = 0; i < aPointClouds.size() - 1; i++)
253 {
254 Math3d::MulMatMat(aPairwiseTransformations.at(i).rotation,
255 tAccumulatedTransform.rotation,
256 tAccumulatedTransform.rotation);
257 Math3d::MulMatVec(aPairwiseTransformations.at(i).rotation,
258 tAccumulatedTransform.translation,
259 aPairwiseTransformations.at(i).translation,
260 tAccumulatedTransform.translation);
261 Math3d::Transpose(tAccumulatedTransform.rotation, tInverseTransform.rotation);
262 Math3d::MulMatVec(tInverseTransform.rotation,
263 tAccumulatedTransform.translation,
264 tInverseTransform.translation);
265 Math3d::MulVecScalar(
266 tInverseTransform.translation, -1, tInverseTransform.translation);
267
268#pragma omp parallel for schedule(static, 100)
269 for (size_t j = 0; j < aPointClouds.at(i + 1).size(); j++)
270 {
271 CColorICP::TransformPointXYZRGBI(aPointClouds.at(i + 1).at(j),
272 tInverseTransform.rotation,
273 tInverseTransform.translation);
274 aPointClouds.at(nFusionResultIndex).at(nIndexOffset + j) =
275 aPointClouds.at(i + 1).at(j);
276 }
277
278 if (i > 0)
279 {
280 nIndexOffset += aPointClouds.at(i).size();
281 }
282 }
283 }
284 else
285 {
286 const int nStartIndex = aHypothesisPoints.size() / 2;
287 nFusionResultIndex = nStartIndex;
288 int nLowIndex = nStartIndex - 1;
289 int nHighIndex = nStartIndex + 1;
290
291 // match and add earlier and later segmentations alternately
292 while (nLowIndex >= 0 || nHighIndex < (int)aHypothesisPoints.size())
293 {
294 // update fused point cloud into ICP after adding both an earlier and later segmentation
295 for (int i = 0; i < nParallelityFactor; i++)
296 {
297 pColorICPInstances[i]->SetScenePointcloud(aPointClouds.at(nStartIndex));
298 }
299
300 // match the next earlier segmentation
301 if (nLowIndex >= 0)
302 {
303 Mat3d mBestRotation = Math3d::unit_mat;
304 Vec3d vBestTranslation = Math3d::zero_vec;
305 float fMinDist = FLT_MAX;
306
307#pragma omp parallel for schedule(static, 1)
308 for (int i = 0; i < nNumDifferentInitializations; i++)
309 {
310 const int nThreadNumber = omp_get_thread_num();
311
312 // rotate points
313 std::vector<CColorICP::CPointXYZRGBI> aRotatedPointCloud;
314 aRotatedPointCloud.resize(aPointClouds.at(nLowIndex).size());
315
316 for (size_t j = 0; j < aPointClouds.at(nLowIndex).size(); j++)
317 {
318 aRotatedPointCloud.at(j) = aPointClouds.at(nLowIndex).at(j);
320 aRotatedPointCloud.at(j), aRotations.at(i), Math3d::zero_vec);
321 }
322
323 Mat3d mRotation;
324 Vec3d vTranslation;
325 float fDist = pColorICPInstances[nThreadNumber]->SearchObject(
326 aRotatedPointCloud, mRotation, vTranslation);
327
328#pragma omp critical
329 if (fDist < fMinDist)
330 {
331 Math3d::MulMatMat(mRotation, aRotations.at(i), mBestRotation);
332 Math3d::SetVec(vBestTranslation, vTranslation);
333 fMinDist = fDist;
334 }
335 }
336
337 ARMARX_VERBOSE_S << "Index " << nLowIndex << ": dist " << fMinDist;
338
339 for (size_t i = 0; i < aPointClouds.at(nLowIndex).size(); i++)
340 {
342 aPointClouds.at(nLowIndex).at(i), mBestRotation, vBestTranslation);
343 aPointClouds.at(nStartIndex).push_back(aPointClouds.at(nLowIndex).at(i));
344 }
345
346 nLowIndex--;
347 }
348
349 // match the next later segmentation
350 if (nHighIndex < (int)aHypothesisPoints.size())
351 {
352 Mat3d mBestRotation = Math3d::unit_mat;
353 Vec3d vBestTranslation = Math3d::zero_vec;
354 float fMinDist = FLT_MAX;
355
356#pragma omp parallel for schedule(static, 1)
357 for (int i = 0; i < nNumDifferentInitializations; i++)
358 {
359 const int nThreadNumber = omp_get_thread_num();
360
361 // rotate points
362 std::vector<CColorICP::CPointXYZRGBI> aRotatedPointCloud;
363 aRotatedPointCloud.resize(aPointClouds.at(nHighIndex).size());
364
365 for (size_t j = 0; j < aPointClouds.at(nHighIndex).size(); j++)
366 {
367 aRotatedPointCloud.at(j) = aPointClouds.at(nHighIndex).at(j);
369 aRotatedPointCloud.at(j), aRotations.at(i), Math3d::zero_vec);
370 }
371
372 Mat3d mRotation;
373 Vec3d vTranslation;
374 float fDist = pColorICPInstances[nThreadNumber]->SearchObject(
375 aRotatedPointCloud, mRotation, vTranslation);
376
377#pragma omp critical
378 if (fDist < fMinDist)
379 {
380 Math3d::MulMatMat(mRotation, aRotations.at(i), mBestRotation);
381 Math3d::SetVec(vBestTranslation, vTranslation);
382 fMinDist = fDist;
383 }
384 }
385
386 ARMARX_VERBOSE_S << "Index " << nHighIndex << ": dist" << fMinDist;
387
388 for (size_t i = 0; i < aPointClouds.at(nHighIndex).size(); i++)
389 {
391 aPointClouds.at(nHighIndex).at(i), mBestRotation, vBestTranslation);
392 aPointClouds.at(nStartIndex).push_back(aPointClouds.at(nHighIndex).at(i));
393 }
394
395 nHighIndex++;
396 }
397 }
398 }
399
400 ARMARX_VERBOSE_S << "Done";
401
402
403 // return result
404 if (!pFusedHypothesis)
405 {
406 pFusedHypothesis = new CObjectHypothesis();
407 }
408
409 pFusedHypothesis->eType = CObjectHypothesis::eRGBD;
410 pFusedHypothesis->aConfirmedPoints.clear();
411 pFusedHypothesis->aConfirmedPoints.resize(aPointClouds.at(nFusionResultIndex).size());
412 pFusedHypothesis->aVisibleConfirmedPoints.clear();
413 pFusedHypothesis->aVisibleConfirmedPoints.resize(
414 aPointClouds.at(nFusionResultIndex).size());
415
416 for (size_t i = 0; i < aPointClouds.at(nFusionResultIndex).size(); i++)
417 {
418 pFusedHypothesis->aConfirmedPoints.at(i) =
419 CColorICP::ConvertFromXYZRGBI(aPointClouds.at(nFusionResultIndex).at(i));
420 pFusedHypothesis->aVisibleConfirmedPoints.at(i) =
421 CColorICP::ConvertFromXYZRGBI(aPointClouds.at(nFusionResultIndex).at(i));
422 }
423
425 pFusedHypothesis->vCenter,
426 pFusedHypothesis->fMaxExtent);
427 pFusedHypothesis->fMaxExtent = 2 * sqrtf(pFusedHypothesis->fMaxExtent);
428
429 for (int i = 0; i < nParallelityFactor; i++)
430 {
431 delete pColorICPInstances[i];
432 }
433
434 gettimeofday(&tEnd, 0);
435 long tTimeDiff = (1000 * tEnd.tv_sec + tEnd.tv_usec / 1000) -
436 (1000 * tStart.tv_sec + tStart.tv_usec / 1000);
437 ARMARX_VERBOSE_S << "Time for pointcloud fusion: " << tTimeDiff << " ms";
438 }
439
440} // namespace CSegmentedPointCloudFusion
#define M_PI
Definition MathTools.h:17
#define OLP_ICP_COLOR_DISTANCE_WEIGHT
#define OLP_ICP_CUTOFF_DISTANCE
void SetScenePointcloud(std::vector< CPointXYZRGBI > aScenePoints)
Definition ColorICP.cpp:58
static CHypothesisPoint * ConvertFromXYZRGBI(CPointXYZRGBI pPoint)
Definition ColorICP.cpp:358
static void TransformPointXYZRGBI(CPointXYZRGBI &pPoint, Mat3d mRotation, Vec3d vTranslation)
Definition ColorICP.cpp:372
static CPointXYZRGBI ConvertToXYZRGBI(CHypothesisPoint *pPoint)
Definition ColorICP.cpp:343
float SearchObject(const std::vector< CPointXYZRGBI > &aObjectPoints, Mat3d &mRotation, Vec3d &vTranslation, const float fBestDistanceUntilNow=FLT_MAX)
Definition ColorICP.cpp:109
void SetParameters(float fColorWeight=OLP_ICP_COLOR_DISTANCE_WEIGHT, float fCutoffDistance=FLT_MAX, float fConvergenceDelta=0.01f, int nMaxIterations=30, int nKdTreeBucketSize=50)
Definition ColorICP.cpp:238
std::vector< CHypothesisPoint * > aConfirmedPoints
std::vector< CHypothesisPoint * > aVisibleConfirmedPoints
#define ARMARX_VERBOSE_S
Definition Logging.h:207
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
void GetMeanAndVariance(const std::vector< CHypothesisPoint * > &pHypothesisPoints, Vec3d &vMean, float &fVariance)
Definition OLPTools.cpp:772
void FusePointClouds(std::vector< CObjectHypothesis * > aHypotheses, CObjectHypothesis *&pFusedHypothesis)