OpenCV-Python Tutorial : A Candy from Official Main Page(五)

4.5FAST Algorithm for Corner Detection

4.5.1FAST算法

我們已了解多種特征檢測器,其中許多效果出色。但從實時應用的角度來看,它們的速度仍不夠快。一個典型例子是計算資源有限的SLAM(同步定位與建圖)移動機器人。

為解決此問題,Edward Rosten和Tom Drummond在2006年的論文《機器學習用于高速角點檢測》中提出了FAST(加速分段測試特征)算法(2010年進行了修訂)。以下是算法的基本概要,更多細節請參閱原始論文

  • 選擇圖像中的一個像素點?p,判斷其是否為關鍵點。設其強度值為?I?
  • 選擇合適的閾值?t
  • 考慮以待測像素?p?為中心的 16 個像素組成的圓形鄰域(見下圖)。

現在,如果在該圓形鄰域(16 個像素)內,存在?n?個連續的像素,它們全都比?I? + t?更亮,或者全都比?I? - t?更暗(如上圖中的白色虛線所示),則像素?p?被判定為一個角點。n?通常被選定為 12。

有人提出了一種高速測試方法來排除大量的非角點。該測試僅檢查位于 1、9、5 和 13 位置的四個像素(首先測試 1 和 9 是否過亮或過暗。如果是,則再檢查 5 和 13)。如果?p?是一個角點,那么這四個點中至少有三個必須全部比?I? + t?更亮,或者全部比?I? - t?更暗。如果這兩種情況都不滿足,那么?p?就不可能是一個角點。然后,可以對通過此測試的候選點應用完整的段測試標準,即檢查圓周上的所有像素。該檢測器本身表現出高性能,但也存在幾個弱點:

  • 當?n?< 12 時,它無法拒絕那么多的候選點。
  • 像素點的選擇并非最優,因為其效率取決于問題的(檢測)順序和角點外觀的分布。
  • 高速測試的結果被丟棄了(未被充分利用)。
  • 在相鄰位置會檢測到多個特征點。

前三個問題通過機器學習方法解決。最后一個問題使用非極大值抑制 (Non-maximal Suppression) 解決。

4.5.2機器學習角點檢測器

FAST 算法比其他現有的角點檢測器快好幾倍。但它對高噪聲水平不魯棒,且依賴于閾值的選擇。

cv.FastFeatureDetector_create()功能: 創建一個 FAST 檢測器對象。參數: 此函數可以接受多個參數來配置檢測器的行為,最常見的包括:threshold: 中心像素與周圍圓形鄰域像素的強度差閾值。用來判斷一個像素是更亮、更暗還是相似。值越小,檢測到的角點越多,但也可能包含更多噪聲。
nonmaxSuppression: 是否啟用非極大值抑制。
True (默認): 啟用。對于相鄰的多個角點,只保留響應最強的那個。這通常能獲得更好的、分布更均勻的角點。
False: 禁用。可能會在一小片區域檢測到多個密集的角點。
type: 指定高速測試的模式。通常是 cv.FAST_FEATURE_DETECTOR_TYPE_9_16(檢查 9 個連續像素,使用 16 像素的圓)或其他變體。通常使用默認值即可。
返回值: 返回一個 FastFeatureDetector 對象,你可以用它來調用 .detect() 等方法。
fast.detect()功能: 使用之前創建的 FAST 檢測器在圖像中查找關鍵點。參數:image: 輸入的圖像(必須是單通道灰度圖)。
mask: 可選參數。指定搜索關鍵點的區域。只有在掩模非零的區域才會進行檢測。
返回值: 返回一個關鍵點列表。每個關鍵點都是一個特殊的對象,包含以下重要屬性:pt: 關鍵點的 (x, y) 坐標。例如 keypoint.pt 會返回 (123.0, 45.0)。
size: 關鍵點的有效鄰域直徑。
response: 關鍵點的響應強度(例如,FAST 的評分函數 V)。響應越強,該點越可能是“好”的角點。
angle: 關鍵點的方向(度),-1 表示未計算。
octave: 檢測到該關鍵點的金字塔層級。
cv.drawKeypoints(image, keypoints, outImage, color=None, flags=None)
image: 輸入圖像。通常是你想在上面繪制關鍵點的原始圖像(彩色或灰度)。
keypoints: 關鍵點列表。這是由 fast.detect(), sift.detect(), orb.detect() 等特征檢測器函數返回的結果。
outImage: 輸出圖像。繪制了關鍵點后的圖像。可以是 None,也可以是一個已存在的圖像數組。
color: 關鍵點的顏色。指定繪制關鍵點時使用的顏色。
格式通常為 (B, G, R) 元組,例如 (0, 255, 0) 表示綠色。
默認是隨機顏色。
flags: 繪制標志。這是一個非常重要的參數,它控制著關鍵點的繪制方式。是 cv.DRAW_MATCHES_FLAGS_* 系列的常量。
cv.DRAW_MATCHES_FLAGS_DEFAULT (或 None): 默認方式。只繪制關鍵點的中心點(一個小圓點)。
cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS: 推薦使用。為每個關鍵點繪制一個帶方向的圓,圓的尺寸表示關鍵點的大小,從圓心發出的射線表示關鍵點的方向。這種方式信息量更大。
cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS: 不繪制單個的關鍵點(通常在與 DRAW_RICH_KEYPOINTS 結合使用時意義不大)。
cv.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG: 在輸出的圖像矩陣上直接繪制,而不是先創建一個副本。(通常不直接使用)
返回值: 返回繪制了關鍵點的輸出圖像。

