Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

How to replace an instance with cv::Mat members with another instance via pointer?

I'm doing online destructive clustering (clusters replace clustered objects) on a list of class instances (stl::list<percepunit>).

Background

My list of current percepUnits is: stl::list<percepUnit> units; and for each iteration I get a new list of input percepUnits stl::list<percepUnit> scratch; that need to be clustered with the units.

I want to maintain a fixed number of percepUnits (so units.size() is constant), so for each new scratch percepUnit I need to merge it with the nearest percepUnit in units. Following is a code snippet that builds a list (dists) of structures (percepUnitDist) that contain pointers to each pair of items in scratch and units percepDist.scratchUnit = &(*scratchUnit); and percepDist.unit = &(*unit); and their distance. Additionally, for each item in scratch I keep track of which item in units has the least distance minDists.

// For every scratch percepUnit:
for (scratchUnit = scratch.begin(); scratchUnit != scratch.end(); scratchUnit++) { 
    float minDist=2025.1172; // This is the max possible distance in unnormalized CIELuv, and much larger than the normalized dist.
    // For every percepUnit:
    for (unit = units.begin(); unit != units.end(); unit++) { 

        // compare pairs
        float dist = featureDist(*scratchUnit, *unit, FGBG);
        //cout << "distance: " << dist << endl;

        // Put pairs in a structure that caches their distances
        percepUnitDist percepDist;
        percepDist.scratchUnit = &(*scratchUnit); // address of where scratchUnit points to.
        percepDist.unit = &(*unit);
        percepDist.dist = dist;

        // Figure out the percepUnit that is closest to this scratchUnit.
        if (dist < minDist)
            minDist = dist;

        dists.push_back(percepDist); // append dist struct
    }
    minDists.push_back(minDist); // append the min distance to the nearest percepUnit for this particular scratchUnit.
}

So now I just need to loop through the percepUnitDist items in dists and match the distances with the minimum distances to figure out which percepUnit in scratch should be merged with which percepUnit in units. The merging process mergePerceps() creates a new percepUnit which is a weighted average of the "parent" percepUnits in scratch and units.

My understanding is that the following code should do the job just fine, but for some reason memory usage increases with each of these "merge" operations. Even if I provide a method that calls clone() for cv::Mat members, I still get an increase in memory. If I don't copy the cv::Mats at all there is no unexpected memory increase. If I don't "replace" the old instance with the newly merged one, I also don't see memory increase. This is related to this question

// Loop through dists and merge all the closest pairs.
// Loop through all dists
for (distIter = dists.begin(); distIter != dists.end(); distIter++) {
    // Loop through all minDists for each scratchUnit.
    for (minDistsIter = minDists.begin(); minDistsIter != minDists.end(); minDistsIter++) {
        // if this is the closest cluster, and the closest cluster has not already been merged, and the scratch has not already been merged.
        if (*minDistsIter == distIter->dist and not distIter->scratchUnit->remove) {

            percepUnit newUnit;
            mergePerceps(*(distIter->scratchUnit), *(distIter->unit), newUnit, FGBG);
            *(distIter->unit) = newUnit; // replace the cluster with the new merged version.

            distIter->scratchUnit->remove = true;
        }
    }
}

For completeness, here is the whole percepUnit class. Note the cv::Mat members. Following is the mergePerceps() function and the mergeImages() function on which it depends used above:

