Volume Cartographer 2.27.0
PointSetIO.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <algorithm>
6#include <array>
7#include <cstddef>
8#include <fstream>
9#include <regex>
10#include <sstream>
11#include <string>
12#include <vector>
13
19
20namespace volcart
21{
22
27enum class IOMode { ASCII = 0, BINARY };
28
43template <typename T>
45{
46public:
48 struct Header {
49 std::size_t width{0};
50 std::size_t height{0};
51 std::size_t size{0};
52 std::size_t dim{0};
53 bool ordered{false};
54 std::string type;
55 };
56
66 const volcart::filesystem::path& path, IOMode mode = IOMode::BINARY)
67 {
68 switch (mode) {
69 case IOMode::BINARY:
70 return ReadOrderedPointSetBinary(path);
71 case IOMode::ASCII:
72 return ReadOrderedPointSetAscii(path);
73 default:
74 throw IOException("unsupported IOMode");
75 }
76 }
77
83 const volcart::filesystem::path& path, IOMode mode = IOMode::BINARY)
84 {
85 switch (mode) {
86 case IOMode::BINARY:
87 return ReadPointSetBinary(path);
88 case IOMode::ASCII:
89 return ReadPointSetAscii(path);
90 default:
91 throw IOException("unsupported IOMode");
92 }
93 }
99 const volcart::filesystem::path& path,
100 const OrderedPointSet<T>& ps,
101 IOMode mode = IOMode::BINARY)
102 {
103 switch (mode) {
104 case IOMode::BINARY:
105 return WriteOrderedPointSetBinary(path, ps);
106 case IOMode::ASCII:
107 return WriteOrderedPointSetAscii(path, ps);
108 }
109 }
110
112 static void WritePointSet(
113 const volcart::filesystem::path& path,
114 const PointSet<T>& ps,
115 IOMode mode = IOMode::BINARY)
116 {
117 switch (mode) {
118 case IOMode::BINARY:
119 return WritePointSetBinary(path, ps);
120 case IOMode::ASCII:
121 return WritePointSetAscii(path, ps);
122 }
123 }
128 static std::string MakeHeader(PointSet<T> ps)
129 {
130 std::stringstream ss;
131 ss << "size: " << ps.size() << std::endl;
132 ss << "dim: " << T::channels << std::endl;
133 ss << "ordered: false" << std::endl;
134
135 // Output type information
136 ss << "type: ";
137 if (std::is_same<typename T::value_type, int>::value) {
138 ss << "int" << std::endl;
139 } else if (std::is_same<typename T::value_type, float>::value) {
140 ss << "float" << std::endl;
141 } else if (std::is_same<typename T::value_type, double>::value) {
142 ss << "double" << std::endl;
143 } else {
144 auto msg = "unsupported type";
145 throw IOException(msg);
146 }
147
148 ss << "version: " << PointSet<T>::FORMAT_VERSION << std::endl;
149 ss << PointSet<T>::HEADER_TERMINATOR << std::endl;
150
151 return ss.str();
152 }
155 {
156 std::stringstream ss;
157 ss << "width: " << ps.width() << std::endl;
158 ss << "height: " << ps.height() << std::endl;
159 ss << "dim: " << T::channels << std::endl;
160 ss << "ordered: true" << std::endl;
161
162 // Output type information
163 ss << "type: ";
164 if (std::is_same<typename T::value_type, int>::value) {
165 ss << "int" << std::endl;
166 } else if (std::is_same<typename T::value_type, float>::value) {
167 ss << "float" << std::endl;
168 } else if (std::is_same<typename T::value_type, double>::value) {
169 ss << "double" << std::endl;
170 } else {
171 auto msg = "unsupported type";
172 throw IOException(msg);
173 }
174
175 ss << "version: " << PointSet<T>::FORMAT_VERSION << std::endl;
176 ss << PointSet<T>::HEADER_TERMINATOR << std::endl;
177
178 return ss.str();
179 }
180
188 static Header ParseHeader(std::ifstream& infile, bool ordered = true)
189 {
190 // Regexes
191 std::regex comments{"^#"};
192 std::regex width{"^width"};
193 std::regex height{"^height"};
194 std::regex dim{"^dim"};
195 std::regex ordering{"^ordered"};
196 std::regex type{"^type"};
197 std::regex size{"^size"};
198 std::regex version{"^version"};
199 std::regex validTypes{"^(float)|(double)|(int)"};
200 std::regex headerTerminator{PointSet<T>::HEADER_TERMINATOR_REGEX};
201
202 Header h;
203 std::string line;
204 std::vector<std::string> strs;
205
206 while (std::getline(infile, line)) {
207 trim(line);
208 strs = split(line, ':');
209 std::for_each(
210 std::begin(strs), std::end(strs), [](auto& s) { trim(s); });
211
212 // Comments: look like:
213 // # This is a comment
214 // # This is another comment
215 if (std::regex_match(strs[0], comments)) {
216 continue;
217 }
218
219 // Width
220 else if (std::regex_match(strs[0], width)) {
221 h.width = std::stoul(strs[1]);
222 }
223
224 // Height
225 else if (std::regex_match(strs[0], height)) {
226 h.height = std::stoul(strs[1]);
227 }
228
229 // Size
230 else if (std::regex_match(strs[0], size)) {
231 h.size = std::stoul(strs[1]);
232 }
233
234 // Dim
235 else if (std::regex_match(strs[0], dim)) {
236 auto parsedDim = std::stoul(strs[1]);
237 if (parsedDim != T::channels) {
238 auto msg =
239 "Incorrect dimension read for template specification";
240 throw IOException(msg);
241 }
242 h.dim = parsedDim;
243 }
244
245 // Ordering
246 else if (std::regex_match(strs[0], ordering)) {
247 to_lower(strs[1]);
248 if (strs[1] == "true") {
249 h.ordered = true;
250 } else if (strs[1] == "false") {
251 h.ordered = false;
252 } else {
253 auto msg = "'ordered' key must have value 'true'/'false'";
254 throw IOException(msg);
255 }
256 }
257
258 // Type
259 else if (std::regex_match(strs[0], type)) {
260 if (!std::regex_match(strs[1], validTypes)) {
261 auto msg = "Valid types are int, float, double. Got: '" +
262 strs[1] + "'";
263 throw IOException(msg);
264 }
265
266 // type validation
267 std::string readerType;
268 if (std::is_same_v<typename T::value_type, int>) {
269 readerType = "int";
270 } else if (std::is_same_v<typename T::value_type, float>) {
271 readerType = "float";
272 } else if (std::is_same_v<typename T::value_type, double>) {
273 readerType = "double";
274 } else {
275 throw IOException("unsupported reader type");
276 }
277 if (strs[1] != readerType) {
278 auto msg = "Type mismatch: vcps filetype '" + strs[1] +
279 "' not compatible with reader type '" +
280 readerType + "'";
281 throw IOException(msg);
282 }
283 h.type = strs[1];
284 }
285
286 // Version
287 else if (std::regex_match(strs[0], version)) {
288 auto fileVersion = std::stoi(strs[1]);
289 if (fileVersion != PointSet<T>::FORMAT_VERSION) {
290 auto msg = "Version mismatch. VCPS file version is " +
291 strs[1] + ", processing version is " +
292 std::to_string(PointSet<T>::FORMAT_VERSION) +
293 ".";
294 throw IOException(msg);
295 }
296 }
297
298 // End of the header
299 else if (std::regex_match(line, headerTerminator)) {
300 break;
301 }
302
303 // Ignore everything else
304 else {
305 continue;
306 }
307 strs.clear();
308 }
309
310 // Set size
311 if (!ordered && h.width > 0 && h.height > 0) {
312 h.size = h.width * h.height;
313 }
314
315 // Sanity check. Do we have a valid pointset header?
316 if (h.type.empty()) {
317 const auto* msg = "Must provide type";
318 throw IOException(msg);
319 } else if (h.dim == 0) {
320 const auto* msg = "Must provide dim";
321 throw IOException(msg);
322 } else if (!ordered && h.size == 0) {
323 const auto* msg = "Unordered pointsets must have a size";
324 throw IOException(msg);
325 } else if (ordered && (h.width == 0 || h.height == 0)) {
326 const auto* msg =
327 "Ordered pointsets must have a nonzero width and height";
328 throw IOException(msg);
329 } else if (ordered && !h.ordered) {
330 const auto* msg =
331 "Tried to read unordered pointset with ordered PointSetIO";
332 throw IOException(msg);
333 }
334
335 return h;
336 }
339private:
342 static PointSet<T> ReadPointSetAscii(const volcart::filesystem::path& path)
343 {
344 std::ifstream infile{path.string()};
345 if (!infile.is_open()) {
346 auto msg = "could not open file '" + path.string() + "'";
347 throw IOException(msg);
348 }
349
350 // Get header
351 auto header = PointSetIO<T>::ParseHeader(infile, false);
352 PointSet<T> ps{header.size};
353
354 for (std::size_t i = 0; i < header.size; ++i) {
355 std::array<typename T::value_type, T::channels> values;
356 for (std::size_t d = 0; d < header.dim; ++d) {
357 infile >> values[d];
358 }
359 ps.push_back(T{values.data()});
360 }
361
362 return ps;
363 }
364
367 const volcart::filesystem::path& path)
368 {
369 std::ifstream infile{path.string()};
370 if (!infile.is_open()) {
371 auto msg = "could not open file '" + path.string() + "'";
372 throw IOException(msg);
373 }
374
375 // Get header
376 auto header = PointSetIO<T>::ParseHeader(infile, true);
377 OrderedPointSet<T> ps{header.width};
378
379 std::vector<T> points;
380 points.reserve(header.width);
381 for (std::size_t h = 0; h < header.height; ++h) {
382 for (std::size_t w = 0; w < header.width; ++w) {
383 std::array<typename T::value_type, T::channels> values;
384 for (std::size_t d = 0; d < header.dim; ++d) {
385 infile >> values.at(d);
386 }
387 points.emplace_back(values.data());
388 }
389 ps.pushRow(points);
390 points.clear();
391 }
392
393 return ps;
394 }
395
397 static PointSet<T> ReadPointSetBinary(const volcart::filesystem::path& path)
398 {
399 std::ifstream infile{path.string(), std::ios::binary};
400 if (!infile.is_open()) {
401 auto msg = "could not open file '" + path.string() + "'";
402 throw IOException(msg);
403 }
404 auto header = PointSetIO<T>::ParseHeader(infile, false);
405 PointSet<T> ps{header.size};
406
407 // Size of binary elements to read
408 std::size_t typeBytes{};
409 if (header.type == "float") {
410 typeBytes = sizeof(float);
411 } else if (header.type == "double") {
412 typeBytes = sizeof(double);
413 } else if (header.type == "int") {
414 typeBytes = sizeof(int);
415 }
416
417 // Read data
418 T t;
419 for (std::size_t i = 0; i < header.size; ++i) {
420 auto nbytes = header.dim * typeBytes;
421 infile.read(reinterpret_cast<char*>(t.val), nbytes);
422 ps.push_back(t);
423 }
424
425 return ps;
426 }
427
430 const volcart::filesystem::path& path)
431 {
432 std::ifstream infile{path.string(), std::ios::binary};
433 if (!infile.is_open()) {
434 auto msg = "could not open file '" + path.string() + "'";
435 throw IOException(msg);
436 }
437 auto header = PointSetIO<T>::ParseHeader(infile, true);
438 OrderedPointSet<T> ps{header.width};
439
440 // Size of binary elements to read
441 std::size_t typeBytes = sizeof(int);
442 if (header.type == "float") {
443 typeBytes = sizeof(float);
444 } else if (header.type == "double") {
445 typeBytes = sizeof(double);
446 } else if (header.type == "int") {
447 typeBytes = sizeof(int);
448 } else {
449 auto msg = "Unrecognized type: " + header.type;
450 throw IOException(msg);
451 }
452
453 // Read data
454 T t;
455 std::size_t nbytes = header.width * header.dim * typeBytes;
456 std::vector<T> points(header.width, 0);
457 points.reserve(header.width);
458 for (std::size_t h = 0; h < header.height; ++h) {
459 infile.read(reinterpret_cast<char*>(points.data()), nbytes);
460 ps.pushRow(points);
461 }
462
463 return ps;
464 }
470 const volcart::filesystem::path& path, PointSet<T> ps)
471 {
472 std::ofstream outfile{path.string()};
473 if (!outfile.is_open()) {
474 auto msg = "could not open file '" + path.string() + "'";
475 throw IOException(msg);
476 }
477
478 auto header = PointSetIO<T>::MakeHeader(ps);
479 outfile << header;
480 for (const auto& p : ps) {
481 for (std::size_t i = 0; i < T::channels; ++i) {
482 outfile << p(i) << " ";
483 }
484 outfile << std::endl;
485 }
486
487 outfile.flush();
488 outfile.close();
489 if (outfile.fail()) {
490 auto msg = "failure writing file '" + path.string() + "'";
491 throw IOException(msg);
492 }
493 }
494
497 const volcart::filesystem::path& path, PointSet<T> ps)
498 {
499 std::ofstream outfile{path.string(), std::ios::binary};
500 if (!outfile.is_open()) {
501 auto msg = "could not open file '" + path.string() + "'";
502 throw IOException(msg);
503 }
504
505 auto header = PointSetIO<T>::MakeHeader(ps);
506 outfile.write(header.c_str(), header.size());
507
508 for (const auto& p : ps) {
509 auto nbytes = T::channels * sizeof(typename T::value_type);
510 outfile.write(reinterpret_cast<const char*>(p.val), nbytes);
511 }
512
513 outfile.flush();
514 outfile.close();
515 if (outfile.fail()) {
516 auto msg = "failure writing file '" + path.string() + "'";
517 throw IOException(msg);
518 }
519 }
520
523 const volcart::filesystem::path& path, OrderedPointSet<T> ps)
524 {
525 std::ofstream outfile{path.string()};
526 if (!outfile.is_open()) {
527 auto msg = "could not open file '" + path.string() + "'";
528 throw IOException(msg);
529 }
530
531 auto header = PointSetIO<T>::MakeOrderedHeader(ps);
532 outfile << header;
533 for (const auto& p : ps) {
534 for (std::size_t i = 0; i < T::channels; ++i) {
535 outfile << p(i) << " ";
536 }
537 outfile << std::endl;
538 }
539
540 outfile.flush();
541 outfile.close();
542 if (outfile.fail()) {
543 auto msg = "failure writing file '" + path.string() + "'";
544 throw IOException(msg);
545 }
546 }
547
550 const volcart::filesystem::path& path, OrderedPointSet<T> ps)
551 {
552 std::ofstream outfile{path.string(), std::ios::binary};
553 if (!outfile.is_open()) {
554 auto msg = "could not open file '" + path.string() + "'";
555 throw IOException(msg);
556 }
557
558 auto header = PointSetIO<T>::MakeOrderedHeader(ps);
559 outfile.write(header.c_str(), header.size());
560
561 for (const auto& p : ps) {
562 auto nbytes = T::channels * sizeof(typename T::value_type);
563 outfile.write(reinterpret_cast<const char*>(p.val), nbytes);
564 }
565
566 outfile.flush();
567 outfile.close();
568 if (outfile.fail()) {
569 auto msg = "failure writing file '" + path.string() + "'";
570 throw IOException(msg);
571 }
572 }
574};
575} // namespace volcart
IO operation exception.
Definition: Exceptions.hpp:23
Holds a collection of ordered points.
std::size_t height() const
Return the number of rows in the OrderedPointSet.
std::size_t width() const
Return the number of columns in the OrderedPointSet.
Read and write PointSet and OrderedPointSet.
Definition: PointSetIO.hpp:45
static OrderedPointSet< T > ReadOrderedPointSetAscii(const volcart::filesystem::path &path)
Read an ASCII OrderedPointSet.
Definition: PointSetIO.hpp:366
static void WritePointSetBinary(const volcart::filesystem::path &path, PointSet< T > ps)
Write a binary PointSet.
Definition: PointSetIO.hpp:496
static OrderedPointSet< T > ReadOrderedPointSet(const volcart::filesystem::path &path, IOMode mode=IOMode::BINARY)
Read OrderedPointSet from file.
Definition: PointSetIO.hpp:65
static PointSet< T > ReadPointSet(const volcart::filesystem::path &path, IOMode mode=IOMode::BINARY)
Read PointSet from file.
Definition: PointSetIO.hpp:82
static std::string MakeOrderedHeader(OrderedPointSet< T > ps)
Generate an OrderedPointSet header string.
Definition: PointSetIO.hpp:154
static void WriteOrderedPointSet(const volcart::filesystem::path &path, const OrderedPointSet< T > &ps, IOMode mode=IOMode::BINARY)
Write an OrderedPointSet to disk.
Definition: PointSetIO.hpp:98
static Header ParseHeader(std::ifstream &infile, bool ordered=true)
Parse a PointSet/OrderedPointSet file header.
Definition: PointSetIO.hpp:188
static PointSet< T > ReadPointSetAscii(const volcart::filesystem::path &path)
Read an ASCII PointSet.
Definition: PointSetIO.hpp:342
static void WritePointSetAscii(const volcart::filesystem::path &path, PointSet< T > ps)
Write an ASCII PointSet.
Definition: PointSetIO.hpp:469
static std::string MakeHeader(PointSet< T > ps)
Generate a PointSet header string.
Definition: PointSetIO.hpp:128
static OrderedPointSet< T > ReadOrderedPointSetBinary(const volcart::filesystem::path &path)
Read a binary OrderedPointSet.
Definition: PointSetIO.hpp:429
static PointSet< T > ReadPointSetBinary(const volcart::filesystem::path &path)
Read a binary PointSet.
Definition: PointSetIO.hpp:397
static void WriteOrderedPointSetBinary(const volcart::filesystem::path &path, OrderedPointSet< T > ps)
Write a binary OrderedPointSet.
Definition: PointSetIO.hpp:549
static void WriteOrderedPointSetAscii(const volcart::filesystem::path &path, OrderedPointSet< T > ps)
Write an ASCII OrderedPointSet.
Definition: PointSetIO.hpp:522
static void WritePointSet(const volcart::filesystem::path &path, const PointSet< T > &ps, IOMode mode=IOMode::BINARY)
Write a PointSet to disk.
Definition: PointSetIO.hpp:112
Holds a collection of points.
Definition: PointSet.hpp:26
std::size_t size() const
Get the size of the PointSet.
Definition: PointSet.hpp:88
IOMode
IO Mode for file readers/writers.
Definition: PointSetIO.hpp:27
Volume Cartographer library
static void to_lower(std::string &s)
Convert string characters to lower case (in place)
Definition: String.hpp:39
static void trim(std::string &s)
Trim from both ends (in place)
Definition: String.hpp:149
static std::vector< std::string > split(const std::string &s, const Ds &... ds)
Split a string by a delimiter.
Definition: String.hpp:179
PointSet file header information.
Definition: PointSetIO.hpp:48