Differences of Using "Const Cv::Mat &", "Cv::Mat &", "Cv::Mat" or "Const Cv::Mat" as Function Parameters

Differences of using const cv::Mat &, cv::Mat &, cv::Mat or const cv::Mat as function parameters?

It's all because OpenCV uses Automatic Memory Management.

OpenCV handles all the memory automatically.

First of all, std::vector, Mat, and other data structures used by the functions and methods have destructors that deallocate the underlying memory buffers when needed. This means that the destructors do not always deallocate the buffers as in case of Mat. They take into account possible data sharing. A destructor decrements the reference counter associated with the matrix data buffer. The buffer is deallocated if and only if the reference counter reaches zero, that is, when no other structures refer to the same buffer. Similarly, when a Mat instance is copied, no actual data is really copied. Instead, the reference counter is incremented to memorize that there is another owner of the same data. There is also the Mat::clone method that creates a full copy of the matrix data.

That said, in order to make two cv::Mats point to different things, you need to allocate memory separately for them. For example, the following will work as expected:

void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input.clone(); // Input, Output now have seperate memory
Output += 1;
}

P.S: cv::Mat contains an int* refcount that points to the reference counter. Check out Memory management and reference counting for more details:

Mat is a structure that keeps matrix/image characteristics (rows and columns number, data type etc) and a pointer to data. So nothing prevents us from having several instances of Mat corresponding to the same data. A Mat keeps a reference count that tells if data has to be deallocated when a particular instance of Mat is destroyed.


Differences between sending cv::Mat, const cv::Mat, const cv::Mat& or cv::Mat& as arguments to a function:

  1. cv::Mat Input: pass a copy of Input's header. Its header will not be changed outside of this function, but can be changed within the function. For example:

    void sillyFunc(cv::Mat Input, cv::Mat& Output){
    Input = cv::Mat::ones(4, 4, CV_32F); // OK, but only changed within the function
    //...
    }
  2. const cv::Mat Input: pass a copy of Input's header. Its header will not be changed outside of or within the function. For example:

    void sillyFunc(const cv::Mat Input, cv::Mat& Output){
    Input = cv::Mat::ones(4, 4, CV_32F); // Error, even when changing within the function
    //...
    }
  3. const cv::Mat& Input: pass a reference of Input's header. Guarantees that Input's header will not be changed outside of or within the function. For example:

    void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
    Input = cv::Mat::ones(4, 4, CV_32F); // Error when trying to change the header
    ...
    }
  4. cv::Mat& Input: pass a reference of Input's header. Changes to Input's header happen outside of and within the function. For example:

    void sillyFunc(cv::Mat& Input, cv::Mat& Output){
    Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change
    ...
    }

P.S.2: I must point out that, in all the four situations (cv::Mat, const cv::Mat, const cv::Mat& or cv::Mat&), only the access to the Mat's header is restrained, not to the data it points to. For example, you can change its data in all the four situations and its data will indeed change outside of and within the function:

/*** will work for all the four situations ***/
//void sillyFunc(cv::Mat Input){
//void sillyFunc(const cv::Mat Input){
//void sillyFunc(const cv::Mat &Input){
void sillyFunc(cv::Mat &Input){
Input.data[0] = 5; // its data will be changed here
}

Workings of ``const Mat* samples`` param in OpenCV's ``calcCovarMatrix`` function?

I'm convinced that the authors of OpenCV preferred the const Mat*, int pair of arguments over a std::vector because this is more flexible.

Imagine a function which has to process a series of certain objects.

In C++, a series of objects can be stored with a std::vector. But what if the series of that objects is static const i.e. can be defined at compile time? A plain old C array of that objects might do the job as well.

A function which can process such a series of objects could accept a const std::vector&. If applied to a C array, a temporary vector instance had to be build. The C++ code is relatively simple but it leaves a queasy feeling in the stomache because the array contents has to be copied into the temporary std::vector instance just to pass it to the function.

