Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpIoTools.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software 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; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Directory management.
32  */
33 
39 #ifndef VP_IO_TOOLS_H
40 #define VP_IO_TOOLS_H
41 
42 #include <visp3/core/vpConfig.h>
43 
44 #include <iostream>
45 #include <sstream>
46 #include <stdint.h> //for uint32_t related types ; works also with >= VS2010 / _MSC_VER >= 1600
47 #include <stdlib.h>
48 #include <string>
49 #include <vector>
50 #include <numeric>
51 #include <visp3/core/vpColor.h>
52 
53 #include <memory>
54 #include <map>
55 #include <cassert>
56 #include <complex>
57 
58 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
59 
60 namespace visp
61 {
62 #ifndef DOXYGEN_SHOULD_SKIP_THIS
63 // https://github.com/BinomialLLC/basis_universal/blob/ad9386a4a1cf2a248f7bbd45f543a7448db15267/encoder/basisu_miniz.h#L665
64 static inline unsigned long vp_mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_len)
65 {
66  static const unsigned int s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
67  0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
68  unsigned int crcu32 = static_cast<unsigned int>(crc);
69  if (!ptr) return 0;
70  crcu32 = ~crcu32;
71  while (buf_len--) {
72  unsigned char b = *ptr++;
73  crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
74  crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
75  }
76  return ~crcu32;
77 }
78 #endif // DOXYGEN_SHOULD_SKIP_THIS
79 
80 #ifdef VISP_HAVE_MINIZ
81 namespace cnpy
82 {
83 // Copyright (C) 2011 Carl Rogers
84 // Released under MIT License
85 // license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
86 struct NpyArray
87 {
88  NpyArray(const std::vector<size_t> &_shape, size_t _word_size, bool _fortran_order) :
89  shape(_shape), word_size(_word_size), fortran_order(_fortran_order)
90  {
91  num_vals = 1;
92  for (size_t i = 0; i < shape.size(); ++i) num_vals *= shape[i];
93  data_holder = std::shared_ptr<std::vector<char> >(
94  new std::vector<char>(num_vals * word_size));
95  }
96 
98 
99  template<typename T>
100  T *data()
101  {
102  return reinterpret_cast<T *>(&(*data_holder)[0]);
103  }
104 
105  template<typename T>
106  const T *data() const
107  {
108  return reinterpret_cast<T *>(&(*data_holder)[0]);
109  }
110 
111  template<typename T>
112  std::vector<T> as_vec() const
113  {
114  const T *p = data<T>();
115  return std::vector<T>(p, p+num_vals);
116  }
117 
118  size_t num_bytes() const
119  {
120  return data_holder->size();
121  }
122 
123  std::shared_ptr<std::vector<char> > data_holder;
124  std::vector<size_t> shape;
125  size_t word_size;
127  size_t num_vals;
128 };
129 
130 using npz_t = std::map<std::string, NpyArray>;
131 VISP_EXPORT npz_t npz_load(std::string fname);
132 VISP_EXPORT char BigEndianTest();
133 VISP_EXPORT char map_type(const std::type_info &t);
134 template<typename T> std::vector<char> create_npy_header(const std::vector<size_t> &shape);
135 VISP_EXPORT void parse_npy_header(FILE *fp, size_t &word_size, std::vector<size_t> &shape, bool &fortran_order);
136 VISP_EXPORT void parse_npy_header(unsigned char *buffer, size_t &word_size, std::vector<size_t> &shape, bool &fortran_order);
137 VISP_EXPORT void parse_zip_footer(FILE *fp, uint16_t &nrecs, size_t &global_header_size, size_t &global_header_offset);
138 VISP_EXPORT NpyArray npz_load(std::string fname, std::string varname);
139 VISP_EXPORT NpyArray npy_load(std::string fname);
140 
141 template<typename T> std::vector<char> &operator+=(std::vector<char> &lhs, const T rhs)
142 {
143  //write in little endian
144  for (size_t byte = 0; byte < sizeof(T); ++byte) {
145  char val = *((char *)&rhs+byte);
146  lhs.push_back(val);
147  }
148  return lhs;
149 }
150 
151 template<> inline std::vector<char> &operator+=(std::vector<char> &lhs, const std::string rhs)
152 {
153  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
154  return lhs;
155 }
156 
157 template<> inline std::vector<char> &operator+=(std::vector<char> &lhs, const char *rhs)
158 {
159 //write in little endian
160  size_t len = strlen(rhs);
161  lhs.reserve(len);
162  for (size_t byte = 0; byte < len; ++byte) {
163  lhs.push_back(rhs[byte]);
164  }
165  return lhs;
166 }
167 
178 template<typename T> void npy_save(std::string fname, const T *data, const std::vector<size_t> shape, std::string mode = "w")
179 {
180  FILE *fp = NULL;
181  std::vector<size_t> true_data_shape; //if appending, the shape of existing + new data
182 
183  if (mode == "a") fp = fopen(fname.c_str(), "r+b");
184 
185  if (fp) {
186  //file exists. we need to append to it. read the header, modify the array size
187  size_t word_size;
188  bool fortran_order;
189  parse_npy_header(fp, word_size, true_data_shape, fortran_order);
190  assert(!fortran_order);
191 
192  if (word_size != sizeof(T)) {
193  std::cout<<"libnpy error: "<<fname<<" has word size "<<word_size<<" but npy_save appending data sized "<<sizeof(T)<<"\n";
194  assert(word_size == sizeof(T));
195  }
196  if (true_data_shape.size() != shape.size()) {
197  std::cout<<"libnpy error: npy_save attempting to append misdimensioned data to "<<fname<<"\n";
198  assert(true_data_shape.size() != shape.size());
199  }
200 
201  for (size_t i = 1; i < shape.size(); ++i) {
202  if (shape[i] != true_data_shape[i]) {
203  std::cout<<"libnpy error: npy_save attempting to append misshaped data to "<<fname<<"\n";
204  assert(shape[i] == true_data_shape[i]);
205  }
206  }
207  true_data_shape[0] += shape[0];
208  }
209  else {
210  fp = fopen(fname.c_str(), "wb");
211  true_data_shape = shape;
212  }
213 
214  std::vector<char> header = create_npy_header<T>(true_data_shape);
215  size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
216 
217  fseek(fp, 0, SEEK_SET);
218  fwrite(&header[0], sizeof(char), header.size(), fp);
219  fseek(fp, 0, SEEK_END);
220  fwrite(data, sizeof(T), nels, fp);
221  fclose(fp);
222 }
223 
235 template<typename T> void npz_save(std::string zipname, std::string fname, const T *data, const std::vector<size_t> &shape, std::string mode = "w")
236 {
237  //first, append a .npy to the fname
238  fname += ".npy";
239 
240  //now, on with the show
241  FILE *fp = NULL;
242  uint16_t nrecs = 0;
243  size_t global_header_offset = 0;
244  std::vector<char> global_header;
245 
246  if (mode == "a") fp = fopen(zipname.c_str(), "r+b");
247 
248  if (fp) {
249  //zip file exists. we need to add a new npy file to it.
250  //first read the footer. this gives us the offset and size of the global header
251  //then read and store the global header.
252  //below, we will write the the new data at the start of the global header then append the global header and footer below it
253  size_t global_header_size;
254  parse_zip_footer(fp, nrecs, global_header_size, global_header_offset);
255  fseek(fp, static_cast<long>(global_header_offset), SEEK_SET);
256  global_header.resize(global_header_size);
257  size_t res = fread(&global_header[0], sizeof(char), global_header_size, fp);
258  if (res != global_header_size) {
259  throw std::runtime_error("npz_save: header read error while adding to existing zip");
260  }
261  fseek(fp, static_cast<long>(global_header_offset), SEEK_SET);
262  }
263  else {
264  fp = fopen(zipname.c_str(), "wb");
265  }
266 
267  std::vector<char> npy_header = create_npy_header<T>(shape);
268 
269  size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
270  size_t nbytes = nels*sizeof(T) + npy_header.size();
271 
272  //get the CRC of the data to be added
273  uint32_t crc = vp_mz_crc32(0L, (uint8_t *)&npy_header[0], npy_header.size());
274  crc = vp_mz_crc32(crc, (uint8_t *)data, nels*sizeof(T));
275 
276  //build the local header
277  std::vector<char> local_header;
278  local_header += "PK"; //first part of sig
279  local_header += static_cast<uint16_t>(0x0403); //second part of sig
280  local_header += static_cast<uint16_t>(20); //min version to extract
281  local_header += static_cast<uint16_t>(0); //general purpose bit flag
282  local_header += static_cast<uint16_t>(0); //compression method
283  local_header += static_cast<uint16_t>(0); //file last mod time
284  local_header += static_cast<uint16_t>(0); //file last mod date
285  local_header += static_cast<uint32_t>(crc); //crc
286  local_header += static_cast<uint32_t>(nbytes); //compressed size
287  local_header += static_cast<uint32_t>(nbytes); //uncompressed size
288  local_header += static_cast<uint16_t>(fname.size()); //fname length
289  local_header += static_cast<uint16_t>(0); //extra field length
290  local_header += fname;
291 
292  //build global header
293  global_header += "PK"; //first part of sig
294  global_header += static_cast<uint16_t>(0x0201); //second part of sig
295  global_header += static_cast<uint16_t>(20); //version made by
296  global_header.insert(global_header.end(), local_header.begin()+4, local_header.begin()+30);
297  global_header += static_cast<uint16_t>(0); //file comment length
298  global_header += static_cast<uint16_t>(0); //disk number where file starts
299  global_header += static_cast<uint16_t>(0); //internal file attributes
300  global_header += static_cast<uint32_t>(0); //external file attributes
301  global_header += static_cast<uint32_t>(global_header_offset); //relative offset of local file header, since it begins where the global header used to begin
302  global_header += fname;
303 
304  //build footer
305  std::vector<char> footer;
306  footer += "PK"; //first part of sig
307  footer += static_cast<uint16_t>(0x0605); //second part of sig
308  footer += static_cast<uint16_t>(0); //number of this disk
309  footer += static_cast<uint16_t>(0); //disk where footer starts
310  footer += static_cast<uint16_t>(nrecs+1); //number of records on this disk
311  footer += static_cast<uint16_t>(nrecs+1); //total number of records
312  footer += static_cast<uint32_t>(global_header.size()); //nbytes of global headers
313  footer += static_cast<uint32_t>(global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array
314  footer += static_cast<uint16_t>(0); //zip file comment length
315 
316  //write everything
317  fwrite(&local_header[0], sizeof(char), local_header.size(), fp);
318  fwrite(&npy_header[0], sizeof(char), npy_header.size(), fp);
319  fwrite(data, sizeof(T), nels, fp);
320  fwrite(&global_header[0], sizeof(char), global_header.size(), fp);
321  fwrite(&footer[0], sizeof(char), footer.size(), fp);
322  fclose(fp);
323 }
324 
334 template<typename T> void npy_save(std::string fname, const std::vector<T> data, std::string mode = "w")
335 {
336  std::vector<size_t> shape;
337  shape.push_back(data.size());
338  npy_save(fname, &data[0], shape, mode);
339 }
340 
351 template<typename T> void npz_save(std::string zipname, std::string fname, const std::vector<T> data, std::string mode = "w")
352 {
353  std::vector<size_t> shape;
354  shape.push_back(data.size());
355  npz_save(zipname, fname, &data[0], shape, mode);
356 }
357 
358 template<typename T> std::vector<char> create_npy_header(const std::vector<size_t> &shape)
359 {
360  std::vector<char> dict;
361  dict += "{'descr': '";
362  dict += BigEndianTest();
363  dict += map_type(typeid(T));
364  dict += std::to_string(sizeof(T));
365  dict += "', 'fortran_order': False, 'shape': (";
366  dict += std::to_string(shape[0]);
367  for (size_t i = 1; i < shape.size(); ++i) {
368  dict += ", ";
369  dict += std::to_string(shape[i]);
370  }
371  if (shape.size() == 1) dict += ",";
372  dict += "), }";
373  //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n
374  int remainder = 16 - (10 + dict.size()) % 16;
375  dict.insert(dict.end(), remainder, ' ');
376  dict.back() = '\n';
377 
378  std::vector<char> header;
379  header += static_cast<char>(0x93);
380  header += "NUMPY";
381  header += static_cast<char>(0x01); //major version of numpy format
382  header += static_cast<char>(0x00); //minor version of numpy format
383  header += static_cast<uint16_t>(dict.size());
384  header.insert(header.end(), dict.begin(), dict.end());
385 
386  return header;
387 }
388 
389 } // namespace cnpy
390 #endif
391 } // namespace VISP_NAMESPACE_NAME
392 #endif
393 
502 class VISP_EXPORT vpIoTools
503 {
504 
505 public:
506  static const std::string &getBuildInformation();
507  static std::string getTempPath();
508  static void getUserName(std::string &username);
509  static std::string getUserName();
510  static std::string getenv(const std::string &env);
511  static std::string getViSPImagesDataPath();
512  static void getVersion(const std::string &version, unsigned int &major, unsigned int &minor, unsigned int &patch);
513  static bool checkDirectory(const std::string &dirname);
514  static bool checkFifo(const std::string &filename);
515  static bool checkFilename(const std::string &filename);
516  static bool copy(const std::string &src, const std::string &dst);
517 
518  static void makeDirectory(const std::string &dirname);
519  static void makeFifo(const std::string &dirname);
520  static std::string makeTempDirectory(const std::string &dirname);
521  static std::string path(const std::string &pathname);
522 
523  static bool remove(const std::string &filename);
524  static bool rename(const std::string &oldfilename, const std::string &newfilename);
525 
530  static const char separator;
531 
532  static std::string toUpperCase(const std::string &input);
533  static std::string toLowerCase(const std::string &input);
534  static std::string getAbsolutePathname(const std::string &pathname);
535  static std::string getFileExtension(const std::string &pathname, bool checkFile = false);
536  static long getIndex(const std::string &filename, const std::string &format);
537  static std::string getName(const std::string &pathname);
538  static std::string getNameWE(const std::string &pathname);
539  static std::string getParent(const std::string &pathname);
540  static std::string createFilePath(const std::string &parent, const std::string &child);
541  static bool isAbsolutePathname(const std::string &pathname);
542  static bool isSamePathname(const std::string &pathname1, const std::string &pathname2);
543  static std::pair<std::string, std::string> splitDrive(const std::string &pathname);
544  static std::vector<std::string> splitChain(const std::string &chain, const std::string &sep);
545  static std::vector<std::string> getDirFiles(const std::string &dirname);
546 
551  // read configuration file
552  static bool loadConfigFile(const std::string &confFile);
553  static bool readConfigVar(const std::string &var, float &value);
554  static bool readConfigVar(const std::string &var, double &value);
555  static bool readConfigVar(const std::string &var, int &value);
556  static bool readConfigVar(const std::string &var, unsigned int &value);
557  static bool readConfigVar(const std::string &var, bool &value);
558  static bool readConfigVar(const std::string &var, std::string &value);
559  static bool readConfigVar(const std::string &var, vpColor &value);
560  static bool readConfigVar(const std::string &var, vpArray2D<double> &value, const unsigned int &nCols = 0,
561  const unsigned int &nRows = 0);
562 
563  // construct experiment filename & path
564  static void setBaseName(const std::string &s);
565  static void setBaseDir(const std::string &dir);
566  static void addNameElement(const std::string &strTrue, const bool &cond = true, const std::string &strFalse = "");
567  static void addNameElement(const std::string &strTrue, const double &val);
568  static std::string getBaseName();
569  static std::string getFullName();
570 
571  // write files
572  static void saveConfigFile(const bool &actuallySave = true);
573  static void createBaseNamePath(const bool &empty = false);
575 
576  static void readBinaryValueLE(std::ifstream &file, int16_t &short_value);
577  static void readBinaryValueLE(std::ifstream &file, uint16_t &ushort_value);
578  static void readBinaryValueLE(std::ifstream &file, int32_t &int_value);
579  static void readBinaryValueLE(std::ifstream &file, uint32_t &int_value);
580  static void readBinaryValueLE(std::ifstream &file, float &float_value);
581  static void readBinaryValueLE(std::ifstream &file, double &double_value);
582 
583  static void writeBinaryValueLE(std::ofstream &file, const int16_t short_value);
584  static void writeBinaryValueLE(std::ofstream &file, const uint16_t ushort_value);
585  static void writeBinaryValueLE(std::ofstream &file, const int32_t int_value);
586  static void writeBinaryValueLE(std::ofstream &file, const uint32_t int_value);
587  static void writeBinaryValueLE(std::ofstream &file, float float_value);
588  static void writeBinaryValueLE(std::ofstream &file, double double_value);
589 
590  static bool parseBoolean(std::string input);
591  static std::string trim(std::string s);
592 
593 protected:
594  static std::string baseName;
595  static std::string baseDir;
596  static std::string configFile;
597  static std::vector<std::string> configVars;
598  static std::vector<std::string> configValues;
599 
600 #ifndef DOXYGEN_SHOULD_SKIP_THIS
601  static int mkdir_p(const std::string &path, int mode);
602 #endif
603 };
604 END_VISP_NAMESPACE
605 #endif
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
File and directories basic tools.
Definition: vpIoTools.h:503
static std::vector< std::string > configVars
Definition: vpIoTools.h:597
static std::string baseDir
Definition: vpIoTools.h:595
static std::string configFile
Definition: vpIoTools.h:596
static std::vector< std::string > configValues
Definition: vpIoTools.h:598
static std::string baseName
Definition: vpIoTools.h:594
static const char separator
Definition: vpIoTools.h:530
VISP_EXPORT char map_type(const std::type_info &t)
std::vector< char > create_npy_header(const std::vector< size_t > &shape)
Definition: vpIoTools.h:358
void npz_save(std::string zipname, std::string fname, const T *data, const std::vector< size_t > &shape, std::string mode="w")
Definition: vpIoTools.h:235
VISP_EXPORT npz_t npz_load(std::string fname)
VISP_EXPORT void parse_zip_footer(FILE *fp, uint16_t &nrecs, size_t &global_header_size, size_t &global_header_offset)
void npy_save(std::string fname, const T *data, const std::vector< size_t > shape, std::string mode="w")
Definition: vpIoTools.h:178
VISP_EXPORT void parse_npy_header(FILE *fp, size_t &word_size, std::vector< size_t > &shape, bool &fortran_order)
std::vector< char > & operator+=(std::vector< char > &lhs, const T rhs)
Definition: vpIoTools.h:141
std::map< std::string, NpyArray > npz_t
Definition: vpIoTools.h:130
VISP_EXPORT NpyArray npy_load(std::string fname)
VISP_EXPORT char BigEndianTest()
Definition: vpIoTools.h:61
std::shared_ptr< std::vector< char > > data_holder
Definition: vpIoTools.h:123
size_t num_bytes() const
Definition: vpIoTools.h:118
std::vector< size_t > shape
Definition: vpIoTools.h:124
const T * data() const
Definition: vpIoTools.h:106
std::vector< T > as_vec() const
Definition: vpIoTools.h:112
NpyArray(const std::vector< size_t > &_shape, size_t _word_size, bool _fortran_order)
Definition: vpIoTools.h:88