import numpy as np
import cv2 as cv
import imutils
class Stitcher:
def init(self):
self.isv3 = imutils.is_cv3()
def stitch(self,imgs, ratio = 0.75, reprojThresh = 4.0, showMatches = False):
print('A')
(img2, img1) = imgs
擷取關鍵點和描述符
(kp1, des1) = self.detectAndDescribe(img1)
(kp2, des2) = self.detectAndDescribe(img2)
print(len(kp1),len(des1))
print(len(kp2), len(des2))
R = self.matchKeyPoints(kp1, kp2, des1, des2, ratio, reprojThresh)
如果沒有足夠的最佳比對點,M為None
if R is None:
return None
(good, M, mask) = R
print(M)
對img1透視變換,M是ROI區域矩陣, 變換後的大小是(img1.w+img2.w, img1.h)
result = cv.warpPerspective(img1, M, (img1.shape[1] + img2.shape[1], img1.shape[0]))
将img2的值賦給結果圖像
result[0:img2.shape[0], 0:img2.shape[1]] = img2
是否需要顯示ROI區域
if showMatches:
vis = self.drawMatches1(img1, img2, kp1, kp2, good, mask)
return (result, vis)
return result
def detectAndDescribe(self,img):
print('B')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
檢查我們使用的是否是penCV3.x
if self.isv3:
sift = cv.xfeatures2d.SIFT_create()
(kps, des) = sift.detectAndCompute(img, None)
else:
sift = cv.FastFeatureDetector_create('SIFT')
kps = sift.detect(gray)
des = sift.compute(gray, kps)
kps = np.float32([kp.pt for kp in kps]) # **********************************
傳回關鍵點和描述符
return (kps, des)
def matchKeyPoints(self,kp1, kp2, des1, des2, ratio, reprojThresh):
print('C')
初始化BF,因為使用的是SIFT ,是以使用預設參數
matcher = cv.DescriptorMatcher_create('BruteForce')
bf = cv.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
matches = matcher.knnMatch(des1, des2, 2) #***********************************
擷取理想比對
good = []
for m in matches:
if len(m) == 2 and m[0].distance < ratio * m[1].distance:
good.append((m[0].trainIdx, m[0].queryIdx))
print(len(good))
最少要有四個點才能做透視變換
if len(good) > 4:
擷取關鍵點的坐标
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)
src_pts = np.float32([kp1[i] for (_, i) in good])
dst_pts = np.float32([kp2[i] for (i, _) in good])
通過兩個圖像的關鍵點計算變換矩陣
(M, mask) = cv.findHomography(src_pts, dst_pts, cv.RANSAC, reprojThresh)
傳回最佳比對點、變換矩陣和掩模
return (good, M, mask)
如果不滿足最少四個 就傳回None
return None
def drawMatches(img1, img2, kp1, kp2, matches, mask, M):
獲得原圖像的高和寬
h, w = img1.shape[:2]
使用得到的變換矩陣對原圖像的四個角進行變換,獲得目标圖像上對應的坐标
pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)
dst = cv.perspectiveTransform(pts, M)
matchesMask = mask.ravel().tolist()
draw_params = dict(matchColor = (0, 255, 0),
singlePointColor = None,
matchesMask = matchesMask,
flags = 2)
img = cv.drawMatches(img1, kp1, img2, kp2, matches, None, **draw_params)
return img
def drawMatches1(self,img1, img2, kp1, kp2, metches,mask):
print('D')
(hA,wA) = img1.shape[:2]
(hB,wB) = img2.shape[:2]
vis = np.zeros((max(hA,hB), wA+wB, 3), dtype='uint8')
vis[0:hA, 0:wA] = img1
vis[0:hB, wA:] = img2
for ((trainIdx, queryIdx),s) in zip(metches, mask):
if s == 1:
ptA = (int(kp1[queryIdx][0]), int(kp1[queryIdx][1]))
ptB = (int(kp2[trainIdx][0])+wA, int(kp2[trainIdx][1]))
cv.line(vis, ptA, ptB, (0, 255, 0), 1)
return vis
def show():
img1 = cv.imread('image/sedona_left_01.png')
img2 = cv.imread('image/sedona_right_01.png')
img1 = imutils.resize(img1, width=400)
img2 = imutils.resize(img2, width=400)
stitcher = cv.Stitcher()
(result, vis) = stitcher.stitch([img1, img2])
# (result, vis) = stitch([img1,img2], showMatches=True)
cv.imshow('image A', img1)
cv.imshow('image B', img2)
cv.imshow('keyPoint Matches', vis)
cv.imshow('Result', result)
cv.waitKey(0)
cv.destroyAllWindows()
show()