Home > database >  Using std::async with a method receiving a cv::OutputArray in order to assign it doesn't work
Using std::async with a method receiving a cv::OutputArray in order to assign it doesn't work

Time:05-18

I have the following function:

void MyClass::myFunc(cv::OutputArray dst) const {
    cv::Mat result;
    ...
    dst.assign(result);
}

If I run it like this:

cv::Mat result;
myFunc(result);
otherFunc(result);

It works fine and otherFunc recieves result modified by myFunc.

But if I use std::async like this:

cv::Mat result;
auto resultFuture = std::async(&MyClass::myFunc, this, result);
resultFuture.wait();
otherFunc(result);

otherFunc receives empty result. What am I doing wrong?

CodePudding user response:

The root cause is that passing arguments by reference (&) to a function to run via std::async is problematic. You can read about it here: Passing arguments to std::async by reference fails (in your case there is no a compilation error, but the link explains the issue in general).

And in your case you use cv::OutputArray which is defined in opencv as a refernce type:

typedef const _OutputArray& OutputArray;

I assume you wanted the reference semantics, since you expected your result object to be updated by myFunc.

The solution is to use std::ref.
But since the result object you pass is a cv::Mat, it preferable and more straightforward that myFunc will receive a cv::Mat&.
I also managed to produce a solution using a cv::OutputArray, but it requires an ugly cast (in addition to the std::ref). It works fine on MSVC, but I am not sure it is will be generally valid.

Below is the code demostrating these 2 options. I recomend to use the 1st approach if you can. You can call otherFunc(result); at the point where I print the dimensions of result after it is initialized.

#include <opencv2/core/core.hpp>
#include <future>
#include <iostream>

// Test using a cv::Mat &:
class MyClass1
{
    void myFunc(cv::Mat & dst) const
    {
        cv::Mat result(4, 3, CV_8UC1);
        result = 1; // ... initialize some values
        dst = result;   // instead of using cv::OutputArray::assign
    }
public:
    void test()
    {
        cv::Mat result;
        std::cout << "MyClass1: before: " << result.cols << " x " << result.rows << std::endl;
        auto resultFuture = std::async(&MyClass1::myFunc, this, std::ref(result));
        resultFuture.wait();
        // Here result will be properly set.
        std::cout << "MyClass1: after: " << result.cols << " x " << result.rows << std::endl;
    }
};

// Test using a cv::OutputArray:
class MyClass2
{
    void myFunc(cv::OutputArray dst) const
    {
        cv::Mat result(4, 3, CV_8UC1); 
        result = 1; // ... initialize some values
        dst.assign(result);
    }
public:
    void test()
    {
        cv::Mat result;
        std::cout << "MyClass2: before: " << result.cols << " x " << result.rows << std::endl;
        auto resultFuture = std::async(&MyClass2::myFunc, this, std::ref(static_cast<cv::OutputArray>(result)));
        resultFuture.wait();
        // Here result will be properly set.
        std::cout << "MyClass2: after: " << result.cols << " x " << result.rows << std::endl;
    }
};

int main()
{
    // Test receiving a cv::Mat&:
    MyClass1 m1;
    m1.test();
    // Test receiving a cv::OutputArray:
    MyClass2 m2;
    m2.test();
    return 0;
}
  • Related