1. 程式人生 > >python opencv入門 特徵點匹配+單應性查詢目標(39)

python opencv入門 特徵點匹配+單應性查詢目標(39)

內容來自OpenCV-Python Tutorials 自己翻譯整理

目標:
我們將結合特徵點匹配和尋找單應性的方法,使用calib3d模組在複雜的影象當中尋找已知目標。

基礎:

這裡簡單說一下什麼事單應變換,如果有說的不對,還請各位看官斧正。

一般來講,二維的影象變換可以分成這幾類

  • 等距變換:簡單的說就是對一個影象使用旋轉、平移等操作。對應的矩陣也是旋轉平移的矩陣
  • 相似變換:把等距變換再加上一個尺度,支援對影象按比例的放大和縮小
  • 仿射變換:仿射變換可以看成是旋轉變換+非均勻縮放的複合,這個非均勻不是隨意的,也是按照係數來實現的,例如上初中時候學到的斜二測+上個旋轉,就是仿射變換。單應變換和仿射變換很像,不過二者自由度不同,而且單應變換使用的是齊次座標,可以產生透視的效果。可以認為單應變換包含仿射變換,或者粗暴的理解為仿射變換加上“近大遠小”的效果。
  • 射影變換:它是建立在齊次座標系上的,也就是能產生“近大遠小”的效果。有八個自由度,與單應變換相當接近了
  • 二維單應變換:把相似變換、仿射變換、射影變換疊加在一起,使得單應變換具有前三個變換的性質。

綜上,簡答的來理解單應變換,就是相當於你用一個照相機,正對著一個物體照一張照片。然後你再換個角度,換個距離,換個一個高度對著這個物體再拍一張。那麼,你可以用單應變換找到這兩張圖片對應關係。

上節課我們使用了一個查詢影象找到了它的一些特徵點,我們又使用另外一個訓練影象,同樣找到了一些特徵點,並且找出他們之間的最佳匹配。
簡單的說,我們在另外一組圖片當中,找到了一個目標圖片的部分位置。這個資訊足夠在訓練圖片中精確的找到目標圖片當中的物件了。

為了達到上面的目的,我們使用一個calib3d的模組,其中有cv2.findHomography()函式。如果我們傳入一組兩個影象對應的點集,它會自動找到對應透視變換的目標物件。然後我們可以使用cv2.perspectiveTransform() 函式來找到對應目標。引數至少需要四個正確的對應點來找到這個變換。

我們會看到在匹配的過程中,可能出現一些誤差,這些誤差可能會影響結果。為了解決這個問題,演算法使用了RANSAC(隨機抽樣一致演算法)或者LEAST_MEDIAN(最小中位數平方法?),這兩個方法可以通過設定引數來決定使用哪一個。所以一個優秀的匹配點會提供一個正確的估計結果,這個結果叫做內點(inliers),剩下的點被稱為外點。cv2.findHomography()返回一個蒙版,可以指定內點和外點。

程式碼:

首先使用SIFT演算法找到特徵點,然後使用比值測試找到最佳匹配。

import numpy as np
import cv2 
from matplotlib import pyplot as plt

MIN_MATCH_COUNT = 10

img1 = cv2.imread('1.jpg',0)
img2 = cv2.imread('26.jpg',0)

sift = cv2.xfeatures2d.SIFT_create()

kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

FLANN_INDEX_KDTREE = 0#kd樹
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary

flann = cv2.FlannBasedMatcher(index_params,search_params)

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)

現在我們要設定一個條件,至少要找到十個匹配才能開始尋找目標(設定MIN_MATCH_COUNT引數)。否則顯示資訊沒有足夠的匹配點。

如果找到了足夠的匹配點,我們可以在兩幅影象中,提取匹配到的關鍵點的位置資訊。將他們當做引數傳遞給要尋找對應透視變換的演算法。一旦我們得到的了這個3×3的變換矩陣,我們就可以使用它來變換查詢圖片的角點資訊來對應到訓練圖片的角點資訊。然後把它們畫出來。

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 = 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

最後畫出我們的“內點”(如果成功找到目標)或者匹配點(失敗)
原圖:

這裡寫圖片描述
這裡寫圖片描述

這裡寫圖片描述

import numpy as np
import cv2 
from matplotlib import pyplot as plt


MIN_MATCH_COUNT = 10

img1 = cv2.imread('a.jpg',0)
img2 = cv2.imread('b.jpg',0)

sift = cv2.xfeatures2d.SIFT_create()

kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

FLANN_INDEX_KDTREE = 0#kd樹
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary

flann = cv2.FlannBasedMatcher(index_params,search_params)

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 = 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)

plt.imshow(img3, 'gray'),plt.show()