百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

opencv-使用SIFT or SURF实现图像拼接

toyiye 2024-08-10 21:28 32 浏览 0 评论

之前的章节学习了各种图像特征的查找算法,比如SIFT, SURF,FAST, ORB等等,这些算法可以用来干什么呢?可以用来做图像相似度评价,比如图像分类搜索,也可以用来做全景图像拼接,接下来我们就尝试SIFT/SURF算法进行全景图像的拼接。

思路

  1. 提取要拼接的两张图片的特征点、特征描述符;
  2. 将两张图片中对应的位置点找到,匹配起来;
  3. 如果找到了足够多的匹配点,就能将两幅图拼接起来,拼接前,可能需要将第二幅图透视旋转一下,利用找到的关键点,将第二幅图透视旋转到一个与第一幅图相同的可以拼接的角度;
  4. 进行拼接;
  5. 进行拼接后的一些处理,让效果看上去更好。

实现方法

  1. 提取图片的特征点、描述符,可以使用opencv创建一个SIFT对象,SIFT对象使用DoG方法检测关键点,并对每个关键点周围的区域计算特征向量。在实现时,可以使用比SIFT快的SURF方法,使用Hessian算法检测关键点。因为只是进行全景图拼接,在使用SURF时,还可以调节它的参数,减少一些关键点,只获取64维而不是128维的向量等,加快速度。
  2. 在分别提取好了两张图片的关键点和特征向量以后,可以利用它们进行两张图片的匹配。在拼接图片中,可以使用Knn进行匹配,但是使用FLANN快速匹配库更快,图片拼接,需要用到FLANN的单应性匹配。
  3. 单应性匹配完之后可以获得透视变换H矩阵,用这个的逆矩阵来对第二幅图片进行透视变换,将其转到和第一张图一样的视角,为下一步拼接做准备。
  4. 透视变换完的图片,其大小就是最后全景图的大小,它的右边是透视变换以后的图片,左边是黑色没有信息。拼接时可以比较简单地处理,通过numpy数组选择直接把第一张图加到它的左边,覆盖掉重叠部分,得到拼接图片,这样做非常快,但是最后效果不是很好,中间有一条分割痕迹非常明显。使用opencv指南中图像金字塔的代码对拼接好的图片进行处理,整个图片平滑了,中间的缝还是特别突兀。
  5. 直接拼效果不是很好,可以把第一张图叠在左边,但是对第一张图和它的重叠区做一些加权处理,重叠部分,离左边图近的,左边图的权重就高一些,离右边近的,右边旋转图的权重就高一些,然后两者相加,使得过渡是平滑地,这样看上去效果好一些,速度就比较慢。如果是用SURF来做,时间主要画在平滑处理上而不是特征点提取和匹配。

python_opencv中主要使用的函数

基于python 3.7和对应的python-opencv

  1. cv2.xfeatures2d.SURF_create ([hessianThreshold[, nOctaves[, nOctaveLayers[, extended[, upright]]]]]) :该函数用于生成一个SURF对象,在使用时,为提高速度,可以适当提高hessianThreshold,以减少检测的关键点的数量,可以extended=False,只生成64维的描述符而不是128维,令upright=True,不检测关键点的方向。
  2. cv2.SURF.detectAndCompute(image, mask[, descriptors[, useProvidedKeypoints]]) :该函数用于计算图片的关键点和描述符,需要对两幅图都进行计算。
  3. flann=cv2.FlannBasedMatcher(indexParams,searchParams),match=flann.knnMatch(descrip1,descrip2,k=2) :flann快速匹配器有两个参数,一个是indexParams,一个是searchParams,都用手册上建议的值就可以。在创建了匹配器得到匹配数组match以后,就可以参考Lowe给出的参数,对匹配进行过滤,过滤掉不好的匹配。其中返回值match包括了两张图的描述符距离distance 、训练图(第二张)的描述符索引trainIdx 、查询的图(第一张)的描述符索引queryIdx 这几个属性。
  4. M,mask=cv2.findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask]]]):这个函数实现单应性匹配,返回的M是一个矩阵,即对关键点srcPoints做M变换能变到dstPoints的位置
  5. warpImg=cv2.warpPerspective(src,np.linalg.inv(M),dsize[,dst[,flags[,borderMode[,borderValue]]]]):用这个函数进行透视变换,变换视角。src是要变换的图片,np.linalg.inv(M)是④中M的逆矩阵,得到方向一致的图片。
  6. a=b.copy() 实现深度复制,Python中默认是按引用复制,a=b是a指向b的内存。
  7. draw_params = dict(matchColor = (0,255,0),singlePointColor = (255,0,0),matchesMask = matchMask,flags = 2),img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params):使用drawMatches可以画出匹配的好的关键点,matchMask是比较好的匹配点,之间用绿色线连接起来。

