项目实战:用python的opencv库实现全景图像拼接


前言

opencv库是python中重要的图像处理库,也被称为计算机视觉开发库,这篇文章我们用利用opencv库来实现全景图像的拼接,总体上来说包含sift匹配和ransac误匹配剔除两大模块,那么话不多说,我们开始今天的正题。

一、程序主要架构

为了大家开始就有一个清晰的认知,首先介绍一下整个程序的架构,整个程序总体来说包含两个py文件,主文件作用是实现两张图像的匹配并且拼接起来,副文件作用是将图像文件读取进来,然后直接调用主文件中的函数,最后将各个步骤的结果展示出来,下面的图可以很好的表示出来。在这里插入图片描述


二、程序实现原理

1.sift特征点匹配算法

sift特征点匹配是图像匹配发展过程中的一座里程碑,在这个方法被提出之后,图像匹配的正确性与准确性都得到了显著的提高,时隔多年,在这个人工智能与深度学习炙手可热的时代,这个经典的方法仍然是主流方法之一。与其他匹配方法一样,从总体上该方法包含特征点检测特征点描述特征点匹配三大步骤。
1.特征点检测
在这里插入图片描述
2.特征点描述

在这里插入图片描述
3.特征点匹配
在这里插入图片描述

2.ransac误匹配剔除

RANASC算法全称为随机采样一致性方法,在sift特征匹配后,在正确匹配占据多数的情况下,使用此算法进行误匹配的剔除,从而计算sift匹配的正确率。
具体步骤为:

Step1:N对特征点钟随机取3个点,计算仿射矩阵H

Step2:验证其余的N-3对点是否符合H变换,如果符合,记为内点,否则记为外点

Step3:返回STEP1,不断迭代

Step4:达到迭代次数,结束迭代,比较Nk(Nk为第k次迭代内点数目)

Step5:选择Nk最大时对应的Hk,去掉此次迭代的外点,视其为错误点

三.代码实现过程

首先来介绍一下代码中用到的主要函数,其实这些函数uu们对计算机视觉有一定的了解的话已经是常客了,不了解的uu不用着急,我来一一介绍。

函数名作用需要参数返回值
imread读取图像图像存储的路径彩色图像为三维矩阵,灰白图像为一维矩阵
cvtcolor将图像灰度化彩色图像矩阵灰白图像一维矩阵,值在0-255之间
SIFT_create建立sift实例化对象各个初始参数,例如边缘检测阈值edgeThreshold,高斯金字塔初始尺度值noctavelayerssift对象
dedectAndcompute探测图像特征向量图像矩阵第一个参数kp是特征点的位置坐标,第二个参数des是对应的特征向量,是一个32*1维的向量
drawkeypoints将特征点的位置画出一个小圆圈参数1:指定绘制在那副图像上,参数2:特征点的坐标矩阵kp,参数3:特征点绘制的画布图像,可选参数圆圈大小和圆圈颜色无返回值
drawMatches将匹配后的同名点连线原始图像1,图像1特征点,原始图像2,图像2特征点,线的眼色(默认为随机彩色)无返回值

接着介绍一下重要的实现过程。
第一就是在得到两幅图像的特征点之后,进行knn暴力匹配,并且将同名点特征不明显的点对进行去消除。

matcher = cv2.BFMatcher()

# 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
rawMatches = matcher.knnMatch(featuresA, featuresB, 2)

matches = []
for m in rawMatches:
    # 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对
    if len(m) == 2 and m[0].distance < m[1].distance * ratio:
        # 存储两个点在featuresA, featuresB中的索引值
        matches.append((m[0].trainIdx, m[0].queryIdx))

第二是如何根据特征点匹配结果将两幅图像拼接起来,我们知道两幅图像的一般拍摄视角不同,我需要至少四对同名点来计算变换矩阵M,M的形式如下:在这里插入图片描述
这个变换矩阵后将A图像乘以M便可以将两幅图像变换到同一个视角,之后将B图片根据实际位置传入A图像最右端或者最左端就可以实现原始图像的拼接。
具体的实现代码如下:

