首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

OpenCV SIFT 特徵匹配篩選與包圍盒繪製實戰

     編輯:程式設計疑難解答

在 OpenCV 進行 SIFT 特徵檢測與匹配後,如何從篩選出的優質匹配點 (`DMatch`) 反向提取對應的關鍵點 (`KeyPoint`),並計算其包圍矩形以繪製目標區域邊框?本文提供完整邏輯鏈路與核心代碼片段。

核心流程:匹配點 → 關鍵點 → 包圍盒

  1. 特徵檢測與描述:`SiftFeatureDetector::detect()` → `SiftDescriptorExtractor::compute()`。
  2. 暴力匹配:`BruteForceMatcher>::match()` 獲得初始 `vector matches`。
  3. 篩選優質匹配:常用 **Lowe's 比率測試** (knnMatch 取前兩近鄰,距離比 < 0.7) 或 **距離閾值/前 N 最佳** (如代碼中 `nth_element` 取前 15)。
  4. 關鍵點提取 (關鍵步驟):遍歷篩選後的 `matches`,透過 `queryIdx` (查詢圖索引) 和 `trainIdx` (訓練圖索引) 從 `keypoints_1`、`keypoints_2` 取出對應 `KeyPoint`。
  5. 計算包圍矩形:將提取的關鍵點座標 (`KeyPoint.pt`) 轉為 `Point2f` 陣列,呼叫 `cv::boundingRect()` 或 `cv::minAreaRect()` (最小外接旋轉矩形)。
  6. 繪製:在原圖或拼接圖上 `rectangle()` / `polylines()`。

關鍵代碼實現 (C++)

// 假設已完成:detect -> compute -> match -> 篩選得 good_matches (vector)
// 1. 提取查詢圖 (img_1) 上的匹配關鍵點座標
std::vector queryPts;
for (const auto& m : good_matches) {
    queryPts.push_back(keypoints_1[m.queryIdx].pt); // queryIdx 對應 keypoints_1
}

// 2. 計算包圍矩形
if (!queryPts.empty()) {
    cv::Rect bbox = cv::boundingRect(queryPts); // 軸對齊矩形
    // 或 cv::RotatedRect rbox = cv::minAreaRect(queryPts); // 最小外接旋轉矩形

    // 3. 繪製到 img_1 (或拼接圖 img_matches)
    cv::rectangle(img_1, bbox, cv::Scalar(0, 255, 0), 2); // 綠框
    // 旋轉矩形繪製
    // cv::Point2f vertices[4]; rbox.points(vertices);
    // for (int i=0; i<4; ++i) cv::line(img_1, vertices[i], vertices[(i+1)%4], cv::Scalar(0,0,255), 2);
}

// 同理可提取訓練圖 (img_2) 關鍵點:keypoints_2[m.trainIdx].pt

完整流程整合示意

int main() {
    // ... 讀圖、灰度轉換、SIFT detect/compute ...
    
    // 匹配 + 比率測試篩選 (優於單純取前N)
    std::vector> knn_matches;
    matcher.knnMatch(descriptors_1, descriptors_2, knn_matches, 2);
    std::vector good_matches;
    for (const auto& knn : knn_matches) {
        if (knn[0].distance < 0.7 * knn[1].distance) // Lowe's ratio test
            good_matches.push_back(knn[0]);
    }

    // 提取查詢圖關鍵點座標並畫包圍盒
    std::vector obj_pts;
    for (const auto& m : good_matches) obj_pts.push_back(keypoints_1[m.queryIdx].pt);
    if (obj_pts.size() >= 4) { // 至少4點才能求單應性/有意義包圍盒
        cv::Rect bbox = cv::boundingRect(obj_pts);
        cv::rectangle(img_1, bbox, cv::Scalar(0,255,0), 2);
    }

    // 繪製匹配線段 (可選)
    cv::drawMatches(img_1, keypoints_1, img_2, keypoints_2, good_matches, img_matches);
    cv::imshow("Matches with BBox", img_matches);
    cv::waitKey(0);
    return 0;
}

進階:透視變換定位具體物體區域

若需精確框出變換後的物體區域 (而非關鍵點包圍盒),需進一步計算**單應性矩陣 (Homography)**:

if (good_matches.size() >= 4) {
    std::vector obj, scene;
    for (auto& m : good_matches) {
        obj.push_back(keypoints_1[m.queryIdx].pt);
        scene.push_back(keypoints_2[m.trainIdx].pt);
    }
    cv::Mat H = cv::findHomography(obj, scene, cv::RANSAC); // 魯棒估計
    // 以查詢圖四角透視變換到訓練圖
    std::vector obj_corners = { {0,0}, {img_1.cols,0}, {img_1.cols,img_1.rows}, {0,img_1.rows} };
    std::vector scene_corners(4);
    cv::perspectiveTransform(obj_corners, scene_corners, H);
    // 畫出變換後的四邊形
    for (int i=0; i<4; ++i)
        cv::line(img_2, scene_corners[i], scene_corners[(i+1)%4], cv::Scalar(0,255,0), 3);
}

總結

**`DMatch.queryIdx` / `trainIdx` 是連接匹配結果與原始關鍵點的橋樑**。提取 `KeyPoint.pt` 座標後,`boundingRect()` 給軸對齊框,`minAreaRect()` 給旋轉框,`findHomography()` + `perspectiveTransform()` 給精確透視框。根據應用場景 (簡單可視化 vs 精確定位) 選擇合適層級的包圍盒繪製方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved