PointCloudPlyWriter.cpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2023 SICK AG, Waldkirch
3 //
4 // SPDX-License-Identifier: Unlicense
5 
6 #include "PointCloudPlyWriter.h"
7 
8 #include "VisionaryEndian.h"
9 #include <cmath>
10 #include <fstream>
11 #include <sstream>
12 
13 namespace visionary {
14 
15 bool PointCloudPlyWriter::WriteFormatPLY(const char* filename,
16  const std::vector<PointXYZ>& points,
17  bool useBinary,
18  InvalidPointPresentation presentation)
19 {
20  return WriteFormatPLY(filename, points, std::vector<uint32_t>(), std::vector<uint16_t>(), useBinary, presentation);
21 }
22 
23 bool PointCloudPlyWriter::WriteFormatPLY(const char* filename,
24  const std::vector<PointXYZ>& points,
25  const std::vector<uint32_t>& rgbaMap,
26  bool useBinary,
27  InvalidPointPresentation presentation)
28 {
29  return WriteFormatPLY(filename, points, rgbaMap, std::vector<uint16_t>(), useBinary, presentation);
30 }
31 
32 bool PointCloudPlyWriter::WriteFormatPLY(const char* filename,
33  const std::vector<PointXYZ>& points,
34  const std::vector<uint16_t>& intensityMap,
35  bool useBinary,
36  InvalidPointPresentation presentation)
37 {
38  return WriteFormatPLY(filename, points, std::vector<uint32_t>(), intensityMap, useBinary, presentation);
39 }
40 
41 bool PointCloudPlyWriter::WriteFormatPLY(const char* filename,
42  const std::vector<PointXYZ>& points,
43  const std::vector<uint32_t>& rgbaMap,
44  const std::vector<uint16_t>& intensityMap,
45  bool useBinary,
46  InvalidPointPresentation presentation)
47 {
48  bool success = true;
49 
50  bool hasColors = points.size() == rgbaMap.size();
51  bool hasIntensities = points.size() == intensityMap.size();
52 
53  std::ofstream stream; // output file stream for PLY file
54  std::stringstream
55  strstream; // temporary data buffer stream
56  // Reason: On presentation mode INVALID_SKIP we only know the number of "element vertex ?"
57  // in the header of the PLY file after iterating through the "vector<PointXYZ>& points"
58  // and checking if it has the value NaN. The header has to be printed before and
59  // therefore the data part has to be temprorarily buffered.
60 
61  int numberOfValidPoints = 0;
62  if (useBinary == false)
63  {
64  // Write all points
65  for (size_t i = 0; i < points.size(); i++)
66  {
67  PointXYZ point = points.at(i);
68 
69  // Handle PLY file presentation of X Y Z values (nan, 0.0 or SKIP)
70  if (presentation == INVALID_AS_NAN)
71  {
72  strstream << point.x << " " << point.y << " " << point.z;
73  }
74  if (presentation == INVALID_AS_ZERO)
75  {
76  if (std::isnan(point.x))
77  strstream << "0.0";
78  else
79  strstream << point.x;
80  strstream << " ";
81 
82  if (std::isnan(point.y))
83  strstream << "0.0";
84  else
85  strstream << point.y;
86  strstream << " ";
87 
88  if (std::isnan(point.z))
89  strstream << "0.0";
90  else
91  strstream << point.z;
92  }
93  if (presentation == INVALID_AS_NAN || presentation == INVALID_AS_ZERO)
94  {
95  if (hasColors)
96  {
97  const auto rgba = reinterpret_cast<const uint8_t*>(&rgbaMap.at(i));
98  strstream << " " << static_cast<uint32_t>(rgba[0]) << " " << static_cast<uint32_t>(rgba[1]) << " "
99  << static_cast<uint32_t>(rgba[2]);
100  }
101  if (hasIntensities)
102  {
103  float intensity = static_cast<float>(intensityMap.at(i)) / 65535.0f;
104  strstream << " " << intensity;
105  }
106  strstream << "\n";
107  }
108  if (presentation == INVALID_SKIP)
109  {
110  // The X and Y values are calculated using the Z/distance value which is received by the device.
111  // So X and Y should only be NaN if Z/distance is NaN.
112  if (std::isnan(point.z))
113  {
114  continue;
115  }
116  else
117  {
118  strstream << point.x << " " << point.y << " " << point.z;
119 
120  if (hasColors)
121  {
122  const auto rgba = reinterpret_cast<const uint8_t*>(&rgbaMap.at(i));
123  strstream << " " << static_cast<uint32_t>(rgba[0]) << " " << static_cast<uint32_t>(rgba[1]) << " "
124  << static_cast<uint32_t>(rgba[2]);
125  }
126  if (hasIntensities)
127  {
128  float intensity = static_cast<float>(intensityMap.at(i)) / 65535.0f;
129  strstream << " " << intensity;
130  }
131  strstream << "\n";
132 
133  numberOfValidPoints++;
134  }
135  }
136  }
137  }
138  else
139  {
140  // Write all points
141  for (size_t i = 0; i < points.size(); i++)
142  {
143  PointXYZ point = points.at(i);
144 
145  // Handle PLY file presentation of X Y Z values (nan, 0.0 or SKIP)
146  if (presentation == INVALID_AS_NAN)
147  {
148  // Nothing to do, use values as they are available
149  }
150  if (presentation == INVALID_AS_ZERO)
151  {
152  if (std::isnan(point.x))
153  point.x = 0.0f;
154  if (std::isnan(point.y))
155  point.y = 0.0f;
156  if (std::isnan(point.z))
157  point.z = 0.0f;
158  }
159  if (presentation == INVALID_AS_NAN || presentation == INVALID_AS_ZERO)
160  {
161  float x = nativeToLittleEndian(point.x);
162  float y = nativeToLittleEndian(point.y);
163  float z = nativeToLittleEndian(point.z);
164 
165  strstream.write(reinterpret_cast<char*>(&x), 4);
166  strstream.write(reinterpret_cast<char*>(&y), 4);
167  strstream.write(reinterpret_cast<char*>(&z), 4);
168 
169  if (hasColors)
170  {
171  strstream.write(reinterpret_cast<const char*>(&rgbaMap.at(i)), 3);
172  }
173  if (hasIntensities)
174  {
175  float intensity = static_cast<float>(intensityMap.at(i)) / 65535.0f;
176  strstream.write(reinterpret_cast<const char*>(&intensity), 4);
177  }
178  }
179  if (presentation == INVALID_SKIP)
180  {
181  // The X and Y values are calculated using the Z/distance value which is received by the device.
182  // So X and Y should only be NaN if Z/distance is NaN.
183  if (std::isnan(point.z))
184  {
185  continue;
186  }
187  else
188  {
189  float x = nativeToLittleEndian(point.x);
190  float y = nativeToLittleEndian(point.y);
191  float z = nativeToLittleEndian(point.z);
192 
193  strstream.write(reinterpret_cast<char*>(&x), 4);
194  strstream.write(reinterpret_cast<char*>(&y), 4);
195  strstream.write(reinterpret_cast<char*>(&z), 4);
196 
197  if (hasColors)
198  {
199  strstream.write(reinterpret_cast<const char*>(&rgbaMap.at(i)), 3);
200  }
201  if (hasIntensities)
202  {
203  float intensity = static_cast<float>(intensityMap.at(i)) / 65535.0f;
204  strstream.write(reinterpret_cast<const char*>(&intensity), 4);
205  }
206 
207  numberOfValidPoints++;
208  }
209  }
210  }
211  }
212 
213  // Open file
214  stream.open(filename, useBinary ? (std::ios_base::out | std::ios_base::binary) : std::ios_base::out);
215 
216  if (stream.is_open())
217  {
218  // Write header
219  stream << "ply\n";
220  stream << "format " << (useBinary ? "binary_little_endian" : "ascii") << " 1.0\n";
221 
222  if (presentation == INVALID_SKIP)
223  {
224  stream << "element vertex " << numberOfValidPoints << "\n";
225  }
226  else
227  {
228  stream << "element vertex " << points.size() << "\n";
229  }
230 
231  stream << "property float x\n";
232  stream << "property float y\n";
233  stream << "property float z\n";
234  if (hasColors)
235  {
236  stream << "property uchar red\n";
237  stream << "property uchar green\n";
238  stream << "property uchar blue\n";
239  }
240  if (hasIntensities)
241  {
242  stream << "property float intensity\n";
243  }
244  stream << "end_header\n";
245  }
246  else
247  {
248  success = false;
249  }
250 
251  // Add the temporarily buffered data part to the ofstream
252  stream << strstream.rdbuf();
253 
254  // Close file
255  stream.close();
256 
257  return success;
258 }
259 
261 
263 } // namespace visionary
VisionaryEndian.h
visionary::PointXYZ::x
float x
Definition: PointXYZ.h:12
visionary::nativeToLittleEndian
T nativeToLittleEndian(const T &val)
Definition: VisionaryEndian.h:284
visionary::PointCloudPlyWriter::~PointCloudPlyWriter
virtual ~PointCloudPlyWriter()
visionary::INVALID_AS_NAN
@ INVALID_AS_NAN
Not a Number = nan.
Definition: PointCloudPlyWriter.h:18
visionary
Definition: MD5.cpp:44
visionary::INVALID_AS_ZERO
@ INVALID_AS_ZERO
Zero = 0.0.
Definition: PointCloudPlyWriter.h:21
f
f
visionary::PointXYZ
Definition: PointXYZ.h:10
visionary::INVALID_SKIP
@ INVALID_SKIP
Skip => don't print the number.
Definition: PointCloudPlyWriter.h:24
visionary::PointXYZ::z
float z
Definition: PointXYZ.h:14
visionary::PointCloudPlyWriter::WriteFormatPLY
static bool WriteFormatPLY(const char *filename, const std::vector< PointXYZ > &points, bool useBinary, InvalidPointPresentation presentation=INVALID_AS_NAN)
Save a point cloud to a file in Polygon File Format (PLY), see: https://en.wikipedia....
Definition: PointCloudPlyWriter.cpp:15
PointCloudPlyWriter.h
boost::iterators::i
D const & i
Definition: iterator_facade.hpp:956
visionary::PointCloudPlyWriter::PointCloudPlyWriter
PointCloudPlyWriter()
visionary::PointXYZ::y
float y
Definition: PointXYZ.h:13
visionary::InvalidPointPresentation
InvalidPointPresentation
Definition: PointCloudPlyWriter.h:15


sick_visionary_ros
Author(s): SICK AG TechSupport 3D Snapshot
autogenerated on Thu Feb 8 2024 03:45:47