如何通过指针将实例替换为另一个实例?

时间:2013-09-12 18:45:30

标签: c++ pointers opencv stl

我在类实例列表(stl :: list)上进行在线破坏性集群(集群替换集群对象)。

背景

我当前的percepUnits列表是:stl::list<percepUnit> units;,对于每次迭代,我得到一个新的输入percepUnits stl::list<percepUnit> scratch;列表,需要与单位进行聚类。

我想维持一个固定数量的percepUnits(所以units.size()是常量),所以对于每个新的划痕percepUnit我需要将它与最近的percepUnit以单位合并。以下是一个代码段,用于构建结构列表(dists)(percepUnitDist),其中包含指向每个项目对的指针,以及单位percepDist.scratchUnit = &(*scratchUnit);percepDist.unit = &(*unit);及其距离。此外,对于每个项目,我都会跟踪单位中距离最短的项目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.
}

所以现在我只需要遍历percepUnitDist中的dists项,并将距离与最小距离进行匹配,以确定哪些percepUnit应该与哪个percepUnit以单位合并。合并过程mergePerceps()创建一个新的percepUnit,它是临时和单位中“父”percepUnits的加权平均值。

问题

我想用mergePerceps()构造的新的percepUnit替换单位列表中的实例,但我想在循环遍历percepUnitDists的上下文中这样做。这是我目前的代码:

// 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;
        }
    }
}

我认为我可以使用*(distIter->unit) = newUnit;通过percepUnitDist指针替换单元中的实例与新的percepUnit实例,但这似乎不起作用,因为我看到内存泄漏,暗示实例这些单位没有被取代。

如何删除单位列表中的percepUnit并将其替换为新的percepUnit实例,以使新单位位于同一位置?

EDIT1

Here是percepUnit类。注意cv :: Mat成员。以下是mergePerceps()函数和它所依赖的mergeImages()函数:

// 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.
}

EDIT2

更改复制和赋值运算符会产生性能副作用,但似乎无法解决问题。所以我添加了一个自定义函数来进行替换,就像复制操作符复制每个成员一样,并确保这些副本很深。问题是我最终还是会泄漏。

所以我改变了这一行:*(distIter->unit) = newUnit;

(*(distIter->unit)).clone(newUnit)

克隆方法如下:

// 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;
}

然而,我仍然看到内存增加。如果我注释掉单个替换线,则不会发生增加。所以我仍然被困住了。

EDIT3

如果我在上面的函数中禁用了cv :: Mat克隆代码,我可以防止内存增加:

// 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;
}

EDIT4

虽然我仍然无法解释这个问题,但我确实注意到了另一个提示。我意识到,如果我不通过featureDist()规范化我用于集群的那些功能,那么这个泄漏也可以停止(但继续克隆cv :: Mats)。真正奇怪的是,我完全重写了代码,但问题仍然存在。

这是featureDist函数:

float clustering::featureDist(percepUnit unitA, percepUnit unitB, const string FGBG) {
    float distance=0;

    if (FGBG == "BG") {
        for (unsigned int i=0; i<unitA.featuresNorm.rows; i++) { 
            distance += pow(abs(unitA.featuresNorm.at<float>(i) - unitB.featuresNorm.at<float>(i)),0.5);
            //cout << "unitA.featuresNorm[" << i << "]: " << unitA.featuresNorm[i] << endl;
            //cout << "unitB.featuresNorm[" << i << "]: " << unitB.featuresNorm[i] << endl;
        }
    // for FG, don't use normalized colour features.
    // TODO To include the area use i=4
    } else if (FGBG == "FG") { 
        for (unsigned int i=4; i<unitA.features.rows; i++) { 
            distance += pow(abs(unitA.features.at<float>(i) - unitB.features.at<float>(i)),0.5);
        }
    } else {
        cout << "FGBG argument was not FG or BG, returning 0." <<endl;
        return 0;
    }

    return pow(distance,2);
}

特征曾经是浮点数的向量,因此规范化代码如下:

