Visual Servoing Platform  version 3.6.1 under development (2024-04-20)
vpIoTools.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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 
34 #ifndef _vpIoTools_h_
35 #define _vpIoTools_h_
36 
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 namespace visp
60 {
61 // https://github.com/BinomialLLC/basis_universal/blob/ad9386a4a1cf2a248f7bbd45f543a7448db15267/encoder/basisu_miniz.h#L665
62 static inline unsigned long vp_mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_len)
63 {
64  static const unsigned int s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
65  0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
66  unsigned int crcu32 = (unsigned int)crc;
67  if (!ptr) return 0;
68  crcu32 = ~crcu32;
69  while (buf_len--) {
70  unsigned char b = *ptr++;
71  crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
72  crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
73  }
74  return ~crcu32;
75 }
76 
77 namespace cnpy
78 {
79 // Copyright (C) 2011 Carl Rogers
80 // Released under MIT License
81 // license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
82 struct NpyArray
83 {
84  NpyArray(const std::vector<size_t> &_shape, size_t _word_size, bool _fortran_order) :
85  shape(_shape), word_size(_word_size), fortran_order(_fortran_order)
86  {
87  num_vals = 1;
88  for (size_t i = 0; i < shape.size(); ++i) num_vals *= shape[i];
89  data_holder = std::shared_ptr<std::vector<char> >(
90  new std::vector<char>(num_vals * word_size));
91  }
92 
94 
95  template<typename T>
96  T *data()
97  {
98  return reinterpret_cast<T *>(&(*data_holder)[0]);
99  }
100 
101  template<typename T>
102  const T *data() const
103  {
104  return reinterpret_cast<T *>(&(*data_holder)[0]);
105  }
106 
107  template<typename T>
108  std::vector<T> as_vec() const
109  {
110  const T *p = data<T>();
111  return std::vector<T>(p, p+num_vals);
112  }
113 
114  size_t num_bytes() const
115  {
116  return data_holder->size();
117  }
118 
119  std::shared_ptr<std::vector<char> > data_holder;
120  std::vector<size_t> shape;
121  size_t word_size;
123  size_t num_vals;
124 };
125 
126 using npz_t = std::map<std::string, NpyArray>;
127 VISP_EXPORT npz_t npz_load(std::string fname);
128 
129 
130 VISP_EXPORT char BigEndianTest();
131 VISP_EXPORT char map_type(const std::type_info &t);
132 template<typename T> std::vector<char> create_npy_header(const std::vector<size_t> &shape);
133 VISP_EXPORT void parse_npy_header(FILE *fp, size_t &word_size, std::vector<size_t> &shape, bool &fortran_order);
134 VISP_EXPORT void parse_npy_header(unsigned char *buffer, size_t &word_size, std::vector<size_t> &shape, bool &fortran_order);
135 VISP_EXPORT void parse_zip_footer(FILE *fp, uint16_t &nrecs, size_t &global_header_size, size_t &global_header_offset);
136 VISP_EXPORT NpyArray npz_load(std::string fname, std::string varname);
137 VISP_EXPORT NpyArray npy_load(std::string fname);
138 
139 template<typename T> std::vector<char> &operator+=(std::vector<char> &lhs, const T rhs)
140 {
141  //write in little endian
142  for (size_t byte = 0; byte < sizeof(T); ++byte) {
143  char val = *((char *)&rhs+byte);
144  lhs.push_back(val);
145  }
146  return lhs;
147 }
148 
149 template<> inline std::vector<char> &operator+=(std::vector<char> &lhs, const std::string rhs)
150 {
151  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
152  return lhs;
153 }
154 
155 template<> inline std::vector<char> &operator+=(std::vector<char> &lhs, const char *rhs)
156 {
157 //write in little endian
158  size_t len = strlen(rhs);
159  lhs.reserve(len);
160  for (size_t byte = 0; byte < len; ++byte) {
161  lhs.push_back(rhs[byte]);
162  }
163  return lhs;
164 }
165 
176 template<typename T> void npy_save(std::string fname, const T *data, const std::vector<size_t> shape, std::string mode = "w")
177 {
178  FILE *fp = NULL;
179  std::vector<size_t> true_data_shape; //if appending, the shape of existing + new data
180 
181  if (mode == "a") fp = fopen(fname.c_str(), "r+b");
182 
183  if (fp) {
184  //file exists. we need to append to it. read the header, modify the array size
185  size_t word_size;
186  bool fortran_order;
187  parse_npy_header(fp, word_size, true_data_shape, fortran_order);
188  assert(!fortran_order);
189 
190  if (word_size != sizeof(T)) {
191  std::cout<<"libnpy error: "<<fname<<" has word size "<<word_size<<" but npy_save appending data sized "<<sizeof(T)<<"\n";
192  assert(word_size == sizeof(T));
193  }
194  if (true_data_shape.size() != shape.size()) {
195  std::cout<<"libnpy error: npy_save attempting to append misdimensioned data to "<<fname<<"\n";
196  assert(true_data_shape.size() != shape.size());
197  }
198 
199  for (size_t i = 1; i < shape.size(); ++i) {
200  if (shape[i] != true_data_shape[i]) {
201  std::cout<<"libnpy error: npy_save attempting to append misshaped data to "<<fname<<"\n";
202  assert(shape[i] == true_data_shape[i]);
203  }
204  }
205  true_data_shape[0] += shape[0];
206  }
207  else {
208  fp = fopen(fname.c_str(), "wb");
209  true_data_shape = shape;
210  }
211 
212  std::vector<char> header = create_npy_header<T>(true_data_shape);
213  size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
214 
215  fseek(fp, 0, SEEK_SET);
216  fwrite(&header[0], sizeof(char), header.size(), fp);
217  fseek(fp, 0, SEEK_END);
218  fwrite(data, sizeof(T), nels, fp);
219  fclose(fp);
220 }
221 
233 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")
234 {
235  //first, append a .npy to the fname
236  fname += ".npy";
237 
238  //now, on with the show
239  FILE *fp = NULL;
240  uint16_t nrecs = 0;
241  size_t global_header_offset = 0;
242  std::vector<char> global_header;
243 
244  if (mode == "a") fp = fopen(zipname.c_str(), "r+b");
245 
246  if (fp) {
247  //zip file exists. we need to add a new npy file to it.
248  //first read the footer. this gives us the offset and size of the global header
249  //then read and store the global header.
250  //below, we will write the the new data at the start of the global header then append the global header and footer below it
251  size_t global_header_size;
252  parse_zip_footer(fp, nrecs, global_header_size, global_header_offset);
253  fseek(fp, static_cast<long>(global_header_offset), SEEK_SET);
254  global_header.resize(global_header_size);
255  size_t res = fread(&global_header[0], sizeof(char), global_header_size, fp);
256  if (res != global_header_size) {
257  throw std::runtime_error("npz_save: header read error while adding to existing zip");
258  }
259  fseek(fp, static_cast<long>(global_header_offset), SEEK_SET);
260  }
261  else {
262  fp = fopen(zipname.c_str(), "wb");
263  }
264 
265  std::vector<char> npy_header = create_npy_header<T>(shape);
266 
267  size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
268  size_t nbytes = nels*sizeof(T) + npy_header.size();
269 
270  //get the CRC of the data to be added
271  uint32_t crc = vp_mz_crc32(0L, (uint8_t *)&npy_header[0], npy_header.size());
272  crc = vp_mz_crc32(crc, (uint8_t *)data, nels*sizeof(T));
273 
274  //build the local header
275  std::vector<char> local_header;
276  local_header += "PK"; //first part of sig
277  local_header += (uint16_t)0x0403; //second part of sig
278  local_header += (uint16_t)20; //min version to extract
279  local_header += (uint16_t)0; //general purpose bit flag
280  local_header += (uint16_t)0; //compression method
281  local_header += (uint16_t)0; //file last mod time
282  local_header += (uint16_t)0; //file last mod date
283  local_header += (uint32_t)crc; //crc
284  local_header += (uint32_t)nbytes; //compressed size
285  local_header += (uint32_t)nbytes; //uncompressed size
286  local_header += (uint16_t)fname.size(); //fname length
287  local_header += (uint16_t)0; //extra field length
288  local_header += fname;
289 
290  //build global header
291  global_header += "PK"; //first part of sig
292  global_header += (uint16_t)0x0201; //second part of sig
293  global_header += (uint16_t)20; //version made by
294  global_header.insert(global_header.end(), local_header.begin()+4, local_header.begin()+30);
295  global_header += (uint16_t)0; //file comment length
296  global_header += (uint16_t)0; //disk number where file starts
297  global_header += (uint16_t)0; //internal file attributes
298  global_header += (uint32_t)0; //external file attributes
299  global_header += (uint32_t)global_header_offset; //relative offset of local file header, since it begins where the global header used to begin
300  global_header += fname;
301 
302  //build footer
303  std::vector<char> footer;
304  footer += "PK"; //first part of sig
305  footer += (uint16_t)0x0605; //second part of sig
306  footer += (uint16_t)0; //number of this disk
307  footer += (uint16_t)0; //disk where footer starts
308  footer += (uint16_t)(nrecs+1); //number of records on this disk
309  footer += (uint16_t)(nrecs+1); //total number of records
310  footer += (uint32_t)global_header.size(); //nbytes of global headers
311  footer += (uint32_t)(global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array
312  footer += (uint16_t)0; //zip file comment length
313 
314  //write everything
315  fwrite(&local_header[0], sizeof(char), local_header.size(), fp);
316  fwrite(&npy_header[0], sizeof(char), npy_header.size(), fp);
317  fwrite(data, sizeof(T), nels, fp);
318  fwrite(&global_header[0], sizeof(char), global_header.size(), fp);
319  fwrite(&footer[0], sizeof(char), footer.size(), fp);
320  fclose(fp);
321 }
322 
332 template<typename T> void npy_save(std::string fname, const std::vector<T> data, std::string mode = "w")
333 {
334  std::vector<size_t> shape;
335  shape.push_back(data.size());
336  npy_save(fname, &data[0], shape, mode);
337 }
338 
349 template<typename T> void npz_save(std::string zipname, std::string fname, const std::vector<T> data, std::string mode = "w")
350 {
351  std::vector<size_t> shape;
352  shape.push_back(data.size());
353  npz_save(zipname, fname, &data[0], shape, mode);
354 }
355 
356 template<typename T> std::vector<char> create_npy_header(const std::vector<size_t> &shape)
357 {
358  std::vector<char> dict;
359  dict += "{'descr': '";
360  dict += BigEndianTest();
361  dict += map_type(typeid(T));
362  dict += std::to_string(sizeof(T));
363  dict += "', 'fortran_order': False, 'shape': (";
364  dict += std::to_string(shape[0]);
365  for (size_t i = 1; i < shape.size(); ++i) {
366  dict += ", ";
367  dict += std::to_string(shape[i]);
368  }
369  if (shape.size() == 1) dict += ",";
370  dict += "), }";
371  //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n
372  int remainder = 16 - (10 + dict.size()) % 16;
373  dict.insert(dict.end(), remainder, ' ');
374  dict.back() = '\n';
375 
376  std::vector<char> header;
377  header += (char)0x93;
378  header += "NUMPY";
379  header += (char)0x01; //major version of numpy format
380  header += (char)0x00; //minor version of numpy format
381  header += (uint16_t)dict.size();
382  header.insert(header.end(), dict.begin(), dict.end());
383 
384  return header;
385 }
386 
387 } // namespace cnpy
388 } // namespace visp
389 #endif
390 
490 class VISP_EXPORT vpIoTools
491 {
492 
493 public:
494  static const std::string &getBuildInformation();
495  static std::string getTempPath();
496  static void getUserName(std::string &username);
497  static std::string getUserName();
498  static std::string getenv(const std::string &env);
499  static std::string getViSPImagesDataPath();
500  static void getVersion(const std::string &version, unsigned int &major, unsigned int &minor, unsigned int &patch);
501  static bool checkDirectory(const std::string &dirname);
502  static bool checkFifo(const std::string &filename);
503  static bool checkFilename(const std::string &filename);
504  static bool copy(const std::string &src, const std::string &dst);
505 
506  static void makeDirectory(const std::string &dirname);
507  static void makeFifo(const std::string &dirname);
508  static std::string makeTempDirectory(const std::string &dirname);
509  static std::string path(const std::string &pathname);
510 
511  static bool remove(const std::string &filename);
512  static bool rename(const std::string &oldfilename, const std::string &newfilename);
513 
518  static const char separator;
519 
520  static std::string toUpperCase(const std::string &input);
521  static std::string toLowerCase(const std::string &input);
522  static std::string getAbsolutePathname(const std::string &pathname);
523  static std::string getFileExtension(const std::string &pathname, bool checkFile = false);
524  static long getIndex(const std::string &filename, const std::string &format);
525  static std::string getName(const std::string &pathname);
526  static std::string getNameWE(const std::string &pathname);
527  static std::string getParent(const std::string &pathname);
528  static std::string createFilePath(const std::string &parent, const std::string &child);
529  static bool isAbsolutePathname(const std::string &pathname);
530  static bool isSamePathname(const std::string &pathname1, const std::string &pathname2);
531  static std::pair<std::string, std::string> splitDrive(const std::string &pathname);
532  static std::vector<std::string> splitChain(const std::string &chain, const std::string &sep);
533  static std::vector<std::string> getDirFiles(const std::string &dirname);
534 
539  // read configuration file
540  static bool loadConfigFile(const std::string &confFile);
541  static bool readConfigVar(const std::string &var, float &value);
542  static bool readConfigVar(const std::string &var, double &value);
543  static bool readConfigVar(const std::string &var, int &value);
544  static bool readConfigVar(const std::string &var, unsigned int &value);
545  static bool readConfigVar(const std::string &var, bool &value);
546  static bool readConfigVar(const std::string &var, std::string &value);
547  static bool readConfigVar(const std::string &var, vpColor &value);
548  static bool readConfigVar(const std::string &var, vpArray2D<double> &value, const unsigned int &nCols = 0,
549  const unsigned int &nRows = 0);
550 
551  // construct experiment filename & path
552  static void setBaseName(const std::string &s);
553  static void setBaseDir(const std::string &dir);
554  static void addNameElement(const std::string &strTrue, const bool &cond = true, const std::string &strFalse = "");
555  static void addNameElement(const std::string &strTrue, const double &val);
556  static std::string getBaseName();
557  static std::string getFullName();
558 
559  // write files
560  static void saveConfigFile(const bool &actuallySave = true);
561  static void createBaseNamePath(const bool &empty = false);
563 
564  static void readBinaryValueLE(std::ifstream &file, int16_t &short_value);
565  static void readBinaryValueLE(std::ifstream &file, uint16_t &ushort_value);
566  static void readBinaryValueLE(std::ifstream &file, int32_t &int_value);
567  static void readBinaryValueLE(std::ifstream &file, uint32_t &int_value);
568  static void readBinaryValueLE(std::ifstream &file, float &float_value);
569  static void readBinaryValueLE(std::ifstream &file, double &double_value);
570 
571  static void writeBinaryValueLE(std::ofstream &file, const int16_t short_value);
572  static void writeBinaryValueLE(std::ofstream &file, const uint16_t ushort_value);
573  static void writeBinaryValueLE(std::ofstream &file, const int32_t int_value);
574  static void writeBinaryValueLE(std::ofstream &file, const uint32_t int_value);
575  static void writeBinaryValueLE(std::ofstream &file, float float_value);
576  static void writeBinaryValueLE(std::ofstream &file, double double_value);
577 
578  static bool parseBoolean(std::string input);
579  static std::string trim(std::string s);
580 
581 protected:
582  static std::string baseName;
583  static std::string baseDir;
584  static std::string configFile;
585  static std::vector<std::string> configVars;
586  static std::vector<std::string> configValues;
587 
588 #ifndef DOXYGEN_SHOULD_SKIP_THIS
589  static int mkdir_p(const std::string &path, int mode);
590 #endif
591 };
592 #endif
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:152
File and directories basic tools.
Definition: vpIoTools.h:491
static std::vector< std::string > configVars
Definition: vpIoTools.h:585
static std::string baseDir
Definition: vpIoTools.h:583
static std::string baseName
Definition: vpIoTools.h:582
static std::string configFile
Definition: vpIoTools.h:584
static std::vector< std::string > configValues
Definition: vpIoTools.h:586
static const char separator
Definition: vpIoTools.h:518
VISP_EXPORT char map_type(const std::type_info &t)
Definition: vpIoTools.cpp:135
std::vector< char > create_npy_header(const std::vector< size_t > &shape)
Definition: vpIoTools.h:356
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:233
VISP_EXPORT npz_t npz_load(std::string fname)
Definition: vpIoTools.cpp:342
VISP_EXPORT void parse_zip_footer(FILE *fp, uint16_t &nrecs, size_t &global_header_size, size_t &global_header_offset)
Definition: vpIoTools.cpp:253
void npy_save(std::string fname, const T *data, const std::vector< size_t > shape, std::string mode="w")
Definition: vpIoTools.h:176
VISP_EXPORT void parse_npy_header(FILE *fp, size_t &word_size, std::vector< size_t > &shape, bool &fortran_order)
Definition: vpIoTools.cpp:197
std::vector< char > & operator+=(std::vector< char > &lhs, const T rhs)
Definition: vpIoTools.h:139
std::map< std::string, NpyArray > npz_t
Definition: vpIoTools.h:126
VISP_EXPORT NpyArray npy_load(std::string fname)
Definition: vpIoTools.cpp:470
VISP_EXPORT char BigEndianTest()
Definition: vpIoTools.cpp:129
Definition: vpIoTools.h:60
static unsigned long vp_mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_len)
Definition: vpIoTools.h:62
std::shared_ptr< std::vector< char > > data_holder
Definition: vpIoTools.h:119
size_t num_bytes() const
Definition: vpIoTools.h:114
std::vector< size_t > shape
Definition: vpIoTools.h:120
const T * data() const
Definition: vpIoTools.h:102
std::vector< T > as_vec() const
Definition: vpIoTools.h:108
NpyArray(const std::vector< size_t > &_shape, size_t _word_size, bool _fortran_order)
Definition: vpIoTools.h:84