2012-03-02 26 views
15

Tôi đang làm việc trên một dự án bằng cách sử dụng trình dò ​​tính năng Orb trong OpenCV 2.3.1. Tôi đang tìm các kết quả phù hợp giữa 8 hình ảnh khác nhau, 6 trong số đó rất giống nhau (20 cm khác biệt ở vị trí camera, dọc theo thanh trượt tuyến tính để không có thang đo hoặc phương sai quay), và sau đó 2 hình ảnh lấy từ góc 45 độ từ bên. Mã của tôi đang tìm kiếm nhiều kết quả khớp chính xác giữa các hình ảnh rất giống nhau, nhưng rất ít đối với các hình ảnh được chụp từ một góc nhìn khác hơn. Tôi đã bao gồm những gì tôi nghĩ là các phần thích hợp của mã của tôi, vui lòng cho tôi biết nếu bạn cần thêm thông tin.OpenCV Orb không tìm thấy các kết quả phù hợp khi các phép quay vòng/quy mô được giới thiệu

// set parameters 

int numKeyPoints = 1500; 
float distThreshold = 15.0; 

//instantiate detector, extractor, matcher 

detector = new cv::OrbFeatureDetector(numKeyPoints); 
extractor = new cv::OrbDescriptorExtractor; 
matcher = new cv::BruteForceMatcher<cv::HammingLUT>; 

//Load input image detect keypoints 

cv::Mat img1; 
std::vector<cv::KeyPoint> img1_keypoints; 
cv::Mat img1_descriptors; 
cv::Mat img2; 
std::vector<cv::KeyPoint> img2_keypoints 
cv::Mat img2_descriptors; 
img1 = cv::imread(fList[0].string(), CV_LOAD_IMAGE_GRAYSCALE); 
img2 = cv::imread(fList[1].string(), CV_LOAD_IMAGE_GRAYSCALE); 
detector->detect(img1, img1_keypoints); 
detector->detect(img2, img2_keypoints); 
extractor->compute(img1, img1_keypoints, img1_descriptors); 
extractor->compute(img2, img2_keypoints, img2_descriptors); 

//Match keypoints using knnMatch to find the single best match for each keypoint 
//Then cull results that fall below given distance threshold 

std::vector<std::vector<cv::DMatch> > matches; 
matcher->knnMatch(img1_descriptors, img2_descriptors, matches, 1); 
int matchCount=0; 
for (int n=0; n<matches.size(); ++n) { 
    if (matches[n].size() > 0){ 
     if (matches[n][0].distance > distThreshold){ 
      matches[n].erase(matches[n].begin()); 
     }else{ 
      ++matchCount; 
     } 
    } 
} 

Trả lời

22

Tôi đã nhận được đủ kết quả trùng khớp hữu ích bằng cách thay đổi quy trình lọc kết quả phù hợp. Phương pháp trước đây của tôi đã loại bỏ rất nhiều trận đấu tốt chỉ dựa trên giá trị khoảng cách của họ. Lớp học RobustMatcher mà tôi tìm thấy trong Sách hướng dẫn lập trình ứng dụng thị giác máy tính OpenCV2 đã kết thúc hoạt động tuyệt vời. Bây giờ tất cả các trận đấu của tôi là chính xác, tôi đã có thể nhận được kết quả đủ tốt bằng cách tăng số lượng các điểm chính mà máy dò ORB đang tìm kiếm. Sử dụng RobustMatcher với SIFT hoặc SURF vẫn cho kết quả tốt hơn nhiều, nhưng tôi nhận được dữ liệu có thể sử dụng với ORB ngay bây giờ.

//RobustMatcher class taken from OpenCV2 Computer Vision Application Programming Cookbook Ch 9 
class RobustMatcher { 
    private: 
    // pointer to the feature point detector object 
    cv::Ptr<cv::FeatureDetector> detector; 
    // pointer to the feature descriptor extractor object 
    cv::Ptr<cv::DescriptorExtractor> extractor; 
    // pointer to the matcher object 
    cv::Ptr<cv::DescriptorMatcher > matcher; 
    float ratio; // max ratio between 1st and 2nd NN 
    bool refineF; // if true will refine the F matrix 
    double distance; // min distance to epipolar 
    double confidence; // confidence level (probability) 
    public: 
    RobustMatcher() : ratio(0.65f), refineF(true), 
         confidence(0.99), distance(3.0) { 
     // ORB is the default feature 
     detector= new cv::OrbFeatureDetector(); 
     extractor= new cv::OrbDescriptorExtractor(); 
     matcher= new cv::BruteForceMatcher<cv::HammingLUT>; 
    } 

