1. sift.detectAndComputer(gray, None) # 計算出圖像的關鍵點和sift特征向量
參數說明:gray表示輸入的圖片
2.cv2.findHomography(kpA, kpB, cv2.RANSAC, reproThresh) # 計算出單應性矩陣
參數說明:kpA表示圖像A關鍵點的坐標, kpB圖像B關鍵點的坐標, 使用隨機抽樣一致性算法來進行迭代,reproThresh表示每次抽取樣本的個數
3.cv2.warpPespective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0])) # 獲得根據單應性矩陣變化后的圖像
參數說明:image表示輸入圖像,H表示單應性的矩陣,(imageA.shape[1] + imageB.shape[1], imageA.shape[0])表示矩陣變化后的維度
4. cv2.line(imageA, kpsA, imageB, kpsB, (0,0,255), 2) 進行畫出直線的操作
參數說明:imageA和imageB表示輸入圖片, kpsA和kpsB表示關鍵點的坐標(x, y) ,(0, 0, 255)表示顏色, 2表示直線的寬度
RANSAC算法(隨機抽樣一致性算法), 對於左邊的圖,可以看到使用最小二乘法盡可能多的滿足點可以分布在擬合曲線周圍,減小均分根誤差,因此擬合的曲線在一定程度上容易發生偏離,而RANSAC卻不會出現這種情況
RANSCA原理, 因為擬合一條直線只需要兩個點,因此我們每次隨機選取兩個點,做出直線,划定一個距離,判斷落在直線周圍距離范圍點的個數,不斷的迭代,直到找出擬合的直線,使得點落在上面最多的擬合曲線
圖像拼接的關鍵是在於對圖像進行變化,變化后的點與需要拼接的圖片中的sift點,越接近,即歐式距離越短,對圖像拼接的過程中,至少需要有4對特征點,求取變化矩陣Hi
我們使用RANSAC不斷去取隨機從兩個圖像中取4對的sift特征點,計算出H,定義損失值,即x’,與x的距離,即y‘與y的距離之和是否是最小值,不斷迭代,找出最佳的H
上述就是計算出來了H值,也就是變化矩陣
代碼思路:
第一步:對圖像進行灰度化,使用sift.detectAndCompute(image, None) 進行ksp關鍵點,dpSIFT特征向量,將kps進行向量化操作,即kps.pt
第二步:構建BMFmatch匹配器,獲得符合條件的匹配值,matches獲得的是ksp關鍵點的匹配值得索引,使用索引獲得符合條件的kspA和kspB
第三步:使用cv2.findHomography(kpA, kpB, cv2.RANSAC,reproThresh) 隨機抽取4個點,求得最合適的H變化矩陣
第四步:使用獲得的變化矩陣H, cv.warpPerspective 對imageA求取變化后的圖像
第五步:將imageB加入到變化后的圖像獲得最終圖像
第六步:如果需要進行展示,構造新的圖像,尺寸為imageA.shape[0], imageB.shape[1] +imageA.shape[1], 使用matches的索引,使用cv2.line將符合條件的點進行連接
第七步:返回最終的結果,進行畫圖展示
import cv2 import numpy as np import matplotlib.pyplot as plt class Stitcher: def stitch(self, imgs, ratio=0.75, reproThresh=4, showMathes = False): (imageB, imageA) = imgs # 第一步:計算kpsA和dpsA (kpsA, dpsA) = self.detectandcompute(imageA) (kpsB, dpsB) = self.detectandcompute(imageB) # 獲得變化的矩陣H M = self.matchKeypoint(kpsA, dpsA, kpsB, dpsB, ratio, reproThresh) if M is None: return None (matches, H, status) = M # 第四步:使用cv2.warpPerspective獲得經過H變化后的圖像 result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageB.shape[0])) # 第五步:將圖像B填充到進過H變化后的圖像,獲得最終的圖像 result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB if showMathes: # 第六步:對圖像的關鍵點進行連接 via = self.showMatches(imageA, imageB, kpsA, kpsB, matches, status) return (via, result) return result # 進行畫圖操作 def showMatches(self, imageA, imageB, kpsA, kpsB, matches, status): # 將兩個圖像進行拼接 # 根據圖像的大小,構造全零矩陣 via = np.zeros((max(imageB.shape[0], imageA.shape[0]), imageA.shape[1] + imageB.shape[1], 3), np.uint8) # 將圖像A和圖像B放到全部都是零的圖像中 via[0:imageA.shape[0], 0:imageA.shape[1]] = imageA via[0:imageB.shape[0], imageA.shape[1]:] = imageB # 根據matches中的索引,構造出點的位置信息 for (trainIdx, queryIdx), s in zip(matches, status): if s==1: ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1])) ptB = (int(kpsB[trainIdx][0] + imageA.shape[1]), int(kpsB[trainIdx][1])) # 使用cv2.line進行畫圖操作 cv2.line(via, ptA, ptB, (0, 255, 0), 1) return via def matchKeypoint(self, kpsA, dpsA, kpsB, dpsB, ratio, reproThresh): # 第二步:實例化BFM匹配, 找出符合添加的關鍵點的索引 bf = cv2.BFMatcher() matcher = bf.knnMatch(dpsA, dpsB, 2) matches = [] for match in matcher: if len(match) == 2 and match[0].distance < match[1].distance * ratio: # 加入match[0]的索引 matches.append((match[0].trainIdx, match[0].queryIdx)) #第三步:使用cv2.findHomography找出符合添加的H矩陣 if len(matches) > 4: # 根據索引找出符合條件的位置 kpsA = np.float32([kpsA[i] for (_, i) in matches]) kpsB = np.float32([kpsB[i] for (i, _) in matches]) (H, status) = cv2.findHomography(kpsA, kpsB, cv2.RANSAC, reproThresh) return (matches, H, status) return None def cv_show(self, img, name): cv2.imshow(name, img) def detectandcompute(self, image): # 進行灰度值轉化 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #實例化sift函數 sift = cv2.xfeatures2d.SIFT_create() # 獲得kps關鍵點和dps特征向量sift kps, dps = sift.detectAndCompute(gray, None) # 獲得特征點的位置信息, 並轉換數據類型 kps = np.float32([kp.pt for kp in kps]) return (kps, dps)
使用cv2.warpPesctive即根據H變化后的圖片 經過圖像拼接后的result圖片 經過圖片關鍵點連接的圖片