ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvPointCloudInterpolator.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 
9 
10 // CV_DB_LIB
11 #include "ecvPointCloud.h"
12 #include "ecvScalarField.h"
13 
14 // cloudViewer
15 #include <DgmOctree.h>
18 
19 struct SFPair {
21  cloudViewer::ScalarField* sfOut = 0)
22  : in(sfIn), out(sfOut) {}
25 };
26 
28  void** additionalParameters,
30  // additional parameters
31  // const ccPointCloud* srcCloud =
32  // reinterpret_cast<ccPointCloud*>(additionalParameters[0]);
33  const cloudViewer::DgmOctree* srcOctree =
34  reinterpret_cast<cloudViewer::DgmOctree*>(additionalParameters[1]);
35  std::vector<SFPair>* scalarFields =
36  reinterpret_cast<std::vector<SFPair>*>(additionalParameters[2]);
38  reinterpret_cast<const ccPointCloudInterpolator::Parameters*>(
39  additionalParameters[3]);
40 
41  bool normalDistWeighting = false;
42  double interpSigma2x2 = 0;
44  interpSigma2x2 = 2 * params->sigma * params->sigma;
45  normalDistWeighting = (interpSigma2x2 > 0);
46  }
47 
48  // structure for nearest neighbors search
49  bool useKNN = (params->method ==
52  {
53  nNSS.level = cell.level;
54  if (useKNN) {
55  nNSS.minNumberOfNeighbors = params->knn;
56  }
57  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level,
58  nNSS.cellPos, true);
60  nNSS.cellCenter);
61  }
62 
63  std::vector<double> sumValues;
64  size_t sfCount = scalarFields->size();
65  assert(sfCount != 0);
66  sumValues.resize(sfCount);
67 
68  // for each point of the current cell (destination octree) we look for its
69  // nearest neighbours in the source cloud
70  unsigned pointCount = cell.points->size();
71  for (unsigned i = 0; i < pointCount; i++) {
72  unsigned outPointIndex = cell.points->getPointGlobalIndex(i);
73  cell.points->getPoint(i, nNSS.queryPoint);
74 
75  // look for neighbors (either inside a sphere or the k nearest ones)
76  // warning: there may be more points at the end of
77  // nNSS.pointsInNeighbourhood than the actual nearest neighbors
78  // (neighborCount)!
79  unsigned neighborCount = 0;
80 
81  if (useKNN) {
82  neighborCount =
83  srcOctree->findNearestNeighborsStartingFromCell(nNSS);
84  neighborCount = std::min(neighborCount, params->knn);
85  } else {
86  neighborCount = srcOctree->findNeighborsInASphereStartingFromCell(
87  nNSS, params->radius, false);
88  }
89 
90  if (neighborCount) {
92  // median
93  std::vector<ScalarType> values;
94  values.resize(neighborCount);
95  unsigned medianIndex = std::max(neighborCount / 2, 1u) - 1;
96 
97  for (unsigned j = 0; j < sfCount; ++j) {
98  const cloudViewer::ScalarField* sf = scalarFields->at(j).in;
99  for (unsigned k = 0; k < neighborCount; ++k) {
101  nNSS.pointsInNeighbourhood[k];
102  values[k] = sf->getValue(P.pointIndex);
103  }
104  std::sort(values.begin(), values.end());
105 
106  ScalarType median = values[medianIndex];
107  scalarFields->at(j).out->setValue(outPointIndex, median);
108  }
109  } else // average or weighted average
110  {
111  double sumW = 0;
112  std::fill(sumValues.begin(), sumValues.end(), 0);
113  for (unsigned k = 0; k < neighborCount; ++k) {
115  nNSS.pointsInNeighbourhood[k];
116  double w = 1.0;
117  if (normalDistWeighting) {
118  w = exp(-P.squareDistd / interpSigma2x2);
119  }
120  sumW += w;
121  for (unsigned j = 0; j < sfCount; ++j) {
122  sumValues[j] += w * scalarFields->at(j).in->getValue(
123  P.pointIndex);
124  }
125  }
126 
127  if (sumW > 0) {
128  for (unsigned j = 0; j < sfCount; ++j) {
129  ScalarType s =
130  static_cast<ScalarType>(sumValues[j] / sumW);
131  scalarFields->at(j).out->setValue(outPointIndex, s);
132  }
133  } else {
134  // we assume the scalar fields have all been initialized to
135  // NAN_VALUE
136  }
137  }
138  } else {
139  // we assume the scalar fields have all been initialized to
140  // NAN_VALUE
141  }
142 
143  if (nProgress && !nProgress->oneStep()) {
144  return false;
145  }
146  }
147 
148  return true;
149 }
150 
152  ccPointCloud* destCloud,
153  ccPointCloud* srcCloud,
154  const std::vector<int>& inSFIndexes,
155  const Parameters& params,
156  cloudViewer::GenericProgressCallback* progressCb /*=0*/,
157  unsigned char octreeLevel /*=0*/) {
158  if (!destCloud || !srcCloud || srcCloud->size() == 0 ||
159  srcCloud->getNumberOfScalarFields() == 0) {
161  "[InterpolateScalarFieldsFrom] Invalid/empty input cloud(s)!");
162  return false;
163  }
164 
165  // check that both bounding boxes intersect!
166  ccBBox box = destCloud->getOwnBB();
167  ccBBox otherBox = srcCloud->getOwnBB();
168 
169  CCVector3 dimSum = box.getDiagVec() + otherBox.getDiagVec();
170  CCVector3 dist = box.getCenter() - otherBox.getCenter();
171  if (fabs(dist.x) > dimSum.x / 2 || fabs(dist.y) > dimSum.y / 2 ||
172  fabs(dist.z) > dimSum.z / 2) {
174  "[InterpolateScalarFieldsFrom] Clouds are too far from each "
175  "other! Can't proceed.");
176  return false;
177  }
178 
179  // now copy the scalar fields
180  bool overwrite = false;
181  std::vector<SFPair> scalarFields;
182  try {
183  scalarFields.reserve(inSFIndexes.size());
184  } catch (const std::bad_alloc&) {
185  CVLog::Error("Not enough memory");
186  return false;
187  }
188  for (size_t i = 0; i < inSFIndexes.size(); ++i) {
189  int inSFIndex = inSFIndexes[i];
190  if (inSFIndex < 0 ||
191  inSFIndex >=
192  static_cast<int>(srcCloud->getNumberOfScalarFields())) {
193  // invalid index
194  CVLog::Warning(QString("[InterpolateScalarFieldsFrom] Source cloud "
195  "has no scalar field with index #%1")
196  .arg(inSFIndex));
197  assert(false);
198  return false;
199  }
200 
201  const char* sfName = srcCloud->getScalarFieldName(inSFIndex);
202  int outSFIndex = destCloud->getScalarFieldIndexByName(sfName);
203  if (outSFIndex < 0) {
204  outSFIndex = destCloud->addScalarField(sfName);
205  if (outSFIndex < 0) {
206  CVLog::Error("Not enough memory!");
207  return false;
208  }
209  } else {
210  overwrite = true;
211  }
212 
213  cloudViewer::ScalarField* inSF = srcCloud->getScalarField(inSFIndex);
214  cloudViewer::ScalarField* outSF = destCloud->getScalarField(outSFIndex);
215  scalarFields.push_back(SFPair(inSF, outSF));
216 
217  outSF->fill(NAN_VALUE);
218  }
219 
220  if (params.method == Parameters::NEAREST_NEIGHBOR) {
221  // compute the closest-point set of 'this cloud' relatively to 'input
222  // cloud' (to get a mapping between the resulting vertices and the input
223  // points)
224  QSharedPointer<cloudViewer::ReferenceCloud> CPSet =
225  destCloud->computeCPSet(*srcCloud, progressCb, octreeLevel);
226  if (!CPSet) {
227  return false;
228  }
229 
230  unsigned CPSetSize = CPSet->size();
231  assert(CPSetSize == destCloud->size());
232 
233  // now copy the scalar fields
234  for (SFPair& sfPair : scalarFields) {
235  for (unsigned i = 0; i < CPSetSize; ++i) {
236  unsigned pointIndex = CPSet->getPointGlobalIndex(i);
237  sfPair.out->setValue(i, sfPair.in->getValue(pointIndex));
238  }
239  }
240  } else {
241  if ((params.method == Parameters::K_NEAREST_NEIGHBORS &&
242  params.knn == 0) ||
243  (params.method == Parameters::RADIUS && params.radius <= 0)) {
244  // invalid input
245  CVLog::Warning("[InterpolateScalarFieldsFrom] Invalid input");
246  assert(false);
247  return false;
248  }
249 
250  assert(srcCloud && destCloud);
251 
252  // we spatially 'synchronize' the octrees
253  cloudViewer::DgmOctree *_srcOctree = 0, *_destOctree = 0;
256  srcCloud, destCloud, _srcOctree, _destOctree,
257  /*maxSearchDist*/ 0, progressCb);
258 
259  QScopedPointer<cloudViewer::DgmOctree> srcOctree(_srcOctree),
260  destOctree(_destOctree);
261 
263  // not enough memory (or invalid input)
265  "[InterpolateScalarFieldsFrom] Failed to build the "
266  "octrees");
267  return false;
268  }
269 
270  if (octreeLevel == 0) {
271  if (params.method ==
273  octreeLevel =
274  srcOctree->findBestLevelForAGivenPopulationPerCell(
275  params.knn);
276  } else {
277  octreeLevel =
278  srcOctree
279  ->findBestLevelForAGivenNeighbourhoodSizeExtraction(
280  params.radius);
281  }
282  }
283 
284  try {
285  // additional parameters
286  void* additionalParameters[] = {
287  reinterpret_cast<void*>(srcCloud),
288  reinterpret_cast<void*>(srcOctree.data()),
289  reinterpret_cast<void*>(&scalarFields), (void*)(&params)};
290 
291  if (destOctree->executeFunctionForAllCellsAtLevel(
292  octreeLevel, cellSFInterpolator, additionalParameters,
293  true, progressCb, "Scalar field interpolation",
294  0) == 0) {
295  // something went wrong
297  "[InterpolateScalarFieldsFrom] Failed to perform the "
298  "interpolation");
299  return false;
300  }
301  } catch (const std::bad_alloc&) {
302  // not enough memory
303  CVLog::Warning("[InterpolateScalarFieldsFrom] Not enough memory");
304  return false;
305  }
306  }
307 
308  // now copy the scalar fields
309  for (SFPair& sfPair : scalarFields) {
310  sfPair.out->computeMinAndMax();
311  }
312 
313  if (overwrite) {
315  "[InterpolateScalarFieldsFrom] Some scalar fields with the "
316  "same names have been overwritten");
317  }
318 
319  // We must update the VBOs
320  destCloud->colorsHaveChanged();
321 
322  return true;
323 }
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Definition: CVConst.h:76
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Bounding box structure.
Definition: ecvBBox.h:25
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
static bool InterpolateScalarFieldsFrom(ccPointCloud *destCloud, ccPointCloud *srccloud, const std::vector< int > &sfIndexes, const Parameters &params, cloudViewer::GenericProgressCallback *progressCb=0, unsigned char octreeLevel=0)
Interpolate scalar fields from another cloud.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
void colorsHaveChanged()
QSharedPointer< cloudViewer::ReferenceCloud > computeCPSet(ccGenericPointCloud &otherCloud, cloudViewer::GenericProgressCallback *progressCb=nullptr, unsigned char octreeLevel=0)
Computes the closest point of this cloud relatively to another cloud.
Vector3Tpl< T > getDiagVec() const
Returns diagonal vector.
Definition: BoundingBox.h:169
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
The octree structure used throughout the library.
Definition: DgmOctree.h:39
void getCellPos(CellCode code, unsigned char level, Tuple3i &cellPos, bool isCodeTruncated) const
Definition: DgmOctree.cpp:498
unsigned findNearestNeighborsStartingFromCell(NearestNeighboursSearchStruct &nNSS, bool getOnlyPointsWithValidScalar=false) const
Definition: DgmOctree.cpp:1655
int findNeighborsInASphereStartingFromCell(NearestNeighboursSearchStruct &nNSS, double radius, bool sortValues=true) const
Advanced form of the nearest neighbours search algorithm (in a sphere)
Definition: DgmOctree.cpp:2479
void computeCellCenter(CellCode code, unsigned char level, CCVector3 &center, bool isCodeTruncated=false) const
Definition: DgmOctree.h:862
SOReturnCode
Return codes for DistanceComputationTools::synchronizeOctrees.
static SOReturnCode synchronizeOctrees(GenericIndexedCloudPersist *comparedCloud, GenericIndexedCloudPersist *referenceCloud, DgmOctree *&comparedOctree, DgmOctree *&referenceOctree, PointCoordinateType maxSearchDist=0, GenericProgressCallback *progressCb=nullptr)
Synchronizes (and re-build if necessary) two octrees.
bool oneStep()
Increments total progress value of a single unit.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
unsigned size() const override
Definition: PointCloudTpl.h:38
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
void fill(ScalarType fillValue=0)
Fills the array with a particular value.
Definition: ScalarField.h:77
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
bool cellSFInterpolator(const cloudViewer::DgmOctree::octreeCell &cell, void **additionalParameters, cloudViewer::NormalizedProgress *nProgress)
cloudViewer::NormalizedProgress * nProgress
unsigned char octreeLevel
const cloudViewer::ScalarField * in
SFPair(const cloudViewer::ScalarField *sfIn=0, cloudViewer::ScalarField *sfOut=0)
cloudViewer::ScalarField * out
Generic interpolation parameters.
unsigned char level
Level of subdivision of the octree at which to start the search.
Definition: DgmOctree.h:171
Tuple3i cellPos
Position in the octree of the cell including the query point.
Definition: DgmOctree.h:184
CCVector3 cellCenter
Coordinates of the center of the cell including the query point.
Definition: DgmOctree.h:189
unsigned minNumberOfNeighbors
Minimal number of neighbours to find.
Definition: DgmOctree.h:177
Structure used during nearest neighbour search.
Definition: DgmOctree.h:101
double squareDistd
Point associated distance value.
Definition: DgmOctree.h:107
Octree cell descriptor.
Definition: DgmOctree.h:354
ReferenceCloud * points
Set of points lying inside this cell.
Definition: DgmOctree.h:365
const DgmOctree * parentOctree
Octree to which the cell belongs.
Definition: DgmOctree.h:359
unsigned char level
Cell level of subdivision.
Definition: DgmOctree.h:367
CellCode truncatedCode
Truncated cell code.
Definition: DgmOctree.h:361