    // Set the feature detector 
    void setFeatureDetector(
     cv::Ptr<cv::FeatureDetector>& detect) { 
    detector= detect; 
    } 
    // Set the descriptor extractor 
    void setDescriptorExtractor(
     cv::Ptr<cv::DescriptorExtractor>& desc) { 
    extractor= desc; 
    } 
    // Set the matcher 
    void setDescriptorMatcher(
     cv::Ptr<cv::DescriptorMatcher>& match) { 
    matcher= match; 
    } 
    // Set confidence level 
    void setConfidenceLevel(
     double conf) { 
    confidence= conf; 
    } 
    //Set MinDistanceToEpipolar 
    void setMinDistanceToEpipolar(
     double dist) { 
    distance= dist; 
    } 
    //Set ratio 
    void setRatio(
     float rat) { 
    ratio= rat; 
    } 

    // Clear matches for which NN ratio is > than threshold 
    // return the number of removed points 
    // (corresponding entries being cleared, 
    // i.e. size will be 0) 
    int ratioTest(std::vector<std::vector<cv::DMatch> > 
               &matches) { 
    int removed=0; 
     // for all matches 
    for (std::vector<std::vector<cv::DMatch> >::iterator 
      matchIterator= matches.begin(); 
     matchIterator!= matches.end(); ++matchIterator) { 
      // if 2 NN has been identified 
      if (matchIterator->size() > 1) { 
       // check distance ratio 
       if ((*matchIterator)[0].distance/ 
        (*matchIterator)[1].distance > ratio) { 
        matchIterator->clear(); // remove match 
        removed++; 
       } 
      } else { // does not have 2 neighbours 
       matchIterator->clear(); // remove match 
       removed++; 
      } 
    } 
    return removed; 
    } 

    // Insert symmetrical matches in symMatches vector 
    void symmetryTest(
     const std::vector<std::vector<cv::DMatch> >& matches1, 
     const std::vector<std::vector<cv::DMatch> >& matches2, 
     std::vector<cv::DMatch>& symMatches) { 
    // for all matches image 1 -> image 2 
    for (std::vector<std::vector<cv::DMatch> >:: 
      const_iterator matchIterator1= matches1.begin(); 
     matchIterator1!= matches1.end(); ++matchIterator1) { 
     // ignore deleted matches 
     if (matchIterator1->size() < 2) 
      continue; 
     // for all matches image 2 -> image 1 
     for (std::vector<std::vector<cv::DMatch> >:: 
      const_iterator matchIterator2= matches2.begin(); 
      matchIterator2!= matches2.end(); 
      ++matchIterator2) { 
      // ignore deleted matches 
      if (matchIterator2->size() < 2) 
       continue; 
      // Match symmetry test 
      if ((*matchIterator1)[0].queryIdx == 
       (*matchIterator2)[0].trainIdx && 
       (*matchIterator2)[0].queryIdx == 
       (*matchIterator1)[0].trainIdx) { 
       // add symmetrical match 
       symMatches.push_back(
        cv::DMatch((*matchIterator1)[0].queryIdx, 
          (*matchIterator1)[0].trainIdx, 
          (*matchIterator1)[0].distance)); 
       break; // next match in image 1 -> image 2 
      } 
     } 
    } 
    } 

