图像全景拼接

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 实例,其包含两个可选参数:normTypecrossCheck.

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方法检测:


将图片右进行视角变换
图像全景拼接_第1张图片
全景拼接
图像全景拼接_第2张图片
knnmatch检测:


视角转换
图像全景拼接_第3张图片
图像拼接
图像全景拼接_第4张图片

你可能感兴趣的:(opencv)