void clustering::normalize(list<percepUnit> &scratch, list<percepUnit> &units) {

    list<percepUnit>::iterator unit;
    list<percepUnit*>::iterator unitPtr;
    vector<float> min,max;
    list<percepUnit*> masterList; // list of pointers.

    // generate pointers
    for (unit = scratch.begin(); unit != scratch.end(); unit++)
        masterList.push_back(&(*unit)); // add pointer to where unit points to.
    for (unit = units.begin(); unit != units.end(); unit++)
        masterList.push_back(&(*unit)); // add pointer to where unit points to.

    int numFeatures = masterList.front()->features.size(); // all percepts have the same number of features.
    min.resize(numFeatures); // allocate for the number of features we have.
    max.resize(numFeatures);

    // Loop through all units to get feature values
    for (int i=0; i<numFeatures; i++) { 

        min[i] = masterList.front()->features[i]; // starting point.
        max[i] = min[i];

        // calculate min and max for each feature.
        for (unitPtr = masterList.begin(); unitPtr != masterList.end(); unitPtr++) {

            if ((*unitPtr)->features[i] < min[i]) 
                min[i] = (*unitPtr)->features[i];
            if ((*unitPtr)->features[i] > max[i])
                max[i] = (*unitPtr)->features[i];
        }
    }

    // Normalize features according to min/max.
    for (int i=0; i<numFeatures; i++) { 
        for (unitPtr = masterList.begin(); unitPtr != masterList.end(); unitPtr++) {
            (*unitPtr)->featuresNorm[i] = ((*unitPtr)->features[i]-min[i]) / (max[i]-min[i]);
            (*unitPtr)->normalized = true;
        }
    }
}

我将功能类型更改为cv :: Mat,因此我可以使用opencv规范化函数,所以我重写了规范化函数,如下所示:

void clustering::normalize(list<percepUnit> &scratch, list<percepUnit> &units) {

    Mat featureMat = Mat(1,units.size()+scratch.size(), CV_32FC1, Scalar(0));
    list<percepUnit>::iterator unit;

    // For each feature
    for (int i=0; i< units.begin()->features.rows; i++) {

        // for each unit in units
        int j=0;
        float value;
        for (unit = units.begin(); unit != units.end(); unit++) {
            // Populate featureMat j is the unit index, i is the feature index.
            value = unit->features.at<float>(i);
            featureMat.at<float>(j) = value;
            j++;
        }
        // for each unit in scratch
        for (unit = scratch.begin(); unit != scratch.end(); unit++) {
            // Populate featureMat j is the unit index, i is the feature index.
            value = unit->features.at<float>(i);
            featureMat.at<float>(j) = value;
            j++;
        }

        // Normalize this featureMat in place
        cv::normalize(featureMat, featureMat, 0, 1, NORM_MINMAX);

        // set normalized values in percepUnits from featureMat
        // for each unit in units
        j=0;
        for (unit = units.begin(); unit != units.end(); unit++) {
            // Populate percepUnit featuresNorm, j is the unit index, i is the feature index.
            value = featureMat.at<float>(j);
            unit->featuresNorm.at<float>(i) = value;
            j++;
        }
        // for each unit in scratch
        for (unit = scratch.begin(); unit != scratch.end(); unit++) {
            // Populate percepUnit featuresNorm, j is the unit index, i is the feature index.
            value = featureMat.at<float>(j);
            unit->featuresNorm.at<float>(i) = value;
            j++;
        }
    }
}

我无法理解mergePercepts和规范化之间的相互作用,特别是因为规范化是一个完全重写的函数。

更新

Massif和我/ proc内存报告不一致。 Massif表示规范化对内存使用没有影响,只有注释掉percepUnit :: clone()操作会绕过泄漏。

Here是所有代码,如果交互是我错过的其他地方。

Here是同一代码的另一个版本,删除了对OpenCV GPU的依赖,以方便测试......

1 个答案:

答案 0 :(得分:0)

Nghia(在opencv论坛上)建议我尝试使感知器保持恒定大小。果然,如果我修复了percepUnit的cv :: Mat成员的尺寸和类型,那么泄漏就会消失

所以在我看来,OpenCV中的一个错误会影响在类成员的不同大小的Mats上调用clone()和copyTo()。到目前为止无法在简单的程序中重现。泄漏似乎足够小,可能是标题泄漏,而不是基础图像数据。