LasWaveformLoader.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. //##########################################################################
  2. //# #
  3. //# CLOUDCOMPARE PLUGIN: LAS-IO Plugin #
  4. //# #
  5. //# This program is free software; you can redistribute it and/or modify #
  6. //# it under the terms of the GNU General Public License as published by #
  7. //# the Free Software Foundation; version 2 of the License. #
  8. //# #
  9. //# This program is distributed in the hope that it will be useful, #
  10. //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
  11. //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
  12. //# GNU General Public License for more details. #
  13. //# #
  14. //# COPYRIGHT: Thomas Montaigu #
  15. //# #
  16. //##########################################################################
  17. #include "LasWaveformLoader.h"
  18. #include "LasDetails.h"
  19. #include <QDataStream>
  20. static bool ParseWavepacketDescriptorVlr(const laszip_vlr_struct& vlr, WaveformDescriptor& descriptor)
  21. {
  22. if (vlr.record_length_after_header < 26)
  23. {
  24. return false;
  25. }
  26. auto data = QByteArray::fromRawData(reinterpret_cast<const char*>(vlr.data), vlr.record_length_after_header);
  27. QDataStream stream(data);
  28. stream.setByteOrder(QDataStream::ByteOrder::LittleEndian);
  29. uint8_t compressionType;
  30. stream >> descriptor.bitsPerSample >> compressionType >> descriptor.numberOfSamples >> descriptor.samplingRate_ps >> descriptor.digitizerGain >> descriptor.digitizerOffset;
  31. if (descriptor.digitizerGain == 0.0)
  32. {
  33. // shouldn't be 0 by default!
  34. descriptor.digitizerGain = 1.0;
  35. }
  36. return true;
  37. }
  38. static ccPointCloud::FWFDescriptorSet ParseWaveformDescriptorVlrs(const laszip_vlr_struct* vlrs,
  39. laszip_U32 numVlrs)
  40. {
  41. ccPointCloud::FWFDescriptorSet descriptors;
  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. ccLog::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. LasWaveformLoader::LasWaveformLoader(const laszip_header_struct& laszipHeader,
  61. const QString& lasFilename,
  62. ccPointCloud& pointCloud)
  63. : isPointFormatExtended(laszipHeader.point_data_format >= 6)
  64. {
  65. descriptors = ParseWaveformDescriptorVlrs(laszipHeader.vlrs, laszipHeader.number_of_variable_length_records);
  66. ccLog::Print("[LAS] %d Waveform Packet Descriptor VLRs found", descriptors.size());
  67. QFile fwfDataSource;
  68. if (laszipHeader.start_of_waveform_data_packet_record != 0)
  69. {
  70. ccLog::Print("[LAS] Waveform data is located within the las file");
  71. fwfDataSource.setFileName(lasFilename);
  72. if (!fwfDataSource.open(QFile::ReadOnly))
  73. {
  74. ccLog::Warning(QString("[LAS] Failed to re open the las file: %1").arg(fwfDataSource.errorString()));
  75. return;
  76. }
  77. if (!fwfDataSource.seek(laszipHeader.start_of_waveform_data_packet_record))
  78. {
  79. ccLog::Warning(QString("[LAS] Failed to find the associated waveform data packets header"));
  80. return;
  81. }
  82. QDataStream stream(&fwfDataSource);
  83. LasDetails::EvlrHeader evlrHeader;
  84. stream >> evlrHeader;
  85. if (stream.status() == QDataStream::Status::ReadPastEnd)
  86. {
  87. ccLog::Warning(QString("[LAS] Failed to read the associated waveform data packets"));
  88. return;
  89. }
  90. if (!evlrHeader.isWaveFormDataPackets())
  91. {
  92. ccLog::Warning("[LAS] Invalid waveform EVLR");
  93. return;
  94. }
  95. fwfDataCount = evlrHeader.recordLength;
  96. fwfDataOffset = LasDetails::EvlrHeader::SIZE;
  97. if (fwfDataCount == 0)
  98. {
  99. ccLog::Warning(QString("[LAS] Invalid waveform data packet size (0). We'll load all the "
  100. "remaining part of the file!"));
  101. fwfDataCount = fwfDataSource.size() - fwfDataSource.pos();
  102. }
  103. }
  104. else if (laszipHeader.global_encoding & 4)
  105. {
  106. QFileInfo info(lasFilename);
  107. QString wdpFilename = info.path() + "/" + info.completeBaseName() + ".wdp";
  108. fwfDataSource.setFileName(wdpFilename);
  109. if (!fwfDataSource.open(QFile::ReadOnly))
  110. {
  111. ccLog::Warning(QString("[LAS] Failed to read the associated waveform data packets file "
  112. "(looking for '%1'): %2")
  113. .arg(wdpFilename)
  114. .arg(fwfDataSource.errorString()));
  115. return;
  116. }
  117. fwfDataCount = fwfDataSource.size();
  118. if (fwfDataCount > LasDetails::EvlrHeader::SIZE)
  119. {
  120. QDataStream stream(&fwfDataSource);
  121. LasDetails::EvlrHeader evlrHeader;
  122. stream >> evlrHeader;
  123. if (evlrHeader.isWaveFormDataPackets())
  124. {
  125. // this is a valid EVLR header, we can skip it
  126. auto p = fwfDataSource.pos();
  127. fwfDataCount -= LasDetails::EvlrHeader::SIZE;
  128. fwfDataOffset = LasDetails::EvlrHeader::SIZE;
  129. }
  130. else
  131. {
  132. // this doesn't look like a valid EVLR
  133. fwfDataSource.seek(0);
  134. }
  135. }
  136. ccLog::Print(QString("[LAS] Waveform Data Packets are in an external file located at %1").arg(wdpFilename));
  137. }
  138. if (fwfDataSource.isOpen() && fwfDataCount != 0)
  139. {
  140. ccPointCloud::FWFDataContainer* container{nullptr};
  141. try
  142. {
  143. container = new ccPointCloud::FWFDataContainer;
  144. container->resize(fwfDataCount);
  145. pointCloud.waveforms().resize(pointCloud.capacity());
  146. }
  147. catch (const std::bad_alloc&)
  148. {
  149. ccLog::Warning(QString("[LAS] Not enough memory to import the waveform data"));
  150. delete container;
  151. return;
  152. }
  153. fwfDataSource.read((char*)container->data(), fwfDataCount);
  154. fwfDataSource.close();
  155. pointCloud.fwfData() = ccPointCloud::SharedFWFDataContainer(container);
  156. }
  157. }
  158. void LasWaveformLoader::loadWaveform(ccPointCloud& pointCloud, const laszip_point& currentPoint) const
  159. {
  160. assert(pointCloud.size() > 0);
  161. if (fwfDataCount == 0)
  162. {
  163. return;
  164. }
  165. auto data = QByteArray::fromRawData(reinterpret_cast<const char*>(currentPoint.wave_packet), 29);
  166. QDataStream stream(data);
  167. stream.setByteOrder(QDataStream::ByteOrder::LittleEndian);
  168. uint8_t descriptorIndex = 0;
  169. quint64 byteOffset = 0;
  170. uint32_t byteCount = 0;
  171. float returnPointLocation = 0;
  172. float x_t = 0, y_t = 0, z_t = 0;
  173. returnPointLocation = ((float*)&currentPoint.wave_packet[13])[0];
  174. x_t = ((float*)&currentPoint.wave_packet[17])[0];
  175. y_t = ((float*)&currentPoint.wave_packet[21])[0];
  176. z_t = ((float*)&currentPoint.wave_packet[25])[0];
  177. stream >> descriptorIndex >> byteOffset >> byteCount;
  178. ccPointCloud::FWFDescriptorSet& cloudDescriptors = pointCloud.fwfDescriptors();
  179. if (descriptors.contains(descriptorIndex) && !cloudDescriptors.contains(descriptorIndex))
  180. {
  181. cloudDescriptors.insert(descriptorIndex, descriptors.value(descriptorIndex));
  182. }
  183. else if (!descriptors.contains(descriptorIndex))
  184. {
  185. if (byteCount != 0) // otherwise it's just a blank/missing waveform
  186. {
  187. ccLog::Warning("[LAS] No valid descriptor vlr for index %d", descriptorIndex);
  188. }
  189. return;
  190. }
  191. if (byteOffset < fwfDataOffset)
  192. {
  193. ccLog::Warning("[LAS] Waveform byte offset is smaller that fwfDataOffset");
  194. byteOffset = fwfDataOffset;
  195. }
  196. byteOffset -= fwfDataOffset;
  197. if (byteOffset + byteCount > fwfDataCount)
  198. {
  199. ccLog::Warning("[LAS] Waveform byte count for point %u is bigger than actual fwf data",
  200. pointCloud.size() - 1);
  201. byteCount = (fwfDataCount - byteOffset);
  202. }
  203. ccWaveform& w = pointCloud.waveforms()[pointCloud.size() - 1];
  204. w.setDescriptorID(descriptorIndex);
  205. w.setDataDescription(byteOffset, byteCount);
  206. w.setEchoTime_ps(returnPointLocation);
  207. w.setBeamDir(CCVector3f(x_t, y_t, z_t));
  208. if (isPointFormatExtended)
  209. {
  210. w.setReturnIndex(currentPoint.extended_return_number);
  211. }
  212. else
  213. {
  214. w.setReturnIndex(currentPoint.return_number);
  215. }
  216. }