Ask Your Question
3

What is the best practise for passing cv::Mats around

asked 2013-06-06 19:28:20 -0600

B. Bogart gravatar image

updated 2013-07-09 16:53:27 -0600

Hello all,

I've been working on a project for a while not realizing I've been creating a lot of memory leaks as I had not run the program for long enough until recently. It's a fairly large program, so I'll provide a single example that should describe the overall problem.

Firstly, my image data is stored in a class (percepUnit) which has a number of cv::Mat members:

class percepUnit {
    public:
        cv::Mat image; // percept itself
        cv::Mat mask; // alpha channel
        cv::Mat alphaImage; // mask + image.

        // Create RGBA image from RGB+Mask
        cv::Mat applyAlpha(cv::Mat image, cv::Mat mask);

}

I want to apply the mask to the image to create the alpha image. This is what the class method looks like:

// Apply the mask as an alpha channel
cv::Mat percepUnit::applyAlpha(cv::Mat image, cv::Mat mask) {
    vector<cv::Mat> channels;
    cv::Mat alphaImage;
    if (image.rows == mask.rows and image.cols == mask.cols) {
        cv::split(image,channels); // break image into channels
        channels.push_back(mask); // append alpha channel

        cv::merge(channels,alphaImage); // combine channels
    }

    return alphaImage;
}

Which is used in the percepUnit constructor:

