特征點(diǎn)的檢測(cè)與匹配(ORB,SIFT,SURFT比較)
本文旨在總結(jié)opencv-python上特征點(diǎn)的檢測(cè)和匹配。
1、特征點(diǎn)的檢測(cè)(包括:ORB,SIFT,SURFT)
2、特偵點(diǎn)匹配方法 (包括:暴力法,F(xiàn)LANN,以及隨機(jī)抽樣一致性優(yōu)化RANSAC算法)
注:由于SURF專利問題,所以opencv官方包目前不支持SURF但支持ORB和SIFT,安裝opencv-contrib-python包就可以解決
pip uninstall opencv-python
pip install opencv-contrib-python==3.4.2.17
一、特征點(diǎn)檢測(cè)
1、ORB算法
大致步驟:
ORB 的特點(diǎn)是速度超快,而且在一定程度上不受噪點(diǎn)和圖像變換的影響,例如旋轉(zhuǎn)和縮放變換等。
原理(步驟:)
(1)對(duì)圖像進(jìn)行多個(gè)版本的下采樣,構(gòu)建圖像金字塔。對(duì)每個(gè)采樣圖像進(jìn)行特征點(diǎn)檢測(cè)。
(2)采用FAST特征點(diǎn)檢測(cè)算法來檢測(cè)特征點(diǎn)。
(3)采用BRIEF描述子來描述每個(gè)特征點(diǎn)。注:描述子可以理解為一個(gè)用來描述特征點(diǎn)的向量,每個(gè)特偵點(diǎn)都有對(duì)應(yīng)的描述子,后面的特征點(diǎn)匹配就是對(duì)每個(gè)特征點(diǎn)之間描述子是否相似的判斷。
關(guān)于FAST特征點(diǎn)檢測(cè)算法:
FAST算法定義:特征點(diǎn)是如果某個(gè)像素點(diǎn)和他周圍領(lǐng)域足夠多的像素點(diǎn)處于不同區(qū)域,那么這個(gè)像素點(diǎn)就是特征點(diǎn)。
對(duì)于灰度圖,及特征點(diǎn)處灰度值與周圍足夠多像素灰度值不同。即設(shè)定一個(gè)閾值若16個(gè)點(diǎn)中有n(取12)個(gè)點(diǎn)的灰度與中心點(diǎn)的灰度差超過閾值則判斷為特征點(diǎn)。
關(guān)于BRIEF描述子
后面再補(bǔ)充—
實(shí)現(xiàn)代碼如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def ORB(img):
"""
ORB角點(diǎn)檢測(cè)
實(shí)例化ORB對(duì)象
"""
orb = cv2.ORB_create(nfeatures=500)
"""檢測(cè)關(guān)鍵點(diǎn),計(jì)算特征描述符"""
kp, des = orb.detectAndCompute(img, None)
# 將關(guān)鍵點(diǎn)繪制在圖像上
img2 = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0), flags=0)
cv2.imwrite("1.jpg", img2)
# 畫圖
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img2[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
img1 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = ORB(img1)
結(jié)果如圖:
可以看出ORB可以提取一些關(guān)鍵的特征點(diǎn)
2、SIFT算法
SIFT特征點(diǎn):尺度不變特征變換(Scale-invariant feature transform):大致方法是首先搜索所有尺度下圖像的位置,通過高斯微分函數(shù)來識(shí)別潛在的對(duì)于尺度和旋轉(zhuǎn)不變的興趣點(diǎn),然后在候選位置通過擬合精細(xì)的模型來確定位置和尺度,再基于圖像梯度,分配給關(guān)鍵點(diǎn)一個(gè)或多個(gè)方向,最后在每個(gè)關(guān)鍵點(diǎn)的周圍鄰域,在選定的尺度下測(cè)量圖像局部梯度來描述關(guān)鍵點(diǎn)。(總的來說就是根據(jù)梯度描述關(guān)鍵點(diǎn))
圖像高斯金字塔就考慮了這兩個(gè)方面:① 圖像的遠(yuǎn)近程度;② 圖像的模糊程度(理解為粗細(xì)更好)。前者通過上下采樣來實(shí)現(xiàn),后者通過高斯平滑處理來實(shí)現(xiàn)。
所謂的高斯金字塔是由原始圖像的n層下采樣圖像組成,然后在每個(gè)下采樣的層中利用不同核大小的高斯濾波來生成t張圖像,t張圖像除了第一張和最后一張其余圖像在本張、上張、下張一共26個(gè)點(diǎn)中看是否為極值。
具體原理可參考
代碼如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def SIFT(img):
# SIFT算法關(guān)鍵點(diǎn)檢測(cè)
# 讀取圖像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# SIFT關(guān)鍵點(diǎn)檢測(cè)
# 1. 實(shí)例化sift
sift = cv2.xfeatures2d.SIFT_create()
# 2. 利用sift.detectAndCompute()檢測(cè)關(guān)鍵點(diǎn)并計(jì)算
kp, des = sift.detectAndCompute(gray, None)
# gray: 進(jìn)行關(guān)鍵帶你檢測(cè)的圖像,注意是灰度圖像
# kp: 關(guān)鍵點(diǎn)信息,包括位置,尺度,方向信息
# des: 關(guān)鍵點(diǎn)描述符,每個(gè)關(guān)鍵點(diǎn)對(duì)應(yīng)128個(gè)梯度信息的特征向量
# 3. 將關(guān)鍵點(diǎn)檢測(cè)結(jié)果繪制在圖像上
# cv2.drawKeypoints(image, keypoints, outputimage, color, flags)
# image: 原始圖像
# keypoints: 關(guān)鍵點(diǎn)信息,將其繪制在圖像上
# outputimage: 輸出圖片,可以是原始圖像
# color: 顏色設(shè)置,通過修改(b, g, r)的值,更改畫筆的顏色,b = 藍(lán)色, g = 綠色, r = 紅色
# flags: 繪圖功能的標(biāo)識(shí)設(shè)置
# 1. cv2.DRAW_MATCHES_FLAGS_DEFAULT: 創(chuàng)建輸出圖像矩陣,使用現(xiàn)存的輸出圖像繪制匹配對(duì)象和特征點(diǎn),對(duì)每一個(gè)關(guān)鍵點(diǎn)只繪制中間點(diǎn)
# 2. cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG: 不創(chuàng)建輸出圖像矩陣,而是在輸出圖像上繪制匹配對(duì)
# 3. cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS: 對(duì)每一個(gè)特征點(diǎn)繪制帶大小和方向的關(guān)鍵點(diǎn)圖形
# 4. cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS: 單點(diǎn)的特征點(diǎn)不被繪制
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
cv2.imwrite("1.jpg", img)
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
img1 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SIFT(img1)
結(jié)果如圖:
看以看出SIFT算法所求的特征點(diǎn)遠(yuǎn)遠(yuǎn)大于ORB
3、SURF算法
SURF特征點(diǎn):加速穩(wěn)健特征(Speeded Up Robust Features):加速版的SIFT。通過修改濾波器的尺寸和模糊度從而得到不同層級(jí)的影像,別的和SIFT差不多。
Surf是可以說是對(duì)Sift算法的改進(jìn),該算子在保持 SIFT 算子優(yōu)良性能特點(diǎn)的基礎(chǔ)上,同時(shí)解決了 SIFT 計(jì)算復(fù)雜度高、耗時(shí)長(zhǎng)的缺點(diǎn),提升了算法的執(zhí)行效率,為算法在實(shí)時(shí)計(jì)算機(jī)視覺系統(tǒng)中應(yīng)用提供了可能,SIFT在一般的計(jì)算機(jī)對(duì)于日常使用的圖片想做到實(shí)時(shí)基本上是不可能的。
代碼如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def SURF(img):
surf = cv2.xfeatures2d.SURF_create()
kp, des = surf.detectAndCompute(img, None)
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
cv2.imwrite("1.jpg", img)
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
img1 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
結(jié)果如圖
可以看到和SIFT差不多密密麻麻的一大片,密集恐懼癥看的都哭了,
可達(dá)鴨,你…綠了。 可達(dá)鴨:我%#@#¥%#¥#¥¥##¥¥%!你不準(zhǔn)丑化我,我可是頂流。
特征點(diǎn)檢測(cè)還有些其他的算法,就先介紹這么多,以后有時(shí)間在總結(jié)。
二、特征點(diǎn)匹配算法
1、暴力求解
讓前一個(gè)圖像的每一個(gè)特征點(diǎn)直接遍歷后一個(gè)圖像的所有特征點(diǎn),找到最小值即為匹配,真的是簡(jiǎn)單粗暴。
代碼:暴力求解+SURF
import numpy as np
import cv2
from matplotlib import pyplot as plt
def SURF(img):
surf = cv2.xfeatures2d.SURF_create()
kp, des = surf.detectAndCompute(img, None)
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
def ByBFMatcher(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
"""
(1)暴力法
:param img1: 匹配圖像1
:param img2: 匹配圖像2
:param kp1: 匹配圖像1的特征點(diǎn)
:param kp2: 匹配圖像2的特征點(diǎn)
:param des1: 匹配圖像1的描述子
:param des2: 匹配圖像2的描述子
:return:
"""
if (flag == "SIFT" or flag == "sift"):
# SIFT方法或SURF
bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck=False)
else:
# ORB方法
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)
ms = bf.match(des1, des2)
# ms = sorted(ms, key=lambda x: x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imwrite("1.jpg", img3)
cv2.imshow("Matches", img3)
cv2.waitKey(0)
return ms
img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByBFMatcher(img1, img2, kp1, kp2, des1, des2, "SIFT")
結(jié)果如下
可以看得出來,什么也看不出來,一團(tuán)亂麻,而且一看就有很多誤匹配的,不急后文講優(yōu)化的時(shí)候會(huì)解決這一問題?!?br> 可達(dá)鴨:喂喂喂,我人都沒了,你這可惡的人類。
如果想使用ORB就可以把上面的ORB函數(shù)粘貼進(jìn)來,然后修改這幾行,SIFT同理。
kp1, des1 = ORB(img1)
kp2, des2 = ORB(img2)
matches = ByBFMatcher(img1, img2, kp1, kp2, des1, des2, "ORB")
1、FLANN求解
使用快速近似最近鄰搜索算法尋找。
代碼:FLANN + SURF
import numpy as np
import cv2
from matplotlib import pyplot as plt
def SURF(img):
surf = cv2.xfeatures2d.SURF_create()
kp, des = surf.detectAndCompute(img, None)
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
"""
(1)FLANN匹配器
:param img1: 匹配圖像1
:param img2: 匹配圖像2
:param kp1: 匹配圖像1的特征點(diǎn)
:param kp2: 匹配圖像2的特征點(diǎn)
:param des1: 匹配圖像1的描述子
:param des2: 匹配圖像2的描述子
:return:
"""
if (flag == "SIFT" or flag == "sift"):
# SIFT方法
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE,
trees=5)
search_params = dict(check=50)
else:
# ORB方法
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH,
table_number=6,
key_size=12,
multi_probe_level=1)
search_params = dict(check=50)
# 定義FLANN參數(shù)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.match(des1, des2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow("Matches", img3)
cv2.imwrite("1.jpg", img3)
cv2.waitKey(0)
return matches
img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
結(jié)果如下
依舊看不見鴨鴨。
三、優(yōu)化
由上圖匹配結(jié)果可以看出存在較多誤匹配現(xiàn)象所以必須采取手段來實(shí)現(xiàn)減少誤匹配。
1、RANSAC算法
RANSAC算法,是隨機(jī)抽樣一致。
代碼如下:FLANN + SURF + RANSAC
import numpy as np
import cv2
from matplotlib import pyplot as plt
def SURF(img):
surf = cv2.xfeatures2d.SURF_create()
kp, des = surf.detectAndCompute(img, None)
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
"""
(1)FLANN匹配器
:param img1: 匹配圖像1
:param img2: 匹配圖像2
:param kp1: 匹配圖像1的特征點(diǎn)
:param kp2: 匹配圖像2的特征點(diǎn)
:param des1: 匹配圖像1的描述子
:param des2: 匹配圖像2的描述子
:return:
"""
if (flag == "SIFT" or flag == "sift"):
# SIFT方法
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE,
trees=5)
search_params = dict(check=50)
else:
# ORB方法
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH,
table_number=6,
key_size=12,
multi_probe_level=1)
search_params = dict(check=50)
# 定義FLANN參數(shù)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.match(des1, des2)
# img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# cv2.imshow("Matches", img3)
# cv2.imwrite("1.jpg", img3)
# cv2.waitKey(0)
return matches
def RANSAC(img1, img2, kp1, kp2, matches):
MIN_MATCH_COUNT = 10
# store all the good matches as per Lowe's ratio test.
matchType = type(matches[0])
good = []
print(matchType)
if isinstance(matches[0], cv2.DMatch):
# 搜索使用的是match
good = matches
else:
# 搜索使用的是knnMatch
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: 3x3 變換矩陣.
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.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 = cv2.perspectiveTransform(pts, M)
#
# img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
print
"Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT)
matchesMask = None
draw_params = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=matchesMask, # draw only inliers
flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)
draw_params1 = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=None, # draw only inliers
flags=2)
img33 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params1)
cv2.imwrite("1.jpg", img33)
cv2.imwrite("2.jpg", img3)
cv2.imshow("before", img33)
cv2.imshow("now", img3)
cv2.waitKey(0)
img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
RANSAC(img1, img2, kp1, kp2, matches)
結(jié)果:
不使用RANSAC
使用RANSAC
可以看到匹配的特征點(diǎn)減少了很多,但是還是存在誤匹配的現(xiàn)象,效果不夠理想。
2、使用knnMatch
就是將flann.knnMatch改為flann.match
使用knnMatch得到的matches會(huì)是一個(gè)tuple,里面儲(chǔ)存兩個(gè)match。
match結(jié)構(gòu)為:
queryIdx:當(dāng)前幀特征點(diǎn)序號(hào)
trainIdx:下一幀最為匹配的特征點(diǎn)序號(hào)
distance:距離
一個(gè)match存最接近,一個(gè)match存次最接近。
代碼如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def SURF(img):
surf = cv2.xfeatures2d.SURF_create()
kp, des = surf.detectAndCompute(img, None)
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
"""
(1)FLANN匹配器
:param img1: 匹配圖像1
:param img2: 匹配圖像2
:param kp1: 匹配圖像1的特征點(diǎn)
:param kp2: 匹配圖像2的特征點(diǎn)
:param des1: 匹配圖像1的描述子
:param des2: 匹配圖像2的描述子
:return:
"""
if (flag == "SIFT" or flag == "sift"):
# SIFT方法
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE,
trees=5)
search_params = dict(check=50)
else:
# ORB方法
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH,
table_number=6,
key_size=12,
multi_probe_level=1)
search_params = dict(check=50)
# 定義FLANN參數(shù)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
# img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# cv2.imshow("Matches", img3)
# cv2.imwrite("1.jpg", img3)
# cv2.waitKey(0)
return matches
def RANSAC(img1, img2, kp1, kp2, matches):
MIN_MATCH_COUNT = 10
# store all the good matches as per Lowe's ratio test.
matchType = type(matches[0])
good = []
print(matchType)
if isinstance(matches[0], cv2.DMatch):
# 搜索使用的是match
good = matches
else:
# 搜索使用的是knnMatch
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: 3x3 變換矩陣.
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.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 = cv2.perspectiveTransform(pts, M)
#
# img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
print
"Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT)
matchesMask = None
draw_params = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=matchesMask, # draw only inliers
flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)
draw_params1 = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=None, # draw only inliers
flags=2)
img33 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params1)
cv2.imwrite("1.jpg", img33)
cv2.imwrite("2.jpg", img3)
cv2.imshow("before", img33)
cv2.imshow("now", img3)
cv2.waitKey(0)
img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
RANSAC(img1, img2, kp1, kp2, matches)
結(jié)果:
使用FLANN + SURF(match) + RANSAC
使用FLANN + SURF(knnMatch) + 不使用RANSAC
使用FLANN + SURF(knnMatch) + RANSAC
由上面可以看出,就算不使用RANSAC,單單就FLANN + SURF(knnMatch)的效果就已經(jīng)比FLANN + SURF(match) + RANSAC好很多,當(dāng)然效果最好的還是FLANN + SURF(knnMatch) + RANSAC。
可達(dá)鴨:我就一直綠唄。
四、總結(jié)
1、總的來說就時(shí)效性應(yīng)該是ORB>SURF>SIFT, FLANN>暴力求解。具體對(duì)比可以參考這篇文章
2、從效果來看FLANN + SURF/SURF(knnMatch) + RANSAC是最好的。ORB好像不支持knnMatch,我后面再測(cè)測(cè)看。
3、整個(gè)程序源碼放在最后,大家可以依據(jù)自己的需要組合成不的方式。
程序代碼:
要想使用knnMatch,把SURT,SIFT函數(shù)里面的文章來源:http://www.zghlxwxcb.cn/news/detail-461677.html
matches = flann.match(des1, des2)
改為文章來源地址http://www.zghlxwxcb.cn/news/detail-461677.html
matches = flann.knnMatch(des1, des2, k=2)
"""
圖像特征點(diǎn)的檢測(cè)與匹配
主要涉及:
1、ORB
2、SIFT
3、SURF
"""
"""
一、圖像特征點(diǎn)的檢測(cè)
"""
import numpy as np
import cv2
from matplotlib import pyplot as plt
def ORB(img):
"""
ORB角點(diǎn)檢測(cè)
實(shí)例化ORB對(duì)象
"""
orb = cv2.ORB_create(nfeatures=500)
"""檢測(cè)關(guān)鍵點(diǎn),計(jì)算特征描述符"""
kp, des = orb.detectAndCompute(img, None)
# 將關(guān)鍵點(diǎn)繪制在圖像上
img2 = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0), flags=0)
# 畫圖
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img2[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
def SIFT(img):
# SIFT算法關(guān)鍵點(diǎn)檢測(cè)
# 讀取圖像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# SIFT關(guān)鍵點(diǎn)檢測(cè)
# 1. 實(shí)例化sift
sift = cv2.xfeatures2d.SIFT_create()
# 2. 利用sift.detectAndCompute()檢測(cè)關(guān)鍵點(diǎn)并計(jì)算
kp, des = sift.detectAndCompute(gray, None)
# gray: 進(jìn)行關(guān)鍵帶你檢測(cè)的圖像,注意是灰度圖像
# kp: 關(guān)鍵點(diǎn)信息,包括位置,尺度,方向信息
# des: 關(guān)鍵點(diǎn)描述符,每個(gè)關(guān)鍵點(diǎn)對(duì)應(yīng)128個(gè)梯度信息的特征向量
# 3. 將關(guān)鍵點(diǎn)檢測(cè)結(jié)果繪制在圖像上
# cv2.drawKeypoints(image, keypoints, outputimage, color, flags)
# image: 原始圖像
# keypoints: 關(guān)鍵點(diǎn)信息,將其繪制在圖像上
# outputimage: 輸出圖片,可以是原始圖像
# color: 顏色設(shè)置,通過修改(b, g, r)的值,更改畫筆的顏色,b = 藍(lán)色, g = 綠色, r = 紅色
# flags: 繪圖功能的標(biāo)識(shí)設(shè)置
# 1. cv2.DRAW_MATCHES_FLAGS_DEFAULT: 創(chuàng)建輸出圖像矩陣,使用現(xiàn)存的輸出圖像繪制匹配對(duì)象和特征點(diǎn),對(duì)每一個(gè)關(guān)鍵點(diǎn)只繪制中間點(diǎn)
# 2. cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG: 不創(chuàng)建輸出圖像矩陣,而是在輸出圖像上繪制匹配對(duì)
# 3. cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS: 對(duì)每一個(gè)特征點(diǎn)繪制帶大小和方向的關(guān)鍵點(diǎn)圖形
# 4. cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS: 單點(diǎn)的特征點(diǎn)不被繪制
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
def SURF(img):
surf = cv2.xfeatures2d.SURF_create()
kp, des = surf.detectAndCompute(img, None)
cv2.drawKeypoints(img, kp, img, (0, 255, 0))
# 圖像顯示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
return kp, des
"""
2.圖像特征點(diǎn)匹配方法
(1)暴力法
(2)FLANN匹配器
"""
def ByBFMatcher(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
"""
(1)暴力法
:param img1: 匹配圖像1
:param img2: 匹配圖像2
:param kp1: 匹配圖像1的特征點(diǎn)
:param kp2: 匹配圖像2的特征點(diǎn)
:param des1: 匹配圖像1的描述子
:param des2: 匹配圖像2的描述子
:return:
"""
if (flag == "SIFT" or flag == "sift"):
# SIFT方法
bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck=False)
else:
# ORB方法
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)
ms = bf.knnMatch(des1, des2, k=2)
# ms = sorted(ms, key=lambda x: x.distance)
# img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# cv2.imshow("Matches", img3)
# cv2.waitKey(0)
return ms
def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
"""
(1)FLANN匹配器
:param img1: 匹配圖像1
:param img2: 匹配圖像2
:param kp1: 匹配圖像1的特征點(diǎn)
:param kp2: 匹配圖像2的特征點(diǎn)
:param des1: 匹配圖像1的描述子
:param des2: 匹配圖像2的描述子
:return:
"""
if (flag == "SIFT" or flag == "sift"):
# SIFT方法
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE,
trees=5)
search_params = dict(check=50)
else:
# ORB方法
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH,
table_number=6,
key_size=12,
multi_probe_level=1)
search_params = dict(check=50)
# 定義FLANN參數(shù)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
return matches
"""
優(yōu)化匹配結(jié)果
RANSAC算法是RANdom SAmple Consensus的縮寫,意為隨機(jī)抽樣一致
"""
def RANSAC(img1, img2, kp1, kp2, matches):
MIN_MATCH_COUNT = 10
# store all the good matches as per Lowe's ratio test.
matchType = type(matches[0])
good = []
print(matchType)
if isinstance(matches[0], cv2.DMatch):
# 搜索使用的是match
good = matches
else:
# 搜索使用的是knnMatch
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: 3x3 變換矩陣.
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.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 = cv2.perspectiveTransform(pts, M)
#
# img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
print
"Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT)
matchesMask = None
draw_params = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=matchesMask, # draw only inliers
flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)
draw_params1 = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=None, # draw only inliers
flags=2)
img33 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params1)
cv2.imshow("before", img33)
cv2.imshow("now", img3)
cv2.waitKey(0)
img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
RANSAC(img1, img2, kp1, kp2, matches)
到了這里,關(guān)于特征點(diǎn)的檢測(cè)與匹配(ORB,SIFT,SURFT比較)[opencv-python]的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!