Ask Your Question
0

Bug? Increasing memory usage per iteration when using findContours()

asked 2013-08-20 18:58:08 -0600

B. Bogart gravatar image

updated 2013-08-21 20:02:08 -0600

Hello all,

I've been trying to debug this for a month, sure it was my bad programming practise, but I think it may be a bug, so I'm asking here first before I report.

Consider the following code:

#include <sys/resource.h> // memory management.
#include <stdio.h>
#include <iostream>
#include <iomanip>

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/video/background_segm.hpp"

using namespace std;
using namespace cv;

// Load frame from disk.
void readFrame(int frameNum, Mat &frame) {
    // Construct filenames
    Mat image;
    stringstream number, filename;

    number << setw(7) << setfill('0') << frameNum; // expecting over 1e10 images over the installation period.
    filename << "../images/store-" << number.str() << ".jpg"; // assumes jpegs!//
    cout << "Loading filename: " << filename.str() << endl;

    image = imread( filename.str() );

    if (image.empty() or !image.data) {
        cout << "Input image empty:\n";
    }

    frame = image.clone();
}

// Class to hold the perceptual chunks.
class percepUnit {

    public:
        cv::Mat image; // percept itself
        cv::Mat mask; // alpha channel

        // constructor method
        percepUnit(cv::Mat &ROI, cv::Mat &alpha, int ix, int iy, int iw, int ih, int area)  {
            image = ROI.clone();
            mask = alpha.clone();
        }
};

// Segment foreground from background
void segmentForeground(list<percepUnit*> &percepUnitsForeground, Mat &foreground, Mat &frame) {
    Mat contourImage = Mat(foreground.rows, foreground.cols, CV_8UC1, Scalar::all(0));
    vector<vector<Point>> contours;
    int area;

    // The following causes strange spikes in memory usage:
    // find contours
    findContours(foreground, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    for (int idx = 0; idx < contours.size(); idx++) {

        area = contourArea(contours[idx]);

        if (area > 100) {

            percepUnit *thisUnit = new percepUnit(frame, contourImage, 0, 0, 100,100, area);
            percepUnitsForeground.push_back(thisUnit); // Append to percepUnits
        }
    }

    /* The following does not:
    findContours(foreground, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    for (int idx = 0; idx < contours.size(); idx++) {
        area = contourArea(contours[idx]);
    }*/

    /* Neither does this:
    for (int idx = 0; idx < 10; idx++) {
        percepUnit *thisUnit = new percepUnit(frame, contourImage, 0, 0, 100,100, area);
        percepUnitsForeground.push_back(thisUnit); // Append to percepUnits
    }*/
}

int main(int argc, const char** argv)
{
    int frameCount = 78298; 
    Mat frame, foreground;
    BackgroundSubtractorMOG2 MOG2model;
    list<percepUnit*> scratchPercepUnitsForeground;

    // add rusage stuff
    struct rusage usage; // memory usage.

    for(int i=0; i<= 75; i++)
    {
        // run full segmenter here.  (background disabled)

        readFrame(frameCount, frame); // was frame = readFrame();

        // Only process if this frame actually loaded (non empty)
        if ( not frame.empty() ) {

            MOG2model(frame,foreground); // Update MOG2 model, downscale?

            // before we segment again clear scratch
            // TODO how to delete the actual memory allocated? Run delete on everything?
            for (list<percepUnit*>::iterator percepIter = scratchPercepUnitsForeground.begin(); 
                 percepIter != scratchPercepUnitsForeground.end();
                 percepIter++) {

                delete *percepIter; // delete what we point to.
                //percepIter = scratchPercepUnitsForeground.erase(percepIter); // remove the pointer itself, and update the iterator.
            }
            // Added with EDIT1
            scratchPercepUnitsForeground.clear();

            // Segment the foreground regions and generate boolImage to extract from background.
            segmentForeground(scratchPercepUnitsForeground, foreground, frame);

        }

        frameCount++;

        getrusage(RUSAGE_SELF, &usage);
        cout << "DEBUG leakTest_bug_report " << i << " " << usage.ru_maxrss/1024.0 << endl;
    }

    return 0;
}

If you use the images available here, you will find that the program's memory usage increases, and it seems to increase with the number of foreground contours. Since I'm clearing my storage (scratchPercepUnitsForeground) for each frame, I don't ... (more)

edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted
1

answered 2013-08-21 18:59:00 -0600

Moster gravatar image

updated 2013-08-21 19:04:00 -0600

long rss = 0L;
FILE* fp = NULL;
if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
    cout << "cant open \n";     /* Can't open? */
if ( fscanf( fp, "%*s%ld", &rss ) != 1 )
{
   fclose( fp );
   cout << "cant read \n";      /* Can't read? */
}
fclose( fp );
cout << (size_t)rss  << "\n";

Found this way to measure the memory usage. This seems to work better than the ru_maxrss. At least it shows a more constant usage and most likely this is also the current usage and not maxrss.

edit flag offensive delete link more

Comments

I assume the RSS is measured in bytes?

B. Bogart gravatar imageB. Bogart ( 2013-08-21 20:03:07 -0600 )edit

Its the number of pages used in memory.

Moster gravatar imageMoster ( 2013-08-22 03:08:02 -0600 )edit

Could you explain the difference between rss and maxrss? If other processes request more memory, does maxrss shrink while rss stays stable?

How can I convert from pages to bytes?

B. Bogart gravatar imageB. Bogart ( 2013-08-22 10:51:39 -0600 )edit

I dont exactly know the difference, but the rss should be the current memory consumption. The maxrss actually should be the peak memory consumption (although from the looks of it, it does crap).

cout << (size_t)rss * (size_t)sysconf( _SC_PAGESIZE);

Moster gravatar imageMoster ( 2013-08-22 13:19:43 -0600 )edit
1

answered 2013-08-21 01:43:33 -0600

Moster gravatar image

I think the problem is your delete loop. You erase an object in the loop with list.erase(). The good thing about erase is that it automatically adjusts the list iterator. So your percepIter++ skips some of the list entries.

 for (list<percepUnit*>::iterator percepIter = scratchPercepUnitsForeground.begin(); 
             percepIter != scratchPercepUnitsForeground.end();) {

            delete *percepIter; // delete what we point to.
            percepIter = scratchPercepUnitsForeground.erase(percepIter); // remove the pointer itself, and update the iterator.
        }

Hopefully Im right :)

