? ? ? ?一般而言,如果一個物體在一幅圖像中被檢測到關鍵點,那么同一個物體在其他圖像中也會檢測到同一個關鍵點。圖像匹配是關鍵點的常用功能之一,它的作用包括關聯同一場景的兩幅圖像、檢測圖像中事物的發生地點等等。
1.局部模板匹配
? ? ? ? 憑單個像素就判斷兩個關鍵點的相似度顯然是不夠的,因此要在匹配過程中考慮每個關鍵
點周圍的圖像塊,對圖像塊中的像素進行逐個比較,但是并不是最可靠的。
? ? ? 第一步,使用FAST 檢測器進行關鍵點提取:
// 定義特征檢測器
cv::Ptr<cv::FeatureDetector> ptrDetector; // 泛型檢測器指針
ptrDetector= // 這里選用FAST 檢測器
cv::FastFeatureDetector::create(80);
// 檢測關鍵點
ptrDetector->detect(image1,keypoints1);
ptrDetector->detect(image2,keypoints2);
? ? ? 第二步,定義匹配框,在每個圖像對的關鍵點之間進行匹配,這里使用逐像素相差匹配:
// 在第二幅圖像中找出與第一幅圖像中的每個關鍵點最匹配的
cv::Mat result;
std::vector<cv::DMatch> matches;
// 針對圖像一的全部關鍵點
for (int i=0; i<keypoints1.size(); i++) {// 定義圖像塊neighborhood.x = keypoints1[i].pt.x-nsize/2;neighborhood.y = keypoints1[i].pt.y-nsize/2;// 如果鄰域超出圖像范圍,就繼續處理下一個點if (neighborhood.x<0 || neighborhood.y<0 ||neighborhood.x+nsize >= image1.cols ||neighborhood.y+nsize >= image1.rows)continue;// 第一幅圖像的塊patch1 = image1(neighborhood);// 存放最匹配的值cv::DMatch bestMatch;// 針對第二幅圖像的全部關鍵點for (int j=0; j<keypoints2.size(); j++) {// 定義圖像塊neighborhood.x = keypoints2[j].pt.x-nsize/2;neighborhood.y = keypoints2[j].pt.y-nsize/2;// 如果鄰域超出圖像范圍,就繼續處理下一個點if (neighborhood.x<0 || neighborhood.y<0 ||neighborhood.x + nsize >= image2.cols ||neighborhood.y + nsize >= image2.rows)continue;// 第二幅圖像的塊patch2 = image2(neighborhood);// 匹配兩個圖像塊cv::matchTemplate(patch1,patch2,result, cv::TM_SQDIFF);// 檢查是否為最佳匹配if (result.at<float>(0,0) < bestMatch.distance) {bestMatch.distance= result.at<float>(0,0);bestMatch.queryIdx= i;bestMatch.trainIdx= j;}}// 添加最佳匹配matches.push_back(bestMatch);}
? ? ? 第三步,選擇置信度最高的一些點,進行展示:
// 提取25 個最佳匹配項
std::nth_element(matches.begin(),matches.begin() + 25,matches.end());
matches.erase(matches.begin() + 25,matches.end());// 繪制圖像
cv::Mat matchImage;
cv::drawMatches(image1,keypoints1, // 第一幅圖像image2,keypoints2, // 第二幅圖像matches, // 匹配項的向量cv::Scalar(255,255,255), // 線條顏色cv::Scalar(255,255,255)); // 點的顏色
? ? ? ? 上述方法使用圖塊之間相似度進行評估,也可以使用opencv中的區域模板匹配方法進一步增大搜索精確度:
// 定義搜索區域
cv::Mat roi(image2, // 這里用圖像的上半部分
cv::Rect(0,0,image2.cols,image2.rows/2));
// 進行模板匹配
cv::matchTemplate(roi, // 搜索區域
target, // 模板
result, // 結果
cv::TM_SQDIFF); // 相似度
// 找到最相似的位置
double minVal, maxVal;
cv::Point minPt, maxPt;
cv::minMaxLoc(result, &minVal, &maxVal, &minPt, &maxPt);
// 在相似度最高的位置繪制矩形
// 本例中為minPt
cv::rectangle(roi, cv::Rect(minPt.x, minPt.y,
target.cols, target.rows), 255);
2.描述并匹配局部強度值模式
? ? ? ?在圖像分析中,可以用鄰域包含的視覺信息來標識每個特征點,以便區分各個特征點。特征描述子通常是一個N 維的向量,在光照變化和拍攝角度發生微小扭曲時,它描述特征點的方式不會發生變化,?通常可以用簡單的差值矩陣來比較描述子,例如用歐幾里得距離等
? ? ? ?基于特征的方法都包含一個檢測器和一個描述子組件,與cv::Feature2D 相關的類也一樣,它們都有一個檢測函數(用于檢測興趣點)和一個計算函數(用于計算興趣點的描述子)cv::SURF
和cv::SIFT,檢測流程和上述一致。
// 1. 定義關鍵點的容器
std::vector<cv::KeyPoint> keypoints1;
std::vector<cv::KeyPoint> keypoints2;// 2. 定義特征檢測器
cv::Ptr<cv::Feature2D> ptrFeature2D =
cv::xfeatures2d::SURF::create(2000.0);// 3. 檢測關鍵點
ptrFeature2D->detect(image1,keypoints1);
ptrFeature2D->detect(image2,keypoints2);// 4. 提取描述子
cv::Mat descriptors1;
cv::Mat descriptors2;
ptrFeature2D->compute(image1,keypoints1,descriptors1);
ptrFeature2D->compute(image2,keypoints2,descriptors2);// 5. 構造匹配器
cv::BFMatcher matcher(cv::NORM_L2);cv::BFMatcher matcher2(cv::NORM_L2, // 度量差距
true); // 可以開啟 交叉檢查標志// 匹配兩幅圖像的描述子
std::vector<cv::DMatch> matches;
matcher.match(descriptors1,descriptors2, matches);
? ? ? 好的特征描述子不受照明和視角微小變動的影響,也不受圖像中噪聲的影響,因此它們通常
基于局部強度值的差值,SURF 描述子在關鍵點周圍局部地應用下面的簡易內核:
? ? ? ? 第一個內核度量水平方向的局部強度值差值(標為dx),第二個內核度量垂直方向的差值(標為dy)。通常將用于提取描述子向量的鄰域尺寸定為特征值縮放因子的20 倍(即20σ)。然后把這個正方形區域劃分成更小的4×4 子區域。對于每個子區域,在5×5 等分的位置上(用尺寸為2σ的內核)計算內核反饋值(dx 和dy)。
? ? ?使用SURF 和SIFT 的特征和描述子可以進行尺度無關的匹配,能夠取得較好的效果。
3.用二值描述子匹配關鍵點
? ? ? ? 上述描述子是浮點數類型的向量,大小為64、128等,這導致對它們的操作將耗資巨大,為了減少內存使用、降低計算量,人們引入了將一組比特位(0 和1)組合成二值描述子的概念。這里的難點在于,既要易于計算,又要在場景和視角變化時保持魯棒性。
// 1. 定義特征檢測器/描述子
// Construct the ORB feature object
cv::Ptr<cv::Feature2D> feature = cv::ORB::create(60);
// 大約60 個特征點
// 檢測并描述關鍵點
// 2. 檢測ORB 特征
feature->detectAndCompute(image1, cv::noArray(),
keypoints1, descriptors1);
feature->detectAndCompute(image2, cv::noArray(),
keypoints2, descriptors2);// 3. 構建匹配器
cv::BFMatcher matcher(cv::NORM_HAMMING); // 二值描述子一律使用Hamming 規范】// 4.匹配兩幅圖像的描述子
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
? ? ? ?ORB 算法在多個尺度下檢測特征點,這些特征點含有方向。基于這些特征點,ORB 描述子通過簡單比較強度值,提取出每個關鍵點的表征,在BRIEF 描述子的基礎上構建的,然后在關鍵點周圍的鄰域內隨機選取一對像素點,創建一個二值描述子。
? ? ? ?比較這兩個像素點的強度值,如果第一個點的強度值較大,就把對應描述子的位(bit)設為1,否則就設為0。對一批隨機像素點對進行上述處理,就產生了一個由若干位(bit)組成的描述子,通常采用128 到512 位(成對地測試)。