percepUnit::percepUnit(cv::Mat ROI, cv::Mat alpha, int ix, int iy, int iw, int ih, int area) {

    // Deep copies.
    image = ROI.clone();
    mask = alpha.clone();

    // Make alpha image 
    // there may be a more efficient way of doing this. (drawMat() for RGBA?)
    this->alphaImage = applyAlpha(image, mask);

After some searching it does seem like returning cv::Mats is not a good idea.

This is the valgrind output:

==25374== 13,422,700 bytes in 21 blocks are possibly lost in loss record 17,975 of 17,982
==25374==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==25374==    by 0x33C0BA90: cv::fastMalloc(unsigned long) (in /usr/local/lib/libopencv_core.so.2.4.5)
==25374==    by 0x33C51BF1: cv::Mat::create(int, int const*, int) (in /usr/local/lib/libopencv_core.so.2.4.5)
==25374==    by 0x33C7313B: cv::_OutputArray::create(int, int const*, int, int, bool, int) const (in /usr/local/lib/libopencv_core.so.2.4.5)
==25374==    by 0x33D98DA7: cv::merge(cv::Mat const*, unsigned long, cv::_OutputArray const&) (in /usr/local/lib/libopencv_core.so.2.4.5)
==25374==    by 0x33D999D0: cv::merge(cv::_InputArray const&, cv::_OutputArray const&) (in /usr/local/lib/libopencv_core.so.2.4.5)
==25374==    by 0x33D99ABD: cv::merge(std::vector<cv::Mat, std::allocator<cv::Mat> > const&, cv::_OutputArray const&) (in /usr/local/lib/libopencv_core.so.2.4.5)
==25374==    by 0x415A95: percepUnit::applyAlpha(cv::Mat, cv::Mat) (percepUnit.cpp:19)
==25374==    by 0x415C73: percepUnit::percepUnit(cv::Mat, cv::Mat, int, int, int, int, int) (percepUnit.cpp:212)

Should I rewrite the applyAlpha method to take the cv::Mat &alphaImage as an argument and forgo the return statement?

EDIT: Here is my new applAlpha function:

// Apply the mask as an alpha channel
void percepUnit::applyAlpha(const cv::Mat &image, const cv::Mat &mask, cv::Mat &alphaImage) {
    // Avoid merge, helps with memory leak?
    cv::Mat src[] = {image, mask ...
(more)
edit retag flag offensive close merge delete

5 answers

Sort by ยป oldest newest most voted
11

answered 2013-06-07 02:07:12 -0600

SR gravatar image

updated 2013-06-10 16:54:28 -0600

Technically, there is not a big difference between passing cv::Mat or cv::Mat& just because this specific class uses smart pointers internally. (This does not apply to arbitrary C++ objects.)

Input / Output Arguments: If the argument is an input argument I pass it as const reference const cv::Mat&. If it is an output or an input-output argument then I pass it as cv::Mat&. Using references avoids copying the matrix header (the data is never copied unless clone() is a called) and const marks my arguments as input arguments.

Function return values: If a function returns a single matrix I usually return it as cv::Mat as it does not involve copying the data (just the matrix header) and it further allows to mark the instance as const directly in the calling code: const cv::Mat mat = f(...); This short notation also implicitly marks mat as read-only which is quite usefuly when using OpenMP in your programs as you do not need to clare it shared or firstprivate.

edit flag offensive delete link more

Comments

Thank you. Combining both answers seems to have helped with the memory leak, but it still exists. (see above)

B. Bogart gravatar imageB. Bogart ( 2013-06-07 16:09:59 -0600 )edit

Do you ever free Rect *boundingRect?

SR gravatar imageSR ( 2013-06-08 03:25:04 -0600 )edit
1

@SR is right, you should 'delete' what you have 'new'ed ;). Also note that you don't need to release your matrices, they will be released internally if the scope is left (i.e. mask.release(); isn't necessary).

Guanta gravatar imageGuanta ( 2013-06-08 05:43:57 -0600 )edit

Thank you! I'm running another test now. I was not looking at Rect because the leak was so big, but then I am calling that function quite often. Still, is it possible for an opencv program to have no leaks at all? (ie are their minor leaks in the code itself?)

B. Bogart gravatar imageB. Bogart ( 2013-06-08 20:27:23 -0600 )edit

In my experience you'll find minor leaks in most programs. But these may also be false positives found by the memory checker.

SR gravatar imageSR ( 2013-06-10 13:35:45 -0600 )edit

See my second edit above. Still having a significant memory leak.

B. Bogart gravatar imageB. Bogart ( 2013-07-10 13:03:36 -0600 )edit
7

answered 2013-06-07 02:32:23 -0600

Vladislav Vinogradov gravatar image

updated 2013-06-07 02:43:18 -0600

You can also use mixChannels function (it must be faster than split/merge approach, because it doesn't require additional buffers):

cv::Mat percepUnit::applyAlpha(const cv::Mat& image, const cv::Mat& mask) 
{
    cv::Mat alphaImage;
    cv::Mat src[] = {image, mask};
    int from_to[] = {0,0, 1,1, 2,2, 3,3};
    cv::mixChannels(src, 2, &alphaImage, 1, from_to, 4);
    return alphaImage;
}
edit flag offensive delete link more
2

answered 2015-11-19 14:28:20 -0600

matman gravatar image

I recommend you to use cv::InputArray, cv::OutputArray and cv::InputOutputArray (link) instead of cv::Mat&. Its much more flexible. In OpenCV 3.0 you can pass cv::Mat, std::vector, cv::UMat and even cv::cuda::GpuMat with it.

You can pass through cv::InputArray into OpenCV functions, where OpenCLwill be used, if available (cv::UMat).

And in my oppinion its cleaner to code with. You can take a look in the source code to learn how to use it.

edit flag offensive delete link more
1

answered 2015-11-19 06:41:50 -0600

emiswelt gravatar image

Since it's not explicitly stated yet, the following is dangerous, since the default Mat constructor does not copy data and a stack reference, wrapped into a Mat object, is returned.

cv::Mat calculateStuff()
{
    int data[] = {1, 2, 3};
    return cv::Mat(1, 3, data, CV_8UC1);
}
edit flag offensive delete link more
0

answered 2014-02-21 11:22:31 -0600

B. Bogart gravatar image

It was recommended by @Nghia that I try and make the percepts a constant size. Sure enough, if I fix the dimensions and type of the cv::Mat members of percepUnit, then the leak disappears.

So it seems to me this is a bug in OpenCV that effects calling clone() and copyTo() on Mats of different sizes that are class members. So far unable to reproduce in a simple program. The leak does seem small enough that it may be the headers leaking, rather than the underlying image data.

edit flag offensive delete link more

Question Tools

3 followers

Stats

Asked: 2013-06-06 19:28:20 -0600

Seen: 46,251 times

Last updated: Nov 19 '15