完整工作流程總結

  1. 導入 OpenCV:?import cv2 as cv

  2. 讀取圖像并轉為灰度圖:?gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

  3. 創建檢測器:?fast = cv.FastFeatureDetector_create(...)

  4. 檢測關鍵點:?kp = fast.detect(gray, None)

  5. (可選) 繪制/使用關鍵點:?cv.drawKeypoints(...)?或直接訪問?kp?列表中的屬性進行計算。

demo:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as pltimg = cv.imread('image4.png', cv.IMREAD_GRAYSCALE)fast = cv.FastFeatureDetector_create()kp = fast.detect(img,None)
img2 = cv.drawKeypoints(img,kp,None,(0,255,0))# Print all default params
print( "Threshold: {}".format(fast.getThreshold()) )
print( "nonmaxSuppression:{}".format(fast.getNonmaxSuppression()) )
print( "neighborhood: {}".format(fast.getType()) )
print( "Total Keypoints with nonmaxSuppression: {}".format(len(kp)) )cv.imwrite('fast_true1.png', img2)fast.setNonmaxSuppression(0)
kp = fast.detect(img,None)print( "Total Keypoints without nonmaxSuppression: {}".format(len(kp)) )img3 = cv.drawKeypoints(img,kp,None,color=(0,255,0))
cv.imwrite('fast_true2.png', img3)

4.6BRIEF (Binary Robust Independent Elementary Features)

我們知道 SIFT 使用 128 維向量作為描述符。由于使用浮點數,它基本上需要 512 字節。類似地,SURF 也至少需要 256 字節(對于 64 維)。為成千上萬個特征創建這樣的向量會占用大量內存,這對于資源受限的應用(尤其是嵌入式系統)是不可行的。內存越大,匹配所需的時間就越長。

但實際匹配可能并不需要所有這些維度。我們可以使用多種方法(如 PCA、LDA 等)來壓縮它。甚至還有其他方法,如使用 LSH(局部敏感哈希)進行哈希處理,將 SIFT 的浮點描述符轉換為二進制字符串。這些二進制字符串使用漢明距離進行特征匹配。這提供了更好的加速效果,因為計算漢明距離僅需進行異或運算和位計數,這在具有 SSE 指令的現代 CPU 中非常快。但這里,我們需要先找到描述符,然后才能應用哈希,這并沒有解決我們最初的內存問題。

BRIEF?就在此時應運而生。它提供了一種捷徑,無需先找到描述符就能直接獲得二進制字符串。它取一個平滑后的圖像塊,并以一種獨特的方式(論文中 explained)選擇一組位置對。然后對這些位置對進行像素強度比較。例如,令第一個位置對為 p ?和 q 。如果 ,則結果為 1,否則為 0。這對所有 個位置對進行處理,得到一個 維的比特串。

這個 可以是 128、256 或 512。OpenCV 支持所有這些尺寸,但默認情況下是 256(OpenCV 以字節表示。因此實際值為 16、32 和 64 字節)。一旦得到這個比特串,你就可以使用漢明距離來匹配這些描述符。

重要的一點是,BRIEF 是一種特征描述符,它不提供任何查找特征點的方法。因此,你必須使用其他任何特征檢測器,如 SIFT、SURF 等。該論文推薦使用 CenSurE(一種快速檢測器),并且 BRIEF 對于 CenSurE 特征點的效果甚至比 SURF 點略好。

簡而言之,BRIEF 是一種更快的特征描述符計算與匹配方法。除非存在大的面內旋轉,否則它也能提供較高的識別率。

OpenCV 中的 STAR(CenSurE)

STAR 是一種源自 CenSurE 的特征檢測器。然而,與使用正方形、六邊形和八邊形等多邊形來逼近圓形的 CenSurE 不同,STAR 使用 2 個重疊的正方形來模擬圓形:1 個直立,1 個旋轉 45 度。這些多邊形是雙層的。它們可以看作是具有粗邊框的多邊形。邊框和內部封閉區域的權重符號相反。這比其他尺度空間檢測器具有更好的計算特性,并且能夠實時實現。與 SIFT 和 SURF 在子采樣像素上尋找極值(這會在較大尺度上影響精度)不同,CenSurE 在金字塔的所有尺度上使用全空間分辨率來創建特征向量。

