在 OpenCV 進行 SIFT 特徵檢測與匹配後,如何從篩選出的優質匹配點 (`DMatch`) 反向提取對應的關鍵點 (`KeyPoint`),並計算其包圍矩形以繪製目標區域邊框?本文提供完整邏輯鏈路與核心代碼片段。
// 假設已完成: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 精確定位) 選擇合適層級的包圍盒繪製方案。