// Function to construct an accumulation.
void clustering::mergeImages(Mat &scratch, Mat &unit, cv::Mat &merged, const string maskOrImage, const string FGBG, const float scratchWeight, const float unitWeight) {

    int width, height, type=CV_8UC3;
    Mat scratchImagePad, unitImagePad, scratchImage, unitImage;

    // use the resolution and aspect of the largest of the pair.
    if (unit.cols > scratch.cols)
        width = unit.cols;
    else
        width = scratch.cols;

    if (unit.rows > scratch.rows)
        height = unit.rows;
    else
        height = scratch.rows;

    if (maskOrImage == "mask") 
        type = CV_8UC1; // single channel mask
    else if (maskOrImage == "image")
        type = CV_8UC3; // three channel image
    else
        cout << "maskOrImage is not 'mask' or 'image'\n";

    merged = Mat(height, width, type, Scalar::all(0));
    scratchImagePad = Mat(height, width, type, Scalar::all(0));
    unitImagePad = Mat(height, width, type, Scalar::all(0));

    // weight images before summation.
    // because these pass by reference, they mess up the images in memory!
    scratch *= scratchWeight;
    unit *= unitWeight;

    // copy images into padded images.
    scratch.copyTo(scratchImagePad(Rect((scratchImagePad.cols-scratch.cols)/2,
                                             (scratchImagePad.rows-scratch.rows)/2,
                                              scratch.cols,
                                              scratch.rows)));

    unit.copyTo(unitImagePad(Rect((unitImagePad.cols-unit.cols)/2,
                                       (unitImagePad.rows-unit.rows)/2,
                                        unit.cols,
                                        unit.rows)));

    merged = scratchImagePad+unitImagePad;
}

// Merge two perceps and return a new percept to replace them.
void clustering::mergePerceps(percepUnit scratch, percepUnit unit, percepUnit &mergedUnit, const string FGBG) {

    Mat accumulation;
    Mat accumulationMask;
    Mat meanColour;
    int x, y, w, h, area;
    float l,u,v;
    int numMerges=0;
    std::vector<float> featuresVar; // Normalized, Sum, Variance.
    //float featuresVarMin, featuresVarMax; // min and max variance accross all features.
    float scratchWeight, unitWeight;

    if (FGBG == "FG") {
        // foreground percepts don't get merged as much.
        scratchWeight = 0.65;
        unitWeight = 1-scratchWeight;
    } else {
        scratchWeight = 0.85;
        unitWeight = 1-scratchWeight;
    }

    // Images TODO remove the meanColour if needbe.
    mergeImages(scratch.image, unit.image, accumulation, "image", FGBG, scratchWeight, unitWeight);
    mergeImages(scratch.mask, unit.mask, accumulationMask, "mask", FGBG, scratchWeight, unitWeight);
    mergeImages(scratch.meanColour, unit.meanColour, meanColour, "image", "FG", scratchWeight, unitWeight); // merge images 


    // Position and size.
    x = (scratch.x1*scratchWeight) + (unit.x1*unitWeight);
    y = (scratch.y1*scratchWeight) + (unit.y1*unitWeight);
    w = (scratch.w*scratchWeight) + (unit.w*unitWeight);
    h = (scratch.h*scratchWeight) + (unit.h*unitWeight);

    // area
    area = (scratch.area*scratchWeight) + (unit.area*unitWeight);

    // colour
    l = (scratch.l*scratchWeight) + (unit.l*unitWeight);
    u = (scratch.u*scratchWeight) + (unit.u*unitWeight);
    v = (scratch.v*scratchWeight) + (unit.v*unitWeight);

    // Number of merges
    if (scratch.numMerges < 1 and unit.numMerges < 1) { // both units are patches
        numMerges = 1;
    } else if (scratch.numMerges < 1 and unit.numMerges >= 1) { // unit A is a patch, B a percept
        numMerges = unit.numMerges + 1;
    } else if (scratch.numMerges >= 1 and unit.numMerges < 1) { // unit A is a percept, B a patch.
        numMerges = scratch.numMerges + 1;
        cout << "merged scratch??" <<endl;
        // TODO this may be an impossible case.
    } else { // both units are percepts
        numMerges = scratch.numMerges + unit.numMerges;
        cout << "Merging two already merged Percepts" <<endl;
        // TODO this may be an impossible case.
    }

    // Create unit.
    mergedUnit = percepUnit(accumulation, accumulationMask, x, y, w, h, area); // time is the earliest value in times?
    mergedUnit.l = l; // members not in the constrcutor.
    mergedUnit.u = u;
    mergedUnit.v = v;
    mergedUnit.numMerges = numMerges;
    mergedUnit.meanColour = meanColour;
    mergedUnit.pActivated = unit.pActivated; // new clusters retain parent's history of activation.
    mergedUnit.scratch = false;
    mergedUnit.habituation = unit.habituation; // we inherent the habituation of the cluster we merged with.
}