4.7ORB (Oriented FAST and Rotated BRIEF)

作為一名 OpenCV 愛好者,關于 ORB 最重要的一點是它出自“OpenCV Labs”。該算法由 Ethan Rublee, Vincent Rabaud, Kurt Konolige 和 Gary R. Bradski 在他們 2011 年的論文?《ORB: SIFT 或 SURF 的高效替代品》?中提出。正如標題所言,它在計算成本、匹配性能,尤其是專利方面,是 SIFT 和 SURF 的一個很好的替代品。是的,SIFT 和 SURF 是申請了專利的,使用它們需要付費。但 ORB 沒有!

ORB 基本上是?FAST 關鍵點檢測器和?BRIEF 描述符的融合,并進行了許多修改以增強性能。首先,它使用 FAST 尋找關鍵點,然后應用?Harris 角點測度從中找出前 N 個最好的點。它還使用圖像金字塔來產生多尺度特征。但有一個問題是,FAST 不計算方向。那么旋轉不變性如何實現呢?作者提出了以下修改。

它計算了以角點為中心的圖像塊的強度加權質心。從該角點指向質心的向量方向即為該點的方向。為了提高旋轉不變性,使用 x 和 y 計算圖像矩,計算應在半徑為 ?r (?r ?是圖像塊的大小)的圓形區域內進行。

對于描述符,ORB 使用 BRIEF 描述符。但我們已經知道 BRIEF 在旋轉情況下表現很差。因此 ORB 所做的就是根據關鍵點的方向來“引導”(steer)BRIEF。對于在位置的任何包含 ?n ?個二進制測試的特征集,定義一個 的矩陣 ?S ,其中包含了這些像素的坐標。然后利用圖像塊的方向 theta,找到其旋轉矩陣并旋轉 ?S ,得到被引導(旋轉)后的版本

ORB 將角度離散化為 $ 2\pi/30 $(12 度)的增量,并構建了一個預計算 BRIEF 模式的查找表。只要關鍵點方向 $\theta$ 在不同視角下保持一致,就會使用正確的點集來計算其描述符。

BRIEF 有一個重要特性:每個位特征具有高方差且其均值接近 0.5。但一旦其沿關鍵點方向被引導后,就會失去這個屬性而變得更加分散。高方差使得特征更具區分度,因為它對輸入有不同的響應。另一個理想特性是測試之間不相關,這樣每個測試都會對結果有貢獻。為了解決所有這些問題,ORB 在所有可能的二進制測試中運行一種貪婪搜索,以找到那些同時具有高方差、均值接近 0.5 且不相關的測試。結果被稱為?rBRIEF(Rotation-aware BRIEF)。

對于描述符匹配,它使用了在傳統 LSH 基礎上改進的多探針 LSH(multi-probe LSH)。論文指出,ORB 比 SURF 和 SIFT 快得多,并且 ORB 描述符的性能優于 SURF。ORB 是低功耗設備(如進行全景拼接等應用)的一個非常好的選擇。

cv.ORB_create()功能: 創建一個 ORB 檢測器對象。參數: 此函數可以接受多個參數來配置 ORB 檢測器的行為。以下是一些最常用的參數:nfeatures: 保留的最佳特征點的最大數量。默認值為 500。
scaleFactor: 構建圖像金字塔時的尺度因子。例如,scaleFactor=1.2 表示每層金字塔是下一層的 1.2 倍。值越大,金字塔層數越少,計算越快,但可能漏掉某些尺度的特征。 默認值為 1.2。
nlevels: 圖像金字塔的層數。默認值為 8。
edgeThreshold: 圖像邊界的閾值,由于特征點需要完整的鄰域,邊界上的點會被忽略。默認值為 31。
firstLevel: 將原圖像作為金字塔的第幾層。默認值為 0。
WTA_K: 用于生成 rBRIEF 描述符的每個元素的點數。默認是 2(即一次比較兩個點,產生 0 或 1)。如果為 3 或 4,則一次比較 3 或 4 個點,取亮度最高的那個點的索引(0,1,2 或 0,1,2,3),這會生成需要更多位來表示的描述符。
scoreType: 關鍵點評分類型。cv.ORB_HARRIS_SCORE(默認)使用 Harris 角點響應函數來排名特征點;cv.ORB_FAST_SCORE 使用 FAST 方法的一個稍低質量的替代方案,但速度更快。
patchSize: 用于生成描述符的圖像塊大小。默認值為 31。
返回值: 返回一個 ORB 對象,你可以用它來調用 .detect(), .compute(), 或 .detectAndCompute() 等方法。
orb.detect()功能: 使用之前創建的 ORB 檢測器在圖像中查找關鍵點。注意:此方法只檢測關鍵點的位置、尺度和方向,不計算描述符。參數:image: 輸入的圖像(必須是單通道灰度圖)。
mask: 可選參數。指定搜索關鍵點的區域。只有在掩模非零的區域才會進行檢測。
返回值: 返回一個關鍵點列表。每個關鍵點都是一個特殊的對象,包含以下重要屬性:pt: 關鍵點的 (x, y) 坐標。例如 keypoint.pt 會返回 (123.0, 45.0)。
size: 關鍵點的有效鄰域直徑。
angle: 關鍵點的方向(度),由 ORB 算法計算得出,提供了旋轉不變性。
response: 關鍵點的響應強度(例如,Harris 角點響應或 FAST 得分)。響應越強,該點越可能是“好”的特征點。
octave: 檢測到該關鍵點的金字塔層級。

