Visual Servoing Platform  version 3.6.1 under development (2024-04-19)
vpArray2D.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  * This class implements an 2D array as a template class.
32  */
33 #ifndef _vpArray2D_h_
34 #define _vpArray2D_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 
125 template <class Type> class vpArray2D
126 {
127 protected:
129  unsigned int rowNum;
131  unsigned int colNum;
133  Type **rowPtrs;
135  unsigned int dsize;
136 
137 public:
139  Type *data;
140 
141 public:
146  vpArray2D<Type>() : rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr) { }
147 
152  :
153 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
154  vpArray2D<Type>()
155 #else
156  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
157 #endif
158  {
159  resize(A.rowNum, A.colNum, false, false);
160  memcpy(data, A.data, static_cast<size_t>(rowNum) * static_cast<size_t>(colNum) * sizeof(Type));
161  }
162 
169  vpArray2D<Type>(unsigned int r, unsigned int c)
170  :
171 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
172  vpArray2D<Type>()
173 #else
174  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
175 #endif
176  {
177  resize(r, c);
178  }
179 
187  vpArray2D<Type>(unsigned int r, unsigned int c, Type val)
188  :
189 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
190  vpArray2D<Type>()
191 #else
192  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
193 #endif
194  {
195  resize(r, c, false, false);
196  *this = val;
197  }
198 
210  vpArray2D<Type>(const std::vector<Type> &vec, unsigned int r = 0, unsigned int c = 0)
211  :
212 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
213  vpArray2D<Type>()
214 #else
215  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
216 #endif
217  {
218  if (r > 0 && c > 0) {
219  if ((r * c) != vec.size()) {
221  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
222  }
223  resize(r, c, false, false);
224  }
225  else if ((c == 0) && (r == 0)) {
227  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Using rows = 0 and cols = 0 is ambiguous", r, c, vec.size()));
228  }
229  else if (c == 0) {
230  if (r != vec.size()) {
232  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
233  }
234  resize(static_cast<unsigned int>(vec.size()), 1, false, false);
235  }
236  else if (r == 0) {
237  if (c != vec.size()) {
239  "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
240  }
241  resize(1, static_cast<unsigned int>(vec.size()), false, false);
242  }
243 
244 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
245  std::copy(vec.begin(), vec.end(), data);
246 #else
247  memcpy(data, vec.data(), vec.size() * sizeof(Type));
248 #endif
249  }
250 
251 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
252  vpArray2D<Type>(vpArray2D<Type> &&A) noexcept
253  {
254  rowNum = A.rowNum;
255  colNum = A.colNum;
256  rowPtrs = A.rowPtrs;
257  dsize = A.dsize;
258  data = A.data;
259 
260  A.rowNum = 0;
261  A.colNum = 0;
262  A.rowPtrs = nullptr;
263  A.dsize = 0;
264  A.data = nullptr;
265  }
266 
267  explicit vpArray2D<Type>(const std::initializer_list<Type> &list) : vpArray2D<Type>()
268  {
269  resize(1, static_cast<unsigned int>(list.size()), false, false);
270  std::copy(list.begin(), list.end(), data);
271  }
272 
273  explicit vpArray2D<Type>(unsigned int nrows, unsigned int ncols, const std::initializer_list<Type> &list)
274  : rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
275  {
276  if (nrows * ncols != static_cast<unsigned int>(list.size())) {
277  std::ostringstream oss;
278  oss << "Cannot create a vpArray2D of size (" << nrows << ", " << ncols << ") with a list of size " << list.size();
279  throw vpException(vpException::dimensionError, oss.str());
280  }
281 
282  resize(nrows, ncols, false, false);
283  std::copy(list.begin(), list.end(), data);
284  }
285 
286  explicit vpArray2D<Type>(const std::initializer_list<std::initializer_list<Type> > &lists) : vpArray2D<Type>()
287  {
288  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
289  for (auto &l : lists) {
290  if (static_cast<unsigned int>(l.size()) > ncols) {
291  ncols = static_cast<unsigned int>(l.size());
292  }
293  }
294 
295  resize(nrows, ncols, false, false);
296  auto it = lists.begin();
297  for (unsigned int i = 0; i < rowNum; i++, ++it) {
298  std::copy(it->begin(), it->end(), rowPtrs[i]);
299  }
300  }
301 #endif
302 
306  virtual ~vpArray2D<Type>()
307  {
308  if (data != nullptr) {
309  free(data);
310  data = nullptr;
311  }
312 
313  if (rowPtrs != nullptr) {
314  free(rowPtrs);
315  rowPtrs = nullptr;
316  }
317  rowNum = colNum = dsize = 0;
318  }
319 
322 
327  inline unsigned int getCols() const { return colNum; }
328 
329  Type getMaxValue() const;
330 
331  Type getMinValue() const;
332 
337  inline unsigned int getRows() const { return rowNum; }
339  inline unsigned int size() const { return colNum * rowNum; }
340 
352  void resize(unsigned int nrows, unsigned int ncols, bool flagNullify = true, bool recopy_ = true)
353  {
354  if ((nrows == rowNum) && (ncols == colNum)) {
355  if (flagNullify && this->data != nullptr) {
356  memset(this->data, 0, this->dsize * sizeof(Type));
357  }
358  }
359  else {
360  bool recopy = !flagNullify && recopy_; // priority to flagNullify
361  const bool recopyNeeded = (ncols != this->colNum && this->colNum > 0 && ncols > 0 && (!flagNullify || recopy));
362  Type *copyTmp = nullptr;
363  unsigned int rowTmp = 0, colTmp = 0;
364 
365  // Recopy case per case is required if number of cols has changed;
366  // structure of Type array is not the same in this case.
367  if (recopyNeeded && this->data != nullptr) {
368  copyTmp = new Type[this->dsize];
369  memcpy(copyTmp, this->data, sizeof(Type) * this->dsize);
370  rowTmp = this->rowNum;
371  colTmp = this->colNum;
372  }
373 
374  // Reallocation of this->data array
375  this->dsize = nrows * ncols;
376  Type *tmp_data = reinterpret_cast<Type *>(realloc(this->data, this->dsize * sizeof(Type)));
377  if (tmp_data) {
378  this->data = tmp_data;
379  }
380  else {
381  this->data = nullptr;
382  }
383 
384  if ((nullptr == this->data) && (0 != this->dsize)) {
385  if (copyTmp != nullptr) {
386  delete[] copyTmp;
387  }
388  throw(vpException(vpException::memoryAllocationError, "Memory allocation error when allocating 2D array data"));
389  }
390 
391  Type **tmp_rowPtrs = reinterpret_cast<Type **>(realloc(this->rowPtrs, nrows * sizeof(Type *)));
392  if (tmp_rowPtrs) {
393  this->rowPtrs = tmp_rowPtrs;
394  }
395  else {
396  this->rowPtrs = nullptr;
397  }
398  if ((nullptr == this->rowPtrs) && (0 != this->dsize)) {
399  if (copyTmp != nullptr) {
400  delete[] copyTmp;
401  }
403  "Memory allocation error when allocating 2D array rowPtrs"));
404  }
405 
406  // Update rowPtrs
407  {
408  Type **t_ = rowPtrs;
409  for (unsigned int i = 0; i < dsize; i += ncols) {
410  *t_++ = this->data + i;
411  }
412  }
413 
414  this->rowNum = nrows;
415  this->colNum = ncols;
416 
417  // Recopy of this->data array values or nullify
418  if (flagNullify) {
419  memset(this->data, 0, (size_t)(this->dsize) * sizeof(Type));
420  }
421  else if (recopyNeeded && this->rowPtrs != nullptr) {
422  // Recopy...
423  unsigned int minRow = (this->rowNum < rowTmp) ? this->rowNum : rowTmp;
424  unsigned int minCol = (this->colNum < colTmp) ? this->colNum : colTmp;
425  for (unsigned int i = 0; i < this->rowNum; ++i) {
426  for (unsigned int j = 0; j < this->colNum; ++j) {
427  if ((minRow > i) && (minCol > j)) {
428  (*this)[i][j] = copyTmp[i * colTmp + j];
429  }
430  else {
431  (*this)[i][j] = 0;
432  }
433  }
434  }
435  }
436 
437  if (copyTmp != nullptr) {
438  delete[] copyTmp;
439  }
440  }
441  }
442 
443  void reshape(unsigned int nrows, unsigned int ncols)
444  {
445  if (dsize == 0) {
446  resize(nrows, ncols);
447  return;
448  }
449 
450  if (nrows * ncols != dsize) {
451  std::ostringstream oss;
452  oss << "Cannot reshape array of total size " << dsize << " into shape (" << nrows << ", " << ncols << ")";
453  throw vpException(vpException::dimensionError, oss.str());
454  }
455 
456  rowNum = nrows;
457  colNum = ncols;
458  if (rowPtrs) {
459  Type **tmp = reinterpret_cast<Type **>(realloc(rowPtrs, nrows * sizeof(Type *)));
460  if (tmp) {
461  this->rowPtrs = tmp;
462  }
463  }
464  if (rowPtrs) {
465  // Update rowPtrs
466  Type **t_ = rowPtrs;
467  for (unsigned int i = 0; i < dsize; i += ncols) {
468  *t_++ = data + i;
469  }
470  }
471  }
472 
473 
484  void insert(const vpArray2D<Type> &A, unsigned int r, unsigned int c)
485  {
486  if ((r + A.getRows()) <= rowNum && (c + A.getCols()) <= colNum) {
487  if (A.colNum == colNum && data != nullptr && A.data != nullptr && A.data != data) {
488  memcpy(data + r * colNum, A.data, sizeof(Type) * A.size());
489  }
490  else if (data != nullptr && A.data != nullptr && A.data != data) {
491  for (unsigned int i = r; i < (r + A.getRows()); ++i) {
492  memcpy(data + i * colNum + c, A.data + (i - r) * A.colNum, sizeof(Type) * A.colNum);
493  }
494  }
495  }
496  else {
497  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
498  A.getRows(), A.getCols(), rowNum, colNum, r, c);
499  }
500  }
501 
505  bool operator==(const vpArray2D<Type> &A) const;
509  bool operator!=(const vpArray2D<Type> &A) const;
510 
513  {
514  std::fill(data, data + dsize, x);
515  return *this;
516  }
517 
522  {
523  resize(A.rowNum, A.colNum, false, false);
524  if (data != nullptr && A.data != nullptr && data != A.data) {
525  memcpy(data, A.data, (size_t)rowNum * (size_t)colNum * sizeof(Type));
526  }
527  return *this;
528  }
529 
530 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
531  vpArray2D<Type> &operator=(vpArray2D<Type> &&other) noexcept
532  {
533  if (this != &other) {
534  if (data) {
535  free(data);
536  }
537  if (rowPtrs) {
538  free(rowPtrs);
539  }
540 
541  rowNum = other.rowNum;
542  colNum = other.colNum;
543  rowPtrs = other.rowPtrs;
544  dsize = other.dsize;
545  data = other.data;
546 
547  other.rowNum = 0;
548  other.colNum = 0;
549  other.rowPtrs = nullptr;
550  other.dsize = 0;
551  other.data = nullptr;
552  }
553 
554  return *this;
555  }
556 
557  vpArray2D<Type> &operator=(const std::initializer_list<Type> &list)
558  {
559  if (dsize != static_cast<unsigned int>(list.size())) {
560  resize(1, static_cast<unsigned int>(list.size()), false, false);
561  }
562  std::copy(list.begin(), list.end(), data);
563 
564  return *this;
565  }
566 
567  vpArray2D<Type> &operator=(const std::initializer_list<std::initializer_list<Type> > &lists)
568  {
569  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
570  for (auto &l : lists) {
571  if (static_cast<unsigned int>(l.size()) > ncols) {
572  ncols = static_cast<unsigned int>(l.size());
573  }
574  }
575 
576  resize(nrows, ncols, false, false);
577  auto it = lists.begin();
578  for (unsigned int i = 0; i < rowNum; i++, ++it) {
579  std::copy(it->begin(), it->end(), rowPtrs[i]);
580  }
581 
582  return *this;
583  }
584 
585 #ifdef VISP_HAVE_NLOHMANN_JSON
586  vpArray2D<Type> &operator=(const nlohmann::json &j) = delete;
587 #endif
588 #endif
589 
591  inline Type *operator[](unsigned int i) { return rowPtrs[i]; }
593  inline Type *operator[](unsigned int i) const { return rowPtrs[i]; }
594 
600  friend std::ostream &operator<<(std::ostream &s, const vpArray2D<Type> &A)
601  {
602  if (A.data == nullptr || A.size() == 0) {
603  return s;
604  }
605  std::ios_base::fmtflags original_flags = s.flags();
606 
607  s.precision(10);
608  for (unsigned int i = 0; i < A.getRows(); ++i) {
609  for (unsigned int j = 0; j < A.getCols() - 1; ++j) {
610  s << A[i][j] << " ";
611  }
612  // We don't add " " after the last row element
613  s << A[i][A.getCols() - 1];
614  // We don't add a \n char on the end of the last array line
615  if (i < A.getRows() - 1) {
616  s << std::endl;
617  }
618  }
619 
620  s.flags(original_flags); // restore s to standard state
621 
622  return s;
623  }
624 
626 
634 
635  //---------------------------------
636  // Inherited array I/O Static Public Member Functions
637  //---------------------------------
653  static bool load(const std::string &filename, vpArray2D<Type> &A, bool binary = false, char *header = nullptr)
654  {
655  std::fstream file;
656 
657  if (!binary) {
658  file.open(filename.c_str(), std::fstream::in);
659  }
660  else {
661  file.open(filename.c_str(), std::fstream::in | std::fstream::binary);
662  }
663 
664  if (!file) {
665  file.close();
666  return false;
667  }
668 
669  if (!binary) {
670  std::string h;
671  bool headerIsDecoded = false;
672  do {
673  std::streampos pos = file.tellg();
674  char line[256];
675  file.getline(line, 256);
676  std::string prefix("# ");
677  std::string line_(line);
678  if (line_.compare(0, 2, prefix.c_str()) == 0) {
679  // Line is a comment
680  // If we are not on the first line, we should add "\n" to the end of
681  // the previous line
682  if (pos) {
683  h += "\n";
684  }
685  h += line_.substr(2); // Remove "# "
686  }
687  else {
688  // rewind before the line
689  file.seekg(pos, file.beg);
690  headerIsDecoded = true;
691  }
692  } while (!headerIsDecoded);
693 
694  if (header != nullptr) {
695 #if defined(__MINGW32__) || \
696  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
697  snprintf(header, h.size() + 1, "%s", h.c_str());
698 #else
699  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
700 #endif
701  }
702 
703  unsigned int rows, cols;
704  file >> rows;
705  file >> cols;
706 
707  if (rows >= (std::numeric_limits<unsigned int>::max)() || cols >= (std::numeric_limits<unsigned int>::max)()) {
708  throw vpException(vpException::badValue, "Array exceed the max size.");
709  }
710 
711  A.resize(rows, cols);
712 
713  Type value;
714  for (unsigned int i = 0; i < rows; ++i) {
715  for (unsigned int j = 0; j < cols; ++j) {
716  file >> value;
717  A[i][j] = value;
718  }
719  }
720  }
721  else {
722  char c = '0';
723  std::string h;
724  // Decode header until '\0' char that ends the header string
725  while (c != '\0') {
726  file.read(&c, 1);
727  h += c;
728  }
729  if (header != nullptr) {
730 #if defined(__MINGW32__) || \
731  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
732  snprintf(header, h.size() + 1, "%s", h.c_str());
733 #else
734  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
735 #endif
736  }
737 
738  unsigned int rows, cols;
739  file.read((char *)&rows, sizeof(unsigned int));
740  file.read((char *)&cols, sizeof(unsigned int));
741  A.resize(rows, cols);
742 
743  Type value;
744  for (unsigned int i = 0; i < rows; ++i) {
745  for (unsigned int j = 0; j < cols; ++j) {
746  file.read((char *)&value, sizeof(Type));
747  A[i][j] = value;
748  }
749  }
750  }
751 
752  file.close();
753  return true;
754  }
767  static bool loadYAML(const std::string &filename, vpArray2D<Type> &A, char *header = nullptr)
768  {
769  std::fstream file;
770 
771  file.open(filename.c_str(), std::fstream::in);
772 
773  if (!file) {
774  file.close();
775  return false;
776  }
777 
778  unsigned int rows = 0, cols = 0;
779  std::string h;
780  std::string line, subs;
781  bool inheader = true;
782  unsigned int i = 0, j;
783  unsigned int lineStart = 0;
784 
785  while (getline(file, line)) {
786  if (inheader) {
787  if (rows == 0 && line.compare(0, 5, "rows:") == 0) {
788  std::stringstream ss(line);
789  ss >> subs;
790  ss >> rows;
791  }
792  else if (cols == 0 && line.compare(0, 5, "cols:") == 0) {
793  std::stringstream ss(line);
794  ss >> subs;
795  ss >> cols;
796  }
797  else if (line.compare(0, 5, "data:") == 0) {
798  inheader = false;
799  }
800  else {
801  h += line + "\n";
802  }
803  }
804  else {
805  // if i == 0, we just got out of the header: initialize matrix
806  // dimensions
807  if (i == 0) {
808  if (rows == 0 || cols == 0) {
809  file.close();
810  return false;
811  }
812  A.resize(rows, cols);
813  // get indentation level which is common to all lines
814  lineStart = (unsigned int)line.find("[") + 1;
815  }
816  std::stringstream ss(line.substr(lineStart, line.find("]") - lineStart));
817  j = 0;
818  while (getline(ss, subs, ',')) {
819  A[i][j++] = atof(subs.c_str());
820  }
821  i++;
822  }
823  }
824 
825  if (header != nullptr) {
826  std::string h_ = h.substr(0, h.size() - 1); // Remove last '\n' char
827 #if defined(__MINGW32__) || \
828  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
829  snprintf(header, h_.size() + 1, "%s", h_.c_str());
830 #else
831  _snprintf_s(header, h_.size() + 1, _TRUNCATE, "%s", h_.c_str());
832 #endif
833  }
834 
835  file.close();
836  return true;
837  }
838 
855  static bool save(const std::string &filename, const vpArray2D<Type> &A, bool binary = false, const char *header = "")
856  {
857  std::fstream file;
858 
859  if (!binary) {
860  file.open(filename.c_str(), std::fstream::out);
861  }
862  else {
863  file.open(filename.c_str(), std::fstream::out | std::fstream::binary);
864  }
865 
866  if (!file) {
867  file.close();
868  return false;
869  }
870 
871  if (!binary) {
872  unsigned int i = 0;
873  file << "# ";
874  while (header[i] != '\0') {
875  file << header[i];
876  if (header[i] == '\n') {
877  file << "# ";
878  }
879  i++;
880  }
881  file << std::endl;
882  file << A.getRows() << "\t" << A.getCols() << std::endl;
883  file << A << std::endl;
884  }
885  else {
886  int headerSize = 0;
887  while (header[headerSize] != '\0') {
888  headerSize++;
889  }
890  file.write(header, (size_t)headerSize + (size_t)1);
891  unsigned int matrixSize;
892  matrixSize = A.getRows();
893  file.write((char *)&matrixSize, sizeof(unsigned int));
894  matrixSize = A.getCols();
895  file.write((char *)&matrixSize, sizeof(unsigned int));
896  Type value;
897  for (unsigned int i = 0; i < A.getRows(); ++i) {
898  for (unsigned int j = 0; j < A.getCols(); ++j) {
899  value = A[i][j];
900  file.write((char *)&value, sizeof(Type));
901  }
902  }
903  }
904 
905  file.close();
906  return true;
907  }
950  static bool saveYAML(const std::string &filename, const vpArray2D<Type> &A, const char *header = "")
951  {
952  std::fstream file;
953 
954  file.open(filename.c_str(), std::fstream::out);
955 
956  if (!file) {
957  file.close();
958  return false;
959  }
960 
961  unsigned int i = 0;
962  bool inIndent = false;
963  std::string indent = "";
964  bool checkIndent = true;
965  while (header[i] != '\0') {
966  file << header[i];
967  if (checkIndent) {
968  if (inIndent) {
969  if (header[i] == ' ') {
970  indent += " ";
971  }
972  else if (indent.length() > 0) {
973  checkIndent = false;
974  }
975  }
976  if (header[i] == '\n' || (inIndent && header[i] == ' ')) {
977  inIndent = true;
978  }
979  else {
980  inIndent = false;
981  }
982  }
983  i++;
984  }
985 
986  if (i != 0) {
987  file << std::endl;
988  }
989  file << "rows: " << A.getRows() << std::endl;
990  file << "cols: " << A.getCols() << std::endl;
991 
992  if (indent.length() == 0) {
993  indent = " ";
994  }
995 
996  file << "data: " << std::endl;
997  unsigned int j;
998  for (i = 0; i < A.getRows(); ++i) {
999  file << indent << "- [";
1000  for (j = 0; j < A.getCols() - 1; ++j) {
1001  file << A[i][j] << ", ";
1002  }
1003  file << A[i][j] << "]" << std::endl;
1004  }
1005 
1006  file.close();
1007  return true;
1008  }
1009 #ifdef VISP_HAVE_NLOHMANN_JSON
1010  //template<typename Type>
1011  template<class T>
1012  friend void from_json(const nlohmann::json &j, vpArray2D<T> &array);
1013  //template<typename Type>
1014  template<class T>
1015  friend void to_json(nlohmann::json &j, const vpArray2D<T> &array);
1016 #endif
1017 
1029  static vpArray2D<Type> conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode);
1030 
1043  static void conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode);
1044 
1057  vpArray2D<Type> insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c);
1058 
1072  static void insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c);
1074 };
1075 
1079 template <class Type> Type vpArray2D<Type>::getMinValue() const
1080 {
1081  Type *dataptr = data;
1082  Type min = *dataptr;
1083  dataptr++;
1084  for (unsigned int i = 0; i < dsize - 1; ++i) {
1085  if (*dataptr < min) {
1086  min = *dataptr;
1087  }
1088  dataptr++;
1089  }
1090  return min;
1091 }
1092 
1096 template <class Type> Type vpArray2D<Type>::getMaxValue() const
1097 {
1098  Type *dataptr = data;
1099  Type max = *dataptr;
1100  dataptr++;
1101  for (unsigned int i = 0; i < dsize - 1; ++i) {
1102  if (*dataptr > max) {
1103  max = *dataptr;
1104  }
1105  dataptr++;
1106  }
1107  return max;
1108 }
1109 
1116 template <class Type> vpArray2D<Type> vpArray2D<Type>::hadamard(const vpArray2D<Type> &m) const
1117 {
1118  if (m.getRows() != rowNum || m.getCols() != colNum) {
1119  throw(vpException(vpException::dimensionError, "Hadamard product: bad dimensions!"));
1120  }
1121 
1122  vpArray2D<Type> out;
1123  out.resize(rowNum, colNum, false);
1124 
1125  for (unsigned int i = 0; i < dsize; ++i) {
1126  out.data[i] = data[i] * m.data[i];
1127  }
1128 
1129  return out;
1130 }
1131 
1132 template <class Type> vpArray2D<Type> vpArray2D<Type>::t() const
1133 {
1134  vpArray2D<Type> At(colNum, rowNum);
1135  for (unsigned int i = 0; i < rowNum; ++i) {
1136  for (unsigned int j = 0; j < colNum; ++j) {
1137  At[j][i] = (*this)[i][j];
1138  }
1139  }
1140  return At;
1141 }
1142 
1143 template <class Type> vpArray2D<Type> vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode)
1144 {
1145  vpArray2D<Type> res;
1146  conv2(M, kernel, res, mode);
1147  return res;
1148 }
1149 
1150 template <class Type> void vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode)
1151 {
1152  if (M.getRows() * M.getCols() == 0 || kernel.getRows() * kernel.getCols() == 0)
1153  return;
1154 
1155  if (mode == "valid") {
1156  if (kernel.getRows() > M.getRows() || kernel.getCols() > M.getCols())
1157  return;
1158  }
1159 
1160  vpArray2D<Type> M_padded, res_same;
1161 
1162  if (mode == "full" || mode == "same") {
1163  const unsigned int pad_x = kernel.getCols() - 1;
1164  const unsigned int pad_y = kernel.getRows() - 1;
1165  M_padded.resize(M.getRows() + 2 * pad_y, M.getCols() + 2 * pad_x, true, false);
1166  M_padded.insert(M, pad_y, pad_x);
1167 
1168  if (mode == "same") {
1169  res.resize(M.getRows(), M.getCols(), false, false);
1170  res_same.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1171  }
1172  else {
1173  res.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1174  }
1175  }
1176  else if (mode == "valid") {
1177  M_padded = M;
1178  res.resize(M.getRows() - kernel.getRows() + 1, M.getCols() - kernel.getCols() + 1);
1179  }
1180  else {
1181  return;
1182  }
1183 
1184  if (mode == "same") {
1185  for (unsigned int i = 0; i < res_same.getRows(); ++i) {
1186  for (unsigned int j = 0; j < res_same.getCols(); ++j) {
1187  for (unsigned int k = 0; k < kernel.getRows(); ++k) {
1188  for (unsigned int l = 0; l < kernel.getCols(); ++l) {
1189  res_same[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1190  }
1191  }
1192  }
1193  }
1194 
1195  const unsigned int start_i = kernel.getRows() / 2;
1196  const unsigned int start_j = kernel.getCols() / 2;
1197  for (unsigned int i = 0; i < M.getRows(); ++i) {
1198  memcpy(res.data + i * M.getCols(), res_same.data + (i + start_i) * res_same.getCols() + start_j,
1199  sizeof(Type) * M.getCols());
1200  }
1201  }
1202  else {
1203  for (unsigned int i = 0; i < res.getRows(); ++i) {
1204  for (unsigned int j = 0; j < res.getCols(); ++j) {
1205  for (unsigned int k = 0; k < kernel.getRows(); ++k) {
1206  for (unsigned int l = 0; l < kernel.getCols(); ++l) {
1207  res[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1208  }
1209  }
1210  }
1211  }
1212  }
1213 }
1214 
1215 template<class Type> vpArray2D<Type> vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c)
1216 {
1217  vpArray2D<Type> C;
1218 
1219  insert(A, B, C, r, c);
1220 
1221  return C;
1222 }
1223 
1224 template<class Type> void vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c)
1225 {
1226  if (((r + B.getRows()) <= A.getRows()) && ((c + B.getCols()) <= A.getCols())) {
1227  C.resize(A.getRows(), A.getCols(), false, false);
1228 
1229  for (unsigned int i = 0; i < A.getRows(); ++i) {
1230  for (unsigned int j = 0; j < A.getCols(); ++j) {
1231  if (i >= r && i < (r + B.getRows()) && j >= c && j < (c + B.getCols())) {
1232  C[i][j] = B[i - r][j - c];
1233  }
1234  else {
1235  C[i][j] = A[i][j];
1236  }
1237  }
1238  }
1239  }
1240  else {
1241  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
1242  B.getRows(), B.getCols(), A.getCols(), A.getRows(), r, c);
1243  }
1244 }
1245 
1246 template <class Type> bool vpArray2D<Type>::operator==(const vpArray2D<Type> &A) const
1247 {
1248  if (A.rowNum != rowNum || A.colNum != colNum) {
1249  return false;
1250  }
1251 
1252  for (unsigned int i = 0; i < A.size(); ++i) {
1253  if (data[i] != A.data[i]) {
1254  return false;
1255  }
1256  }
1257 
1258  return true;
1259 }
1260 
1264 template <> inline bool vpArray2D<double>::operator==(const vpArray2D<double> &A) const
1265 {
1266  if (A.rowNum != rowNum || A.colNum != colNum) {
1267  return false;
1268  }
1269 
1270  for (unsigned int i = 0; i < A.size(); ++i) {
1271  if (fabs(data[i] - A.data[i]) > std::numeric_limits<double>::epsilon()) {
1272  return false;
1273  }
1274  }
1275 
1276  return true;
1277 }
1278 
1282 template <> inline bool vpArray2D<float>::operator==(const vpArray2D<float> &A) const
1283 {
1284  if (A.rowNum != rowNum || A.colNum != colNum) {
1285  return false;
1286  }
1287 
1288  for (unsigned int i = 0; i < A.size(); ++i) {
1289  if (fabsf(data[i] - A.data[i]) > std::numeric_limits<float>::epsilon()) {
1290  return false;
1291  }
1292  }
1293 
1294  return true;
1295 }
1296 
1300 template <class Type> bool vpArray2D<Type>::operator!=(const vpArray2D<Type> &A) const { return !(*this == A); }
1301 
1302 #ifdef VISP_HAVE_NLOHMANN_JSON
1303 
1304 
1305 template <class Type>
1306 inline void from_json(const nlohmann::json &j, vpArray2D<Type> &array)
1307 {
1308  if (j.is_array()) {
1309  const unsigned int nrows = static_cast<unsigned int>(j.size());
1310  if (nrows == 0) { // Initialize an empty array, Finished
1311  array.resize(0, 0);
1312  return;
1313  }
1314  unsigned int ncols = 0;
1315  bool first = true;
1316  for (const auto &item : j) { // Find number of columns, validate that all rows have same number of cols
1317  if (!item.is_array()) {
1318  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with a JSON object that is not an array of array");
1319  }
1320  if (first) {
1321  first = false;
1322  ncols = static_cast<unsigned int>(item.size());
1323  }
1324  else if (ncols != item.size()) {
1325  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with JSON row arrays that are not of the same size");
1326  }
1327  }
1328  array.resize(nrows, ncols);
1329  unsigned i = 0;
1330  for (const auto &item : j) {
1331  std::vector<Type> row = item;
1332  std::copy(row.begin(), row.end(), array.rowPtrs[i]);
1333  ++i;
1334  }
1335  }
1336  else if (j.is_object()) {
1337  const unsigned ncols = j.at("cols");
1338  const unsigned nrows = j.at("rows");
1339  array.resize(nrows, ncols);
1340  const nlohmann::json jData = j.at("data");
1341  if (!jData.is_array() || jData.size() != nrows * ncols) {
1342  std::stringstream ss;
1343  ss << "JSON \"data\" field must be an array of size " << nrows * ncols;
1344  throw vpException(vpException::badValue, ss.str());
1345  }
1346  unsigned i = 0;
1347  for (const auto &jValue : jData) {
1348  array.data[i] = jValue;
1349  ++i;
1350  }
1351  }
1352  else {
1353  throw vpException(vpException::badValue, "Trying to read a vpArray2D from something that is not an array or object");
1354  }
1355 }
1356 
1357 
1358 template <class Type>
1359 inline void to_json(nlohmann::json &j, const vpArray2D<Type> &array)
1360 {
1361  j = {
1362  {"cols", array.colNum},
1363  {"rows", array.rowNum},
1364  {"type", "vpArray2D"}
1365  };
1366 
1367  nlohmann::json::array_t data;
1368  data.reserve(array.size());
1369  for (unsigned i = 0; i < array.size(); ++i) {
1370  data.push_back(array.data[i]);
1371  }
1372  j["data"] = data;
1373 }
1374 #endif
1375 #endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:126
unsigned int getCols() const
Definition: vpArray2D.h:327
vpArray2D< Type > insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1215
static void conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, vpArray2D< Type > &res, const std::string &mode)
Definition: vpArray2D.h:1150
Type * data
Address of the first element of the data array.
Definition: vpArray2D.h:139
static bool loadYAML(const std::string &filename, vpArray2D< Type > &A, char *header=nullptr)
Definition: vpArray2D.h:767
Type ** rowPtrs
Address of the first element of each rows.
Definition: vpArray2D.h:133
void insert(const vpArray2D< Type > &A, unsigned int r, unsigned int c)
Definition: vpArray2D.h:484
vpArray2D< Type > & operator=(const vpArray2D< Type > &A)
Definition: vpArray2D.h:521
Type getMinValue() const
Definition: vpArray2D.h:1079
Type * operator[](unsigned int i)
Set element using A[i][j] = x.
Definition: vpArray2D.h:591
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:352
static void insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, vpArray2D< Type > &C, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1224
static bool saveYAML(const std::string &filename, const vpArray2D< Type > &A, const char *header="")
Definition: vpArray2D.h:950
unsigned int rowNum
Number of rows in the array.
Definition: vpArray2D.h:129
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:1143
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition: vpArray2D.h:600
unsigned int dsize
Current array size (rowNum * colNum)
Definition: vpArray2D.h:135
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:339
static bool load(const std::string &filename, vpArray2D< Type > &A, bool binary=false, char *header=nullptr)
Definition: vpArray2D.h:653
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1132
unsigned int getRows() const
Definition: vpArray2D.h:337
bool operator!=(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1300
vpArray2D< Type > & operator=(Type x)
Set all the elements of the array to x.
Definition: vpArray2D.h:512
vpArray2D< Type > hadamard(const vpArray2D< Type > &m) const
Definition: vpArray2D.h:1116
Type getMaxValue() const
Definition: vpArray2D.h:1096
static bool save(const std::string &filename, const vpArray2D< Type > &A, bool binary=false, const char *header="")
Definition: vpArray2D.h:855
friend void from_json(const nlohmann::json &j, vpArray2D< T > &array)
void reshape(unsigned int nrows, unsigned int ncols)
Definition: vpArray2D.h:443
Type * operator[](unsigned int i) const
Get element using x = A[i][j].
Definition: vpArray2D.h:593
unsigned int colNum
Number of columns in the array.
Definition: vpArray2D.h:131
bool operator==(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1246
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:85
@ dimensionError
Bad dimension.
Definition: vpException.h:83
@ memoryAllocationError
Memory allocation error.
Definition: vpException.h:76