Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpArray2D.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  * This class implements an 2D array as a template class.
32  */
33 #ifndef VP_ARRAY2D_H
34 #define VP_ARRAY2D_H
35 
36 #include <fstream>
37 #include <iostream>
38 #include <limits>
39 #include <math.h>
40 #include <ostream>
41 #include <sstream>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <vector>
45 
46 #include <visp3/core/vpConfig.h>
47 #include <visp3/core/vpException.h>
48 
49 #ifdef VISP_HAVE_NLOHMANN_JSON
50 #include <nlohmann/json.hpp>
51 #endif
52 
144 template <class Type> class vpArray2D
145 {
146 public:
148  Type *data;
149 
154  vpArray2D<Type>() : data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0) { }
155 
160  :
161 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
162  vpArray2D<Type>()
163 #else
164  data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0)
165 #endif
166  {
167  resize(A.rowNum, A.colNum, false, false);
168  memcpy(data, A.data, static_cast<size_t>(rowNum) * static_cast<size_t>(colNum) * sizeof(Type));
169  }
170 
177  vpArray2D<Type>(unsigned int r, unsigned int c)
178  :
179 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
180  vpArray2D<Type>()
181 #else
182  data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0)
183 #endif
184  {
185  resize(r, c);
186  }
187 
195  vpArray2D<Type>(unsigned int r, unsigned int c, Type val)
196  :
197 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
198  vpArray2D<Type>()
199 #else
200  data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0)
201 #endif
202  {
203  resize(r, c, false, false);
204  *this = val;
205  }
206 
218  vpArray2D<Type>(const std::vector<Type> &vec, unsigned int r = 0, unsigned int c = 0)
219  :
220 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
221  vpArray2D<Type>()
222 #else
223  data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0)
224 #endif
225  {
226  if ((r > 0) && (c > 0)) {
227  if ((r * c) != vec.size()) {
229  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
230  }
231  resize(r, c, false, false);
232  }
233  else if ((c == 0) && (r == 0)) {
235  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Using rows = 0 and cols = 0 is ambiguous", r, c, vec.size()));
236  }
237  else if (c == 0) {
238  if (r != vec.size()) {
240  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
241  }
242  resize(static_cast<unsigned int>(vec.size()), 1, false, false);
243  }
244  else if (r == 0) {
245  if (c != vec.size()) {
247  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
248  }
249  resize(1, static_cast<unsigned int>(vec.size()), false, false);
250  }
251 
252 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
253  std::copy(vec.begin(), vec.end(), data);
254 #else
255  memcpy(data, vec.data(), vec.size() * sizeof(Type));
256 #endif
257  }
258 
259 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
260  vpArray2D<Type>(vpArray2D<Type> &&A) noexcept
261  {
262  rowNum = A.rowNum;
263  colNum = A.colNum;
264  rowPtrs = A.rowPtrs;
265  dsize = A.dsize;
266  data = A.data;
267 
268  A.rowNum = 0;
269  A.colNum = 0;
270  A.rowPtrs = nullptr;
271  A.dsize = 0;
272  A.data = nullptr;
273  }
274 
275  VP_EXPLICIT vpArray2D<Type>(const std::initializer_list<Type> &list) : vpArray2D<Type>()
276  {
277  resize(1, static_cast<unsigned int>(list.size()), false, false);
278  std::copy(list.begin(), list.end(), data);
279  }
280 
281  VP_EXPLICIT vpArray2D<Type>(unsigned int nrows, unsigned int ncols, const std::initializer_list<Type> &list)
282  : data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0)
283  {
284  if ((nrows * ncols) != static_cast<unsigned int>(list.size())) {
285  std::ostringstream oss;
286  oss << "Cannot create a vpArray2D of size (" << nrows << ", " << ncols << ") with a list of size " << list.size();
287  throw vpException(vpException::dimensionError, oss.str());
288  }
289 
290  resize(nrows, ncols, false, false);
291  std::copy(list.begin(), list.end(), data);
292  }
293 
294  VP_EXPLICIT vpArray2D<Type>(const std::initializer_list<std::initializer_list<Type> > &lists) : vpArray2D<Type>()
295  {
296  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
297  for (auto &l : lists) {
298  if (static_cast<unsigned int>(l.size()) > ncols) {
299  ncols = static_cast<unsigned int>(l.size());
300  }
301  }
302 
303  resize(nrows, ncols, false, false);
304  auto it = lists.begin();
305  for (unsigned int i = 0; i < rowNum; ++i, ++it) {
306  std::copy(it->begin(), it->end(), rowPtrs[i]);
307  }
308  }
309 #endif
310 
314  virtual ~vpArray2D<Type>()
315  {
316  if (data != nullptr) {
317  free(data);
318  data = nullptr;
319  }
320 
321  if (rowPtrs != nullptr) {
322  free(rowPtrs);
323  rowPtrs = nullptr;
324  }
325  rowNum = 0;
326  colNum = 0;
327  dsize = 0;
328  }
329 
332 
337  inline unsigned int getCols() const { return colNum; }
338 
339  Type getMaxValue() const;
340 
341  Type getMinValue() const;
342 
347  inline unsigned int getRows() const { return rowNum; }
349  inline unsigned int size() const { return colNum * rowNum; }
350 
362  void resize(unsigned int nrows, unsigned int ncols, bool flagNullify = true, bool recopy_ = true)
363  {
364  if ((nrows == rowNum) && (ncols == colNum)) {
365  if (flagNullify && (this->data != nullptr)) {
366  memset(this->data, 0, this->dsize * sizeof(Type));
367  }
368  }
369  else {
370  bool recopy = (!flagNullify) && recopy_; // priority to flagNullify
371  bool colcond = (ncols != this->colNum) && (this->colNum > 0) && (ncols > 0);
372  const bool recopyNeeded = colcond && ((!flagNullify) || recopy);
373  Type *copyTmp = nullptr;
374  unsigned int rowTmp = 0, colTmp = 0;
375 
376  // Recopy case per case is required if number of cols has changed;
377  // structure of Type array is not the same in this case.
378  if (recopyNeeded && (this->data != nullptr)) {
379  copyTmp = new Type[this->dsize];
380  memcpy(copyTmp, this->data, sizeof(Type) * this->dsize);
381  rowTmp = this->rowNum;
382  colTmp = this->colNum;
383  }
384 
385  // Reallocation of this->data array
386  this->dsize = nrows * ncols;
387  Type *tmp_data = reinterpret_cast<Type *>(realloc(this->data, this->dsize * sizeof(Type)));
388  if (tmp_data) {
389  this->data = tmp_data;
390  }
391  else {
392  this->data = nullptr;
393  }
394 
395  if ((nullptr == this->data) && (0 != this->dsize)) {
396  if (copyTmp != nullptr) {
397  delete[] copyTmp;
398  }
399  throw(vpException(vpException::memoryAllocationError, "Memory allocation error when allocating 2D array data"));
400  }
401 
402  Type **tmp_rowPtrs = reinterpret_cast<Type **>(realloc(this->rowPtrs, nrows * sizeof(Type *)));
403  if (tmp_rowPtrs) {
404  this->rowPtrs = tmp_rowPtrs;
405  }
406  else {
407  this->rowPtrs = nullptr;
408  }
409  if ((nullptr == this->rowPtrs) && (0 != this->dsize)) {
410  if (copyTmp != nullptr) {
411  delete[] copyTmp;
412  }
414  "Memory allocation error when allocating 2D array rowPtrs"));
415  }
416 
417  // Update rowPtrs
418 
419  Type **t_ = rowPtrs;
420  for (unsigned int i = 0; i < dsize; i += ncols) {
421  *t_++ = this->data + i;
422  }
423 
424  this->rowNum = nrows;
425  this->colNum = ncols;
426 
427  // Recopy of this->data array values or nullify
428  if (flagNullify) {
429  memset(this->data, 0, static_cast<size_t>(this->dsize) * sizeof(Type));
430  }
431  else if (recopyNeeded && (this->rowPtrs != nullptr)) {
432  // Recopy...
433  unsigned int minRow = (this->rowNum < rowTmp) ? this->rowNum : rowTmp;
434  unsigned int minCol = (this->colNum < colTmp) ? this->colNum : colTmp;
435  for (unsigned int i = 0; i < this->rowNum; ++i) {
436  for (unsigned int j = 0; j < this->colNum; ++j) {
437  if ((minRow > i) && (minCol > j)) {
438  (*this)[i][j] = copyTmp[(i * colTmp) + j];
439  }
440  else {
441  (*this)[i][j] = 0;
442  }
443  }
444  }
445  }
446 
447  if (copyTmp != nullptr) {
448  delete[] copyTmp;
449  }
450  }
451  }
452 
453  void reshape(unsigned int nrows, unsigned int ncols)
454  {
455  if (dsize == 0) {
456  resize(nrows, ncols);
457  return;
458  }
459 
460  if ((nrows * ncols) != dsize) {
461  std::ostringstream oss;
462  oss << "Cannot reshape array of total size " << dsize << " into shape (" << nrows << ", " << ncols << ")";
463  throw vpException(vpException::dimensionError, oss.str());
464  }
465 
466  rowNum = nrows;
467  colNum = ncols;
468  if (rowPtrs) {
469  Type **tmp = reinterpret_cast<Type **>(realloc(rowPtrs, nrows * sizeof(Type *)));
470  if (tmp) {
471  this->rowPtrs = tmp;
472  }
473  }
474  if (rowPtrs) {
475  // Update rowPtrs
476  Type **t_ = rowPtrs;
477  for (unsigned int i = 0; i < dsize; i += ncols) {
478  *t_++ = data + i;
479  }
480  }
481  }
482 
483 
494  void insert(const vpArray2D<Type> &A, unsigned int r, unsigned int c)
495  {
496  if (((r + A.getRows()) <= rowNum) && ((c + A.getCols()) <= colNum)) {
497  if ((A.colNum == colNum) && (data != nullptr) && (A.data != nullptr) && (A.data != data)) {
498  memcpy(data + (r * colNum), A.data, sizeof(Type) * A.size());
499  }
500  else if ((data != nullptr) && (A.data != nullptr) && (A.data != data)) {
501  unsigned int a_rows = A.getRows();
502  for (unsigned int i = r; i < (r + a_rows); ++i) {
503  memcpy(data + (i * colNum) + c, A.data + ((i - r) * A.colNum), sizeof(Type) * A.colNum);
504  }
505  }
506  }
507  else {
508  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
509  A.getRows(), A.getCols(), rowNum, colNum, r, c);
510  }
511  }
512 
516  bool operator==(const vpArray2D<Type> &A) const;
520  bool operator!=(const vpArray2D<Type> &A) const;
521 
524  {
525  std::fill(data, data + dsize, x);
526  return *this;
527  }
528 
533  {
534  resize(A.rowNum, A.colNum, false, false);
535  if ((data != nullptr) && (A.data != nullptr) && (data != A.data)) {
536  memcpy(data, A.data, static_cast<size_t>(rowNum) * static_cast<size_t>(colNum) * sizeof(Type));
537  }
538  return *this;
539  }
540 
541 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
543  {
544  if (this != &other) {
545  if (data) {
546  free(data);
547  }
548  if (rowPtrs) {
549  free(rowPtrs);
550  }
551 
552  rowNum = other.rowNum;
553  colNum = other.colNum;
554  rowPtrs = other.rowPtrs;
555  dsize = other.dsize;
556  data = other.data;
557 
558  other.rowNum = 0;
559  other.colNum = 0;
560  other.rowPtrs = nullptr;
561  other.dsize = 0;
562  other.data = nullptr;
563  }
564 
565  return *this;
566  }
567 
568  vpArray2D<Type> &operator=(const std::initializer_list<Type> &list)
569  {
570  if (dsize != static_cast<unsigned int>(list.size())) {
571  resize(1, static_cast<unsigned int>(list.size()), false, false);
572  }
573  std::copy(list.begin(), list.end(), data);
574 
575  return *this;
576  }
577 
578  vpArray2D<Type> &operator=(const std::initializer_list<std::initializer_list<Type> > &lists)
579  {
580  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
581  for (auto &l : lists) {
582  if (static_cast<unsigned int>(l.size()) > ncols) {
583  ncols = static_cast<unsigned int>(l.size());
584  }
585  }
586 
587  resize(nrows, ncols, false, false);
588  auto it = lists.begin();
589  for (unsigned int i = 0; i < rowNum; ++i, ++it) {
590  std::copy(it->begin(), it->end(), rowPtrs[i]);
591  }
592 
593  return *this;
594  }
595 
596 #ifdef VISP_HAVE_NLOHMANN_JSON
597  vpArray2D<Type> &operator=(const nlohmann::json &j) = delete;
598 #endif
599 #endif
600 
602  inline Type *operator[](unsigned int i) { return rowPtrs[i]; }
604  inline Type *operator[](unsigned int i) const { return rowPtrs[i]; }
605 
611  friend std::ostream &operator<<(std::ostream &s, const vpArray2D<Type> &A)
612  {
613  if ((A.data == nullptr) || (A.size() == 0)) {
614  return s;
615  }
616  std::ios_base::fmtflags original_flags = s.flags();
617  const unsigned int precision = 10;
618  s.precision(precision);
619  unsigned int a_rows = A.getRows();
620  unsigned int a_cols = A.getCols();
621  for (unsigned int i = 0; i < a_rows; ++i) {
622  for (unsigned int j = 0; j < (a_cols - 1); ++j) {
623  s << A[i][j] << " ";
624  }
625  // We don't add " " after the last row element
626  s << A[i][a_cols - 1];
627  // We don't add a \n char on the end of the last array line
628  if (i < (a_rows - 1)) {
629  s << std::endl;
630  }
631  }
632 
633  s.flags(original_flags); // restore s to standard state
634 
635  return s;
636  }
637 
639 
647 
648  //---------------------------------
649  // Inherited array I/O Static Public Member Functions
650  //---------------------------------
666  static bool load(const std::string &filename, vpArray2D<Type> &A, bool binary = false, char *header = nullptr)
667  {
668  std::fstream file;
669 
670  if (!binary) {
671  file.open(filename.c_str(), std::fstream::in);
672  }
673  else {
674  file.open(filename.c_str(), std::fstream::in | std::fstream::binary);
675  }
676 
677  if (!file) {
678  file.close();
679  return false;
680  }
681 
682  if (!binary) {
683  std::string h;
684  bool headerIsDecoded = false;
685  do {
686  std::streampos pos = file.tellg();
687  char line[FILENAME_MAX];
688  file.getline(line, FILENAME_MAX);
689  std::string prefix("# ");
690  std::string line_(line);
691  if (line_.compare(0, prefix.size(), prefix.c_str()) == 0) {
692  // Line is a comment
693  // If we are not on the first line, we should add "\n" to the end of
694  // the previous line
695  if (pos) {
696  h += "\n";
697  }
698  h += line_.substr(prefix.size()); // Remove "# "
699  }
700  else {
701  // rewind before the line
702  file.seekg(pos, file.beg);
703  headerIsDecoded = true;
704  }
705  } while (!headerIsDecoded);
706 
707  if (header != nullptr) {
708 #if defined(__MINGW32__) || \
709  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
710  snprintf(header, h.size() + 1, "%s", h.c_str());
711 #else
712  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
713 #endif
714  }
715 
716  unsigned int rows, cols;
717  file >> rows;
718  file >> cols;
719 
720  if ((rows >= std::numeric_limits<unsigned int>::max()) || (cols >= std::numeric_limits<unsigned int>::max())) {
721  throw vpException(vpException::badValue, "Array exceed the max size.");
722  }
723 
724  A.resize(rows, cols);
725 
726  Type value;
727  for (unsigned int i = 0; i < rows; ++i) {
728  for (unsigned int j = 0; j < cols; ++j) {
729  file >> value;
730  A[i][j] = value;
731  }
732  }
733  }
734  else {
735  char c = '0';
736  std::string h;
737  // Decode header until '\0' char that ends the header string
738  while (c != '\0') {
739  file.read(&c, 1);
740  h += c;
741  }
742  if (header != nullptr) {
743 #if defined(__MINGW32__) || \
744  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
745  snprintf(header, h.size() + 1, "%s", h.c_str());
746 #else
747  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
748 #endif
749  }
750 
751  unsigned int rows, cols;
752  file.read(reinterpret_cast<char *>(&rows), sizeof(unsigned int));
753  file.read(reinterpret_cast<char *>(&cols), sizeof(unsigned int));
754  A.resize(rows, cols);
755 
756  Type value;
757  for (unsigned int i = 0; i < rows; ++i) {
758  for (unsigned int j = 0; j < cols; ++j) {
759  file.read(reinterpret_cast<char *>(&value), sizeof(Type));
760  A[i][j] = value;
761  }
762  }
763  }
764 
765  file.close();
766  return true;
767  }
768 
780  static bool loadYAML(const std::string &filename, vpArray2D<Type> &A, char *header = nullptr)
781  {
782  std::fstream file;
783 
784  file.open(filename.c_str(), std::fstream::in);
785 
786  if (!file) {
787  file.close();
788  return false;
789  }
790 
791  unsigned int rows = 0, cols = 0;
792  std::string h;
793  std::string line, subs;
794  bool inheader = true;
795  unsigned int i = 0, j;
796  unsigned int lineStart = 0;
797 
798  while (getline(file, line)) {
799  if (inheader) {
800  const std::string str_rows("rows:");
801  const std::string str_cols("cols:");
802  const std::string str_data("data:");
803  if ((rows == 0) && (line.compare(0, str_rows.size(), str_rows.c_str()) == 0)) {
804  std::stringstream ss(line);
805  ss >> subs;
806  ss >> rows;
807  }
808  else if ((cols == 0) && (line.compare(0, str_cols.size(), str_cols.c_str()) == 0)) {
809  std::stringstream ss(line);
810  ss >> subs;
811  ss >> cols;
812  }
813  else if (line.compare(0, str_data.size(), str_data.c_str()) == 0) {
814  inheader = false;
815  }
816  else {
817  h += line + "\n";
818  }
819  }
820  else {
821  // if i == 0, we just got out of the header: initialize matrix
822  // dimensions
823  if (i == 0) {
824  if ((rows == 0) || (cols == 0)) {
825  file.close();
826  return false;
827  }
828  A.resize(rows, cols);
829  // get indentation level which is common to all lines
830  lineStart = static_cast<unsigned int>(line.find("[")) + 1;
831  }
832  std::stringstream ss(line.substr(lineStart, line.find("]") - lineStart));
833  j = 0;
834  while (getline(ss, subs, ',')) {
835  A[i][j++] = atof(subs.c_str());
836  }
837  ++i;
838  }
839  }
840 
841  if (header != nullptr) {
842  std::string h_ = h.substr(0, h.size() - 1); // Remove last '\n' char
843 #if defined(__MINGW32__) || \
844  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
845  snprintf(header, h_.size() + 1, "%s", h_.c_str());
846 #else
847  _snprintf_s(header, h_.size() + 1, _TRUNCATE, "%s", h_.c_str());
848 #endif
849  }
850 
851  file.close();
852  return true;
853  }
854 
871  static bool save(const std::string &filename, const vpArray2D<Type> &A, bool binary = false, const char *header = "")
872  {
873  std::fstream file;
874 
875  if (!binary) {
876  file.open(filename.c_str(), std::fstream::out);
877  }
878  else {
879  file.open(filename.c_str(), std::fstream::out | std::fstream::binary);
880  }
881 
882  if (!file) {
883  file.close();
884  return false;
885  }
886 
887  if (!binary) {
888  unsigned int i = 0;
889  file << "# ";
890  while (header[i] != '\0') {
891  file << header[i];
892  if (header[i] == '\n') {
893  file << "# ";
894  }
895  ++i;
896  }
897  file << std::endl;
898  file << A.getRows() << "\t" << A.getCols() << std::endl;
899  file << A << std::endl;
900  }
901  else {
902  int headerSize = 0;
903  while (header[headerSize] != '\0') {
904  ++headerSize;
905  }
906  file.write(header, static_cast<size_t>(headerSize)+static_cast<size_t>(1));
907  unsigned int matrixSize;
908  matrixSize = A.getRows();
909  file.write(reinterpret_cast<char *>(&matrixSize), sizeof(unsigned int));
910  matrixSize = A.getCols();
911  file.write(reinterpret_cast<char *>(&matrixSize), sizeof(unsigned int));
912  Type value;
913  unsigned int a_rows = A.getRows();
914  unsigned int a_cols = A.getCols();
915  for (unsigned int i = 0; i < a_rows; ++i) {
916  for (unsigned int j = 0; j < a_cols; ++j) {
917  value = A[i][j];
918  file.write(reinterpret_cast<char *>(&value), sizeof(Type));
919  }
920  }
921  }
922 
923  file.close();
924  return true;
925  }
926 
969  static bool saveYAML(const std::string &filename, const vpArray2D<Type> &A, const char *header = "")
970  {
971  std::fstream file;
972 
973  file.open(filename.c_str(), std::fstream::out);
974 
975  if (!file) {
976  file.close();
977  return false;
978  }
979 
980  unsigned int i = 0;
981  bool inIndent = false;
982  std::string indent = "";
983  bool checkIndent = true;
984  while (header[i] != '\0') {
985  file << header[i];
986  if (checkIndent) {
987  if (inIndent) {
988  if (header[i] == ' ') {
989  indent += " ";
990  }
991  else if (indent.length() > 0) {
992  checkIndent = false;
993  }
994  }
995  if ((header[i] == '\n') || (inIndent && (header[i] == ' '))) {
996  inIndent = true;
997  }
998  else {
999  inIndent = false;
1000  }
1001  }
1002  ++i;
1003  }
1004 
1005  if (i != 0) {
1006  file << std::endl;
1007  }
1008  file << "rows: " << A.getRows() << std::endl;
1009  file << "cols: " << A.getCols() << std::endl;
1010 
1011  if (indent.length() == 0) {
1012  indent = " ";
1013  }
1014 
1015  file << "data: " << std::endl;
1016  unsigned int j;
1017  unsigned int a_rows = A.getRows();
1018  unsigned int a_cols = A.getCols();
1019  for (i = 0; i < a_rows; ++i) {
1020  file << indent << "- [";
1021  for (j = 0; j < (a_cols - 1); ++j) {
1022  file << A[i][j] << ", ";
1023  }
1024  file << A[i][j] << "]" << std::endl;
1025  }
1026 
1027  file.close();
1028  return true;
1029  }
1030 #ifdef VISP_HAVE_NLOHMANN_JSON
1031  //template<typename Type>
1032  template<class T>
1033  friend void from_json(const nlohmann::json &j, vpArray2D<T> &array);
1034  //template<typename Type>
1035  template<class T>
1036  friend void to_json(nlohmann::json &j, const vpArray2D<T> &array);
1037 #endif
1038 
1050  static vpArray2D<Type> conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode);
1051 
1064  static void conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode);
1065 
1078  vpArray2D<Type> insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c);
1079 
1093  static void insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c);
1095 
1096 protected:
1098  unsigned int rowNum;
1100  unsigned int colNum;
1102  Type **rowPtrs;
1104  unsigned int dsize;
1105 };
1106 
1110 template <class Type> Type vpArray2D<Type>::getMinValue() const
1111 {
1112  Type *dataptr = data;
1113  Type min = *dataptr;
1114  ++dataptr;
1115  for (unsigned int i = 0; i < (dsize - 1); ++i) {
1116  if (*dataptr < min) {
1117  min = *dataptr;
1118  }
1119  ++dataptr;
1120  }
1121  return min;
1122 }
1123 
1127 template <class Type> Type vpArray2D<Type>::getMaxValue() const
1128 {
1129  Type *dataptr = data;
1130  Type max = *dataptr;
1131  ++dataptr;
1132  for (unsigned int i = 0; i < (dsize - 1); ++i) {
1133  if (*dataptr > max) {
1134  max = *dataptr;
1135  }
1136  ++dataptr;
1137  }
1138  return max;
1139 }
1140 
1147 template <class Type> vpArray2D<Type> vpArray2D<Type>::hadamard(const vpArray2D<Type> &m) const
1148 {
1149  if ((m.getRows() != rowNum) || (m.getCols() != colNum)) {
1150  throw(vpException(vpException::dimensionError, "Hadamard product: bad dimensions!"));
1151  }
1152 
1153  vpArray2D<Type> out;
1154  out.resize(rowNum, colNum, false);
1155 
1156  for (unsigned int i = 0; i < dsize; ++i) {
1157  out.data[i] = data[i] * m.data[i];
1158  }
1159 
1160  return out;
1161 }
1162 
1163 template <class Type> vpArray2D<Type> vpArray2D<Type>::t() const
1164 {
1165  vpArray2D<Type> At(colNum, rowNum);
1166  for (unsigned int i = 0; i < rowNum; ++i) {
1167  for (unsigned int j = 0; j < colNum; ++j) {
1168  At[j][i] = (*this)[i][j];
1169  }
1170  }
1171  return At;
1172 }
1173 
1174 template <class Type> vpArray2D<Type> vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode)
1175 {
1176  vpArray2D<Type> res;
1177  conv2(M, kernel, res, mode);
1178  return res;
1179 }
1180 
1181 template <class Type> void vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode)
1182 {
1183  if (((M.getRows() * M.getCols()) == 0) || ((kernel.getRows() * kernel.getCols()) == 0)) {
1184  return;
1185  }
1186 
1187  if (mode == "valid") {
1188  if ((kernel.getRows() > M.getRows()) || (kernel.getCols() > M.getCols())) {
1189  return;
1190  }
1191  }
1192 
1193  vpArray2D<Type> M_padded, res_same;
1194 
1195  if ((mode == "full") || (mode == "same")) {
1196  const unsigned int pad_x = kernel.getCols() - 1;
1197  const unsigned int pad_y = kernel.getRows() - 1;
1198  const unsigned int pad = 2;
1199  M_padded.resize(M.getRows() + (pad * pad_y), M.getCols() + (pad * pad_x), true, false);
1200  M_padded.insert(M, pad_y, pad_x);
1201 
1202  if (mode == "same") {
1203  res.resize(M.getRows(), M.getCols(), false, false);
1204  res_same.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1205  }
1206  else {
1207  res.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1208  }
1209  }
1210  else if (mode == "valid") {
1211  M_padded = M;
1212  res.resize((M.getRows() - kernel.getRows()) + 1, (M.getCols() - kernel.getCols()) + 1);
1213  }
1214  else {
1215  return;
1216  }
1217 
1218  if (mode == "same") {
1219  unsigned int res_same_rows = res_same.getRows();
1220  unsigned int res_same_cols = res_same.getCols();
1221  unsigned int kernel_rows = kernel.getRows();
1222  unsigned int kernel_cols = kernel.getCols();
1223  for (unsigned int i = 0; i < res_same_rows; ++i) {
1224  for (unsigned int j = 0; j < res_same_cols; ++j) {
1225  for (unsigned int k = 0; k < kernel_rows; ++k) {
1226  for (unsigned int l = 0; l < kernel_cols; ++l) {
1227  res_same[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1228  }
1229  }
1230  }
1231  }
1232 
1233  const unsigned int start_i = kernel.getRows() / 2;
1234  const unsigned int start_j = kernel.getCols() / 2;
1235  unsigned int m_rows = M.getRows();
1236  for (unsigned int i = 0; i < m_rows; ++i) {
1237  memcpy(res.data + (i * M.getCols()), res_same.data + ((i + start_i) * res_same.getCols()) + start_j,
1238  sizeof(Type) * M.getCols());
1239  }
1240  }
1241  else {
1242  unsigned int res_rows = res.getRows();
1243  unsigned int res_cols = res.getCols();
1244  unsigned int kernel_rows = kernel.getRows();
1245  unsigned int kernel_cols = kernel.getCols();
1246  for (unsigned int i = 0; i < res_rows; ++i) {
1247  for (unsigned int j = 0; j < res_cols; ++j) {
1248  for (unsigned int k = 0; k < kernel_rows; ++k) {
1249  for (unsigned int l = 0; l < kernel_cols; ++l) {
1250  res[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1251  }
1252  }
1253  }
1254  }
1255  }
1256 }
1257 
1258 template<class Type> vpArray2D<Type> vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c)
1259 {
1260  vpArray2D<Type> C;
1261 
1262  insert(A, B, C, r, c);
1263 
1264  return C;
1265 }
1266 
1267 template<class Type> void vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c)
1268 {
1269  if (((r + B.getRows()) <= A.getRows()) && ((c + B.getCols()) <= A.getCols())) {
1270  C.resize(A.getRows(), A.getCols(), false, false);
1271 
1272  unsigned int a_rows = A.getRows();
1273  unsigned int a_cols = A.getCols();
1274  for (unsigned int i = 0; i < a_rows; ++i) {
1275  for (unsigned int j = 0; j < a_cols; ++j) {
1276  if ((i >= r) && (i < (r + B.getRows())) && (j >= c) && (j < (c + B.getCols()))) {
1277  C[i][j] = B[i - r][j - c];
1278  }
1279  else {
1280  C[i][j] = A[i][j];
1281  }
1282  }
1283  }
1284  }
1285  else {
1286  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
1287  B.getRows(), B.getCols(), A.getCols(), A.getRows(), r, c);
1288  }
1289 }
1290 
1291 template <class Type> bool vpArray2D<Type>::operator==(const vpArray2D<Type> &A) const
1292 {
1293  if ((A.rowNum != rowNum) || (A.colNum != colNum)) {
1294  return false;
1295  }
1296 
1297  unsigned int a_size = A.size();
1298  for (unsigned int i = 0; i < a_size; ++i) {
1299  if (data[i] != A.data[i]) {
1300  return false;
1301  }
1302  }
1303 
1304  return true;
1305 }
1306 
1310 template <> inline bool vpArray2D<double>::operator==(const vpArray2D<double> &A) const
1311 {
1312  if ((A.rowNum != rowNum) || (A.colNum != colNum)) {
1313  return false;
1314  }
1315 
1316  unsigned int a_size = A.size();
1317  for (unsigned int i = 0; i < a_size; ++i) {
1318  if (fabs(data[i] - A.data[i]) > std::numeric_limits<double>::epsilon()) {
1319  return false;
1320  }
1321  }
1322 
1323  return true;
1324 }
1325 
1329 template <> inline bool vpArray2D<float>::operator==(const vpArray2D<float> &A) const
1330 {
1331  if ((A.rowNum != rowNum) || (A.colNum != colNum)) {
1332  return false;
1333  }
1334 
1335  unsigned int a_size = A.size();
1336  for (unsigned int i = 0; i < a_size; ++i) {
1337  if (fabsf(data[i] - A.data[i]) > std::numeric_limits<float>::epsilon()) {
1338  return false;
1339  }
1340  }
1341 
1342  return true;
1343 }
1344 
1348 template <class Type> bool vpArray2D<Type>::operator!=(const vpArray2D<Type> &A) const { return !(*this == A); }
1349 
1350 #ifdef VISP_HAVE_NLOHMANN_JSON
1351 template <class Type>
1352 inline void from_json(const nlohmann::json &j, vpArray2D<Type> &array)
1353 {
1354  if (j.is_array()) {
1355  const unsigned int nrows = static_cast<unsigned int>(j.size());
1356  if (nrows == 0) { // Initialize an empty array, Finished
1357  array.resize(0, 0);
1358  return;
1359  }
1360  unsigned int ncols = 0;
1361  bool first = true;
1362  for (const auto &item : j) { // Find number of columns, validate that all rows have same number of cols
1363  if (!item.is_array()) {
1364  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with a JSON object that is not an array of array");
1365  }
1366  if (first) {
1367  first = false;
1368  ncols = static_cast<unsigned int>(item.size());
1369  }
1370  else if (ncols != item.size()) {
1371  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with JSON row arrays that are not of the same size");
1372  }
1373  }
1374  array.resize(nrows, ncols);
1375  unsigned i = 0;
1376  for (const auto &item : j) {
1377  std::vector<Type> row = item;
1378  std::copy(row.begin(), row.end(), array.rowPtrs[i]);
1379  ++i;
1380  }
1381  }
1382  else if (j.is_object()) {
1383  const unsigned ncols = j.at("cols");
1384  const unsigned nrows = j.at("rows");
1385  array.resize(nrows, ncols);
1386  const nlohmann::json jData = j.at("data");
1387  if (!jData.is_array() || jData.size() != nrows * ncols) {
1388  std::stringstream ss;
1389  ss << "JSON \"data\" field must be an array of size " << nrows * ncols;
1390  throw vpException(vpException::badValue, ss.str());
1391  }
1392  unsigned i = 0;
1393  for (const auto &jValue : jData) {
1394  array.data[i] = jValue;
1395  ++i;
1396  }
1397  }
1398  else {
1399  throw vpException(vpException::badValue, "Trying to read a vpArray2D from something that is not an array or object");
1400  }
1401 }
1402 
1403 
1404 template <class Type>
1405 inline void to_json(nlohmann::json &j, const vpArray2D<Type> &array)
1406 {
1407  j = {
1408  {"cols", array.colNum},
1409  {"rows", array.rowNum},
1410  {"type", "vpArray2D"}
1411  };
1412 
1413  nlohmann::json::array_t data;
1414  data.reserve(array.size());
1415  for (unsigned i = 0; i < array.size(); ++i) {
1416  data.push_back(array.data[i]);
1417  }
1418  j["data"] = data;
1419 }
1420 #endif
1421 
1422 END_VISP_NAMESPACE
1423 
1424 #endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:145
vpArray2D< Type > & operator=(const nlohmann::json &j)=delete
unsigned int getCols() const
Definition: vpArray2D.h:337
vpArray2D< Type > insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1258
static void conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, vpArray2D< Type > &res, const std::string &mode)
Definition: vpArray2D.h:1181
vpArray2D< Type > & operator=(const std::initializer_list< Type > &list)
Definition: vpArray2D.h:568
Type * data
Address of the first element of the data array.
Definition: vpArray2D.h:148
static bool loadYAML(const std::string &filename, vpArray2D< Type > &A, char *header=nullptr)
Definition: vpArray2D.h:780
Type ** rowPtrs
Address of the first element of each rows.
Definition: vpArray2D.h:1102
void insert(const vpArray2D< Type > &A, unsigned int r, unsigned int c)
Definition: vpArray2D.h:494
vpArray2D< Type > & operator=(const vpArray2D< Type > &A)
Definition: vpArray2D.h:532
Type getMinValue() const
Definition: vpArray2D.h:1110
Type * operator[](unsigned int i)
Set element using A[i][j] = x.
Definition: vpArray2D.h:602
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:362
vpArray2D< Type > & operator=(vpArray2D< Type > &&other) noexcept
Definition: vpArray2D.h:542
static void insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, vpArray2D< Type > &C, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1267
static bool saveYAML(const std::string &filename, const vpArray2D< Type > &A, const char *header="")
Definition: vpArray2D.h:969
unsigned int rowNum
Number of rows in the array.
Definition: vpArray2D.h:1098
friend void to_json(nlohmann::json &j, const vpArray2D< T > &array)
static vpArray2D< Type > conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, const std::string &mode)
Definition: vpArray2D.h:1174
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition: vpArray2D.h:611
unsigned int dsize
Current array size (rowNum * colNum)
Definition: vpArray2D.h:1104
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:349
static bool load(const std::string &filename, vpArray2D< Type > &A, bool binary=false, char *header=nullptr)
Definition: vpArray2D.h:666
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1163
unsigned int getRows() const
Definition: vpArray2D.h:347
bool operator!=(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1348
vpArray2D< Type > & operator=(Type x)
Set all the elements of the array to x.
Definition: vpArray2D.h:523
vpArray2D< Type > & operator=(const std::initializer_list< std::initializer_list< Type > > &lists)
Definition: vpArray2D.h:578
vpArray2D< Type > hadamard(const vpArray2D< Type > &m) const
Definition: vpArray2D.h:1147
Type getMaxValue() const
Definition: vpArray2D.h:1127
static bool save(const std::string &filename, const vpArray2D< Type > &A, bool binary=false, const char *header="")
Definition: vpArray2D.h:871
friend void from_json(const nlohmann::json &j, vpArray2D< T > &array)
void reshape(unsigned int nrows, unsigned int ncols)
Definition: vpArray2D.h:453
Type * operator[](unsigned int i) const
Get element using x = A[i][j].
Definition: vpArray2D.h:604
unsigned int colNum
Number of columns in the array.
Definition: vpArray2D.h:1100
bool operator==(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1291
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ memoryAllocationError
Memory allocation error.
Definition: vpException.h:64