全景拼接python旗舰版

news2024/9/28 1:21:07

前言

在这个项目中,您将构建一个管道,将几幅图像拼接成一个全景图。您还将捕获一组您自己的图像来报告最终的结果。

步骤1 特征检测与描述

本项目的第一步是对序列中的每幅图像分别进行特征检测。回想一下我们在这个类中介绍过的一些特征探测器:哈里斯角、Blob探测器、SIFT探测器等。与检测步骤相结合,就进行了特征描述。您的特性必须对某些转换是不变的。想想你将收集数据集的方式。你需要什么样的不变性?探索在OpenCV中可用的特性描述符实现: SIFT、SURF、ORB等

1.使用cv2.imread读取输入的图像。

2.将图像转换为灰度(例如,使用cv2.cvtColor)以进行特征提取。您也可以通过cv2.resize调整图像的大小(例如,在参考约塞米蒂序列中那样的480像素的高度),以使以下步骤运行得更快。

3.选择您的特征检测器。cv2是一个很好的开始。SIFT_creve(或cv2.xfeatures2d。SIFT_Creve在旧的OpenCV版本中)。

4.使用检测和计算方法提取关键点和关键点特征。

5.图1使用cv2.drawKeypoints可视化了检测到的关键点:

def flt_show(image, title='sift detects'):
    plt.figure(figsize=(10,10)) # Canvas 10x magnification
    plt.title(title,fontsize=20) # title
    plt.axis('off') # Hidden axis
    plt.imshow(cv2.cvtColor(image,cv2.COLOR_BGR2RGB)) # print image
    plt.show() # Display canvas
    
# 1. Read the input images using cv2.imread.
def feature_detection_and_description(image_path):
    image = cv2.imread(image_path)
    # RGB to Gray
    image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    # Create detector
    sift_detecter = sift = cv2.SIFT_create()
    # Detection picture
    kp,des = sift.detectAndCompute(image,None) # Keypoints and descriptors
    # Plot key points
    img2 = cv2.drawKeypoints(image,kp,None,(255,0,0),4)
    # show
    flt_show(img2)

    # Release resources
    cv2.waitKey(0)
    cv2.destroyAllWindows()

feature_detection_and_description('yosemite1.jpg')

步骤2 特征匹配和图像对齐

一旦计算出了关键点和特征,代码就需要在连续的图像对之间建立良好的匹配。为此,您需要选择一个距离函数和一个匹配算法。

