ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvGBLSensor.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ecvGBLSensor.h"
9 
10 // Local
11 #include "ecvDisplayTools.h"
12 #include "ecvPointCloud.h"
13 #include "ecvProgressDialog.h"
14 #include "ecvSphere.h"
15 
16 // Qt
17 #include <QCoreApplication>
18 
19 // maximum depth buffer dimension (width or height)
20 static const int s_MaxDepthBufferSize = (1 << 14); // 16384
21 
22 enum Errors {
27 };
28 
29 QString ccGBLSensor::GetErrorString(int errorCode) {
30  switch (errorCode) {
31  case ERROR_BAD_INPUT:
32  return "Internal error: bad input";
33  case ERROR_MEMORY:
34  return "Error: not enough memory";
36  return "Error: process cancelled by user";
37  case ERROR_DB_TOO_SMALL:
38  return "Error: depth buffer is void (check input cloud and angular "
39  "steps)";
40  default:
41  assert(false);
42  break;
43  }
44 
45  return QString("unknown error (code: %i)").arg(errorCode);
46 }
47 
48 ccGBLSensor::ccGBLSensor(ROTATION_ORDER rotOrder /*=YAW_THEN_PITCH*/)
49  : ccSensor("TLS/GBL"),
50  m_phiMin(0),
51  m_phiMax(0),
52  m_deltaPhi(0),
53  m_pitchAnglesAreShifted(false),
54  m_thetaMin(0),
55  m_thetaMax(0),
56  m_deltaTheta(0),
57  m_yawAnglesAreShifted(false),
58  m_rotationOrder(rotOrder),
59  m_sensorRange(0),
60  m_uncertainty(static_cast<PointCoordinateType>(0.005)) {
61  // graphic representation
62  lockVisibility(false);
64 }
65 
67  : ccSensor(sensor),
68  m_phiMin(sensor.m_phiMin),
69  m_phiMax(sensor.m_phiMax),
70  m_deltaPhi(sensor.m_deltaPhi),
71  m_pitchAnglesAreShifted(sensor.m_pitchAnglesAreShifted),
72  m_thetaMin(sensor.m_thetaMin),
73  m_thetaMax(sensor.m_thetaMax),
74  m_deltaTheta(sensor.m_deltaTheta),
75  m_yawAnglesAreShifted(sensor.m_yawAnglesAreShifted),
76  m_rotationOrder(sensor.m_rotationOrder),
77  m_sensorRange(sensor.m_sensorRange),
78  m_uncertainty(sensor.m_uncertainty) {}
79 
81 
83  PointCoordinateType maxPhi) {
84  m_phiMin = minPhi;
85  m_phiMax = maxPhi;
86 
87  if (m_phiMax > static_cast<PointCoordinateType>(M_PI))
89 
91 }
92 
94  if (m_deltaPhi != dPhi) {
96  m_deltaPhi = dPhi;
97  }
98 }
99 
101  PointCoordinateType maxTheta) {
102  m_thetaMin = minTehta;
103  m_thetaMax = maxTheta;
104 
105  if (m_thetaMax > static_cast<PointCoordinateType>(M_PI))
106  m_yawAnglesAreShifted = true;
107 
109 }
110 
112  if (m_deltaTheta != dTheta) {
114  m_deltaTheta = dTheta;
115  }
116 }
117 
118 void ccGBLSensor::projectPoint(const CCVector3& sourcePoint,
119  CCVector2& destPoint,
120  PointCoordinateType& depth,
121  double posIndex /*=0*/) const {
122  // project point in sensor world
123  CCVector3 P = sourcePoint;
124 
125  // sensor to world global transformation = sensor position * rigid
126  // transformation
127  ccIndexedTransformation sensorPos; // identity by default
128  if (m_posBuffer)
129  m_posBuffer->getInterpolatedTransformation(posIndex, sensorPos);
130  sensorPos *= m_rigidTransformation;
131 
132  // apply (inverse) global transformation (i.e world to sensor)
133  sensorPos.inverse().apply(P);
134 
135  // convert to 2D sensor field of view + compute its distance
136  switch (m_rotationOrder) {
137  case YAW_THEN_PITCH: {
138  // yaw = angle around Z, starting from 0 in the '+X' direction
139  destPoint.x = atan2(P.y, P.x);
140  // pitch = angle around the lateral axis, between -pi (-Z) to pi
141  // (+Z) by default
142  destPoint.y = atan2(P.z, sqrt(P.x * P.x + P.y * P.y));
143  break;
144  }
145  case PITCH_THEN_YAW: {
146  // FIXME
147  // yaw = angle around Z, starting from 0 in the '+X' direction
148  destPoint.x = -atan2(sqrt(P.y * P.y + P.z * P.z), P.x);
149  // pitch = angle around the lateral axis, between -pi (-Z) to pi
150  // (+Z) by default
151  destPoint.y = -atan2(P.y, P.z);
152  break;
153  }
154  default:
155  assert(false);
156  }
157 
158  // if the yaw angles are shifted
159  if (m_yawAnglesAreShifted && destPoint.x < 0)
160  destPoint.x += static_cast<PointCoordinateType>(2.0 * M_PI);
161  // if the pitch angles are shifted
162  if (m_pitchAnglesAreShifted && destPoint.y < 0)
163  destPoint.y += static_cast<PointCoordinateType>(2.0 * M_PI);
164 
165  depth = P.norm();
166 }
167 
169  PointCoordinateType pitch,
170  unsigned& i,
171  unsigned& j) const {
172  if (m_depthBuffer.zBuff.empty()) {
173  return false;
174  }
175 
176  assert(m_depthBuffer.deltaTheta != 0 && m_depthBuffer.deltaPhi != 0);
177 
178  // yaw
179  if (yaw < m_thetaMin || yaw > m_thetaMax + m_depthBuffer.deltaTheta)
180  return false;
181 
182  i = static_cast<unsigned>(
184  if (i == m_depthBuffer.width) --i;
185  // yaw angles are in the wrong way! (because they are expressed relatively
186  // to the sensor)
187  assert(i < m_depthBuffer.width);
188  i = (m_depthBuffer.width - 1) - i;
189 
190  // pitch
191  if (pitch < m_phiMin || pitch > m_phiMax + m_depthBuffer.deltaPhi)
192  return false;
193  j = static_cast<unsigned>(
194  floor((pitch - m_phiMin) / m_depthBuffer.deltaPhi));
195  if (j == m_depthBuffer.height) --j;
196  assert(j < m_depthBuffer.height);
197 
198  return true;
199 }
200 
203  const NormalGrid& theNorms,
204  double posIndex /*=0*/) const {
205  if (!cloud || theNorms.capacity() == 0) return nullptr;
206 
208  if (size == 0) return nullptr; // depth buffer empty/not initialized!
209 
210  NormalGrid* normalGrid = new NormalGrid;
211  try {
212  static const CCVector3 zeroNormal(0, 0, 0);
213  normalGrid->resize(size, zeroNormal);
214  } catch (const std::bad_alloc&) {
215  // not enough memory
216  delete normalGrid;
217  return nullptr;
218  }
219 
220  // sensor to world global transformation = sensor position * rigid
221  // transformation
222  ccIndexedTransformation sensorPos; // identity by default
223  if (m_posBuffer)
224  m_posBuffer->getInterpolatedTransformation(posIndex, sensorPos);
225  sensorPos *= m_rigidTransformation;
226 
227  // poject each point + normal
228  {
229  cloud->placeIteratorAtBeginning();
230  unsigned pointCount = cloud->size();
231  for (unsigned i = 0; i < pointCount; ++i) {
232  const CCVector3* P = cloud->getNextPoint();
233  const CCVector3& N = theNorms[i];
234 
235  // project point
236  CCVector2 Q;
237  PointCoordinateType depth1;
238  projectPoint(*P, Q, depth1, m_activeIndex);
239 
240  CCVector3 S;
241 
242  CCVector3 U = *P - sensorPos.getTranslationAsVec3D();
243  PointCoordinateType distToSensor = U.norm();
244 
245  if (cloudViewer::GreaterThanEpsilon(distToSensor)) {
246  PointCoordinateType squareS2D = (S.x * S.x + S.y * S.y);
247  if (cloudViewer::GreaterThanEpsilon(squareS2D)) {
248  // and point+normal
249  CCVector3 P2 = *P + CCVector3(N);
250  CCVector2 S2;
251  PointCoordinateType depth2;
252  projectPoint(P2, S2, depth2, m_activeIndex);
253 
254  // normal component along sensor viewing dir.
255  S.z = -N.dot(U) / distToSensor;
256 
257  // deduce other normals components
258  PointCoordinateType coef =
259  sqrt((PC_ONE - S.z * S.z) / squareS2D);
260  S.x = coef * (S2.x - Q.x);
261  S.y = coef * (S2.y - Q.y);
262  } else {
263  S.x = 0;
264  S.y = 0;
265  }
266  } else {
267  S = CCVector3(N);
268  }
269 
270  // project in Z-buffer
271  unsigned x = 0;
272  unsigned y = 0;
273  if (convertToDepthMapCoords(Q.x, Q.y, x, y)) {
274  // add the transformed normal
275  CCVector3& newN = normalGrid->at(y * m_depthBuffer.width + x);
276  newN += S;
277  } else {
278  // shouldn't happen!
279  assert(false);
280  }
281  }
282  }
283 
284  // normalize
285  {
286  for (unsigned i = 0; i < m_depthBuffer.height * m_depthBuffer.width;
287  ++i) {
288  normalGrid->at(i).normalize();
289  }
290  }
291 
292  return normalGrid;
293 }
294 
296  cloudViewer::GenericCloud* cloud, const ColorGrid& theColors) const {
297  if (!cloud || theColors.capacity() == 0) return nullptr;
298 
299  unsigned gridSize = m_depthBuffer.height * m_depthBuffer.width;
300  if (gridSize == 0)
301  return nullptr; // depth buffer empty or not
302  // initialized!
303 
304  // number of points per cell of the depth map
305  std::vector<size_t> pointPerDMCell;
306  try {
307  pointPerDMCell.resize(gridSize, 0);
308  } catch (const std::bad_alloc&) {
309  // not enough memory
310  return nullptr;
311  }
312 
313  // temp. array for accumulation
314  std::vector<ecvColor::Rgbf> colorAccumGrid;
315  {
316  ecvColor::Rgbf blackF(0, 0, 0);
317  try {
318  colorAccumGrid.resize(gridSize, blackF);
319  } catch (const std::bad_alloc&) {
320  // not enough memory
321  return nullptr;
322  }
323  }
324 
325  // final array
326  ColorGrid* colorGrid = new ColorGrid;
327  try {
328  colorGrid->resize(gridSize, ecvColor::black);
329  } catch (const std::bad_alloc&) {
330  delete colorGrid;
331  return nullptr; // not enough memory
332  }
333 
334  // project colors
335  {
336  unsigned pointCount = cloud->size();
337  cloud->placeIteratorAtBeginning();
338  {
339  for (unsigned i = 0; i < pointCount; ++i) {
340  const CCVector3* P = cloud->getNextPoint();
341  CCVector2 Q;
342  PointCoordinateType depth;
343  projectPoint(*P, Q, depth, m_activeIndex);
344 
345  unsigned x, y;
346  if (convertToDepthMapCoords(Q.x, Q.y, x, y)) {
347  unsigned index = y * m_depthBuffer.width + x;
348 
349  // accumulate color
350  const ecvColor::Rgb& srcC = theColors[i];
351  ecvColor::Rgbf& destC = colorAccumGrid[index];
352 
353  destC.r += srcC.r;
354  destC.g += srcC.g;
355  destC.b += srcC.b;
356  ++pointPerDMCell[index];
357  } else {
358  // shouldn't happen!
359  assert(false);
360  }
361  }
362  }
363  }
364 
365  // normalize
366  {
367  for (unsigned i = 0; i < gridSize; ++i) {
368  if (pointPerDMCell[i] != 0) {
369  const ecvColor::Rgbf& srcC = colorAccumGrid[i];
370  ecvColor::Rgb& destC = colorGrid->at(i);
371  destC.r =
372  static_cast<ColorCompType>(srcC.r / pointPerDMCell[i]);
373  destC.g =
374  static_cast<ColorCompType>(srcC.g / pointPerDMCell[i]);
375  destC.b =
376  static_cast<ColorCompType>(srcC.b / pointPerDMCell[i]);
377  }
378  }
379  }
380 
381  return colorGrid;
382 }
383 
386 struct Interval {
388  Interval() : start(-1), span(0) {}
390  int start;
392  int span;
393 
395  template <class T>
396  static Interval FindBiggest(const std::vector<T>& values,
397  T intValue,
398  bool allowLoop = true) {
399  // look for the largest 'empty' part
400  Interval firstEmptyPart, bestEmptyPart, currentEmptyPart;
401 
402  for (size_t i = 0; i < values.size(); ++i) {
403  // no point for the current angle?
404  if (values[i] == intValue) {
405  // new empty part?
406  if (currentEmptyPart.span == 0) {
407  currentEmptyPart.start = static_cast<int>(i);
408  }
409  currentEmptyPart.span++;
410  } else {
411  // current empty part stops (if any)
412  if (currentEmptyPart.span != 0) {
413  // specific case: remember the very first interval (start ==
414  // 0)
415  if (currentEmptyPart.start == 0) {
416  firstEmptyPart = currentEmptyPart;
417  }
418  if (bestEmptyPart.span < currentEmptyPart.span) {
419  bestEmptyPart = currentEmptyPart;
420  }
421  currentEmptyPart = Interval();
422  }
423  }
424  }
425 
426  // specific case: merge the very first and the very last parts
427  if (allowLoop && firstEmptyPart.span != 0 &&
428  currentEmptyPart.span != 0) {
429  currentEmptyPart.span += firstEmptyPart.span;
430  }
431 
432  // last interval
433  if (bestEmptyPart.span < currentEmptyPart.span) {
434  bestEmptyPart = currentEmptyPart;
435  }
436 
437  return bestEmptyPart;
438  }
439 };
440 
442  assert(theCloud);
443  if (!theCloud) {
444  // invalid input parameter
445  return false;
446  }
447 
448  std::vector<bool> nonEmptyAnglesYaw, nonEmptyAnglesPitch;
449  try {
450  nonEmptyAnglesYaw.resize(360, false);
451  nonEmptyAnglesPitch.resize(360, false);
452  } catch (const std::bad_alloc&) {
453  // not enough memory
454  return false;
455  }
456  // force no shift for auto search (we'll fix this later if necessary)
457  m_yawAnglesAreShifted = false;
458  m_pitchAnglesAreShifted = false;
459 
460  unsigned pointCount = theCloud->size();
461 
462  PointCoordinateType minPitch = 0, maxPitch = 0, minYaw = 0, maxYaw = 0;
463  PointCoordinateType maxDepth = 0;
464  {
465  // first project all points to compute the (yaw,ptich) ranges
466  theCloud->placeIteratorAtBeginning();
467  for (unsigned i = 0; i < pointCount; ++i) {
468  const CCVector3* P = theCloud->getNextPoint();
469  CCVector2 Q;
470  PointCoordinateType depth;
471  // Q.x and Q.y are inside [-pi;pi] by default (result of atan2)
472  projectPoint(*P, Q, depth, m_activeIndex);
473 
474  // yaw
475  int angleYaw = static_cast<int>(cloudViewer::RadiansToDegrees(Q.x));
476  assert(angleYaw >= -180 && angleYaw <= 180);
477  if (angleYaw == 180) // 360 degrees warp
478  angleYaw = -180;
479  nonEmptyAnglesYaw[180 + angleYaw] = true;
480  if (i != 0) {
481  if (minYaw > Q.x)
482  minYaw = Q.x;
483  else if (maxYaw < Q.x)
484  maxYaw = Q.x;
485  } else {
486  minYaw = maxYaw = Q.x;
487  }
488 
489  // pitch
490  int anglePitch =
491  static_cast<int>(cloudViewer::RadiansToDegrees(Q.y));
492  assert(anglePitch >= -180 && anglePitch <= 180);
493  if (anglePitch == 180) anglePitch = -180;
494  nonEmptyAnglesPitch[180 + anglePitch] = true;
495  if (i != 0) {
496  if (minPitch > Q.y)
497  minPitch = Q.y;
498  else if (maxPitch < Q.y)
499  maxPitch = Q.y;
500  } else {
501  minPitch = maxPitch = Q.y;
502  }
503 
504  if (depth > maxDepth) maxDepth = depth;
505  }
506  }
507 
508  Interval bestEmptyPartYaw =
509  Interval::FindBiggest<bool>(nonEmptyAnglesYaw, false, true);
510  Interval bestEmptyPartPitch =
511  Interval::FindBiggest<bool>(nonEmptyAnglesPitch, false, true);
512 
514  (bestEmptyPartYaw.start != 0 && bestEmptyPartYaw.span > 1 &&
515  bestEmptyPartYaw.start + bestEmptyPartYaw.span < 360);
517  (bestEmptyPartPitch.start != 0 && bestEmptyPartPitch.span > 1 &&
518  bestEmptyPartPitch.start + bestEmptyPartPitch.span < 360);
519 
521  // we re-project all the points in order to update the boundaries!
522  theCloud->placeIteratorAtBeginning();
523  for (unsigned i = 0; i < pointCount; ++i) {
524  const CCVector3* P = theCloud->getNextPoint();
525  CCVector2 Q;
526  PointCoordinateType depth;
527  projectPoint(*P, Q, depth, m_activeIndex);
528 
529  if (i != 0) {
530  if (minYaw > Q.x)
531  minYaw = Q.x;
532  else if (maxYaw < Q.x)
533  maxYaw = Q.x;
534 
535  if (minPitch > Q.y)
536  minPitch = Q.y;
537  else if (maxPitch < Q.y)
538  maxPitch = Q.y;
539  } else {
540  minYaw = maxYaw = Q.x;
541  minPitch = maxPitch = Q.y;
542  }
543  }
544  }
545 
546  setYawRange(minYaw, maxYaw);
547  setPitchRange(minPitch, maxPitch);
548  setSensorRange(maxDepth);
549 
550  return true;
551 }
552 
554  int& errorCode,
555  ccPointCloud* projectedCloud /*=0*/) {
556  assert(theCloud);
557  if (!theCloud) {
558  // invlalid input parameter
559  errorCode = ERROR_BAD_INPUT;
560  return false;
561  }
562 
563  // clear previous Z-buffer (if any)
565 
566  // init new Z-buffer
567  {
568  PointCoordinateType deltaTheta = m_deltaTheta;
569  PointCoordinateType deltaPhi = m_deltaPhi;
570 
571  // yaw as X
572  int width = static_cast<int>(
574  if (width > s_MaxDepthBufferSize) {
575  deltaTheta = (m_thetaMax - m_thetaMin) /
578  }
579  // pitch as Y
580  int height = static_cast<int>(ceil((m_phiMax - m_phiMin) / m_deltaPhi));
582  deltaPhi = (m_phiMax - m_phiMin) /
585  }
586 
587  if (width <= 0 || height <= 0) {
588  // depth buffer dimensions are too small?!
589  errorCode = ERROR_DB_TOO_SMALL;
590  return false;
591  }
592 
593  unsigned zBuffSize = width * height;
594  try {
595  assert(m_depthBuffer.zBuff.empty());
596  m_depthBuffer.zBuff.resize(zBuffSize, 0);
597  } catch (const std::bad_alloc&) {
598  // not enough memory
599  errorCode = ERROR_MEMORY;
600  return false;
601  }
602 
603  m_depthBuffer.width = static_cast<unsigned>(width);
604  m_depthBuffer.height = static_cast<unsigned>(height);
605  m_depthBuffer.deltaTheta = deltaTheta;
606  m_depthBuffer.deltaPhi = deltaPhi;
607  }
608 
609  unsigned pointCount = theCloud->size();
610 
611  // project points and accumulate them in Z-buffer
612  {
613  if (projectedCloud) {
614  projectedCloud->clear();
615  if (!projectedCloud->reserve(pointCount) ||
616  !projectedCloud->enableScalarField()) {
617  // not enough memory
618  errorCode = ERROR_MEMORY;
620  return false;
621  }
622  }
623 
624  theCloud->placeIteratorAtBeginning();
625  {
626  // progress bar
627  ecvProgressDialog pdlg(true);
628  cloudViewer::NormalizedProgress nprogress(&pdlg, pointCount);
629  pdlg.setMethodTitle(QObject::tr("Depth buffer"));
630  pdlg.setInfo(QObject::tr("Points: %L1").arg(pointCount));
631  pdlg.start();
632  QCoreApplication::processEvents();
633 
634  for (unsigned i = 0; i < pointCount; ++i) {
635  const CCVector3* P = theCloud->getNextPoint();
636  CCVector2 Q;
637  PointCoordinateType depth;
638  projectPoint(*P, Q, depth, m_activeIndex);
639 
640  unsigned x, y;
641  if (convertToDepthMapCoords(Q.x, Q.y, x, y)) {
642  PointCoordinateType& zBuf =
644  zBuf = std::max(zBuf, depth);
645  m_sensorRange = std::max(m_sensorRange, depth);
646  }
647 
648  if (projectedCloud) {
649  projectedCloud->addPoint(CCVector3(Q.x, Q.y, 0));
650  projectedCloud->setPointScalarValue(i, depth);
651  }
652 
653  if (!nprogress.oneStep()) {
654  // cancelled by user
655  errorCode = ERROR_PROC_CANCELLED;
657  return false;
658  }
659  }
660  }
661  }
662 
664 
665  errorCode = 0;
666  return true;
667 }
668 
669 unsigned char ccGBLSensor::checkVisibility(const CCVector3& P) const {
670  if (m_depthBuffer.zBuff.empty()) // no z-buffer?
671  {
672  return POINT_VISIBLE;
673  }
674 
675  // project point
676  CCVector2 Q;
677  PointCoordinateType depth;
678  projectPoint(P, Q, depth, m_activeIndex);
679 
680  // out of sight
681  if (depth > m_sensorRange) {
682  return POINT_OUT_OF_RANGE;
683  }
684 
685  unsigned x, y;
686  if (!convertToDepthMapCoords(Q.x, Q.y, x, y)) {
687  // out of field of view
688  return POINT_OUT_OF_FOV;
689  }
690 
691  // hidden?
692  if (depth > m_depthBuffer.zBuff[y * m_depthBuffer.width + x] *
693  (1.0f + m_uncertainty)) {
694  return POINT_HIDDEN;
695  }
696 
697  return POINT_VISIBLE;
698 }
699 
701  // we draw a little 3d representation of the sensor
702  if (!MACRO_Draw3D(context)) return;
703 
704  // DGM FIXME: this display routine is crap!
706  return;
707  }
708 
709  // build-up the normal representation own 'context'
710  CC_DRAW_CONTEXT cameraContext = context;
711  // force line width
712  cameraContext.currentLineWidth = 2;
713  cameraContext.opacity = 0.2f;
714 
715  // apply rigid transformation
716  ccIndexedTransformation sensorPos;
717  if (!getAbsoluteTransformation(sensorPos, m_activeIndex)) {
718  // no visible position for this index!
719  return;
720  }
721 
722  const double halfHeadSize = 0.3;
723  double scale = static_cast<double>(m_scale);
724 
725  // sensor axes
726  {
727  double axisLength = halfHeadSize * scale;
728  CCVector3d C(0.0, 0.0, 0.0);
729  m_axis.clear();
730  m_axis.points_.push_back(Eigen::Vector3d(C.u));
731  m_axis.points_.push_back(Eigen::Vector3d(C.x + axisLength, C.y, C.z));
732  m_axis.points_.push_back(Eigen::Vector3d(C.x, C.y + axisLength, C.z));
733  m_axis.points_.push_back(Eigen::Vector3d(C.x, C.y, C.z + axisLength));
734 
735  // x
736  m_axis.lines_.push_back(Eigen::Vector2i(0, 1));
738 
739  // y
740  m_axis.lines_.push_back(Eigen::Vector2i(0, 2));
742 
743  // z
744  m_axis.lines_.push_back(Eigen::Vector2i(0, 3));
746  }
747 
748  // sensor head
749  {
750  Eigen::Vector3d minCorner(-halfHeadSize, -halfHeadSize, -halfHeadSize);
751  Eigen::Vector3d maxCorner(halfHeadSize, halfHeadSize, halfHeadSize);
752  minCorner *= scale;
753  maxCorner *= scale;
754  ccBBox bbox(minCorner, maxCorner);
757  }
758 
759  // sensor legs
760  {
761  CCVector3d headConnect =
762  /*headCenter*/ -CCVector3d(0.0, 0.0, halfHeadSize * scale);
763 
764  m_leg.clear();
765  m_leg.points_.push_back(Eigen::Vector3d(headConnect.u));
766  m_leg.points_.push_back(Eigen::Vector3d(-scale, -scale, -scale));
767  m_leg.points_.push_back(Eigen::Vector3d(-scale, scale, -scale));
768  m_leg.points_.push_back(Eigen::Vector3d(scale, 0.0, -scale));
769 
770  m_leg.lines_.push_back(Eigen::Vector2i(0, 1));
772 
773  m_leg.lines_.push_back(Eigen::Vector2i(0, 2));
775 
776  m_leg.lines_.push_back(Eigen::Vector2i(0, 3));
778  }
779 
780  // tranformation
781  {
782  Eigen::Matrix4d transformation = ccGLMatrixd::ToEigenMatrix4(sensorPos);
783  m_obbHead.Transform(transformation);
784  m_leg.Transform(transformation);
785  m_axis.Transform(transformation);
786  }
787 
788  cameraContext.visible = context.visible;
789  cameraContext.viewID = context.viewID;
790  ecvDisplayTools::Draw(cameraContext, this);
791 }
792 
793 ccBBox ccGBLSensor::getOwnBB(bool withGLFeatures /*=false*/) {
794  if (!withGLFeatures) {
795  return ccBBox();
796  }
797 
798  // get sensor position
799  ccIndexedTransformation sensorPos;
800  if (!getAbsoluteTransformation(sensorPos, m_activeIndex)) {
801  return ccBBox();
802  }
803 
804  ccPointCloud cloud;
805  if (!cloud.reserve(8)) {
806  // not enough memory?!
807  return ccBBox();
808  }
809 
818 
819  cloud.applyRigidTransformation(sensorPos);
820  return cloud.getOwnBB(false);
821 }
822 
824  // get sensor position
825  ccIndexedTransformation sensorPos;
826  if (!getAbsoluteTransformation(sensorPos, m_activeIndex)) return ccBBox();
827 
828  trans = sensorPos;
829 
830  return ccBBox(CCVector3(-m_scale, -m_scale, -m_scale),
832 }
833 
835 
837  context.viewID = this->getViewId();
839 }
840 
843  CVLog::Warning("[ccGBLSensor::applyViewport] No associated display!");
844  return false;
845  }
846 
848  if (!getActiveAbsoluteTransformation(trans)) {
849  return false;
850  }
851  // scanner main directions
852  CCVector3d sensorX(trans.data()[0], trans.data()[1], trans.data()[2]);
853  CCVector3d sensorY(trans.data()[4], trans.data()[5], trans.data()[6]);
854  CCVector3d sensorZ(trans.data()[8], trans.data()[9], trans.data()[10]);
855 
856  switch (getRotationOrder()) {
858  double theta = (getMinYaw() + getMaxYaw()) / 2;
859  ccGLMatrixd rotz;
860  rotz.initFromParameters(theta, sensorZ, CCVector3d(0, 0, 0));
861  rotz.applyRotation(sensorX);
862  rotz.applyRotation(sensorY);
863 
864  double phi = 0; //(getMinPitch() + getMaxPitch())/2;
865  ccGLMatrixd roty;
866  roty.initFromParameters(
867  -phi, sensorY,
868  CCVector3d(0, 0, 0)); // theta = 0 corresponds to the
869  // upward vertical direction!
870  roty.applyRotation(sensorX);
871  roty.applyRotation(sensorZ);
872 
873  break;
874  }
876  double phi = (getMinPitch() + getMaxPitch()) / 2;
877  ccGLMatrixd roty;
878  roty.initFromParameters(
879  -phi, sensorY,
880  CCVector3d(0, 0, 0)); // theta = 0 corresponds to the
881  // upward vertical direction!
882  roty.applyRotation(sensorX);
883  roty.applyRotation(sensorZ);
884 
885  double theta = (getMinYaw() + getMaxYaw()) / 2;
886  ccGLMatrixd rotz;
887  rotz.initFromParameters(theta, sensorZ, CCVector3d(0, 0, 0));
888  rotz.applyRotation(sensorX);
889  rotz.applyRotation(sensorY);
890  break;
891  }
892  default:
893  assert(false);
894  break;
895  }
896 
897  // center camera on sensor
898  CCVector3d sensorCenterd = CCVector3d::fromArray(trans.getTranslation());
899  ccGLMatrixd viewMat = ccGLMatrixd::FromViewDirAndUpDir(sensorX, sensorZ);
900  viewMat.invert();
901  viewMat.setTranslation(sensorCenterd);
902  // TODO: can we set the right FOV?
903  // ecvDisplayTools::SetupProjectiveViewport(viewMat, 0, 1.0f, true,
904  // true);
905  ecvDisplayTools::SetupProjectiveViewport(viewMat, 0, 1.0f, true, false);
906 
907  return true;
908 }
909 
910 bool ccGBLSensor::toFile_MeOnly(QFile& out, short dataVersion) const {
911  assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly));
912  if (dataVersion < 38) {
913  assert(false);
914  return false;
915  }
916 
917  if (!ccSensor::toFile_MeOnly(out, dataVersion)) return false;
918 
919  // rotation order (dataVersion>=34)
920  uint32_t rotOrder = m_rotationOrder;
921  if (out.write((const char*)&rotOrder, 4) < 0) return WriteError();
922 
923  // other parameters (dataVersion>=34)
924  QDataStream outStream(&out);
925  outStream << m_phiMin;
926  outStream << m_phiMax;
927  outStream << m_deltaPhi;
928  outStream << m_thetaMin;
929  outStream << m_thetaMax;
930  outStream << m_deltaTheta;
931  outStream << m_sensorRange;
932  outStream << m_uncertainty;
933  outStream << m_scale;
934 
935  // other parameters (dataVersion>=38)
936  outStream << m_pitchAnglesAreShifted;
937  outStream << m_yawAnglesAreShifted;
938 
939  return true;
940 }
941 
943  // GBL sensor requires version 38+
944  return std::max(static_cast<short>(38),
946 }
947 
949  short dataVersion,
950  int flags,
951  LoadedIDMap& oldToNewIDMap) {
952  if (!ccSensor::fromFile_MeOnly(in, dataVersion, flags, oldToNewIDMap))
953  return false;
954 
955  // rotation order (dataVersion>=34)
956  uint32_t rotOrder = 0;
957  if (in.read((char*)&rotOrder, 4) < 0) return ReadError();
958  m_rotationOrder = static_cast<ROTATION_ORDER>(rotOrder);
959 
960  // parameters (dataVersion>=34)
961  QDataStream inStream(&in);
965  1);
967  1);
969  1);
971  1);
972  if (dataVersion < 38) {
973  ScalarType sensorRange{};
974  ScalarType uncertainty{};
976  &sensorRange, 1);
978  &uncertainty, 1);
979  m_sensorRange = static_cast<PointCoordinateType>(sensorRange);
980  m_uncertainty = static_cast<PointCoordinateType>(uncertainty);
981  } else {
983  &m_sensorRange, 1);
985  &m_uncertainty, 1);
986  }
988 
989  // other parameters (dataVersion>=38)
990  if (dataVersion >= 38) {
991  inStream >> m_pitchAnglesAreShifted;
992  inStream >> m_yawAnglesAreShifted;
993  }
994 
995  return true;
996 }
constexpr unsigned char POINT_VISIBLE
Definition: CVConst.h:92
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Definition: CVConst.h:67
constexpr unsigned char POINT_HIDDEN
Definition: CVConst.h:94
constexpr double M_PI
Pi.
Definition: CVConst.h:19
constexpr unsigned char POINT_OUT_OF_FOV
Definition: CVConst.h:98
constexpr unsigned char POINT_OUT_OF_RANGE
Definition: CVConst.h:96
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Definition: CVGeom.h:804
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int width
int size
int height
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:36
Type y
Definition: CVGeom.h:36
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
Type norm() const
Returns vector norm.
Definition: CVGeom.h:424
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
Bounding box structure.
Definition: ecvBBox.h:25
unsigned height
Buffer height.
PointCoordinateType deltaTheta
Yaw step (may differ from the sensor's)
std::vector< PointCoordinateType > zBuff
Z-Buffer grid.
void clear()
Clears the buffer.
PointCoordinateType deltaPhi
Pitch step (may differ from the sensor's)
unsigned width
Buffer width.
virtual void lockVisibility(bool state)
Locks/unlocks visibility.
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
void projectPoint(const CCVector3 &sourcePoint, CCVector2 &destPoint, PointCoordinateType &depth, double posIndex=0) const
Projects a point in the sensor world.
bool computeDepthBuffer(cloudViewer::GenericCloud *cloud, int &errorCode, ccPointCloud *projectedCloud=nullptr)
PointCoordinateType m_thetaMin
Minimal yaw limit (in radians)
Definition: ecvGBLSensor.h:269
PointCoordinateType m_deltaTheta
Yaw step (in radians)
Definition: ecvGBLSensor.h:274
PointCoordinateType m_thetaMax
Maximal yaw limit (in radians)
Definition: ecvGBLSensor.h:272
ccBBox getOwnFitBB(ccGLMatrix &trans) override
Returns best-fit bounding-box (if available)
bool m_yawAnglesAreShifted
Definition: ecvGBLSensor.h:277
ROTATION_ORDER m_rotationOrder
Mirrors rotation order.
Definition: ecvGBLSensor.h:280
PointCoordinateType getMinPitch() const
Returns the minimal pitch limit (in radians)
Definition: ecvGBLSensor.h:94
ROTATION_ORDER
The order of inner-rotations of the sensor (body/mirrors)
Definition: ecvGBLSensor.h:33
PointCoordinateType m_phiMin
Minimal pitch limit (in radians)
Definition: ecvGBLSensor.h:257
PointCoordinateType m_deltaPhi
Pitch step (in radians)
Definition: ecvGBLSensor.h:262
bool m_pitchAnglesAreShifted
Definition: ecvGBLSensor.h:265
PointCoordinateType getMinYaw() const
Returns the minimal yaw limit (in radians)
Definition: ecvGBLSensor.h:119
void setYawRange(PointCoordinateType minTheta, PointCoordinateType maxTheta)
Sets the yaw scanning limits.
std::vector< CCVector3 > NormalGrid
2D grid of normals
Definition: ecvGBLSensor.h:180
ColorGrid * projectColors(cloudViewer::GenericCloud *cloud, const ColorGrid &rgbColors) const
bool convertToDepthMapCoords(PointCoordinateType yaw, PointCoordinateType pitch, unsigned &i, unsigned &j) const
ccGBLSensor(ROTATION_ORDER rotOrder=YAW_THEN_PITCH)
Default constructor.
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
void setPitchRange(PointCoordinateType minPhi, PointCoordinateType maxPhi)
Sets the pitch scanning limits.
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
NormalGrid * projectNormals(cloudViewer::GenericCloud *cloud, const NormalGrid &norms, double posIndex=0) const
Projects a set of point cloud normals in the sensor world.
cloudViewer::geometry::LineSet m_leg
Definition: ecvGBLSensor.h:291
bool computeAutoParameters(cloudViewer::GenericCloud *theCloud)
Computes angular parameters automatically (all but the angular steps!)
static QString GetErrorString(int errorCode)
Returns the error string corresponding to an error code.
PointCoordinateType m_phiMax
Maximal pitch limit (in radians)
Definition: ecvGBLSensor.h:260
std::vector< ecvColor::Rgb > ColorGrid
2D grid of colors
Definition: ecvGBLSensor.h:195
void drawMeOnly(CC_DRAW_CONTEXT &context) override
Draws the entity only (not its children)
bool applyViewport() override
Apply sensor 'viewport' to a 3D view.
short minimumFileVersion_MeOnly() const override
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
unsigned char checkVisibility(const CCVector3 &P) const override
void setSensorRange(PointCoordinateType range)
Sets the sensor max. range.
Definition: ecvGBLSensor.h:142
PointCoordinateType getMaxYaw() const
Returns the maximal yaw limit (in radians)
Definition: ecvGBLSensor.h:122
ccDepthBuffer m_depthBuffer
Associated Z-buffer.
Definition: ecvGBLSensor.h:288
void setYawStep(PointCoordinateType dTheta)
Sets the yaw step.
ecvOrientedBBox m_obbHead
Definition: ecvGBLSensor.h:290
PointCoordinateType m_uncertainty
Z-buffer uncertainty.
Definition: ecvGBLSensor.h:285
virtual void hideShowDrawings(CC_DRAW_CONTEXT &context) override
PointCoordinateType m_sensorRange
Sensor max range.
Definition: ecvGBLSensor.h:283
ROTATION_ORDER getRotationOrder() const
Returns the sensor internal rotations order.
Definition: ecvGBLSensor.h:156
void clearDepthBuffer()
Removes the associated depth buffer.
cloudViewer::geometry::LineSet m_axis
Definition: ecvGBLSensor.h:292
PointCoordinateType getMaxPitch() const
Returns the maximal pitch limit (in radians)
Definition: ecvGBLSensor.h:97
void setPitchStep(PointCoordinateType dPhi)
Sets the pitch step.
virtual void clearDrawings() override
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
void applyRotation(Vector3Tpl< float > &vec) const
Applies rotation only to a 3D vector (in place) - float version.
T * getTranslation()
Retruns a pointer to internal translation.
T * data()
Returns a pointer to internal data.
static ccGLMatrixTpl< double > FromViewDirAndUpDir(const Vector3Tpl< double > &forward, const Vector3Tpl< double > &up)
Generates a 'viewing' matrix from a looking vector and a 'up' direction.
void invert()
Inverts transformation.
void apply(float vec[3]) const
Applies transformation to a 3D vector (in place) - float version.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
void initFromParameters(T alpha_rad, const Vector3Tpl< T > &axis3D, const Vector3Tpl< T > &t3D)
Inits transformation from a rotation axis, an angle and a translation.
static Eigen::Matrix< double, 4, 4 > ToEigenMatrix4(const ccGLMatrixTpl< float > &mat)
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
@ SELECTION_FIT_BBOX
Definition: ecvHObject.h:607
QString getViewId() const
Definition: ecvHObject.h:225
virtual void setSelectionBehavior(SelectionBehavior mode)
Sets selection behavior (when displayed)
Definition: ecvHObject.h:616
bool getInterpolatedTransformation(double index, ccIndexedTransformation &trans, double maxIndexDistForInterpolation=DBL_MAX) const
ccIndexedTransformation inverse() const
Returns inverse transformation.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
void clear() override
Clears the entity from all its points and features.
virtual void applyRigidTransformation(const ccGLMatrix &trans) override
Applies a rigid transformation (rotation + translation)
Generic sensor interface.
Definition: ecvSensor.h:27
short minimumFileVersion_MeOnly() const override
Definition: ecvSensor.cpp:155
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
Definition: ecvSensor.cpp:161
PointCoordinateType m_scale
Sensor graphic representation scale.
Definition: ecvSensor.h:169
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
Definition: ecvSensor.cpp:123
ccIndexedTransformationBuffer * m_posBuffer
Positions buffer (optional)
Definition: ecvSensor.h:152
ccGLMatrix m_rigidTransformation
Rigid transformation between this sensor and its associated positions.
Definition: ecvSensor.h:158
double m_activeIndex
Active index (for displayed position, etc.)
Definition: ecvSensor.h:161
bool getAbsoluteTransformation(ccIndexedTransformation &trans, double index) const
Definition: ecvSensor.cpp:78
bool getActiveAbsoluteTransformation(ccIndexedTransformation &trans) const
Gets currently active absolute transformation.
Definition: ecvSensor.cpp:90
ecvColor::Rgb m_color
Color of the sensor.
Definition: ecvSensor.h:166
QMultiMap< unsigned, unsigned > LoadedIDMap
Map of loaded unique IDs (old ID --> new ID)
static bool ReadError()
Sends a custom error message (read error) and returns 'false'.
static bool WriteError()
Sends a custom error message (write error) and returns 'false'.
static void ScalarsFromDataStream(QDataStream &stream, int flags, ScalarType *out, unsigned count=1)
static void CoordsFromDataStream(QDataStream &stream, int flags, PointCoordinateType *out, unsigned count=1)
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
bool oneStep()
Increments total progress value of a single unit.
void SetColor(const Eigen::Vector3d &color)
Sets the bounding box color.
void setPointScalarValue(unsigned pointIndex, ScalarType value) override
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
bool enableScalarField() override
Definition: PointCloudTpl.h:77
std::vector< Eigen::Vector3d > points_
Points coordinates.
Definition: LineSet.h:156
std::vector< Eigen::Vector3d > colors_
RGB colors of lines.
Definition: LineSet.h:160
std::vector< Eigen::Vector2i > lines_
Lines denoted by the index of points forming the line.
Definition: LineSet.h:158
virtual LineSet & Transform(const Eigen::Matrix4d &transformation) override
Apply transformation (4x4 matrix) to the geometry coordinates.
Definition: LineSet.cpp:67
RGB color structure.
Definition: ecvColorTypes.h:49
static Eigen::Vector3d ToEigen(const Type col[3])
Definition: ecvColorTypes.h:72
static void RemoveEntities(const ccHObject *obj)
static void Draw(const CC_DRAW_CONTEXT &context, const ccHObject *obj)
static bool HideShowEntities(const ccHObject *obj, bool visible)
static QWidget * GetCurrentScreen()
static void SetupProjectiveViewport(const ccGLMatrixd &cameraMatrix, float fov_deg=0.0f, float ar=1.0f, bool viewerBasedPerspective=true, bool bubbleViewMode=false)
static ecvOrientedBBox CreateFromAxisAlignedBoundingBox(const ccBBox &aabox)
virtual ecvOrientedBBox & Transform(const Eigen::Matrix4d &transformation) override
Apply transformation (4x4 matrix) to the geometry coordinates.
Graphical progress indicator (thread-safe)
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void start() override
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
#define MACRO_Draw3D(context)
static const int s_MaxDepthBufferSize
Errors
@ ERROR_PROC_CANCELLED
@ ERROR_MEMORY
@ ERROR_DB_TOO_SMALL
@ ERROR_BAD_INPUT
ImGuiContext * context
Definition: Window.cpp:76
normal_z y
normal_z x
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
float RadiansToDegrees(int radians)
Convert radians to degrees.
Definition: CVMath.h:71
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37
constexpr Rgb black(0, 0, 0)
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb green(0, MAX, 0)
constexpr Rgb yellow(MAX, MAX, 0)
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
int span
Interval span.
int start
Interval start index.
static Interval FindBiggest(const std::vector< T > &values, T intValue, bool allowLoop=true)
Finds the biggest contiguous interval.
Interval()
Default constructor.
Display context.
unsigned char currentLineWidth