基于opencv的影像拼接
問題
自動控制創新實踐要求合并多機器人的slam地圖,既合并有重疊部分的兩張或多張圖片, 比如說下面兩張圖片

1. 特征檢測
使用SIFT演算法進行特征檢測,得到特征圖

2.對特征進行K近鄰演算法進行匹配
得到匹配結果

3. 計算其中一張圖的透視針對另一張圖的透視變換
- 由于無法提前知道兩張圖片的位置關系,對于透視變換,可能圖片會映射到整個選取區域的左邊,這樣的話,無法正常顯示圖片,因此,要對透視變換后的圖片進行面積檢查,如果比原來的圖片面積小太多,就用另一張圖片來進行透視變換
"""
計算兩張圖的透視關系
"""
matchCount = len(matches)
M = getHomography(kpsA, kpsB, matches, reprojThresh=4)
if M is None:
print("Error!")
(matches, H, status) = M
"""
將圖片A進行透視變換并檢查圖片位置
"""
result = cv2.warpPerspective(imageA, H, ((imageA.shape[1] + imageB.shape[1])*2, (imageA.shape[0] + imageB.shape[0]) * 2))
resultAfterCut = cutBlack(result)
# 檢查圖片位置
if np.size(resultAfterCut) < np.size(imageA) * 0.95:
print("圖片位置不對,將自動調換")
# 調換圖片
kpsA,kpsB = swap(kpsA, kpsB)
imageA, imageB = swap(imageA, imageB)
if feature_matching == 'bf':
matches = matchKeyPointsBF(featuresB, featuresA, method=feature_extractor)
elif feature_matching == 'knn':
matches = matchKeyPointsKNN(featuresB, featuresA, ratio=0.75, method=feature_extractor)
if len(matches) < 10:
return None
matchCount = len(matches)
M = getHomography(kpsA, kpsB, matches, reprojThresh=4)
if M is None:
print("Error!")
(matches, H, status) = M
4. 合并圖片,大功告成
得到結果圖,合并效果很完美

試試其他圖片
- 圖片1

融合效果很棒 - 圖片二
這張圖透視就有點過
如何進行多張圖片的合并
很簡單,反復呼叫合并兩張圖片就行,但是有些細節問題,無法提前得知多張圖片的位置,先合并哪個?
既然我們有特征匹配環節,優先合并匹配特征數多的就行,
多張圖片的效果圖
第一個迷宮圖,在百度圖片隨便找的

第二個slam地圖,用的是fr079資料集

