OpenCV cv::Mat without copying and weird memory ownership

Recently I found an interesting OpenCV behaviour. OpenCV cv::Mat is built to be easy to use. It will allocate, free and reallocate it's own internal data. That's fine for most of the uses, but what if you are sensitive to extra memory allocations? As you might expect, C++ developers very often track the memory ownership of any allocated piece of memory, especially when copying memory is not an option. To use cv::Mat without copying the buffer, you can use this class constructor that does not own the memory: cv::Mat::Mat( Size size, int type, void *data, size_t step = AUTO_STEP ); In the constructor documentation, you can read: Pointer to the user data. Matrix constructors ... do not allocate matrix data. Instead, ... which means that no data is copied. ... The external data is not automatically deallocated, so you should take care of it. So far, so good, let's see if this is true cv::Mat original_image = cv::imread(image_path); std::vector image_data(original_image.rows * original_image.cols); cv::Mat gray_image(original_image.rows, original_image.cols, CV_8UC1, image_data.data()); cv::cvtColor(original_image, gray_image, cv::COLOR_BGR2GRAY); printf("AFTER: cv::Mat data: %p\n", image_data.data()); printf("AFTER: image_data data: %p\n", gray_image.data); It prints AFTER: cv::Mat data: 0x138168000 AFTER: image_data data: 0x138168000 Ok, that's great. The data is the same. However, what happens if the given data array is not big enough? Let's change the example to remove one byte of the size. cv::Mat gray_image(original_image.rows, original_image.cols - 1, CV_8UC1, image_data.data()); Surprisingly it works. Why? void cv::cvtColor(InputArray src, OutputArray dst, /* ... */); I found that OutputArray, when the allocated data is not big enough, allocates a new array - and that's where I found the surprise. If the data is not owned by cv::Mat, the pointer is silently replaced by a new allocated one, owned by cv::Mat If I run the example again, let's see the addresses: AFTER: cv::Mat data: 0x160168000 AFTER: image_data data: 0x1601e0000 In conclusion, if you are using cv::Mat providing the memory yourself, you need to be sure the allocated buffer is enough, and preferably also check the image.data to verify if the provided buffer was used. Original post: thiagocafe/OpenCV Mat without copying and weird memory ownership

May 1, 2025 - 03:45
 0
OpenCV cv::Mat without copying and weird memory ownership

Recently I found an interesting OpenCV behaviour.

OpenCV cv::Mat is built to be easy to use. It will allocate, free and reallocate it's own internal data. That's fine for most of the uses, but what if you are sensitive to extra memory allocations?

As you might expect, C++ developers very often track the memory ownership of any allocated piece of memory, especially when copying memory is not an option.

To use cv::Mat without copying the buffer, you can use this class constructor that does not own the memory:

cv::Mat::Mat(
  Size size,
  int type,
  void *data,
  size_t step = AUTO_STEP
);

In the constructor documentation, you can read:

Pointer to the user data. Matrix constructors ... do not allocate matrix data. Instead, ... which means that no data is copied. ... The external data is not automatically deallocated, so you should take care of it.

So far, so good, let's see if this is true

cv::Mat original_image = cv::imread(image_path);
std::vector<uint8_t> image_data(original_image.rows * original_image.cols);
cv::Mat gray_image(original_image.rows, original_image.cols, CV_8UC1, image_data.data());

cv::cvtColor(original_image, gray_image, cv::COLOR_BGR2GRAY);
printf("AFTER: cv::Mat data: %p\n", image_data.data());
printf("AFTER: image_data data: %p\n", gray_image.data);

It prints

AFTER: cv::Mat data: 0x138168000
AFTER: image_data data: 0x138168000

Ok, that's great. The data is the same. However, what happens if the given data array is not big enough?

Let's change the example to remove one byte of the size.

cv::Mat gray_image(original_image.rows, original_image.cols - 1, CV_8UC1, image_data.data());

Surprisingly it works. Why?

void cv::cvtColor(InputArray src, OutputArray dst, /* ... */);

I found that OutputArray, when the allocated data is not big enough, allocates a new array - and that's where I found the surprise. If the data is not owned by cv::Mat, the pointer is silently replaced by a new allocated one, owned by cv::Mat

If I run the example again, let's see the addresses:

AFTER: cv::Mat data: 0x160168000
AFTER: image_data data: 0x1601e0000

In conclusion, if you are using cv::Mat providing the memory yourself, you need to be sure the allocated buffer is enough, and preferably also check the image.data to verify if the provided buffer was used.

Original post: thiagocafe/OpenCV Mat without copying and weird memory ownership