我正在我的项目中训练一个用于闭环检测的 fabMap 算法。培训包括创建描述符、词汇和周柳树。我有一个包含超过 10.000 张图像的数据库。我正在使用一个相当不错的桌面(12 核双线程、32 GB RAM 和 6 GB Nvidia 显卡),我想在训练系统时充分利用它。我正在使用opencv 3.0,启用TBB,在Windows 7,64位系统上。
问题是只有描述符的提取是多线程的。周刘树的聚类和构建在单个线程中执行。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² ~ 8 个月),我不想想象整个数据库需要多长时间。
所以,我的问题是:是否有可能以某种方式并行化 cluster() 方法的执行,以便系统的训练不会花费荒谬的时间?我想过应用 openMP 编译指示,或者为每个循环创建一个线程,但考虑到for()
循环的动态,我认为这是不可能的。虽然我熟悉并行编程和多线程,但我根本不是这个领域的专家。
提前非常感谢!
为了值得一提,我在这里留下了我想出的代码,使用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();
}
}
}
}
};
希望这对某人有帮助...