MVE - Multi-View Environment mve-devel
Loading...
Searching...
No Matches
mesh_io_ply.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2015, Simon Fuhrmann
3 * TU Darmstadt - Graphics, Capture and Massively Parallel Computing
4 * All rights reserved.
5 *
6 * This software may be modified and distributed under the terms
7 * of the BSD 3-Clause license. See the LICENSE.txt file for details.
8 */
9
10#include <iostream>
11#include <fstream>
12#include <stdexcept>
13#include <cstring>
14#include <cerrno>
15
16#include "util/exception.h"
17#include "util/tokenizer.h"
18#include "math/vector.h"
19#include "math/matrix.h"
20#include "mve/depthmap.h"
21#include "mve/mesh_io_ply.h"
22
25
26template <typename T>
27T
28ply_read_value (std::istream& input, PLYFormat format)
29{
30 T value;
31 switch (format)
32 {
33 case PLY_ASCII:
34 input >> value;
35 return value;
36
37 case PLY_BINARY_LE:
38 input.read(reinterpret_cast<char*>(&value), sizeof(T));
39 return util::system::letoh(value);
40
41 case PLY_BINARY_BE:
42 input.read(reinterpret_cast<char*>(&value), sizeof(T));
43 return util::system::betoh(value);
44
45 default:
46 throw std::invalid_argument("Invalid data format");
47 }
48}
49
50/*
51 * Template specialization for 'unsigned char', as the generic template
52 * function will handle it incorrectly (reads one char, not the number).
53 */
54template <>
55unsigned char
56ply_read_value<unsigned char> (std::istream& input, PLYFormat format)
57{
58 switch (format)
59 {
60 case PLY_ASCII:
61 {
62 int temp;
63 input >> temp;
64 return static_cast<unsigned char>(temp);
65 }
66
67 case PLY_BINARY_LE:
68 case PLY_BINARY_BE:
69 {
70 unsigned char value;
71 input.read(reinterpret_cast<char*>(&value), 1);
72 return value;
73 }
74
75 default:
76 throw std::invalid_argument("Invalid data format");
77 }
78}
79
80/* Declare specialization for 'char' but leave it undefined for now. */
81template <>
82char
83ply_read_value<char> (std::istream& input, PLYFormat format);
84
85/* Explicit template instantiation for 'float'. */
86template
87float
88ply_read_value<float> (std::istream& input, PLYFormat format);
89
90/* Explicit template instantiation for 'double'. */
91template
92double
93ply_read_value<double> (std::istream& input, PLYFormat format);
94
95/* Explicit template instantiation for 'unsigned int'. */
96template
97unsigned int
98ply_read_value<unsigned int> (std::istream& input, PLYFormat format);
99
100/* Explicit template instantiation for 'int'. */
101template
102int
103ply_read_value<int> (std::istream& input, PLYFormat format);
104
105/* ---------------------------------------------------------------- */
106
107/* Converts 'num' float values in [0,1] to unsigned char in [0,255]. */
108void
109ply_color_convert (float const* src, unsigned char* dest, int num = 3)
110{
111 for (int c = 0; c < num; ++c)
112 {
113 float color = src[c];
114 color = color * 255.0f;
115 color = std::min(255.0f, std::max(0.0f, color));
116 dest[c] = (unsigned char)(color + 0.5f);
117 }
118}
119
120/* ---------------------------------------------------------------- */
121// TODO check token amount to prevent undefined access
122
124load_ply_mesh (std::string const& filename)
125{
126 /* Precondition checks. */
127 if (filename.empty())
128 throw std::invalid_argument("No filename given");
129
130 /* Open file. */
131 std::ifstream input(filename.c_str(), std::ios::binary);
132 if (!input.good())
133 throw util::FileException(filename, std::strerror(errno));
134
135 /* Start parsing. */
136 std::string buffer;
137 input >> buffer; /* Read "ply" file signature. */
138 if (buffer != "ply")
139 {
140 input.close();
141 throw util::Exception("File format not recognized as PLY-model");
142 }
143
144 /* Discard the rest of the line. */
145 std::getline(input, buffer);
146
147 PLYFormat ply_format = PLY_UNKNOWN;
148 std::size_t num_faces = 0;
149 std::size_t num_vertices = 0;
150 std::size_t num_grid = 0;
151 std::size_t num_tristrips = 0;
152 std::size_t grid_cols = 0;
153 std::size_t grid_rows = 0;
154 std::vector<PLYVertexProperty> v_format;
155 std::vector<PLYFaceProperty> f_format;
156
157 bool critical = false;
158 bool reading_verts = false;
159 bool reading_faces = false;
160 bool reading_grid = false;
161 bool reading_tristrips = false;
162 std::size_t skip_bytes = 0;
163
164 while (input.good())
165 {
166 std::getline(input, buffer);
169
170 //std::cout << "Buffer: " << buffer << std::endl;
171
172 if (buffer == "end_header")
173 break;
174
175 util::Tokenizer header;
176 header.split(buffer);
177
178 if (header.empty())
179 continue;
180
181 if (header[0] == "format")
182 {
183 /* Determine the format. */
184 if (header[1] == "ascii")
185 ply_format = PLY_ASCII;
186 else if (header[1] == "binary_little_endian")
187 ply_format = PLY_BINARY_LE;
188 else if (header[1] == "binary_big_endian")
189 ply_format = PLY_BINARY_BE;
190 else
191 ply_format = PLY_UNKNOWN;
192 }
193 else if (header[0] == "comment")
194 {
195 /* Print all comments to STDOUT and forget data. */
196 std::cout << "PLY Loader: " << buffer << std::endl;
197 }
198 else if (header[0] == "element")
199 {
200 reading_faces = false;
201 reading_verts = false;
202 reading_grid = false;
203 reading_tristrips = false;
204 if (header[1] == "vertex")
205 {
206 reading_verts = true;
207 num_vertices = util::string::convert<std::size_t>(header[2]);
208 }
209 else if (header[1] == "face")
210 {
211 reading_faces = true;
212 num_faces = util::string::convert<std::size_t>(header[2]);
213 }
214 else if (header[1] == "range_grid")
215 {
216 reading_grid = true;
217 num_grid = util::string::convert<std::size_t>(header[2]);
218 }
219 else if (header[1] == "tristrips")
220 {
221 reading_tristrips = true;
222 num_tristrips = util::string::convert<std::size_t>(header[2]);
223 }
224 else
225 {
226 std::cout << "PLY Loader: Element \"" << header[1]
227 << "\" not recognized" << std::endl;
228 }
229
230 /* Aligned range images in PLY format may have a camera inside. */
231 if (header[1] == "camera")
232 skip_bytes = 23 * 4;
233 }
234 else if (header[0] == "obj_info")
235 {
236 if (header[1] == "num_cols")
237 grid_cols = util::string::convert<std::size_t>(header[2]);
238 else if (header[1] == "num_rows")
239 grid_rows = util::string::convert<std::size_t>(header[2]);
240 }
241 else if (header[0] == "property")
242 {
243 if (reading_verts)
244 {
245 /* List of accepted and handled attributes. */
246 if (header[1] == "float" || header[1] == "float32")
247 {
248 /* Accept float x,y,z values. */
249 if (header[2] == "x")
250 v_format.push_back(PLY_V_FLOAT_X);
251 else if (header[2] == "y")
252 v_format.push_back(PLY_V_FLOAT_Y);
253 else if (header[2] == "z")
254 v_format.push_back(PLY_V_FLOAT_Z);
255 else if (header[2] == "nx")
256 v_format.push_back(PLY_V_FLOAT_NX);
257 else if (header[2] == "ny")
258 v_format.push_back(PLY_V_FLOAT_NY);
259 else if (header[2] == "nz")
260 v_format.push_back(PLY_V_FLOAT_NZ);
261 else if (header[2] == "r" || header[2] == "red")
262 v_format.push_back(PLY_V_FLOAT_R);
263 else if (header[2] == "g" || header[2] == "green")
264 v_format.push_back(PLY_V_FLOAT_G);
265 else if (header[2] == "b" || header[2] == "blue")
266 v_format.push_back(PLY_V_FLOAT_B);
267 else if (header[2] == "u")
268 v_format.push_back(PLY_V_FLOAT_U);
269 else if (header[2] == "v")
270 v_format.push_back(PLY_V_FLOAT_V);
271 else if (header[2] == "confidence")
272 v_format.push_back(PLY_V_FLOAT_CONF);
273 else if (header[2] == "value")
274 v_format.push_back(PLY_V_FLOAT_VALUE);
275 else
276 v_format.push_back(PLY_V_IGNORE_FLOAT);
277 }
278 /* List of accepted and handled attributes. */
279 else if (header[1] == "double" || header[1] == "float64")
280 {
281 /* Accept float x,y,z values. */
282 if (header[2] == "x")
283 v_format.push_back(PLY_V_DOUBLE_X);
284 else if (header[2] == "y")
285 v_format.push_back(PLY_V_DOUBLE_Y);
286 else if (header[2] == "z")
287 v_format.push_back(PLY_V_DOUBLE_Z);
288 else
289 v_format.push_back(PLY_V_IGNORE_DOUBLE);
290 }
291 else if (header[1] == "uchar" || header[1] == "uint8")
292 {
293 /* Accept uchar r,g,b values. */
294 if (header[2] == "r" || header[2] == "red"
295 || header[2] == "diffuse_red")
296 v_format.push_back(PLY_V_UINT8_R);
297 else if (header[2] == "g" || header[2] == "green"
298 || header[2] == "diffuse_green")
299 v_format.push_back(PLY_V_UINT8_G);
300 else if (header[2] == "b" || header[2] == "blue"
301 || header[2] == "diffuse_blue")
302 v_format.push_back(PLY_V_UINT8_B);
303 else
304 v_format.push_back(PLY_V_IGNORE_UINT8);
305 }
306 else if (header[1] == "int")
307 {
308 /* Ignore int data types. */
309 v_format.push_back(PLY_V_IGNORE_UINT32);
310 }
311 else
312 {
313 /* Panic on unhandled data types. */
314 std::cout << "PLY Loader: Unrecognized vertex property \""
315 << buffer << "\"" << std::endl;
316 critical = true;
317 continue;
318 }
319 }
320 else if (reading_faces)
321 {
322 if (header[1] == "list")
323 f_format.push_back(PLY_F_VERTEX_INDICES);
324 else if (header[1] == "int")
325 f_format.push_back(PLY_F_IGNORE_UINT32);
326 else if (header[1] == "uchar")
327 f_format.push_back(PLY_F_IGNORE_UINT8);
328 else if (header[1] == "float")
329 f_format.push_back(PLY_F_IGNORE_FLOAT);
330 else
331 {
332 std::cout << "PLY Loader: Unrecognized face property \""
333 << buffer << "\"" << std::endl;
334 critical = true;
335 }
336 }
337 else if (reading_grid)
338 {
339 if (header[1] != "list")
340 {
341 std::cout << "PLY Loader: Unrecognized grid property \""
342 << buffer << "\"" << std::endl;
343 critical = true;
344 }
345 }
346 else if (reading_tristrips)
347 {
348 if (header[1] != "list")
349 {
350 std::cout << "PLY Loader: Unrecognized triangle strips "
351 "property \"" << buffer << "\"" << std::endl;
352 critical = true;
353 }
354 }
355 else
356 {
357 std::cout << "PLY Loader: Skipping property \""
358 << header[1] << "\" without element." << std::endl;
359 }
360 }
361 }
362
363 if (critical || ply_format == PLY_UNKNOWN)
364 {
365 input.close();
366 throw util::Exception("PLY file encoding not recognized by parser");
367 }
368
369 /* Create a new triangle mesh. */
370 TriangleMesh::Ptr mesh = TriangleMesh::create();
371 TriangleMesh::VertexList& vertices = mesh->get_vertices();
372 TriangleMesh::FaceList& faces = mesh->get_faces();
373 TriangleMesh::ColorList& vcolors = mesh->get_vertex_colors();
374 TriangleMesh::ConfidenceList& vconfs = mesh->get_vertex_confidences();
375 TriangleMesh::ValueList& vvalues = mesh->get_vertex_values();
376 TriangleMesh::TexCoordList& tcoords = mesh->get_vertex_texcoords();
377 TriangleMesh::NormalList& vnormals = mesh->get_vertex_normals();
378
379 bool want_colors = false;
380 bool want_vnormals = false;
381 bool want_tex_coords = false;
382
383 for (std::size_t i = 0; i < v_format.size(); ++i)
384 {
385 switch (v_format[i])
386 {
387 case PLY_V_UINT8_R:
388 case PLY_V_UINT8_G:
389 case PLY_V_UINT8_B:
390 case PLY_V_FLOAT_R:
391 case PLY_V_FLOAT_G:
392 case PLY_V_FLOAT_B:
393 want_colors = true;
394 break;
395
396 case PLY_V_FLOAT_NX:
397 case PLY_V_FLOAT_NY:
398 case PLY_V_FLOAT_NZ:
399 want_vnormals = true;
400 break;
401
402 case PLY_V_FLOAT_U:
403 case PLY_V_FLOAT_V:
404 want_tex_coords = true;
405 break;
406
407 default:
408 break;
409 }
410 }
411
412 /* Skip some bytes. Usually because of unknown PLY elements. */
413 if (skip_bytes > 0)
414 {
415 std::cout << "PLY Loader: Skipping " << skip_bytes
416 << " bytes." << std::endl;
417 input.ignore(skip_bytes);
418 }
419
420 /* Start reading the vertex data. */
421 std::cout << "Reading PLY: " << num_vertices << " verts..." << std::flush;
422
423 vertices.reserve(num_vertices);
424 if (want_colors)
425 vcolors.reserve(num_vertices);
426 if (want_vnormals)
427 vnormals.reserve(num_vertices);
428
429 bool eof = false;
430 for (std::size_t i = 0; !eof && i < num_vertices; ++i)
431 {
432 math::Vec3f vertex(0.0f, 0.0f, 0.0f);
433 math::Vec3f vnormal(0.0f, 0.0f, 0.0f);
434 math::Vec4f color(1.0f, 0.5f, 0.5f, 1.0f); // Make it ugly by default
435 math::Vec2f tex_coord(0.0f, 0.0f);
436
437 for (std::size_t n = 0; n < v_format.size(); ++n)
438 {
439 PLYVertexProperty elem = v_format[n];
440 switch (elem)
441 {
442 case PLY_V_FLOAT_X:
443 case PLY_V_FLOAT_Y:
444 case PLY_V_FLOAT_Z:
445 vertex[(int)elem - PLY_V_FLOAT_X]
446 = ply_read_value<float>(input, ply_format);
447 break;
448
449 case PLY_V_DOUBLE_X:
450 case PLY_V_DOUBLE_Y:
451 case PLY_V_DOUBLE_Z:
452 vertex[(int)elem - PLY_V_DOUBLE_X]
453 = ply_read_value<double>(input, ply_format);
454 break;
455
456 case PLY_V_FLOAT_NX:
457 case PLY_V_FLOAT_NY:
458 case PLY_V_FLOAT_NZ:
459 vnormal[(int)elem - PLY_V_FLOAT_NX]
460 = ply_read_value<float>(input, ply_format);
461 break;
462
463 case PLY_V_UINT8_R:
464 case PLY_V_UINT8_G:
465 case PLY_V_UINT8_B:
466 color[(int)elem - PLY_V_UINT8_R]
467 = (float)ply_read_value<unsigned char>(input, ply_format)
468 * (1.0f / 255.0f);
469 break;
470
471 case PLY_V_FLOAT_R:
472 case PLY_V_FLOAT_G:
473 case PLY_V_FLOAT_B:
474 color[(int)elem - PLY_V_FLOAT_R]
475 = ply_read_value<float>(input, ply_format);
476 break;
477
478 case PLY_V_FLOAT_U:
479 case PLY_V_FLOAT_V:
480 tex_coord[(int)elem - PLY_V_FLOAT_U]
481 = ply_read_value<float>(input, ply_format);
482 break;
483
484 case PLY_V_FLOAT_CONF:
485 vconfs.push_back(ply_read_value<float>(input, ply_format));
486 break;
487
489 vvalues.push_back(ply_read_value<float>(input, ply_format));
490 break;
491
493 ply_read_value<float>(input, ply_format);
494 break;
495
497 ply_read_value<double>(input, ply_format);
498 break;
499
501 ply_read_value<unsigned int>(input, ply_format);
502 break;
503
505 ply_read_value<unsigned char>(input, ply_format);
506 break;
507
508 default:
509 throw std::runtime_error("Unhandled PLY vertex property");
510 }
511 }
512
513 vertices.push_back(vertex);
514 if (want_vnormals)
515 vnormals.push_back(vnormal);
516 if (want_colors)
517 vcolors.push_back(color);
518 if (want_tex_coords)
519 tcoords.push_back(tex_coord);
520
521 eof = input.eof();
522 }
523
524 /* Start reading the face data. */
525 if (num_faces > 0)
526 std::cout << " " << num_faces << " faces..." << std::flush;
527 faces.reserve(num_faces * 3);
528 for (std::size_t i = 0; !eof && i < num_faces; ++i)
529 {
530 for (std::size_t n = 0; n < f_format.size(); ++n)
531 {
532 PLYFaceProperty elem = f_format[n];
533 switch (elem)
534 {
536 {
537 /* Read the amount of vertex indices for the face. */
538 unsigned char n_verts = ply_read_value<unsigned char>(input, ply_format);
539
540 if (n_verts == 3)
541 {
542 /* Process 3-vertex faces. */
543 faces.push_back(ply_read_value<unsigned int>(input, ply_format));
544 faces.push_back(ply_read_value<unsigned int>(input, ply_format));
545 faces.push_back(ply_read_value<unsigned int>(input, ply_format));
546 }
547 else if (n_verts == 4)
548 {
549 /* Process 4-vertex faces. */
550 unsigned int a = ply_read_value<unsigned int>(input, ply_format);
551 unsigned int b = ply_read_value<unsigned int>(input, ply_format);
552 unsigned int c = ply_read_value<unsigned int>(input, ply_format);
553 unsigned int d = ply_read_value<unsigned int>(input, ply_format);
554#if 1
555 /* Interpret as tetmesh. */
556 faces.push_back(a);
557 faces.push_back(b);
558 faces.push_back(c);
559 faces.push_back(d);
560#else
561 /* Triangulate 4-polygon (naive technique!). */
562 faces.push_back(a);
563 faces.push_back(b);
564 faces.push_back(c);
565
566 faces.push_back(c);
567 faces.push_back(d);
568 faces.push_back(a);
569#endif
570 }
571 else if (!input.eof())
572 {
573 std::cout << "PLY Loader: Ignoring face with "
574 << static_cast<int>(n_verts)
575 << " vertices!" << std::endl;
576 for (int vid = 0; vid < n_verts; ++vid)
577 ply_read_value<unsigned int>(input, ply_format);
578 }
579 break;
580 }
581
583 ply_read_value<int>(input, ply_format);
584 break;
586 ply_read_value<unsigned char>(input, ply_format);
587 break;
589 ply_read_value<float>(input, ply_format);
590 break;
591
592 default:
593 throw std::runtime_error("Unhandled PLY face property");
594 }
595
596 eof = input.eof();
597 }
598 }
599
600 /* Start reading grid data. */
601 if (!eof && num_grid != 0 && num_grid == grid_rows * grid_cols)
602 {
603 std::cout << " " << num_grid << " range grid values..." << std::flush;
604 Image<unsigned int> img(grid_cols, grid_rows, 1);
605 for (std::size_t i = 0; i < num_grid; ++i)
606 {
607 unsigned char indicator = ply_read_value<unsigned char>(input, ply_format);
608 if (!indicator)
609 img[i] = static_cast<unsigned int>(-1);
610 else
611 img[i] = ply_read_value<unsigned int>(input, ply_format);
612
613 eof = input.eof();
614 }
616 }
617
618 /* Start reading triangle strips data. */
619 if (!eof && num_tristrips)
620 {
621 std::cout << " triangle strips..." << std::flush;
622 for (std::size_t i = 0; i < num_tristrips; ++i)
623 {
624 unsigned int num = ply_read_value<unsigned int>(input, ply_format);
625 int last_id1 = -1, last_id2 = -1;
626 bool inverted = false;
627 for (unsigned int j = 0; j < num; ++j)
628 {
629 int idx = ply_read_value<int>(input, ply_format);
630 if (idx < 0)
631 {
632 last_id1 = -1;
633 last_id2 = -1;
634 inverted = false;
635 continue;
636 }
637 if (last_id1 != -1)
638 {
639 faces.push_back(inverted ? last_id2 : last_id1);
640 faces.push_back(inverted ? last_id1 : last_id2);
641 faces.push_back(idx);
642 inverted = !inverted;
643 }
644 last_id1 = last_id2;
645 last_id2 = idx;
646 }
647 }
648 }
649
650 /* Close the file stream. */
651 input.close();
652 std::cout << " done." << std::endl;
653 if (eof)
654 std::cout << "WARNING: Premature EOF detected!" << std::endl;
655
656 return mesh;
657}
658
659/* ---------------------------------------------------------------- */
660
661void
662load_xf_file (std::string const& filename, float* ctw)
663{
664 if (filename.empty())
665 throw std::invalid_argument("No filename given");
666
667 std::ifstream in(filename.c_str());
668 if (!in.good())
669 throw util::FileException(filename, std::strerror(errno));
670
671 int values_read = 0;
672 while (values_read < 16 && !in.eof())
673 {
674 in >> ctw[values_read];
675 values_read += 1;
676 }
677
678 in.close();
679
680 if (values_read < 16)
681 throw util::Exception("Unexpected EOF");
682}
683
684/* ---------------------------------------------------------------- */
685
686void
687save_ply_mesh (TriangleMesh::ConstPtr mesh, std::string const& filename,
688 SavePLYOptions const& options)
689{
690 if (mesh == nullptr)
691 throw std::invalid_argument("Null mesh given");
692 if (filename.empty())
693 throw std::invalid_argument("No filename given");
694
695 TriangleMesh::VertexList const& verts(mesh->get_vertices());
696 TriangleMesh::ColorList const& vcolors(mesh->get_vertex_colors());
697 TriangleMesh::NormalList const& vnormals(mesh->get_vertex_normals());
698 TriangleMesh::ConfidenceList const& conf(mesh->get_vertex_confidences());
699 TriangleMesh::ValueList const& vvalues(mesh->get_vertex_values());
700 TriangleMesh::FaceList const& faces(mesh->get_faces());
701 TriangleMesh::ColorList const& fcolors(mesh->get_face_colors());
702 TriangleMesh::NormalList const& fnormals(mesh->get_face_normals());
703
704 if (faces.size() % options.verts_per_simplex != 0)
705 throw std::invalid_argument("Invalid amount of face indices");
706 std::size_t face_amount = faces.size() / options.verts_per_simplex;
707
708 bool write_vcolors = options.write_vertex_colors;
709 write_vcolors = write_vcolors && verts.size() == vcolors.size();
710 bool write_vnormals = options.write_vertex_normals;
711 write_vnormals = write_vnormals && verts.size() == vnormals.size();
712 bool write_vconfidences = options.write_vertex_confidences;
713 write_vconfidences = write_vconfidences && verts.size() == conf.size();
714 bool write_vvalues = options.write_vertex_values;
715 write_vvalues = write_vvalues && verts.size() == vvalues.size();
716 bool write_fcolors = options.write_face_colors && face_amount;
717 write_fcolors = write_fcolors && fcolors.size() == face_amount;
718 bool write_fnormals = options.write_face_normals && face_amount;
719 write_fnormals = write_fnormals && fnormals.size() == face_amount;
720
721 std::string format_str = (options.format_binary
722 ? "binary_little_endian"
723 : "ascii");
724
725 /* Open output file. */
726 std::ofstream out(filename.c_str(), std::ios::binary);
727 if (!out.good())
728 throw util::FileException(filename, std::strerror(errno));
729
730 std::cout << "Writing PLY file (" << verts.size() << " verts"
731 << (write_vcolors ? ", with colors" : "")
732 << (write_vnormals ? ", with normals" : "")
733 << (write_vconfidences ? ", with confidences" : "")
734 << (write_vvalues ? ", with values" : "")
735 << ", " << face_amount << " faces"
736 << (write_fcolors ? ", with colors" : "")
737 << (write_fnormals ? ", with normals" : "")
738 << ")... " << std::flush;
739
740 /* Generate PLY header. */
741 out << "ply" << std::endl;
742 out << "format " << format_str << " 1.0" << std::endl;
743 out << "comment Export generated by libmve" << std::endl;
744 out << "element vertex " << verts.size() << std::endl;
745 out << "property float x" << std::endl;
746 out << "property float y" << std::endl;
747 out << "property float z" << std::endl;
748
749 if (write_vnormals)
750 {
751 out << "property float nx" << std::endl;
752 out << "property float ny" << std::endl;
753 out << "property float nz" << std::endl;
754 }
755
756 if (write_vcolors)
757 {
758 out << "property uchar red" << std::endl;
759 out << "property uchar green" << std::endl;
760 out << "property uchar blue" << std::endl;
761 }
762
763 if (write_vconfidences)
764 {
765 out << "property float confidence" << std::endl;
766 }
767
768 if (write_vvalues)
769 {
770 out << "property float value" << std::endl;
771 }
772
773 if (face_amount)
774 {
775 out << "element face " << face_amount << std::endl;
776 out << "property list uchar int vertex_indices" << std::endl;
777
778 if (write_fnormals)
779 {
780 out << "property float nx" << std::endl;
781 out << "property float ny" << std::endl;
782 out << "property float nz" << std::endl;
783 }
784
785 if (write_fcolors)
786 {
787 out << "property uchar red" << std::endl;
788 out << "property uchar green" << std::endl;
789 out << "property uchar blue" << std::endl;
790 }
791 }
792
793 out << "end_header" << std::endl;
794
795 if (options.format_binary)
796 {
797 /* Output data in BINARY format. */
798 for (std::size_t i = 0; i < verts.size(); ++i)
799 {
800 out.write((char const*)*verts[i], 3 * sizeof(float));
801 if (write_vnormals)
802 out.write((char const*)*vnormals[i], 3 * sizeof(float));
803 if (write_vcolors)
804 {
805 unsigned char color[3];
806 ply_color_convert(*vcolors[i], color);
807 out.write((char const*)color, 3);
808 }
809 if (write_vconfidences)
810 out.write((char const*)&conf[i], sizeof(float));
811 if (write_vvalues)
812 out.write((char const*)&vvalues[i], sizeof(float));
813 }
814
815 unsigned char verts_per_simplex_uchar = options.verts_per_simplex;
816 for (std::size_t i = 0; i < face_amount; ++i)
817 {
818 out.write((char const*)&verts_per_simplex_uchar, 1);
819 out.write((char const*)&faces[i * options.verts_per_simplex],
820 options.verts_per_simplex * sizeof(unsigned int));
821 if (write_fnormals)
822 out.write((char const*)*fnormals[i], 3 * sizeof(float));
823 if (write_fcolors)
824 {
825 unsigned char color[3];
826 ply_color_convert(*fcolors[i], color);
827 out.write((char const*)color, 3);
828 }
829 }
830 }
831 else
832 {
833 /* Output data in ASCII format. */
834 out << std::fixed << std::setprecision(7);
835 for (std::size_t i = 0; i < verts.size(); ++i)
836 {
837 out << verts[i][0] << " "
838 << verts[i][1] << " "
839 << verts[i][2];
840
841 if (write_vnormals)
842 for (int c = 0; c < 3; ++c)
843 out << " " << vnormals[i][c];
844 if (write_vcolors)
845 for (int c = 0; c < 3; ++c)
846 {
847 float color = 255.0f * vcolors[i][c];
848 color = std::min(255.0f, std::max(0.0f, color));
849 out << " " << (int)(color + 0.5f);
850 }
851 if (write_vconfidences)
852 out << " " << conf[i];
853 if (write_vvalues)
854 out << " " << vvalues[i];
855 out << std::endl;
856 }
857
858 for (std::size_t i = 0; i < face_amount; ++i)
859 {
860 out << options.verts_per_simplex;
861 for (unsigned int j = 0; j < options.verts_per_simplex; ++j)
862 out << " " << faces[i * options.verts_per_simplex + j];
863 if (write_fnormals)
864 for (int j = 0; j < 3; ++j)
865 out << " " << fnormals[i][j];
866 if (write_fcolors)
867 for (int j = 0; j < 3; ++j)
868 {
869 float color = 255.0f * fcolors[i][j];
870 color = std::min(255.0f, std::max(0.0f, color));
871 out << " " << (int)(color + 0.5f);
872 }
873 out << std::endl;
874 }
875 }
876
877 out.close();
878 std::cout << "done." << std::endl;
879}
880
881/* ---------------------------------------------------------------- */
882
883void
884save_ply_view (std::string const& filename, CameraInfo const& camera,
885 FloatImage::ConstPtr depth_map, FloatImage::ConstPtr confidence_map,
886 ByteImage::ConstPtr color_image)
887{
888 /* Some error and inconsistency checking. */
889 if (depth_map == nullptr)
890 throw std::invalid_argument("Null depth map given");
891 //if (confidence_map == nullptr)
892 // throw std::invalid_argument("Null confidence map given");
893 if (filename.empty())
894 throw std::invalid_argument("No filename given");
895
896 int const width = depth_map->width();
897 int const height = depth_map->height();
898 math::Matrix3f invproj;
899 camera.fill_inverse_calibration(*invproj, width, height);
900
901 if (confidence_map != nullptr && (confidence_map->height() != height
902 || confidence_map->width() != width))
903 throw std::invalid_argument("Confidence map dimension does not match");
904
905 if (color_image != nullptr && (color_image->width() != width
906 || color_image->height() != height))
907 throw std::invalid_argument("Color image dimension does not match");
908
909 /* Open output file. */
910 std::ofstream out(filename.c_str(), std::ios::binary);
911 if (!out.good())
912 throw util::FileException(filename, std::strerror(errno));
913
914 std::cout << "Writing PLY file for image size "
915 << width << "x" << height << "..." << std::endl;
916
917 std::cout << "Counting... " << std::flush;
918
919 /* Count valid depth values. */
920 int num_verts = 0;
921 int num_pixels = width * height;
922 if (confidence_map != nullptr)
923 {
924 for (int i = 0; i < num_pixels; ++i)
925 if (confidence_map->at(i, 0) > 0.0f)
926 num_verts += 1;
927 }
928 else
929 {
930 for (int i = 0; i < num_pixels; ++i)
931 if (depth_map->at(i, 0) > 0.0f)
932 num_verts += 1;
933 }
934
935 std::cout << "(" << num_verts << "), " << std::flush;
936
937 /* Write PLY header. */
938 out << "ply" << std::endl;
939 out << "format binary_little_endian 1.0" << std::endl;
940 out << "comment Export generated by libmve" << std::endl;
941 out << "obj_info num_cols " << width << std::endl;
942 out << "obj_info num_rows " << height << std::endl;
943 out << "element vertex " << num_verts << std::endl;
944 out << "property float x" << std::endl;
945 out << "property float y" << std::endl;
946 out << "property float z" << std::endl;
947
948 if (color_image != nullptr)
949 {
950 out << "property uchar red" << std::endl;
951 out << "property uchar green" << std::endl;
952 out << "property uchar blue" << std::endl;
953 }
954
955 if (confidence_map != nullptr)
956 {
957 out << "property float confidence" << std::endl;
958 }
959
960 out << "element range_grid " << (width * height) << std::endl;
961 out << "property list uchar int vertex_indices" << std::endl;
962 out << "end_header" << std::endl;
963
964 /* Write out vertices, color and confidence. */
965 std::cout << "writing vertices... " << std::flush;
966 for (int i = 0; i < num_pixels; ++i)
967 {
968 int const x = i % width;
969 int const y = i / width;
970 int const yinv = height - y - 1;
971
972 float confidence = 0.0f;
973 if (confidence_map != nullptr)
974 {
975 confidence = confidence_map->at(x, yinv, 0);
976 if (confidence <= 0.0f)
977 continue;
978 }
979
980 float depth = depth_map->at(x, yinv, 0);
981 if (depth <= 0.0f)
982 continue;
983
984 /* Build per-pixel viewing dir (in camera coords). */
985 math::Vec3f pos = mve::geom::pixel_3dpos(x, yinv, depth, invproj);
986
987 /* Convert vertex to world coords and write to file. */
988 out.write((char const*)pos.begin(), 3 * sizeof(float));
989 if (color_image != nullptr)
990 {
991 unsigned char const* c_off = &color_image->at(x, yinv, 0);
992 out.write((char const*)c_off, 3);
993 }
994
995 if (confidence_map != nullptr)
996 out.write((char const*)&confidence, sizeof(float));
997 }
998
999 std::cout << "writing range points... " << std::flush;
1000
1001 /* Write out range points. */
1002 std::size_t vertex_id = 0;
1003 for (int i = 0; i < num_pixels; ++i)
1004 {
1005 int const x = i % width;
1006 int const y = i / width;
1007 int const yinv = height - y - 1;
1008
1009 bool valid = true;
1010 if (confidence_map != nullptr && confidence_map->at(x, yinv, 0) <= 0.0f)
1011 valid = false;
1012 if (valid && depth_map->at(x, yinv, 0) <= 0.0f)
1013 valid = false;
1014
1015 out.write((char const*)&valid, 1);
1016 if (valid)
1017 {
1018 out.write((char const*)&vertex_id, sizeof(int));
1019 vertex_id++;
1020 }
1021 }
1022
1023 out.close();
1024
1025 std::cout << "done." << std::endl;
1026}
1027
1028/* ---------------------------------------------------------------- */
1029
1030void
1031save_ply_view (View::Ptr view, std::string const& filename)
1032{
1033 save_ply_view(view, filename, "depthmap", "confidence", "undistorted");
1034}
1035
1036/* ---------------------------------------------------------------- */
1037
1038void
1039save_ply_view (View::Ptr view, std::string const& filename,
1040 std::string const& depthmap, std::string const& confidence,
1041 std::string const& color_image)
1042{
1043 if (view == nullptr)
1044 throw std::invalid_argument("Null view given");
1045
1046 CameraInfo const& cam(view->get_camera());
1047 FloatImage::Ptr dm = view->get_float_image(depthmap);
1048 FloatImage::Ptr cm = view->get_float_image(confidence);
1049 ByteImage::Ptr ci = view->get_byte_image(color_image);
1050 save_ply_view(filename, cam, dm, cm, ci);
1051}
1052
1053/* ---------------------------------------------------------------- */
1054
1055void
1056save_xf_file (std::string const& filename, CameraInfo const& camera)
1057{
1058 math::Matrix4f ctw;
1059 camera.fill_cam_to_world(*ctw);
1060 save_xf_file(filename, *ctw);
1061}
1062
1063/* ---------------------------------------------------------------- */
1064
1065void
1066save_xf_file (std::string const& filename, float const* ctw)
1067{
1068 std::cout << "Writing XF file " << filename << "..." << std::endl;
1069
1070 std::ofstream out(filename.c_str(), std::ios::binary);
1071 if (!out.good())
1072 throw util::FileException(filename, std::strerror(errno));
1073
1074 for (std::size_t i = 0; i < 16; ++i)
1075 out << ctw[i] << (i % 4 == 3 ? "\n" : " ");
1076
1077 out.close();
1078}
1079
1080/* ---------------------------------------------------------------- */
1081
1083load_ply_depthmap (std::string const& filename)
1084{
1085 /* Precondition checks. */
1086 if (filename.empty())
1087 throw std::invalid_argument("No filename given");
1088
1089 /* Open file. */
1090 std::fstream input(filename.c_str());
1091 if (!input.good())
1092 throw util::FileException(filename, std::strerror(errno));
1093
1094 /* Start parsing. */
1095 std::string buffer;
1096 input >> buffer; /* Read "ply" file signature. */
1097 if (buffer != "ply")
1098 {
1099 input.close();
1100 throw util::Exception("File format not recognized as PLY file");
1101 }
1102
1103 /* Discard the rest of the line. */
1104 std::getline(input, buffer);
1105
1106 std::size_t n_verts = 0;
1107 int n_grid = 0;
1108 int width = 0;
1109 int height = 0;
1110 while (input.good())
1111 {
1112 std::getline(input, buffer);
1115
1117 t.split(buffer);
1118
1119 if (t.empty())
1120 continue;
1121
1122 if (t.size() == 3 && t[0] == "element" && t[1] == "vertex")
1123 n_verts = util::string::convert<std::size_t>(t[2]);
1124 if (t.size() == 3 && t[0] == "element" && t[1] == "range_grid")
1125 n_grid = util::string::convert<int>(t[2]);
1126 if (t.size() == 3 && t[0] == "obj_info" && t[1] == "num_cols")
1127 width = util::string::convert<int>(t[2]);
1128 if (t.size() == 3 && t[0] == "obj_info" && t[1] == "num_rows")
1129 height = util::string::convert<int>(t[2]);
1130 if (t.size() == 1 && t[0] == "end_header")
1131 break;
1132 }
1133
1134 if (!n_verts || !n_grid || !width || !height || n_grid != width * height)
1135 {
1136 input.close();
1137 throw util::Exception("File headers not recognized as depthmap");
1138 }
1139
1140 /* Read vertices. */
1142 for (std::size_t i = 0; i < n_verts; ++i)
1143 {
1144 float x, y, z;
1145 input >> x >> y >> z;
1146 verts.push_back(math::Vec3f(x, y, z));
1147 }
1148
1149 /* Discard newline. */
1150 std::getline(input, buffer);
1151
1152 FloatImage::Ptr ret(FloatImage::create(width, height, 1));
1153 /* Read range grid. */
1154 for (int i = 0; i < n_grid; ++i)
1155 {
1156 // Flip y-axis
1157 int const idx = (height - (i / width) - 1) * width + (i % width);
1158
1159 std::getline(input, buffer);
1163 t.split(buffer);
1164
1165 if (input.eof())
1166 {
1167 std::cout << "Warning: Early EOF while parsing PLY" << std::endl;
1168 break;
1169 }
1170
1171 if (t.empty() || t[0] != "1")
1172 {
1173 ret->at(idx) = 0.0f;
1174 }
1175 else
1176 {
1177 std::size_t vid = util::string::convert<std::size_t>(t[1]);
1178 if (vid >= verts.size())
1179 {
1180 std::cout << "Warning: Vertex index out of bounds" << std::endl;
1181 ret->at(idx) = 0.0f;
1182 }
1183 else
1184 {
1185 ret->at(idx) = verts[vid].norm();
1186 }
1187 }
1188 }
1189
1190 input.close();
1191 return ret;
1192}
1193
Matrix class for arbitrary dimensions and types.
Definition matrix.h:54
Vector class for arbitrary dimensions and types.
Definition vector.h:87
T * begin(void)
Definition vector.h:583
Multi-channel image class of arbitrary but homogenous data type.
Definition image.h:40
std::shared_ptr< Image< T > > Ptr
Definition image.h:42
std::shared_ptr< Image< T > const > ConstPtr
Definition image.h:43
std::vector< math::Vec3f > VertexList
Definition mesh.h:33
std::vector< float > ConfidenceList
Definition mesh.h:35
std::vector< math::Vec4f > ColorList
Definition mesh.h:34
std::vector< float > ValueList
Definition mesh.h:36
std::vector< math::Vec3f > NormalList
Definition mesh.h:95
std::vector< math::Vec2f > TexCoordList
Definition mesh.h:96
std::shared_ptr< TriangleMesh > Ptr
Definition mesh.h:92
std::vector< VertexID > FaceList
Definition mesh.h:97
std::shared_ptr< TriangleMesh const > ConstPtr
Definition mesh.h:93
std::shared_ptr< View > Ptr
Definition view.h:68
Universal, simple exception class.
Definition exception.h:24
Exception class for file exceptions with additional filename.
Definition exception.h:53
Simple tokenizer.
Definition tokenizer.h:29
void split(std::string const &str, char delim=' ', bool keep_empty=false)
Very simple tokenziation at a given delimiter characater.
Definition tokenizer.h:62
unsigned int vertex_id
#define MVE_NAMESPACE_BEGIN
Definition defines.h:13
#define MVE_NAMESPACE_END
Definition defines.h:14
#define MVE_GEOM_NAMESPACE_END
Definition defines.h:20
#define MVE_GEOM_NAMESPACE_BEGIN
Definition defines.h:19
void rangegrid_triangulate(Image< unsigned int > const &grid, TriangleMesh::Ptr mesh)
Algorithm to triangulate range grids.
Definition depthmap.cc:420
TriangleMesh::Ptr load_ply_mesh(std::string const &filename)
Loads a triangle mesh from a PLY model file.
void load_xf_file(std::string const &filename, float *ctw)
Load XF file, typically with camera to world transformation.
math::Vec3f pixel_3dpos(int64_t x, int64_t y, float depth, math::Matrix3f const &invproj)
Function that calculates the pixel 3D position in camera coordinates for pixel (x,...
Definition depthmap.cc:150
FloatImage::Ptr load_ply_depthmap(std::string const &filename)
Loads a depth map from a PLY file.
void save_ply_view(std::string const &filename, CameraInfo const &camera, FloatImage::ConstPtr depth_map, FloatImage::ConstPtr confidence_map, ByteImage::ConstPtr color_image)
Stores a scanalize-compatible PLY file from a depth map.
PLYFaceProperty
PLY face element properties.
@ PLY_F_VERTEX_INDICES
@ PLY_F_IGNORE_UINT32
@ PLY_F_IGNORE_UINT8
@ PLY_F_IGNORE_FLOAT
void save_ply_mesh(TriangleMesh::ConstPtr mesh, std::string const &filename, SavePLYOptions const &options)
Stores a PLY file from a triangle mesh.
PLYFormat
PLY data encoding formats.
PLYVertexProperty
PLY vertex element properties.
@ PLY_V_IGNORE_FLOAT
@ PLY_V_IGNORE_UINT8
@ PLY_V_IGNORE_UINT32
@ PLY_V_FLOAT_VALUE
@ PLY_V_IGNORE_DOUBLE
T ply_read_value(std::istream &input, PLYFormat format)
Reads a value from the input stream given the PLY format.
void ply_color_convert(float const *src, unsigned char *dest, int num=3)
void save_xf_file(std::string const &filename, CameraInfo const &camera)
Stores a scanalyze compatible XF file with camera transformation from camera to world coordinates.
void clip_newlines(std::string *str)
Clips newlines from the end of the string, in-place.
Definition strings.h:317
void clip_whitespaces(std::string *str)
Clips whitespaces from the front and end of the string, in-place.
Definition strings.h:299
T betoh(T const &x)
Big endian to host order conversion.
T letoh(T const &x)
Little endian to host order conversion.
Per-view camera information with various helper functions.
Definition camera.h:24
void fill_inverse_calibration(float *mat, float width, float height) const
Stores 3x3 inverse calibration (or inverse projection) matrix.
Definition camera.cc:180
void fill_cam_to_world(float *mat) const
Stores camera to world 4x4 matrix in array pointed to by mat.
Definition camera.cc:83
Options struct for saving PLY files.
Definition mesh_io_ply.h:51
unsigned int verts_per_simplex
Definition mesh_io_ply.h:59