当前位置: 代码网 > it编程>前端脚本>Python > OpenCV处理图片拼接

OpenCV处理图片拼接

2024年08月04日 Python 我要评论
def stich:外部接口函数def detectAndDescribe:用于图片的特征点提取,内部逻辑函数def matchKeypoints:特征点匹配def drawMatches:显示2图片的特征点匹配,用线勾勒出来,方便观察细节(可选)

图片拼接(实战项目三)

主题思路

  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()
    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()
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com