OpenCV中聚类方法的并行化

时间:2014-04-01 09:41:27

标签: c++ multithreading opencv parallel-processing

我正在训练一个fabMap算法,用于在我的项目中进行循环关闭检测。培训包括描述符,词汇和Chow-Liu树的创建。我有一个超过10,000张图像的数据库。我正在使用一个非常好的桌面(12核双线程,32 GB RAM和6 GB Nvidia显卡),我想在训练我的系统时充分利用它。我在Windows 7,64位系统上使用opencv 3.0,支持TBB。

问题是只有描述符的提取是多线程的。 Chow-Liu树的聚类和构建在单个线程中执行。 BOWMSCTrainer类的cluster()方法有3个嵌套的for()循环,每个循环都依赖于前一个循环,甚至嵌套循环的大小也是动态分配的。这是cluster()方法的核心:

//_descriptors is a Matrix wherein each row is a descriptor

Mat icovar = Mat::eye(_descriptors.cols,_descriptors.cols,_descriptors.type());

std::vector<Mat> initialCentres;
initialCentres.push_back(_descriptors.row(0));
for (int i = 1; i < _descriptors.rows; i++) {
    double minDist = DBL_MAX;
    for (size_t j = 0; j < initialCentres.size(); j++) {
        minDist = std::min(minDist,
            cv::Mahalanobis(_descriptors.row(i),initialCentres[j],
            icovar));
    }
    if (minDist > clusterSize)
        initialCentres.push_back(_descriptors.row(i));
}

std::vector<std::list<cv::Mat> > clusters;
clusters.resize(initialCentres.size());
for (int i = 0; i < _descriptors.rows; i++) {
    int index = 0; double dist = 0, minDist = DBL_MAX;
    for (size_t j = 0; j < initialCentres.size(); j++) {
        dist = cv::Mahalanobis(_descriptors.row(i),initialCentres[j],icovar);
        if (dist < minDist) {
            minDist = dist;
            index = (int)j;
        }
    }
    clusters[index].push_back(_descriptors.row(i));
}

// TODO: throw away small clusters.

Mat vocabulary;
Mat centre = Mat::zeros(1,_descriptors.cols,_descriptors.type());
for (size_t i = 0; i < clusters.size(); i++) {
    centre.setTo(0);
    for (std::list<cv::Mat>::iterator Ci = clusters[i].begin(); Ci != clusters[i].end(); Ci++) {
        centre += *Ci;
    }
    centre /= (double)clusters[i].size();
    vocabulary.push_back(centre);
}

return vocabulary;
}

为了了解培训需要多长时间,我对数据库进行了下采样。我开始时只有10张图像(约20.000个描述符),大约需要40分钟。对于100张图像(约300,000个描述符)的样本,整个过程大约花了60个小时,我担心1000张图像(这会产生一个不错的词汇)可能需要8个月(!),(如果方法是O( n²) - > 60小时* 10 2~8个月)我不想想整个数据库需要多长时间。

所以,我的问题是:是否有可能以某种方式并行化cluster()方法的执行,以便系统的训练不会花费大量的时间?我已经考虑过应用openMP pragma,或者为每个循环创建一个线程,但我不认为这可能是for()循环的动态。虽然我熟悉并行编程和多线程,但我并不是这个领域的专家。

非常感谢提前!

1 个答案:

答案 0 :(得分:1)

值得一提的是,我使用OpenCV的parallel_for调用留下了我提出的代码。我还在代码中添加了一个功能,现在它删除了小于阈值的所有群集。该代码有效地加快了这个过程:

//The first nest of fors remains untouched, but the following ones: 

std::vector<std::list<cv::Mat> > clusters;
clusters.resize(initialCentres.size());

Mutex lock = Mutex();
parallel_for_(cv::Range(0, _descriptors.rows - 1),
        for_createClusters(clusters, initialCentres, icovar, _descriptors, lock));

Mat vocabulary;
Mat centre = Mat::zeros(1,_descriptors.cols,_descriptors.type());
parallel_for_(cv::Range(0, clusters.size() - 1), for_estimateCentres(clusters,
        vocabulary, centre, minSize, lock));

并且,在标题中:

//parallel_for_ for creating clusters:
class CV_EXPORTS for_createClusters: public ParallelLoopBody {
private:

std::vector<std::list<cv::Mat> >& bufferCluster;
const std::vector<Mat> initCentres;
const Mat icovar;
const Mat descriptorsParallel;
Mutex& lock_for;

public:
for_createClusters(std::vector<std::list<cv::Mat> >& _buffCl,
        const std::vector<Mat> _initCentres, const Mat _icovar,
        const Mat _descriptors, Mutex& _lock_for)
: bufferCluster (_buffCl), initCentres(_initCentres), icovar(_icovar),
  descriptorsParallel(_descriptors), lock_for(_lock_for){}


virtual void operator()( const cv::Range &r ) const
{
    for (register int f = r.start; f != r.end; ++f)
    {
        int index = 0; double dist = 0, minDist = DBL_MAX;
        for (register size_t j = 0; j < initCentres.size(); j++) {
            dist = cv::Mahalanobis(descriptorsParallel.row(f),
                    initCentres[j],icovar);
            if (dist < minDist) {
                minDist = dist;
                index = (int)j;
            }
        }
        {
//              AutoLock Lock(lock_for);
            lock_for.lock();
            bufferCluster[index].push_back(descriptorsParallel.row(f));
            lock_for.unlock();
        }
    }
    }
};

class CV_EXPORTS for_estimateCentres: public ParallelLoopBody {
private:

const std::vector<std::list<cv::Mat> > bufferCluster;
Mat& vocabulary;
const Mat centre;
const int minSizCl;
Mutex& lock_for;

public:
for_estimateCentres(const std::vector<std::list<cv::Mat> > _bufferCluster,
        Mat& _vocabulary, const Mat _centre, const int _minSizCl, Mutex& _lock_for)
: bufferCluster(_bufferCluster), vocabulary(_vocabulary),
  centre(_centre), minSizCl(_minSizCl), lock_for(_lock_for){}

virtual void operator()( const cv::Range &r ) const
{
    Mat ctr = Mat::zeros(1, centre.cols,centre.type());

    for (register int f = r.start; f != r.end; ++f){
        ctr.setTo(0);
        //Not taking into account small clusters
        if(bufferCluster[f].size() >= (size_t) minSizCl)
        {
            for (register std::list<cv::Mat>::const_iterator
                    Ci = bufferCluster[f].begin();
                    Ci != bufferCluster[f].end(); Ci++)
                        ctr += *Ci;

            ctr /= (double)bufferCluster[f].size();

            {
//              AutoLock Lock(lock_for);
                lock_for.lock();
                vocabulary.push_back(ctr);
                lock_for.unlock();
            }
        }
    }
  }
};

希望这对某人有帮助......