The opposite case: the function accepts a pointer to start object and a count (like usual in C). Such a function can be applied to C arrays as well as to std::vector because std::vector provide a data() method which provides a const pointer to its first element and a size() method. Furthermore, it is granted that the vector elements are stored consecutively like in a C array.

So, my simple example:

#include <cassert>
#include <cmath>
#include <iostream>
#include <vector>

// Pi (from Windows 7 calculator)
const float Pi = 3.1415926535897932384626433832795;

struct Point {
float x, y;
};

std::ostream& operator<<(std::ostream &out, const Point &point)
{
return out << '(' << point.x << ", " << point.y << ')';
}

Point average(const Point *points, size_t size)
{
assert(size > 0);
Point sum = points[0];
for (size_t i = 1; i < size; ++i) {
sum.x += points[i].x; sum.y += points[i].y;
}
return { sum.x / (unsigned)size, sum.y / (unsigned)size };
}

static const Point square[] = {
{ -0.5f, -0.5f },
{ +0.5f, -0.5f },
{ +0.5f, +0.5f },
{ -0.5f, +0.5f }
};
static const size_t sizeSquare = sizeof square / sizeof *square;

int main()
{
// process points of a static const square (using average() with an array)
std::cout << "CoG of " << sizeSquare << " points of square: "
<< average(square, sizeSquare) << '\n';
// build a tesselated circle
std::vector<Point> circle;
const unsigned n = 16;
for (unsigned i = 0; i < n; ++i) {
const float angle = i * 2 * Pi / n;
circle.push_back({ std::sin(angle), std::cos(angle) });
}
// process points of that circle (using average() with a vector)
std::cout << "CoG of " << circle.size() << " points of circle: "
<< average(circle.data(), circle.size()) << '\n';
// done
return 0;
}

Output:

CoG of 4 points of square: (0, 0)
CoG of 16 points of circle: (-5.58794e-09, 4.47035e-08)

Live Demo on coliru

For convenience, the following alternative definition could be added for std::vector:

static inline Point average(const std::vector<Point> &points)
{
return average(points.data(), points.size());
}

A generic solution would provide instead an alternative with two iterators which could be applied to any container. (The C++ standard library is ful of examples for this.)

