如何使用 Emgu CV 的 Surf 库来匹配图像库

发布于 2024-10-16 19:58:14 字数 573 浏览 4 评论 0原文

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 之一上进行分支——而且我不知道如何构建那个决策树。

我几天前才开始阅读这个主题,所以为我的天真道歉。

Emgu CV's Example set has an example on how to use SURFDetector to detect features from a feature and then use Features2DTracker's MatchFeature call (which seems to use KNN) to match a "model" image to an "observed" image. This part makes sense.

Now, if I wanted to build a library of images, each using image's SURF features to find the best match for a given image, what are my options? Instead of doing a brute force match with each image in my library, can I build a tree? I'm confused because Emgu seems to be building some sort of tree, but only between two images:

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

I've read almost every thread on the site on the subject but can't understand how to get started. I also though about using histogram matching -- splitting each RGB channel into bins and comparing the normalized count. Instead of calculating the euclidean distance to each image in the library, if I wanted to partition my search space based on RGB count, that would still mean branching on one of R,G,B -- and I'm not sure how to build that decision tree.

I only started reading about this topic a few days ago, so apologies for my naivety.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雨落□心尘 2024-10-23 19:58:14

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

You could take a lookat EMGU CV's TrafficSignRecognition. It is the same as the SURFFeature example, but applied in real life. It is able to detect whether the given image matches with the image given and how many there are. I tried it. You can take a look at it.

过潦 2024-10-23 19:58:14

SURFTracker 似乎使用了 OpenCV 附带的 FLANN(近似最近邻的快速库)库(也有 Emgu 绑定),同时它:

  • 从模板图像中提取的描述符构建一棵树(这样可以更快地匹配点)样本与模板的相对应)。因此,该树仅针对一张图像(模板)构建。
  • 当给定样本时,它提取描述符,计算匹配(模板和图像描述符之间的配对),同时考虑匹配点的空间一致性(右侧到右侧,左侧到左侧)

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

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

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

SURFTracker seems to use the FLANN (Fast Library for Approximate Nearest Neighbors) library that comes with OpenCV (so has Emgu bindings, too) while it:

  • builds a tree from the descriptors extracted from the template image (so that it's faster to match the points of the sample to those of the template). So the tree is built for one image only (the template).
  • when given a sample, it extracts the descriptors, calculates a match (a pairing between the template and the image descriptors), taking into account the spatial consistency of the matching points, too (right side to right side, left side to left side)

Supposing you'd like to be faster than simply doing the above procedure for every image, you would have to build one tree out of every descriptor for every image, and put that into a FLANN Index while keeping track of which descriptor came from which image (probably in a separate array).

When given an image, you could extract all the descriptors from it, and match them one by one to the FLANN tree (this is faster than doing it with a different tree for every template descriptor collection). So for every descriptor X in the sample, you get a most similar descriptor Y that comes from image Z. These can be used as votes for similar images (see http://en.wikipedia.org/wiki/Bag_of_words_model).

However, this method doesn't take into account the spatial consistency of the points... but it's possible to check that, too, for the top k images we have votes for (k << N, the number of all images in the system).

初见你 2024-10-23 19:58:14

此代码为每个图像创建一个矩阵,将它们全部附加在一起,然后创建一个 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);

This code makes a Matrix of each image, appends them all together, then gets makes a FLANN Index, does an KNN search on it, and then returns the matches. All the code is here:

/// <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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文