TODO: 实现图片的全景拼接
流程:
(1)检测左右2图片的SIFT关键特征点,并计算特征描述
(2)使用KNN检测来自左右2图的SIFT特征,进行匹配
(3)计算视角变换矩阵H,用变换矩阵H对右图进行变换,左图加入到变换后的图像获得最终图像
(4)使用调用已经编辑好的函数运行程序进行全景拼接
(1)检测左右2图片的SIFT关键特征点,并计算特征描述
def sift_kp(image):
"""
将读取进行灰度化转化,并输出图像、关键点,计算描述符
:param image:
:return: kp_image, kp, des
"""
# 灰度值转换
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 创建sift对象
sift = cv2.xfeatures2d.SIFT_create()
# 计算特征点
kp, des = sift.detectAndCompute(image, None)
# 绘制特征点
kp_image = cv2.drawKeypoints(gray_image, kp, None)
return kp_image, kp, des
sift = cv2.xfeatures2d.SIFT_create() 在4.0以后的opencv版本可以直接使用SIFT_create()进行sift对象的创建
(2)使用KNN检测来自左右2图的SIFT特征,进行匹配
BF 匹配,Brute-Force Matcher,暴力匹配. 其原理比较简单,首先从集合A中选择一个特征的描述子,然后与集合B中所有的其他特征计算某种相似度,进行匹配,并返回最接近的项.
OpenCV 中,首先使用 cv2.BFMatcher()创建 BFMatcher 实例,其包含两个可选参数:
normType
和crossCheck.
normType
:如 NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2.
NORM_L1 和 NORM_L2 更适用于 SIFT 和 SURF 描述子;
NORM_HAMMING 和 ORB、BRISK、BRIEF 一起使用;
NORM_HAMMING2 用于 WTA_K==3或4 的 ORB 描述子.
crossCheck
:默认为 False,其寻找每个查询描述子的 k 个最近邻.
若值为 True,则 knnMatch() 算法 k=1,仅返回(i, j)的匹配结果,
即集合A中的第 i 个描述子在集合B中的第 j 个描述子是最佳匹配.
也就是说,两个集合中的两个描述子特征是互相匹配的.
其提供连续的结果.
当有足够的匹配项时,其往往能够找到最佳的匹配结果.
def get_good_match(des1, des2):
"""
使用匹配器进行匹配进行匹配返回最佳的匹配值
:param des1:
:param des2:
:return: good
"""
# 使用**cv.BFMatcher**()创建BFMatcher对象
# 它使用第一组中一个特征的描述符,并使用一些距离计算将其与第二组中的所有其他特征匹配。并返回最接近的一个。
bf = cv2.BFMatcher()
# 利用匹配器 匹配两个描述符的相近成都
# (knn 匹配可以返回k个最佳的匹配项 bf返回所有的匹配项)
matches = bf.knnMatch(des1, des2, k=2) # des1为模板图,des2为匹配图
# 按照相近程度,进行排序
matches = sorted(matches, key=lambda x: x[0].distance / x[1].distance)
good = []
for m, n in matches:
print(m.distance, n.distance)
# 对欧式距离进行筛选
if m.distance < 0.75 *n.distance:
good.append(m)
return good
调用
knnMatch
方法进行匹配:match = bf.knnMatch(des1, des2, k)
参数des1,des2是描述子,就是通过SIFT\SURF\ORB等特征提取算法计算出来的描述子;参数k表示取欧式距离最近的前k个关键点,就是计算第一组每个描述子和第二组所有描述子之间的欧式距离,然后取距离最小的前k对儿。当k=1就和match方法的结果一样
knnMatch()函数返回的三个值:
queryIdx:测试图像的特征点描述符的下标(第几个特征点描述符),同时也是描述符对应特征点的下标。
trainIdx:样本图像的特征点描述符下标,同时也是描述符对应特征点的下标。
distance:代表这图像匹配的特征点描述符的欧式距离,数值越小也就说明两个特征点越相近。
使用match检测来自左右2图的SIFT特征,进行匹配
match函数返回的也是三个值:
queryIdx:测试图像的特征点描述符的下标(第几个特征点描述符),同时也是描述符对应特征点的下标。
trainIdx:样本图像的特征点描述符下标,同时也是描述符对应特征点的下标。
distance:代表这图像匹配的特征点描述符的欧式距离,数值越小也就说明两个特征点越相近。
代码部分需要修改如下:
def get_good_match(des1, des2):
bf = cv2.BFMatcher()
matches = bf.match(des1, des2) # des1为模板图,des2为匹配图
good = []
for m in matches:
good.append(m)
return good
(3)计算视角变换矩阵H,用变换矩阵H对右图进行变换,左图加入到变换后的图像获得最终图像
在Python中可以使用findHomography()函数进行透视变换
Homography(透视变换):透视变换就是将图像投影到一个新的视平面。上面提到了一个叫做透视矩阵的东西,其实就是单应性矩阵,用来表示两幅图的对应点的变换关系。在全景图拼接时,很多图像会由于拍摄角度等问题出现一些方向上的不同步
def siftimg_rightleftment(img_right, img_left):
"""
用变换矩阵H对右图进行变换,左图加入到变换后
:param img_right:
:param img_left:
:return:result
"""
_, kp1, des1 = sift_kp(img_right)
_, kp2, des2 = sift_kp(img_left)
goodMatch = get_good_match(des1, des2)
if len(goodMatch) > 4:
# 获取匹配对的点坐标
ptsA = np.float32([kp1[m.queryIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
ptsB = np.float32([kp2[m.trainIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
ransacReprojThreshold = 4
H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, ransacReprojThreshold)
# ![将图片右进行视角变换](https://img-blog.csdnimg.cn/511e139a7bfe4f0494cbd7a9adbb9466.png)
,result是变换后图片
result = cv2.warpPerspective(img_right, H, (img_right.shape[1] + img_left.shape[1], img_right.shape[0]))
# 展示图片
cvshow('result_medium', result)
result[0:img_left.shape[0], 0:img_left.shape[1]] = img_left
cvshow('result',result)
4、使用调用已经编辑好的函数运行程序进行全景拼接
# 特征匹配+全景拼接
import numpy as np
import cv2
# 读取拼接图片(注意图片左右的放置)
# 是对右边的图形做变换
img_right = cv2.imread(r'pin4.png')
img_left = cv2.imread(r'pin3.png')
img_right = cv2.resize(img_right,(800,400))
# 保证两张图一样大
img_left = cv2.resize(img_left, (img_right.shape[1], img_right.shape[0]))
kpimg_right, kp1, des1 = sift_kp(img_right)
kpimg_left, kp2, des2 = sift_kp(img_left)
# 同时显示原图和关键点检测后的图
cvshow('img_left', np.hstack((img_left, kpimg_left)))
cvshow('img_right', np.hstack((img_right, kpimg_right)))
goodMatch = get_good_match(des1, des2)
all_goodmatch_img = cv2.drawMatches(img_right, kp1, img_left, kp2, goodMatch, None, flags=2)
# goodmatch_img自己设置前多少个goodMatch[:10]
goodmatch_img = cv2.drawMatches(img_right, kp1, img_left, kp2, goodMatch[:10], None, flags=2)
cvshow('Keypoint Matches1', all_goodmatch_img)
cvshow('Keypoint Matches2', goodmatch_img)
# 把图片拼接成全景图
result = siftimg_rightlignment(img_right, img_left)
cvshow('result', result)
match方法检测:
将图片右进行视角变换
全景拼接
knnmatch检测:
视角转换
图像拼接