Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpArray2D.h
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * This class implements an 2D array as a template class.
32 */
33#ifndef VP_ARRAY2D_H
34#define VP_ARRAY2D_H
35
36#include <fstream>
37#include <iostream>
38#include <limits>
39#include <math.h>
40#include <ostream>
41#include <sstream>
42#include <stdlib.h>
43#include <string.h>
44#include <vector>
45
46#include <visp3/core/vpConfig.h>
47#include <visp3/core/vpException.h>
48#include <visp3/core/vpMath.h>
49
50#ifdef VISP_HAVE_NLOHMANN_JSON
51#include VISP_NLOHMANN_JSON(json.hpp)
52#endif
53
145template <class Type> class vpArray2D
146{
147public:
149 Type *data;
150
155 vpArray2D() : data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), isMemoryOwner(true), isRowPtrsOwner(true) { }
156
161 :
162#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
163 vpArray2D()
164#else
165 data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), isMemoryOwner(true), isRowPtrsOwner(true)
166#endif
167 {
168 resize(A.rowNum, A.colNum, false, false);
169 memcpy(data, A.data, static_cast<size_t>(rowNum) * static_cast<size_t>(colNum) * sizeof(Type));
170 }
171
178 vpArray2D(unsigned int r, unsigned int c)
179 :
180#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
181 vpArray2D()
182#else
183 data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), isMemoryOwner(true), isRowPtrsOwner(true)
184#endif
185 {
186 resize(r, c);
187 }
188
196 vpArray2D(unsigned int r, unsigned int c, Type val)
197 :
198#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
199 vpArray2D()
200#else
201 data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), isMemoryOwner(true), isRowPtrsOwner(true)
202#endif
203 {
204 resize(r, c, false, false);
205 *this = val;
206 }
207
219 vpArray2D(const std::vector<Type> &vec, unsigned int r = 0, unsigned int c = 0)
220 :
221#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
222 vpArray2D()
223#else
224 data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), isMemoryOwner(true), isRowPtrsOwner(true)
225#endif
226 {
227 if ((r > 0) && (c > 0)) {
228 if ((r * c) != vec.size()) {
230 "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
231 }
232 resize(r, c, false, false);
233 }
234 else if ((c == 0) && (r == 0)) {
236 "Cannot initialize vpArray(%d, %d) from std::vector(%d). Using rows = 0 and cols = 0 is ambiguous", r, c, vec.size()));
237 }
238 else if (c == 0) {
239 if (r != vec.size()) {
241 "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
242 }
243 resize(static_cast<unsigned int>(vec.size()), 1, false, false);
244 }
245 else if (r == 0) {
246 if (c != vec.size()) {
248 "Cannot initialize vpArray(%d, %d) from std::vector(%d). Wrong dimension", r, c, vec.size()));
249 }
250 resize(1, static_cast<unsigned int>(vec.size()), false, false);
251 }
252
253#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
254 std::copy(vec.begin(), vec.end(), data);
255#else
256 memcpy(data, vec.data(), vec.size() * sizeof(Type));
257#endif
258 }
259
260#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
262 {
263 rowNum = A.rowNum;
264 colNum = A.colNum;
265 rowPtrs = A.rowPtrs;
266 dsize = A.dsize;
267 data = A.data;
268 isMemoryOwner = A.isMemoryOwner;
269 isRowPtrsOwner = A.isRowPtrsOwner;
270
271 A.rowNum = 0;
272 A.colNum = 0;
273 A.rowPtrs = nullptr;
274 A.dsize = 0;
275 A.data = nullptr;
276 }
277
278 VP_EXPLICIT vpArray2D(const std::initializer_list<Type> &list) : vpArray2D()
279 {
280 resize(1, static_cast<unsigned int>(list.size()), false, false);
281 std::copy(list.begin(), list.end(), data);
282 }
283
284 VP_EXPLICIT vpArray2D(unsigned int nrows, unsigned int ncols, const std::initializer_list<Type> &list)
285 : data(nullptr), rowNum(0), colNum(0), rowPtrs(nullptr), dsize(0), isMemoryOwner(true), isRowPtrsOwner(true)
286 {
287 if ((nrows * ncols) != static_cast<unsigned int>(list.size())) {
288 std::ostringstream oss;
289 oss << "Cannot create a vpArray2D of size (" << nrows << ", " << ncols << ") with a list of size " << list.size();
290 throw vpException(vpException::dimensionError, oss.str());
291 }
292
293 resize(nrows, ncols, false, false);
294 std::copy(list.begin(), list.end(), data);
295 }
296
297 VP_EXPLICIT vpArray2D(const std::initializer_list<std::initializer_list<Type> > &lists) : vpArray2D()
298 {
299 unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
300 for (auto &l : lists) {
301 if (static_cast<unsigned int>(l.size()) > ncols) {
302 ncols = static_cast<unsigned int>(l.size());
303 }
304 }
305
306 resize(nrows, ncols, false, false);
307 auto it = lists.begin();
308 for (unsigned int i = 0; i < rowNum; ++i, ++it) {
309 std::copy(it->begin(), it->end(), rowPtrs[i]);
310 }
311 }
312#endif
313
325 {
327 v.rowNum = A.rowNum;
328 v.colNum = A.colNum;
329 v.rowPtrs = A.rowPtrs;
330 v.dsize = A.dsize;
331 v.data = A.data;
332 v.isMemoryOwner = false;
333 v.isRowPtrsOwner = false;
334 return v;
335 }
336
346 static vpArray2D<Type> view(Type *data, unsigned int numRows, unsigned int numCols)
347 {
349 vpArray2D<Type>::view(v, data, numRows, numCols);
350 return v;
351 }
352
363 static void view(vpArray2D<Type> &v, Type *data, unsigned int numRows, unsigned int numCols)
364 {
365 unsigned int oldRows = v.rowNum;
366 unsigned int oldCols = v.colNum;
367 Type *oldData = v.data;
368 v.rowNum = numRows;
369 v.colNum = numCols;
370 v.dsize = numRows * numCols;
371
372 if ((v.isMemoryOwner == true) && (v.data != nullptr)) {
373 free(v.data);
374 }
375 v.data = data;
376 v.isMemoryOwner = false;
377 bool requiresRowPtrRealloc = data != oldData || numRows != oldRows || numCols != oldCols;
378
379 if (requiresRowPtrRealloc) {
380 if ((v.isRowPtrsOwner == true) && (v.rowPtrs != nullptr)) {
381 free(v.rowPtrs);
382 }
383 v.isRowPtrsOwner = true;
384 v.rowPtrs = reinterpret_cast<Type **>(malloc(v.rowNum * sizeof(Type *)));
385 for (unsigned int i = 0; i < v.rowNum; ++i) {
386 v.rowPtrs[i] = data + i * v.colNum;
387 }
388 }
389 }
390
391
392
396 virtual ~vpArray2D()
397 {
398 if (data != nullptr) {
399 if (isMemoryOwner) {
400 free(data);
401 }
402 data = nullptr;
403 }
404
405 if (rowPtrs != nullptr) {
406 if (isRowPtrsOwner) {
407 free(rowPtrs);
408 }
409 rowPtrs = nullptr;
410 }
411 rowNum = 0;
412 colNum = 0;
413 dsize = 0;
414 }
415
418
423 inline unsigned int getCols() const { return colNum; }
424
425 Type getMaxValue() const;
426
427 Type getMinValue() const;
428
433 inline unsigned int getRows() const { return rowNum; }
435 inline unsigned int size() const { return colNum * rowNum; }
436
448 void resize(unsigned int nrows, unsigned int ncols, bool flagNullify = true, bool recopy_ = true)
449 {
450 if ((nrows == rowNum) && (ncols == colNum)) {
451 if (flagNullify && (this->data != nullptr)) {
452 memset(this->data, 0, this->dsize * sizeof(Type));
453 }
454 }
455 else {
456 if (!isMemoryOwner) {
457 throw vpException(vpException::badValue, "Cannot resize an array that is a view of another array");
458 }
459 bool recopy = (!flagNullify) && recopy_; // priority to flagNullify
460 bool colcond = (ncols != this->colNum) && (this->colNum > 0) && (ncols > 0);
461 const bool recopyNeeded = colcond && ((!flagNullify) || recopy);
462 Type *copyTmp = nullptr;
463 unsigned int rowTmp = 0, colTmp = 0;
464
465 // Recopy case per case is required if number of cols has changed;
466 // structure of Type array is not the same in this case.
467 if (recopyNeeded && (this->data != nullptr)) {
468 copyTmp = new Type[this->dsize];
469 memcpy(copyTmp, this->data, sizeof(Type) * this->dsize);
470 rowTmp = this->rowNum;
471 colTmp = this->colNum;
472 }
473
474 // Reallocation of this->data array
475 this->dsize = nrows * ncols;
476 Type *tmp_data = reinterpret_cast<Type *>(realloc(this->data, this->dsize * sizeof(Type)));
477 if (tmp_data) {
478 this->data = tmp_data;
479 }
480 else {
481 this->data = nullptr;
482 }
483
484 if ((nullptr == this->data) && (0 != this->dsize)) {
485 if (copyTmp != nullptr) {
486 delete[] copyTmp;
487 }
488 throw(vpException(vpException::memoryAllocationError, "Memory allocation error when allocating 2D array data"));
489 }
490
491 Type **tmp_rowPtrs = reinterpret_cast<Type **>(realloc(this->rowPtrs, nrows * sizeof(Type *)));
492 if (tmp_rowPtrs) {
493 this->rowPtrs = tmp_rowPtrs;
494 }
495 else {
496 this->rowPtrs = nullptr;
497 }
498 if ((nullptr == this->rowPtrs) && (0 != this->dsize)) {
499 if (copyTmp != nullptr) {
500 delete[] copyTmp;
501 }
503 "Memory allocation error when allocating 2D array rowPtrs"));
504 }
505
506 // Update rowPtrs
507
508 Type **t_ = rowPtrs;
509 for (unsigned int i = 0; i < dsize; i += ncols) {
510 *t_++ = this->data + i;
511 }
512
513 this->rowNum = nrows;
514 this->colNum = ncols;
515
516 // Recopy of this->data array values or nullify
517 if (flagNullify) {
518 // If dsize = 0 nothing to do
519 if ((nullptr != this->data) && (0 != this->dsize)) {
520 memset(this->data, 0, static_cast<size_t>(this->dsize) * sizeof(Type));
521 }
522 }
523 else if (recopyNeeded && (this->rowPtrs != nullptr)) {
524 // Recopy...
525 unsigned int minRow = (this->rowNum < rowTmp) ? this->rowNum : rowTmp;
526 unsigned int minCol = (this->colNum < colTmp) ? this->colNum : colTmp;
527 for (unsigned int i = 0; i < this->rowNum; ++i) {
528 for (unsigned int j = 0; j < this->colNum; ++j) {
529 if ((minRow > i) && (minCol > j)) {
530 (*this)[i][j] = copyTmp[(i * colTmp) + j];
531 }
532 else {
533 (*this)[i][j] = 0;
534 }
535 }
536 }
537 }
538
539 if (copyTmp != nullptr) {
540 delete[] copyTmp;
541 }
542 }
543 }
544
545 void reshape(unsigned int nrows, unsigned int ncols)
546 {
547 if (dsize == 0) {
548 resize(nrows, ncols);
549 return;
550 }
551
552 if ((nrows * ncols) != dsize) {
553 std::ostringstream oss;
554 oss << "Cannot reshape array of total size " << dsize << " into shape (" << nrows << ", " << ncols << ")";
556 }
557
558 rowNum = nrows;
559 colNum = ncols;
560 if (rowPtrs) {
561 Type **tmp = reinterpret_cast<Type **>(realloc(rowPtrs, nrows * sizeof(Type *)));
562 if (tmp) {
563 this->rowPtrs = tmp;
564 }
565 }
566 if (rowPtrs) {
567 // Update rowPtrs
568 Type **t_ = rowPtrs;
569 for (unsigned int i = 0; i < dsize; i += ncols) {
570 *t_++ = data + i;
571 }
572 }
573 }
574
575
586 void insert(const vpArray2D<Type> &A, unsigned int r, unsigned int c)
587 {
588 if (((r + A.getRows()) <= rowNum) && ((c + A.getCols()) <= colNum)) {
589 if ((A.colNum == colNum) && (data != nullptr) && (A.data != nullptr) && (A.data != data)) {
590 memcpy(data + (r * colNum), A.data, sizeof(Type) * A.size());
591 }
592 else if ((data != nullptr) && (A.data != nullptr) && (A.data != data)) {
593 unsigned int a_rows = A.getRows();
594 for (unsigned int i = r; i < (r + a_rows); ++i) {
595 memcpy(data + (i * colNum) + c, A.data + ((i - r) * A.colNum), sizeof(Type) * A.colNum);
596 }
597 }
598 }
599 else {
600 throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
601 A.getRows(), A.getCols(), rowNum, colNum, r, c);
602 }
603 }
604
608 bool operator==(const vpArray2D<Type> &A) const;
612 bool operator!=(const vpArray2D<Type> &A) const;
613
616 {
617 std::fill(data, data + dsize, x);
618 return *this;
619 }
620
625 {
626 resize(A.rowNum, A.colNum, false, false);
627 if ((data != nullptr) && (A.data != nullptr) && (data != A.data)) {
628 memcpy(data, A.data, static_cast<size_t>(rowNum) * static_cast<size_t>(colNum) * sizeof(Type));
629 }
630 return *this;
631 }
632
633#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
635 {
636 if (this != &other) {
637 if (isMemoryOwner && data) {
638 free(data);
639 }
640 if (isRowPtrsOwner && rowPtrs) {
641 free(rowPtrs);
642 }
643
644 rowNum = other.rowNum;
645 colNum = other.colNum;
646 rowPtrs = other.rowPtrs;
647 dsize = other.dsize;
648 data = other.data;
649 isMemoryOwner = other.isMemoryOwner;
650 isRowPtrsOwner = other.isRowPtrsOwner;
651
652 other.rowNum = 0;
653 other.colNum = 0;
654 other.rowPtrs = nullptr;
655 other.dsize = 0;
656 other.data = nullptr;
657 }
658
659 return *this;
660 }
661
662 vpArray2D<Type> &operator=(const std::initializer_list<Type> &list)
663 {
664 if (dsize != static_cast<unsigned int>(list.size())) {
665 resize(1, static_cast<unsigned int>(list.size()), false, false);
666 }
667 std::copy(list.begin(), list.end(), data);
668
669 return *this;
670 }
671
672 vpArray2D<Type> &operator=(const std::initializer_list<std::initializer_list<Type> > &lists)
673 {
674 unsigned int nrows = static_cast<unsigned int>(lists.size()), ncols = 0;
675 for (auto &l : lists) {
676 if (static_cast<unsigned int>(l.size()) > ncols) {
677 ncols = static_cast<unsigned int>(l.size());
678 }
679 }
680
681 resize(nrows, ncols, false, false);
682 auto it = lists.begin();
683 for (unsigned int i = 0; i < rowNum; ++i, ++it) {
684 std::copy(it->begin(), it->end(), rowPtrs[i]);
685 }
686
687 return *this;
688 }
689
690#ifdef VISP_HAVE_NLOHMANN_JSON
691 vpArray2D<Type> &operator=(const nlohmann::json &j) = delete;
692#endif
693#endif
694
696 inline Type *operator[](unsigned int i) { return rowPtrs[i]; }
698 inline Type *operator[](unsigned int i) const { return rowPtrs[i]; }
699
705 friend std::ostream &operator<<(std::ostream &s, const vpArray2D<Type> &A)
706 {
707 if ((A.data == nullptr) || (A.size() == 0)) {
708 return s;
709 }
710 std::ios_base::fmtflags original_flags = s.flags();
711 const unsigned int precision = 10;
712 s.precision(precision);
713 unsigned int a_rows = A.getRows();
714 unsigned int a_cols = A.getCols();
715 for (unsigned int i = 0; i < a_rows; ++i) {
716 for (unsigned int j = 0; j < (a_cols - 1); ++j) {
717 s << A[i][j] << " ";
718 }
719 // We don't add " " after the last row element
720 s << A[i][a_cols - 1];
721 // We don't add a \n char on the end of the last array line
722 if (i < (a_rows - 1)) {
723 s << std::endl;
724 }
725 }
726
727 s.flags(original_flags); // restore s to standard state
728
729 return s;
730 }
731
733
741
742 //---------------------------------
743 // Inherited array I/O Static Public Member Functions
744 //---------------------------------
747
760 static bool load(const std::string &filename, vpArray2D<Type> &A, bool binary = false, char *header = nullptr)
761 {
762 std::fstream file;
763
764 if (!binary) {
765 file.open(filename.c_str(), std::fstream::in);
766 }
767 else {
768 file.open(filename.c_str(), std::fstream::in | std::fstream::binary);
769 }
770
771 if (!file) {
772 file.close();
773 return false;
774 }
775
776 if (!binary) {
777 std::string h;
778 bool headerIsDecoded = false;
779 do {
780 std::streampos pos = file.tellg();
781 char line[FILENAME_MAX];
782 file.getline(line, FILENAME_MAX);
783 std::string prefix("# ");
784 std::string line_(line);
785 if (line_.compare(0, prefix.size(), prefix.c_str()) == 0) {
786 // Line is a comment
787 // If we are not on the first line, we should add "\n" to the end of
788 // the previous line
789 if (pos) {
790 h += "\n";
791 }
792 h += line_.substr(prefix.size()); // Remove "# "
793 }
794 else {
795 // rewind before the line
796 file.seekg(pos, file.beg);
797 headerIsDecoded = true;
798 }
799 } while (!headerIsDecoded);
800
801 if (header != nullptr) {
802#if defined(__MINGW32__) || \
803 !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
804 snprintf(header, h.size() + 1, "%s", h.c_str());
805#else
806 _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
807#endif
808 }
809
810 unsigned int rows, cols;
811 file >> rows;
812 file >> cols;
813
814 if ((rows >= std::numeric_limits<unsigned int>::max()) || (cols >= std::numeric_limits<unsigned int>::max())) {
815 throw vpException(vpException::badValue, "Array exceed the max size.");
816 }
817
818 A.resize(rows, cols);
819
820 Type value;
821 for (unsigned int i = 0; i < rows; ++i) {
822 for (unsigned int j = 0; j < cols; ++j) {
823 file >> value;
824 A[i][j] = value;
825 }
826 }
827 }
828 else {
829 char c = '0';
830 std::string h;
831 // Decode header until '\0' char that ends the header string
832 while (c != '\0') {
833 file.read(&c, 1);
834 h += c;
835 }
836 if (header != nullptr) {
837#if defined(__MINGW32__) || \
838 !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
839 snprintf(header, h.size() + 1, "%s", h.c_str());
840#else
841 _snprintf_s(header, h.size() + 1, _TRUNCATE, "%s", h.c_str());
842#endif
843 }
844
845 unsigned int rows, cols;
846 file.read(reinterpret_cast<char *>(&rows), sizeof(unsigned int));
847 file.read(reinterpret_cast<char *>(&cols), sizeof(unsigned int));
848 A.resize(rows, cols);
849
850 Type value;
851 for (unsigned int i = 0; i < rows; ++i) {
852 for (unsigned int j = 0; j < cols; ++j) {
853 file.read(reinterpret_cast<char *>(&value), sizeof(Type));
854 A[i][j] = value;
855 }
856 }
857 }
858
859 file.close();
860 return true;
861 }
862
874 static bool loadYAML(const std::string &filename, vpArray2D<Type> &A, char *header = nullptr)
875 {
876 std::fstream file;
877
878 file.open(filename.c_str(), std::fstream::in);
879
880 if (!file) {
881 file.close();
882 return false;
883 }
884
885 unsigned int rows = 0, cols = 0;
886 std::string h;
887 std::string line, subs;
888 bool inheader = true;
889 unsigned int i = 0, j;
890 unsigned int lineStart = 0;
891
892 while (getline(file, line)) {
893 if (inheader) {
894 const std::string str_rows("rows:");
895 const std::string str_cols("cols:");
896 const std::string str_data("data:");
897 if ((rows == 0) && (line.compare(0, str_rows.size(), str_rows.c_str()) == 0)) {
898 std::stringstream ss(line);
899 ss >> subs;
900 ss >> rows;
901 }
902 else if ((cols == 0) && (line.compare(0, str_cols.size(), str_cols.c_str()) == 0)) {
903 std::stringstream ss(line);
904 ss >> subs;
905 ss >> cols;
906 }
907 else if (line.compare(0, str_data.size(), str_data.c_str()) == 0) {
908 inheader = false;
909 }
910 else {
911 h += line + "\n";
912 }
913 }
914 else {
915 // if i == 0, we just got out of the header: initialize matrix
916 // dimensions
917 if (i == 0) {
918 if ((rows == 0) || (cols == 0)) {
919 file.close();
920 return false;
921 }
922 A.resize(rows, cols);
923 // get indentation level which is common to all lines
924 lineStart = static_cast<unsigned int>(line.find("[")) + 1;
925 }
926 std::stringstream ss(line.substr(lineStart, line.find("]") - lineStart));
927 j = 0;
928 while (getline(ss, subs, ',')) {
929 A[i][j++] = static_cast<Type>(atof(subs.c_str()));
930 }
931 ++i;
932 }
933 }
934
935 if (header != nullptr) {
936 std::string h_ = h.substr(0, h.size() - 1); // Remove last '\n' char
937#if defined(__MINGW32__) || \
938 !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
939 snprintf(header, h_.size() + 1, "%s", h_.c_str());
940#else
941 _snprintf_s(header, h_.size() + 1, _TRUNCATE, "%s", h_.c_str());
942#endif
943 }
944
945 file.close();
946 return true;
947 }
948
965 static bool save(const std::string &filename, const vpArray2D<Type> &A, bool binary = false, const char *header = "")
966 {
967 std::fstream file;
968
969 if (!binary) {
970 file.open(filename.c_str(), std::fstream::out);
971 }
972 else {
973 file.open(filename.c_str(), std::fstream::out | std::fstream::binary);
974 }
975
976 if (!file) {
977 file.close();
978 return false;
979 }
980
981 if (!binary) {
982 unsigned int i = 0;
983 file << "# ";
984 while (header[i] != '\0') {
985 file << header[i];
986 if (header[i] == '\n') {
987 file << "# ";
988 }
989 ++i;
990 }
991 file << std::endl;
992 file << A.getRows() << "\t" << A.getCols() << std::endl;
993 file << A << std::endl;
994 }
995 else {
996 int headerSize = 0;
997 while (header[headerSize] != '\0') {
998 ++headerSize;
999 }
1000 file.write(header, static_cast<std::streamsize>(headerSize)+static_cast<std::streamsize>(1));
1001 unsigned int matrixSize;
1002 matrixSize = A.getRows();
1003 file.write(reinterpret_cast<char *>(&matrixSize), sizeof(unsigned int));
1004 matrixSize = A.getCols();
1005 file.write(reinterpret_cast<char *>(&matrixSize), sizeof(unsigned int));
1006 Type value;
1007 unsigned int a_rows = A.getRows();
1008 unsigned int a_cols = A.getCols();
1009 for (unsigned int i = 0; i < a_rows; ++i) {
1010 for (unsigned int j = 0; j < a_cols; ++j) {
1011 value = A[i][j];
1012 file.write(reinterpret_cast<char *>(&value), sizeof(Type));
1013 }
1014 }
1015 }
1016
1017 file.close();
1018 return true;
1019 }
1020
1061 \sa loadYAML()
1062 */
1063 static bool saveYAML(const std::string &filename, const vpArray2D<Type> &A, const char *header = "")
1064 {
1065 std::fstream file;
1066
1067 file.open(filename.c_str(), std::fstream::out);
1068
1069 if (!file) {
1070 file.close();
1071 return false;
1072 }
1073
1074 unsigned int i = 0;
1075 bool inIndent = false;
1076 std::string indent = "";
1077 bool checkIndent = true;
1078 while (header[i] != '\0') {
1079 file << header[i];
1080 if (checkIndent) {
1081 if (inIndent) {
1082 if (header[i] == ' ') {
1083 indent += " ";
1084 }
1085 else if (indent.length() > 0) {
1086 checkIndent = false;
1087 }
1088 }
1089 if ((header[i] == '\n') || (inIndent && (header[i] == ' '))) {
1090 inIndent = true;
1091 }
1092 else {
1093 inIndent = false;
1094 }
1095 }
1096 ++i;
1097 }
1098
1099 if (i != 0) {
1100 file << std::endl;
1101 }
1102 file << "rows: " << A.getRows() << std::endl;
1103 file << "cols: " << A.getCols() << std::endl;
1104
1105 if (indent.length() == 0) {
1106 indent = " ";
1107 }
1108
1109 file << "data: " << std::endl;
1110 unsigned int j;
1111 unsigned int a_rows = A.getRows();
1112 unsigned int a_cols = A.getCols();
1113 for (i = 0; i < a_rows; ++i) {
1114 file << indent << "- [";
1115 for (j = 0; j < (a_cols - 1); ++j) {
1116 file << A[i][j] << ", ";
1117 }
1118 file << A[i][j] << "]" << std::endl;
1119 }
1120
1121 file.close();
1122 return true;
1123 }
1124#ifdef VISP_HAVE_NLOHMANN_JSON
1125 //template<typename Type>
1126 template<class T>
1127 friend void from_json(const nlohmann::json &j, vpArray2D<T> &array);
1128 //template<typename Type>
1129 template<class T>
1130 friend void to_json(nlohmann::json &j, const vpArray2D<T> &array);
1131#endif
1132
1142 \note This is a very basic implementation that does not use FFT.
1143 */
1144 static vpArray2D<Type> conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode);
1145
1158 static void conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode);
1159
1172 vpArray2D<Type> insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c);
1173
1187 static void insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c);
1188 //@}
1189
1190 static bool isFinite(const vpArray2D<double> &A)
1191 {
1192 const unsigned int s = A.size();
1193 for (unsigned int i = 0; i < s; ++i) {
1194 if (!vpMath::isFinite(A.data[i])) {
1195 return false;
1196 }
1197 }
1198 return true;
1199 }
1200
1201protected:
1203 unsigned int rowNum;
1205 unsigned int colNum;
1207 Type **rowPtrs;
1209 unsigned int dsize;
1213 bool isRowPtrsOwner;
1214
1215};
1216
1220template <class Type> Type vpArray2D<Type>::getMinValue() const
1221{
1222 Type *dataptr = data;
1223 Type min = *dataptr;
1224 ++dataptr;
1225 for (unsigned int i = 0; i < (dsize - 1); ++i) {
1226 if (*dataptr < min) {
1227 min = *dataptr;
1228 }
1229 ++dataptr;
1230 }
1231 return min;
1232}
1233
1237template <class Type> Type vpArray2D<Type>::getMaxValue() const
1238{
1239 Type *dataptr = data;
1240 Type max = *dataptr;
1241 ++dataptr;
1242 for (unsigned int i = 0; i < (dsize - 1); ++i) {
1243 if (*dataptr > max) {
1244 max = *dataptr;
1245 }
1246 ++dataptr;
1247 }
1248 return max;
1249}
1250
1257template <class Type> vpArray2D<Type> vpArray2D<Type>::hadamard(const vpArray2D<Type> &m) const
1258{
1259 if ((m.getRows() != rowNum) || (m.getCols() != colNum)) {
1260 throw(vpException(vpException::dimensionError, "Hadamard product: bad dimensions!"));
1261 }
1262
1263 vpArray2D<Type> out;
1264 out.resize(rowNum, colNum, false);
1265
1266 for (unsigned int i = 0; i < dsize; ++i) {
1267 out.data[i] = data[i] * m.data[i];
1268 }
1269
1270 return out;
1271}
1272
1273template <class Type> vpArray2D<Type> vpArray2D<Type>::t() const
1274{
1276 for (unsigned int i = 0; i < rowNum; ++i) {
1277 for (unsigned int j = 0; j < colNum; ++j) {
1278 At[j][i] = (*this)[i][j];
1279 }
1280 }
1281 return At;
1282}
1283
1284template <class Type> vpArray2D<Type> vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, const std::string &mode)
1285{
1286 vpArray2D<Type> res;
1287 conv2(M, kernel, res, mode);
1288 return res;
1289}
1290
1291template <class Type> void vpArray2D<Type>::conv2(const vpArray2D<Type> &M, const vpArray2D<Type> &kernel, vpArray2D<Type> &res, const std::string &mode)
1292{
1293 if (((M.getRows() * M.getCols()) == 0) || ((kernel.getRows() * kernel.getCols()) == 0)) {
1294 return;
1295 }
1296
1297 if (mode == "valid") {
1298 if ((kernel.getRows() > M.getRows()) || (kernel.getCols() > M.getCols())) {
1299 return;
1300 }
1301 }
1302
1303 vpArray2D<Type> M_padded, res_same;
1304
1305 if ((mode == "full") || (mode == "same")) {
1306 const unsigned int pad_x = kernel.getCols() - 1;
1307 const unsigned int pad_y = kernel.getRows() - 1;
1308 const unsigned int pad = 2;
1309 M_padded.resize(M.getRows() + (pad * pad_y), M.getCols() + (pad * pad_x), true, false);
1310 M_padded.insert(M, pad_y, pad_x);
1311
1312 if (mode == "same") {
1313 res.resize(M.getRows(), M.getCols(), false, false);
1314 res_same.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1315 }
1316 else {
1317 res.resize(M.getRows() + pad_y, M.getCols() + pad_x, true, false);
1318 }
1319 }
1320 else if (mode == "valid") {
1321 M_padded = M;
1322 res.resize((M.getRows() - kernel.getRows()) + 1, (M.getCols() - kernel.getCols()) + 1);
1323 }
1324 else {
1325 return;
1326 }
1327
1328 if (mode == "same") {
1329 unsigned int res_same_rows = res_same.getRows();
1330 unsigned int res_same_cols = res_same.getCols();
1331 unsigned int kernel_rows = kernel.getRows();
1332 unsigned int kernel_cols = kernel.getCols();
1333 for (unsigned int i = 0; i < res_same_rows; ++i) {
1334 for (unsigned int j = 0; j < res_same_cols; ++j) {
1335 for (unsigned int k = 0; k < kernel_rows; ++k) {
1336 for (unsigned int l = 0; l < kernel_cols; ++l) {
1337 res_same[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1338 }
1339 }
1340 }
1341 }
1342
1343 const unsigned int start_i = kernel.getRows() / 2;
1344 const unsigned int start_j = kernel.getCols() / 2;
1345 unsigned int m_rows = M.getRows();
1346 for (unsigned int i = 0; i < m_rows; ++i) {
1347 memcpy(res.data + (i * M.getCols()), res_same.data + ((i + start_i) * res_same.getCols()) + start_j,
1348 sizeof(Type) * M.getCols());
1349 }
1350 }
1351 else {
1352 unsigned int res_rows = res.getRows();
1353 unsigned int res_cols = res.getCols();
1354 unsigned int kernel_rows = kernel.getRows();
1355 unsigned int kernel_cols = kernel.getCols();
1356 for (unsigned int i = 0; i < res_rows; ++i) {
1357 for (unsigned int j = 0; j < res_cols; ++j) {
1358 for (unsigned int k = 0; k < kernel_rows; ++k) {
1359 for (unsigned int l = 0; l < kernel_cols; ++l) {
1360 res[i][j] += M_padded[i + k][j + l] * kernel[kernel.getRows() - k - 1][kernel.getCols() - l - 1];
1361 }
1362 }
1363 }
1364 }
1365 }
1366}
1367
1368template<class Type> vpArray2D<Type> vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, unsigned int r, unsigned int c)
1369{
1371
1372 insert(A, B, C, r, c);
1373
1374 return C;
1375}
1376
1377template<class Type> void vpArray2D<Type>::insert(const vpArray2D<Type> &A, const vpArray2D<Type> &B, vpArray2D<Type> &C, unsigned int r, unsigned int c)
1378{
1379 if (((r + B.getRows()) <= A.getRows()) && ((c + B.getCols()) <= A.getCols())) {
1380 C.resize(A.getRows(), A.getCols(), false, false);
1381
1382 unsigned int a_rows = A.getRows();
1383 unsigned int a_cols = A.getCols();
1384 for (unsigned int i = 0; i < a_rows; ++i) {
1385 for (unsigned int j = 0; j < a_cols; ++j) {
1386 if ((i >= r) && (i < (r + B.getRows())) && (j >= c) && (j < (c + B.getCols()))) {
1387 C[i][j] = B[i - r][j - c];
1388 }
1389 else {
1390 C[i][j] = A[i][j];
1391 }
1392 }
1393 }
1394 }
1395 else {
1396 throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) array in (%dx%d) array at position (%d,%d)",
1397 B.getRows(), B.getCols(), A.getCols(), A.getRows(), r, c);
1398 }
1399}
1400
1401template <class Type> bool vpArray2D<Type>::operator==(const vpArray2D<Type> &A) const
1402{
1403 if ((A.rowNum != rowNum) || (A.colNum != colNum)) {
1404 return false;
1405 }
1406
1407 unsigned int a_size = A.size();
1408 for (unsigned int i = 0; i < a_size; ++i) {
1409 if (data[i] != A.data[i]) {
1410 return false;
1411 }
1412 }
1413
1414 return true;
1415}
1416
1420template <> inline bool vpArray2D<double>::operator==(const vpArray2D<double> &A) const
1421{
1422 if ((A.rowNum != rowNum) || (A.colNum != colNum)) {
1423 return false;
1424 }
1425
1426 unsigned int a_size = A.size();
1427 for (unsigned int i = 0; i < a_size; ++i) {
1428 if (fabs(data[i] - A.data[i]) > std::numeric_limits<double>::epsilon()) {
1429 return false;
1430 }
1431 }
1432
1433 return true;
1434}
1435
1439template <> inline bool vpArray2D<float>::operator==(const vpArray2D<float> &A) const
1440{
1441 if ((A.rowNum != rowNum) || (A.colNum != colNum)) {
1442 return false;
1443 }
1444
1445 unsigned int a_size = A.size();
1446 for (unsigned int i = 0; i < a_size; ++i) {
1447 if (fabsf(data[i] - A.data[i]) > std::numeric_limits<float>::epsilon()) {
1448 return false;
1449 }
1450 }
1451
1452 return true;
1453}
1454
1458template <class Type> bool vpArray2D<Type>::operator!=(const vpArray2D<Type> &A) const { return !(*this == A); }
1459
1460#ifdef VISP_HAVE_NLOHMANN_JSON
1461template <class Type>
1462inline void from_json(const nlohmann::json &j, vpArray2D<Type> &array)
1463{
1464 if (j.is_array()) {
1465 const unsigned int nrows = static_cast<unsigned int>(j.size());
1466 if (nrows == 0) { // Initialize an empty array, Finished
1467 array.resize(0, 0);
1468 return;
1469 }
1470 unsigned int ncols = 0;
1471 bool first = true;
1472 for (const auto &item : j) { // Find number of columns, validate that all rows have same number of cols
1473 if (!item.is_array()) {
1474 throw vpException(vpException::badValue, "Trying to instantiate a 2D array with a JSON object that is not an array of array");
1475 }
1476 if (first) {
1477 first = false;
1478 ncols = static_cast<unsigned int>(item.size());
1479 }
1480 else if (ncols != item.size()) {
1481 throw vpException(vpException::badValue, "Trying to instantiate a 2D array with JSON row arrays that are not of the same size");
1482 }
1483 }
1484 array.resize(nrows, ncols);
1485 unsigned i = 0;
1486 for (const auto &item : j) {
1487 std::vector<Type> row = item;
1488 std::copy(row.begin(), row.end(), array.rowPtrs[i]);
1489 ++i;
1490 }
1491 }
1492 else if (j.is_object()) {
1493 const unsigned ncols = j.at("cols");
1494 const unsigned nrows = j.at("rows");
1495 array.resize(nrows, ncols);
1496 const nlohmann::json jData = j.at("data");
1497 if (!jData.is_array() || jData.size() != nrows * ncols) {
1498 std::stringstream ss;
1499 ss << "JSON \"data\" field must be an array of size " << nrows * ncols;
1500 throw vpException(vpException::badValue, ss.str());
1501 }
1502 unsigned i = 0;
1503 for (const auto &jValue : jData) {
1504 array.data[i] = jValue;
1505 ++i;
1506 }
1507 }
1508 else {
1509 throw vpException(vpException::badValue, "Trying to read a vpArray2D from something that is not an array or object");
1510 }
1511}
1512
1513
1514template <class Type>
1515inline void to_json(nlohmann::json &j, const vpArray2D<Type> &array)
1516{
1517 j = {
1518 {"cols", array.colNum},
1519 {"rows", array.rowNum},
1520 {"type", "vpArray2D"}
1521 };
1522
1523 nlohmann::json::array_t data;
1524 data.reserve(array.size());
1525 for (unsigned i = 0; i < array.size(); ++i) {
1526 data.push_back(array.data[i]);
1527 }
1528 j["data"] = data;
1529}
1530#endif
1531
1532END_VISP_NAMESPACE
1533
1534#endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition vpArray2D.h:146
vpArray2D(vpArray2D< Type > &&A) noexcept
Definition vpArray2D.h:261
unsigned int getCols() const
Definition vpArray2D.h:423
static void view(vpArray2D< Type > &v, Type *data, unsigned int numRows, unsigned int numCols)
Create an array view of a raw data pointer. After this function has been called, the array data can b...
Definition vpArray2D.h:363
Type * operator[](unsigned int i)
Set element using A[i][j] = x.
Definition vpArray2D.h:696
Type * data
Address of the first element of the data array.
Definition vpArray2D.h:149
vpArray2D(const vpArray2D< Type > &A)
Definition vpArray2D.h:160
bool isRowPtrsOwner
Whether this array owns the row pointers.
Definition vpArray2D.h:1211
static bool loadYAML(const std::string &filename, vpArray2D< Type > &A, char *header=nullptr)
Definition vpArray2D.h:874
Type ** rowPtrs
Address of the first element of each rows.
Definition vpArray2D.h:1205
vpArray2D(const std::vector< Type > &vec, unsigned int r=0, unsigned int c=0)
Definition vpArray2D.h:219
vpArray2D< Type > & operator=(Type x)
Set all the elements of the array to x.
Definition vpArray2D.h:615
void insert(const vpArray2D< Type > &A, unsigned int r, unsigned int c)
Definition vpArray2D.h:586
Type getMinValue() const
Definition vpArray2D.h:1220
vpArray2D< Type > & operator=(const std::initializer_list< Type > &list)
Definition vpArray2D.h:662
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition vpArray2D.h:448
vpArray2D< Type > & operator=(const vpArray2D< Type > &A)
Definition vpArray2D.h:624
Type * operator[](unsigned int i) const
Get element using x = A[i][j].
Definition vpArray2D.h:698
static bool saveYAML(const std::string &filename, const vpArray2D< Type > &A, const char *header="")
Definition vpArray2D.h:1061
static vpArray2D< Type > view(const vpArray2D< Type > &A)
Creates a view of the Matrix A. A view shares the same underlying memory as the original array....
Definition vpArray2D.h:324
unsigned int rowNum
Number of rows in the array.
Definition vpArray2D.h:1201
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition vpArray2D.h:705
static vpArray2D< Type > view(Type *data, unsigned int numRows, unsigned int numCols)
Create an array view of a raw data pointer. This data is not owned by the resulting array and should ...
Definition vpArray2D.h:346
vpArray2D< Type > & operator=(const nlohmann::json &j)=delete
friend void to_json(nlohmann::json &j, const vpArray2D< T > &array)
VP_EXPLICIT vpArray2D(unsigned int nrows, unsigned int ncols, const std::initializer_list< Type > &list)
Definition vpArray2D.h:284
static vpArray2D< Type > conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, const std::string &mode)
Definition vpArray2D.h:1284
unsigned int dsize
Current array size (rowNum * colNum).
Definition vpArray2D.h:1207
unsigned int size() const
Return the number of elements of the 2D array.
Definition vpArray2D.h:435
VP_EXPLICIT vpArray2D(const std::initializer_list< Type > &list)
Definition vpArray2D.h:278
static bool load(const std::string &filename, vpArray2D< Type > &A, bool binary=false, char *header=nullptr)
Definition vpArray2D.h:760
VP_EXPLICIT vpArray2D(const std::initializer_list< std::initializer_list< Type > > &lists)
Definition vpArray2D.h:297
vpArray2D< Type > & operator=(const std::initializer_list< std::initializer_list< Type > > &lists)
Definition vpArray2D.h:672
vpArray2D< Type > & operator=(vpArray2D< Type > &&other) noexcept
Definition vpArray2D.h:634
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition vpArray2D.h:1273
static bool isFinite(const vpArray2D< double > &A)
Definition vpArray2D.h:1188
unsigned int getRows() const
Definition vpArray2D.h:433
bool operator!=(const vpArray2D< Type > &A) const
Definition vpArray2D.h:1458
bool isMemoryOwner
Whether this array owns the memory it points to.
Definition vpArray2D.h:1209
vpArray2D(unsigned int r, unsigned int c, Type val)
Definition vpArray2D.h:196
vpArray2D(unsigned int r, unsigned int c)
Definition vpArray2D.h:178
virtual ~vpArray2D()
Definition vpArray2D.h:396
vpArray2D< Type > hadamard(const vpArray2D< Type > &m) const
Definition vpArray2D.h:1257
Type getMaxValue() const
Definition vpArray2D.h:1237
static bool save(const std::string &filename, const vpArray2D< Type > &A, bool binary=false, const char *header="")
Definition vpArray2D.h:965
friend void from_json(const nlohmann::json &j, vpArray2D< T > &array)
void reshape(unsigned int nrows, unsigned int ncols)
Definition vpArray2D.h:545
unsigned int colNum
Number of columns in the array.
Definition vpArray2D.h:1203
bool operator==(const vpArray2D< Type > &A) const
Definition vpArray2D.h:1401
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
@ dimensionError
Bad dimension.
Definition vpException.h:71
@ memoryAllocationError
Memory allocation error.
Definition vpException.h:64
static bool isFinite(double value)
Definition vpMath.cpp:198