demo:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as pltimg = cv.imread('image4.png')
img1 = cv.cvtColor(img,cv.COLOR_BGR2GRAY)orb = cv.ORB_create()kp = orb.detect(img1,None)kp,des = orb.compute(img1,kp)img2 = cv.drawKeypoints(img,kp,None,color=(255,0,0),flags=0)
cv.imwrite('orb.jpg',img2)

4.8Feature Matching

4.8.1Brute-Force 匹配器基礎

Brute-Force 匹配器很簡單。它獲取第一組(查詢圖像)中一個特征的描述符,并使用某種距離計算方式與第二組(訓練圖像)中的所有其他特征進行匹配。然后返回最接近的一個。

對于 BF 匹配器,我們首先需要使用?cv.BFMatcher()?創建 BFMatcher 對象。它接受兩個可選參數:

  1. 第一個是?normType(規范類型)。它指定要使用的距離測量方式。默認是?cv.NORM_L2。這對于 SIFT、SURF 等描述符很好(cv.NORM_L1?也可用)。對于基于二進制字符串的描述符,如 ORB、BRIEF、BRISK 等,應該使用?cv.NORM_HAMMING,它使用漢明距離作為測量方式。如果 ORB 使用?WTA_K == 3或?4(即每個元素用多個比特表示),則應使用?cv.NORM_HAMMING2

  2. 第二個參數是一個布爾變量?crossCheck(交叉驗證),默認為 false。如果為 true,匹配器只返回那些滿足 (i,j) 條件的匹配項:集合 A 中的第 i 個描述符在集合 B 中的最佳匹配是第 j 個描述符,并且反過來,集合 B 中的第 j 個描述符在集合 A 中的最佳匹配也必須是第 i 個描述符。也就是說,兩組中的兩個特征應該互相匹配。它能提供一致的結果,并且是 SIFT 論文中 D.Lowe 提出的比率檢驗(ratio test)的一個很好的替代方案。

一旦創建完成,有兩個重要的方法:BFMatcher.match()?和?BFMatcher.knnMatch()。第一個方法返回最佳匹配。第二個方法返回?k 個最佳匹配,其中 k 由用戶指定。當我們需要對這些匹配進行額外處理時,這個方法會很有用。

就像我們使用?cv.drawKeypoints()?來繪制關鍵點一樣,cv.drawMatches()?幫助我們繪制匹配項。它將兩幅圖像水平堆疊,并從第一幅圖像到第二幅圖像繪制線條來顯示最佳匹配。還有一個?cv.drawMatchesKnn?可以繪制所有 k 個最佳匹配。如果 k=2,它將為每個關鍵點繪制兩條匹配線。因此,如果我們想選擇性地繪制,必須傳遞一個掩碼(mask)。

4.8.2Brute-Force Matching with ORB Descriptors

demo:

import numpy as np
import cv2 as cv
import matplotlib.pyplot as pltimg1 = cv.imread('image4.png',cv.IMREAD_GRAYSCALE)
img2 = cv.imread('image4.png',cv.IMREAD_GRAYSCALE)# Initiate ORB detector
orb = cv.ORB_create()kp1,des1 = orb.detectAndCompute(img1,None)
kp2,des2 = orb.detectAndCompute(img2,None)# create BFMatcher object
bf = cv.BFMatcher(cv.NORM_HAMMING,crossCheck=True)# Match descriptors.
matches = bf.match(des1,des2)# Sort them in the order of their distance.
matches = sorted(matches,key = lambda x:x.distance)# Draw first 30 matches.
img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:30],None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS,matchColor=[0,0,255])plt.imshow(img3),plt.show()

4.8.3What is this Matcher Object?

什么是這個匹配器對象(Matcher Object)?

matches = bf.match(des1, des2)?這行代碼的結果是一個由?DMatch 對象?組成的列表。這個 DMatch 對象具有以下屬性:

  • DMatch.distance?- 描述符之間的距離。距離越小,匹配越好

  • DMatch.trainIdx?- 在訓練描述符集(第二張圖片?des2)中的描述符的索引。

  • DMatch.queryIdx?- 在查詢描述符集(第一張圖片?des1)中的描述符的索引。

  • DMatch.imgIdx?- 訓練圖像(第二張圖片)的索引。

