Visual Servoing Platform  version 3.6.1 under development (2024-03-18)
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 
45 #include <visp3/core/vpConfig.h>
46 #include <visp3/core/vpException.h>
47 
48 #ifdef VISP_HAVE_NLOHMANN_JSON
49 #include <nlohmann/json.hpp>
50 #endif
51 
124 template <class Type> class vpArray2D
125 {
126 protected:
128  unsigned int rowNum;
130  unsigned int colNum;
132  Type **rowPtrs;
134  unsigned int dsize;
135 
136 public:
138  Type *data;
139 
140 public:
145  vpArray2D<Type>() : rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr) { }
146 
151  :
152 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
153  vpArray2D<Type>()
154 #else
155  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
156 #endif
157  {
158  resize(A.rowNum, A.colNum, false, false);
159  memcpy(data, A.data, static_cast<size_t>(rowNum) * static_cast<size_t>(colNum) * sizeof(Type));
160  }
161 
168  vpArray2D<Type>(unsigned int r, unsigned int c)
169  :
170 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
171  vpArray2D<Type>()
172 #else
173  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
174 #endif
175  {
176  resize(r, c);
177  }
178 
186  vpArray2D<Type>(unsigned int r, unsigned int c, Type val)
187  :
188 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
189  vpArray2D<Type>()
190 #else
191  rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
192 #endif
193  {
194  resize(r, c, false, false);
195  *this = val;
196  }
197 
198 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
199  vpArray2D<Type>(vpArray2D<Type> &&A) noexcept
200  {
201  rowNum = A.rowNum;
202  colNum = A.colNum;
203  rowPtrs = A.rowPtrs;
204  dsize = A.dsize;
205  data = A.data;
206 
207  A.rowNum = 0;
208  A.colNum = 0;
209  A.rowPtrs = nullptr;
210  A.dsize = 0;
211  A.data = nullptr;
212  }
213 
214  explicit vpArray2D<Type>(const std::initializer_list<Type> &list) : vpArray2D<Type>()
215  {
216  resize(1, static_cast<unsigned int>(list.size()), false, false);
217  std::copy(list.begin(), list.end(), data);
218  }
219 
220  explicit vpArray2D<Type>(unsigned int nrows, unsigned int ncols, const std::initializer_list<Type> &list)
221  : rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), data(nullptr)
222  {
223  if (nrows * ncols != static_cast<unsigned int>(list.size())) {
224  std::ostringstream oss;
225  oss << "Cannot create a vpArray2D of size (" << nrows << ", " << ncols << ") with a list of size " << list.size();
226  throw vpException(vpException::dimensionError, oss.str());
227  }
228 
229  resize(nrows, ncols, false, false);
230  std::copy(list.begin(), list.end(), data);
231  }
232 
233  explicit vpArray2D<Type>(const std::initializer_list<std::initializer_list<Type> > &lists) : vpArray2D<Type>()
234  {
235  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
236  for (auto &l : lists) {
237  if (static_cast<unsigned int>(l.size()) > ncols) {
238  ncols = static_cast<unsigned int>(l.size());
239  }
240  }
241 
242  resize(nrows, ncols, false, false);
243  auto it = lists.begin();
244  for (unsigned int i = 0; i < rowNum; i++, ++it) {
245  std::copy(it->begin(), it->end(), rowPtrs[i]);
246  }
247  }
248 #endif
249 
253  virtual ~vpArray2D<Type>()
254  {
255  if (data != nullptr) {
256  free(data);
257  data = nullptr;
258  }
259 
260  if (rowPtrs != nullptr) {
261  free(rowPtrs);
262  rowPtrs = nullptr;
263  }
264  rowNum = colNum = dsize = 0;
265  }
266 
269 
274  inline unsigned int getCols() const { return colNum; }
275 
276  Type getMaxValue() const;
277 
278  Type getMinValue() const;
279 
284  inline unsigned int getRows() const { return rowNum; }
286  inline unsigned int size() const { return colNum * rowNum; }
287 
299  void resize(unsigned int nrows, unsigned int ncols, bool flagNullify = true, bool recopy_ = true)
300  {
301  if ((nrows == rowNum) && (ncols == colNum)) {
302  if (flagNullify && this->data != nullptr) {
303  memset(this->data, 0, this->dsize * sizeof(Type));
304  }
305  }
306  else {
307  bool recopy = !flagNullify && recopy_; // priority to flagNullify
308  const bool recopyNeeded = (ncols != this->colNum && this->colNum > 0 && ncols > 0 && (!flagNullify || recopy));
309  Type *copyTmp = nullptr;
310  unsigned int rowTmp = 0, colTmp = 0;
311 
312  // Recopy case per case is required if number of cols has changed;
313  // structure of Type array is not the same in this case.
314  if (recopyNeeded && this->data != nullptr) {
315  copyTmp = new Type[this->dsize];
316  memcpy(copyTmp, this->data, sizeof(Type) * this->dsize);
317  rowTmp = this->rowNum;
318  colTmp = this->colNum;
319  }
320 
321  // Reallocation of this->data array
322  this->dsize = nrows * ncols;
323  Type *tmp_data = reinterpret_cast<Type *>(realloc(this->data, this->dsize * sizeof(Type)));
324  if (tmp_data) {
325  this->data = tmp_data;
326  }
327  else {
328  this->data = nullptr;
329  }
330 
331  if ((nullptr == this->data) && (0 != this->dsize)) {
332  if (copyTmp != nullptr) {
333  delete[] copyTmp;
334  }
335  throw(vpException(vpException::memoryAllocationError, "Memory allocation error when allocating 2D array data"));
336  }
337 
338  Type **tmp_rowPtrs = reinterpret_cast<Type **>(realloc(this->rowPtrs, nrows * sizeof(Type *)));
339  if (tmp_rowPtrs) {
340  this->rowPtrs = tmp_rowPtrs;
341  }
342  else {
343  this->rowPtrs = nullptr;
344  }
345  if ((nullptr == this->rowPtrs) && (0 != this->dsize)) {
346  if (copyTmp != nullptr) {
347  delete[] copyTmp;
348  }
350  "Memory allocation error when allocating 2D array rowPtrs"));
351  }
352 
353  // Update rowPtrs
354  {
355  Type **t_ = rowPtrs;
356  for (unsigned int i = 0; i < dsize; i += ncols) {
357  *t_++ = this->data + i;
358  }
359  }
360 
361  this->rowNum = nrows;
362  this->colNum = ncols;
363 
364  // Recopy of this->data array values or nullify
365  if (flagNullify) {
366  memset(this->data, 0, (size_t)(this->dsize) * sizeof(Type));
367  }
368  else if (recopyNeeded && this->rowPtrs != nullptr) {
369  // Recopy...
370  unsigned int minRow = (this->rowNum < rowTmp) ? this->rowNum : rowTmp;
371  unsigned int minCol = (this->colNum < colTmp) ? this->colNum : colTmp;
372  for (unsigned int i = 0; i < this->rowNum; ++i) {
373  for (unsigned int j = 0; j < this->colNum; ++j) {
374  if ((minRow > i) && (minCol > j)) {
375  (*this)[i][j] = copyTmp[i * colTmp + j];
376  }
377  else {
378  (*this)[i][j] = 0;
379  }
380  }
381  }
382  }
383 
384  if (copyTmp != nullptr) {
385  delete[] copyTmp;
386  }
387  }
388  }
389 
390  void reshape(unsigned int nrows, unsigned int ncols)
391  {
392  if (dsize == 0) {
393  resize(nrows, ncols);
394  return;
395  }
396 
397  if (nrows * ncols != dsize) {
398  std::ostringstream oss;
399  oss << "Cannot reshape array of total size " << dsize << " into shape (" << nrows << ", " << ncols << ")";
400  throw vpException(vpException::dimensionError, oss.str());
401  }
402 
403  rowNum = nrows;
404  colNum = ncols;
405  if (rowPtrs) {
406  Type **tmp = reinterpret_cast<Type **>(realloc(rowPtrs, nrows * sizeof(Type *)));
407  if (tmp) {
408  this->rowPtrs = tmp;
409  }
410  }
411  if (rowPtrs) {
412  // Update rowPtrs
413  Type **t_ = rowPtrs;
414  for (unsigned int i = 0; i < dsize; i += ncols) {
415  *t_++ = data + i;
416  }
417  }
418  }
419 
420 
431  void insert(const vpArray2D<Type> &A, unsigned int r, unsigned int c)
432  {
433  if ((r + A.getRows()) <= rowNum && (c + A.getCols()) <= colNum) {
434  if (A.colNum == colNum && data != nullptr && A.data != nullptr && A.data != data) {
435  memcpy(data + r * colNum, A.data, sizeof(Type) * A.size());
436  }
437  else if (data != nullptr && A.data != nullptr && A.data != data) {
438  for (unsigned int i = r; i < (r + A.getRows()); i++) {
439  memcpy(data + i * colNum + c, A.data + (i - r) * A.colNum, sizeof(Type) * A.colNum);
440  }
441  }
442  }
443  else {
444  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
445  A.getRows(), A.getCols(), rowNum, colNum, r, c);
446  }
447  }
448 
452  bool operator==(const vpArray2D<Type> &A) const;
456  bool operator!=(const vpArray2D<Type> &A) const;
457 
460  {
461  std::fill(data, data + dsize, x);
462  return *this;
463  }
464 
469  {
470  resize(A.rowNum, A.colNum, false, false);
471  if (data != nullptr && A.data != nullptr && data != A.data) {
472  memcpy(data, A.data, (size_t)rowNum * (size_t)colNum * sizeof(Type));
473  }
474  return *this;
475  }
476 
477 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
478  vpArray2D<Type> &operator=(vpArray2D<Type> &&other) noexcept
479  {
480  if (this != &other) {
481  if (data) {
482  free(data);
483  }
484  if (rowPtrs) {
485  free(rowPtrs);
486  }
487 
488  rowNum = other.rowNum;
489  colNum = other.colNum;
490  rowPtrs = other.rowPtrs;
491  dsize = other.dsize;
492  data = other.data;
493 
494  other.rowNum = 0;
495  other.colNum = 0;
496  other.rowPtrs = nullptr;
497  other.dsize = 0;
498  other.data = nullptr;
499  }
500 
501  return *this;
502  }
503 
504  vpArray2D<Type> &operator=(const std::initializer_list<Type> &list)
505  {
506  if (dsize != static_cast<unsigned int>(list.size())) {
507  resize(1, static_cast<unsigned int>(list.size()), false, false);
508  }
509  std::copy(list.begin(), list.end(), data);
510 
511  return *this;
512  }
513 
514  vpArray2D<Type> &operator=(const std::initializer_list<std::initializer_list<Type> > &lists)
515  {
516  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
517  for (auto &l : lists) {
518  if (static_cast<unsigned int>(l.size()) > ncols) {
519  ncols = static_cast<unsigned int>(l.size());
520  }
521  }
522 
523  resize(nrows, ncols, false, false);
524  auto it = lists.begin();
525  for (unsigned int i = 0; i < rowNum; i++, ++it) {
526  std::copy(it->begin(), it->end(), rowPtrs[i]);
527  }
528 
529  return *this;
530  }
531 
532 #ifdef VISP_HAVE_NLOHMANN_JSON
533  vpArray2D<Type> &operator=(const nlohmann::json &j) = delete;
534 #endif
535 #endif
536 
538  inline Type *operator[](unsigned int i) { return rowPtrs[i]; }
540  inline Type *operator[](unsigned int i) const { return rowPtrs[i]; }
541 
547  friend std::ostream &operator<<(std::ostream &s, const vpArray2D<Type> &A)
548  {
549  if (A.data == nullptr || A.size() == 0) {
550  return s;
551  }
552  std::ios_base::fmtflags original_flags = s.flags();
553 
554  s.precision(10);
555  for (unsigned int i = 0; i < A.getRows(); i++) {
556  for (unsigned int j = 0; j < A.getCols() - 1; j++) {
557  s << A[i][j] << " ";
558  }
559  // We don't add " " after the last row element
560  s << A[i][A.getCols() - 1];
561  // We don't add a \n char on the end of the last array line
562  if (i < A.getRows() - 1) {
563  s << std::endl;
564  }
565  }
566 
567  s.flags(original_flags); // restore s to standard state
568 
569  return s;
570  }
571 
573 
581 
582  //---------------------------------
583  // Inherited array I/O Static Public Member Functions
584  //---------------------------------
600  static bool load(const std::string &filename, vpArray2D<Type> &A, bool binary = false, char *header = nullptr)
601  {
602  std::fstream file;
603 
604  if (!binary) {
605  file.open(filename.c_str(), std::fstream::in);
606  }
607  else {
608  file.open(filename.c_str(), std::fstream::in | std::fstream::binary);
609  }
610 
611  if (!file) {
612  file.close();
613  return false;
614  }
615 
616  if (!binary) {
617  std::string h;
618  bool headerIsDecoded = false;
619  do {
620  std::streampos pos = file.tellg();
621  char line[256];
622  file.getline(line, 256);
623  std::string prefix("# ");
624  std::string line_(line);
625  if (line_.compare(0, 2, prefix.c_str()) == 0) {
626  // Line is a comment
627  // If we are not on the first line, we should add "\n" to the end of
628  // the previous line
629  if (pos) {
630  h += "\n";
631  }
632  h += line_.substr(2); // Remove "# "
633  }
634  else {
635  // rewind before the line
636  file.seekg(pos, file.beg);
637  headerIsDecoded = true;
638  }
639  } while (!headerIsDecoded);
640 
641  if (header != nullptr) {
642 #if defined(__MINGW32__) || \
643  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
644  snprintf(header, h.size() + 1, "%s", h.c_str());
645 #else
646  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
647 #endif
648  }
649 
650  unsigned int rows, cols;
651  file >> rows;
652  file >> cols;
653 
654  if (rows >= (std::numeric_limits<unsigned int>::max)() || cols >= (std::numeric_limits<unsigned int>::max)()) {
655  throw vpException(vpException::badValue, "Array exceed the max size.");
656  }
657 
658  A.resize(rows, cols);
659 
660  Type value;
661  for (unsigned int i = 0; i < rows; i++) {
662  for (unsigned int j = 0; j < cols; j++) {
663  file >> value;
664  A[i][j] = value;
665  }
666  }
667  }
668  else {
669  char c = '0';
670  std::string h;
671  // Decode header until '\0' char that ends the header string
672  while (c != '\0') {
673  file.read(&c, 1);
674  h += c;
675  }
676  if (header != nullptr) {
677 #if defined(__MINGW32__) || \
678  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
679  snprintf(header, h.size() + 1, "%s", h.c_str());
680 #else
681  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
682 #endif
683  }
684 
685  unsigned int rows, cols;
686  file.read((char *)&rows, sizeof(unsigned int));
687  file.read((char *)&cols, sizeof(unsigned int));
688  A.resize(rows, cols);
689 
690  Type value;
691  for (unsigned int i = 0; i < rows; i++) {
692  for (unsigned int j = 0; j < cols; j++) {
693  file.read((char *)&value, sizeof(Type));
694  A[i][j] = value;
695  }
696  }
697  }
698 
699  file.close();
700  return true;
701  }
714  static bool loadYAML(const std::string &filename, vpArray2D<Type> &A, char *header = nullptr)
715  {
716  std::fstream file;
717 
718  file.open(filename.c_str(), std::fstream::in);
719 
720  if (!file) {
721  file.close();
722  return false;
723  }
724 
725  unsigned int rows = 0, cols = 0;
726  std::string h;
727  std::string line, subs;
728  bool inheader = true;
729  unsigned int i = 0, j;
730  unsigned int lineStart = 0;
731 
732  while (getline(file, line)) {
733  if (inheader) {
734  if (rows == 0 && line.compare(0, 5, "rows:") == 0) {
735  std::stringstream ss(line);
736  ss >> subs;
737  ss >> rows;
738  }
739  else if (cols == 0 && line.compare(0, 5, "cols:") == 0) {
740  std::stringstream ss(line);
741  ss >> subs;
742  ss >> cols;
743  }
744  else if (line.compare(0, 5, "data:") == 0) {
745  inheader = false;
746  }
747  else {
748  h += line + "\n";
749  }
750  }
751  else {
752  // if i == 0, we just got out of the header: initialize matrix
753  // dimensions
754  if (i == 0) {
755  if (rows == 0 || cols == 0) {
756  file.close();
757  return false;
758  }
759  A.resize(rows, cols);
760  // get indentation level which is common to all lines
761  lineStart = (unsigned int)line.find("[") + 1;
762  }
763  std::stringstream ss(line.substr(lineStart, line.find("]") - lineStart));
764  j = 0;
765  while (getline(ss, subs, ',')) {
766  A[i][j++] = atof(subs.c_str());
767  }
768  i++;
769  }
770  }
771 
772  if (header != nullptr) {
773  std::string h_ = h.substr(0, h.size() - 1); // Remove last '\n' char
774 #if defined(__MINGW32__) || \
775  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
776  snprintf(header, h_.size() + 1, "%s", h_.c_str());
777 #else
778  _snprintf_s(header, h_.size() + 1, _TRUNCATE, "%s", h_.c_str());
779 #endif
780  }
781 
782  file.close();
783  return true;
784  }
785 
802  static bool save(const std::string &filename, const vpArray2D<Type> &A, bool binary = false, const char *header = "")
803  {
804  std::fstream file;
805 
806  if (!binary) {
807  file.open(filename.c_str(), std::fstream::out);
808  }
809  else {
810  file.open(filename.c_str(), std::fstream::out | std::fstream::binary);
811  }
812 
813  if (!file) {
814  file.close();
815  return false;
816  }
817 
818  if (!binary) {
819  unsigned int i = 0;
820  file << "# ";
821  while (header[i] != '\0') {
822  file << header[i];
823  if (header[i] == '\n') {
824  file << "# ";
825  }
826  i++;
827  }
828  file << std::endl;
829  file << A.getRows() << "\t" << A.getCols() << std::endl;
830  file << A << std::endl;
831  }
832  else {
833  int headerSize = 0;
834  while (header[headerSize] != '\0') {
835  headerSize++;
836  }
837  file.write(header, (size_t)headerSize + (size_t)1);
838  unsigned int matrixSize;
839  matrixSize = A.getRows();
840  file.write((char *)&matrixSize, sizeof(unsigned int));
841  matrixSize = A.getCols();
842  file.write((char *)&matrixSize, sizeof(unsigned int));
843  Type value;
844  for (unsigned int i = 0; i < A.getRows(); i++) {
845  for (unsigned int j = 0; j < A.getCols(); j++) {
846  value = A[i][j];
847  file.write((char *)&value, sizeof(Type));
848  }
849  }
850  }
851 
852  file.close();
853  return true;
854  }
897  static bool saveYAML(const std::string &filename, const vpArray2D<Type> &A, const char *header = "")
898  {
899  std::fstream file;
900 
901  file.open(filename.c_str(), std::fstream::out);
902 
903  if (!file) {
904  file.close();
905  return false;
906  }
907 
908  unsigned int i = 0;
909  bool inIndent = false;
910  std::string indent = "";
911  bool checkIndent = true;
912  while (header[i] != '\0') {
913  file << header[i];
914  if (checkIndent) {
915  if (inIndent) {
916  if (header[i] == ' ') {
917  indent += " ";
918  }
919  else if (indent.length() > 0) {
920  checkIndent = false;
921  }
922  }
923  if (header[i] == '\n' || (inIndent && header[i] == ' ')) {
924  inIndent = true;
925  }
926  else {
927  inIndent = false;
928  }
929  }
930  i++;
931  }
932 
933  if (i != 0) {
934  file << std::endl;
935  }
936  file << "rows: " << A.getRows() << std::endl;
937  file << "cols: " << A.getCols() << std::endl;
938 
939  if (indent.length() == 0) {
940  indent = " ";
941  }
942 
943  file << "data: " << std::endl;
944  unsigned int j;
945  for (i = 0; i < A.getRows(); ++i) {
946  file << indent << "- [";
947  for (j = 0; j < A.getCols() - 1; ++j) {
948  file << A[i][j] << ", ";
949  }
950  file << A[i][j] << "]" << std::endl;
951  }
952 
953  file.close();
954  return true;
955  }
956 #ifdef VISP_HAVE_NLOHMANN_JSON
957  //template<typename Type>
958  template<class T>
959  friend void from_json(const nlohmann::json &j, vpArray2D<T> &array);
960  //template<typename Type>
961  template<class T>
962  friend void to_json(nlohmann::json &j, const vpArray2D<T> &array);
963 #endif
964 
976  static vpArray2D<Type> conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode);
977 
990  static void conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode);
991 
1004  vpArray2D<Type> insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c);
1005 
1019  static void insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c);
1021 };
1022 
1026 template <class Type> Type vpArray2D<Type>::getMinValue() const
1027 {
1028  Type *dataptr = data;
1029  Type min = *dataptr;
1030  dataptr++;
1031  for (unsigned int i = 0; i < dsize - 1; i++) {
1032  if (*dataptr < min) {
1033  min = *dataptr;
1034  }
1035  dataptr++;
1036  }
1037  return min;
1038 }
1039 
1043 template <class Type> Type vpArray2D<Type>::getMaxValue() const
1044 {
1045  Type *dataptr = data;
1046  Type max = *dataptr;
1047  dataptr++;
1048  for (unsigned int i = 0; i < dsize - 1; i++) {
1049  if (*dataptr > max) {
1050  max = *dataptr;
1051  }
1052  dataptr++;
1053  }
1054  return max;
1055 }
1056 
1063 template <class Type> vpArray2D<Type> vpArray2D<Type>::hadamard(const vpArray2D<Type> &m) const
1064 {
1065  if (m.getRows() != rowNum || m.getCols() != colNum) {
1066  throw(vpException(vpException::dimensionError, "Hadamard product: bad dimensions!"));
1067  }
1068 
1069  vpArray2D<Type> out;
1070  out.resize(rowNum, colNum, false);
1071 
1072  for (unsigned int i = 0; i < dsize; i++) {
1073  out.data[i] = data[i] * m.data[i];
1074  }
1075 
1076  return out;
1077 }
1078 
1079 template <class Type> vpArray2D<Type> vpArray2D<Type>::t() const
1080 {
1081  vpArray2D<Type> At(colNum, rowNum);
1082  for (unsigned int i = 0; i < rowNum; i++) {
1083  for (unsigned int j = 0; j < colNum; j++) {
1084  At[j][i] = (*this)[i][j];
1085  }
1086  }
1087  return At;
1088 }
1089 
1090 template <class Type> vpArray2D<Type> vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode)
1091 {
1092  vpArray2D<Type> res;
1093  conv2(M, kernel, res, mode);
1094  return res;
1095 }
1096 
1097 template <class Type> void vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode)
1098 {
1099  if (M.getRows() * M.getCols() == 0 || kernel.getRows() * kernel.getCols() == 0)
1100  return;
1101 
1102  if (mode == "valid") {
1103  if (kernel.getRows() > M.getRows() || kernel.getCols() > M.getCols())
1104  return;
1105  }
1106 
1107  vpArray2D<Type> M_padded, res_same;
1108 
1109  if (mode == "full" || mode == "same") {
1110  const unsigned int pad_x = kernel.getCols() - 1;
1111  const unsigned int pad_y = kernel.getRows() - 1;
1112  M_padded.resize(M.getRows() + 2 * pad_y, M.getCols() + 2 * pad_x, true, false);
1113  M_padded.insert(M, pad_y, pad_x);
1114 
1115  if (mode == "same") {
1116  res.resize(M.getRows(), M.getCols(), false, false);
1117  res_same.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1118  }
1119  else {
1120  res.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1121  }
1122  }
1123  else if (mode == "valid") {
1124  M_padded = M;
1125  res.resize(M.getRows() - kernel.getRows() + 1, M.getCols() - kernel.getCols() + 1);
1126  }
1127  else {
1128  return;
1129  }
1130 
1131  if (mode == "same") {
1132  for (unsigned int i = 0; i < res_same.getRows(); i++) {
1133  for (unsigned int j = 0; j < res_same.getCols(); j++) {
1134  for (unsigned int k = 0; k < kernel.getRows(); k++) {
1135  for (unsigned int l = 0; l < kernel.getCols(); l++) {
1136  res_same[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1137  }
1138  }
1139  }
1140  }
1141 
1142  const unsigned int start_i = kernel.getRows() / 2;
1143  const unsigned int start_j = kernel.getCols() / 2;
1144  for (unsigned int i = 0; i < M.getRows(); i++) {
1145  memcpy(res.data + i * M.getCols(), res_same.data + (i + start_i) * res_same.getCols() + start_j,
1146  sizeof(Type) * M.getCols());
1147  }
1148  }
1149  else {
1150  for (unsigned int i = 0; i < res.getRows(); i++) {
1151  for (unsigned int j = 0; j < res.getCols(); j++) {
1152  for (unsigned int k = 0; k < kernel.getRows(); k++) {
1153  for (unsigned int l = 0; l < kernel.getCols(); l++) {
1154  res[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1155  }
1156  }
1157  }
1158  }
1159  }
1160 }
1161 
1162 template<class Type> vpArray2D<Type> vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c)
1163 {
1164  vpArray2D<Type> C;
1165 
1166  insert(A, B, C, r, c);
1167 
1168  return C;
1169 }
1170 
1171 template<class Type> void vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c)
1172 {
1173  if (((r + B.getRows()) <= A.getRows()) && ((c + B.getCols()) <= A.getCols())) {
1174  C.resize(A.getRows(), A.getCols(), false, false);
1175 
1176  for (unsigned int i = 0; i < A.getRows(); i++) {
1177  for (unsigned int j = 0; j < A.getCols(); j++) {
1178  if (i >= r && i < (r + B.getRows()) && j >= c && j < (c + B.getCols())) {
1179  C[i][j] = B[i - r][j - c];
1180  }
1181  else {
1182  C[i][j] = A[i][j];
1183  }
1184  }
1185  }
1186  }
1187  else {
1188  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
1189  B.getRows(), B.getCols(), A.getCols(), A.getRows(), r, c);
1190  }
1191 }
1192 
1193 template <class Type> bool vpArray2D<Type>::operator==(const vpArray2D<Type> &A) const
1194 {
1195  if (A.rowNum != rowNum || A.colNum != colNum) {
1196  return false;
1197  }
1198 
1199  for (unsigned int i = 0; i < A.size(); i++) {
1200  if (data[i] != A.data[i]) {
1201  return false;
1202  }
1203  }
1204 
1205  return true;
1206 }
1207 
1211 template <> inline bool vpArray2D<double>::operator==(const vpArray2D<double> &A) const
1212 {
1213  if (A.rowNum != rowNum || A.colNum != colNum) {
1214  return false;
1215  }
1216 
1217  for (unsigned int i = 0; i < A.size(); i++) {
1218  if (fabs(data[i] - A.data[i]) > std::numeric_limits<double>::epsilon()) {
1219  return false;
1220  }
1221  }
1222 
1223  return true;
1224 }
1225 
1229 template <> inline bool vpArray2D<float>::operator==(const vpArray2D<float> &A) const
1230 {
1231  if (A.rowNum != rowNum || A.colNum != colNum) {
1232  return false;
1233  }
1234 
1235  for (unsigned int i = 0; i < A.size(); i++) {
1236  if (fabsf(data[i] - A.data[i]) > std::numeric_limits<float>::epsilon()) {
1237  return false;
1238  }
1239  }
1240 
1241  return true;
1242 }
1243 
1247 template <class Type> bool vpArray2D<Type>::operator!=(const vpArray2D<Type> &A) const { return !(*this == A); }
1248 
1249 #ifdef VISP_HAVE_NLOHMANN_JSON
1250 
1251 
1252 template <class Type>
1253 inline void from_json(const nlohmann::json &j, vpArray2D<Type> &array)
1254 {
1255  if (j.is_array()) {
1256  const unsigned int nrows = static_cast<unsigned int>(j.size());
1257  if (nrows == 0) { // Initialize an empty array, Finished
1258  array.resize(0, 0);
1259  return;
1260  }
1261  unsigned int ncols = 0;
1262  bool first = true;
1263  for (const auto &item : j) { // Find number of columns, validate that all rows have same number of cols
1264  if (!item.is_array()) {
1265  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with a JSON object that is not an array of array");
1266  }
1267  if (first) {
1268  first = false;
1269  ncols = static_cast<unsigned int>(item.size());
1270  }
1271  else if (ncols != item.size()) {
1272  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with JSON row arrays that are not of the same size");
1273  }
1274  }
1275  array.resize(nrows, ncols);
1276  unsigned i = 0;
1277  for (const auto &item : j) {
1278  std::vector<Type> row = item;
1279  std::copy(row.begin(), row.end(), array.rowPtrs[i]);
1280  ++i;
1281  }
1282  }
1283  else if (j.is_object()) {
1284  const unsigned ncols = j.at("cols");
1285  const unsigned nrows = j.at("rows");
1286  array.resize(nrows, ncols);
1287  const nlohmann::json jData = j.at("data");
1288  if (!jData.is_array() || jData.size() != nrows * ncols) {
1289  std::stringstream ss;
1290  ss << "JSON \"data\" field must be an array of size " << nrows * ncols;
1291  throw vpException(vpException::badValue, ss.str());
1292  }
1293  unsigned i = 0;
1294  for (const auto &jValue : jData) {
1295  array.data[i] = jValue;
1296  ++i;
1297  }
1298  }
1299  else {
1300  throw vpException(vpException::badValue, "Trying to read a vpArray2D from something that is not an array or object");
1301  }
1302 }
1303 
1304 
1305 template <class Type>
1306 inline void to_json(nlohmann::json &j, const vpArray2D<Type> &array)
1307 {
1308  j = {
1309  {"cols", array.colNum},
1310  {"rows", array.rowNum},
1311  {"type", "vpArray2D"}
1312  };
1313 
1314  nlohmann::json::array_t data;
1315  data.reserve(array.size());
1316  for (unsigned i = 0; i < array.size(); ++i) {
1317  data.push_back(array.data[i]);
1318  }
1319  j["data"] = data;
1320 }
1321 #endif
1322 #endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:125
unsigned int getCols() const
Definition: vpArray2D.h:274
vpArray2D< Type > insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1162
static void conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, vpArray2D< Type > &res, const std::string &mode)
Definition: vpArray2D.h:1097
Type * data
Address of the first element of the data array.
Definition: vpArray2D.h:138
static bool loadYAML(const std::string &filename, vpArray2D< Type > &A, char *header=nullptr)
Definition: vpArray2D.h:714
Type ** rowPtrs
Address of the first element of each rows.
Definition: vpArray2D.h:132
void insert(const vpArray2D< Type > &A, unsigned int r, unsigned int c)
Definition: vpArray2D.h:431
vpArray2D< Type > & operator=(const vpArray2D< Type > &A)
Definition: vpArray2D.h:468
Type getMinValue() const
Definition: vpArray2D.h:1026
Type * operator[](unsigned int i)
Set element using A[i][j] = x.
Definition: vpArray2D.h:538
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:299
static void insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, vpArray2D< Type > &C, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1171
static bool saveYAML(const std::string &filename, const vpArray2D< Type > &A, const char *header="")
Definition: vpArray2D.h:897
unsigned int rowNum
Number of rows in the array.
Definition: vpArray2D.h:128
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:1090
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition: vpArray2D.h:547
unsigned int dsize
Current array size (rowNum * colNum)
Definition: vpArray2D.h:134
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:286
static bool load(const std::string &filename, vpArray2D< Type > &A, bool binary=false, char *header=nullptr)
Definition: vpArray2D.h:600
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1079
unsigned int getRows() const
Definition: vpArray2D.h:284
bool operator!=(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1247
vpArray2D< Type > & operator=(Type x)
Set all the elements of the array to x.
Definition: vpArray2D.h:459
vpArray2D< Type > hadamard(const vpArray2D< Type > &m) const
Definition: vpArray2D.h:1063
Type getMaxValue() const
Definition: vpArray2D.h:1043
static bool save(const std::string &filename, const vpArray2D< Type > &A, bool binary=false, const char *header="")
Definition: vpArray2D.h:802
friend void from_json(const nlohmann::json &j, vpArray2D< T > &array)
void reshape(unsigned int nrows, unsigned int ncols)
Definition: vpArray2D.h:390
Type * operator[](unsigned int i) const
Get element using x = A[i][j].
Definition: vpArray2D.h:540
unsigned int colNum
Number of columns in the array.
Definition: vpArray2D.h:130
bool operator==(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1193
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