├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── compile.sh ├── include └── dtt.h └── test ├── dtt_test.cpp └── dtt_test.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(dtt) 3 | 4 | include(CheckCXXCompilerFlag) 5 | 6 | set(CMAKE_CXX_STANDARD 11) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 10 | 11 | set(CMAKE_BUILD_TYPE Release) 12 | #set(CMAKE_BUILD_TYPE Debug) 13 | 14 | set(output_dir ".") 15 | # First for the generic no-config case (e.g. with mingw) 16 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${output_dir}) 17 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${output_dir}) 18 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${output_dir}) 19 | # Second, for multi-config builds (e.g. msvc) 20 | foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) 21 | string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) 22 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_dir}) 23 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_dir}) 24 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_dir}) 25 | endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES) 26 | 27 | find_package(Eigen3 3.3 REQUIRED NO_MODULE) 28 | find_package(Armadillo 9 REQUIRED) 29 | find_package(ArrayFire REQUIRED) 30 | find_package(Torch REQUIRED) 31 | find_package(OpenCV REQUIRED) 32 | 33 | include_directories(${ARMADILLO_INCLUDE_DIRS}) 34 | include_directories(${OpenCV_INCLUDE_DIRS}) 35 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 36 | 37 | message(STATUS "OpenCV library status:") 38 | message(STATUS " version: ${OpenCV_VERSION}") 39 | message(STATUS " libraries: ${OpenCV_LIBS}") 40 | message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}\n") 41 | 42 | add_executable(dtt_test test/dtt_test.cpp test/dtt_test.h include/dtt.h) 43 | target_link_libraries(dtt_test Eigen3::Eigen ArrayFire::af ${ARMADILLO_LIBRARIES} ${TORCH_LIBRARIES} ${OpenCV_LIBS}) 44 | set_property(TARGET dtt_test PROPERTY CXX_STANDARD 17) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrews Sobral 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data Transfer Tools for C++ Linear Algebra Libraries. 2 | DTT is a **header-only** library that provides data transfer tools between C++ linear algebra libraries. 3 | Currently, it supports data transfer between the following libraries: 4 | * Eigen (>=3.3.x) 5 | * Armadillo (>=9.x) 6 | * OpenCV (cv::Mat) (2.x, 3.x, 4.x) 7 | * ArrayFire (>=3.x) 8 | * LibTorch (PyTorch C++) (>=1.x) 9 | 10 | Current status: 11 | 12 | Last page update: 25/08/2019 13 | 14 | | From/To | Eigen | Armadillo | OpenCV | ArrayFire | LibTorch | 15 | |-----------|:-----:|:---------:|:------:|:---------:|:--------:| 16 | | Eigen | - | X | X | X | X | 17 | | Armadillo | X | - | X | X | X | 18 | | OpenCV | X | X | - | X | X | 19 | | ArrayFire | X | X | X | - | X | 20 | | LibTorch | X | X | X | X | - | 21 | 22 | 23 | Tested on: 24 | * MacBook Pro (13-inch, 2017) 25 | * Mac OS X Mojave (10.14.5), Clang 10 (clang-1000.11.45.5), Xcode 10.1 with the following libraries: 26 | * * OpenCV 4.1.1 (stable, built from source) 27 | * * Eigen 3.3.7 (stable) 28 | * * Armadillo 9.600.5 (stable) 29 | * * ArrayFire 3.6.4 (stable) 30 | * * LibTorch (1.3.0.dev20190820) 31 | 32 | Install dependencies: 33 | ``` 34 | brew install eigen 35 | brew install armadillo 36 | # download and install ArrayFire: https://arrayfire.com/download/ 37 | # download and install LibTorch: https://pytorch.org/get-started/locally/ 38 | # download adn install OpenCV: https://opencv.org/releases/ 39 | ``` 40 | 41 | How to compile and run: 42 | ``` 43 | git clone https://github.com/andrewssobral/dtt.git 44 | cd dtt && mkdir build && cd build 45 | cmake -DCMAKE_PREFIX_PATH=$LIBTORCH_HOME .. 46 | ./dtt_test 47 | ``` 48 | 49 | How to use: 50 | ```c++ 51 | #include 52 | using namespace dtt; 53 | // that's all! 54 | ``` 55 | 56 | List of available functions: 57 | 58 | * From Eigen: 59 | * * eigen2arma 60 | * * eigen2cv 61 | * * eigen2af 62 | * * eigen2libtorch 63 | 64 | * From Armadillo: 65 | * * arma2eigen 66 | * * arma2cv 67 | * * arma2af 68 | * * arma2libtorch 69 | 70 | * From OpenCV: 71 | * * cv2eigen 72 | * * cv2arma 73 | * * cv2af 74 | * * cv2libtorch 75 | 76 | * From ArrayFire: 77 | * * af2eigen 78 | * * af2arma 79 | * * af2cv 80 | * * af2libtorch 81 | 82 | * From LibTorch: 83 | * * libtorch2eigen 84 | * * libtorch2arma 85 | * * libtorch2cv 86 | * * libtorch2af 87 | 88 | See **test/dtt_test.h** and **test/dtt_test.cpp** for usage examples. 89 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | export LIBTORCH_HOME=/path/to/libtorch 2 | rm -rf build 3 | cmake -S . -B build -DCMAKE_PREFIX_PATH=$LIBTORCH_HOME 4 | cmake --build build --config Release 5 | ./build/dtt_test 6 | 7 | # cmake -DCMAKE_PREFIX_PATH=$LIBTORCH_HOME .. -G Xcode 8 | # open dtt.xcodeproj 9 | -------------------------------------------------------------------------------- /include/dtt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * DTT - Data Transfer Tools for C++ Linear Algebra Libraries. 3 | * It supports data transfer between the following libraries: 4 | * Eigen, Armadillo, OpenCV, ArrayFire, LibTorch 5 | */ 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if CV_MAJOR_VERSION >= 4 20 | #define CV_BGR2RGB cv::COLOR_BGR2RGB 21 | #endif 22 | 23 | namespace dtt { 24 | 25 | // same as MatrixXf, but with row-major memory layout 26 | //typedef Eigen::Matrix MatrixXf_rm; 27 | 28 | // MatrixXrm x; instead of MatrixXf_rm x; 29 | template 30 | using MatrixXrm = typename Eigen::Matrix; 31 | 32 | // MatrixX x; instead of Eigen::MatrixXf x; 33 | template 34 | using MatrixX = typename Eigen::Matrix; 35 | 36 | //--------------------------------------------------------------------------- 37 | // Eigen to Armadillo, OpenCV, ArrayFire, LibTorch, File 38 | //--------------------------------------------------------------------------- 39 | 40 | arma::mat eigen2arma(Eigen::MatrixXd& E, bool copy=true) { 41 | return arma::mat(E.data(), E.rows(), E.cols(), /*copy_aux_mem*/copy, /*strict*/false); 42 | } 43 | 44 | af::array eigen2af(Eigen::MatrixXf& E){ 45 | af::array A(E.rows(), E.cols(), E.data()); 46 | return A; 47 | } 48 | 49 | //void eigen2cv(const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, cv::Mat& dst) 50 | 51 | template 52 | torch::Tensor eigen2libtorch(MatrixX &M) { 53 | Eigen::Matrix E(M); 54 | std::vector dims = {E.rows(), E.cols()}; 55 | auto T = torch::from_blob(E.data(), dims).clone(); //.to(torch::kCPU); 56 | return T; 57 | } 58 | 59 | template 60 | torch::Tensor eigen2libtorch(MatrixXrm &E, bool copydata=true) { 61 | std::vector dims = {E.rows(), E.cols()}; 62 | auto T = torch::from_blob(E.data(), dims); 63 | if (copydata) 64 | return T.clone(); 65 | else 66 | return T; 67 | } 68 | 69 | void eigen2file(Eigen::MatrixXf E, std::string filename) { 70 | std::ofstream file(filename); 71 | if (file.is_open()) 72 | file << E << '\n'; 73 | file.close(); 74 | } 75 | 76 | //--------------------------------------------------------------------------- 77 | // Armadillo to Eigen, OpenCV, ArrayFire, File 78 | //--------------------------------------------------------------------------- 79 | 80 | Eigen::MatrixXd arma2eigen(arma::mat& A) { 81 | return Eigen::Map(A.memptr(), A.n_rows, A.n_cols); 82 | } 83 | 84 | template 85 | void arma2cv(const arma::Mat& A, cv::Mat_& C) { 86 | cv::transpose(cv::Mat_(static_cast(A.n_cols), 87 | static_cast(A.n_rows), 88 | const_cast(A.memptr())), C); 89 | }; 90 | 91 | /* 92 | For arma2af, consider arma::Mat as a float matrix. 93 | Returns a copy of arma::Mat data. 94 | 95 | The root matrix class is arma::Mat, where type is one of: 96 | float, double, std::complex, std::complex, short, int, long, and unsigned versions of short, int, long 97 | arma::mat = arma::Mat 98 | arma::dmat = arma::Mat 99 | arma::fmat = arma::Mat 100 | 101 | arma::mat A = arma::randu(5,5); 102 | arma::fmat B = arma::conv_to::from(A); 103 | */ 104 | //reinterpret_cast 105 | //af::array arma2af(arma::mat& M){ 106 | template 107 | af::array arma2af(arma::Mat& M) { 108 | af::array A(static_cast(M.n_cols), 109 | static_cast(M.n_rows), 110 | const_cast(M.memptr())); 111 | //af::transposeInPlace(A); 112 | return A; 113 | } 114 | 115 | template 116 | torch::Tensor arma2libtorch(arma::Mat& M) { 117 | arma::Mat A = arma::trans(M); // arma::mat A = M.t(); // equivalent to arma::trans(M), but more compact 118 | std::vector dims = {static_cast(M.n_rows), static_cast(M.n_cols)}; 119 | auto T = torch::from_blob(const_cast(A.memptr()), dims).clone(); //.to(torch::kCPU); 120 | return T; 121 | } 122 | 123 | void arma2file(arma::mat& A, std::string filename) { 124 | A.save(filename); 125 | } 126 | 127 | //--------------------------------------------------------------------------- 128 | // OpenCV to Eigen, Armadillo, ArrayFire, LibTorch, File 129 | //--------------------------------------------------------------------------- 130 | 131 | //void cv2eigen(const Mat& src, Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst) 132 | 133 | template 134 | MatrixX cv2eigen(cv::Mat &C) { 135 | Eigen::Map> E(C.ptr(), C.rows, C.cols); 136 | return E; 137 | } 138 | 139 | template 140 | arma::Mat cv2arma(cv::Mat &C, bool copy=true) { 141 | /* 142 | OpenCV (cv::Mat) is Row-major order and Armadillo is Column-major order. 143 | If copy=true, arma::inplace_trans(A); should be used to keep 144 | the Row-major order from cv::Mat. 145 | */ 146 | //return arma::Mat(cvMatIn.data, cvMatIn.rows, cvMatIn.cols, false, false); 147 | return arma::Mat(reinterpret_cast(C.data), 148 | static_cast(C.cols), 149 | static_cast(C.rows), 150 | /*copy_aux_mem*/copy, 151 | /*strict*/false); 152 | } 153 | 154 | /* 155 | For cv2af, consider cv::Mat as a float matrix. 156 | Should be used with af::transposeInPlace(A); 157 | to keep the Row-major order from OpenCV. 158 | Returns a copy of cv::Mat data. 159 | */ 160 | template 161 | af::array cv2af(cv::Mat &C){ 162 | af::array A(C.cols, C.rows, reinterpret_cast(C.data)); 163 | return A; 164 | } 165 | 166 | torch::Tensor cv2libtorch(cv::Mat &C, bool copydata=true, bool is_cv_image=false) { 167 | int kCHANNELS = C.channels(); 168 | if (is_cv_image) { 169 | if (kCHANNELS == 3) { 170 | cv::cvtColor(C, C, CV_BGR2RGB); 171 | C.convertTo(C, CV_32FC3, 1.0f / 255.0f); 172 | } else // considering channels = 1 173 | C.convertTo(C, CV_32FC1, 1.0f / 255.0f); 174 | auto T = torch::from_blob(C.data, {1, C.rows, C.cols, kCHANNELS}); 175 | T = T.permute({0, 3, 1, 2}); 176 | return T; 177 | } else { 178 | std::vector dims; 179 | at::TensorOptions options(at::kFloat); 180 | if (kCHANNELS == 1) { 181 | C.convertTo(C, CV_32FC1); 182 | dims = {C.rows, C.cols}; 183 | } 184 | else { // considering channels = 3 185 | C.convertTo(C, CV_32FC3); 186 | dims = {C.rows, C.cols, kCHANNELS}; 187 | } 188 | //auto T = torch::from_blob(C.ptr(), dims, options).clone(); 189 | //auto T = torch::from_blob(C.data, at::IntList(dims), options); 190 | auto T = torch::from_blob(C.data, dims, options); 191 | if (copydata) 192 | return T.clone(); 193 | else 194 | return T; 195 | } 196 | } 197 | 198 | void cv2file(cv::Mat M, std::string filename) { 199 | std::ofstream file(filename); 200 | if (file.is_open()) 201 | file << M << '\n'; 202 | file.close(); 203 | } 204 | 205 | //--------------------------------------------------------------------------- 206 | // ArrayFire to Eigen, Armadillo, OpenCV, LibTorch 207 | //--------------------------------------------------------------------------- 208 | // Eigen::MatrixXf af2eigen(af::array &A) { 209 | // float* data = A.host(); 210 | // Eigen::Map E(data, A.dims(0), A.dims(1)); 211 | // return E; 212 | // } 213 | 214 | template 215 | MatrixX af2eigen(af::array &A) { 216 | Eigen::Map> E(A.host(), A.dims(0), A.dims(1)); 217 | return E; 218 | } 219 | 220 | template 221 | arma::Mat af2arma(af::array &A, bool copy=true) { 222 | return arma::Mat(reinterpret_cast(A.host()), 223 | static_cast(A.dims(0)), 224 | static_cast(A.dims(1)), 225 | /*copy_aux_mem*/copy, 226 | /*strict*/false); 227 | } 228 | 229 | template 230 | cv::Mat_ af2cv(af::array &A) { 231 | return cv::Mat_(static_cast(A.dims(1)), 232 | static_cast(A.dims(0)), 233 | reinterpret_cast(A.host())).t(); 234 | } 235 | 236 | template 237 | torch::Tensor af2libtorch(af::array &M) { 238 | af::array A = af::transpose(M); 239 | std::vector dims = {static_cast(A.dims(0)), static_cast(A.dims(1))}; 240 | auto T = torch::from_blob(reinterpret_cast(A.host()), dims).clone(); //.to(torch::kCPU); 241 | return T; 242 | } 243 | 244 | //--------------------------------------------------------------------------- 245 | // LibTorch to Eigen, Armadillo, OpenCV, ArrayFire 246 | //--------------------------------------------------------------------------- 247 | 248 | template 249 | Eigen::Matrix libtorch2eigen(torch::Tensor &Tin) { 250 | /* 251 | LibTorch is Row-major order and Eigen is Column-major order. 252 | MatrixXrm uses Eigen::RowMajor for compatibility. 253 | */ 254 | auto T = Tin.to(torch::kCPU); 255 | Eigen::Map> E(T.data_ptr(), T.size(0), T.size(1)); 256 | return E; 257 | } 258 | 259 | template 260 | arma::Mat libtorch2arma(torch::Tensor &Tin, bool copy=true) { 261 | /* 262 | LibTorch is Row-major order and Armadillo is Column-major order. 263 | If copy=true, arma::inplace_trans(A); should be used to keep 264 | the Row-major order from LibTorch. 265 | */ 266 | auto T = Tin.to(torch::kCPU); 267 | return arma::Mat(reinterpret_cast(T.data_ptr()), 268 | static_cast(T.size(0)), 269 | static_cast(T.size(1)), 270 | /*copy_aux_mem*/copy, 271 | /*strict*/false); 272 | } 273 | 274 | // Consider torch::Tensor as a float matrix 275 | cv::Mat libtorch2cv(torch::Tensor &Tin, bool copy=true) { 276 | auto T = Tin.to(torch::kCPU); 277 | cv::Mat C; 278 | if (copy) { 279 | cv::Mat M(T.size(0), T.size(1), CV_32FC1, T.data()); 280 | M.copyTo(C); 281 | } else 282 | C = cv::Mat(T.size(0), T.size(1), CV_32FC1, T.data()); 283 | return C; 284 | } 285 | 286 | /* 287 | If libtorch2af, consider torch::Tensor as a float matrix. 288 | Should be used with af::transposeInPlace(A); 289 | to keep the Row-major order from LibTorch. 290 | Returns a copy of torch::Tensor data. 291 | */ 292 | template 293 | af::array libtorch2af(torch::Tensor &Tin){ 294 | auto T = Tin.to(torch::kCPU); 295 | af::array A(T.size(0), T.size(1), T.data()); 296 | return A; 297 | } 298 | 299 | } 300 | -------------------------------------------------------------------------------- /test/dtt_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * DTT - Data Transfer Tools for C++ Linear Algebra Libraries. 3 | * It supports data transfer between the following libraries: 4 | * Eigen, Armadillo, OpenCV, ArrayFire, LibTorch 5 | */ 6 | #include "dtt_test.h" 7 | 8 | void debug_info() { 9 | std::cout << "Eigen version: " << EIGEN_WORLD_VERSION << EIGEN_MAJOR_VERSION << EIGEN_MINOR_VERSION << std::endl; 10 | std::cout << "Armadillo version: " << arma::arma_version::as_string() << std::endl; 11 | std::cout << "Using OpenCV version " << CV_VERSION << std::endl; 12 | } 13 | 14 | int main(int argc, const char **argv) { 15 | debug_info(); 16 | test_libs(); 17 | test_libs_conversion(); 18 | } 19 | -------------------------------------------------------------------------------- /test/dtt_test.h: -------------------------------------------------------------------------------- 1 | /** 2 | * DTT - Data Transfer Tools for C++ Linear Algebra Libraries. 3 | * It supports data transfer between the following libraries: 4 | * Eigen, Armadillo, OpenCV, ArrayFire, LibTorch 5 | */ 6 | #pragma once 7 | 8 | #include 9 | 10 | using namespace dtt; 11 | 12 | void print_line() { 13 | std::cout << std::string(50, '-') << std::endl; 14 | } 15 | 16 | //--------------------------------------------------------------------------- 17 | // Eigen to Armadillo, OpenCV, ArrayFire, LibTorch 18 | //--------------------------------------------------------------------------- 19 | 20 | void test_eigen_arma() { 21 | print_line(); 22 | std::cout << "Testing Eigen to Armadillo (copy 3x3 matrix):" << std::endl; 23 | // Eigen 24 | Eigen::MatrixXd E = Eigen::MatrixXd::Random(3,3); 25 | std::cout << "EigenMat:\n" << E << std::endl; 26 | // Armadillo 27 | arma::mat A = eigen2arma(E); 28 | std::cout << "ArmaMat:\n" << A << std::endl; 29 | // re-check after changes 30 | A(0,0) = 0; 31 | std::cout << "ArmaMat:\n" << A << std::endl; 32 | std::cout << "EigenMat:\n" << E << std::endl; 33 | } 34 | 35 | // TODO: test_eigen_arma with no-copy 36 | 37 | void test_eigen_opencv() { 38 | print_line(); 39 | std::cout << "Testing Eigen to OpenCV (copy 3x3 matrix):" << std::endl; 40 | // Eigen 41 | //Eigen::MatrixXd E = Eigen::MatrixXd::Random(3,3); 42 | Eigen::MatrixXf E = Eigen::MatrixXf::Random(3,3); 43 | std::cout << "EigenMat:\n" << E << std::endl; 44 | // OpenCV 45 | cv::Mat C; 46 | cv::eigen2cv(E, C); 47 | std::cout << "cvMat:\n" << C << std::endl; 48 | // re-check after changes 49 | //C.at(0,0) = 0; 50 | C.at(0,0) = 0; 51 | std::cout << "cvMat:\n" << C << std::endl; 52 | std::cout << "EigenMat:\n" << E << std::endl; 53 | } 54 | 55 | // TODO: test_eigen_opencv with no-copy 56 | 57 | void test_eigen_af() { 58 | print_line(); 59 | std::cout << "Testing Eigen to ArrayFire (copy 3x3 matrix):" << std::endl; 60 | // Eigen 61 | Eigen::MatrixXf E = Eigen::MatrixXf::Random(3,3); 62 | std::cout << "EigenMat:\n" << E << std::endl; 63 | // ArrayFire 64 | //af::array A(E.rows(), E.cols(), E.data()); 65 | af::array A = eigen2af(E); 66 | std::cout << "AfMat:" << std::endl; 67 | af_print(A); 68 | // re-check after changes 69 | A(0,0) = 0; 70 | std::cout << "AfMat:" << std::endl; 71 | af_print(A); 72 | std::cout << "EigenMat:\n" << E << std::endl; 73 | } 74 | 75 | // TODO: test_eigen_af with no-copy 76 | 77 | void test_eigen_libtorch1() { // CM = Column-major storage 78 | print_line(); 79 | std::cout << "Testing Eigen(CM) to LibTorch (copy 3x3 matrix):" << std::endl; 80 | // Eigen 81 | //MatrixX = Eigen::Matrix 82 | //MatrixX = MatrixXf 83 | Eigen::MatrixXf E(3,3); 84 | // E << 1.010101, 2.020202, 3.030303, 85 | // 4.040404, 5.050505, 6.060606, 86 | // 7.070707, 8.080808, 9.090909; 87 | E = Eigen::MatrixXf::Random(3,3); 88 | std::cout << "EigenMat:\n" << E << std::endl; 89 | // LibTorch 90 | torch::Tensor T = eigen2libtorch(E); 91 | std::cout << "LibTorch:" << std::endl; 92 | std::cout << T << std::endl; 93 | // re-check after changes 94 | T[0][0] = 0; 95 | std::cout << "LibTorch:" << std::endl; 96 | std::cout << T << std::endl; 97 | std::cout << "EigenMat:\n" << E << std::endl; 98 | } 99 | 100 | void test_eigen_libtorch2() { // RM = Row-major storage (same as LibTorch) 101 | print_line(); 102 | std::cout << "Testing Eigen(RM) to LibTorch (copy 3x3 matrix):" << std::endl; 103 | // Eigen 104 | // MatrixXrm = Eigen::Matrix 105 | MatrixXrm E(3,3); 106 | E << 1.010101, 2.020202, 3.030303, 107 | 4.040404, 5.050505, 6.060606, 108 | 7.070707, 8.080808, 9.090909; 109 | std::cout << "EigenMat:\n" << E << std::endl; 110 | // LibTorch 111 | torch::Tensor T = eigen2libtorch(E); 112 | std::cout << "LibTorch:" << std::endl; 113 | std::cout << T << std::endl; 114 | // re-check after changes 115 | T[0][0] = 0; 116 | std::cout << "LibTorch:" << std::endl; 117 | std::cout << T << std::endl; 118 | std::cout << "EigenMat:\n" << E << std::endl; 119 | } 120 | 121 | void test_eigen_libtorch3() { // RM = Row-major storage (same as LibTorch) 122 | print_line(); 123 | std::cout << "Testing Eigen(RM) to LibTorch (no-copy 3x3 matrix):" << std::endl; 124 | // Eigen 125 | // MatrixXrm = Eigen::Matrix 126 | MatrixXrm E(3,3); 127 | E << 1.010101, 2.020202, 3.030303, 128 | 4.040404, 5.050505, 6.060606, 129 | 7.070707, 8.080808, 9.090909; 130 | std::cout << "EigenMat:\n" << E << std::endl; 131 | // LibTorch 132 | torch::Tensor T = eigen2libtorch(E, false); 133 | std::cout << "LibTorch:" << std::endl; 134 | std::cout << T << std::endl; 135 | // re-check after changes 136 | T[0][0] = 0; 137 | std::cout << "LibTorch:" << std::endl; 138 | std::cout << T << std::endl; 139 | std::cout << "EigenMat:\n" << E << std::endl; 140 | } 141 | 142 | //--------------------------------------------------------------------------- 143 | // Armadillo to Eigen, OpenCV, ArrayFire, LibTorch 144 | //--------------------------------------------------------------------------- 145 | 146 | void test_arma_eigen() { 147 | print_line(); 148 | std::cout << "Testing Armadillo to Eigen (copy 3x3 matrix):" << std::endl; 149 | // Armadillo 150 | arma::mat A = arma::randu(3,3); // arma::mat = arma::Mat 151 | std::cout << "ArmaMat:\n" << A << std::endl; 152 | // Eigen 153 | Eigen::MatrixXd E = arma2eigen(A); 154 | std::cout << "EigenMat:\n" << E << std::endl; 155 | // re-check after changes 156 | E(0,0) = 0; 157 | std::cout << "EigenMat:\n" << E << std::endl; 158 | std::cout << "ArmaMat:\n" << A << std::endl; 159 | } 160 | 161 | // TODO: test_arma_eigen with no-copy 162 | 163 | void test_arma_opencv() { 164 | print_line(); 165 | std::cout << "Testing Armadillo to OpenCV (copy 3x3 matrix):" << std::endl; 166 | // Armadillo 167 | //arma::mat A = arma::randu(3,3); // arma::mat = arma::Mat 168 | arma::Mat A = arma::randu>(3,3); 169 | std::cout << "ArmaMat:\n" << A << std::endl; 170 | // OpenCV 171 | cv::Mat_ C(3,3); 172 | arma2cv(A, C); 173 | std::cout << "cvMat:\n" << C << std::endl; 174 | // re-check after changes 175 | C.at(0,0) = 0; 176 | std::cout << "cvMat:\n" << C << std::endl; 177 | std::cout << "ArmaMat:\n" << A << std::endl; 178 | } 179 | 180 | // TODO: test_arma_opencv with no-copy 181 | 182 | void test_arma_af() { 183 | print_line(); 184 | std::cout << "Testing Armadillo to ArrayFire (copy 3x3 matrix):" << std::endl; 185 | // Armadillo 186 | //arma::mat M = arma::randu(3,3); // arma::mat = arma::Mat 187 | arma::Mat M = arma::randu>(3,3); 188 | std::cout << "ArmaMat:\n" << M << std::endl; 189 | // ArrayFire 190 | auto A = arma2af(M); 191 | std::cout << "AfMat:" << std::endl; 192 | af_print(A); 193 | // re-check after changes 194 | A(1,1) = 0; 195 | std::cout << "AfMat:" << std::endl; 196 | af_print(A); 197 | std::cout << "ArmaMat:\n" << M << std::endl; 198 | } 199 | 200 | void test_arma_libtorch() { 201 | print_line(); 202 | std::cout << "Testing Armadillo to LibTorch (copy 3x3 matrix):" << std::endl; 203 | // Armadillo 204 | //arma::mat A = arma::randu(3,3); // arma::mat = arma::Mat 205 | arma::Mat A = arma::randu>(3,3); 206 | std::cout << "ArmaMat:\n" << A << std::endl; 207 | // LibTorch 208 | torch::Tensor T = arma2libtorch(A); 209 | std::cout << "LibTorch:" << std::endl; 210 | std::cout << T << std::endl; 211 | // re-check after changes 212 | T[0][0] = 0; 213 | std::cout << "LibTorch:" << std::endl; 214 | std::cout << T << std::endl; 215 | std::cout << "ArmaMat:\n" << A << std::endl; 216 | } 217 | 218 | //--------------------------------------------------------------------------- 219 | // OpenCV to Eigen, Armadillo, ArrayFire, LibTorch 220 | //--------------------------------------------------------------------------- 221 | 222 | void test_opencv_eigen() { 223 | print_line(); 224 | std::cout << "Testing OpenCV to Eigen (copy 3x3 matrix):" << std::endl; 225 | // OpenCV 226 | cv::Mat C(3, 3, CV_32FC1); 227 | cv::randn(C, 0.0f, 1.0f); 228 | std::cout << "cvMat:\n" << C << std::endl; 229 | // Eigen 230 | Eigen::MatrixXd E; 231 | cv::cv2eigen(C, E); 232 | //auto E = cv2eigen(C); 233 | std::cout << "EigenMat:\n" << E << std::endl; 234 | // re-check after changes 235 | E(0,0) = 0; 236 | std::cout << "EigenMat:\n" << E << std::endl; 237 | std::cout << "cvMat:\n" << C << std::endl; 238 | } 239 | 240 | // TODO: test_opencv_eigen with no-copy 241 | 242 | void test_opencv_arma() { 243 | print_line(); 244 | std::cout << "Testing OpenCV to Armadillo (copy 3x3 matrix):" << std::endl; 245 | // OpenCV 246 | cv::Mat C(3, 3, CV_32FC1); 247 | cv::randn(C, 0.0f, 1.0f); 248 | std::cout << "cvMat:\n" << C << std::endl; 249 | // Armadillo 250 | auto A = cv2arma(C); 251 | arma::inplace_trans(A); 252 | std::cout << "ArmaMat:\n" << A << std::endl; 253 | // re-check after changes 254 | A(0,0) = 0; 255 | std::cout << "ArmaMat:\n" << A << std::endl; 256 | std::cout << "cvMat:\n" << C << std::endl; 257 | } 258 | 259 | // TODO: test_opencv_arma with no-copy 260 | 261 | void test_opencv_af() { 262 | print_line(); 263 | std::cout << "Testing OpenCV to ArrayFire (copy 3x3 matrix):" << std::endl; 264 | // OpenCV 265 | cv::Mat C(3, 3, CV_32FC1); 266 | cv::randn(C, -1.0f, 1.0f); 267 | std::cout << "cvMat:\n" << C << std::endl; 268 | // ArrayFire 269 | auto A = cv2af(C); 270 | af::transposeInPlace(A); 271 | std::cout << "AfMat:" << std::endl; 272 | af_print(A); 273 | // re-check after changes 274 | A(0,0) = 0; 275 | std::cout << "AfMat:" << std::endl; 276 | af_print(A); 277 | std::cout << "cvMat:\n" << C << std::endl; 278 | } 279 | 280 | void test_opencv_libtorch1() { 281 | print_line(); 282 | std::cout << "Testing OpenCV to LibTorch (copy 3x3 matrix):" << std::endl; 283 | // OpenCV 284 | cv::Mat C(3, 3, CV_32FC1); 285 | cv::randn(C, 0.0f, 1.0f); 286 | std::cout << "cvMat:\n" << C << std::endl; 287 | // LibTorch 288 | torch::Tensor T = cv2libtorch(C); 289 | std::cout << "LibTorch:" << std::endl; 290 | std::cout << T << std::endl; 291 | // re-check after changes 292 | T[0][0] = 0; 293 | std::cout << "LibTorch:" << std::endl; 294 | std::cout << T << std::endl; 295 | std::cout << "cvMat:\n" << C << std::endl; 296 | } 297 | 298 | void test_opencv_libtorch2() { 299 | print_line(); 300 | std::cout << "Testing OpenCV to LibTorch (no-copy 3x3 matrix):" << std::endl; 301 | // OpenCV 302 | cv::Mat C(3, 3, CV_32FC1); 303 | cv::randn(C, 0.0f, 1.0f); 304 | std::cout << "cvMat:\n" << C << std::endl; 305 | // LibTorch 306 | torch::Tensor T = cv2libtorch(C, false); 307 | std::cout << "LibTorch:" << std::endl; 308 | std::cout << T << std::endl; 309 | // re-check after changes 310 | T[0][0] = 0; 311 | std::cout << "LibTorch:" << std::endl; 312 | std::cout << T << std::endl; 313 | std::cout << "cvMat:\n" << C << std::endl; 314 | } 315 | 316 | //--------------------------------------------------------------------------- 317 | // ArrayFire to Eigen, Armadillo, OpenCV, LibTorch 318 | //--------------------------------------------------------------------------- 319 | 320 | void test_af_eigen() { 321 | print_line(); 322 | //af::info(); 323 | std::cout << "Testing ArrayFire to Eigen (copy 3x3 matrix):" << std::endl; 324 | // ArrayFire 325 | af::array A = af::randu(3,3, f32); 326 | std::cout << "AfMat:" << std::endl; 327 | af_print(A); 328 | //float* data = A.host(); 329 | //Eigen::Map E(data, A.dims(0), A.dims(1)); 330 | //Eigen::MatrixXf E = af2eigen(A); 331 | auto E = af2eigen(A); 332 | std::cout << "EigenMat:\n" << E << std::endl; 333 | // re-check after changes 334 | E(0,0) = 0; 335 | std::cout << "EigenMat:\n" << E << std::endl; 336 | std::cout << "AfMat:" << std::endl; 337 | af_print(A); 338 | } 339 | 340 | // TODO: test_af_eigen with no-copy (same device) 341 | 342 | void test_af_arma() { 343 | print_line(); 344 | //af::info(); 345 | std::cout << "Testing ArrayFire to Armadillo (copy 3x3 matrix):" << std::endl; 346 | // ArrayFire 347 | af::array A = af::randu(3,3, f32); 348 | std::cout << "AfMat:" << std::endl; 349 | af_print(A); 350 | // Armadillo 351 | auto M = af2arma(A); 352 | //arma::inplace_trans(A); 353 | std::cout << "ArmaMat:\n" << M << std::endl; 354 | // re-check after changes 355 | M(0,0) = 0; 356 | std::cout << "ArmaMat:\n" << M << std::endl; 357 | std::cout << "AfMat:" << std::endl; 358 | af_print(A); 359 | } 360 | 361 | // TODO: test_af_arma with no-copy (same device) 362 | 363 | void test_af_cv() { 364 | print_line(); 365 | //af::info(); 366 | std::cout << "Testing ArrayFire to OpenCV (copy 3x3 matrix):" << std::endl; 367 | // ArrayFire 368 | af::array A = af::randu(3,3,f32); // first row, second column. 369 | std::cout << "AfMat:" << std::endl; 370 | af_print(A); 371 | // OpenCV 372 | auto C = af2cv(A); 373 | std::cout << "cvMat:\n" << C << std::endl; 374 | // re-check after changes 375 | C.at(0,0) = 0; 376 | std::cout << "cvMat:\n" << C << std::endl; 377 | std::cout << "AfMat:" << std::endl; 378 | af_print(A); 379 | } 380 | 381 | void test_af_libtorch() { 382 | print_line(); 383 | //af::info(); 384 | std::cout << "Testing ArrayFire to LibTorch (copy 3x3 matrix):" << std::endl; 385 | // ArrayFire 386 | af::array A = af::randu(3,3,f32); // first row, second column. 387 | std::cout << "AfMat:" << std::endl; 388 | af_print(A); 389 | // LibTorch 390 | torch::Tensor T = af2libtorch(A); 391 | std::cout << "LibTorch:" << std::endl; 392 | std::cout << T << std::endl; 393 | // re-check after changes 394 | T[0][0] = 0; 395 | std::cout << "LibTorch:" << std::endl; 396 | std::cout << T << std::endl; 397 | std::cout << "AfMat:" << std::endl; 398 | af_print(A); 399 | } 400 | 401 | //--------------------------------------------------------------------------- 402 | // LibTorch to Eigen, Armadillo, OpenCV, ArrayFire 403 | //--------------------------------------------------------------------------- 404 | 405 | void test_libtorch_eigen1() { 406 | print_line(); 407 | std::cout << "Testing LibTorch to Eigen (copy 3x3 matrix):" << std::endl; 408 | // LibTorch 409 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 410 | torch::Tensor T = torch::rand({3, 3}); 411 | std::cout << "LibTorch:" << std::endl; 412 | std::cout << T << std::endl; 413 | // Eigen 414 | auto E = libtorch2eigen(T); 415 | std::cout << "EigenMat:\n" << E << std::endl; 416 | // re-check after changes 417 | E(0,0) = 0; 418 | std::cout << "EigenMat:\n" << E << std::endl; 419 | std::cout << "LibTorch:" << std::endl; 420 | std::cout << T << std::endl; 421 | } 422 | 423 | void test_libtorch_eigen2() { 424 | print_line(); 425 | std::cout << "Testing LibTorch to Eigen (no-copy 3x3 matrix):" << std::endl; 426 | // LibTorch 427 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 428 | torch::Tensor T = torch::rand({3, 3}); 429 | std::cout << "LibTorch:" << std::endl; 430 | std::cout << T << std::endl; 431 | // Eigen 432 | Eigen::Map> E(T.data_ptr(), T.size(0), T.size(1)); 433 | std::cout << "EigenMat:\n" << E << std::endl; 434 | // re-check after changes 435 | E(0,0) = 0; 436 | std::cout << "EigenMat:\n" << E << std::endl; 437 | std::cout << "LibTorch:" << std::endl; 438 | std::cout << T << std::endl; 439 | } 440 | 441 | void test_libtorch_arma1() { 442 | print_line(); 443 | std::cout << "Testing LibTorch to Armadillo (copy 3x3 matrix):" << std::endl; 444 | // LibTorch 445 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 446 | torch::Tensor T = torch::rand({3, 3}); 447 | std::cout << "LibTorch:" << std::endl; 448 | std::cout << T << std::endl; 449 | // Armadillo 450 | auto A = libtorch2arma(T); 451 | arma::inplace_trans(A); 452 | std::cout << "ArmaMat:\n" << A << std::endl; 453 | // re-check after changes 454 | A(0,0) = 0; 455 | std::cout << "ArmaMat:\n" << A << std::endl; 456 | std::cout << "LibTorch:" << std::endl; 457 | std::cout << T << std::endl; 458 | } 459 | 460 | void test_libtorch_arma2() { 461 | print_line(); 462 | std::cout << "Testing LibTorch to Armadillo (no-copy 3x3 matrix):" << std::endl; 463 | // LibTorch 464 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 465 | torch::Tensor T = torch::rand({3, 3}); 466 | std::cout << "LibTorch:" << std::endl; 467 | std::cout << T << std::endl; 468 | // Armadillo 469 | auto A = libtorch2arma(T, /*copy*/false); 470 | std::cout << "ArmaMat:\n" << A << std::endl; 471 | std::cout << ">>> Note that ArmaMat is transposed <<<" << std::endl; 472 | // re-check after changes 473 | A(0,0) = 0; 474 | std::cout << "ArmaMat:\n" << A << std::endl; 475 | std::cout << "LibTorch:" << std::endl; 476 | std::cout << T << std::endl; 477 | } 478 | 479 | void test_libtorch_opencv1() { 480 | print_line(); 481 | std::cout << "Testing LibTorch to OpenCV (copy 3x3 matrix):" << std::endl; 482 | // LibTorch 483 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 484 | torch::Tensor T = torch::rand({3, 3}); 485 | std::cout << "LibTorch:" << std::endl; 486 | std::cout << T << std::endl; 487 | // OpenCV 488 | cv::Mat C = libtorch2cv(T); 489 | std::cout << "cvMat:\n" << C << std::endl; 490 | // re-check after changes 491 | C.at(0,0) = 0; 492 | std::cout << "cvMat:\n" << C << std::endl; 493 | std::cout << "LibTorch:" << std::endl; 494 | std::cout << T << std::endl; 495 | } 496 | 497 | void test_libtorch_opencv2() { 498 | print_line(); 499 | std::cout << "Testing LibTorch to OpenCV (no-copy 3x3 matrix):" << std::endl; 500 | // LibTorch 501 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 502 | torch::Tensor T = torch::rand({3, 3}); 503 | std::cout << "LibTorch:" << std::endl; 504 | std::cout << T << std::endl; 505 | // OpenCV 506 | cv::Mat C = libtorch2cv(T, false); 507 | std::cout << "cvMat:\n" << C << std::endl; 508 | // re-check after changes 509 | C.at(0,0) = 0; 510 | std::cout << "cvMat:\n" << C << std::endl; 511 | std::cout << "LibTorch:" << std::endl; 512 | std::cout << T << std::endl; 513 | } 514 | 515 | void test_libtorch_af() { 516 | print_line(); 517 | std::cout << "Testing LibTorch to ArrayFire (copy 3x3 matrix):" << std::endl; 518 | // LibTorch 519 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 520 | torch::Tensor T = torch::rand({3, 3}); 521 | std::cout << "LibTorch:" << std::endl; 522 | std::cout << T << std::endl; 523 | // ArrayFire 524 | auto A = libtorch2af(T); 525 | af::transposeInPlace(A); 526 | std::cout << "AfMat:" << std::endl; 527 | af_print(A); 528 | // re-check after changes 529 | A(0,0) = 0; 530 | std::cout << "AfMat:" << std::endl; 531 | af_print(A); 532 | std::cout << "LibTorch:" << std::endl; 533 | std::cout << T << std::endl; 534 | } 535 | 536 | // TODO: test_libtorch_af with no-copy (if same device) 537 | 538 | //--------------------------------------------------------------------------- 539 | // Call functions 540 | //--------------------------------------------------------------------------- 541 | 542 | void test_libs_conversion() { 543 | test_eigen_opencv(); 544 | test_eigen_arma(); 545 | test_eigen_af(); 546 | test_eigen_libtorch1(); 547 | test_eigen_libtorch2(); 548 | test_eigen_libtorch3(); 549 | test_arma_eigen(); 550 | test_arma_opencv(); 551 | test_arma_af(); 552 | test_arma_libtorch(); 553 | test_opencv_eigen(); 554 | test_opencv_arma(); 555 | test_opencv_af(); 556 | test_opencv_libtorch1(); 557 | test_opencv_libtorch2(); 558 | test_af_eigen(); 559 | test_af_arma(); 560 | test_af_cv(); 561 | test_af_libtorch(); 562 | test_libtorch_eigen1(); 563 | test_libtorch_eigen2(); 564 | test_libtorch_arma1(); 565 | test_libtorch_arma2(); 566 | test_libtorch_opencv1(); 567 | test_libtorch_opencv2(); 568 | test_libtorch_af(); 569 | } 570 | 571 | //--------------------------------------------------------------------------- 572 | // Set of functions to test each library 573 | //--------------------------------------------------------------------------- 574 | 575 | void test_opencv() { 576 | print_line(); 577 | std::cout << "Testing OpenCV" << std::endl; 578 | cv::Mat C(3, 3, CV_32FC1); 579 | cv::randn(C, 0.0f, 1.0f); 580 | std::cout << C << std::endl; 581 | } 582 | 583 | void test_eigen() { 584 | print_line(); 585 | std::cout << "Testing Eigen" << std::endl; 586 | Eigen::MatrixXd E(2,2); 587 | E(0,0) = 3; 588 | E(1,0) = 2.5; 589 | E(0,1) = -1; 590 | E(1,1) = E(1,0) + E(0,1); 591 | std::cout << E << std::endl; 592 | } 593 | 594 | void test_armadillo() { 595 | print_line(); 596 | std::cout << "Testing Armadillo" << std::endl; 597 | arma::Mat A = arma::randu(3,3); 598 | std::cout << "A:\n" << A << "\n"; 599 | } 600 | 601 | void test_arrayfire() { 602 | print_line(); 603 | std::cout << "Testing ArrayFire" << std::endl; 604 | try { 605 | //int device = argc > 1 ? atoi(argv[1]) : 0; 606 | //af::setDevice(device); 607 | af::info(); 608 | af::array a = af::randu(100); 609 | double sum = af::sum(a); 610 | printf("sum: %g\n", sum); 611 | } catch (af::exception& e) { 612 | fprintf(stderr, "%s\n", e.what()); 613 | throw; 614 | } 615 | } 616 | 617 | void test_libtorch() { 618 | print_line(); 619 | std::cout << "Testing LibTorch" << std::endl; 620 | torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU); 621 | torch::Tensor T = torch::rand({2, 3}); 622 | std::cout << T << std::endl; 623 | } 624 | 625 | void test_libs() { 626 | test_opencv(); 627 | test_eigen(); 628 | test_armadillo(); 629 | test_arrayfire(); 630 | test_libtorch(); 631 | } 632 | --------------------------------------------------------------------------------