如果训练图像集很大,训练OpenCV DescriptorMatcher可能是一项耗时的操作。因此,似乎将训练好的DescriptorMatcher数据保存到磁盘以便以后重新加载将是一件非常明显的事情。
不幸的是,似乎没有任何明显的解决方案来满足这种需求。
我最接近找到答案的是this thread in the OpenCV discussion group。该线程始于2009年,人们仍在寻找2011年的答案!
从该线程获取的代码片段看起来应该从文件重新加载索引:
FileStorage fs("data.xml",FileStorage::READ);
Mat data;
fs["mtx"] >> data;
flann::Index idx(data,"index.bin");
但我无法从中了解如何实现完整的保存/加载功能。
以防它是相关的,我正在使用OpenCV 2.3.1。
答案 0 :(得分:5)
我在论坛或邮件列表上没有看到答案。我不得不深入研究OpenCV源代码(2.4.5)以了解如何完成这项工作。它需要子类化来获取FlannBasedMatcher的受保护成员。
关键是将算法设置为FLANN_INDEX_SAVED
,将文件名设置为indexParams
。
另外值得注意的是:
描述符必须在readIndex()之前传递给add()
对于要构建的索引,必须先对其进行匹配,然后调用write()。 train()似乎没有做任何事情,除了构造匹配器(不提供描述符)
这适用于SURF描述符。对于完整的解决方案,可能还需要保存/恢复匹配器的IndexParams和/或SearchParams。
接下来要做的是压缩索引(使用gzip),它可以减少3-4倍,并且解压缩的成本相对较低。这必须是OpenCV中的一个补丁。
class SaveableMatcher : public cv::FlannBasedMatcher
{
public:
SaveableMatcher()
{
}
virtual ~SaveableMatcher()
{
}
void printParams()
{
printf("SaveableMatcher::printParams: \n\t"
"addedDescCount=%d\n\t"
"flan distance_t=%d\n\t"
"flan algorithm_t=%d\n",
addedDescCount,
flannIndex->getDistance(),
flannIndex->getAlgorithm());
vector<std::string> names;
vector<int> types;
vector<std::string> strValues;
vector<double> numValues;
indexParams->getAll(names, types, strValues, numValues);
for (size_t i = 0; i < names.size(); i++)
printf("\tindex param: %s:\t type=%d val=%s %.2f\n",
names[i].c_str(), types[i],
strValues[i].c_str(), numValues[i]);
names.clear();
types.clear();
strValues.clear();
numValues.clear();
searchParams->getAll(names, types, strValues, numValues);
for (size_t i = 0; i < names.size(); i++)
printf("\tsearch param: %s:\t type=%d val=%s %.2f\n",
names[i].c_str(), types[i],
strValues[i].c_str(), numValues[i]);
}
void readIndex(const char* filename)
{
indexParams->setAlgorithm(cvflann::FLANN_INDEX_SAVED);
indexParams->setString("filename", filename);
// construct flannIndex now, so printParams works
train();
printParams();
}
void writeIndex(const char* filename)
{
printParams();
flannIndex->save(filename);
}
};
答案 1 :(得分:3)
在OpenCV 2.4.0中(但也在2.3.1a中)有:
// Reads matcher object from a file node
virtual void read( const FileNode& );
// Writes matcher object to a file storage
virtual void write( FileStorage& ) const;
至少为FlannDescriptorMatcher实现,但实现似乎只保存匹配器的IndexParams。相反,flann :: Index_有一个保存和加载方法(在2.3.1中有保存,而加载似乎可以使用SavedIndexParams
答案 2 :(得分:3)
很久以前就问过这个问题了,所以你可能已经有了答案,但我只是使用类似你所展示的代码实现了一些东西。我没有保存DescriptorMatcher,但为集合中的每个图像创建了一个descriptors.xml文件,然后将所有这些文件加载到Vector中,作为train()调用的输入。 这将程序的运行时间从2分钟减少到5秒,但将描述符加载到Vector中需要3-4秒。 加载整个训练过的索引会很方便。我仍然在寻找一种方法。
答案 3 :(得分:1)
我查看了OpenCV 3.2.0代码,发现基于Flann的匹配器的write()/ read()函数仍然没有保存/加载训练好的数据。受到Wally的回答的启发,我创建了一个类似的可爱的matcher类,继承自FlannBasedMatcher。请注意,这个可保存的匹配器目前仅适用于SURF描述符。
这个可保存的类FlannBasedSavableMatcher将indexParams,searchParams和训练的描述符写入xml / yml文件,但必须将flannIndex写入单独的二进制文件,因为flannIndex的save()方法仅支持原始二进制格式。
以下是课程标题。
#ifndef INCLUDES_FLANNBASEDSAVABLEMATCHER_H_
#define INCLUDES_FLANNBASEDSAVABLEMATCHER_H_
#include <string>
#include <opencv2/core.hpp>
#include <opencv2/features2d.hpp>
namespace cv
{
class FlannBasedSavableMatcher : public FlannBasedMatcher
{
private:
std::vector<std::string> trainedImgFilenameList;
std::string flannIndexFileDir;
std::string flannIndexFilename;
public:
FlannBasedSavableMatcher();
virtual ~FlannBasedSavableMatcher();
std::vector<std::string> getTrainedImgFilenameList();
void setTrainedImgFilenameList(const std::vector<std::string>& imgFilenameList);
void setFlannIndexFileDir(const std::string& dir);
void setFlannIndexFilename(const std::string& filename);
virtual void read(const FileNode& fn);
virtual void write(FileStorage& fs) const;
static Ptr<FlannBasedSavableMatcher> create();
};
}
#endif /* INCLUDES_FLANNBASEDSAVABLEMATCHER_H_ */
可以在以下位置找到源文件 https://github.com/renweizhukov/LearningOpenCV/blob/master/FlannKnnSavableMatching1toN/src/FlannBasedSavableMatcher.cpp,同时可以在以下位置找到示例用法 https://github.com/renweizhukov/LearningOpenCV/blob/master/FlannKnnSavableMatching1toN/src/main.cpp