Changing the copy and assignment operators had performance side-effects and did not seem to resolve the problem. So I've added a custom function to do the replacement, which just like the copy operator makes copies of each member and make's sure those copies are deep. The problem is that I still end up with a leak.

So I've changed this line: *(distIter->unit) = newUnit;

to this: (*(distIter->unit)).clone(newUnit)

Where the clone method is as follows:

// Deep Copy of members
void percepUnit::clone(const percepUnit &source) {
    // Deep copy of Mats
    this->image = source.image.clone();
    this->mask = source.mask.clone();
    this->alphaImage = source.alphaImage.clone();
    this->meanColour = source.meanColour.clone();

    // shallow copies of everything else    
    this->alpha = source.alpha;
    this->fadingIn = source.fadingIn;
    this->fadingHold = source.fadingHold;
    this->fadingOut = source.fadingOut;
    this->l = source.l;
    this->u = source.u;
    this->v = source.v;
    this->x1 = source.x1;
    this->y1 = source.y1;
    this->w = source.w;
    this->h = source.h;
    this->x2 = source.x2;
    this->y2 = source.y2;
    this->cx = source.cx;
    this->cy = source.cy;
    this->numMerges = source.numMerges;
    this->id = source.id;
    this->area = source.area;
    this->features = source.features;
    this->featuresNorm = source.featuresNorm;
    this->remove = source.remove;
    this->fgKnockout = source.fgKnockout;
    this->colourCalculated = source.colourCalculated;
    this->normalized = source.normalized;
    this->activation = source.activation;
    this->activated = source.activated;
    this->pActivated = source.pActivated;
    this->habituation = source.habituation;
    this->scratch = source.scratch;
    this->FGBG = source.FGBG;
}

And yet, I still see a memory increase.

I can prevent memory from increasing if I disable the cv::Mat cloning code in the function above:

// Deep Copy of members
void percepUnit::clone(const percepUnit &source) {
    /* try releasing Mats first?
    // No effect on memory increase, but the refCount is decremented.
    this->image.release();
    this->mask.release();
    this->alphaImage.release();
    this->meanColour.release();*/

    /* Deep copy of Mats
    this->image = source.image.clone();
    this->mask = source.mask.clone();
    this->alphaImage = source.alphaImage.clone();
    this->meanColour = source.meanColour.clone();*/

    // shallow copies of everything else    
    this->alpha = source.alpha;
    this->fadingIn = source.fadingIn;
    this->fadingHold = source.fadingHold;
    this->fadingOut = source.fadingOut;
    this->l = source.l;
    this->u = source.u;
    this->v = source.v;
    this->x1 = source.x1;
    this->y1 = source.y1;
    this->w = source.w;
    this->h = source.h;
    this->x2 = source.x2;
    this->y2 = source.y2;
    this->cx = source.cx;
    this->cy = source.cy;
    this->numMerges = source.numMerges;
    this->id = source.id;
    this->area = source.area;
    this->features = source.features;
    this->featuresNorm = source.featuresNorm;
    this->remove = source.remove;
    this->fgKnockout = source.fgKnockout;
    this->colourCalculated = source.colourCalculated;
    this->normalized = source.normalized;
    this->activation = source.activation;
    this->activated = source.activated;
    this->pActivated = source.pActivated;
    this->habituation = source.habituation;
    this->scratch = source.scratch;
    this->FGBG = source.FGBG;
}

What am I missing?

click to hide/show revision 2
retagged