    // Identify good matches using RANSAC 
    // Return fundemental matrix 
    cv::Mat ransacTest(
     const std::vector<cv::DMatch>& matches, 
     const std::vector<cv::KeyPoint>& keypoints1, 
     const std::vector<cv::KeyPoint>& keypoints2, 
     std::vector<cv::DMatch>& outMatches) { 
    // Convert keypoints into Point2f 
    std::vector<cv::Point2f> points1, points2; 
    cv::Mat fundemental; 
    for (std::vector<cv::DMatch>:: 
     const_iterator it= matches.begin(); 
     it!= matches.end(); ++it) { 
     // Get the position of left keypoints 
     float x= keypoints1[it->queryIdx].pt.x; 
     float y= keypoints1[it->queryIdx].pt.y; 
     points1.push_back(cv::Point2f(x,y)); 
     // Get the position of right keypoints 
     x= keypoints2[it->trainIdx].pt.x; 
     y= keypoints2[it->trainIdx].pt.y; 
     points2.push_back(cv::Point2f(x,y)); 
    } 
    // Compute F matrix using RANSAC 
    std::vector<uchar> inliers(points1.size(),0); 
    if (points1.size()>0&&points2.size()>0){ 
     cv::Mat fundemental= cv::findFundamentalMat(
     cv::Mat(points1),cv::Mat(points2), // matching points 
      inliers,  // match status (inlier or outlier) 
      CV_FM_RANSAC, // RANSAC method 
      distance,  // distance to epipolar line 
      confidence); // confidence probability 
     // extract the surviving (inliers) matches 
     std::vector<uchar>::const_iterator 
         itIn= inliers.begin(); 
     std::vector<cv::DMatch>::const_iterator 
         itM= matches.begin(); 
     // for all matches 
     for (;itIn!= inliers.end(); ++itIn, ++itM) { 
     if (*itIn) { // it is a valid match 
      outMatches.push_back(*itM); 
      } 
     } 
     if (refineF) { 
     // The F matrix will be recomputed with 
     // all accepted matches 
      // Convert keypoints into Point2f 
      // for final F computation 
      points1.clear(); 
      points2.clear(); 
      for (std::vector<cv::DMatch>:: 
       const_iterator it= outMatches.begin(); 
       it!= outMatches.end(); ++it) { 
       // Get the position of left keypoints 
       float x= keypoints1[it->queryIdx].pt.x; 
       float y= keypoints1[it->queryIdx].pt.y; 
       points1.push_back(cv::Point2f(x,y)); 
       // Get the position of right keypoints 
       x= keypoints2[it->trainIdx].pt.x; 
       y= keypoints2[it->trainIdx].pt.y; 
       points2.push_back(cv::Point2f(x,y)); 
      } 
      // Compute 8-point F from all accepted matches 
      if (points1.size()>0&&points2.size()>0){ 
      fundemental= cv::findFundamentalMat(
       cv::Mat(points1),cv::Mat(points2), // matches 
       CV_FM_8POINT); // 8-point method 
      } 
     } 
    } 
    return fundemental; 
    } 

    // Match feature points using symmetry test and RANSAC 
    // returns fundemental matrix 
    cv::Mat match(cv::Mat& image1, 
       cv::Mat& image2, // input images 
    // output matches and keypoints 
    std::vector<cv::DMatch>& matches, 
    std::vector<cv::KeyPoint>& keypoints1, 
    std::vector<cv::KeyPoint>& keypoints2) { 
    // 1a. Detection of the SURF features 
    detector->detect(image1,keypoints1); 
    detector->detect(image2,keypoints2); 
    // 1b. Extraction of the SURF descriptors 
    cv::Mat descriptors1, descriptors2; 
    extractor->compute(image1,keypoints1,descriptors1); 
    extractor->compute(image2,keypoints2,descriptors2); 
    // 2. Match the two image descriptors 
    // Construction of the matcher 
    //cv::BruteForceMatcher<cv::L2<float>> matcher; 
    // from image 1 to image 2 
    // based on k nearest neighbours (with k=2) 
    std::vector<std::vector<cv::DMatch> > matches1; 
    matcher->knnMatch(descriptors1,descriptors2, 
     matches1, // vector of matches (up to 2 per entry) 
     2);  // return 2 nearest neighbours 
    // from image 2 to image 1 
    // based on k nearest neighbours (with k=2) 
    std::vector<std::vector<cv::DMatch> > matches2; 
    matcher->knnMatch(descriptors2,descriptors1, 
     matches2, // vector of matches (up to 2 per entry) 
     2);  // return 2 nearest neighbours 
    // 3. Remove matches for which NN ratio is 
    // > than threshold 
    // clean image 1 -> image 2 matches 
    int removed= ratioTest(matches1); 
    // clean image 2 -> image 1 matches 
    removed= ratioTest(matches2); 
    // 4. Remove non-symmetrical matches 
    std::vector<cv::DMatch> symMatches; 
    symmetryTest(matches1,matches2,symMatches); 
    // 5. Validate matches using RANSAC 
    cv::Mat fundemental= ransacTest(symMatches, 
       keypoints1, keypoints2, matches); 
    // return the found fundemental matrix 
    return fundemental; 
    } 
}; 


