1

Emgu CV 的示例集有一个示例,说明如何使用 SURFDetector 从特征中检测特征,然后使用 Features2DTracker 的 MatchFeature 调用(似乎使用 KNN)将“模型”图像与“观察到的”图像匹配。这部分是有道理的。

现在,如果我想构建一个图像库,每个图像库都使用图像的 SURF 功能来找到给定图像的最佳匹配,我有什么选择?我可以构建一棵树,而不是对库中的每个图像进行蛮力匹配吗?我很困惑,因为 Emgu 似乎正在构建某种树,但仅在两个图像之间:

     //Create a SURF Tracker using k-d Tree
     SURFTracker tracker = new SURFTracker(modelFeatures);

我已经阅读了网站上关于该主题的几乎所有主题,但不明白如何开始。我还考虑使用直方图匹配——将每个 RGB 通道分成箱并比较归一化计数。如果我想根据 RGB 计数对搜索空间进行分区,而不是计算到库中每个图像的欧几里得距离,那仍然意味着在 R、G、B 之一上进行分支——而且我不确定如何构建那棵决策树。

几天前我才开始阅读有关此主题的内容,因此为我的天真道歉。

4

3 回答 3

1

你可以看看 EMGU CV 的 TrafficSignRecognition。它与 SURFFeature 示例相同,但应用于现实生活。它能够检测给定图像是否与给定图像匹配以及有多少。我尝试过这个。你可以看看它。

于 2011-02-12T02:50:19.473 回答
1

SURFTracker 似乎使用了 OpenCV 附带的 FLANN(用于近似最近邻居的快速库)库(Emgu 绑定也是如此),而它:

  • 从模板图像中提取的描述符构建一棵树(以便更快地将样本点与模板点匹配)。所以树只为一个图像(模板)构建。
  • 当给定一个样本时,它提取描述符,计算匹配(模板和图像描述符之间的配对),同时考虑匹配点的空间一致性(右侧到右侧,左侧到左侧)

假设您希望比简单地为每个图像执行上述过程更快,您必须从每个图像的每个描述符中构建棵树,并将其放入 FLANN 索引中,同时跟踪哪个描述符来自哪个图像(可能在一个单独的数组中)。