updated 2013-09-17 01:37:34 -0600

berak gravatar image

How to replace an instance with cv::Mat members with another instance via pointer?

I'm doing online destructive clustering (clusters replace clustered objects) on a list of class instances (stl::list<percepunit>).

Background

My list of current percepUnits is: stl::list<percepUnit> units; and for each iteration I get a new list of input percepUnits stl::list<percepUnit> scratch; that need to be clustered with the units.

I want to maintain a fixed number of percepUnits (so units.size() is constant), so for each new scratch percepUnit I need to merge it with the nearest percepUnit in units. Following is a code snippet that builds a list (dists) of structures (percepUnitDist) that contain pointers to each pair of items in scratch and units percepDist.scratchUnit = &(*scratchUnit); and percepDist.unit = &(*unit); and their distance. Additionally, for each item in scratch I keep track of which item in units has the least distance minDists.

// For every scratch percepUnit:
for (scratchUnit = scratch.begin(); scratchUnit != scratch.end(); scratchUnit++) { 
    float minDist=2025.1172; // This is the max possible distance in unnormalized CIELuv, and much larger than the normalized dist.
    // For every percepUnit:
    for (unit = units.begin(); unit != units.end(); unit++) { 

        // compare pairs
        float dist = featureDist(*scratchUnit, *unit, FGBG);
        //cout << "distance: " << dist << endl;

        // Put pairs in a structure that caches their distances
        percepUnitDist percepDist;
        percepDist.scratchUnit = &(*scratchUnit); // address of where scratchUnit points to.
        percepDist.unit = &(*unit);
        percepDist.dist = dist;

        // Figure out the percepUnit that is closest to this scratchUnit.
        if (dist < minDist)
            minDist = dist;

        dists.push_back(percepDist); // append dist struct
    }
    minDists.push_back(minDist); // append the min distance to the nearest percepUnit for this particular scratchUnit.
}

So now I just need to loop through the percepUnitDist items in dists and match the distances with the minimum distances to figure out which percepUnit in scratch should be merged with which percepUnit in units. The merging process mergePerceps() creates a new percepUnit which is a weighted average of the "parent" percepUnits in scratch and units.

My understanding is that the following code should do the job just fine, but for some reason memory usage increases with each of these "merge" operations. Even if I provide a method that calls clone() for cv::Mat members, I still get an increase in memory. If I don't copy the cv::Mats at all there is no unexpected memory increase. If I don't "replace" the old instance with the newly merged one, I also don't see memory increase. This is related to this question

// Loop through dists and merge all the closest pairs.
// Loop through all dists
for (distIter = dists.begin(); distIter != dists.end(); distIter++) {
    // Loop through all minDists for each scratchUnit.
    for (minDistsIter = minDists.begin(); minDistsIter != minDists.end(); minDistsIter++) {
        // if this is the closest cluster, and the closest cluster has not already been merged, and the scratch has not already been merged.
        if (*minDistsIter == distIter->dist and not distIter->scratchUnit->remove) {

            percepUnit newUnit;
            mergePerceps(*(distIter->scratchUnit), *(distIter->unit), newUnit, FGBG);
            *(distIter->unit) = newUnit; // replace the cluster with the new merged version.

            distIter->scratchUnit->remove = true;
        }
    }
}

For completeness, here is the whole percepUnit class. Note the cv::Mat members. Following is the mergePerceps() function and the mergeImages() function on which it depends used above:

