Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Test_TensorImpl.cpp 6.26 KiB
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>

#include "aidge/data/Tensor.hpp"

#include "aidge/backend/opencv/data/TensorImpl.hpp"
#include "aidge/backend/opencv/utils/Utils.hpp"


using namespace Aidge;

TEST_CASE("Tensor creation opencv", "[Tensor][OpenCV]") {
    SECTION("from const array") {
        Tensor x;
        x.setDataType(Aidge::DataType::Int32);
        x.setBackend("opencv");
        x = Array3D<int,2,2,2>{
        {
            {
                {1, 2},
                {3, 4}
            },
            {
                {5, 6},
                {7, 8}
            }
        }};

        Tensor xCopy;
        xCopy.setDataType(Aidge::DataType::Int32);
        xCopy.setBackend("opencv");
        xCopy = Array3D<int,2,2,2>{
        {
            {
                {1, 2},
                {3, 4}
            },
            {
                {5, 6},
                {7, 8}
            }
        }};

        Tensor xFloat;
        xFloat.setBackend("opencv");
        xFloat = Array3D<float,2,2,2>{
        {
            {
                {1., 2.},
                {3., 4.}
            },
            {
                {5., 6.},
                {7., 8.}
            }
        }};

        SECTION("Tensor features") {
            REQUIRE(x.nbDims() == 3);
            REQUIRE(x.dims()[0] == 2);
            REQUIRE(x.dims()[1] == 2);
            REQUIRE(x.dims()[2] == 2);
            REQUIRE(x.size() == 8);
        }

        SECTION("OpenCV tensor features") {
            REQUIRE(static_cast<TensorImpl_opencv<int>*>(x.getImpl().get())->getCvMat().rows == 2);
            REQUIRE(static_cast<TensorImpl_opencv<int>*>(x.getImpl().get())->getCvMat().cols == 2);
            REQUIRE(static_cast<TensorImpl_opencv<int>*>(x.getImpl().get())->getCvMat().dims == 2);
            REQUIRE(static_cast<TensorImpl_opencv<int>*>(x.getImpl().get())->getCvMat().total() == 4);
            REQUIRE(static_cast<TensorImpl_opencv<int>*>(x.getImpl().get())->getCvMat().channels() == 2);
        }

        SECTION("Access to array") {
            REQUIRE(static_cast<int*>(x.getImpl()->rawPtr())[0] == 1);
            REQUIRE(static_cast<int*>(x.getImpl()->rawPtr())[7] == 8);
        }

        SECTION("get function") {
            REQUIRE(x.get<int>({0,0,0}) == 1);
            REQUIRE(x.get<int>({0,0,1}) == 2);
            REQUIRE(x.get<int>({0,1,1}) == 4);
            REQUIRE(x.get<int>({1,1,0}) == 7);
            x.set<int>({1, 1, 1}, 36);
            REQUIRE(x.get<int>({1,1,1}) == 36);
        }

        SECTION("Pretty printing for debug") {
            REQUIRE_NOTHROW(x.print());
        }

        SECTION("Tensor (in)equality") {
            REQUIRE(x == xCopy);
            REQUIRE_FALSE(x == xFloat);
        }
    }
}

template <typename T>
cv::Mat createRandomMat(int rows, int cols) {
    cv::Mat randomMat(rows, cols, cv::DataType<T>::type);

    cv::randu(randomMat, cv::Scalar::all(0), cv::Scalar::all(255));

    return randomMat;
}

TEMPLATE_TEST_CASE("Tensor setBackend", "[Tensor_setBackend][OpenCV]", signed char, unsigned char, short, unsigned short, int, float, double) {
    constexpr int num_test_matrices = 50;

    SECTION("Test create tensor from opencv and convert to cpu") {
        // Generate random cv::mat
        for (int i = 0; i < num_test_matrices; ++i) {
            // Opencv mat have maximum 512 channels
            int ch = std::rand() % 512 + 1;
            int rows = std::rand() % 10 + 1;
            int cols = std::rand() % 10 + 1;

            std::vector<cv::Mat> channels;
            cv::Mat mat;

            for (int c = 0; c < ch; ++c){
                // Create a random matrix
                cv::Mat randomMat = createRandomMat<TestType>(rows, cols);
                // Add each random matrix to the vector 
                channels.push_back(randomMat);
            }
            // Merge the vector of cv mat into one cv mat
            cv::merge(channels, mat);

            // Check the size and datatype of the matrix
            REQUIRE(mat.rows == rows);
            REQUIRE(mat.cols == cols);
            REQUIRE(mat.channels() == ch);
            REQUIRE(mat.depth() == cv::DataType<TestType>::type);

            // Instanciate a tensor opencv
            auto tensorOcvToCpu = tensorOpencv(mat);

            // Check the size of the tensor
            REQUIRE(mat.rows == tensorOcvToCpu->dims()[1]);
            REQUIRE(mat.cols == tensorOcvToCpu->dims()[0]);
            REQUIRE(mat.channels() == tensorOcvToCpu->dims()[2]);

            // Check the matrix inside the tensor coorresponds to the matrix
            TensorImpl_opencv_* tImpl_opencv = dynamic_cast<TensorImpl_opencv_*>(tensorOcvToCpu->getImpl().get());
            auto mat_tensor = tImpl_opencv->getCvMat();
            
            REQUIRE(mat_tensor.size() == mat.size());
            REQUIRE(cv::countNonZero(mat_tensor != mat) == 0);

            // Use setBackend to change the backend from OpenCV to CPU
            tensorOcvToCpu->setBackend("cpu");

            // Split the mat into channels
            std::vector<cv::Mat> channels_split;
            cv::split(mat, channels_split);

            // Get the ptr to the std::vector<TestType> as a void * with rawPtr()
            auto cpu_ptr = static_cast<TestType*>(tensorOcvToCpu->getImpl()->rawPtr());

            // Compare the tensor cpu values with the cv mat in an elementwise fashion
            // Loop over channels
            for (int c = 0; c < ch; ++c) {
                // Loop over rows
                for (int i = 0; i < rows; ++i) {
                    // Loop over columns
                    for (int j = 0; j < cols; ++j) {
                        
                        TestType elementValue = channels_split[c].at<TestType>(i, j);
                        // The copyFrom with setBackend() copy the whole opencv Matrix
                        // The resulting data layout is [H=row, W=cols, C=channels]
                        TestType elementValue_cpu = cpu_ptr[i*(ch*cols)+ j*ch + c];

                        std::cout << "valeur matrice : " << elementValue << std::endl;
                        std::cout << "valeur tensor : " << elementValue_cpu << std::endl;
                        
                        REQUIRE(elementValue == elementValue_cpu);
                    }
                }
            }
        }
    }
}