From 7a2540d2aa5080d4091572c02d2ac4372f3fe8d0 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Fri, 8 May 2020 02:11:45 -0400 Subject: [PATCH 01/14] add eigen tensor conversion functions --- modules/core/include/opencv2/core/eigen.hpp | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 741648edb8fb..2a6f2b2ac8ae 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -46,6 +46,7 @@ #define OPENCV_CORE_EIGEN_HPP #include "opencv2/core.hpp" +#include #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined @@ -59,6 +60,21 @@ namespace cv //! @addtogroup core_eigen //! @{ +/** + * Convert a 3-dimensional Eigen::Tensor of size H x W x C to a cv::Mat with C channels + * where: + * H = number of rows + * W = number of columns + * C = number of channels + */ +template static inline +void eigen2cv( const Eigen::Tensor<_Tp, 3, Eigen::RowMajor> &src, OutputArray dst ) +{ + Mat _src(src.dimension(0), src.dimension(1), + CV_MAKE_TYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); + _src.copyTo(dst); +} + template static inline void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) { @@ -91,6 +107,24 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo } } +/** + * Convert a cv::Mat containing C channels to a 3-dimensional Eigen::Tensor of size H x W x C + * where: + * H = number of rows + * W = number of columns + * C = number of channels + */ +template static inline +void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, Eigen::RowMajor> &dst ) +{ + dst.resize(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKE_TYPE(DataType<_Tp>::type, src.channels()), dst.data()); + if (src.type() == _dst.type()) + src.copyTo(_dst); + else + src.convertTo(_dst, _dst.type()); +} + template static inline void cv2eigen( const Mat& src, Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst ) From 6ea7f340c8a024616bd56893300b9fc5b6251c4b Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Fri, 8 May 2020 02:12:12 -0400 Subject: [PATCH 02/14] add eigen tensor conversion tests --- modules/core/test/test_mat.cpp | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 58eafd074821..bf5d9e6766e5 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2084,6 +2084,48 @@ TEST(Core_Eigen, eigen2cv_check_Mat_type) } #endif // HAVE_EIGEN +#ifdef HAVE_EIGEN +TEST(Core_Eigen, cv2eigen_check_tensor_conversion) +{ + Mat A(2, 3, CV_32FC3); + float value = 0; + for(int row=0; row(row,col)[ch] = value++; + + Eigen::Tensor eigen_A; + cv2eigen(A, eigen_A); + + value = 0; + for(int row=0; row A_eigen(2,3,3); + float value = 0; + for(int row=0; row(row,col)[ch]); +} +#endif // HAVE_EIGEN + TEST(Mat, regression_12943) // memory usage: ~4.5 Gb { applyTestTag(CV_TEST_TAG_MEMORY_6GB); From c0c457ce1726c892b9bf4db5d1ebf80160816bf8 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Mon, 18 May 2020 20:11:22 -0400 Subject: [PATCH 03/14] add support for column major order --- modules/core/include/opencv2/core/eigen.hpp | 43 ++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 2a6f2b2ac8ae..7e6d59b02710 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -67,12 +67,18 @@ namespace cv * W = number of columns * C = number of channels */ -template static inline -void eigen2cv( const Eigen::Tensor<_Tp, 3, Eigen::RowMajor> &src, OutputArray dst ) +template static inline +void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) { - Mat _src(src.dimension(0), src.dimension(1), - CV_MAKE_TYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); - _src.copyTo(dst); + if (_layout & Eigen::RowMajorBit) { + Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); + _src.copyTo(dst); + } else { + const std::array shuffle{2, 1, 0}; + Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor = src.swap_layout().shuffle(shuffle); + Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), row_major_tensor.data()); + _src.copyTo(dst); + } } template static inline @@ -114,15 +120,26 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo * W = number of columns * C = number of channels */ -template static inline -void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, Eigen::RowMajor> &dst ) +template static inline +void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) { - dst.resize(src.rows, src.cols, src.channels()); - Mat _dst(src.rows, src.cols, CV_MAKE_TYPE(DataType<_Tp>::type, src.channels()), dst.data()); - if (src.type() == _dst.type()) - src.copyTo(_dst); - else - src.convertTo(_dst, _dst.type()); + if (_layout & Eigen::RowMajorBit) { + dst.resize(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), dst.data()); + if (src.type() == _dst.type()) + src.copyTo(_dst); + else + src.convertTo(_dst, _dst.type()); + } else { + Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), row_major_tensor.data()); + if (src.type() == _dst.type()) + src.copyTo(_dst); + else + src.convertTo(_dst, _dst.type()); + const std::array shuffle{2, 1, 0}; + dst = row_major_tensor.swap_layout().shuffle(shuffle); + } } template static inline From 8a1eed3c3287a0e5878fd4186663fb94262db55a Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Mon, 18 May 2020 20:11:46 -0400 Subject: [PATCH 04/14] update eigen tensor tests --- modules/core/test/test_mat.cpp | 45 ++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index bf5d9e6766e5..663e47b3fb78 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2094,35 +2094,54 @@ TEST(Core_Eigen, cv2eigen_check_tensor_conversion) for(int ch=0; ch(row,col)[ch] = value++; - Eigen::Tensor eigen_A; - cv2eigen(A, eigen_A); + Eigen::Tensor row_tensor; + cv2eigen(A, row_tensor); + + float* mat_ptr = (float*)A.data; + float* tensor_ptr = row_tensor.data(); + for (int i=0; i< row_tensor.size(); i++) + ASSERT_FLOAT_EQ(mat_ptr[i], tensor_ptr[i]); + Eigen::Tensor col_tensor; + cv2eigen(A, col_tensor); value = 0; for(int row=0; row A_eigen(2,3,3); + Eigen::Tensor row_tensor(2,3,3); + Eigen::Tensor col_tensor(2,3,3); float value = 0; - for(int row=0; row(row,col)[ch]); + for(int row=0; row(row,col)[ch]); } #endif // HAVE_EIGEN From 112b0178906ea93ca765d7d72a55caed539c1bfb Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Tue, 19 May 2020 16:13:27 -0400 Subject: [PATCH 05/14] fix coding style and add conditional compilation --- modules/core/include/opencv2/core/eigen.hpp | 67 +++++++++++++++------ modules/core/test/test_mat.cpp | 13 ++-- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 7e6d59b02710..77c2ae3f8a3d 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -46,40 +46,57 @@ #define OPENCV_CORE_EIGEN_HPP #include "opencv2/core.hpp" + +#if EIGEN_VERSION_AT_LEAST(3,3,0) #include +#endif // EIGEN_VERSION_AT_LEAST(3,3,0) #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined #pragma warning( disable: 4127 ) //conditional expression is constant #pragma warning( disable: 4244 ) //conversion from '__int64' to 'int', possible loss of data #endif - + namespace cv { //! @addtogroup core_eigen //! @{ -/** - * Convert a 3-dimensional Eigen::Tensor of size H x W x C to a cv::Mat with C channels - * where: - * H = number of rows - * W = number of columns - * C = number of channels - */ + +#if EIGEN_VERSION_AT_LEAST(3, 3, 0) +/** @brief Converts an Eigen::Tensor to a cv::Mat. + + The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: + H = number of rows + W = number of columns + C = number of channels + + Usage: + \code + Eigen::Tensor a_tensor(...); + // populate tensor with values + cv::Mat a_mat; + eigen2cv(a_tensor, a_mat); + \endcode +*/ template static inline void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) { - if (_layout & Eigen::RowMajorBit) { + if (_layout & Eigen::RowMajorBit) + { Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); _src.copyTo(dst); - } else { + } + else + { const std::array shuffle{2, 1, 0}; Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor = src.swap_layout().shuffle(shuffle); Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), row_major_tensor.data()); _src.copyTo(dst); } } +#endif // EIGEN_VERSION_AT_LEAST(3,3,0) template static inline void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) @@ -113,24 +130,35 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo } } -/** - * Convert a cv::Mat containing C channels to a 3-dimensional Eigen::Tensor of size H x W x C - * where: - * H = number of rows - * W = number of columns - * C = number of channels - */ +#if EIGEN_VERSION_AT_LEAST(3, 3, 0) +/** @brief Converts a cv::Mat to an Eigen::Tensor. + + The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: + H = number of rows + W = number of columns + C = number of channels + + Usage: + \code + cv::Mat a_mat(...); + // populate Mat with values + Eigen::Tensor a_tensor(...); + cv2eigen(a_mat, a_tensor); +*/ template static inline void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) { - if (_layout & Eigen::RowMajorBit) { + if (_layout & Eigen::RowMajorBit) + { dst.resize(src.rows, src.cols, src.channels()); Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), dst.data()); if (src.type() == _dst.type()) src.copyTo(_dst); else src.convertTo(_dst, _dst.type()); - } else { + } + else + { Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor(src.rows, src.cols, src.channels()); Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), row_major_tensor.data()); if (src.type() == _dst.type()) @@ -141,6 +169,7 @@ void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) dst = row_major_tensor.swap_layout().shuffle(shuffle); } } +#endif // EIGEN_VERSION_AT_LEAST(3,3,0) template static inline void cv2eigen( const Mat& src, diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 663e47b3fb78..57b42ed44206 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2083,8 +2083,8 @@ TEST(Core_Eigen, eigen2cv_check_Mat_type) //EXPECT_EQ(CV_64FC1, d_mat.type()); } #endif // HAVE_EIGEN - -#ifdef HAVE_EIGEN + +#if EIGEN_VERSION_AT_LEAST(3, 3, 0) TEST(Core_Eigen, cv2eigen_check_tensor_conversion) { Mat A(2, 3, CV_32FC3); @@ -2110,9 +2110,9 @@ TEST(Core_Eigen, cv2eigen_check_tensor_conversion) for(int ch=0; ch row_tensor(2,3,3); @@ -2120,7 +2120,8 @@ TEST(Core_Eigen, eigen2cv_check_tensor_conversion) float value = 0; for(int row=0; row(row,col)[ch]); } -#endif // HAVE_EIGEN +#endif // EIGEN_VERSION_AT_LEAST(3, 3, 0) TEST(Mat, regression_12943) // memory usage: ~4.5 Gb { From fc760749100e2512080362b16ff771dabe9cbe47 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Tue, 19 May 2020 16:45:48 -0400 Subject: [PATCH 06/14] fix conditional compilation checks --- modules/core/include/opencv2/core/eigen.hpp | 12 ++++++------ modules/core/test/test_mat.cpp | 12 ++++++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 77c2ae3f8a3d..a2d455c0ece3 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -47,9 +47,9 @@ #include "opencv2/core.hpp" -#if EIGEN_VERSION_AT_LEAST(3,3,0) +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 #include -#endif // EIGEN_VERSION_AT_LEAST(3,3,0) +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined @@ -64,7 +64,7 @@ namespace cv //! @{ -#if EIGEN_VERSION_AT_LEAST(3, 3, 0) +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 /** @brief Converts an Eigen::Tensor to a cv::Mat. The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: @@ -96,7 +96,7 @@ void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) _src.copyTo(dst); } } -#endif // EIGEN_VERSION_AT_LEAST(3,3,0) +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 template static inline void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) @@ -130,7 +130,7 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo } } -#if EIGEN_VERSION_AT_LEAST(3, 3, 0) +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 /** @brief Converts a cv::Mat to an Eigen::Tensor. The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: @@ -169,7 +169,7 @@ void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) dst = row_major_tensor.swap_layout().shuffle(shuffle); } } -#endif // EIGEN_VERSION_AT_LEAST(3,3,0) +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 template static inline void cv2eigen( const Mat& src, diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 57b42ed44206..0e49978a0f6a 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2084,7 +2084,8 @@ TEST(Core_Eigen, eigen2cv_check_Mat_type) } #endif // HAVE_EIGEN -#if EIGEN_VERSION_AT_LEAST(3, 3, 0) +#ifdef HAVE_EIGEN +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 TEST(Core_Eigen, cv2eigen_check_tensor_conversion) { Mat A(2, 3, CV_32FC3); @@ -2110,9 +2111,11 @@ TEST(Core_Eigen, cv2eigen_check_tensor_conversion) for(int ch=0; ch= 3 && EIGEN_MAJOR_VERSION >= 3 +#endif // HAVE_EIGEN -#if EIGEN_VERSION_AT_LEAST(3, 3, 0) +#ifdef HAVE_EIGEN +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 TEST(Core_Eigen, eigen2cv_check_tensor_conversion) { Eigen::Tensor row_tensor(2,3,3); @@ -2144,7 +2147,8 @@ TEST(Core_Eigen, eigen2cv_check_tensor_conversion) for(int ch=0; ch(row,col)[ch]); } -#endif // EIGEN_VERSION_AT_LEAST(3, 3, 0) +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#endif // HAVE_EIGEN TEST(Mat, regression_12943) // memory usage: ~4.5 Gb { From d5d5d5fb9b49b8389f2ada185a4d42a5468500a8 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Tue, 19 May 2020 16:54:46 -0400 Subject: [PATCH 07/14] remove whitespace --- modules/core/include/opencv2/core/eigen.hpp | 6 +++--- modules/core/test/test_mat.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index a2d455c0ece3..97b0caa31665 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -56,7 +56,7 @@ #pragma warning( disable: 4127 ) //conditional expression is constant #pragma warning( disable: 4244 ) //conversion from '__int64' to 'int', possible loss of data #endif - + namespace cv { @@ -66,7 +66,7 @@ namespace cv #if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 /** @brief Converts an Eigen::Tensor to a cv::Mat. - + The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: H = number of rows W = number of columns @@ -137,7 +137,7 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo H = number of rows W = number of columns C = number of channels - + Usage: \code cv::Mat a_mat(...); diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 0e49978a0f6a..ac81a7c45daf 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2083,7 +2083,7 @@ TEST(Core_Eigen, eigen2cv_check_Mat_type) //EXPECT_EQ(CV_64FC1, d_mat.type()); } #endif // HAVE_EIGEN - + #ifdef HAVE_EIGEN #if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 TEST(Core_Eigen, cv2eigen_check_tensor_conversion) From 776a2d538960665d8d13c742c13031b3a08ad8e2 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Tue, 19 May 2020 23:49:13 -0400 Subject: [PATCH 08/14] rearrange functions for easier reading --- modules/core/include/opencv2/core/eigen.hpp | 85 ++++++++++----------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 97b0caa31665..5c73249c34b8 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -63,7 +63,6 @@ namespace cv //! @addtogroup core_eigen //! @{ - #if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 /** @brief Converts an Eigen::Tensor to a cv::Mat. @@ -83,54 +82,20 @@ namespace cv template static inline void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) { - if (_layout & Eigen::RowMajorBit) - { - Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); - _src.copyTo(dst); - } - else + if( !(_layout & Eigen::RowMajorBit) ) { const std::array shuffle{2, 1, 0}; Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor = src.swap_layout().shuffle(shuffle); Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), row_major_tensor.data()); _src.copyTo(dst); } -} -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 - -template static inline -void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) -{ - if( !(src.Flags & Eigen::RowMajorBit) ) - { - Mat _src(src.cols(), src.rows(), traits::Type<_Tp>::value, - (void*)src.data(), src.outerStride()*sizeof(_Tp)); - transpose(_src, dst); - } else { - Mat _src(src.rows(), src.cols(), traits::Type<_Tp>::value, - (void*)src.data(), src.outerStride()*sizeof(_Tp)); + Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); _src.copyTo(dst); } } -// Matx case -template static inline -void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, - Matx<_Tp, _rows, _cols>& dst ) -{ - if( !(src.Flags & Eigen::RowMajorBit) ) - { - dst = Matx<_Tp, _cols, _rows>(static_cast(src.data())).t(); - } - else - { - dst = Matx<_Tp, _rows, _cols>(static_cast(src.data())); - } -} - -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 /** @brief Converts a cv::Mat to an Eigen::Tensor. The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: @@ -148,29 +113,61 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo template static inline void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) { - if (_layout & Eigen::RowMajorBit) + if( !(_layout & Eigen::RowMajorBit) ) { - dst.resize(src.rows, src.cols, src.channels()); - Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), dst.data()); + Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), row_major_tensor.data()); if (src.type() == _dst.type()) src.copyTo(_dst); else src.convertTo(_dst, _dst.type()); + const std::array shuffle{2, 1, 0}; + dst = row_major_tensor.swap_layout().shuffle(shuffle); } else { - Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor(src.rows, src.cols, src.channels()); - Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), row_major_tensor.data()); + dst.resize(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), dst.data()); if (src.type() == _dst.type()) src.copyTo(_dst); else src.convertTo(_dst, _dst.type()); - const std::array shuffle{2, 1, 0}; - dst = row_major_tensor.swap_layout().shuffle(shuffle); } } #endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +template static inline +void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) +{ + if( !(src.Flags & Eigen::RowMajorBit) ) + { + Mat _src(src.cols(), src.rows(), traits::Type<_Tp>::value, + (void*)src.data(), src.outerStride()*sizeof(_Tp)); + transpose(_src, dst); + } + else + { + Mat _src(src.rows(), src.cols(), traits::Type<_Tp>::value, + (void*)src.data(), src.outerStride()*sizeof(_Tp)); + _src.copyTo(dst); + } +} + +// Matx case +template static inline +void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, + Matx<_Tp, _rows, _cols>& dst ) +{ + if( !(src.Flags & Eigen::RowMajorBit) ) + { + dst = Matx<_Tp, _cols, _rows>(static_cast(src.data())).t(); + } + else + { + dst = Matx<_Tp, _rows, _cols>(static_cast(src.data())); + } +} + template static inline void cv2eigen( const Mat& src, Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst ) From c477a0885d7c16fa9214e1789cfad6a66e2e1463 Mon Sep 17 00:00:00 2001 From: Joshua Bradley Date: Wed, 20 May 2020 01:13:42 -0400 Subject: [PATCH 09/14] reformat function documentation and add tensormap unit test --- modules/core/include/opencv2/core/eigen.hpp | 70 ++++++++++++++------- modules/core/test/test_mat.cpp | 20 ++++++ 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 5c73249c34b8..1fcfbb672a39 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -66,18 +66,18 @@ namespace cv #if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 /** @brief Converts an Eigen::Tensor to a cv::Mat. - The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: - H = number of rows - W = number of columns - C = number of channels - - Usage: - \code - Eigen::Tensor a_tensor(...); - // populate tensor with values - cv::Mat a_mat; - eigen2cv(a_tensor, a_mat); - \endcode +The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: + H = number of rows + W = number of columns + C = number of channels + +Usage: +\code +Eigen::Tensor a_tensor(...); +// populate tensor with values +cv::Mat a_mat; +eigen2cv(a_tensor, a_mat); +\endcode */ template static inline void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) @@ -98,17 +98,18 @@ void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) /** @brief Converts a cv::Mat to an Eigen::Tensor. - The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: - H = number of rows - W = number of columns - C = number of channels - - Usage: - \code - cv::Mat a_mat(...); - // populate Mat with values - Eigen::Tensor a_tensor(...); - cv2eigen(a_mat, a_tensor); +The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: + H = number of rows + W = number of columns + C = number of channels + +Usage: +\code +cv::Mat a_mat(...); +// populate Mat with values +Eigen::Tensor a_tensor(...); +cv2eigen(a_mat, a_tensor); +\endcode */ template static inline void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) @@ -134,6 +135,29 @@ void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) src.convertTo(_dst, _dst.type()); } } + +/** @brief Maps cv::Mat data to an Eigen::TensorMap. + +The method wraps an existing Mat data array with an Eigen TensorMap of shape (H x W x C) where: + H = number of rows + W = number of columns + C = number of channels + +Explicit instantiation of the return type is required. + +The example below initializes a cv::Mat and produces an Eigen::TensorMap: +\code +float arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; +cv::Mat a_mat(2, 2, CV_32FC3, arr); +Eigen::TensorMap> color_tensor = cv2eigen_tensormap(a_mat); +\endcode +*/ +template static inline +Eigen::TensorMap> cv2eigen_tensormap(const cv::InputArray &src) +{ + cv::Mat mat = src.getMat(); + return Eigen::TensorMap>((_Tp *)mat.data, mat.rows, mat.cols, mat.channels()); +} #endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 template static inline diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index ac81a7c45daf..d1139e45eb19 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2150,6 +2150,26 @@ TEST(Core_Eigen, eigen2cv_check_tensor_conversion) #endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 #endif // HAVE_EIGEN +#ifdef HAVE_EIGEN +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +TEST(Core_Eigen, cv2eigen_tensormap_check_tensormap_access) +{ + float arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + Mat a_mat(2, 2, CV_32FC3, arr); + Eigen::TensorMap> a_tensor = cv2eigen_tensormap(a_mat); + + for(int i=0; i(row,col)[ch], a_tensor(i,j,ch)); + ASSERT_EQ(&a_mat.at(row,col)[ch], &a_tensor(i,j,ch)) + } + } + } +} +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#endif // HAVE_EIGEN + TEST(Mat, regression_12943) // memory usage: ~4.5 Gb { applyTestTag(CV_TEST_TAG_MEMORY_6GB); From b8a97b3f6d4ee40be4e7c774fd366ab622896d97 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Wed, 20 May 2020 01:44:27 -0400 Subject: [PATCH 10/14] cleanup documentation of unit test --- modules/core/include/opencv2/core/eigen.hpp | 10 +++++----- modules/core/test/test_mat.cpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 1fcfbb672a39..fee937f73ec9 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -75,7 +75,7 @@ The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: \code Eigen::Tensor a_tensor(...); // populate tensor with values -cv::Mat a_mat; +Mat a_mat; eigen2cv(a_tensor, a_mat); \endcode */ @@ -105,7 +105,7 @@ The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: Usage: \code -cv::Mat a_mat(...); +Mat a_mat(...); // populate Mat with values Eigen::Tensor a_tensor(...); cv2eigen(a_mat, a_tensor); @@ -148,14 +148,14 @@ Explicit instantiation of the return type is required. The example below initializes a cv::Mat and produces an Eigen::TensorMap: \code float arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; -cv::Mat a_mat(2, 2, CV_32FC3, arr); -Eigen::TensorMap> color_tensor = cv2eigen_tensormap(a_mat); +Mat a_mat(2, 2, CV_32FC3, arr); +Eigen::TensorMap> a_tensormap = cv2eigen_tensormap(a_mat); \endcode */ template static inline Eigen::TensorMap> cv2eigen_tensormap(const cv::InputArray &src) { - cv::Mat mat = src.getMat(); + Mat mat = src.getMat(); return Eigen::TensorMap>((_Tp *)mat.data, mat.rows, mat.cols, mat.channels()); } #endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index d1139e45eb19..4f8163985140 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2161,8 +2161,8 @@ TEST(Core_Eigen, cv2eigen_tensormap_check_tensormap_access) for(int i=0; i(row,col)[ch], a_tensor(i,j,ch)); - ASSERT_EQ(&a_mat.at(row,col)[ch], &a_tensor(i,j,ch)) + ASSERT_FLOAT_EQ(a_mat.at(i,j)[ch], a_tensor(i,j,ch)); + ASSERT_EQ(&a_mat.at(i,j)[ch], &a_tensor(i,j,ch)); } } } From 24e0252752d46237984e826abc1210be6872f87a Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Thu, 21 May 2020 11:44:22 -0400 Subject: [PATCH 11/14] remove condition duplication --- modules/core/include/opencv2/core/eigen.hpp | 9 +++++---- modules/core/test/test_mat.cpp | 18 ++++++------------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index fee937f73ec9..1ae4342e15d8 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -47,9 +47,10 @@ #include "opencv2/core.hpp" -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MINOR_VERSION >= 3 #include -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#define OPENCV_EIGEN_TENSOR_SUPPORT +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MINOR_VERSION >= 3 #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined @@ -63,7 +64,7 @@ namespace cv //! @addtogroup core_eigen //! @{ -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#ifdef OPENCV_EIGEN_TENSOR_SUPPORT /** @brief Converts an Eigen::Tensor to a cv::Mat. The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: @@ -158,7 +159,7 @@ Eigen::TensorMap> cv2eigen_tensormap(cons Mat mat = src.getMat(); return Eigen::TensorMap>((_Tp *)mat.data, mat.rows, mat.cols, mat.channels()); } -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#endif // OPENCV_EIGEN_TENSOR_SUPPORT template static inline void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 4f8163985140..43e0078ec8e0 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2084,8 +2084,7 @@ TEST(Core_Eigen, eigen2cv_check_Mat_type) } #endif // HAVE_EIGEN -#ifdef HAVE_EIGEN -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#ifdef OPENCV_EIGEN_TENSOR_SUPPORT TEST(Core_Eigen, cv2eigen_check_tensor_conversion) { Mat A(2, 3, CV_32FC3); @@ -2111,11 +2110,9 @@ TEST(Core_Eigen, cv2eigen_check_tensor_conversion) for(int ch=0; ch= 3 && EIGEN_MAJOR_VERSION >= 3 -#endif // HAVE_EIGEN +#endif // OPENCV_EIGEN_TENSOR_SUPPORT -#ifdef HAVE_EIGEN -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#ifdef OPENCV_EIGEN_TENSOR_SUPPORT TEST(Core_Eigen, eigen2cv_check_tensor_conversion) { Eigen::Tensor row_tensor(2,3,3); @@ -2147,11 +2144,9 @@ TEST(Core_Eigen, eigen2cv_check_tensor_conversion) for(int ch=0; ch(row,col)[ch]); } -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 -#endif // HAVE_EIGEN +#endif // OPENCV_EIGEN_TENSOR_SUPPORT -#ifdef HAVE_EIGEN -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#ifdef OPENCV_EIGEN_TENSOR_SUPPORT TEST(Core_Eigen, cv2eigen_tensormap_check_tensormap_access) { float arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; @@ -2167,8 +2162,7 @@ TEST(Core_Eigen, cv2eigen_tensormap_check_tensormap_access) } } } -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 -#endif // HAVE_EIGEN +#endif // OPENCV_EIGEN_TENSOR_SUPPORT TEST(Mat, regression_12943) // memory usage: ~4.5 Gb { From 021a5c5c80f36eaf39a6c4808283c94d0bba0783 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Thu, 21 May 2020 11:58:51 -0400 Subject: [PATCH 12/14] check Eigen major version, not minor version --- modules/core/include/opencv2/core/eigen.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 1ae4342e15d8..aac168eabfe7 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -47,10 +47,10 @@ #include "opencv2/core.hpp" -#if EIGEN_WORLD_VERSION >= 3 && EIGEN_MINOR_VERSION >= 3 +#if EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 #include #define OPENCV_EIGEN_TENSOR_SUPPORT -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MINOR_VERSION >= 3 +#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined From 486e03305d204ad220ea07dd054acdb40f3ac8ac Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Thu, 21 May 2020 12:20:24 -0400 Subject: [PATCH 13/14] restrict to Eigen v3.3.0+ --- modules/core/include/opencv2/core/eigen.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index aac168eabfe7..ea20cf2e38aa 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -50,7 +50,7 @@ #if EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 #include #define OPENCV_EIGEN_TENSOR_SUPPORT -#endif // EIGEN_WORLD_VERSION >= 3 && EIGEN_MAJOR_VERSION >= 3 +#endif // EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined From e6cb44f35c9f00e443a6ccbed7954d1bc9626349 Mon Sep 17 00:00:00 2001 From: Josh Bradley Date: Fri, 22 May 2020 16:50:08 -0400 Subject: [PATCH 14/14] add documentation note and add type checking to cv2eigen_tensormap() --- modules/core/include/opencv2/core/eigen.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index ea20cf2e38aa..8c250efe4cd7 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -146,6 +146,9 @@ The method wraps an existing Mat data array with an Eigen TensorMap of shape (H Explicit instantiation of the return type is required. +@note Caller should be aware of the lifetime of the cv::Mat instance and take appropriate safety measures. +The cv::Mat instance will retain ownership of the data and the Eigen::TensorMap will lose access when the cv::Mat data is deallocated. + The example below initializes a cv::Mat and produces an Eigen::TensorMap: \code float arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; @@ -156,8 +159,9 @@ Eigen::TensorMap> a_tensormap = cv2eige template static inline Eigen::TensorMap> cv2eigen_tensormap(const cv::InputArray &src) { - Mat mat = src.getMat(); - return Eigen::TensorMap>((_Tp *)mat.data, mat.rows, mat.cols, mat.channels()); + Mat mat = src.getMat(); + CV_CheckTypeEQ(mat.type(), CV_MAKETYPE(traits::Type<_Tp>::value, mat.channels()), ""); + return Eigen::TensorMap>((_Tp *)mat.data, mat.rows, mat.cols, mat.channels()); } #endif // OPENCV_EIGEN_TENSOR_SUPPORT