diff --git a/modules/core/include/opencv2/core/eigen.hpp b/modules/core/include/opencv2/core/eigen.hpp index 741648edb8fb..8c250efe4cd7 100644 --- a/modules/core/include/opencv2/core/eigen.hpp +++ b/modules/core/include/opencv2/core/eigen.hpp @@ -47,6 +47,11 @@ #include "opencv2/core.hpp" +#if EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 +#include +#define OPENCV_EIGEN_TENSOR_SUPPORT +#endif // EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 + #if defined _MSC_VER && _MSC_VER >= 1200 #pragma warning( disable: 4714 ) //__forceinline is not inlined #pragma warning( disable: 4127 ) //conditional expression is constant @@ -59,6 +64,107 @@ namespace cv //! @addtogroup core_eigen //! @{ +#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: + H = number of rows + W = number of columns + C = number of channels + +Usage: +\code +Eigen::Tensor a_tensor(...); +// populate tensor with values +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) ) + { + 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); + } + else + { + Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); + _src.copyTo(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 +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 ) +{ + if( !(_layout & Eigen::RowMajorBit) ) + { + 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 + { + 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()); + } +} + +/** @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. + +@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}; +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) +{ + 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 + 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 58eafd074821..43e0078ec8e0 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2084,6 +2084,86 @@ TEST(Core_Eigen, eigen2cv_check_Mat_type) } #endif // HAVE_EIGEN +#ifdef OPENCV_EIGEN_TENSOR_SUPPORT +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 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 row_tensor(2,3,3); + Eigen::Tensor col_tensor(2,3,3); + float value = 0; + for(int row=0; row(row,col)[ch]); +} +#endif // OPENCV_EIGEN_TENSOR_SUPPORT + +#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}; + Mat a_mat(2, 2, CV_32FC3, arr); + Eigen::TensorMap> a_tensor = cv2eigen_tensormap(a_mat); + + for(int i=0; i(i,j)[ch], a_tensor(i,j,ch)); + ASSERT_EQ(&a_mat.at(i,j)[ch], &a_tensor(i,j,ch)); + } + } + } +} +#endif // OPENCV_EIGEN_TENSOR_SUPPORT + TEST(Mat, regression_12943) // memory usage: ~4.5 Gb { applyTestTag(CV_TEST_TAG_MEMORY_6GB);