效果還行,
完整代碼
- 代碼已同步到github:https://github.com/799034552/concat_pic
注釋很詳細,不用怕看不懂
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
from numpy.core.defchararray import array
# ================================================================== #
# 選擇特征提取器函式
# ================================================================== #
def detectAndDescribe(image, method=None):
assert method is not None, "You need to define a feature detection method. Values are: 'sift', 'surf'"
if method == 'sift':
descriptor = cv2.xfeatures2d.SIFT_create()
elif method == 'surf':
descriptor = cv2.xfeatures2d.SURF_create()
elif method == 'brisk':
descriptor = cv2.BRISK_create()
elif method == 'orb':
descriptor = cv2.ORB_create()
(kps, features) = descriptor.detectAndCompute(image, None)
return (kps, features)
# ================================================================== #
# 暴力檢測函式
# ================================================================== #
def matchKeyPointsBF(featuresA, featuresB, method):
bf = createMatcher(method, crossCheck=True)
best_matches = bf.match(featuresA,featuresB)
rawMatches = sorted(best_matches, key = lambda x:x.distance)
print("Raw matches (Brute force):", len(rawMatches))
return rawMatches
# ================================================================== #
# 使用knn檢測函式
# ================================================================== #
def matchKeyPointsKNN(featuresA, featuresB, ratio, method):
bf = createMatcher(method, crossCheck=False)
rawMatches = bf.knnMatch(featuresA, featuresB, 2)
# print("Raw matches (knn):", len(rawMatches))
matches = []
for m,n in rawMatches:
if m.distance < n.distance * ratio:
matches.append(m)
# print(f"knn匹配的特征點數量:{len(matches)}")
return matches
# ================================================================== #
#
# ================================================================== #
def createMatcher(method,crossCheck):
"Create and return a Matcher Object"
if method == 'sift' or method == 'surf':
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=crossCheck)
elif method == 'orb' or method == 'brisk':
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=crossCheck)
return bf
# ================================================================== #
# 計算關鍵點的透視關系
# ================================================================== #
def getHomography(kpsA, kpsB, matches, reprojThresh):
# convert the keypoints to numpy arrays
kpsA = np.float32([kp.pt for kp in kpsA])
kpsB = np.float32([kp.pt for kp in kpsB])
if len(matches) > 4:
# construct the two sets of points
ptsA = np.float32([kpsA[m.queryIdx] for m in matches])
ptsB = np.float32([kpsB[m.trainIdx] for m in matches])
# estimate the homography between the sets of points
(H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,
reprojThresh)
return (matches, H, status)
else:
return None
# ================================================================== #
# 去除影像黑邊
# ================================================================== #
def cutBlack(pic):
rows, cols = np.where(pic[:,:,0] !=0)
min_row, max_row = min(rows), max(rows) +1
min_col, max_col = min(cols), max(cols) +1
return pic[min_row:max_row,min_col:max_col,:]
# ================================================================== #
# 調換
# ================================================================== #
def swap(a, b):
return b,a
# ================================================================== #
# 主要的函式
# 合并兩張圖(合并多張圖基于此函式)
# ================================================================== #
def handle(path1, path2, isShow = False):
"""
定義超引數
"""
feature_extractor = 'sift'
feature_matching = 'knn'
"""
讀取原始影像
"""
if isinstance(path2,str):
imageA = cv2.imread(path2)
imageA = cv2.cvtColor(imageA,cv2.COLOR_BGR2RGB)
else:
imageA = path2
imageA_gray = cv2.cvtColor(imageA, cv2.COLOR_RGB2GRAY)
if isinstance(path1,str):
imageB = cv2.imread(path1)
imageB = cv2.cvtColor(imageB,cv2.COLOR_BGR2RGB)
else:
imageB = path1
t = np.size(imageB)
imageB_gray = cv2.cvtColor(imageB, cv2.COLOR_RGB2GRAY)
"""
顯示輸入的兩張圖片
"""
if isShow:
f = plt.figure(figsize=(10,4))
f.add_subplot(1,2,1)
plt.title("imageB")
plt.imshow(imageB)
plt.xticks([]),plt.yticks([])
f.add_subplot(1,2,2)
plt.title("imageA")
plt.imshow(imageA)
plt.xticks([]),plt.yticks([])
"""
提取兩證圖片的特征
"""
kpsA, featuresA = detectAndDescribe(imageA_gray, method=feature_extractor)
kpsB, featuresB = detectAndDescribe(imageB_gray, method=feature_extractor)
"""
顯示關鍵點
"""
if isShow:
fig, (ax1,ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10,4), constrained_layout=False)
ax1.imshow(cv2.drawKeypoints(imageA_gray,kpsA,None,color=(0,255,0)))
ax1.set_xlabel("(a)key point", fontsize=14)
ax2.imshow(cv2.drawKeypoints(imageB_gray,kpsB,None,color=(0,255,0)))
ax2.set_xlabel("(b)key point", fontsize=14)
"""
進行特征匹配
"""
if feature_matching == 'bf':
matches = matchKeyPointsBF(featuresA, featuresB, method=feature_extractor)
img3 = cv2.drawMatches(imageA,kpsA,imageB,kpsB,matches[:100],
None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
elif feature_matching == 'knn':
matches = matchKeyPointsKNN(featuresA, featuresB, ratio=0.75, method=feature_extractor)
if len(matches) < 10:
return None
img3 = cv2.drawMatches(imageA,kpsA,imageB,kpsB,np.random.choice(matches,100),
None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
"""
匹配的特征展示
"""
if isShow:
fig = plt.figure(figsize=(10,4))
plt.imshow(img3)
plt.title("feature match")
plt.axis('off')
"""
計算兩張圖的透視關系
"""
matchCount = len(matches)
M = getHomography(kpsA, kpsB, matches, reprojThresh=4)
if M is None:
print("Error!")
(matches, H, status) = M
"""
將圖片A進行透視變換并檢查圖片位置
"""
result = cv2.warpPerspective(imageA, H, ((imageA.shape[1] + imageB.shape[1])*2, (imageA.shape[0] + imageB.shape[0]) * 2))
resultAfterCut = cutBlack(result)
# 檢查圖片位置
if np.size(resultAfterCut) < np.size(imageA) * 0.95:
print("圖片位置不對,將自動調換")
# 調換圖片
kpsA,kpsB = swap(kpsA, kpsB)
imageA, imageB = swap(imageA, imageB)
if feature_matching == 'bf':
matches = matchKeyPointsBF(featuresB, featuresA, method=feature_extractor)
elif feature_matching == 'knn':
matches = matchKeyPointsKNN(featuresB, featuresA, ratio=0.75, method=feature_extractor)
if len(matches) < 10:
return None
matchCount = len(matches)
M = getHomography(kpsA, kpsB, matches, reprojThresh=4)
if M is None:
print("Error!")
(matches, H, status) = M
result = cv2.warpPerspective(imageA, H, ((imageA.shape[1] + imageB.shape[1])*2, (imageA.shape[0] + imageB.shape[0]) * 2))
"""
合并圖片
"""
# cv2.imshow("Perspective transformation", result)
# cv2.waitKey(0)
result[0:imageB.shape[0], 0:imageB.shape[1]] = np.maximum(imageB, result[0:imageB.shape[0], 0:imageB.shape[1]])
result = cutBlack(result)
return result, matchCount
# ================================================================== #
# 合并多張圖
# ================================================================== #
def handleMulti(*args, isShow = False):
print(isShow)
l = len(args)
if isShow:
row = math.ceil(l / 3)
f = plt.figure(figsize=(10, 4))
for i in range(l):
f.add_subplot(row, 3, i + 1)
plt.title(f"image({i+1})")
plt.axis("off")
plt.imshow(cv2.cvtColor(cv2.imread(args[i]), cv2.COLOR_BGR2RGB))
assert(l > 1)
isHandle = [0 for i in range(l - 1)]
nowPic = args[0]
args = args[1:]
for j in range(l - 1):
isHas = False # 在一輪中是否找到
matchCountList = []
resultList = []
indexList = []
for i in range(l - 1):
if (isHandle[i] == 1):
continue
result, matchCount = handle(nowPic, args[i])
if not result is None:
matchCountList.append(matchCount)
resultList.append(result)
indexList.append(i)
isHas = True
if not isHas: # 一輪找完都沒有可以合并的
return None
else:
index = matchCountList.index(max(matchCountList))
nowPic = resultList[index]
isHandle[indexList[index]] = 1
print(f"合并第{indexList[index] + 2}個")
# cv2.imshow("temp", nowPic)
# cv2.waitKey(0)
return nowPic
# ================================================================== #
# 主函式
# ================================================================== #
if __name__ == "__main__":
"""
處理兩張圖,可以列印特征點與對應關系
"""
result, _ = handle("./input/222.png", "./input/111.png", isShow=True)
if not result is None:
cv2.imshow("result", result[:, :, [2, 1, 0]])
plt.show()
cv2.waitKey(0)
else:
print("沒有找到對應特征點,無法合并")
exit()
"""
處理多張圖,不可以列印特征點與對應關系
"""
# result = handleMulti(
# "./input/migong (1).png",
# "./input/migong (2).png",
# "./input/migong (3).png",
# isShow=True)
# result = handleMulti("./input/111.png", "./input/222.png","./input/333.png", isShow=True)
# result = handleMulti("./input/foto7A.jpg", "./input/foto7B.jpg") #合并的不好的圖
result = handleMulti("./input/intel_lab (1).png",
"./input/intel_lab (2).png",
"./input/intel_lab (3).png",
"./input/intel_lab (4).png",
"./input/intel_lab (5).png",
"./input/intel_lab (6).png",
isShow=True)
if not result is None:
cv2.imshow("result", result[:, :, [2, 1, 0]])
plt.show()
cv2.waitKey(0)
else:
print("沒有找到對應特征點,無法合并")
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/348520.html
標籤:其他
上一篇:Jetson (Nano/NX/TX2)Jetpack4.*Cuda10.2 CuDNN8.0 Opencv4.安裝caffe