当给定图像时,您可以从中提取所有描述符,并将它们一一匹配到 FLANN 树(这比为每个模板描述符集合使用不同的树更快)。因此,对于样本中的每个描述符 X,您都会从图像 Z 中获得一个最相似的描述符 Y。这些可以用作对相似图像的投票(参见http://en.wikipedia.org/wiki/Bag_of_words_model)。

但是,这种方法没有考虑点的空间一致性……但是也可以检查,对于我们投票的前 k 个图像 (k << N, 中所有图像的数量系统)。

于 2011-03-05T21:28:49.257 回答
1

此代码为每个图像创建一个矩阵,将它们全部附加在一起,然后生成一个 FLANN 索引,对其进行 KNN 搜索,然后返回匹配项。所有代码都在这里:

/// <summary>
/// Main method.
/// </summary>
public IList<IndecesMapping> Match()
{
    string[] dbImages = {"1.jpg", "2.jpg", "3.jpg"};
    string queryImage = "query.jpg";

    IList<IndecesMapping> imap;

    // compute descriptors for each image
    var dbDescsList = ComputeMultipleDescriptors(dbImages, out imap);

    // concatenate all DB images descriptors into single Matrix
    Matrix<float> dbDescs = ConcatDescriptors(dbDescsList);

    // compute descriptors for the query image
    Matrix<float> queryDescriptors = ComputeSingleDescriptors(queryImage);

    FindMatches(dbDescs, queryDescriptors, ref imap);

    return imap;
}

/// <summary>
/// Computes image descriptors.
/// </summary>
/// <param name="fileName">Image filename.</param>
/// <returns>The descriptors for the given image.</returns>
public Matrix<float> ComputeSingleDescriptors(string fileName)
{
    Matrix<float> descs;

    using (Image<Gray, Byte> img = new Image<Gray, byte>(fileName))
    {
        VectorOfKeyPoint keyPoints = detector.DetectKeyPointsRaw(img, null);
        descs = detector.ComputeDescriptorsRaw(img, null, keyPoints);
    }

    return descs;
}

/// <summary>
/// Convenience method for computing descriptors for multiple images.
/// On return imap is filled with structures specifying which descriptor ranges in the concatenated matrix belong to what image. 
/// </summary>
/// <param name="fileNames">Filenames of images to process.</param>
/// <param name="imap">List of IndecesMapping to hold descriptor ranges for each image.</param>
/// <returns>List of descriptors for the given images.</returns>
public IList<Matrix<float>> ComputeMultipleDescriptors(string[] fileNames, out IList<IndecesMapping> imap)
{
    imap = new List<IndecesMapping>();

    IList<Matrix<float>> descs = new List<Matrix<float>>();

    int r = 0;

    for (int i = 0; i < fileNames.Length; i++)
    {
        var desc = ComputeSingleDescriptors(fileNames[i]);
        descs.Add(desc);

        imap.Add(new IndecesMapping()
        {
            fileName = fileNames[i],
            IndexStart = r,
            IndexEnd = r + desc.Rows - 1
        });

        r += desc.Rows;
    }

    return descs;
}

/// <summary>
/// Computes 'similarity' value (IndecesMapping.Similarity) for each image in the collection against our query image.
/// </summary>
/// <param name="dbDescriptors">Query image descriptor.</param>
/// <param name="queryDescriptors">Consolidated db images descriptors.</param>
/// <param name="images">List of IndecesMapping to hold the 'similarity' value for each image in the collection.</param>
public void FindMatches(Matrix<float> dbDescriptors, Matrix<float> queryDescriptors, ref IList<IndecesMapping> imap)
{
    var indices = new Matrix<int>(queryDescriptors.Rows, 2); // matrix that will contain indices of the 2-nearest neighbors found
    var dists = new Matrix<float>(queryDescriptors.Rows, 2); // matrix that will contain distances to the 2-nearest neighbors found

    // create FLANN index with 4 kd-trees and perform KNN search over it look for 2 nearest neighbours
    var flannIndex = new Index(dbDescriptors, 4);
    flannIndex.KnnSearch(queryDescriptors, indices, dists, 2, 24);

    for (int i = 0; i < indices.Rows; i++)
    {
        // filter out all inadequate pairs based on distance between pairs
        if (dists.Data[i, 0] < (0.6 * dists.Data[i, 1]))
        {
            // find image from the db to which current descriptor range belongs and increment similarity value.
            // in the actual implementation this should be done differently as it's not very efficient for large image collections.
            foreach (var img in imap)
            {
                if (img.IndexStart <= i && img.IndexEnd >= i)
                {
                    img.Similarity++;
                    break;
                }
            }
        }
    }
}

/// <summary>
/// Concatenates descriptors from different sources (images) into single matrix.
/// </summary>
/// <param name="descriptors">Descriptors to concatenate.</param>
/// <returns>Concatenated matrix.</returns>
public Matrix<float> ConcatDescriptors(IList<Matrix<float>> descriptors)
{
    int cols = descriptors[0].Cols;
    int rows = descriptors.Sum(a => a.Rows);

    float[,] concatedDescs = new float[rows, cols];

    int offset = 0;

    foreach (var descriptor in descriptors)
    {
        // append new descriptors
        Buffer.BlockCopy(descriptor.ManagedArray, 0, concatedDescs, offset, sizeof(float) * descriptor.ManagedArray.Length);
        offset += sizeof(float) * descriptor.ManagedArray.Length;
    }

    return new Matrix<float>(concatedDescs);
}

public class IndecesMapping
{
  public int IndexStart { get; set; }
  public int IndexEnd { get; set; }
  public int Similarity { get; set; }
  public string fileName { get; set; }
}

private const double surfHessianThresh = 300;
private const bool surfExtendedFlag = true;
private SURFDetector detector = new SURFDetector(surfHessianThresh, surfExtendedFlag);
于 2015-04-28T18:27:58.287 回答