Visual Servoing Platform  version 3.6.0 under development (2023-09-27)
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 
130 template <class Type> class vpArray2D
131 {
132 protected:
134  unsigned int rowNum;
136  unsigned int colNum;
138  Type **rowPtrs;
140  unsigned int dsize;
141 
142 public:
144  Type *data;
145 
146 public:
151  vpArray2D<Type>() : rowNum(0), colNum(0), rowPtrs(NULL), dsize(0), data(NULL) { }
152 
157  :
158 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
159  vpArray2D<Type>()
160 #else
161  rowNum(0), colNum(0), rowPtrs(NULL), dsize(0), data(NULL)
162 #endif
163  {
164  resize(A.rowNum, A.colNum, false, false);
165  memcpy(data, A.data, (size_t)rowNum * (size_t)colNum * sizeof(Type));
166  }
167 
174  vpArray2D<Type>(unsigned int r, unsigned int c)
175  :
176 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
177  vpArray2D<Type>()
178 #else
179  rowNum(0), colNum(0), rowPtrs(NULL), dsize(0), data(NULL)
180 #endif
181  {
182  resize(r, c);
183  }
184 
192  vpArray2D<Type>(unsigned int r, unsigned int c, Type val)
193  :
194 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
195  vpArray2D<Type>()
196 #else
197  rowNum(0), colNum(0), rowPtrs(NULL), dsize(0), data(NULL)
198 #endif
199  {
200  resize(r, c, false, false);
201  *this = val;
202  }
203 
204 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
205  vpArray2D<Type>(vpArray2D<Type> &&A) noexcept
206  {
207  rowNum = A.rowNum;
208  colNum = A.colNum;
209  rowPtrs = A.rowPtrs;
210  dsize = A.dsize;
211  data = A.data;
212 
213  A.rowNum = 0;
214  A.colNum = 0;
215  A.rowPtrs = NULL;
216  A.dsize = 0;
217  A.data = NULL;
218  }
219 
220  explicit vpArray2D<Type>(const std::initializer_list<Type> &list) : vpArray2D<Type>()
221  {
222  resize(1, static_cast<unsigned int>(list.size()), false, false);
223  std::copy(list.begin(), list.end(), data);
224  }
225 
226  explicit vpArray2D<Type>(unsigned int nrows, unsigned int ncols, const std::initializer_list<Type> &list)
227  : rowNum(0), colNum(0), rowPtrs(NULL), dsize(0), data(NULL)
228  {
229  if (nrows * ncols != static_cast<unsigned int>(list.size())) {
230  std::ostringstream oss;
231  oss << "Cannot create a vpArray2D of size (" << nrows << ", " << ncols << ") with a list of size " << list.size();
232  throw vpException(vpException::dimensionError, oss.str());
233  }
234 
235  resize(nrows, ncols, false, false);
236  std::copy(list.begin(), list.end(), data);
237  }
238 
239  explicit vpArray2D<Type>(const std::initializer_list<std::initializer_list<Type> > &lists) : vpArray2D<Type>()
240  {
241  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
242  for (auto &l : lists) {
243  if (static_cast<unsigned int>(l.size()) > ncols) {
244  ncols = static_cast<unsigned int>(l.size());
245  }
246  }
247 
248  resize(nrows, ncols, false, false);
249  auto it = lists.begin();
250  for (unsigned int i = 0; i < rowNum; i++, ++it) {
251  std::copy(it->begin(), it->end(), rowPtrs[i]);
252  }
253  }
254 #endif
255 
259  virtual ~vpArray2D<Type>()
260  {
261  if (data != NULL) {
262  free(data);
263  data = NULL;
264  }
265 
266  if (rowPtrs != NULL) {
267  free(rowPtrs);
268  rowPtrs = NULL;
269  }
270  rowNum = colNum = dsize = 0;
271  }
272 
275 
280  inline unsigned int getCols() const { return colNum; }
281 
282  Type getMaxValue() const;
283 
284  Type getMinValue() const;
285 
290  inline unsigned int getRows() const { return rowNum; }
292  inline unsigned int size() const { return colNum * rowNum; }
293 
305  void resize(unsigned int nrows, unsigned int ncols, bool flagNullify = true, bool recopy_ = true)
306  {
307  if ((nrows == rowNum) && (ncols == colNum)) {
308  if (flagNullify && this->data != NULL) {
309  memset(this->data, 0, this->dsize * sizeof(Type));
310  }
311  }
312  else {
313  bool recopy = !flagNullify && recopy_; // priority to flagNullify
314  const bool recopyNeeded = (ncols != this->colNum && this->colNum > 0 && ncols > 0 && (!flagNullify || recopy));
315  Type *copyTmp = NULL;
316  unsigned int rowTmp = 0, colTmp = 0;
317 
318  // Recopy case per case is required if number of cols has changed;
319  // structure of Type array is not the same in this case.
320  if (recopyNeeded && this->data != NULL) {
321  copyTmp = new Type[this->dsize];
322  memcpy(copyTmp, this->data, sizeof(Type) * this->dsize);
323  rowTmp = this->rowNum;
324  colTmp = this->colNum;
325  }
326 
327  // Reallocation of this->data array
328  this->dsize = nrows * ncols;
329  this->data = (Type *)realloc(this->data, this->dsize * sizeof(Type));
330  if ((NULL == this->data) && (0 != this->dsize)) {
331  if (copyTmp != NULL) {
332  delete[] copyTmp;
333  }
334  throw(vpException(vpException::memoryAllocationError, "Memory allocation error when allocating 2D array data"));
335  }
336 
337  this->rowPtrs = (Type **)realloc(this->rowPtrs, nrows * sizeof(Type *));
338  if ((NULL == this->rowPtrs) && (0 != this->dsize)) {
339  if (copyTmp != NULL) {
340  delete[] copyTmp;
341  }
343  "Memory allocation error when allocating 2D array rowPtrs"));
344  }
345 
346  // Update rowPtrs
347  {
348  Type **t_ = rowPtrs;
349  for (unsigned int i = 0; i < dsize; i += ncols) {
350  *t_++ = this->data + i;
351  }
352  }
353 
354  this->rowNum = nrows;
355  this->colNum = ncols;
356 
357  // Recopy of this->data array values or nullify
358  if (flagNullify) {
359  memset(this->data, 0, (size_t)(this->dsize) * sizeof(Type));
360  }
361  else if (recopyNeeded && this->rowPtrs != NULL) {
362  // Recopy...
363  unsigned int minRow = (this->rowNum < rowTmp) ? this->rowNum : rowTmp;
364  unsigned int minCol = (this->colNum < colTmp) ? this->colNum : colTmp;
365  for (unsigned int i = 0; i < this->rowNum; ++i) {
366  for (unsigned int j = 0; j < this->colNum; ++j) {
367  if ((minRow > i) && (minCol > j)) {
368  (*this)[i][j] = copyTmp[i * colTmp + j];
369  }
370  else {
371  (*this)[i][j] = 0;
372  }
373  }
374  }
375  }
376 
377  if (copyTmp != NULL) {
378  delete[] copyTmp;
379  }
380  }
381  }
382 
383  void reshape(unsigned int nrows, unsigned int ncols)
384  {
385  if (dsize == 0) {
386  resize(nrows, ncols);
387  return;
388  }
389 
390  if (nrows * ncols != dsize) {
391  std::ostringstream oss;
392  oss << "Cannot reshape array of total size " << dsize << " into shape (" << nrows << ", " << ncols << ")";
393  throw vpException(vpException::dimensionError, oss.str());
394  }
395 
396  rowNum = nrows;
397  colNum = ncols;
398  rowPtrs = reinterpret_cast<Type **>(realloc(rowPtrs, nrows * sizeof(Type *)));
399  // Update rowPtrs
400  Type **t_ = rowPtrs;
401  for (unsigned int i = 0; i < dsize; i += ncols) {
402  *t_++ = data + i;
403  }
404  }
405 
406 
417  void insert(const vpArray2D<Type> &A, unsigned int r, unsigned int c)
418  {
419  if ((r + A.getRows()) <= rowNum && (c + A.getCols()) <= colNum) {
420  if (A.colNum == colNum && data != NULL && A.data != NULL && A.data != data) {
421  memcpy(data + r * colNum, A.data, sizeof(Type) * A.size());
422  }
423  else if (data != NULL && A.data != NULL && A.data != data) {
424  for (unsigned int i = r; i < (r + A.getRows()); i++) {
425  memcpy(data + i * colNum + c, A.data + (i - r) * A.colNum, sizeof(Type) * A.colNum);
426  }
427  }
428  }
429  else {
430  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
431  A.getRows(), A.getCols(), rowNum, colNum, r, c);
432  }
433  }
434 
438  bool operator==(const vpArray2D<Type> &A) const;
442  bool operator!=(const vpArray2D<Type> &A) const;
443 
446  {
447  std::fill(data, data + dsize, x);
448  return *this;
449  }
450 
455  {
456  resize(A.rowNum, A.colNum, false, false);
457  if (data != NULL && A.data != NULL && data != A.data) {
458  memcpy(data, A.data, (size_t)rowNum * (size_t)colNum * sizeof(Type));
459  }
460  return *this;
461  }
462 
463 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
465  {
466  if (this != &other) {
467  free(data);
468  free(rowPtrs);
469 
470  rowNum = other.rowNum;
471  colNum = other.colNum;
472  rowPtrs = other.rowPtrs;
473  dsize = other.dsize;
474  data = other.data;
475 
476  other.rowNum = 0;
477  other.colNum = 0;
478  other.rowPtrs = NULL;
479  other.dsize = 0;
480  other.data = NULL;
481  }
482 
483  return *this;
484  }
485 
486  vpArray2D<Type> &operator=(const std::initializer_list<Type> &list)
487  {
488  if (dsize != static_cast<unsigned int>(list.size())) {
489  resize(1, static_cast<unsigned int>(list.size()), false, false);
490  }
491  std::copy(list.begin(), list.end(), data);
492 
493  return *this;
494  }
495 
496  vpArray2D<Type> &operator=(const std::initializer_list<std::initializer_list<Type> > &lists)
497  {
498  unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
499  for (auto &l : lists) {
500  if (static_cast<unsigned int>(l.size()) > ncols) {
501  ncols = static_cast<unsigned int>(l.size());
502  }
503  }
504 
505  resize(nrows, ncols, false, false);
506  auto it = lists.begin();
507  for (unsigned int i = 0; i < rowNum; i++, ++it) {
508  std::copy(it->begin(), it->end(), rowPtrs[i]);
509  }
510 
511  return *this;
512  }
513 
514 #ifdef VISP_HAVE_NLOHMANN_JSON
515  vpArray2D<Type> &operator=(const nlohmann::json &j) = delete;
516 #endif
517 #endif
518 
520  inline Type *operator[](unsigned int i) { return rowPtrs[i]; }
522  inline Type *operator[](unsigned int i) const { return rowPtrs[i]; }
523 
529  friend std::ostream &operator<<(std::ostream &s, const vpArray2D<Type> &A)
530  {
531  if (A.data == NULL || A.size() == 0) {
532  return s;
533  }
534  std::ios_base::fmtflags original_flags = s.flags();
535 
536  s.precision(10);
537  for (unsigned int i = 0; i < A.getRows(); i++) {
538  for (unsigned int j = 0; j < A.getCols() - 1; j++) {
539  s << A[i][j] << " ";
540  }
541  // We don't add " " after the last row element
542  s << A[i][A.getCols() - 1];
543  // We don't add a \n char on the end of the last array line
544  if (i < A.getRows() - 1) {
545  s << std::endl;
546  }
547  }
548 
549  s.flags(original_flags); // restore s to standard state
550 
551  return s;
552  }
553 
555 
563 
564  //---------------------------------
565  // Inherited array I/O Static Public Member Functions
566  //---------------------------------
582  static bool load(const std::string &filename, vpArray2D<Type> &A, bool binary = false, char *header = NULL)
583  {
584  std::fstream file;
585 
586  if (!binary) {
587  file.open(filename.c_str(), std::fstream::in);
588  }
589  else {
590  file.open(filename.c_str(), std::fstream::in | std::fstream::binary);
591  }
592 
593  if (!file) {
594  file.close();
595  return false;
596  }
597 
598  if (!binary) {
599  std::string h;
600  bool headerIsDecoded = false;
601  do {
602  std::streampos pos = file.tellg();
603  char line[256];
604  file.getline(line, 256);
605  std::string prefix("# ");
606  std::string line_(line);
607  if (line_.compare(0, 2, prefix.c_str()) == 0) {
608  // Line is a comment
609  // If we are not on the first line, we should add "\n" to the end of
610  // the previous line
611  if (pos) {
612  h += "\n";
613  }
614  h += line_.substr(2); // Remove "# "
615  }
616  else {
617  // rewind before the line
618  file.seekg(pos, file.beg);
619  headerIsDecoded = true;
620  }
621  } while (!headerIsDecoded);
622 
623  if (header != NULL) {
624 #if defined(__MINGW32__) || \
625  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
626  snprintf(header, h.size() + 1, "%s", h.c_str());
627 #else
628  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
629 #endif
630  }
631 
632  unsigned int rows, cols;
633  file >> rows;
634  file >> cols;
635 
636  if (rows >= (std::numeric_limits<unsigned int>::max)() || cols >= (std::numeric_limits<unsigned int>::max)()) {
637  throw vpException(vpException::badValue, "Array exceed the max size.");
638  }
639 
640  A.resize(rows, cols);
641 
642  Type value;
643  for (unsigned int i = 0; i < rows; i++) {
644  for (unsigned int j = 0; j < cols; j++) {
645  file >> value;
646  A[i][j] = value;
647  }
648  }
649  }
650  else {
651  char c = '0';
652  std::string h;
653  // Decode header until '\0' char that ends the header string
654  while (c != '\0') {
655  file.read(&c, 1);
656  h += c;
657  }
658  if (header != NULL) {
659 #if defined(__MINGW32__) || \
660  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
661  snprintf(header, h.size() + 1, "%s", h.c_str());
662 #else
663  _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
664 #endif
665  }
666 
667  unsigned int rows, cols;
668  file.read((char *)&rows, sizeof(unsigned int));
669  file.read((char *)&cols, sizeof(unsigned int));
670  A.resize(rows, cols);
671 
672  Type value;
673  for (unsigned int i = 0; i < rows; i++) {
674  for (unsigned int j = 0; j < cols; j++) {
675  file.read((char *)&value, sizeof(Type));
676  A[i][j] = value;
677  }
678  }
679  }
680 
681  file.close();
682  return true;
683  }
696  static bool loadYAML(const std::string &filename, vpArray2D<Type> &A, char *header = NULL)
697  {
698  std::fstream file;
699 
700  file.open(filename.c_str(), std::fstream::in);
701 
702  if (!file) {
703  file.close();
704  return false;
705  }
706 
707  unsigned int rows = 0, cols = 0;
708  std::string h;
709  std::string line, subs;
710  bool inheader = true;
711  unsigned int i = 0, j;
712  unsigned int lineStart = 0;
713 
714  while (getline(file, line)) {
715  if (inheader) {
716  if (rows == 0 && line.compare(0, 5, "rows:") == 0) {
717  std::stringstream ss(line);
718  ss >> subs;
719  ss >> rows;
720  }
721  else if (cols == 0 && line.compare(0, 5, "cols:") == 0) {
722  std::stringstream ss(line);
723  ss >> subs;
724  ss >> cols;
725  }
726  else if (line.compare(0, 5, "data:") == 0) {
727  inheader = false;
728  }
729  else {
730  h += line + "\n";
731  }
732  }
733  else {
734  // if i == 0, we just got out of the header: initialize matrix
735  // dimensions
736  if (i == 0) {
737  if (rows == 0 || cols == 0) {
738  file.close();
739  return false;
740  }
741  A.resize(rows, cols);
742  // get indentation level which is common to all lines
743  lineStart = (unsigned int)line.find("[") + 1;
744  }
745  std::stringstream ss(line.substr(lineStart, line.find("]") - lineStart));
746  j = 0;
747  while (getline(ss, subs, ',')) {
748  A[i][j++] = atof(subs.c_str());
749  }
750  i++;
751  }
752  }
753 
754  if (header != NULL) {
755  std::string h_ = h.substr(0, h.size() - 1); // Remove last '\n' char
756 #if defined(__MINGW32__) || \
757  !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
758  snprintf(header, h_.size() + 1, "%s", h_.c_str());
759 #else
760  _snprintf_s(header, h_.size() + 1, _TRUNCATE, "%s", h_.c_str());
761 #endif
762  }
763 
764  file.close();
765  return true;
766  }
767 
784  static bool save(const std::string &filename, const vpArray2D<Type> &A, bool binary = false, const char *header = "")
785  {
786  std::fstream file;
787 
788  if (!binary) {
789  file.open(filename.c_str(), std::fstream::out);
790  }
791  else {
792  file.open(filename.c_str(), std::fstream::out | std::fstream::binary);
793  }
794 
795  if (!file) {
796  file.close();
797  return false;
798  }
799 
800  if (!binary) {
801  unsigned int i = 0;
802  file << "# ";
803  while (header[i] != '\0') {
804  file << header[i];
805  if (header[i] == '\n') {
806  file << "# ";
807  }
808  i++;
809  }
810  file << std::endl;
811  file << A.getRows() << "\t" << A.getCols() << std::endl;
812  file << A << std::endl;
813  }
814  else {
815  int headerSize = 0;
816  while (header[headerSize] != '\0') {
817  headerSize++;
818  }
819  file.write(header, (size_t)headerSize + (size_t)1);
820  unsigned int matrixSize;
821  matrixSize = A.getRows();
822  file.write((char *)&matrixSize, sizeof(unsigned int));
823  matrixSize = A.getCols();
824  file.write((char *)&matrixSize, sizeof(unsigned int));
825  Type value;
826  for (unsigned int i = 0; i < A.getRows(); i++) {
827  for (unsigned int j = 0; j < A.getCols(); j++) {
828  value = A[i][j];
829  file.write((char *)&value, sizeof(Type));
830  }
831  }
832  }
833 
834  file.close();
835  return true;
836  }
877  static bool saveYAML(const std::string &filename, const vpArray2D<Type> &A, const char *header = "")
878  {
879  std::fstream file;
880 
881  file.open(filename.c_str(), std::fstream::out);
882 
883  if (!file) {
884  file.close();
885  return false;
886  }
887 
888  unsigned int i = 0;
889  bool inIndent = false;
890  std::string indent = "";
891  bool checkIndent = true;
892  while (header[i] != '\0') {
893  file << header[i];
894  if (checkIndent) {
895  if (inIndent) {
896  if (header[i] == ' ') {
897  indent += " ";
898  }
899  else if (indent.length() > 0) {
900  checkIndent = false;
901  }
902  }
903  if (header[i] == '\n' || (inIndent && header[i] == ' ')) {
904  inIndent = true;
905  }
906  else {
907  inIndent = false;
908  }
909  }
910  i++;
911  }
912 
913  if (i != 0) {
914  file << std::endl;
915  }
916  file << "rows: " << A.getRows() << std::endl;
917  file << "cols: " << A.getCols() << std::endl;
918 
919  if (indent.length() == 0) {
920  indent = " ";
921  }
922 
923  file << "data: " << std::endl;
924  unsigned int j;
925  for (i = 0; i < A.getRows(); ++i) {
926  file << indent << "- [";
927  for (j = 0; j < A.getCols() - 1; ++j) {
928  file << A[i][j] << ", ";
929  }
930  file << A[i][j] << "]" << std::endl;
931  }
932 
933  file.close();
934  return true;
935  }
936 #ifdef VISP_HAVE_NLOHMANN_JSON
937  //template<typename Type>
938  template<class T>
939  friend void from_json(const nlohmann::json &j, vpArray2D<T> &array);
940  //template<typename Type>
941  template<class T>
942  friend void to_json(nlohmann::json &j, const vpArray2D<T> &array);
943 #endif
944 
956  static vpArray2D<Type> conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode);
957 
970  static void conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode);
971 
984  vpArray2D<Type> insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c);
985 
999  static void insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c);
1001 };
1002 
1006 template <class Type> Type vpArray2D<Type>::getMinValue() const
1007 {
1008  Type *dataptr = data;
1009  Type min = *dataptr;
1010  dataptr++;
1011  for (unsigned int i = 0; i < dsize - 1; i++) {
1012  if (*dataptr < min) {
1013  min = *dataptr;
1014  }
1015  dataptr++;
1016  }
1017  return min;
1018 }
1019 
1023 template <class Type> Type vpArray2D<Type>::getMaxValue() const
1024 {
1025  Type *dataptr = data;
1026  Type max = *dataptr;
1027  dataptr++;
1028  for (unsigned int i = 0; i < dsize - 1; i++) {
1029  if (*dataptr > max) {
1030  max = *dataptr;
1031  }
1032  dataptr++;
1033  }
1034  return max;
1035 }
1036 
1043 template <class Type> vpArray2D<Type> vpArray2D<Type>::hadamard(const vpArray2D<Type> &m) const
1044 {
1045  if (m.getRows() != rowNum || m.getCols() != colNum) {
1046  throw(vpException(vpException::dimensionError, "Hadamard product: bad dimensions!"));
1047  }
1048 
1049  vpArray2D<Type> out;
1050  out.resize(rowNum, colNum, false);
1051 
1052  for (unsigned int i = 0; i < dsize; i++) {
1053  out.data[i] = data[i] * m.data[i];
1054  }
1055 
1056  return out;
1057 }
1058 
1059 template <class Type> vpArray2D<Type> vpArray2D<Type>::t() const
1060 {
1061  vpArray2D<Type> At(colNum, rowNum);
1062  for (unsigned int i = 0; i < rowNum; i++) {
1063  for (unsigned int j = 0; j < colNum; j++) {
1064  At[j][i] = (*this)[i][j];
1065  }
1066  }
1067  return At;
1068 }
1069 
1070 template <class Type> vpArray2D<Type> vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode)
1071 {
1072  vpArray2D<Type> res;
1073  conv2(M, kernel, res, mode);
1074  return res;
1075 }
1076 
1077 template <class Type> void vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode)
1078 {
1079  if (M.getRows() * M.getCols() == 0 || kernel.getRows() * kernel.getCols() == 0)
1080  return;
1081 
1082  if (mode == "valid") {
1083  if (kernel.getRows() > M.getRows() || kernel.getCols() > M.getCols())
1084  return;
1085  }
1086 
1087  vpArray2D<Type> M_padded, res_same;
1088 
1089  if (mode == "full" || mode == "same") {
1090  const unsigned int pad_x = kernel.getCols() - 1;
1091  const unsigned int pad_y = kernel.getRows() - 1;
1092  M_padded.resize(M.getRows() + 2 * pad_y, M.getCols() + 2 * pad_x, true, false);
1093  M_padded.insert(M, pad_y, pad_x);
1094 
1095  if (mode == "same") {
1096  res.resize(M.getRows(), M.getCols(), false, false);
1097  res_same.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1098  }
1099  else {
1100  res.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1101  }
1102  }
1103  else if (mode == "valid") {
1104  M_padded = M;
1105  res.resize(M.getRows() - kernel.getRows() + 1, M.getCols() - kernel.getCols() + 1);
1106  }
1107  else {
1108  return;
1109  }
1110 
1111  if (mode == "same") {
1112  for (unsigned int i = 0; i < res_same.getRows(); i++) {
1113  for (unsigned int j = 0; j < res_same.getCols(); j++) {
1114  for (unsigned int k = 0; k < kernel.getRows(); k++) {
1115  for (unsigned int l = 0; l < kernel.getCols(); l++) {
1116  res_same[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1117  }
1118  }
1119  }
1120  }
1121 
1122  const unsigned int start_i = kernel.getRows() / 2;
1123  const unsigned int start_j = kernel.getCols() / 2;
1124  for (unsigned int i = 0; i < M.getRows(); i++) {
1125  memcpy(res.data + i * M.getCols(), res_same.data + (i + start_i) * res_same.getCols() + start_j,
1126  sizeof(Type) * M.getCols());
1127  }
1128  }
1129  else {
1130  for (unsigned int i = 0; i < res.getRows(); i++) {
1131  for (unsigned int j = 0; j < res.getCols(); j++) {
1132  for (unsigned int k = 0; k < kernel.getRows(); k++) {
1133  for (unsigned int l = 0; l < kernel.getCols(); l++) {
1134  res[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1135  }
1136  }
1137  }
1138  }
1139  }
1140 }
1141 
1142 template<class Type> vpArray2D<Type> vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c)
1143 {
1144  vpArray2D<Type> C;
1145 
1146  insert(A, B, C, r, c);
1147 
1148  return C;
1149 }
1150 
1151 template<class Type> void vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c)
1152 {
1153  if (((r + B.getRows()) <= A.getRows()) && ((c + B.getCols()) <= A.getCols())) {
1154  C.resize(A.getRows(), A.getCols(), false, false);
1155 
1156  for (unsigned int i = 0; i < A.getRows(); i++) {
1157  for (unsigned int j = 0; j < A.getCols(); j++) {
1158  if (i >= r && i < (r + B.getRows()) && j >= c && j < (c + B.getCols())) {
1159  C[i][j] = B[i - r][j - c];
1160  }
1161  else {
1162  C[i][j] = A[i][j];
1163  }
1164  }
1165  }
1166  }
1167  else {
1168  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
1169  B.getRows(), B.getCols(), A.getCols(), A.getRows(), r, c);
1170  }
1171 }
1172 
1173 template <class Type> bool vpArray2D<Type>::operator==(const vpArray2D<Type> &A) const
1174 {
1175  if (A.rowNum != rowNum || A.colNum != colNum) {
1176  return false;
1177  }
1178 
1179  for (unsigned int i = 0; i < A.size(); i++) {
1180  if (data[i] != A.data[i]) {
1181  return false;
1182  }
1183  }
1184 
1185  return true;
1186 }
1187 
1191 template <> inline bool vpArray2D<double>::operator==(const vpArray2D<double> &A) const
1192 {
1193  if (A.rowNum != rowNum || A.colNum != colNum) {
1194  return false;
1195  }
1196 
1197  for (unsigned int i = 0; i < A.size(); i++) {
1198  if (fabs(data[i] - A.data[i]) > std::numeric_limits<double>::epsilon()) {
1199  return false;
1200  }
1201  }
1202 
1203  return true;
1204 }
1205 
1209 template <> inline bool vpArray2D<float>::operator==(const vpArray2D<float> &A) const
1210 {
1211  if (A.rowNum != rowNum || A.colNum != colNum) {
1212  return false;
1213  }
1214 
1215  for (unsigned int i = 0; i < A.size(); i++) {
1216  if (fabsf(data[i] - A.data[i]) > std::numeric_limits<float>::epsilon()) {
1217  return false;
1218  }
1219  }
1220 
1221  return true;
1222 }
1223 
1227 template <class Type> bool vpArray2D<Type>::operator!=(const vpArray2D<Type> &A) const { return !(*this == A); }
1228 
1229 #ifdef VISP_HAVE_NLOHMANN_JSON
1230 
1231 
1232 template <class Type>
1233 inline void from_json(const nlohmann::json &j, vpArray2D<Type> &array)
1234 {
1235  if (j.is_array()) {
1236  const unsigned int nrows = static_cast<unsigned int>(j.size());
1237  if (nrows == 0) { // Initialize an empty array, Finished
1238  array.resize(0, 0);
1239  return;
1240  }
1241  unsigned int ncols = 0;
1242  bool first = true;
1243  for (const auto &item: j) { // Find number of columns, validate that all rows have same number of cols
1244  if (!item.is_array()) {
1245  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with a JSON object that is not an array of array");
1246  }
1247  if (first) {
1248  first = false;
1249  ncols = static_cast<unsigned int>(item.size());
1250  }
1251  else if (ncols != item.size()) {
1252  throw vpException(vpException::badValue, "Trying to instantiate a 2D array with JSON row arrays that are not of the same size");
1253  }
1254  }
1255  array.resize(nrows, ncols);
1256  unsigned i = 0;
1257  for (const auto &item: j) {
1258  std::vector<Type> row = item;
1259  std::copy(row.begin(), row.end(), array.rowPtrs[i]);
1260  ++i;
1261  }
1262  }
1263  else if (j.is_object()) {
1264  const unsigned ncols = j.at("cols");
1265  const unsigned nrows = j.at("rows");
1266  array.resize(nrows, ncols);
1267  const nlohmann::json jData = j.at("data");
1268  if (!jData.is_array() || jData.size() != nrows * ncols) {
1269  std::stringstream ss;
1270  ss << "JSON \"data\" field must be an array of size " << nrows * ncols;
1271  throw vpException(vpException::badValue, ss.str());
1272  }
1273  unsigned i = 0;
1274  for (const auto &jValue: jData) {
1275  array.data[i] = jValue;
1276  ++i;
1277  }
1278  }
1279  else {
1280  throw vpException(vpException::badValue, "Trying to read a vpArray2D from something that is not an array or object");
1281  }
1282 }
1283 
1284 
1285 template <class Type>
1286 inline void to_json(nlohmann::json &j, const vpArray2D<Type> &array)
1287 {
1288  j = {
1289  {"cols", array.colNum},
1290  {"rows", array.rowNum},
1291  {"type", "vpArray2D"}
1292  };
1293 
1294  nlohmann::json::array_t data;
1295  data.reserve(array.size());
1296  for (unsigned i = 0; i < array.size(); ++i) {
1297  data.push_back(array.data[i]);
1298  }
1299  j["data"] = data;
1300 }
1301 #endif
1302 #endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:131
vpArray2D< Type > & operator=(const nlohmann::json &j)=delete
static bool load(const std::string &filename, vpArray2D< Type > &A, bool binary=false, char *header=NULL)
Definition: vpArray2D.h:582
unsigned int getCols() const
Definition: vpArray2D.h:280
vpArray2D< Type > insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1142
static void conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, vpArray2D< Type > &res, const std::string &mode)
Definition: vpArray2D.h:1077
vpArray2D< Type > & operator=(const std::initializer_list< Type > &list)
Definition: vpArray2D.h:486
Type * data
Address of the first element of the data array.
Definition: vpArray2D.h:144
Type ** rowPtrs
Address of the first element of each rows.
Definition: vpArray2D.h:138
void insert(const vpArray2D< Type > &A, unsigned int r, unsigned int c)
Definition: vpArray2D.h:417
vpArray2D< Type > & operator=(const vpArray2D< Type > &A)
Definition: vpArray2D.h:454
Type getMinValue() const
Definition: vpArray2D.h:1006
Type * operator[](unsigned int i)
Set element using A[i][j] = x.
Definition: vpArray2D.h:520
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:305
vpArray2D< Type > & operator=(vpArray2D< Type > &&other) noexcept
Definition: vpArray2D.h:464
static void insert(const vpArray2D< Type > &A, const vpArray2D< Type > &B, vpArray2D< Type > &C, unsigned int r, unsigned int c)
Definition: vpArray2D.h:1151
static bool saveYAML(const std::string &filename, const vpArray2D< Type > &A, const char *header="")
Definition: vpArray2D.h:877
unsigned int rowNum
Number of rows in the array.
Definition: vpArray2D.h:134
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:1070
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition: vpArray2D.h:529
unsigned int dsize
Current array size (rowNum * colNum)
Definition: vpArray2D.h:140
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:292
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1059
static bool loadYAML(const std::string &filename, vpArray2D< Type > &A, char *header=NULL)
Definition: vpArray2D.h:696
unsigned int getRows() const
Definition: vpArray2D.h:290
bool operator!=(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1227
vpArray2D< Type > & operator=(Type x)
Set all the elements of the array to x.
Definition: vpArray2D.h:445
vpArray2D< Type > & operator=(const std::initializer_list< std::initializer_list< Type > > &lists)
Definition: vpArray2D.h:496
vpArray2D< Type > hadamard(const vpArray2D< Type > &m) const
Definition: vpArray2D.h:1043
Type getMaxValue() const
Definition: vpArray2D.h:1023
static bool save(const std::string &filename, const vpArray2D< Type > &A, bool binary=false, const char *header="")
Definition: vpArray2D.h:784
friend void from_json(const nlohmann::json &j, vpArray2D< T > &array)
void reshape(unsigned int nrows, unsigned int ncols)
Definition: vpArray2D.h:383
Type * operator[](unsigned int i) const
Get element using x = A[i][j].
Definition: vpArray2D.h:522
unsigned int colNum
Number of columns in the array.
Definition: vpArray2D.h:136
bool operator==(const vpArray2D< Type > &A) const
Definition: vpArray2D.h:1173
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