// Function to construct an accumulation.
void clustering::mergeImages(Mat &scratch, Mat &unit, cv::Mat &merged, const string maskOrImage, const string FGBG, const float scratchWeight, const float unitWeight) {

    int width, height, type=CV_8UC3;
    Mat scratchImagePad, unitImagePad, scratchImage, unitImage;

    // use the resolution and aspect of the largest of the pair.
    if (unit.cols > scratch.cols)
        width = unit.cols;
    else
        width = scratch.cols;

    if (unit.rows > scratch.rows)
        height = unit.rows;
    else
        height = scratch.rows;

    if (maskOrImage == "mask") 
        type = CV_8UC1; // single channel mask
    else if (maskOrImage == "image")
        type = CV_8UC3; // three channel image
    else
        cout << "maskOrImage is not 'mask' or 'image'\n";

    merged = Mat(height, width, type, Scalar::all(0));
    scratchImagePad = Mat(height, width, type, Scalar::all(0));
    unitImagePad = Mat(height, width, type, Scalar::all(0));

    // weight images before summation.
    // because these pass by reference, they mess up the images in memory!
    scratch *= scratchWeight;
    unit *= unitWeight;

    // copy images into padded images.
    scratch.copyTo(scratchImagePad(Rect((scratchImagePad.cols-scratch.cols)/2,
                                             (scratchImagePad.rows-scratch.rows)/2,
                                              scratch.cols,
                                              scratch.rows)));

    unit.copyTo(unitImagePad(Rect((unitImagePad.cols-unit.cols)/2,
                                       (unitImagePad.rows-unit.rows)/2,
                                        unit.cols,
                                        unit.rows)));

    merged = scratchImagePad+unitImagePad;
}

// Merge two perceps and return a new percept to replace them.
void clustering::mergePerceps(percepUnit scratch, percepUnit unit, percepUnit &mergedUnit, const string FGBG) {

    Mat accumulation;
    Mat accumulationMask;
    Mat meanColour;
    int x, y, w, h, area;
    float l,u,v;
    int numMerges=0;
    std::vector<float> featuresVar; // Normalized, Sum, Variance.
    //float featuresVarMin, featuresVarMax; // min and max variance accross all features.
    float scratchWeight, unitWeight;

    if (FGBG == "FG") {
        // foreground percepts don't get merged as much.
        scratchWeight = 0.65;
        unitWeight = 1-scratchWeight;
    } else {
        scratchWeight = 0.85;
        unitWeight = 1-scratchWeight;
    }

    // Images TODO remove the meanColour if needbe.
    mergeImages(scratch.image, unit.image, accumulation, "image", FGBG, scratchWeight, unitWeight);
    mergeImages(scratch.mask, unit.mask, accumulationMask, "mask", FGBG, scratchWeight, unitWeight);
    mergeImages(scratch.meanColour, unit.meanColour, meanColour, "image", "FG", scratchWeight, unitWeight); // merge images 


    // Position and size.
    x = (scratch.x1*scratchWeight) + (unit.x1*unitWeight);
    y = (scratch.y1*scratchWeight) + (unit.y1*unitWeight);
    w = (scratch.w*scratchWeight) + (unit.w*unitWeight);
    h = (scratch.h*scratchWeight) + (unit.h*unitWeight);

    // area
    area = (scratch.area*scratchWeight) + (unit.area*unitWeight);

    // colour
    l = (scratch.l*scratchWeight) + (unit.l*unitWeight);
    u = (scratch.u*scratchWeight) + (unit.u*unitWeight);
    v = (scratch.v*scratchWeight) + (unit.v*unitWeight);

    // Number of merges
    if (scratch.numMerges < 1 and unit.numMerges < 1) { // both units are patches
        numMerges = 1;
    } else if (scratch.numMerges < 1 and unit.numMerges >= 1) { // unit A is a patch, B a percept
        numMerges = unit.numMerges + 1;
    } else if (scratch.numMerges >= 1 and unit.numMerges < 1) { // unit A is a percept, B a patch.
        numMerges = scratch.numMerges + 1;
        cout << "merged scratch??" <<endl;
        // TODO this may be an impossible case.
    } else { // both units are percepts
        numMerges = scratch.numMerges + unit.numMerges;
        cout << "Merging two already merged Percepts" <<endl;
        // TODO this may be an impossible case.
    }

    // Create unit.
    mergedUnit = percepUnit(accumulation, accumulationMask, x, y, w, h, area); // time is the earliest value in times?
    mergedUnit.l = l; // members not in the constrcutor.
    mergedUnit.u = u;
    mergedUnit.v = v;
    mergedUnit.numMerges = numMerges;
    mergedUnit.meanColour = meanColour;
    mergedUnit.pActivated = unit.pActivated; // new clusters retain parent's history of activation.
    mergedUnit.scratch = false;
    mergedUnit.habituation = unit.habituation; // we inherent the habituation of the cluster we merged with.
}