I can only assume that the OpenCV authors focused on performance rather than on flexibility (but that's only my personal guess).

OpenCV scalar used with template programming causes compilation error

You can't do it this way since Scalar has different number of dimensions than your mat and the operator = is not resolved in such the case. It's best explained by this inheritance diagram from OpenCV documentation:

Sample Image

As you can see, scalar is a 4 dimensional vector with template type, while each element in your mat is always 1 dimension, but different number of channels denoted as cn. You need to assign the value to each Mat element explicitly using cv::Scalar::operator[] to access each channel separately.

cannot convert from 'cv::Mat *' to 'const cv::Mat'

I am trying to pass an Image from main() to another function by reference

You're actually passing it by pointer. Change:

gpuProcessing( &cpuBayerImage );

to:

gpuProcessing( cpuBayerImage );

I'm guessing cpuBayerImage is just a cv::Mat. The function gpuProcessing takes a reference to const cv::Mat, but instead you are taking the address of it and giving it a pointer to cv::Mat. A pointer and a reference, despite being similar concepts, are not convertible to each other.

Invalid conversion from ‘const std::vectorcv::Mat*’ to ‘std::vectorcv::Mat*’ in c++14

In sliding_window_frames[sliding_window_frames.size()-1-k], the compiler does call the const version of operator[]. Which returns a pointer to const std::vector<cv::Mat>. But then you are trying to assign that pointer to imgs.cf_mat, which is a pointer to non-const std::vector<cv::Mat>. This violates const correctness, and so the compiler rightfully complains.

You have a function that can be called on a const instance of the object, trying to hand out mutable pointers to internals of that object. Either that function shouldn't have been declared const, or it should be handing out const pointers, or the internals it exposes should be marked mutable.

GMock : error: cannot convert ‘cv::MatExpr’ to ‘bool’ in return

The problem here is not the returning type, but the expected call. Specifically EXPECT_CALL(ThreshMock, convertToLab(dummyXY)) makes GMock check if the called parameter is indeed equal to dummyXY. By default it uses the == comparision operator.

But OpenCV declares their comparison as cv::MatExpr operator==(cv::Mat, cv::Mat). It returns a matrix of booleans instead of a bool.

Thus, you have to tell GMock how to match your expected call with a custom matcher.
You create matchers with use of MATCHER_... macros:

MATCHER_P(cvMatMatches, expected, "Match arg cvMat to be equal to expected") {
if (arg.size() != expected.size()) {
return false;
}
auto differingElems = (arg != expected);
return cv::countNonZero(differingElems) == 0;
}

And your test code becomes:

TEST(MockTest, ThreshTest) {
MockThresholder ThreshMock;
cv::Mat dummyXY = cv::Mat::ones(100, 100, CV_8U);
EXPECT_CALL(ThreshMock, convertToLab(cvMatMatches(dummyXY)))
.Times(1)
.WillOnce(::testing::Return(dummyXY));

ThreshMock.convertToLab(dummyXY);
}

Converting cv::Mat to IplImage*

cv::Mat is the new type introduce in OpenCV2.X while the IplImage* is the "legacy" image structure.

Although, cv::Mat does support the usage of IplImage in the constructor parameters, the default library does not provide function for the other way. You will need to extract the image header information manually. (Do remember that you need to allocate the IplImage structure, which is lack in your example).

Convert Mat to Array/Vector in OpenCV

If the memory of the Mat mat is continuous (all its data is continuous), you can directly get its data to a 1D array:

std::vector<uchar> array(mat.rows*mat.cols*mat.channels());
if (mat.isContinuous())
array = mat.data;

Otherwise, you have to get its data row by row, e.g. to a 2D array:

uchar **array = new uchar*[mat.rows];
for (int i=0; i<mat.rows; ++i)
array[i] = new uchar[mat.cols*mat.channels()];

for (int i=0; i<mat.rows; ++i)
array[i] = mat.ptr<uchar>(i);

UPDATE: It will be easier if you're using std::vector, where you can do like this:

std::vector<uchar> array;
if (mat.isContinuous()) {
// array.assign(mat.datastart, mat.dataend); // <- has problems for sub-matrix like mat = big_mat.row(i)
array.assign(mat.data, mat.data + mat.total()*mat.channels());
} else {
for (int i = 0; i < mat.rows; ++i) {
array.insert(array.end(), mat.ptr<uchar>(i), mat.ptr<uchar>(i)+mat.cols*mat.channels());
}
}

p.s.: For cv::Mats of other types, like CV_32F, you should do like this:

std::vector<float> array;
if (mat.isContinuous()) {
// array.assign((float*)mat.datastart, (float*)mat.dataend); // <- has problems for sub-matrix like mat = big_mat.row(i)
array.assign((float*)mat.data, (float*)mat.data + mat.total()*mat.channels());
} else {
for (int i = 0; i < mat.rows; ++i) {
array.insert(array.end(), mat.ptr<float>(i), mat.ptr<float>(i)+mat.cols*mat.channels());
}
}

UPDATE2: For OpenCV Mat data continuity, it can be summarized as follows:

  • Matrices created by imread(), clone(), or a constructor will always be continuous.
  • The only time a matrix will not be continuous is when it borrows data (except the data borrowed is continuous in the big matrix, e.g. 1. single row; 2. multiple rows with full original width) from an existing matrix (i.e. created out of an ROI of a big mat).

Please check out this code snippet for demonstration.



Related Topics



Leave a reply



Submit