ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
LasWaveformLoader.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 "LasWaveformLoader.h"
9 
10 #include "LasDetails.h"
11 
12 #include <QDataStream>
13 
15 {
16  if (vlr.record_length_after_header < 26)
17  {
18  return false;
19  }
20 
21  auto data = QByteArray::fromRawData(reinterpret_cast<const char*>(vlr.data), vlr.record_length_after_header);
22  QDataStream stream(data);
23  stream.setByteOrder(QDataStream::ByteOrder::LittleEndian);
24 
25  uint8_t compressionType;
26  stream >> descriptor.bitsPerSample >> compressionType >> descriptor.numberOfSamples >> descriptor.samplingRate_ps >> descriptor.digitizerGain >> descriptor.digitizerOffset;
27 
28  if (descriptor.digitizerGain == 0.0)
29  {
30  // shouldn't be 0 by default!
31  descriptor.digitizerGain = 1.0;
32  }
33 
34  return true;
35 }
36 
38  laszip_U32 numVlrs)
39 {
41 
42  for (laszip_U32 i = 0; i < numVlrs; ++i)
43  {
44  const laszip_vlr_struct& vlr = vlrs[i];
45  if (strcmp(vlr.user_id, "LASF_Spec") == 0 && 99 < vlr.record_id && vlr.record_id < 355)
46  {
47  WaveformDescriptor descriptor;
48  if (!ParseWavepacketDescriptorVlr(vlr, descriptor))
49  {
50  CVLog::Warning("[LAS] Invalid Descriptor VLR");
51  }
52  else
53  {
54  descriptors.insert(vlr.record_id - 99, descriptor);
55  }
56  }
57  }
58  return descriptors;
59 }
60 
61 LasWaveformLoader::LasWaveformLoader(const laszip_header_struct& laszipHeader,
62  const QString& lasFilename,
63  ccPointCloud& pointCloud)
64  : isPointFormatExtended(laszipHeader.point_data_format >= 6)
65 {
66  descriptors = ParseWaveformDescriptorVlrs(laszipHeader.vlrs, laszipHeader.number_of_variable_length_records);
67  CVLog::Print("[LAS] %d Waveform Packet Descriptor VLRs found", descriptors.size());
68 
69  QFile fwfDataSource;
70  if (laszipHeader.start_of_waveform_data_packet_record != 0)
71  {
72  CVLog::Print("[LAS] Waveform data is located within the las file");
73  fwfDataSource.setFileName(lasFilename);
74  if (!fwfDataSource.open(QFile::ReadOnly))
75  {
76  CVLog::Warning(QString("[LAS] Failed to re open the las file: %1").arg(fwfDataSource.errorString()));
77  return;
78  }
79 
80  if (!fwfDataSource.seek(laszipHeader.start_of_waveform_data_packet_record))
81  {
82  CVLog::Warning(QString("[LAS] Failed to find the associated waveform data packets header"));
83  return;
84  }
85 
86  QDataStream stream(&fwfDataSource);
87  LasDetails::EvlrHeader evlrHeader;
88  stream >> evlrHeader;
89  if (stream.status() == QDataStream::Status::ReadPastEnd)
90  {
91  CVLog::Warning(QString("[LAS] Failed to read the associated waveform data packets"));
92  return;
93  }
94 
95  if (!evlrHeader.isWaveFormDataPackets())
96  {
97  CVLog::Warning("[LAS] Invalid waveform EVLR");
98  return;
99  }
100  fwfDataCount = evlrHeader.recordLength;
101  fwfDataOffset = 0;
102  if (fwfDataCount == 0)
103  {
104  CVLog::Warning(QString("[LAS] Invalid waveform data packet size (0). We'll load all the "
105  "remaining part of the file!"));
106  fwfDataCount = fwfDataSource.size() - fwfDataSource.pos();
107  }
108  }
109  else if (laszipHeader.global_encoding & 4)
110  {
111  QFileInfo info(lasFilename);
112  QString wdpFilename = info.path() + "/" + info.completeBaseName() + ".wdp";
113  fwfDataSource.setFileName(wdpFilename);
114  if (!fwfDataSource.open(QFile::ReadOnly))
115  {
116  CVLog::Warning(QString("[LAS] Failed to read the associated waveform data packets file "
117  "(looking for '%1'): %2")
118  .arg(wdpFilename)
119  .arg(fwfDataSource.errorString()));
120  return;
121  }
122  fwfDataCount = fwfDataSource.size();
123 
125  {
126  QDataStream stream(&fwfDataSource);
127  LasDetails::EvlrHeader evlrHeader;
128  stream >> evlrHeader;
129 
130  if (evlrHeader.isWaveFormDataPackets())
131  {
132  // this is a valid EVLR header, we can skip it
133  auto p = fwfDataSource.pos();
136  }
137  else
138  {
139  // this doesn't look like a valid EVLR
140  fwfDataSource.seek(0);
141  }
142  }
143  CVLog::Print(QString("[LAS] Waveform Data Packets are in an external file located at %1").arg(wdpFilename));
144  }
145 
146  if (fwfDataSource.isOpen() && fwfDataCount != 0)
147  {
148  ccPointCloud::FWFDataContainer* container{nullptr};
149  try
150  {
151  container = new ccPointCloud::FWFDataContainer;
152  container->resize(fwfDataCount);
153  pointCloud.waveforms().resize(pointCloud.capacity());
154  }
155  catch (const std::bad_alloc&)
156  {
157  CVLog::Warning(QString("[LAS] Not enough memory to import the waveform data"));
158  delete container;
159  return;
160  }
161 
162  fwfDataSource.read((char*)container->data(), fwfDataCount);
163  fwfDataSource.close();
164 
165  pointCloud.fwfData() = ccPointCloud::SharedFWFDataContainer(container);
166  }
167 }
168 
169 void LasWaveformLoader::loadWaveform(ccPointCloud& pointCloud, const laszip_point& currentPoint) const
170 {
171  assert(pointCloud.size() > 0);
172  if (fwfDataCount == 0)
173  {
174  return;
175  }
176 
177  auto data = QByteArray::fromRawData(reinterpret_cast<const char*>(currentPoint.wave_packet), 29);
178  QDataStream stream(data);
179  stream.setByteOrder(QDataStream::ByteOrder::LittleEndian);
180 
181  uint8_t descriptorIndex = 0;
182  quint64 byteOffset = 0;
183  uint32_t byteCount = 0;
184  float returnPointLocation = 0;
185  float x_t = 0, y_t = 0, z_t = 0;
186  stream >> descriptorIndex >> byteOffset >> byteCount >> returnPointLocation >> x_t >> y_t >> z_t;
187 
188  ccPointCloud::FWFDescriptorSet& cloudDescriptors = pointCloud.fwfDescriptors();
189  if (descriptors.contains(descriptorIndex) && !cloudDescriptors.contains(descriptorIndex))
190  {
191  cloudDescriptors.insert(descriptorIndex, descriptors.value(descriptorIndex));
192  }
193  else if (!descriptors.contains(descriptorIndex))
194  {
195  CVLog::Warning("[LAS] No valid descriptor vlr for index %d", descriptorIndex);
196  return;
197  }
198 
199  if (byteOffset < fwfDataOffset)
200  {
201  CVLog::Warning("[LAS] Waveform byte offset is smaller that fwfDataOffset");
202  byteOffset = fwfDataOffset;
203  }
204 
205  byteOffset -= fwfDataOffset;
206 
207  if (byteOffset + byteCount > fwfDataCount)
208  {
209  CVLog::Warning("[LAS] Waveform byte count for point %u is bigger than actual fwf data",
210  pointCloud.size() - 1);
211  byteCount = (fwfDataCount - byteOffset);
212  }
213 
214  ccWaveform& w = pointCloud.waveforms()[pointCloud.size() - 1];
215 
216  w.setDescriptorID(descriptorIndex);
217  w.setDataDescription(byteOffset, byteCount);
218  w.setEchoTime_ps(returnPointLocation);
219  w.setBeamDir(CCVector3f(x_t, y_t, z_t));
220 
222  {
223  w.setReturnIndex(currentPoint.extended_return_number);
224  }
225  else
226  {
227  w.setReturnIndex(currentPoint.return_number);
228  }
229 }
Vector3Tpl< float > CCVector3f
Float 3D Vector.
Definition: CVGeom.h:801
laszip_vlr laszip_vlr_struct
Definition: LasDetails.h:46
static ccPointCloud::FWFDescriptorSet ParseWaveformDescriptorVlrs(const laszip_vlr_struct *vlrs, laszip_U32 numVlrs)
static bool ParseWavepacketDescriptorVlr(const laszip_vlr_struct &vlr, WaveformDescriptor &descriptor)
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
Waveform descriptor.
Definition: ecvWaveform.h:23
uint32_t samplingRate_ps
Sampling rate in pico seconds.
Definition: ecvWaveform.h:44
uint8_t bitsPerSample
Number of bits per sample.
Definition: ecvWaveform.h:49
uint32_t numberOfSamples
Number of samples.
Definition: ecvWaveform.h:43
double digitizerOffset
Definition: ecvWaveform.h:47
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
QMap< uint8_t, WaveformDescriptor > FWFDescriptorSet
Waveform descriptors set.
FWFDescriptorSet & fwfDescriptors()
Gives access to the FWF descriptors.
std::vector< ccWaveform > & waveforms()
Gives access to the associated FWF data.
QSharedPointer< const FWFDataContainer > SharedFWFDataContainer
std::vector< uint8_t > FWFDataContainer
Waveform data container.
SharedFWFDataContainer & fwfData()
Gives access to the associated FWF data container.
Waveform.
Definition: ecvWaveform.h:55
void setReturnIndex(uint8_t index)
Sets the return index.
Definition: ecvWaveform.h:143
void setDescriptorID(uint8_t id)
Sets the associated descriptor (ID)
Definition: ecvWaveform.h:70
void setBeamDir(const CCVector3f &dir)
Sets the beam direction.
Definition: ecvWaveform.h:126
void setDataDescription(uint64_t dataOffset, uint32_t byteCount)
Describes the waveform data.
void setEchoTime_ps(float time_ps)
Set the echo time (in picoseconds)
Definition: ecvWaveform.h:132
unsigned size() const override
Definition: PointCloudTpl.h:38
unsigned capacity() const
Returns cloud capacity (i.e. reserved size)
CorePointDescSet * descriptors
static constexpr size_t SIZE
Definition: LasDetails.h:94
bool isWaveFormDataPackets() const
Definition: LasDetails.cpp:35
LasWaveformLoader(const laszip_header_struct &laszipHeader, const QString &lasFilename, ccPointCloud &pointCloud)
ccPointCloud::FWFDescriptorSet descriptors
void loadWaveform(ccPointCloud &pointCloud, const laszip_point &currentPoint) const