Changing the copy and assignment operators had performance side-effects and did not seem to resolve the problem. So I've added a custom function to do the replacement, which just like the copy operator makes copies of each member and make's sure those copies are deep. The problem is that I still end up with a leak.

So I've changed this line: *(distIter->unit) = newUnit;

to this: (*(distIter->unit)).clone(newUnit)

Where the clone method is as follows:

// Deep Copy of members
void percepUnit::clone(const percepUnit &source) {
    // Deep copy of Mats
    this->image = source.image.clone();
    this->mask = source.mask.clone();
    this->alphaImage = source.alphaImage.clone();
    this->meanColour = source.meanColour.clone();

    // shallow copies of everything else    
    this->alpha = source.alpha;
    this->fadingIn = source.fadingIn;
    this->fadingHold = source.fadingHold;
    this->fadingOut = source.fadingOut;
    this->l = source.l;
    this->u = source.u;
    this->v = source.v;
    this->x1 = source.x1;
    this->y1 = source.y1;
    this->w = source.w;
    this->h = source.h;
    this->x2 = source.x2;
    this->y2 = source.y2;
    this->cx = source.cx;
    this->cy = source.cy;
    this->numMerges = source.numMerges;
    this->id = source.id;
    this->area = source.area;
    this->features = source.features;
    this->featuresNorm = source.featuresNorm;
    this->remove = source.remove;
    this->fgKnockout = source.fgKnockout;
    this->colourCalculated = source.colourCalculated;
    this->normalized = source.normalized;
    this->activation = source.activation;
    this->activated = source.activated;
    this->pActivated = source.pActivated;
    this->habituation = source.habituation;
    this->scratch = source.scratch;
    this->FGBG = source.FGBG;
}

And yet, I still see a memory increase.

I can prevent memory from increasing if I disable the cv::Mat cloning code in the function above:

// Deep Copy of members
void percepUnit::clone(const percepUnit &source) {
    /* try releasing Mats first?
    // No effect on memory increase, but the refCount is decremented.
    this->image.release();
    this->mask.release();
    this->alphaImage.release();
    this->meanColour.release();*/

    /* Deep copy of Mats
    this->image = source.image.clone();
    this->mask = source.mask.clone();
    this->alphaImage = source.alphaImage.clone();
    this->meanColour = source.meanColour.clone();*/

    // shallow copies of everything else    
    this->alpha = source.alpha;
    this->fadingIn = source.fadingIn;
    this->fadingHold = source.fadingHold;
    this->fadingOut = source.fadingOut;
    this->l = source.l;
    this->u = source.u;
    this->v = source.v;
    this->x1 = source.x1;
    this->y1 = source.y1;
    this->w = source.w;
    this->h = source.h;
    this->x2 = source.x2;
    this->y2 = source.y2;
    this->cx = source.cx;
    this->cy = source.cy;
    this->numMerges = source.numMerges;
    this->id = source.id;
    this->area = source.area;
    this->features = source.features;
    this->featuresNorm = source.featuresNorm;
    this->remove = source.remove;
    this->fgKnockout = source.fgKnockout;
    this->colourCalculated = source.colourCalculated;
    this->normalized = source.normalized;
    this->activation = source.activation;
    this->activated = source.activated;
    this->pActivated = source.pActivated;
    this->habituation = source.habituation;
    this->scratch = source.scratch;
    this->FGBG = source.FGBG;
}

What am I missing?