edit flag offensive delete link more

Comments

Thanks for trying. This bug first appeared when my list contained instances rather than pointers to instances. I then just used clear() to destruct all instances. This is my first rewrite using a list of pointers to see if that was the cause of the leak, but its not (ie the memory usage is exactly the same). Your comments saved me a little memory, but the memory still increases with the same pattern. I'll see if I can post an image of memory usage for further clarify.

B. Bogart gravatar imageB. Bogart ( 2013-08-21 11:57:46 -0600 )edit

Did you check what scratchPercepUnitsForeground.empty() returns after you leave the for loop. Btw. dont do scratchPercepUnitsForeground.clear(), stay with the erase() inside the for loop. If the for loop is correct, it will be empty. Please check that.

Also, you will have a problem in the last iteration in the loop. You are doing the segmentForeground, but not cleaning it up.

Moster gravatar imageMoster ( 2013-08-21 13:02:15 -0600 )edit

tried the edited version on a rusty xp/vs2008/ocv242/249 box (amd64, still) and could not reproduce.

could you tell, what opencv-version/compiler/os you're using ?

berak gravatar imageberak ( 2013-08-21 15:11:47 -0600 )edit

@Moster, See EDIT2 above. I changed the list of pointers to a list of instances to make the code cleaner and to avoid these problems. (suggested in this cross post: http://stackoverflow.com/questions/18346922/bug-increasing-memory-usage-per-iteration-when-using-findcontours) This code produces pretty clean (leak free) valgrind output, and yet I still see memory increasing.

@berak, did you use the test images I've supplied? tested on opencv 2.4.5 and 2.4.6.1, gcc 4.6.3, ubuntu precise. Two different machines.

B. Bogart gravatar imageB. Bogart ( 2013-08-21 15:45:18 -0600 )edit

B.Bogart, actually no(replaced that with like 1000xrandom access from my img dir). 13 mb download for a testcase was putting me off

berak gravatar imageberak ( 2013-08-21 16:16:55 -0600 )edit

@berak, I have only been able to reproduce the graph above with these images.

B. Bogart gravatar imageB. Bogart ( 2013-08-21 16:32:13 -0600 )edit

k,k. but with a different machine setup, as well. still might reconsider testing with your images ;)

berak gravatar imageberak ( 2013-08-21 17:41:06 -0600 )edit

Question Tools

Stats

Asked: 2013-08-20 18:58:08 -0600

Seen: 2,058 times

Last updated: Aug 21 '13