// set parameters 

int numKeyPoints = 1500; 

//Instantiate robust matcher 

RobustMatcher rmatcher; 

//instantiate detector, extractor, matcher 

detector = new cv::OrbFeatureDetector(numKeyPoints); 
extractor = new cv::OrbDescriptorExtractor; 
matcher = new cv::BruteForceMatcher<cv::HammingLUT>; 

rmatcher.setFeatureDetector(detector); 
rmatcher.setDescriptorExtractor(extractor); 
rmatcher.setDescriptorMatcher(matcher); 

//Load input image detect keypoints 

cv::Mat img1; 
std::vector<cv::KeyPoint> img1_keypoints; 
cv::Mat img1_descriptors; 
cv::Mat img2; 
std::vector<cv::KeyPoint> img2_keypoints 
cv::Mat img2_descriptors; 
std::vector<std::vector<cv::DMatch> > matches; 
img1 = cv::imread(fList[0].string(), CV_LOAD_IMAGE_GRAYSCALE); 
img2 = cv::imread(fList[1].string(), CV_LOAD_IMAGE_GRAYSCALE); 

rmatcher.match(img1, img2, matches, img1_keypoints, img2_keypoints); 
+1

Họ của bạn tương đương với họ của nhà phát triển SIFT, bạn là con trai của David Lowe? :) Tôi cũng quan tâm đến độ mạnh của thuật toán phù hợp và sự khác biệt duy nhất từ ​​thử nghiệm tỷ lệ knn + phổ biến mà tôi thấy ở đây là ** thử nghiệm đối xứng ** - nó có mang lại sự mạnh mẽ đáng kể không? –

+1

Haha, không liên quan đến David Lowe :) Tôi đã nhận thấy rằng tôi có kết quả tốt hơn đáng kể với symmetryTest và ransacTest được thêm vào. Có một hit hiệu suất khá lớn, nhưng tôi không ở trong một môi trường nhạy cảm với hiệu năng khủng khiếp nên nó không phải là một con đường cho tôi. – KLowe

+1

Bạn đề nghị đưa điểm cho kết quả như thế nào? Tôi muốn chạy mã này trên toàn bộ chỉ mục của tôi và tìm thấy kết quả phù hợp nhất. Tôi có nên đếm số lượng các điểm chính sau khi lọc các kết quả phù hợp hoặc thêm tất cả các khoảng cách với nhau hoặc lấy trung bình của khoảng cách không? Tôi không biết điều gì sẽ là một tiêu chí tốt. – Hacky

1

Tôi không nghĩ có bất kỳ điều gì sai với mã của bạn. Từ kinh nghiệm của tôi opencv của ORB là nhạy cảm với các biến thể quy mô.

Bạn có thể xác nhận điều này bằng một thử nghiệm nhỏ, chỉ thực hiện một số hình ảnh với chế độ xoay vòng và một số chỉ có biến thể tỷ lệ. Các vòng quay sẽ có thể phù hợp tốt nhưng những người quy mô sẽ không (tôi nghĩ rằng quy mô giảm là tồi tệ nhất).

Tôi cũng khuyên bạn nên thử phiên bản opencv từ thân cây (xem trang web của opencv để biết hướng dẫn biên dịch), ORB được cập nhật kể từ 2.3.1 và thực hiện tốt hơn một chút nhưng vẫn có vấn đề về quy mô.

+0

Cảm ơn thông tin chi tiết. Tôi sẽ kiểm tra một số tính năng phát hiện khác. Tôi đã tránh sift và lướt sóng, như những gì tôi hiểu cả hai đều được cấp bằng sáng chế. Bạn có bất kỳ thiết bị dò tìm tính năng nào khác mà bạn muốn giới thiệu không? – KLowe

+0

