保存和加载FlannBasedMatcher

8
训练OpenCV DescriptorMatcher可能需要很长时间,特别是当训练图像集很大时。因此,将训练好的DescriptorMatcher数据保存到磁盘以便以后重新加载似乎是一个非常明显的需求。
不幸的是,似乎没有任何明显的解决方案满足这种需求。
我找到的最接近答案的是这个OpenCV讨论组中的帖子。该帖子始于2009年,人们仍在寻找答案直至2011年!
从该帖子中提取的此代码片段看起来应该可以从文件中重新加载索引。
FileStorage fs("data.xml",FileStorage::READ);
Mat data;
fs["mtx"] >> data;
flann::Index idx(data,"index.bin");

但是我还没有从中找到如何实现完整的保存/加载功能。

以防万一,我正在使用OpenCV 2.3.1。

4个回答

6
我在论坛和邮件列表中没有看到这个问题的答案。我不得不深入研究OpenCV源代码(2.4.5),才知道如何完成此操作。需要通过子类化来获取FlannBasedMatcher的protected成员。
关键是将算法设置为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);
}
};

注意:据我理解,描述符本身必须单独进行序列化/反序列化。在加载之前,在调用readIndex之前,必须对描述符进行反序列化并添加(即使用FlannBasedMatcher::add)。 - sircolinton

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_有save和load方法(在2.3.1中有save,而使用SavedIndexParams似乎可以获得load)。


是的,read和write函数只保存IndexParams。我会查看您提到的其他函数。 - Frank Harper

3
这个问题很久以前就被问过了,所以你可能已经有了答案,但我刚刚使用了类似于你展示的代码实现了一些功能。我没有保存DescriptorMatcher,而是为集合中的每个图像创建了一个descriptors.xml文件,然后将所有这些文件加载到Vector中作为train()调用的输入。 这将程序的运行时间从2分钟降低到了5秒,但将描述符加载到Vector中需要3-4秒钟。 将整个训练索引加载进来会很方便。我还在寻找一种方法来做到这一点。

2
我查看了OpenCV 3.2.0的代码,并发现基于Flann的匹配器的write()/read()函数仍然没有保存/加载训练数据。受wally的回答启发,我创建了一个类似的可保存匹配器类,继承自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找到。请注意保留HTML标签。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接