licornea_tools
ply_importer.cc
Go to the documentation of this file.
1 #include "ply_importer.h"
2 #include "string.h"
3 #include <algorithm>
4 
5 namespace tlz {
6 
7 template<typename Point>
8 void ply_importer::read_ascii_(Point* buffer, std::size_t n) {
9  std::string line;
10  std::vector<const char*> properties(number_of_properties_, nullptr);
11 
12  while(n--) {
13  read_line_(line);
14 
15  auto property = properties.begin();
16 
17  bool last_space = true;
18  for(const char& c : line) {
19  bool space = std::isspace(c);
20  if(last_space == space) continue;
21  else if(last_space) *(property++) = &c;
22  last_space = space;
23  }
24 
25  read_ascii_point_(*buffer, properties.data());
26  ++buffer;
27  }
28 }
29 
30 
31 template<typename Point>
32 void ply_importer::read_binary_(Point* out, std::size_t n) {
33  std::streamsize length = n * vertex_length_;
34  std::unique_ptr<byte[]> buffer(new byte[length]);
35  file_.read(reinterpret_cast<char*>(buffer.get()), length);
36 
37  byte* buffer_end = buffer.get() + length;
38  for(byte* in = buffer.get(); in != buffer_end; in += vertex_length_, ++out)
39  read_binary_point_(*out, in);
40 }
41 
42 
43 template<typename T>
44 T ply_importer::read_binary_property_(const property& prop, byte* data) const {
45  std::size_t sz = property_type_size_(prop.type);
46  data += prop.offset;
47  if(! is_host_endian_binary_()) flip_endianness(data, sz);
48  switch(prop.type) {
49  case int8: return static_cast<T>( *reinterpret_cast<const std::int8_t*>(data) );
50  case uint8: return static_cast<T>( *reinterpret_cast<const std::uint8_t*>(data) );
51  case int16: return static_cast<T>( *reinterpret_cast<const std::int16_t*>(data) );
52  case uint16: return static_cast<T>( *reinterpret_cast<const std::uint16_t*>(data) );
53  case int32: return static_cast<T>( *reinterpret_cast<const std::int32_t*>(data) );
54  case uint32: return static_cast<T>( *reinterpret_cast<const std::uint32_t*>(data) );
55  default: break;
56  }
57  if(host_has_iec559_float) switch(prop.type) {
58  case float32: return static_cast<T>( *reinterpret_cast<const float*>(data) );
59  case float64: return static_cast<T>( *reinterpret_cast<const double*>(data) );
60  default: break;
61  }
62  return T();
63 }
64 
65 
66 
67 ply_importer::property_type ply_importer::identify_property_type_(const std::string& nm) {
68  if(nm == "char" || nm == "int8") return int8;
69  else if(nm == "uchar" || nm == "uint8") return uint8;
70  else if(nm == "short" || nm == "int16") return int16;
71  else if(nm == "ushort" || nm == "uint16") return uint16;
72  else if(nm == "int" || nm == "int32") return int32;
73  else if(nm == "uint" || nm == "uint32") return uint32;
74  else if(nm == "float" || nm == "float32") return float32;
75  else if(nm == "double" || nm == "float64") return float64;
76  else if(nm == "list") return list;
77  else throw ply_importer_error(std::string("Unknown property type ") + nm);
78 }
79 
80 
81 ply_importer::property* ply_importer::identify_property_(const std::string& nm_orig) {
82  std::string nm = to_lower(nm_orig);
83  if(nm == "x") return &x_;
84  else if(nm == "y") return &y_;
85  else if(nm == "z") return &z_;
86  else if(nm == "nx") return &nx_;
87  else if(nm == "ny") return &ny_;
88  else if(nm == "nz") return &nz_;
89  else if(nm == "r" || nm == "red") return &r_;
90  else if(nm == "g" || nm == "green") return &g_;
91  else if(nm == "b" || nm == "blue") return &b_;
92  else if(nm == "weight") return &w_;
93  else return nullptr;
94 }
95 
96 
97 void ply_importer::read_header_() {
98  std::string line;
99 
100  std::ifstream::pos_type data_start = 0; // File offset where data starts (aka after 'end_header').
101  std::size_t number_of_elements_before_vertex_data = 0; // Number of data entries before start of vertex data. (for ascii, number of lines)
102  std::ifstream::off_type vertex_data_start_offset = 0; // Number of bytes before start of vertex data. (for ascii, determined in postprocessing)
103 
104  std::ptrdiff_t vertex_property_index = 0; // Index of current vertex property.
105  std::ptrdiff_t vertex_property_data_offset = 0; // Data offset of current vertex property for binary.
106 
107 
108  // First line must be 'ply'.
109  read_line_(line);
110  if(line != "ply") throw ply_importer_error("Not PLY file (First line not 'ply')");
111 
112  // Second line must indicate format.
113  read_line_(line);
114  if(line == "format binary_little_endian 1.0") format_ = binary_little_endian;
115  else if(line == "format binary_big_endian 1.0") format_ = binary_big_endian;
116  else if(line == "format ascii 1.0") format_ = ascii;
117  else throw ply_importer_error("Unknown PLY format: " + line);
118 
119  // Definitions of elements follow.
120  // State before_vertex: Skip definitions until vertex element definition starts.
121  // within_vertex: Reading vertex property definitions.
122  // after_vertex: Skip other defitions after it, until end of header.
123  enum { before_vertex_definition, within_vertex_definition, after_vertex_definition } state = before_vertex_definition;
124  std::size_t number_of_elements = 0; // Number of elements of current type.
125  do {
126  read_line_(line);
127 
128  if(line.substr(0, 8) == "comment ") continue; // Skip comments.
129  if(line.substr(0, 9) == "obj_info ") continue;
130 
131  if(line.substr(0, 8) == "element ") { // Read element definition start.
132  auto pos = line.find(' ', 8);
133  std::string element = line.substr(8, pos-8);
134  number_of_elements = stoul(line.substr(pos + 1)); // Number of elements.
135  if(element == "vertex") {
136  // Vertex definitions start.
137  if(state != before_vertex_definition) throw ply_importer_error("More than one vertex element definitions.");
138  state = within_vertex_definition;
139  number_of_vertices_ = number_of_elements; // Get number of vertices.
140  } else if(state == within_vertex_definition) {
141  // Another element definition after vertices.
142  state = after_vertex_definition;
143  } else if(state == before_vertex_definition) {
144  // Element before vertex definition.
145  // Count number of lines before vertex data start (for ascii).
146  number_of_elements_before_vertex_data += number_of_elements;
147  }
148 
149  } else if(line.substr(0, 9) == "property ") { // Reading property definition for current element.
150  if(state == after_vertex_definition) continue; // After vertices: skip
151 
152  // Data type and property name
153  auto pos = line.find(' ', 9);
154  std::string name = line.substr(pos + 1);
155  property_type type = identify_property_type_(line.substr(9, pos-9));
156  if(type == list) throw ply_importer_error("List property type not supported.");
157  std::size_t type_size = property_type_size_(type);
158 
159  if(is_binary() && state == before_vertex_definition) {
160  // If before vertex, count this entries of this property in total length of data before vertex data (only for binary).
161  vertex_data_start_offset += number_of_elements * type_size;
162 
163  } else if(state == within_vertex_definition) {
164  // When withnin vertex definition, capture XYZRGB data offsets.
165  property* prop = identify_property_(name);
166  if(prop) {
167  // Store data offset (for binary) and index (for ascii) of property
168  // If prop==null, unknown property
169  prop->type = type;
170  prop->offset = vertex_property_data_offset;
171  prop->index = vertex_property_index;
172  }
173  // Increment data offset and index
174  vertex_property_data_offset += type_size;
175  ++vertex_property_index;
176  }
177 
178  } else if(line.substr(0, 10) == "end_header") { // End of header; data follows.
179  if(state == before_vertex_definition) throw ply_importer_error("No vertex element definition.");
180  data_start = file_.tellg(); // Start of data.
181 
182  } else if(! std::all_of(line.begin(), line.end(), isspace)) {
183  // Invalid line, not all whitespace.
184  throw ply_importer_error("Invalid line encountered: " + line);
185  }
186  } while(! data_start);
187 
188  if(!x_ || !y_ || !z_) throw ply_importer_error("X, Y, Z coordinate properties not defined.");
189  has_rgb_ = (r_ && g_ && b_);
190  has_normal_ = (nx_ && ny_ && nz_);
191  has_weight_ = (bool)w_;
192 
193  vertex_length_ = vertex_property_data_offset;
194  number_of_properties_ = vertex_property_index;
195 
196  if(is_binary()) {
197  // Directly calculate vertex data start
198  vertex_data_start_ = data_start + vertex_data_start_offset;
199  } else {
200  // Byte offset unknown, need to skip lines (of variable lengths) to it.
201  skip_lines_(number_of_elements_before_vertex_data);
202  vertex_data_start_ = file_.tellg();
203  }
204 }
205 
206 
208  file_.seekg(vertex_data_start_);
209  current_element_ = 0;
210 }
211 
212 
213 ply_importer::ply_importer(const std::string& filename, line_delimitor ld) :
214 file_(filename, std::ios_base::in | std::ios_base::binary),
215 line_delimitor_(ld != line_delimitor::unknown ? ld : detect_line_delimitor(file_)) {
216  read_header_();
217  rewind();
218 }
219 
220 
221 std::size_t ply_importer::property_type_size_(property_type t) {
222  switch(t) {
223  case int8: case uint8: return 1;
224  case int16: case uint16: return 2;
225  case int32: case uint32: return 4;
226  case float32: return 4;
227  case float64: return 8;
228  default: return 0;
229  }
230 }
231 
232 
233 void ply_importer::read_ascii_point_(point_xyz& pt, const char* props[]) const {
234  pt = point_xyz(
235  strtof(props[x_.index], nullptr),
236  strtof(props[y_.index], nullptr),
237  strtof(props[z_.index], nullptr)
238  );
239 }
240 
241 
242 
243 void ply_importer::read_ascii_point_(point_full& pt, const char* props[]) const {
244  pt = point_xyz(
245  strtof(props[x_.index], nullptr),
246  strtof(props[y_.index], nullptr),
247  strtof(props[z_.index], nullptr)
248  );
249  if(has_rgb_) pt.color = rgb_color(
250  strtol(props[r_.index], nullptr, 10),
251  strtol(props[g_.index], nullptr, 10),
252  strtol(props[b_.index], nullptr, 10)
253  );
254 }
255 
256 
257 void ply_importer::read_binary_point_(point_xyz& pt, byte* data) const {
258  pt = point_xyz(
259  read_binary_property_<float>(x_, data),
260  read_binary_property_<float>(y_, data),
261  read_binary_property_<float>(z_, data)
262  );
263 }
264 
265 
266 void ply_importer::read_binary_point_(point_full& pt, byte* data) const {
267  pt = point_xyz(
268  read_binary_property_<float>(x_, data),
269  read_binary_property_<float>(y_, data),
270  read_binary_property_<float>(z_, data)
271  );
272  if(has_rgb_) pt.color = rgb_color(
273  read_binary_property_<std::uint8_t>(r_, data),
274  read_binary_property_<std::uint8_t>(g_, data),
275  read_binary_property_<std::uint8_t>(b_, data)
276  );
277 }
278 
279 
280 void ply_importer::read(point_xyz* buffer, std::size_t n) {
281  if(current_element_ + n > number_of_vertices_)
282  throw ply_importer_error("attempted to read beyond bounds");
283 
284  if(is_ascii()) read_ascii_(buffer, n);
285  else read_binary_(buffer, n);
286 
287  current_element_ += n;
288 }
289 
290 
291 void ply_importer::read(point_full* buffer, std::size_t n) {
292  if(current_element_ + n > number_of_vertices_)
293  throw ply_importer_error("attempted to read beyond bounds");
294 
295  if(is_ascii()) read_ascii_(buffer, n);
296  else read_binary_(buffer, n);
297 
298  current_element_ += n;
299 }
300 
301 
302 std::ptrdiff_t ply_importer::tell() const {
303  return current_element_;
304 }
305 
306 
307 std::size_t ply_importer::size() const {
308  return number_of_vertices_;
309 }
310 
311 }
bool is_ascii() const
Definition: ply_importer.h:102
ply_importer(const std::string &filename, line_delimitor ld=line_delimitor::unknown)
std::ptrdiff_t tell() const
rgb_color color
Definition: point.h:42
bool is_binary() const
Definition: ply_importer.h:101
const bool host_has_iec559_float
Definition: io.cc:27
RGB color, 8 bit.
Definition: color.h:11
line_delimitor
Definition: io.h:11
void read(point_xyz *, std::size_t sz)
line_delimitor detect_line_delimitor(std::istream &str, std::size_t max_offset)
Detects line delimitor used in given file.
Definition: io.cc:34
std::string to_lower(const std::string &s_orig)
Definition: string.cc:42
std::uint8_t byte
Definition: common.h:17
std::size_t size() const
void flip_endianness(byte *data, std::size_t sz)
Definition: io.cc:100