Điều đó và sift và lướt sóng là cách chậm hơn so với quả cầu. Nếu tôi thực sự cần độ chính xác của lướt web và lập trình cho máy tính để bàn (tôi lập trình cho di động), tôi sẽ thử phiên bản GPU (opencv có triển khai lướt web sử dụng GPU, cũng như của ORB tôi nghĩ) để xem tôi có thể làm cho nó đủ nhanh. Ngoài ra còn có bộ dò FAST, nó nhanh nhưng không chính xác, và máy dò BRIEF. BRIEF không phải là bất biến xoay vòng nhưng bạn có thể hack bằng cách cung cấp cho nó một số hình ảnh truy vấn được xoay (tôi sẽ đọc trang này và mã của nó để xem tóm tắt trong hành động: http://cvlab.epfl.ch/software/brief/index.php) . –

+0

Vì mục đích của tôi, vấn đề chính của tôi là trong quá trình lọc. Tôi tìm thấy một câu trả lời tràn ngăn xếp khác được đề cập đến cuốn sách "Sách hướng dẫn lập trình ứng dụng thị giác máy tính OpenCV 2" Ch9: Ghép hình ảnh bằng cách sử dụng sự đồng thuận mẫu ngẫu nhiên. Về cơ bản, thay vì chỉ chọn tất cả các trận đấu dưới một khoảng cách nhất định, họ đang sử dụng 3 bộ lọc khác nhau để lại cho tôi nhiều trận đấu tốt hơn. Trước đây tôi đã loại bỏ tất cả các trận đấu dưới khoảng cách 15.0, điều này đã để lại cho tôi với tất cả các trận đấu tốt, nhưng tôi đã tiêu diệt rất nhiều trận đấu tốt trong quá trình này. – KLowe

3

Tôi gặp sự cố tương tự với python opencv và đến đây qua google.

Để giải quyết vấn đề của mình, tôi đã viết mã python để lọc phù hợp dựa trên giải pháp @KLowes. Tôi sẽ chia sẻ nó ở đây trong trường hợp ai đó có cùng một vấn đề:

""" Clear matches for which NN ratio is > than threshold """ 
def filter_distance(matches): 
    dist = [m.distance for m in matches] 
    thres_dist = (sum(dist)/len(dist)) * ratio 

    sel_matches = [m for m in matches if m.distance < thres_dist] 
    #print '#selected matches:%d (out of %d)' % (len(sel_matches), len(matches)) 
    return sel_matches 

""" keep only symmetric matches """ 
def filter_asymmetric(matches, matches2, k_scene, k_ftr): 
    sel_matches = [] 
    for match1 in matches: 
     for match2 in matches2: 
      if match1.queryIdx < len(k_ftr) and match2.queryIdx < len(k_scene) and \ 
       match2.trainIdx < len(k_ftr) and match1.trainIdx < len(k_scene) and \ 
          k_ftr[match1.queryIdx] == k_ftr[match2.trainIdx] and \ 
          k_scene[match1.trainIdx] == k_scene[match2.queryIdx]: 
       sel_matches.append(match1) 
       break 
    return sel_matches 

def filter_ransac(matches, kp_scene, kp_ftr, countIterations=2): 
    if countIterations < 1 or len(kp_scene) < minimalCountForHomography: 
     return matches 

    p_scene = [] 
    p_ftr = [] 
    for m in matches: 
     p_scene.append(kp_scene[m.queryIdx].pt) 
     p_ftr.append(kp_ftr[m.trainIdx].pt) 

    if len(p_scene) < minimalCountForHomography: 
     return None 

    F, mask = cv2.findFundamentalMat(np.float32(p_ftr), np.float32(p_scene), cv2.FM_RANSAC) 
    sel_matches = [] 
    for m, status in zip(matches, mask): 
     if status: 
      sel_matches.append(m) 

    #print '#ransac selected matches:%d (out of %d)' % (len(sel_matches), len(matches)) 

    return filter_ransac(sel_matches, kp_scene, kp_ftr, countIterations-1) 



def filter_matches(matches, matches2, k_scene, k_ftr): 
    matches = filter_distance(matches) 
    matches2 = filter_distance(matches2) 
    matchesSym = filter_asymmetric(matches, matches2, k_scene, k_ftr) 
    if len(k_scene) >= minimalCountForHomography: 
     return filter_ransac(matchesSym, k_scene, k_ftr) 

Để lọc phù hợp filter_matches(matches, matches2, k_scene, k_ftr) phải được gọi là nơi matches, matches2 đại diện cho trận đấu thu được bằng cách orb-khớp và k_scene, k_ftr được tương ứng keypoint.

+0

Cảm ơn, điều này thật tuyệt! Tôi đã chỉ về để viết một kịch bản nhỏ python opencv sử dụng tính năng phù hợp và bạn đã lưu cho tôi những rắc rối của porting nó! – KLowe

Các vấn đề liên quan