0

I want write a customized function which use the cv::InputArray as parameters. Within the function, I understand I can use cv::InputArray::getMat to obtain a header of the input cv::Mat.

I have some confusions while passing std::vector to the cv::InputArray.

1.If I pass std::vector into a function, can I still get the std::vector in the function? For example:

void foo(cv::InputArray _input)
{
    std::vector<cv::Point2f> input = _input.getVector() // getVector function doesn't exist
}

std::vector<cv::Point2f> a;
foo(a);

2.If I pass std::vector to the function and use the getMat to get a cv::Mat within the function, how the mat will looks like?

Poly has made a clear explanation in case of std::vector<char>. What if I want to get std::vector<cv::Point2f> in the function, any suggestions?

Thanks very much.

linzhang.robot
  • 359
  • 1
  • 8
  • 23

2 Answers2

3

When you pass vector to the function which takes InputArray, you implicitly call converting constructor InputArray::InputArray(vector). (Converting constructor is explained here: https://stackoverflow.com/a/15077788/928387)

In this constructor, the vector's pointer is simply assigned to obj member variable in InputArray. If you use OpenCV 3.0, InputArray has getObj() method, so you can get vector by the following way:

// Only works on OpenCV 3.0 or above
const std::vector<Point2f>& input = *(const std::vector<Point2f>*)_input.getObj();

If you use OpenCV 2.X, you can use InputArray::getMat(). It returns Mat object that has a pointer to the data. So you can do the following way as well.

// Should Work on any OpenCV version
cv::Mat mat = _input.getMat();
Point2f *data = (Point2f *)mat.data;
int length = mat.total();
std::vector<Point2f> input;
input.assign(data, data + length);

Regarding your second question, if you call InputArray::getMat() on InputArray object with N element, it returns (N*1) matrix.

Community
  • 1
  • 1
Poly
  • 1,053
  • 1
  • 11
  • 16
  • Hi, Thanks for your answer. I tried both ways but neither of them worked. For the first way, the InputArray doesn't have getObj() method so I assume you mean getMat(). This give me compilation error like: cannot convert from 'cv::Mat' to 'const std::vector<_Ty> *'. For the second way, I want to recover vector of cv::Point2f, but the results were not correct. – linzhang.robot Sep 10 '14 at 12:59
  • Seems like only OpenCV 3.0 has getObj() method. I update my original answer to include version description and use Point2f. I successfully recover vector on my machine using OpenCV 2.4.8. Please check it. – Poly Sep 10 '14 at 22:01
1

Note that InputArray::getObj() returns the object by whom it was created. So casting only works, if _input was created using a std::vector! This can be checked via InputArray::isVector().

Otherwise, a new std::vector object must be created. Unfortunately, there is no way to tell std::vector to use existing data. I think it is not even possible when using your own allocator. If you still want a std::vector, use pointers/iterators (either in constructor or in std::vector::assign()) to create a new object with a copy of the data. You can obtain the size directly from _input via InputArray::total().

Vector

Based on the previous observations, I combined the attempts proposed by Poly.

std::vector<Point2f> *input;
if (_input.isVector()) {
    input = static_cast<std::vector<Point2f>*>(_input.getObj());
} else {
    size_t length = _input.total();
    Point2f* data = reinterpret_cast<Point2f*>(_input.getMat().data);
    input = new std::vector<Point2f>(data, data + length);
}

Template

To reuse code for other types, I recommend using templates.

template<class T>
std::vector<T>& getVec(InputArray _input) {
    std::vector<T> *input;
    if (_input.isVector()) {
        input = static_cast<std::vector<T>*>(_input.getObj());
    } else {
        size_t length = _input.total();
        T* data = reinterpret_cast<T*>(_input.getMat().data);
        input = new std::vector<T>(data, data + length);
    }
    return *input;
}

Further, you should check if types are compatible via InputArray::type().

Arrays

If you just want easy indexing, you could of course use standard C-style arrays (Note that C++-style std::array also needs to copy data).

Point2f* data = reinterpret_cast<Point2f*>(_input.getMat().data);

Then you can access data via

Point2f p = data[5];
Community
  • 1
  • 1
darkdragon
  • 392
  • 5
  • 13