4.8.4Brute-Force Matching with SIFT Descriptors and Ratio Test

我們將使用BFMatcher.knnMatch()來獲得k個最佳匹配。在這個例子中,我們將采取k=2

demo:

from turtle import distance
import numpy as np
import cv2 as cv
import matplotlib.pyplot as pltimg1 = cv.imread('image4.png',cv.IMREAD_GRAYSCALE)
img2 = cv.imread('image4.png',cv.IMREAD_GRAYSCALE)# Initiate SIFT detector
sift = cv.SIFT_create()kp1,des1 = sift.detectAndCompute(img1,None)
kp2,des2 = sift.detectAndCompute(img2,None)# BFMatcher with default params
bf = cv.BFMatcher()
matches = bf.knnMatch(des1,des2,k=2)
# print(matches)
# Apply ratio test
good = []
for m,n in matches:if m.distance < 0.75*n.distance:good.append([m])img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)plt.imshow(img3),plt.show()

4.8.5基于 FLANN 的匹配器

FLANN 代表快速近似最近鄰庫(Fast Library for Approximate Nearest Neighbors)。它包含了一系列算法,這些算法針對在大數據集中進行快速最近鄰搜索和高維特征進行了優化。對于大型數據集,它比 BFMatcher 工作得更快。我們將看到第二個基于 FLANN 匹配器的例子。

對于基于 FLANN 的匹配器,我們需要傳遞兩個字典,這兩個字典指定了要使用的算法及其相關參數等。

第一個是?IndexParams(索引參數)。對于各種算法,需要傳遞的信息在 FLANN 文檔中有說明。總結如下:

對于 SIFT/SURF(浮點型描述符):使用?KDTree?算法,你可以傳遞以下參數:

FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
trees=5:構建 5 棵 KDTree,搜索時并行查詢,提高召回率。

對于 ORB/BRIEF(二進制描述符):使用?LSH(局部敏感哈希)算法:

