OpenCV处理图片拼接

图片拼接(实战项目三)

主题思路

  1. 读入图片

  2. 预处理图片

  3. 图片特征提取

  4. 特征处理

  5. 特征匹配

  6. 透视变换

  7. 图片再处理

  8. (可选)图片特征点连线配对

具体代码

Sticher.py

引入头文件
import cv2
import numpy as np
创建类
class Sticher:
自定义函数
  • def stich:外部接口函数

  • def detectAndDescribe:用于图片的特征点提取,内部逻辑函数

  • def matchKeypoints:特征点匹配

  • def drawMatches:显示2图片的特征点匹配,用线勾勒出来,方便观察细节(可选)

展示图片函数:show()
    def show(self, image, name='demo'):
        cv2.imshow(name, image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
外部接口函数:stich()

stich函数传入一个images的列表,里面包含2个图片,经过一系列其他成员函数后将2图片合并

参数说明:

ratio = 0.75 (先验知识,拿来主义~)

# reprojThresh是RANSAC算法中的阈值参数,用于判断是否为内点(inlier)的阈值
# reprojThresh越低,说明受外点影响越小,图像拟合结果更精细,但可能过滤掉一些有价值的匹配点
# reprojThresh越高,说明容忍度越高,但效果可能偏低
    def stich(self, images, ratio=0.75, reprojThresh=4.0, showMatches=False):#showMatches是自己定义的一个选项,没什么用
        # 获取输入图片
        (imageB, imageA) = images
        # 检测A,B图片的SIFT关键特征点,并计算特征描述子
        (kpsA, featureA) = self.detectAndDescribe(imageA)
        (kpsB, featureB) = self.detectAndDescribe(imageB)
        # 经过detectAndDescribe()后,返回的kpsA和kpsB都是经过处理后的特征点集信息
        # kpsA,kpsB里面只包含了特征点的坐标信息(pt),因为该成员方法已经过滤提取
        # 匹配两张图片的所有特征点,返回匹配结果
        M = self.matchKeypoints(kpsA, kpsB, featureA, featureB, ratio, reprojThresh)
​
        # 如果返回结果为空,则没有匹配成功的特征点,退出算法
        if M is None:
            return None
​
        # 若匹配成功
        (matches, H, status) = M
        # 我们处理的思路是:
        # 将右边图片进行透视变换,使之与左边的图片配对
        # H为透视变换矩阵
        # 该函数要把imageA依据关系矩阵H进行透视变换,得到一个(宽,高)尺寸的图片
        # 由于H是综合imageA和imageB得到的,所以我们执行完warnPerspective后,会看到imageA中带上了imageB
        result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
        self.show(result)
        # 参数对应位: (高,宽)
        result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
        self.show(result)
        # 检测是否需要显示图片匹配:
        if showMatches:
            # 生成匹配图片
            vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status)
            return (result, vis)
        return result
特征点提取分析函数:detectAndDescribe()

核心思路:SIFT算法

# 检测图像的SIFT关键特征点并计算特征描述子
def detectAndDescribe(self, image):
    # 寻找图片的特征点先将图片转换为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 建立一个SIFT特征扫描器(可能有的版本有专利保护)
    descriptor = cv2.xfeatures2d.SIFT_create()
    # 检测SIFT特征点,并计算描述子
    # None表示检测全图,没有掩码遮罩
    (kps, features) = descriptor.detectAndCompute(image, None)#唯该函数真正进行特征点分析
    #kps包含一个特征点的一系列信息:一般我们需要的是 kp.pt 即图片的坐标信息(x,y)
    #kps的其他信息如:size(特征点尺度大小),angle(特征点的方向角度),class_id(特征点的唯一标识)
​
    # 将结果转换成Numpy数组
    # 接下来只提取 kps中 pt 的信息,并将其转成浮点32位的形式,格式需服务于后面的透视变换要求
    # kp.pt 这的pt 包含一个特征点的坐标位置信息,一般是一个二维向量
    kps = np.float32([kp.pt for kp in kps])
​
    # 返回特征点集(kps)和特征点局部区域的特征描述信息(features:特征描述子)
    return (kps, features)
特征匹配函数:matchKeypoints()
#注:此时传入的kps都是只保留了特征点的坐标信息的二维数组
def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
    # 建立暴力匹配器(BF算法)
    matcher = cv2.BFMatcher()
    # 使用knn检测来自A,B图的SIFT特征匹配对,k=2
    # 下面进行区域匹配,得到一个粗糙的匹配结果,还要进行过滤
    rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
    matches = []  # 该列表用于存放最终的匹配结果
    for m in rawMatches:
        if len(m) == 2 and m[0].distance < m[1].distance * ratio:
            # trainIdx表示训练图像的特征点索引信息,queryIdx表示查询图像的特征点索引信息
            matches.append((m[0].trainIdx, m[0].queryIdx))
    # matches里面现在存放的是 二维数组,每一个单独的元素都代表一个匹配对的信息
​
    # 当筛选后的匹配对大于4时,(我们得保证至少有4个点,才可以进行透视变换_实际上不是这样理解,底层涉及高数)
    if len(matches) > 4:
        # 获取匹配对的点的坐标
        # 这个for循环表示:
        # A:查询出:查询图像特征点的下表索引i
        # 依据i找到其在A图像的特征点集的信息(此处kpsA已经被提前处理成只有kp.pt坐标位信息了
        ptsA = np.float32([kpsA[i] for (_, i) in matches])
        ptsB = np.float32([kpsB[i] for (i, _) in matches])
        # 经过上述计算后,得到的ptsA,ptsB只是筛选后的特征点的坐标二维数组
        # 计算出透视关系
        # cv2.RANSAC:表示使用 RANSAC(Random Sample Consensus)算法进行鲁棒性估计和选择内点的方法
        # findHomography()与cv2.getPerspectiveTransform()不同的是,前者注重图像配准,后者注重图像透视变换,虽然两者都是用来图片的变换矩阵
        (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)
        # H表示单应性矩阵,表示从原始图像到目标图像的变换矩阵(到时候会拿来对right图像进行透视变换)
        return (matches, H, status)
        # matches里面存放的是二维数组,特征点的下标索引值(训练图片的下标索引值,查询图片的下标索引值)
    return None
(可选)绘制匹配函数:drawMatches()
# 同理,这里的kps都是处理后的
def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status):
    # 提取图片的尺寸信息
    (hA, wA) = imageA.shape[:2]
    (hB, wB) = imageB.shape[:2]
    # 使用np.zero创建一个空白图像(也可以说是创建一个三维数组,本质一样,都是zeros方法)
    vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
    # vis 就是我们用来绘制:特征点间连线的图
    vis[0:hA, 0:wA] = imageA#左边放imageA
    vis[0:hB, wA:] = imageB#右边放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)  # 在图片上画出绿色线条,端点两端连接着2个匹配的特征点
​
    return vis  # vis 代表可视化结果

imageStichering.py

from Sticher import Sticher# 不能直接写import,原理同(java 静态内部类static一样)
import cv2
​
imageA = cv2.imread("right_01.png")
imageB = cv2.imread("left_01.png")
​
#创建一个工具类
Sticher = Sticher()
(result,vis) = Sticher.stich([imageB,imageA],showMatches=True)
​
#显示所有图片
cv2.imshow('line',vis)
cv2.imshow('res',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值