Search code examples
c++opencvtorchlibtorch

How to Convert a torch::Tensor into a cv::InputArray?


I'm trying to use the cv::getAffineTransform(), and I'm stuck on how to convert the tensors into the corrosponding cv::InputArray. I have tried these to face access violation :

torch::Tensor src = torch::tensor({ 1.1, 2.0, 3.3 });
torch::Tensor dst = torch::tensor({ 1.1, 2.0, 3.3 });

cv::Mat input_array;
cv::Mat destination_array;
std::memcpy(input_array.data, src.data_ptr<float>(), sizeof(float) * src.numel());
std::memcpy(destination_array.data, dst.data_ptr<float>(), sizeof(float) * dst.numel());

What am I doing wrong here? Is there a way to share the underlying buffer and avoid the copying at all?


Solution

  • It truns out specifying the dimensions in the cv::Mat was necessary in order for a successful copy! That is, I needed to do this :

    cv::Mat input_array (3, 1, CV_32FC1);
    cv::Mat destination_array (3, 1, CV_32FC1);
    
    std::memcpy(input_array.data, src.data_ptr<float>(), sizeof(float) * src.numel());
    std::memcpy(destination_array.data, dst.data_ptr<float>(), sizeof(float) * dst.numel());
    
    std::cout << input_array << std::endl;
    std::cout << destination_array << std::endl;
    

    And this no more results in an Access violation. and I can verify that the values do get copied :

    [1.1;
     2;
     3.3]
    [1.1;
     2;
     3.3]
    

    Since the former example was using a madeup input data, the cv::getAffineTransform() will crash so here is a more realistic input and output you can run and see that it works:

    Method 1 : Using std::memcpy to copy the data:

    torch::Tensor src = torch::tensor({ {137.47012, 62.52604}, {170.50703, 64.21498}, {154.49675, 80.78379} });
    torch::Tensor dst = torch::tensor({ {38.294598, 51.6963}, {73.5318, 51.5014}, {56.0252, 71.7366} });
    
    std::cout << "src.shapes: " << src.sizes() << std::endl;
    std::cout << "dst.shapes: " << dst.sizes() << std::endl;
    
    int rows = src.sizes()[0];
    int cols = (src.sizes().size() == 1) ? 1 : src.sizes()[1];
    
    cv::Mat input_array (rows, cols, CV_32FC1);
    cv::Mat destination_array (rows, cols, CV_32FC1);
    
    std::memcpy(input_array.data, src.data_ptr<float>(), sizeof(float) * src.numel());
    std::memcpy(destination_array.data, dst.data_ptr<float>(), sizeof(float) * dst.numel());
    
    std::cout << "input_array:\n" << input_array << std::endl;
    std::cout << "destination_array:\n" << destination_array << std::endl;
    
    auto tfm = cv::getAffineTransform(input_array, destination_array);
    std::cout << "tfm:\n" << tfm << std::endl;
    

    And
    Method 2 : Using the underlying buffer instead of copying :

    int height = src.sizes()[0];
    int width = src.sizes()[1];
    cv::Mat input_array(cv::Size{width, height }, CV_32F, src.data_ptr<float>());
    cv::Mat destination_array(cv::Size{ width, height }, CV_32F, dst.data_ptr<float>());