FLANN_INDEX_LSH = 6
index_params = dict(algorithm = FLANN_INDEX_LSH,table_number = 6,   # 推薦值:12key_size = 12,      # 推薦值:20  multi_probe_level = 1) # 推薦值:2

第二個字典是?SearchParams(搜索參數)。它指定了索引中的樹應該被遞歸遍歷的次數。值越高精度越好,但也需要更多時間。如果你想改變這個值,可以傳遞?search_params = dict(checks=100)search_params(搜索參數)作用是控制搜索的精細程度checks:指定遞歸遍歷樹的次數。值越高,搜索越徹底,找到最近鄰的概率越大,但速度越慢。

search_params = dict(checks=50)  # 默認值可能是32

demo:

from turtle import distance
import numpy as np
import cv2 as cv
import matplotlib.pyplot as pltimg1 = cv.imread('image4.png',cv.IMREAD_GRAYSCALE)
img2 = cv.imread('image4.png',cv.IMREAD_GRAYSCALE)# Initiate SIFT detector
sift = cv.SIFT_create()kp1,des1 = sift.detectAndCompute(img1,None)
kp2,des2 = sift.detectAndCompute(img2,None)# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_param = dict(algorithm =FLANN_INDEX_KDTREE,tree = 5)
search_param = dict(checks=50)flann = cv.FlannBasedMatcher(index_param,search_param)matches = flann.knnMatch(des1,des2,k=2)# Need to draw only good matches, so create a mask
matchesMask =[[0,0] for i in range(len(matches))]# ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):if m.distance < 0.9*n.distance:matchesMask[i]=[1,0]draw_params = dict(matchColor = (0,255,0),singlePointColor = (255,125,0),matchesMask = matchesMask,flags = cv.DrawMatchesFlags_DEFAULT)img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)plt.imshow(img3,),plt.show()

4.9Feature Matching + Homography to find Objects

給定兩張有重疊區域的圖像(比如從不同角度拍攝的同一本書、同一棟建筑),本節目標是通過特征匹配找到它們之間的對應點,并計算出一個變換矩陣(單應性矩陣),從而可以將一張圖像“投影”或“對齊”到另一張圖像的視角上。

cv.findHomography()?-?計算變換矩陣

作用:?根據一系列對應的點對,計算出描述兩個平面之間透視變換關系的?3x3 單應性矩陣 (H)

H, mask = cv.findHomography(srcPoints, dstPoints, method=0, ransacReprojThreshold=3.0, maxIters=2000, confidence=0.995)
參數詳解:srcPoints: 源平面中點的坐標。通常是第一張圖像(查詢圖像)中的關鍵點坐標。形狀為 (N, 1, 2) 的 NumPy 數組,其中 N 是點的數量。
dstPoints: 目標平面中點的坐標。與 srcPoints 一一對應。是第二張圖像(訓練圖像)中的關鍵點坐標。形狀必須與 srcPoints 相同。
method: 計算 H 矩陣的方法。
0: 常規方法,使用所有點。如果存在錯誤匹配(離群點),結果會非常差。
cv.RANSAC (推薦): 使用 RANSAC 算法。這是一種魯棒的方法,即使輸入的點對中存在大量錯誤匹配,它也能估算出正確的模型。它會找出一個最能符合內點 (inliers) 的模型。
cv.LMEDS: 最小中值方法。
ransacReprojThreshold: 僅當 method=cv.RANSAC 時有效。它是一個距離閾值(單位為像素),用來判斷一個點是否是內點。這個參數很重要!
原理: 對于每一個點,算法使用計算出的 H 矩陣將 srcPoint 變換到目標平面,得到一個預測點。然后計算這個預測點與真實的 dstPoint 之間的歐氏距離。如果這個距離小于 ransacReprojThreshold,則該點被標記為內點。
如何設置: 值越大,能容忍的誤差越大,被認為是內點的點就越多(可能包含一些錯誤點)。值越小,要求越嚴格,內點越少但更精確。通常設置在 1.0 到 10.0 之間,具體取決于你匹配的精度和圖像分辨率。
maxIters: RANSAC 的最大迭代次數。
confidence: 置信度,表示算法期望找到的內點比例。
返回值:H: 計算得到的 3x3 單應性矩陣。如果沒有找到解,則返回 None。
mask: 可選輸出(尤其在使用 cv.RANSAC 或 cv.LMEDS 時非常有用)。它是一個形狀為 (N, 1) 的數組,其中元素為 0 或 1。
mask[i] == 1: 表示第 i 對點被算法判定為內點,即它符合最終計算出的 H 模型。
mask[i] == 0: 表示第 i 對點是外點 (outlier),即錯誤匹配。

cv.perspectiveTransform()?-?應用變換矩陣

作用:?對一個或多個點執行透視變換。它使用一個已經計算好的變換矩陣(比如由?cv.findHomography()?得到的 H),將輸入的點從源空間變換到目標空間。

dst = cv.perspectiveTransform(src, m)
參數詳解:src: 輸入的點集。可以是單個點 [x, y],但更常見的是多個點的集合。其形狀必須是 (N, 1, 2),其中 N 是點的數量。注意: 這里的點坐標是 (x, y),而不是圖像索引 (row, col)。
m: 變換矩陣。對于透視變換,這就是一個 3x3 的矩陣(例如單應性矩陣 H)。
返回值:dst: 變換后的點集。形狀與 src 相同,為 (N, 1, 2)。

核心區別與聯系:廚師與食譜的比喻

特性cv.findHomography()cv.perspectiveTransform()
角色廚師 (Chef)學徒 (Apprentice)
任務創造食譜 (H矩陣)。根據一些原材料(對應點對srcPoints, dstPoints),研究出烹飪的配方和步驟(計算變換矩陣 H)。執行食譜。已經拿到了廚師寫好的詳細食譜(H 矩陣),只需要按照食譜把新的原材料(新的點集?src)做熟(變換)即可。
輸入兩套對應的點集一套點集 + 一個變換矩陣
輸出一個變換矩陣 (+ 內點掩碼)變換后的點集
過程復雜的數學計算和優化(如 RANSAC)直接的矩陣乘法運算

demo:輸入兩張圖像,計算單應性矩陣:

我們設置一個條件,即至少有10個匹配(由MIN_MATCH_COUNT定義)在那里找到對象。否則,只需顯示一條消息,說沒有足夠的匹配。

如果找到足夠的匹配項,我們將提取兩個圖像中匹配的關鍵點的位置。他們通過尋找透視轉換。一旦我們得到這個3x3變換矩陣,我們就用它來將queryImage的角轉換為trainImage中的相應點。然后我們畫它。

import numpy as np
import cv2 as cv
import matplotlib.pyplot as pltMIN_MATCH_COUNT = 10# 讀取圖像 - 注意:應該是兩張不同的圖像!
# 例如:img1 = 'object.png', img2 = 'scene_with_object.png'
img1 = cv.imread('image1.png', cv.IMREAD_GRAYSCALE)  # 查詢圖像(小物體)
img2 = cv.imread('image2.png', cv.IMREAD_GRAYSCALE)  # 訓練圖像(包含物體的場景)# 檢查圖像是否成功加載
if img1 is None or img2 is None:print("錯誤:無法加載圖像!請檢查文件路徑。")exit()# Initiate SIFT detector
sift = cv.SIFT_create()# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)# 使用FLANN匹配器
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)flann = cv.FlannBasedMatcher(index_params, search_params)# 進行KNN匹配
matches = flann.knnMatch(des1, des2, k=2)# store all the good matches as per Lowe's ratio test.
good = []
for m, n in matches:if m.distance < 0.7 * n.distance:good.append(m)if len(good) > MIN_MATCH_COUNT:src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC, 5.0)matchesMask = mask.ravel().tolist()h, w = img1.shape  # 修正這里# 定義源圖像的四個角點pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)dst = cv.perspectiveTransform(pts, M)# 在目標圖像上繪制變換后的邊界框img2_with_box = cv.polylines(img2, [np.int32(dst)], True, 255, 3, cv.LINE_AA)print(f"找到 {len(good)} 個良好匹配,其中 {sum(matchesMask)} 個是內點")else:print(f"未找到足夠匹配 - {len(good)}/{MIN_MATCH_COUNT}")matchesMask = Noneimg2_with_box = img2  # 如果沒有找到足夠匹配,使用原始圖像draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green colorsinglePointColor=None,matchesMask=matchesMask,  # draw only inliersflags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)# 繪制匹配結果
img3 = cv.drawMatches(img1, kp1, img2_with_box, kp2, good, None, **draw_params)# 顯示結果
plt.figure(figsize=(15, 10))
plt.imshow(img3, cmap='gray')
plt.title('特征匹配與單應性變換結果')
plt.axis('off')
plt.show()

