ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
LasScalarFieldLoader.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 "LasScalarFieldLoader.h"
9 
10 // qCC_db
11 #include <ecvScalarField.h>
12 // System
13 #include <utility>
14 
15 // TODO take by move
16 LasScalarFieldLoader::LasScalarFieldLoader(std::vector<LasScalarField>& standardScalarFields,
17  std::vector<LasExtraScalarField>& extraScalarFields,
18  ccPointCloud& pointCloud)
19  : m_standardFields(standardScalarFields)
20  , m_extraScalarFields(extraScalarFields)
21 {
22  createScalarFieldsForExtraBytes(pointCloud);
23 }
24 
26  const laszip_point& currentPoint)
27 {
29  for (LasScalarField& lasScalarField : m_standardFields)
30  {
31  switch (lasScalarField.id)
32  {
34  error = handleScalarField(lasScalarField, pointCloud, currentPoint.intensity);
35  break;
37  error = handleScalarField(lasScalarField, pointCloud, currentPoint.return_number);
38  break;
40  error = handleScalarField(lasScalarField, pointCloud, currentPoint.number_of_returns);
41  break;
43  error = handleScalarField(lasScalarField, pointCloud, currentPoint.scan_direction_flag);
44  break;
46  error = handleScalarField(lasScalarField, pointCloud, currentPoint.edge_of_flight_line);
47  break;
49  error = handleScalarField(lasScalarField, pointCloud, currentPoint.classification);
50  break;
52  error = handleScalarField(lasScalarField, pointCloud, currentPoint.synthetic_flag);
53  break;
55  error = handleScalarField(lasScalarField, pointCloud, currentPoint.keypoint_flag);
56  break;
58  error = handleScalarField(lasScalarField, pointCloud, currentPoint.withheld_flag);
59  break;
61  error = handleScalarField(lasScalarField, pointCloud, currentPoint.scan_angle_rank);
62  break;
64  error = handleScalarField(lasScalarField, pointCloud, currentPoint.user_data);
65  break;
67  error = handleScalarField(lasScalarField, pointCloud, currentPoint.point_source_ID);
68  break;
70  error = handleGpsTime(lasScalarField, pointCloud, currentPoint.gps_time);
71  break;
73  error = handleScalarField(
74  lasScalarField, pointCloud, currentPoint.extended_scan_angle * SCAN_ANGLE_SCALE);
75  break;
77  error = handleScalarField(lasScalarField, pointCloud, currentPoint.extended_scanner_channel);
78  break;
80  error =
81  handleScalarField(lasScalarField, pointCloud, currentPoint.extended_classification_flags & LasDetails::OVERLAP_FLAG_BIT_MASK);
82  break;
84  error = handleScalarField(lasScalarField, pointCloud, currentPoint.extended_classification);
85  break;
87  error = handleScalarField(lasScalarField, pointCloud, currentPoint.extended_return_number);
88  break;
90  error = handleScalarField(lasScalarField, pointCloud, currentPoint.extended_number_of_returns);
91  break;
93  error = handleScalarField(lasScalarField, pointCloud, currentPoint.rgb[3]);
94  break;
95  }
96 
97  if (error != CC_FERR_NO_ERROR)
98  {
99  return error;
100  }
101  }
102 
103  return CC_FERR_NO_ERROR;
104 }
105 
106 CC_FILE_ERROR LasScalarFieldLoader::handleRGBValue(ccPointCloud& pointCloud, const laszip_point& currentPoint)
107 {
108  if (!pointCloud.hasColors())
109  {
110  uint16_t currentOredRGB = currentPoint.rgb[0] | currentPoint.rgb[1] | currentPoint.rgb[2];
111  if (m_ignoreFieldsWithDefaultValues && currentOredRGB == 0)
112  {
113  // nothing to do
114  return CC_FERR_NO_ERROR;
115  }
116 
117  if (!pointCloud.reserveTheRGBTable())
118  {
120  }
121 
122  if (!m_force8bitRgbMode && currentOredRGB > 255)
123  {
124  // LAS colors use 16bits (as they should)
125  m_colorCompShift = 8;
126  }
127 
128  if (pointCloud.size() != 0)
129  {
130  // set the previous points color (black by default)
131  for (unsigned j = 0; j < pointCloud.size() - 1; ++j)
132  {
133  pointCloud.addRGBColor(ecvColor::black);
134  }
135  }
136  }
137 
138  assert(pointCloud.hasColors());
139 
140  auto red = static_cast<ColorCompType>(currentPoint.rgb[0] >> m_colorCompShift);
141  auto green = static_cast<ColorCompType>(currentPoint.rgb[1] >> m_colorCompShift);
142  auto blue = static_cast<ColorCompType>(currentPoint.rgb[2] >> m_colorCompShift);
143  pointCloud.addRGBColor(ecvColor::Rgb(red, green, blue));
144 
145  return CC_FERR_NO_ERROR;
146 }
147 
149  const laszip_point& currentPoint)
150 {
151  if (currentPoint.num_extra_bytes <= 0 || currentPoint.extra_bytes == nullptr)
152  {
153  return CC_FERR_NO_ERROR;
154  }
155 
156  for (const LasExtraScalarField& extraField : m_extraScalarFields)
157  {
158  if (extraField.byteOffset + extraField.byteSize() > static_cast<unsigned>(currentPoint.num_extra_bytes))
159  {
160  assert(false);
161  return CC_FERR_READING;
162  }
163 
164  laszip_U8* dataStart = currentPoint.extra_bytes + extraField.byteOffset;
165  parseRawValues(extraField, dataStart);
166  switch (extraField.kind())
167  {
169  handleOptionsFor(extraField, m_rawValues.unsignedValues);
170  break;
172  handleOptionsFor(extraField, m_rawValues.signedValues);
173  break;
175  handleOptionsFor(extraField, m_rawValues.floatingValues);
176  break;
177  }
178  }
179  return CC_FERR_NO_ERROR;
180 }
181 
182 template <typename T>
184 LasScalarFieldLoader::handleScalarField(LasScalarField& sfInfo, ccPointCloud& pointCloud, T currentValue)
185 {
186  if (!sfInfo.sf)
187  {
188  if (m_ignoreFieldsWithDefaultValues && currentValue == T{})
189  {
190  return CC_FERR_NO_ERROR;
191  }
192  auto newSf = new ccScalarField(sfInfo.name());
193  sfInfo.sf = newSf;
194  if (!newSf->reserveSafe(pointCloud.capacity()))
195  {
197  }
198 
199  for (unsigned j = 0; j < pointCloud.size() - 1; ++j)
200  {
201  newSf->addElement(static_cast<ScalarType>(T{}));
202  }
203  }
204 
205  if (sfInfo.sf)
206  {
207  sfInfo.sf->addElement(static_cast<ScalarType>(currentValue));
208  }
209  return CC_FERR_NO_ERROR;
210 }
211 
213 LasScalarFieldLoader::handleGpsTime(LasScalarField& sfInfo, ccPointCloud& pointCloud, const double currentValue)
214 {
215  if (!sfInfo.sf)
216  {
217  if (m_ignoreFieldsWithDefaultValues && currentValue == 0.0)
218  {
219  return CC_FERR_NO_ERROR;
220  }
221  auto newSf = new ccScalarField(sfInfo.name());
222  sfInfo.sf = newSf;
223  if (!newSf->reserveSafe(pointCloud.capacity()))
224  {
226  }
227 
228  double timeShift;
229  if (std::isnan(m_manualTimeShiftValue))
230  {
231  timeShift = static_cast<int64_t>(currentValue / 10000.0) * 10000.0;
232  }
233  else
234  {
235  timeShift = m_manualTimeShiftValue;
236  }
237 
238  double shiftedValue = currentValue - timeShift;
239  if (shiftedValue < 1.0e5)
240  {
241  CVLog::Warning("[LAS] Time SF has been shifted to prevent a loss of accuracy (%.2f)", timeShift);
242  }
243  else if (timeShift > 0.0)
244  {
245  CVLog::Warning("[LAS] Time SF has been shifted but accuracy may not be preserved (%.2f)",
246  timeShift);
247  }
248  else
249  {
250  CVLog::Warning("[LAS] Time SF has not been shifted. Accuracy may not be preserved.");
251  }
252 
253  newSf->setGlobalShift(timeShift);
254  for (unsigned j = 0; j < pointCloud.size() - 1; ++j)
255  {
256  newSf->addElement(static_cast<ScalarType>(timeShift));
257  }
258  }
259 
260  if (sfInfo.sf)
261  {
262  sfInfo.sf->addElement(static_cast<ScalarType>(currentValue - sfInfo.sf->getGlobalShift()));
263  }
264  return CC_FERR_NO_ERROR;
265 }
266 
267 bool LasScalarFieldLoader::createScalarFieldsForExtraBytes(ccPointCloud& pointCloud)
268 {
269  for (LasExtraScalarField& extraField : m_extraScalarFields)
270  {
271  switch (extraField.numElements())
272  {
273  case 1:
274  if (pointCloud.getScalarFieldIndexByName(extraField.name) != -1)
275  {
277  snprintf(name, LasExtraScalarField::MAX_NAME_SIZE + 8, "%s (Extra)", extraField.name);
278  extraField.scalarFields[0] = new ccScalarField(name);
279  memcpy(extraField.ccName, name, LasExtraScalarField::MAX_NAME_SIZE + 8);
280  }
281  else
282  {
283  extraField.scalarFields[0] = new ccScalarField(extraField.name);
284  }
285 
286  if (!extraField.scalarFields[0]->reserveSafe(pointCloud.capacity()))
287  {
288  return false;
289  }
290  break;
291  case 2:
292  case 3:
293  for (unsigned dimIndex{0}; dimIndex < extraField.numElements(); ++dimIndex)
294  {
296  snprintf(name, LasExtraScalarField::MAX_NAME_SIZE + 8, "%s [%d]", extraField.name, dimIndex);
297  extraField.scalarFields[dimIndex] = new ccScalarField(name);
298  if (!extraField.scalarFields[dimIndex]->reserveSafe(pointCloud.capacity()))
299  {
300  return false;
301  }
302  }
303  break;
304  }
305  if (extraField.offsetIsRelevant())
306  {
307  for (unsigned i = 0; i < extraField.numElements(); ++i)
308  {
309  extraField.scalarFields[i]->setGlobalShift(extraField.offsets[i]);
310  }
311  }
312  }
313  return true;
314 }
315 
316 template <typename T>
317 ScalarType LasScalarFieldLoader::ParseValueOfType(uint8_t* source)
318 {
319  return static_cast<ScalarType>(*reinterpret_cast<T*>(source));
320 }
321 
322 template <typename T, typename V>
323 V LasScalarFieldLoader::ParseValueOfTypeAs(const uint8_t* source)
324 {
325  return static_cast<V>(*reinterpret_cast<const T*>(source));
326 }
327 
328 void LasScalarFieldLoader::parseRawValues(const LasExtraScalarField& extraField, const uint8_t* dataStart)
329 {
330  for (unsigned i = 0; i < extraField.numElements(); ++i)
331  {
332  switch (extraField.type)
333  {
336  break;
338  m_rawValues.unsignedValues[i] = ParseValueOfTypeAs<uint8_t, uint64_t>(dataStart);
339  break;
341  m_rawValues.unsignedValues[i] = ParseValueOfTypeAs<uint16_t, uint64_t>(dataStart);
342  break;
344  m_rawValues.unsignedValues[i] = ParseValueOfTypeAs<uint32_t, uint64_t>(dataStart);
345  break;
347  m_rawValues.unsignedValues[i] = ParseValueOfTypeAs<uint64_t, uint64_t>(dataStart);
348  break;
350  m_rawValues.signedValues[i] = ParseValueOfTypeAs<int8_t, int64_t>(dataStart);
351  break;
353  m_rawValues.signedValues[i] = ParseValueOfTypeAs<int16_t, int64_t>(dataStart);
354  break;
356  m_rawValues.signedValues[i] = ParseValueOfTypeAs<int32_t, int64_t>(dataStart);
357  break;
359  m_rawValues.signedValues[i] = ParseValueOfTypeAs<int64_t, int64_t>(dataStart);
360  break;
362  m_rawValues.floatingValues[i] = ParseValueOfTypeAs<float, double>(dataStart);
363  break;
365  m_rawValues.floatingValues[i] = ParseValueOfTypeAs<double, double>(dataStart);
366  break;
367  }
368  dataStart += extraField.elementSize();
369  }
370 }
371 
372 template <typename T>
373 void LasScalarFieldLoader::handleOptionsFor(const LasExtraScalarField& extraField, T values[3])
374 {
375  assert(extraField.numElements() <= 3);
376  for (unsigned dimIndex = 0; dimIndex < extraField.numElements(); ++dimIndex)
377  {
378  if (extraField.noDataIsRelevant())
379  {
380  auto noDataValue = ParseValueOfTypeAs<T, T>(static_cast<const uint8_t*>(extraField.noData[dimIndex]));
381  if (noDataValue == values[dimIndex])
382  {
383  extraField.scalarFields[dimIndex]->addElement(ccScalarField::NaN());
384  }
385  }
386  else if (extraField.scaleIsRelevant())
387  {
388  double scaledValue = (values[dimIndex] * extraField.scales[dimIndex]) + (extraField.offsets[dimIndex]);
389  extraField.scalarFields[dimIndex]->addElement(static_cast<ScalarType>(scaledValue));
390  }
391  else
392  {
393  extraField.scalarFields[dimIndex]->addElement(static_cast<ScalarType>(values[dimIndex]));
394  }
395  }
396 }
std::string name
CC_FILE_ERROR
Typical I/O filter errors.
Definition: FileIOFilter.h:20
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_READING
Definition: FileIOFilter.h:26
@ CC_FERR_NOT_ENOUGH_MEMORY
Definition: FileIOFilter.h:31
constexpr double SCAN_ANGLE_SCALE
Definition: LasDetails.h:50
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
This serves the same purpose as LasScalarField but for extra bytes.
static constexpr unsigned MAX_NAME_SIZE
unsigned elementSize() const
double scales[MAX_DIM_SIZE]
uint8_t noData[MAX_DIM_SIZE][8]
unsigned numElements() const
double offsets[MAX_DIM_SIZE]
ccScalarField * scalarFields[MAX_DIM_SIZE]
CC_FILE_ERROR handleRGBValue(ccPointCloud &pointCloud, const laszip_point &currentPoint)
CC_FILE_ERROR handleScalarFields(ccPointCloud &pointCloud, const laszip_point &currentPoint)
CC_FILE_ERROR handleExtraScalarFields(ccPointCloud &pointCloud, const laszip_point &currentPoint)
LasScalarFieldLoader(std::vector< LasScalarField > &standardScalarFields, std::vector< LasExtraScalarField > &extraScalarFields, ccPointCloud &pointCloud)
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
bool hasColors() const override
Returns whether colors are enabled or not.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
A scalar field associated to display-related parameters.
double getGlobalShift() const
Returns the global shift (if any)
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
unsigned size() const override
Definition: PointCloudTpl.h:38
unsigned capacity() const
Returns cloud capacity (i.e. reserved size)
void addElement(ScalarType value)
Definition: ScalarField.h:99
static ScalarType NaN()
Returns the specific NaN value.
Definition: ScalarField.h:46
RGB color structure.
Definition: ecvColorTypes.h:49
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
static void error(char *msg)
Definition: lsd.c:159
constexpr unsigned OVERLAP_FLAG_BIT_MASK
Definition: LasDetails.h:85
constexpr Rgb black(0, 0, 0)
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb blue(0, 0, MAX)
constexpr Rgb green(0, MAX, 0)
const char * name() const
ccScalarField * sf