一.简介
图像拼接(Image Stitching)是一种利用实景图像组成全景空间的技术,它将多幅图像拼接成一幅大尺度图像或360度全景图,接可以看做是场景重建的一种特殊情况,其中图像仅通过平面单应性进行关联。图像拼接在运动检测和跟踪,增强现实,分辨率增强,视频压缩和图像稳定等机器视觉领域有很大的应用。图像拼接的输出是两个输入图像的并集。通常用到四个步骤:(1)特征提取(Feature Extraction):检测输入图像中的特征点。(2)图像配准(Image Registration):建立了图像之间的几何对应关系,使它们可以在一个共同的参照系中进行变换、比较和分析。(3)图像变形(Warping):图像变形是指将其中一幅图像的图像重投影,并将图像放置在更大的画布上。(4)图像融合(Blending):图像融合是通过改变边界附近的图像灰度级,去除这些缝隙,创建混合图像,从而在图像之间实现平滑过渡。混合模式(Blend modes)用于将两层融合到一起。
二.实现方法
(1)用SIFT提取图像中的特征点,并对每个关键点周围的区域计算特征向量。可以使用比SIFT快的SURF方法,但是我的opencv版本为最新版,不知道是专利的原因还是什么原因用SURF = cv2.xfeatures2D.SURF_create ()实例化的时候会报错,网上说可以退opencv版本,但是我这里没有尝试,就用了sift = cv2.SIFT_create()。
(2)在分别提取好了两张图片的关键点和特征向量以后,可以利用它们进行两张图片的匹配。在拼接图片中,可以使用Knn进行匹配,但是使用FLANN快速匹配库更快,图片拼接,需要用到FLANN的单应性匹配。
(3)单应性匹配完之后可以获得透视变换H矩阵,用这个的逆矩阵来对第二幅图片进行透视变换,将其转到和第一张图一样的视角,为下一步拼接做准备。
(4)透视变化完后就可以直接拼接图片了,将图片通过numpy直接加到透视变化完成的图像的左边,覆盖掉重合的部分,得到拼接图片,但是这样拼接得图片中间会有一条很明显的缝隙,可以通过加权平均法,界线的两侧各取一定的比例来融合缝隙,速度快,但不自然。或者羽化法,或者拉普拉斯金字塔融合,效果最好。在这里用的是加权平均法,可以把第一张图叠在左边,但是对第一张图和它的重叠区做一些加权处理,重叠部分,离左边图近的,左边图的权重就高一些,离右边近的,右边旋转图的权重就高一些,然后两者相加,使得过渡是平滑地,这样看上去效果好一些,速度就比较慢。
三.实验图片
四.实验
4.1 直接拼接
代码如下:
#导入库
import cv2
import numpy as np
import sys
from PIL import Image
#图像显示函数
def show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#读取输入图片
ima = cv2.imread("p2.jpg")
imb = cv2.imread("p1.jpg")
A = ima.copy()
B = imb.copy()
imageA = cv2.resize(A,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(B,(0,0),fx=0.2,fy=0.2)
#检测A、B图片的SIFT关键特征点,并计算特征描述子
def detectAndDescribe(image):
# 建立SIFT生成器
sift = cv2.SIFT_create()
# 检测SIFT特征点,并计算描述子
(kps, features) = sift.detectAndCompute(image, None)
# 将结果转换成NumPy数组
kps = np.float32([kp.pt for kp in kps])
# 返回特征点集,及对应的描述特征
return (kps, features)
#检测A、B图片的SIFT关键特征点,并计算特征描述子
kpsA, featuresA = detectAndDescribe(imageA)
kpsB, featuresB = detectAndDescribe(imageB)
# 建立暴力匹配器
bf = cv2.BFMatcher()
# 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
matches = bf.knnMatch(featuresA, featuresB, 2)
good = []
for m in matches:
# 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对
if len(m) == 2 and m[0].distance < m[1].distance * 0.75:
# 存储两个点在featuresA, featuresB中的索引值
good.append((m[0].trainIdx, m[0].queryIdx))
# 当筛选后的匹配对大于4时,计算视角变换矩阵
if len(good) > 4:
# 获取匹配对的点坐标
ptsA = np.float32([kpsA[i] for (_, i) in good])
ptsB = np.float32([kpsB[i] for (i, _) in good])
# 计算视角变换矩阵
H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,4.0)
# 匹配两张图片的所有特征点,返回匹配结果
M = (matches, H, status)
# 如果返回结果为空,没有匹配成功的特征点,退出程序
if M is None:
print("无匹配结果")
sys.exit()
# 否则,提取匹配结果
# H是3x3视角变换矩阵
(matches, H, status) = M
# 将图片A进行视角变换,result是变换后图片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
# 将图片B传入result图片最左端
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
show('res',result)
print(result.shape)
直接凭借得到的结果如下:
我们可以看到在图像拼接出有明显的缝隙:
4.2 进行Multi-band Blending处理缝隙
代码如下:
import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
def show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
MIN = 10
FLANN_INDEX_KDTREE = 0
starttime = time.time()
img1 = cv2.imread(r'D:\software\pycharm\PycharmProjects\computer-version\data\p1.jpg') #query
img2 = cv2.imread(r'D:\software\pycharm\PycharmProjects\computer-version\data\p2.jpg') #train
imageA = cv2.resize(img1,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(img2,(0,0),fx=0.2,fy=0.2)
surf=cv2.xfeatures2d.SIFT_create()#可以改为SIFT
sift = cv2.SIFT_create()
kp1,descrip1 = sift.detectAndCompute(imageA,None)
kp2,descrip2 = sift.detectAndCompute(imageB,None)
#创建字典
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searchParams = dict(checks=50)
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
good=[]
#过滤特征点
for i,(m,n) in enumerate(match):
if(m.distance<0.75*n.distance):
good.append(m)
# 当筛选后的匹配对大于10时,计算视角变换矩阵
if len(good) > MIN:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
M,mask = cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
warpImg = cv2.warpPerspective(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))
direct=warpImg.copy()
direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageA
simple=time.time()
show('res',warpImg)
rows,cols=imageA.shape[:2]
print(rows)
print(cols)
for col in range(0,cols):
# 开始重叠的最左端
if imageA[:, col].any() and warpImg[:, col].any():
left = col
print(left)
break
for col in range(cols-1, 0, -1):
#重叠的最右一列
if imageA[:, col].any() and warpImg[:, col].any():
right = col
print(right)
break
# Multi-band Blending算法
levels = 6
gaussian = cv2.getGaussianKernel(5, 0)
gaussian_pyramid_imageA = [imageA]
gaussian_pyramid_imageB = [warpImg]
laplacian_pyramid_imageA = [imageA]
laplacian_pyramid_imageB = [warpImg]
for i in range(levels):
gaussian_imageA = cv2.pyrDown(gaussian_pyramid_imageA[i])
gaussian_imageB = cv2.pyrDown(gaussian_pyramid_imageB[i])
gaussian_pyramid_imageA.append(gaussian_imageA)
gaussian_pyramid_imageB.append(gaussian_imageB)
for i in range(levels, 0, -1):
laplacian_imageA = cv2.subtract(gaussian_pyramid_imageA[i-1], cv2.pyrUp(gaussian_pyramid_imageA[i], dstsize=gaussian_pyramid_imageA[i-1].shape[:2]))
laplacian_imageB = cv2.subtract(gaussian_pyramid_imageB[i-1], cv2.pyrUp(gaussian_pyramid_imageB[i], dstsize=gaussian_pyramid_imageB[i-1].shape[:2]))
laplacian_pyramid_imageA.append(laplacian_imageA)
laplacian_pyramid_imageB.append(laplacian_imageB)
gaussian_pyramid_mask = [np.ones((imageA.shape[0]//(2**levels), imageA.shape[1]//(2**levels)), np.float32)]
for i in range(levels):
gaussian_mask = cv2.pyrDown(gaussian_pyramid_mask[i])
gaussian_pyramid_mask.append(gaussian_mask)
laplacian_pyramid = []
n = 0
for laplacian_imageA, laplacian_imageB, gaussian_mask in zip(laplacian_pyramid_imageA, laplacian_pyramid_imageB, gaussian_pyramid_mask[::-1]):
rows, cols, dpt = laplacian_imageA.shape
n += 1
laplacian = np.zeros((rows, cols, dpt), np.float32)
for row in range(rows):
for col in range(cols):
if gaussian_mask[row, col] == 1:
laplacian[row, col] = laplacian_imageA[row, col]
else:
laplacian[row, col] = laplacian_imageB[row, col]
laplacian_pyramid.append(laplacian)
#重建图像
image_reconstruct = laplacian_pyramid[0]
for i in range(1, levels):
image_reconstruct = cv2.pyrUp(image_reconstruct, dstsize=laplacian_pyramid[i].shape[:2])
image_reconstruct = cv2.add(image_reconstruct, laplacian_pyramid[i])
for row in range(0, imageA.shape[0]):
for col in range(0, left):
if image_reconstruct[row, col].all() == 0:
image_reconstruct[row, col] = imageA[row, col]
cv2.imshow('result', image_reconstruct)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果如下:
文章来源:https://www.toymoban.com/news/detail-697613.html
分析:从图中可以看出使用Multi-band Blending进行图像平滑后,拼接处的缝隙得到了改善。但是不知道什么原因,左边图像是黑色的。查阅资料发现:如果图片旁出现了黑色部分,是因为图片无法填充满,可以试试拍摄时角度变化大一些。可以尝试通过blending进行融合来达到更好的效果。但是经过试验也没有解决。文章来源地址https://www.toymoban.com/news/detail-697613.html
到了这里,关于计算机视觉----图像拼接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!