參考:OpenCV: Feature Detection and Description

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95343.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95343.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95343.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LINUX 91 SHELL:刪除空文件夾 計數

問題 [rootweb ~]# find -type f -exec echo "file:{}" $path; find: 遺漏“-exec”的參數 您在 /var/spool/mail/root 中有郵件[rootweb ~]# $path/root -bash: /root: 沒有那個文件或目錄 您在 /var/spool/mail/root 中有新郵件 [rootweb ~]# path/root [rootweb ~…

視頻軟解碼技術詳解:原理、應用與未來發展

視頻軟解碼的基本原理 概念解析&#xff1a;CPU主導的通用解碼方式 視頻軟解碼是一種完全依賴通用CPU執行解碼算法的視頻還原技術&#xff0c;其核心特征在于不依賴任何專用硬件模塊&#xff0c;而是通過軟件程序調用CPU的通用計算能力完成壓縮視頻數據的解碼過程[1][2]。與硬…

線性回歸中梯度下降與正規方程以及擬合問題與正則化

線性回歸實戰指南&#xff1a;從理論到實踐 目錄 線性回歸理論基礎機器學習項目開發流程波士頓房價預測實戰梯度下降與正規方程模型評估指標擬合問題與正則化總結與展望 1. 線性回歸理論基礎 1.1 什么是線性回歸&#xff1f; 線性回歸是一種監督學習算法&#xff0c;用于預…

為什么46.1k程序員都在用這個AI繪畫神器?我體驗一周后終于明白了

大家好&#xff0c;我是顧北&#xff0c;一名AI應用探索者&#xff0c;也是GitHub開源項目收集者。說起AI繪畫這事兒&#xff0c;我之前真的是又愛又恨。上個月想給朋友搞張生日賀圖&#xff0c;結果在Stable Diffusion WebUI里折騰了大半天。采樣步數&#xff1f;CFG比例&…

Java基礎第8天總結(map遍歷、Stream流)

選中一部分代碼&#xff0c;然后CTRLALTT&#xff0c;可以在外面套上while循環,try..catch之類的小案例&#xff1a;電影信息管理模塊&#xff1a;用戶可以上架、查詢、下架、下架某個主演參演的電影package Demo;import lombok.AllArgsConstructor; import lombok.Data; impor…

總線矩陣的原理

總線矩陣&#xff08;Bus Matrix&#xff09;是多主設備共享多從設備的智能連接與仲裁核心&#xff0c;本質是一個“靈活的交叉開關陣列”&#xff0c;用于解決多個主設備&#xff08;如CPU、DMA、GPU&#xff09;同時訪問多個從設備&#xff08;如內存、外設、存儲芯片&#x…

硬件開發_基于Zigee組網的果園養殖監控系統

一.系統概述 果園環境監控系統功能如下&#xff1a; 核心控制器&#xff1a;以STM32為核心控制器&#xff0c;承擔整體的數據采集、處理及控制任務。環境參數監測&#xff1a;集成溫度傳感器、CO?傳感器、光照傳感器和土壤濕度傳感器&#xff0c;可實時采集果園內的溫度、二氧…

K8s調度核心:從Pod分配到節點優化

在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;Pod 調度是指 K8s 系統根據特定規則和策略&#xff0c;將 Pod 合理分配到集群中的某個節點&#xff08;Node&#xff09;上運行的過程。其核心目標是確保 Pod 在合適的節點上高效、穩定地運行&#xff0c;充分利用集群資…

Tomcat 企業級運維實戰系列(四):Tomcat 企業級監控

Tomcat 企業級運維實戰系列&#xff08;四&#xff09;&#xff1a;Tomcat 企業級監控一&#xff1a;監控工具1&#xff09;概述2&#xff09;流程3&#xff09;部署二&#xff1a;監控命令1&#xff09;jps2&#xff09;jstack3&#xff09;jmap4&#xff09;MAT 工具分析三&a…

