2011-02-12 253 views
1

Emgu CV的示例集有一個關於如何使用SURFDetector從特徵檢測特徵然後使用Features2DTracker的MatchFeature調用(似乎使用KNN)的示例將「模型」圖像與「觀察」圖像相匹配。這部分是有道理的。如何使用Emgu CV的Surf庫匹配圖像庫

現在,如果我想要構建一個圖像庫,每個圖像的SURF特徵都能找到給定圖像的最佳匹配,我的選擇是什麼?我可以製作一棵樹,而不是與我的圖書館中的每張圖片進行強力匹配。我很困惑,因爲Emgu似乎是建立某種樹的,但兩隻之間的圖像:

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

我看過幾乎每一個線程關於這一主題的網站上,但無法理解如何開始。我也是關於使用直方圖匹配 - 將每個RGB通道分成多個分區並比較標準化計數。不是計算庫中每幅圖像的歐幾里得距離,如果我想根據RGB計數來劃分我的搜索空間,那麼這仍然意味着在R,G,B之一上分支 - 我不確定如何構建那個決策樹。

我前幾天纔開始閱讀這個話題,所以對於我的天真很抱歉。

回答

1

你可以看看EMGU CV的TrafficSignRecognition。它與SURFFeature示例相同,但在現實生活中應用。它能夠檢測給定的圖像是否與給定的圖像匹配以及有多少。我嘗試過這個。你可以看看它。

+1

我查看了代碼,這似乎確定了給定圖像中匹配特定停車標誌的所有地方。它不搜索一組圖像以找到最佳匹配。 – Andy

1

SURFTracker似乎使用FLANN(用於近似近鄰快速庫)附帶OpenCV的(所以有Emgu綁定,太)庫,同時它:

  • 建立從模板中提取的描述符樹圖像(以便將樣本的點與模板的點匹配得更快)。所以樹是爲一個圖像而建(模板)。
  • 當提供樣本時,它會提取描述符,計算匹配(模板和圖像描述符之間的配對),同時考慮到匹配點的空間一致性(右側到右側,左側到左側)

假如你想比簡單地做上述過程爲每個圖像更快,你就必須建立一個每描述每一個形象,並將它放入一個FLANN索引,同時跟蹤哪個描述符來自哪個圖像(可能位於單獨的數組中)。

當給出圖像時,可以從中提取所有描述符,並將它們逐一匹配到FLANN樹(這比使用每個模板描述符集合的不同樹更快)。因此,對於樣本中的每個描述符X,都會得到來自圖像Z的最相似的描述符Y.這些描述符可用作類似圖像的投票(請參閱http://en.wikipedia.org/wiki/Bag_of_words_model)。

但是,這種方法沒有考慮到點的空間一致性......但是也有可能檢查我們對前面的k個圖像所投票的數量(k < < N,系統中的所有圖像)。

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);