1

クラス インスタンスのリスト (stl::list) で、オンラインの破壊的クラスタリング (クラスターがクラスター化されたオブジェクトを置き換える) を実行しています。

バックグラウンド

現在の percepUnits のリストは次のとおりです。stl::list<percepUnit> units;反復ごとにstl::list<percepUnit> scratch;、ユニットでクラスター化する必要がある入力 percepUnits の新しいリストを取得します。

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 の加重平均である新しい percepUnit が作成されます。

質問

ユニット リスト内のインスタンスを、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;
        }
    }
}

を使用して percepUnitDist ポインターを介してユニット内のインスタンスを新しい percepUnit インスタンスに置き換えることができると考えましたが、ユニット内のインスタンスが置き換えられて*(distIter->unit) = newUnit;いないことを意味するメモリリークが発生しているため、機能していないようです。

ユニット リストの percepUnit を削除し、新しいユニットが同じ場所に配置されるように新しい percepUnit インスタンスに置き換えるにはどうすればよいですか?

編集1

これが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)

clone メソッドは次のとおりです。

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

フィーチャは float のベクトルであったため、正規化コードは次のようになりました。

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() 操作をコメント アウトするだけでリークが回避されます。

インタラクションがどこか別の場所にある場合に備えて、すべてのコードを次に示します。

テストを容易にするために、OpenCV GPU への依存を削除した同じコードの別のバージョンを次に示します...

4

1 に答える 1

0

(opencv フォーラムの) Nghia によって、知覚を一定のサイズにしようとすることが推奨されました。案の定、percepUnit の cv::Mat メンバーの次元と型を修正すると、リークは消えます。

したがって、これはOpenCVのバグであり、クラスメンバーである異なるサイズのMatsでclone()およびcopyTo()の呼び出しに影響を与えるようです。これまでのところ、単純なプログラムで再現することはできません。リークは、基になる画像データではなく、ヘッダーがリークしている可能性があるほど十分に小さいようです。

于 2014-03-28T21:57:42.743 に答える