# H是4x4视角变换矩阵
(matches, H, status) = M
# 将图片A进行视角变换,result是变换后图片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
self.cv_show('result', result)
# 将图片B传入result图片最左端
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB

最终的代码是这样的:
主文件py

import numpy as np
import cv2


class Stitcher:

    # 拼接函数
    def stitch(self, images, ratio=0.75, reprojThresh=4.0, showMatches=False):
        # 获取输入图片
        (imageB, imageA) = images
        # 检测A、B图片的SIFT关键特征点,并计算特征描述子
        (kpsA, featuresA) = self.detectAndDescribe(imageA)
        (kpsB, featuresB) = self.detectAndDescribe(imageB)

        # 匹配两张图片的所有特征点,返回匹配结果
        M = self.matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)

        # 如果返回结果为空,没有匹配成功的特征点,退出算法
        if M is None:
            return None

        # 否则,提取匹配结果
        # H是4x4视角变换矩阵
        (matches, H, status) = M
        # 将图片A进行视角变换,result是变换后图片
        result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
        self.cv_show('result', result)
        # 将图片B传入result图片最左端
        result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
        self.cv_show('result', result)
        # 检测是否需要显示图片匹配
        if showMatches:
            # 生成匹配图片
            vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status)
            # 返回结果
            return (result, vis)

        # 返回匹配结果
        return result

    def cv_show(self, name, img):
        cv2.imshow(name, img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def detectAndDescribe(self, image):
        # 将彩色图片转换成灰度图
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        # 建立SIFT生成器
        descriptor = cv2.xfeatures2d.SIFT_create()
        # 检测SIFT特征点,并计算描述子
        (kps, features) = descriptor.detectAndCompute(image, None)

        # 将结果转换成NumPy数组
        kps = np.float32([kp.pt for kp in kps])

        # 返回特征点集,及对应的描述特征
        return (kps, features)

    def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
        # 建立暴力匹配器
        matcher = cv2.BFMatcher()

        # 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
        rawMatches = matcher.knnMatch(featuresA, featuresB, 2)

        matches = []
        for m in rawMatches:
            # 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对
            if len(m) == 2 and m[0].distance < m[1].distance * ratio:
                # 存储两个点在featuresA, featuresB中的索引值
                matches.append((m[0].trainIdx, m[0].queryIdx))

        # 当筛选后的匹配对大于4时,计算视角变换矩阵
        if len(matches) > 4:
            # 获取匹配对的点坐标
            ptsA = np.float32([kpsA[i] for (_, i) in matches])
            ptsB = np.float32([kpsB[i] for (i, _) in matches])

            # 计算视角变换矩阵
            (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)

            # 返回结果
            return (matches, H, status)

        # 如果匹配对小于4时,返回None
        return None

    def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status):
        # 初始化可视化图片,将A、B图左右连接到一起
        (hA, wA) = imageA.shape[:2]
        (hB, wB) = imageB.shape[:2]
        vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
        vis[0:hA, 0:wA] = imageA
        vis[0:hB, wA:] = imageB

        # 联合遍历,画出匹配对
        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]) + wA, int(kpsB[trainIdx][1]))
                cv2.line(vis, ptA, ptB, (0, 255, 0), 1)

        # 返回可视化结果
        return vis

从文件py

from Stitcher import Stitcher
import cv2

# 读取拼接图片
imageA = cv2.imread("left_01.png")
imageB = cv2.imread("right_01.png")

# 把图片拼接成全景图
stitcher = Stitcher()
(result, vis) = stitcher.stitch([imageA, imageB], showMatches=True)