1.选择一个匹配算法和一个距离函数。强力匹配(cv2。你可以做这个工作,因为你以后会摆脱糟糕的比赛!如果您使用二进制特性,如简短或ORB,看看汉明距离函数(cv2。NORM_HAMMING),否则,只需使用欧几里得距离(cv2。norm_l2 ).

2.使用匹配()方法从一对图像中计算匹配,并提供它们的特征作为输入。选择一对有良好重叠的图像。实际上,按照预定义的顺序(例如从左到右)捕捉图像进行全景拼接是很方便的,这样相邻的图像总是有很好的重叠。

3.使用原始的关键点位置和匹配,以两个m×2数组点1和点2的形式构造对应,这样点1[i]和点2[i]分别在第一和第二幅图像中存储匹配的位置。如果您正在使用建议的OpenCV功能,请查看关键点和DMatch类的接口,以了解如何操作返回对象。

4.现在,从对应关系中估计图像之间的同源性变换。匹配项将会有很多离群值,所以你需要选择一个对离群值很健壮的估计器,比如RANSAC。cv2.findHomography对这一步很有用!

5.最后,用模糊的同源性扭曲其中一幅图像: cv2.warpPerspective。

6.图2显示了在所选对图像之间检测到的1249个匹配中的前10个匹配(基于响应分数)。该图是使用cv2.drawMatches创建的。让我们来看看图3和图4。为什么前者的一致性如此成功?为什么在后一个数字中会有问题?

def detectAndDescribe(image):
    # convert the image to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # detect and extract features from the image
    descriptor = cv2.SIFT_create()
    (kps, features) = descriptor.detectAndCompute(image, None)

    # convert the keypoints from KeyPoint objects to NumPy
    # arrays
#     kps = np.float32([kp.pt for kp in kps])

    # return a tuple of keypoints and features
    return (kps, features)

def matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
    # compute the raw matches and initialize the list of actual
    # matches
#     matcher = cv2.DescriptorMatcher_create("BruteForce")
#     rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
#     matches = []
#     # loop over the raw matches
#     for m in rawMatches:
#         # ensure the distance is within a certain ratio of each
#         # other (i.e. Lowe's ratio test)
#         if len(m) == 2 and m[0].distance < m[1].distance * ratio:
#             matches.append((m[0].trainIdx, m[0].queryIdx))
        
    bf = cv2.BFMatcher(cv2.NORM_L2,crossCheck=True)
    matches = bf.match(featuresA,featuresB)
    goods = sorted(matches,key = lambda x:x.distance)
    # computing a homography requires at least 4 matches
    if len(matches) > 4:
        # construct the two sets of points
#         ptsA = np.float32([kpsA[i] for (_, i) in matches])
#         ptsB = np.float32([kpsB[i] for (i, _) in matches])
        ptsA = np.float32([ kpsA[m.queryIdx].pt for m in goods ]).reshape(-1,1,2)
        ptsB = np.float32([ kpsB[m.trainIdx].pt for m in goods ]).reshape(-1,1,2)
        # compute the homography between the two sets of points
        (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,
            reprojThresh)

        # return the matches along with the homograpy matrix
        # and status of each matched point
        return (matches, H, status)

    # otherwise, no homograpy could be computed
    return None

def drawMatches(imageA, imageB, kpsA, kpsB, matches, status):
    img1 = imageA
    img2 = imageB
    if len(matches) > 10:
        src_pts = np.float32([ kpsA[m.queryIdx].pt for m in matches ]).reshape(-1,1,2)
        dst_pts = np.float32([ kpsB[m.trainIdx].pt for m in matches ]).reshape(-1,1,2)

        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
        matchesMask = mask.ravel().tolist()
       
        h,w,_ = img1.shape
        pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
        draw_params = dict(matchColor = (0,255,0), # draw matches in green color
            singlePointColor = None,
            matchesMask = matchesMask, # draw only inliers
            flags = 2)
        
        img_matchers = cv2.drawMatches(imageA,kpsA,imageB,kpsB,matches,None,**draw_params)
    # return the visualization
    return img_matchers

def feature_matching_and_image_alignment(imageL, imageR, ratio=0.75, reprojThresh=4.0, showMatches=False):
    imageA, imageB = imageR, imageL
    # local invariant descriptors from them
    (kpsA, featuresA) = detectAndDescribe(imageA)
    (kpsB, featuresB) = detectAndDescribe(imageB)

    # match features between the two images
    M = matchKeypoints(kpsA, kpsB,
        featuresA, featuresB, ratio, reprojThresh)

    # if the match is None, then there aren't enough matched
    # keypoints to create a panorama
    if M is None:
        return None

    # otherwise, apply a perspective warp to stitch the images
    # together
    (matches, H, status) = M
    result = cv2.warpPerspective(imageA, H,
        (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
    result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB

    # check to see if the keypoint matches should be visualized
    if showMatches:
        vis = drawMatches(imageA, imageB, kpsA, kpsB, matches,
            status)

        # return a tuple of the stitched image and the
        # visualization
        return (result, vis)

    # return the stitched image
    return result

result, vis = feature_matching_and_image_alignment(cv2.imread('yosemite1.jpg'), cv2.imread('yosemite2.jpg'), showMatches=True)
flt_show(vis)
flt_show(result)

步骤3 图像背景去除

现在您已经将原始图像1和图像2扭曲到图像1的平面上,您需要将它们组合起来以产生无缝的结果。如果你简单地添加图像强度,重叠的区域看起来会很不错,但其他区域看起来会很暗,如图3所示。另一个简单的解决方案是将一幅图像复制粘贴到另一幅图像之上。这还将在边界上创建工件,如图5所示。减少伪影强度的一个解决方案是使用自适应权值来混合图像。靠近每幅图像边界的像素不如位于中心的像素可靠。那么如何将这些像素与与这些像素到相应图像边界的距离成比例的权重进行混合呢?我们提供了get_tinghe_变换函数,它取一个扭曲的RGB图像H×W×3,并产生一个单通道距离函数H×W×1。您可以使用这个函数来计算结果为img1 *权重1+img2*权重2。不要忘记将生成的图像规格化到0…255的范围。尝试使用np.最大值(denom,1.0)以避免除以零。将您的图像转换回无符号的8位整数与img.astype(“uint8”)

def get_distance_transform(img_rgb):
    '''
    Get distance to the closest background pixel for an RGB image
    Input:
        img_rgb : np.array , HxWx3 RGB image
    Output:
        dist : np.array , HxWx1 distance image
            each pixel ’s intensity is proportional to
            its distance to the closest background pixel
            scaled to [0..255] for plotti
    '''
    # Threshold the image : any value above 0 maps into 255
    thresh = cv2.threshold(img_rgb , 0 , 255 , cv2 . THRESH_BINARY )[1]
    # Collapse the color dimension
    thresh = thresh.any(axis =2)
    # Pad to make sure the border is treated as background
    thresh = np.pad(thresh, 1, mode='maximum')
    # Get distance transform
    dist = distance_transform_edt(thresh) [1: -1 , 1: -1]
    # HxW -> HxWx1
    dist = dist[: , : , None ]
    return dist / dist . max () * 255.0

def getMaskResult(result, show=False):
    mask = get_distance_transform(result)

    locs = np.where(mask>0)
    xmin = np.min(locs[1])
    xmax = np.max(locs[1])
    ymin = np.min(locs[0])
    ymax = np.max(locs[0])
    result_mask = result[ymin:ymax, xmin:xmax]
    if show:
        flt_show(result, 'before')
        flt_show(result_mask, 'after')
    return result_mask
result_mask = getMaskResult(result, True)

步骤4 缝合多张图像

拼接n个图像的一个简单方法是从左到右对序列进行排序,并逐步应用成对拼接:如果拼接每一对相邻图像,将得到n个−1的全景图。您可以一次又一次地对这些图像应用相同的拼接过程,直到您只留下一个宽的全景图。您只需要在它上面添加一个for循环。

imageList = [cv2.imread('yosemite1.jpg'), cv2.imread('yosemite2.jpg'), cv2.imread('yosemite3.jpg'),cv2.imread('yosemite4.jpg')]

temp_image = feature_matching_and_image_alignment(imageList[0], imageList[1])
temp_image = getMaskResult(temp_image)
flt_show(temp_image1, title='Step 1')
for i in range(2, 4):
    temp_image = feature_matching_and_image_alignment(temp_image, imageList[i])
    temp_image = getMaskResult(temp_image)
    flt_show(temp_image, title='Step {}'.format(i))

步骤5 创建您自己的全景图,并讨论设置和结果。

请注意,这一步将主要根据您提供的讨论进行分级(例如,只捕获一些图像并在不编写的情况下生成结果不会给您很多分数)。为了获得一个好的全景,在捕捉自己的图像时有几件事需要考虑。

imageList = [cv2.imread('1.png'), cv2.imread('2.png'), cv2.imread('3.png'),cv2.imread('4.png'), cv2.imread('5.png')]

temp_image = feature_matching_and_image_alignment(imageList[0], imageList[1])
temp_image = getMaskResult(temp_image)
flt_show(temp_image, title='Step 1')
for i in range(2, 5):
    temp_image = feature_matching_and_image_alignment(temp_image, imageList[i])
    temp_image = getMaskResult(temp_image)
    flt_show(temp_image, title='Step {}'.format(i))

# # # #发表评论

- 1。这已经在步骤4中完成了。

- 2。数据集捕捉了沿河的建筑风景,具有良好的视野和易于区分的特征。

- 3。相邻的图片有很好的重叠。

- 4. 摄像机角度的变化容易引起同态现象。如果天气晴朗,最好待在室外,视野开阔,位置好,光线也明亮。只要两幅图像之间的特征明显,摄像机是否自由移动并不重要。

当三张以上的图片拼接在一起时,由于黑色背景的原因,图像与其他图像的拼接会有很多困难。背景剔除可以解决这个问题。

感谢观看!由于是作业的缘故,文中可能有极少部分会出现语义不通顺的情况,但对本文的目的没有什么影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/339784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

KDS安装步骤

KDS kinetis design studio 软件 第一步官网(https://www.nxp.com/ 注册账号下载set成功下载软件。 随着AI&#xff0c;大数据这些技术的快速发展&#xff0c;与此有关的知识也普及开来。如何在众多网站中寻找最有价值的信息&#xff0c;如何在最短的时间内获得最新的技…

家政服务小程序实战教程09-图文卡片

小程序还有一类需求就是展示服务的列表&#xff0c;我们这里用图文卡片组件来实现&#xff0c;我们先要添加一个标题&#xff0c;使用网格布局来实现 第一列添加一个文本组件&#xff0c;第二列添加一个图标组件 修改文本组件的文本内容&#xff0c;设置外边距 设置第二列的样式…

PHP session反序列化漏洞

session请求过程&#xff1a; 当第一次访问网站时&#xff0c;Seesion_start()函数就会创建一个唯一的Session ID&#xff0c;并自动通过HTTP的响应头&#xff0c;将这个Session ID保存到客户端Cookie中。同时&#xff0c;也在服务器端创建一个以Session ID命名的文件&#xff…

Netty网络应用基础

文章目录前言一、基础概念狭义网络网络应用工程库二、总体框架三、应用分层总结前言 开始之前&#xff0c;咱们先澄清一些基础概念、总体框架和应用分层&#xff0c;避免在后续的讨论中给大家带来误解。 一、基础概念 狭义网络 常规Java后端开发中应用到的计算机网络&#x…

【ArcGIS Pro二次开发】(2):创建一个Add-in项目

Add-In即模块加载项&#xff0c;是一种能够快速扩展桌面应用程序功能的全新扩展方式。 一、创建新项目 1、打开VS2002&#xff0c;选择创建新项目。 2、在搜索框中输入“arcgis pro”&#xff0c;在搜索结果中选择【ArcGIS Pro 模块加载项】创建项目&#xff0c;注意选择语言应…

OpenSSL发布修复程序以解决多个安全漏洞!

OpenSSL 项目已发布修复程序以解决多个安全漏洞&#xff0c;包括开源加密工具包中的一个高严重性错误&#xff0c;该错误可能会使用户遭受恶意攻击。 国际知名白帽黑客、东方联盟创始人郭盛华表示&#xff0c;该问题被追踪为CVE-2023-0286&#xff0c;与类型混淆有关&#xff…

激光雷达相关技术

一、参考资料 17篇点云处理综述-点云语义分割、点云物体检测、自动驾驶中的点云处理…… #三维视觉 #点云 3D点云数据标准 自动驾驶之心 自动驾驶之心-专栏 二、重要信息 1. 黑车检测难题 从2018年至今&#xff0c;高线数机械式激光雷达的测距能力一直停留在200米10%反…

Fastjson 1.83漏洞利用猜想

0x00 前言 这篇是去年五月发到i春秋的~ 在不久前fastjson<1.2.83又爆出来了新的问题,详细内容可以参考 https://github.com/alibaba/fastjson/wiki/security_update_20220523,这篇文章主要是抛转引玉,写一种可能的利用思路,详细的利用链可能要等大佬们来给出了。 文内…

【LeetCode】动态规划总结

动态规划解决的问题 动态规划和贪心的区别&#xff1a; 动态规划是由前一个状态推导出来的&#xff1b; 贪心是局部直接选最优的。 动态规划解题步骤 状态定义&#xff1a;确定dp数组以及下标的含义状态转移方程&#xff1a;确定递推公式初始条件&#xff1a;dp如何初始化遍历…

java学习记录day7

类与对象 对象数组与管理 对象数组就是数组里的每个元素都是对象&#xff1a;House[] home new House[10];使用对象数组实现多个House的管理。 拓展:数组的扩充: 扩充原来数组的一半&#xff1a; public void add(House[] home){int newLen home.length*3/21;home Array…

ARM uboot源码分析3-启动第一阶段

一、start.S 解析7 总结回顾&#xff1a;lowlevel_init.S 中总共做了哪些事情&#xff1a; 检查复位状态、IO 恢复、关看门狗、开发板供电锁存、时钟初始化、DDR 初始化、串口初始化并打印 ‘O’、tzpc 初始化、打印 ‘K’。 其中值得关注的&#xff1a;关看门狗、开发板供电锁…

计算机网络1:Tcp三次握手和四次挥手

一、TCP传输的过程-三次握手 1.建立连接并确认连接&#xff08;三次握手&#xff09; ** 过程&#xff1a; ** &#xff08;1&#xff09;客户端向服务端发出连接请求SYN1&#xff0c;seqx&#xff0c;等待服务端响应.状态由CLOSED转为SYN_SENT &#xff08;2&#xff09;服务…

升级到https

现在很多站长都会考虑将自己的站点从http升级到https&#xff0c;不仅是基于安全的考虑&#xff0c;有的也是因为第三方平台的限制&#xff0c;如谷歌浏览器会将http站点标记为不安全的站点&#xff0c;微信平台要求接入的微信小程序必须使用https等。 那如何将一个http站点升…

C++006-C++分支结构练习题

文章目录C006-C分支结构练习题案例练习题目描述 有一门课不及格的学生题目描述 分段函数题目描述 骑车与走路在线练习&#xff1a;总结C006-C分支结构练习题 在线练习&#xff1a; http://noi.openjudge.cn/ch0104/ https://www.luogu.com.cn/ 案例练习 参考&#xff1a;http…

春招Leetcode刷题日记-D1-贪心算法-分配问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 D1-贪心算法-分配问题何为贪心力扣455. 分发饼干思路代码何为贪心 1、顾名思义&#xff0c;贪心算法采用贪心的策略&#xff0c;保证每次操作都是局部最优的&#xff0c;从而…

神经网络损失函数分布可视化神器

论文名称和地址&#xff1a;Visualizing the Loss Landscape of Neural Netshttps://arxiv.org/pdf/1712.09913.pdf1.1 背景和动机作者主要想研究几个问题&#xff1a;为什么我们能够最小化高度非凸神经损失函数&#xff1f;为什么得到的最小值这个结果具有泛化性&#xff1f;不…

【C语言进阶】预处理与程序环境

目录一.详解编译与链接1.前言2.翻译环境3.剖析编译过程4.运行环境二.预处理详解1.预定义符号2.剖析#define(1).定义标识符(2).定义宏(3).替换规则(4).#和##(5).宏与函数的对比(6).#undef3.条件编译4.文件包含(1).头文件包含的方式(2).嵌套文件包含一.详解编译与链接 1.前言 在…

《流浪地球2》的现实倒影(三):从脑机接口到数字永生

是人&#xff0c;就会死。这个事实听起来或许很悲哀&#xff0c;但电影《流浪地球2》在一开始&#xff0c;就给出了另一种可能性——疯狂科学家进行数字生命实验&#xff0c;通过连接大脑的电极片&#xff0c;将思维意识上传到计算机&#xff0c;从而让人永生。电影开头的这位印…

《Keras深度学习:入门、实战与进阶》CIFAR-10图像识别

本文摘自《Keras深度学习&#xff1a;入门、实战与进阶》。 https://item.jd.com/10038325202263.html 这个数据集由Alex Krizhevsky、Vinod Nair和Geoffrey Hinton收集整理&#xff0c;共包含了60000张3232的彩色图像&#xff0c;50000张用于训练模型、10000张用于评估模型。…

JUC并发编程学习笔记(一)——知识补充(Threadlocal和引用类型)

强引用、弱引用、软引用、虚引用 Java执行 GC(垃圾回收)判断对象是否存活有两种方式&#xff0c;分别是引用计数法和引用链法(可达性分析法)。 **引用计数&#xff1a;**Java堆中给每个对象都有一个引用计数器&#xff0c;每当某个对象在其它地方被引用时&#xff0c;该对象的…