核心代码

import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
MIN = 10 #最小匹配数
starttime=time.time()#计算花费的时间
img1 = cv2.imread('./images/1.jpg') #导入拼接的左图
img2 = cv2.imread('./images/2.jpg') #导入拼接的右图
#img1gray=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)#转换后提升了30%
#img2gray=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
#surf=cv2.xfeatures2d.SURF_create(10000,nOctaves=4,extended=False,upright=True)#使用SURF算法常常出现问题或者匹配点不够的情况
surf=cv2.xfeatures2d.SIFT_create()#改为SIFT算法
kp1,descrip1=surf.detectAndCompute(img1,None) #获取图像1特征点
kp2,descrip2=surf.detectAndCompute(img2,None) #获取图像2特征点
#使用flann的搜索,整体来说分为两步,一是建立索引,二是搜索。
FLANN_INDEX_KDTREE = 0
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searchParams = dict(checks=50)
#使用flann查找上述两张图像中匹配的特征描述子
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
#计算好的匹配数量是否达到最少要求,即10
good=[]
for i,(m,n) in enumerate(match):
 if(m.distance<0.75*n.distance):
 good.append(m)
if len(good)>MIN:
 src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
 ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
 M,mask=cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
 #透视变换
 warpImg = cv2.warpPerspective(img2, np.linalg.inv(M), (img1.shape[1]+img2.shape[1], img2.shape[0]))
 #融合前拼接
 direct=warpImg.copy()
 direct[0:img1.shape[0], 0:img1.shape[1]] =img1
 simple=time.time()
 #变换图像img2
 #cv2.namedWindow("Result", cv2.WINDOW_NORMAL)
 #cv2.imshow("Result",warpImg)
 rows,cols=img1.shape[:2]
 #融合处理接缝
 for col in range(0,cols):
 if img1[:, col].any() and warpImg[:, col].any():#开始重叠的最左端
 left = col
 break
 for col in range(cols-1, 0, -1):
 if img1[:, col].any() and warpImg[:, col].any():#重叠的最右一列
 right = col
 break
 res = np.zeros([rows, cols, 3], np.uint8)
 for row in range(0, rows):
 for col in range(0, cols):
 if not img1[row, col].any():#如果没有原图,用旋转的填充
 res[row, col] = warpImg[row, col]
 elif not warpImg[row, col].any():
 res[row, col] = img1[row, col]
 else:
 srcImgLen = float(abs(col - left))
 testImgLen = float(abs(col - right))
 alpha = srcImgLen / (srcImgLen + testImgLen)
 res[row, col] = np.clip(img1[row, col] * (1-alpha) + warpImg[row, col] * alpha, 0, 255)
 warpImg[0:img1.shape[0], 0:img1.shape[1]]=res
 final=time.time()
 img3=cv2.cvtColor(direct,cv2.COLOR_BGR2RGB)#直接拼接的结果
 plt.imshow(img3,),plt.show()
 img4=cv2.cvtColor(warpImg,cv2.COLOR_BGR2RGB)#融合后拼接的结果
 plt.imshow(img4,),plt.show()
 print("simple stich cost %f"%(simple-starttime))
 print("\ntotal cost %f"%(final-starttime))
 cv2.imwrite("simplepanorma.png",direct)
 cv2.imwrite("bestpanorma.png",warpImg)
 
else:
 print("not enough matches!")

运行结果

原图1.jpg

远景的拼接效果还是不错的,室内场景的效果:

平滑处理后的图像会好很多。SIFT算法比较耗时,超过30秒。但是明显比SURF好,SURF算法对图像的要求比较高,大景深或旋转的图像很难找到足够匹配的点,容易出现失败。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码