# 显示所有图片
cv2.imshow("Image A", imageA)
cv2.imshow("Image B", imageB)
cv2.imshow("Keypoint Matches", vis)
cv2.imshow("Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

总结

这就是今天的全部内容了,当然对算法的具体原理和实现过程并没有过多的介绍,更多的是从程序实现的角度来进行的,如果想要具体了解算法实现原理,可以参考这篇文章非常详细的sift算法介绍,创作不易,喜欢的uu点个赞赞,让我们下篇文章再见!

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
压缩包中包含的具体内容: 对给定数据中的6个不同场景图像,进行全景图拼接操作,具体要求如下: (1) 寻找关键点,获取关键点的位置和尺度信息(DoG检测子已由KeypointDetect文件夹中的detect_features_DoG.m文件实现;请参照该算子,自行编写程序实现Harris-Laplacian检测子)。 (2) 在每一幅图像中,对每个关键点提取待拼接图像的SIFT描述子(编辑SIFTDescriptor.m文件实现该操作,运行EvaluateSIFTDescriptor.m文件检查实现结果)。 (3) 比较来自两幅不同图像的SIFT描述子,寻找匹配关键点(编辑SIFTSimpleMatcher.m文件计算两幅图像SIFT描述子间的Euclidean距离,实现该操作,运行EvaluateSIFTMatcher.m文件检查实现结果)。 (4) 基于图像中的匹配关键点,对两幅图像进行配准。请分别采用最小二乘方法(编辑ComputeAffineMatrix.m文件实现该操作,运行EvaluateAffineMatrix.m文件检查实现结果)和RANSAC方法估计两幅图像间的变换矩阵(编辑RANSACFit.m 文件中的ComputeError()函数实现该操作,运行TransformationTester.m文件检查实现结果)。 (5) 基于变换矩阵,对其中一幅图像进行变换处理,将其与另一幅图像进行拼接。 (6) 对同一场景的多幅图像进行上述操作,实现场景的全景图拼接(编辑MultipleStitch.m文件中的makeTransformToReferenceFrame函数实现该操作)。可以运行StitchTester.m查看拼接结果。 (7) 请比较DoG检测子和Harris-Laplacian检测子的实验结果。图像拼接的效果对实验数据中的几个场景效果不同,请分析原因。 已经实现这些功能,并且编译运行均不报错!
全景图像拼接是将多张拍摄自同一地点、视角不同的照片拼接成一张大的全景图像。OpenCV是一个非常强大的计算机视觉,可以用来实现全景图像拼接。 下面是实现全景图像拼接的基本步骤: 1. 加载图片。使用OpenCV的cv2.imread()函数加载图片。 2. 特征点检测。使用OpenCV的SIFT、SURF、ORB等算法检测每张图片的特征点。 3. 特征点匹配。使用OpenCV的FLANN或者Brute-Force算法对特征点进行匹配。 4. 计算单应性矩阵。使用OpenCV的findHomography函数计算单应性矩阵,将当前图片与上一张图片进行拼接。 5. 图像拼接。使用OpenCV的warpPerspective函数将当前图片进行透视变换,然后将图片拼接到上一张图片上。 6. 重复步骤2-5,直到所有图片拼接完成。 下面是一个基于OpenCV实现全景图像拼接的示例代码: ```python import cv2 import numpy as np # 加载图片 img1 = cv2.imread('img1.jpg') img2 = cv2.imread('img2.jpg') # 特征点检测 sift = cv2.xfeatures2d.SIFT_create() kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # 特征点匹配 bf = cv2.BFMatcher() matches = bf.knnMatch(des1, des2, k=2) good_matches = [] for m, n in matches: if m.distance < 0.75 * n.distance: good_matches.append(m) src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) # 计算单应性矩阵 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) # 图像拼接 result = cv2.warpPerspective(img1, M, (img1.shape[1] + img2.shape[1], img1.shape[0])) result[0:img2.shape[0], 0:img2.shape[1]] = img2 cv2.imshow('result', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这段代码实现了两张图片的拼接。你可以使用这个基本的框架,将多张图片进行拼接,从而实现全景图像拼接
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的神里绫华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值