技術干貨丨HyperMesh 新界面功能與技術升級解析

全文內容選自 Altair 區域技術交流會華東站Altair 高級技術經理 張晨《HyperWorks 2025&#xff1a;下一代建模可視化和二次開發平臺》演講1、引言今天我為大家介紹 HyperMesh——這個大家既熟悉又陌生的工具。說熟悉&#xff0c;是因為它一直是工程仿真領域的主流建模軟件&…

《IC驗證必看|隨機穩定性 / 再現性》

同一用例 A 機 pass、B 機 fail&#xff1f;——SystemVerilog 隨機穩定性 / 可復現性全攻略&#xff08;含代碼與排查清單&#xff09;你該到什么水平&#xff1f;&#xff08;對標 20k / 25k / 30k&#xff09; 20k&#xff08;入門會用&#xff09; 會 randomize()、$urando…

字符編碼的本質

目的 最近做一個加密方面的研究&#xff0c;加密之后的二進制&#xff0c;通過轉碼之后&#xff0c;再也找不回之前的二進制了。 怎么試都不行&#xff0c;真是非常得奇怪&#xff01;&#xff01;&#xff01;&#xff01;先說說字符編碼基礎知識 在信息技術的海洋中&#xff…

網格圖--Day03--網格圖DFS--2658. 網格圖中魚的最大數目,1034. 邊界著色,1020. 飛地的數量

網格圖–Day03–網格圖DFS–2658. 網格圖中魚的最大數目&#xff0c;1034. 邊界著色&#xff0c;1020. 飛地的數量 今天要訓練的題目類型是&#xff1a;【網格圖DFS】&#xff0c;題單來自靈艾山茶府。 適用于需要計算連通塊個數、大小的題目。 部分題目做法不止一種&#xff0…

新能源車焊接中發那科機器人保護氣省氣方法

在新能源汽車制造領域&#xff0c;焊接工藝是保障車身結構強度與安全性的關鍵環節&#xff0c;發那科焊接機器人憑借高精度與穩定性成為產線主力設備。保護氣體消耗在焊接成本中占比顯著&#xff0c;尋找高效省氣方法成為行業降本增效的核心需求。WGFACS節氣裝置以智能化控制技…

CornerNet2025再研究---將目標檢測問題視作關鍵點檢測與配對

CornerNet于2019年3月份提出&#xff0c;CW近期回顧了下這個在當時引起不少關注的目標檢測模型&#xff0c;它的亮點在于提出了一套新的方法論——將目標檢測轉化為對物體成對關鍵點(角點)的檢測。通過將目標物體視作成對的關鍵點&#xff0c;其不需要在圖像上鋪設先驗錨框(anc…

【C++】vector(2)

目錄 1. insert的實現 2. 迭代器失效 2.1 迭代器失效的兩種情況 指向已釋放的內存&#xff08;物理失效&#xff09; 元素移動導致迭代器指向錯誤&#xff08;邏輯失效&#xff09; 2.2 修改代碼 3. erase的實現 ?編輯修改代碼 4. resize的實現 5. 構造函數 5.1 默認…

機器翻譯:python庫translatepy的詳細使用(集成了多種翻譯服務)

更多內容請見: 機器翻譯修煉-專欄介紹和目錄 文章目錄 一、translatepy概述 1.1 translatepy介紹 1.1 安裝 二、基本使用 2.1 初始化 `Translator` 2.2 文本翻譯 2.3 語言檢測 2.4 獲取翻譯備選方案 2.5 單詞音標獲取 2.6 語音合成 2.7 例句查詢 2.8 拼寫檢查 三、高級功能 3.…

Spring Bean生命周期的完全指南

簡介&#xff1a;超越Bean——揭開Spring Bean的隱秘生活 想象一場復雜宏大的舞臺劇。作為觀眾&#xff0c;我們看到的是最終的演出——一個流暢運行的應用程序。但在這光鮮的幕后&#xff0c;隱藏著一套嚴謹細致的流程&#xff1a;選角&#xff08;實例化Bean&#xff09;、試…

網絡安全A模塊專項練習任務九解析

任務九&#xff1a;Linux操作系統安全配置-2任務環境說明&#xff1a; (Linux)系統&#xff1a;用戶名root&#xff0c;密碼1234561. 設置禁止使用最近用過的6個舊密碼&#xff0c;將配置文件中對應的部分截圖&#xff1b;編輯/etc/pam.d/system-auth文件&#xff0c;找到passw…

Linex進程管理

一、進程查看命令1.pstree用于查看進程樹之間的關系&#xff0c;誰是父進程&#xff0c;誰是子進程&#xff0c;可以清楚的看出來是誰創建了誰語法&#xff1a;pstree [選項] -A各進程樹之間的連接以ASCII碼字符來連接-U各進程樹之間的連接以utf8字符來連接&#xff0c;某些終…