Ask Your Question
0

How to free cv::Mat from memory

asked 2019-04-29 09:07:11 -0500

t3rb3d gravatar image

I have a sequence of images (frames) from a camera as an input, what is stored in std::queue<cv::Mat> buffer container. After the processing algorithm is finished for a frame then it is popped. My problem is that somehow the memory usage is constantly rising, however the container size buffer.size() is maximum 1 (so the algorithm is 'eating' up the buffer properly and pops the images).

I would like demonstrate my problem with a minimal reproducible code:

cv::Mat current_frame(500, 500, CV_16UC3);
std::queue<cv::Mat> buffer;
while(true)
{
    buffer.push(current_frame.clone());
    buffer.front().release();
    buffer.pop();
}

This code results in constantly rising memory usage. (Ubuntu system, opencv 3.4.6 version)

My question is that how can you properly release a cv::Mat from memory.

edit retag flag offensive close merge delete

1 answer

Sort by ยป oldest newest most voted
1

answered 2019-04-29 11:12:41 -0500

the code below is from an older question maybe it will be a sample for use of std::queue<cv::Mat> ( i have no deep knowledge about the code )

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/calib3d.hpp"

#include <map>
#include <queue>
#include <thread>
#include <mutex>
#include <atomic>
#include <string>
#include <iostream>

/** @brief Example Class that grabs from cv::VideoCapture into a queue.

It can be used as worker thread.

Even if you grab N frames and create N x Mat in the queue
only few blocks will be really allocated thanks to @c std::vector
and @c cv::Mat memory recycling. Look at @c matMemoryCounter to
know how many memory blocks has been allocated
@author PkLab answer to question:
@see http://answers.opencv.org/question/95193/capture-from-two-webcam/
@note It can be done much better but it's better than nothing :)
@note It requires C++11 or later
*/
class GrabberThread {
private:
    int device;
    std::mutex mtx;
    std::queue<cv::Mat > buffer;
    std::atomic<bool> grabOn;
    cv::VideoCapture cap;
    size_t bufSizeMax;
    std::map<unsigned char*, int> matMemoryCounter;
public:
    GrabberThread() : grabOn(false), device(0), bufSizeMax(0) {};
    ~GrabberThread()
    {
        grabOn.store(false);
        cap.release();
        while (!buffer.empty())
            buffer.pop();
        matMemoryCounter.clear();

    }
    /* @brief initialize members and open the capture device
    @return true if the device has successfully opened
    */
    bool Init(int dev = 0)
    {
        device = dev;
        cap.open(device);
        return cap.isOpened();
    }

    /* @brief Disable the grabbing loop control var.
    This will stop the grabbing thread
    */
    void StopGrabing()
    {
        grabOn.store(false);
    }

    /** @brief This is the thread that grabs into a queue
    @note In real application locks and multiple copyTo can be avoided
    using better queue/circular buffer!
    */
    void GrabThread()
    {
        matMemoryCounter.clear();
        uchar * frameMemoryAddr;

        if (!cap.isOpened()) cap.open(device);
        if (!cap.isOpened()) return;

        cv::Mat tmp;
        grabOn.store(true);
        while (grabOn.load() == true) //this is lock free
        {
            //read() will wait for cam FPS. 
            //keep grab out of lock so that
            //idle time can be used by other threads
            if (!cap.read(tmp))
                continue;
            //get lock only when we have a frame
            mtx.lock();
            /** @warning Avoid @c buffer.push_back(tmp) because it stores items by reference.
            We have to create a new cv::Mat for each frame
            STL and cv::Mat will recycle memory
            */
            buffer.push(cv::Mat(tmp.size(), tmp.type()));
            tmp.copyTo(buffer.back());
            //count how many times this memory block has been used
            frameMemoryAddr = buffer.front().data;
            matMemoryCounter[frameMemoryAddr]++;
            bufSizeMax = std::max(bufSizeMax, buffer.size());

            mtx.unlock();
            /* SHOW THE FRAME if do you want
            string winName = "Live Thread Dev: " + std::to_string(device);
            int font = CV_FONT_HERSHEY_PLAIN;
            cv::putText(tmp, winName, cv::Point(10, 10), font, 1, cv::Scalar(0, 255, 0));
            imshow(winName, tmp);
            cv::waitKey(1);    //just for imshow
            */
        } // while
    } // func

      /** @brief Get the oldest grabbed frame if available

      The frame is copied from the buffer than it can used freely

      @Param [out]frame the the oldest grabbed frame. if not available it will be empty
      @Param [out]pBufSize a pointer where returns current buffer size. If 0 it will be ignored
      @Param [out]pBufSizeMax ...
(more)
edit flag offensive delete link more

Comments

I tried this from the sample code:

    (*buffer).push(cv::Mat(tmp.size(), tmp.type()));
    tmp.copyTo((*buffer).back());

but unfortunately the issue remains.

t3rb3d gravatar imaget3rb3d ( 2019-04-29 12:30:25 -0500 )edit

Question Tools

1 follower

Stats

Asked: 2019-04-29 09:07:11 -0500

Seen: 903 times

Last updated: Apr 29 '19