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
OLP_ICP_COLOR_DISTANCE_WEIGHT
#define OLP_ICP_COLOR_DISTANCE_WEIGHT
Definition: ObjectLearningByPushingDefinitions.h:252
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:737
COLPTools::GetMeanAndVariance
void GetMeanAndVariance(const std::vector< CHypothesisPoint * > &pHypothesisPoints, Vec3d &vMean, float &fVariance)
Definition: OLPTools.cpp:772
CSegmentedPointCloudFusion::FusePointClouds
void FusePointClouds(std::vector< CObjectHypothesis * > aHypotheses, CObjectHypothesis *&pFusedHypothesis)
Definition: SegmentedPointCloudFusion.cpp:38
ColorICP.h
OLP_ICP_CUTOFF_DISTANCE
#define OLP_ICP_CUTOFF_DISTANCE
Definition: ObjectLearningByPushingDefinitions.h:254
CObjectHypothesis::eType
eObjectType eType
Definition: ObjectHypothesis.h:292
CColorICP::SetScenePointcloud
void SetScenePointcloud(std::vector< CPointXYZRGBI > aScenePoints)
Definition: ColorICP.cpp:58
CColorICP::SetParameters
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
CColorICP::SearchObject
float SearchObject(const std::vector< CPointXYZRGBI > &aObjectPoints, Mat3d &mRotation, Vec3d &vTranslation, const float fBestDistanceUntilNow=FLT_MAX)
Definition: ColorICP.cpp:109
CObjectHypothesis::eRGBD
@ eRGBD
Definition: ObjectHypothesis.h:287
OLPTools.h
CSegmentedPointCloudFusion
Definition: SegmentedPointCloudFusion.cpp:35
M_PI
#define M_PI
Definition: MathTools.h:17
CObjectHypothesis::vCenter
Vec3d vCenter
Definition: ObjectHypothesis.h:311
CObjectHypothesis::aVisibleConfirmedPoints
std::vector< CHypothesisPoint * > aVisibleConfirmedPoints
Definition: ObjectHypothesis.h:297
SegmentedPointCloudFusion.h
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
CObjectHypothesis
Definition: ObjectHypothesis.h:244
CObjectHypothesis::fMaxExtent
float fMaxExtent
Definition: ObjectHypothesis.h:319
CColorICP::ConvertToXYZRGBI
static CPointXYZRGBI ConvertToXYZRGBI(CHypothesisPoint *pPoint)
Definition: ColorICP.cpp:343
CObjectHypothesis::aConfirmedPoints
std::vector< CHypothesisPoint * > aConfirmedPoints
Definition: ObjectHypothesis.h:295
ARMARX_VERBOSE_S
#define ARMARX_VERBOSE_S
Definition: Logging.h:207
CColorICP
Definition: ColorICP.h:33
Logging.h
CColorICP::TransformPointXYZRGBI
static void TransformPointXYZRGBI(CPointXYZRGBI &pPoint, Mat3d mRotation, Vec3d vTranslation)
Definition: ColorICP.cpp:372
CColorICP::ConvertFromXYZRGBI
static CHypothesisPoint * ConvertFromXYZRGBI(CPointXYZRGBI pPoint)
Definition: ColorICP.cpp:358