# Passing cv::Mat as argument

Often times when we pass cv::Mat, we forget one important thing: OpenCV matrix does not respect the const modifier. In this post I would like to discuss what happens when cv::Mat is passed around.

## Changing the header of cv::Mat

Consider the following examples1, where we want to change what the datastructure is. Note that we are trying to modify the HEADER of the cv::Mat, hence the behavior.

### Passing a copy – cv::Mat.

This is totally OK, but the scoping rules apply – only local variable will be changed, and no changes will happen outside of the function.

1
2
3
void Foo(cv::Mat out) {
out = cv::Mat::ones(4, 4, CV_32F);  // OK, but only local changes happen.
}


### Passing a reference – cv::Mat&.

This is totally fine, and the changes will propagate to outside of the function.

1
2
3
void Bar(cv::Mat& out) {
out = cv::Mat::ones(4, 4, CV_32F);  // OK, Changes happen everywhere.
}


### Passing a const – const cv::Mat.

This is NOT OK, even for local changes.

1
2
3
void Baz(const cv::Mat out) {
out = cv::Mat::ones(4, 4, CV_32F);  // Error!!! Cannot change the header!
}


### Passing a const reference – const cv::Mat&.

This is NOT OK!

1
2
3
void Qux(const cv::Mat& out) {
out = cv::Mat::ones(4, 4, CV_32F);  // Error!!! Cannot change the header!
}


From the above examples, we see that cv::Mat guarantees that the header of the data structure will respect the const modifiers. However, if we look at the header for the core.hpp we notice that the data in the structure cv::Mat is stored as a raw pointer:

Because of that, when passing the cv::Mat one has to be extra careful. Because the way the data structure is implemented, passing it around as const or const & has slightly different behavior from what is expected.

### Changing the data in the cv::Mat

Now take a look at the functions below, no matter what is being passed, the modifications are allowed – cv::Mat doesn’t respect the const modifiers!2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Foo(cv::Mat out) {
out.data[0] = 5;  // OK globally!
}

void Bar(cv::Mat& out) {
out.data[0] = 5;  // OK globally!
}

void Baz(const cv::Mat out) {
out.data[0] = 5;  // OK globally!
}

void Qux(const cv::Mat& out) {
out.data[0] = 5;  // OK globally!
}


One might say that the examples above are artificial, and anyone writing code, will see it immediately. However, there are cases when the changes will happen in places where you won’t expect it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Foo {
void bar(const cv::Mat& out) const {
cv::Mat baz = out(cv::Rect(0, 0, 10, 10));
cv::cvtColor(baz, baz, cv::COLOR_BGR2RGB);
// Although we don't modify the out, and baz is only a subset,
// it is still changed!!!
}
}
// ...
int main() {
// ...
Foo foo;
foo.bar(image); // The colorspace of image is flipped now!!!
// ...
}


## Conclusion

When handling the cv::Mat – be very careful, and treat it as a smart pointer :).

1. https://stackoverflow.com/a/23486280/3606192

2. Technically it does – cv::Mat is never changed – only the data it’s pointing to.

Updated on

### Zafar Takhirov

I am a recent PhD graduate from Boston University. While my work focuses on digital design,error mitigation, and machine learning, my non-work interests range widely from information theory (go Shannon!), quantum computing, grandfather paradox, Star Trek, Little Mermaid, 'why is the grass green?', 1Q84, etc., etc., etc. If you want to talk about, well, anything - just ping me.

### Hungarian Algorithm

Problem StatementThe problem of the assignment is fairly straight-forward.To describe it, let us consider several examples There are wo...… Continue reading