图像拼接(SIFT与PTGUI)
1.SIFT
在原图上提取描述子并可视化得到如下结果:
得到描述子后进行匹配:
构造单应性矩阵:
from PCV.geometry import homography, warpmodel = homography.RansacModel()
将匹配转换成齐次坐标点:
# 将匹配转换成齐次坐标点的函数def convert_points(j):ndx = matches[j].nonzero()[0]fp = homography.make_homog(l[j+1][ndx,:2].T) ndx2 = [int(matches[j][i]) for i in ndx]tp = homography.make_homog(l[j][ndx2,:2].T) # switch x and y - TODO this should move elsewherefp = vstack([fp[1],fp[0],fp[2]])tp = vstack([tp[1],tp[0],tp[2]])return fp,tpfp,tp = convert_points(0)
获取单应性矩阵和对应该单应性矩阵的正确点对:
H_01 = homography.H_from_ransac(fp,tp,model)[0]
class RansacModel(object):""" 用于测试单应性矩阵的类,其中单应性矩阵是由网站http://www.scipy.org/Cookbook/RANSAC上的ransac.py计算出来的"""def __init__(self,debug=False):self.debug = debugdef fit(self, data):"""计算选取的4个对应的单应性矩阵 """#将其转置,来调用H_from_points()计算单应性矩阵data = data.T# 映射的起始点fp = data[:3,:4]#映射的目标点tp = data[3:,:4]#计算单应性矩阵然后返回return H_from_points(fp,tp)def get_error( self, data, H):"""对所有的对应计算单应性矩阵,然后对每个变换后的点,返回响应的误差"""data = data.T#映射的起始点fp = data[:3]#映射的目标点tp = data[3:]# 变换fpfp_transformed = dot(H,fp)# 归一化齐次坐标fp_transformed = normalize(fp_transformed)# 返回每个点的误差return sqrt( sum((tp-fp_transformed)**2,axis=0) )def H_from_ransac(fp,tp,model,maxiter=1000,match_theshold=10):""" 使用RANSAC稳健性聚集点对应间的单应性矩阵H(ransac.py为从http://www.scipy.org/Cookbook/RANSAC下载的版本).输入: 齐次坐标表示的点fp,tp (3*n数组) """from PCV.tools import ransac# 对应点组data = vstack((fp,tp))# 计算H,并返回H,ransac_data = ransac.ransac(data.T,model,4,maxiter,match_theshold,10,return_all=True)return H,ransac_data['inliers']
切割以及拼接图像:
delta = 2000 # for padding and translationim1 = array(Image.open(imname[0]), "uint8")im2 = array(Image.open(imname[1]), "uint8")###
im_12 = warp.panorama(H_01,im1,im2,delta,delta)##
其中warp.panorama()用于图像扭曲
def panorama(H,fromim,toim,padding=2400,delta=2400):""" 使用单应性矩阵H(使用RANSAC稳健性估计得出),协调两幅图像,创建水平全景图。结果为一幅和toim具有相同高度的图像。padding指定填充像素的数目,delta指定额外的平移量""" #检查图像是灰度图像,还是彩色图像is_color = len(fromim.shape) == 3# 用于geometric_transform()的单应性变换def transf(p):p2 = dot(H,[p[0],p[1],1])return (p2[0]/p2[2],p2[1]/p2[2])if H[1,2]<0: # fromim在右边print 'warp - right'# 变换fromimif is_color:# 在目标图像的右边填充0toim_t = hstack((toim,zeros((toim.shape[0],padding,3))))fromim_t = zeros((toim.shape[0],toim.shape[1]+padding,toim.shape[2]))for col in range(3):fromim_t[:,:,col] = ndimage.geometric_transform(fromim[:,:,col],transf,(toim.shape[0],toim.shape[1]+padding))else:# 在目标图像的右边填充0toim_t = hstack((toim,zeros((toim.shape[0],padding))))fromim_t = ndimage.geometric_transform(fromim,transf,(toim.shape[0],toim.shape[1]+padding)) else:print 'warp - left'#为了补偿填充效果,在左边加入平移量H_delta = array([[1,0,0],[0,1,-delta],[0,0,1]])H = dot(H,H_delta)#fromim变换if is_color:# 在目标图像的左边填充0toim_t = hstack((zeros((toim.shape[0],padding,3)),toim))fromim_t = zeros((toim.shape[0],toim.shape[1]+padding,toim.shape[2]))for col in range(3):fromim_t[:,:,col] = ndimage.geometric_transform(fromim[:,:,col],transf,(toim.shape[0],toim.shape[1]+padding))else:# 在目标图像的左边填充0toim_t = hstack((zeros((toim.shape[0],padding)),toim))fromim_t = ndimage.geometric_transform(fromim,transf,(toim.shape[0],toim.shape[1]+padding))# 协调后返回(将fromim放在toim上)if is_color:# 所有非黑像素alpha = ((fromim_t[:,:,0] * fromim_t[:,:,1] * fromim_t[:,:,2] ) > 0)for col in range(3):toim_t[:,:,col] = fromim_t[:,:,col]*alpha + toim_t[:,:,col]*(1-alpha)else:alpha = (fromim_t > 0)toim_t = fromim_t*alpha + toim_t*(1-alpha)return toim_t
完整代码如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 23 15:36:26 2022
@author: 95490
"""from pylab import *
from numpy import *
from PIL import Image# If you have PCV installed, these imports should work
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift"""
This is the panorama example from section 3.3.
"""def read_features_from_file(filename):"""读取特征属性值,然后将其以矩阵的形式返回"""f = loadtxt(filename)return f[:,:4], f[:,4:] #特征位置,描述子def write_featrues_to_file(filename, locs, desc):"""将特征位置和描述子保存到文件中"""savetxt(filename, hstack((locs,desc)))def plot_features(im, locs, circle=False):"""显示带有特征的图像输入:im(数组图像),locs(每个特征的行、列、尺度和朝向)"""def draw_circle(c,r):t = arange(0,1.01,.01)*2*pix = r*cos(t) + c[0]y = r*sin(t) + c[1]plot(x, y, 'b', linewidth=2)imshow(im)if circle:for p in locs:draw_circle(p[:2], p[2])else:plot(locs[:,0], locs[:,1], 'ob')axis('off')def match(desc1, desc2):"""对于第一幅图像中的每个描述子,选取其在第二幅图像中的匹配输入:desc1(第一幅图像中的描述子),desc2(第二幅图像中的描述子)"""desc1 = array([d/linalg.norm(d) for d in desc1])desc2 = array([d/linalg.norm(d) for d in desc2])dist_ratio = 0.6desc1_size = desc1.shapematchscores = zeros((desc1_size[0],1),'int')desc2t = desc2.T #预先计算矩阵转置for i in range(desc1_size[0]):dotprods = dot(desc1[i,:],desc2t) #向量点乘dotprods = 0.9999*dotprods# 反余弦和反排序,返回第二幅图像中特征的索引indx = argsort(arccos(dotprods))#检查最近邻的角度是否小于dist_ratio乘以第二近邻的角度if arccos(dotprods)[indx[0]] < dist_ratio * arccos(dotprods)[indx[1]]:matchscores[i] = int(indx[0])return matchscoresdef match_twosided(desc1, desc2):"""双向对称版本的match()"""matches_12 = match(desc1, desc2)matches_21 = match(desc2, desc1)ndx_12 = matches_12.nonzero()[0]# 去除不对称的匹配for n in ndx_12:if matches_21[int(matches_12[n])] != n:matches_12[n] = 0return matches_12def appendimages(im1, im2):"""返回将两幅图像并排拼接成的一幅新图像"""#选取具有最少行数的图像,然后填充足够的空行rows1 = im1.shape[0]rows2 = im2.shape[0]if rows1 < rows2:im1 = concatenate((im1, zeros((rows2-rows1,im1.shape[1]))),axis=0)elif rows1 >rows2:im2 = concatenate((im2, zeros((rows1-rows2,im2.shape[1]))),axis=0)return concatenate((im1,im2), axis=1)def plot_matches(im1,im2,locs1,locs2,matchscores,show_below=True):""" 显示一幅带有连接匹配之间连线的图片输入:im1, im2(数组图像), locs1,locs2(特征位置),matchscores(match()的输出),show_below(如果图像应该显示在匹配的下方)"""im3=appendimages(im1,im2)if show_below:im3=vstack((im3,im3))plt.figure(figsize=(20, 10))imshow(im3)cols1 = im1.shape[1]for i in range(len(matchscores)):if matchscores[i]>0:plot([locs1[i,0],locs2[matchscores[i,0],0]+cols1], [locs1[i,1],locs2[matchscores[i,0],1]],'c')axis('off')# set paths to data folder
featname = ['out_sift_'+str(i+1)+'.txt' for i in range(2)]
imname = [str(i+1)+'.jpg' for i in range(2)]# extract features and match
l = {}
d = {}l[0],d[0] = read_features_from_file(featname[0])
l[1],d[1] = read_features_from_file(featname[1])
matches = {}matches[0] = match_twosided(d[1], d[0])### visualize the matches (Figure 3-11 in the book)im1 = array(Image.open(imname[0]))
im2 = array(Image.open(imname[1]))
#figure()
#plot_matches(im2,im1,l[1],l[0],matches[0],show_below=True)# function to convert the matches to hom. points
def convert_points(j):ndx = matches[j].nonzero()[0]fp = homography.make_homog(l[j+1][ndx,:2].T) ndx2 = [int(matches[j][i]) for i in ndx]tp = homography.make_homog(l[j][ndx2,:2].T) # switch x and y - TODO this should move elsewherefp = vstack([fp[1],fp[0],fp[2]])tp = vstack([tp[1],tp[0],tp[2]])return fp,tp# estimate the homographies
model = homography.RansacModel() fp,tp = convert_points(0)
H_01 = homography.H_from_ransac(fp,tp,model)[0] #im 0 to 1 # warp the images
delta = 2000 # for padding and translationim1 = array(Image.open(imname[0]), "uint8")im2 = array(Image.open(imname[1]), "uint8")###
im_12 = warp.panorama(H_01,im1,im2,delta,delta)##figure()
imshow(array(im_12, "uint8"))
axis('off')
show()
运行结果:
2.PTGUI
PTGUI是一款功能强大的全景图片拼接软件,其五个字母来自于Panorama Tools Graphical User Interface。从1996年至今(2022)已经升级到第12版了。
使用PTGui可以快捷方便地制作出360X180度的“完整球型全景图片”(Full spherical panorama),其工作流程非常简便:1、导入一组原始底片;2、运行自动对齐控制点;3、生成并保存全景图片文件。
软件能自动读取底片的镜头参数,识别图片重叠区域的像素特征,然后以“控制点”(control point)的形式进行自动缝合,并进行优化融合。在软件的全景图片编辑器有更丰富的功能,支持多种视图的映射方式,用户也可以手工添加或删除控制点,从而提高拼接的精度。软件支持多种格式的图像文件输入,输出可以选择为高动态范围的图像,拼接后的图像明暗度均一,基本上没有明显的拼接痕迹。软件提供Windows和MAC版本。
2.1导入原始底片
点击加载影像
选择相机参数
2.2对齐影像
点击对齐影像自动对齐控制点,可以预览对齐后效果,效果不好时可以手动添加控制点进行优化。
2.3创建全景
创建全景后即可导出全景图
运行结果:
图像拼接(SIFT与PTGUI)相关推荐
- 图像拼接 SIFT资料合集
最近也注意一些图像拼接方面的文章,很多很多,尤其是全景图拼接的,实际上类似佳能相机附加的软件,好多具备全景图拼接,多幅图像自动软件实现拼接,构成(合成)一幅全景图像(风景). Sift算法,我略知一二 ...
- ADAS辅助驾驶_自动驾驶_技术点列表
0 ADAS 汽车电子及ADAS安全部分相关测试标准 汽车总线特性简述 短距离车间通信V2X简述 先进驾驶辅助系统ADAS接口协议ADASIS v2简介 ADAS在车载导航设备上的应用 先进驾驶辅助系 ...
- (三)计算机视觉 --SIFT特征匹配、地理标记图像匹配及RANSAC图像拼接
目录 一.sift特征检测概述 1.1特征点 1.2sift特征检测 二.sift特征提取与匹配 2.1特征提取并展示 2.2对两张图片进行特征匹配计算 2.3给定一张图片,输出与其匹配最多的三张图片 ...
- 图像拼接领域的经典文章以及常用的算法函数(一)(SIFT,APAP,AANAP,Seam-sutting,HomographyNet等等)
1.特征点的匹配 首先进行SIFT的特征点的匹配: SIFT特征点的提取与匹配是图像拼接的第一步,如何提取到有效的,鲁棒性强的特征点是接下来的工作的重要前提,搞清楚什么是尺度空间极值检测.关键点定位. ...
- 基于SIFT特征的图像拼接融合(matlab+vlfeat实现)
基于SIFT特征的图像拼接融合(matlab+vlfeat实现) piccolo,之前做的东西,简单整理下,不是做图像方向的,写的不好轻喷 主要原理参看SIFT算法详解和SIFT特征匹配算法介绍--寻 ...
- 计算机视觉3 SIFT特征提取与全景图像拼接
1.原理 检测并提取图像的特征和关键点 匹配两个图像之间的描述符 使用RANSAC算法使用我们匹配的特征向量估计单应矩阵 拼接图像 步骤一和步骤二过程是运用SIFT局部描述算子检测图像中的关键点和特征 ...
- 基于sift特征点的图像拼接
最近学习了图像拼接的一些知识,在这里记录一下,方便以后的学习, 博客中的代码均基于python,目前只能用于左右拼接 基于sift特征点的图像拼接包括以下几个步骤: 1.sift特征点的提取 2.利用 ...
- 图像拼接(一)——SIFT算法新手入门级介绍!!!
前言 因为博主毕设和图像拼接有关,所以博主简单地学习了SIFT算法,不知道以后研究生会不会接触图像这块,顺便也为了检验一下自己学的是否透彻,所以在CSDN上写一篇Blog,纪录一下,仅供各位小伙伴参考 ...
- 基于SIFT特征的全景图像拼接
主要分为以下几个步骤: (1) 读入两张图片并分别提取SIFT特征 (2) 利用k-d tree和BBF算法进行特征匹配查找 (3) 利用RANSAC算法筛选匹配点并计算变换矩阵 (3) 图像融合 S ...
最新文章
- 【连载】优秀程序员的 45 个习惯之习惯27
- English Speech-Graduation from University
- java简述会话对象的生命周期_简述Java Web三大作用域对象
- 实践 | kafka 基本使用
- 2020年中职学计算机有前途吗,2020年南昌中专计算机专业都学什么
- PyQt4设置窗口左上角的小图标
- mac 关闭 mysqld 进程(亲测可用)
- 时间数值缺失产生的字符串NaT处理
- 构建AD域 、 管理AD域
- Oracle 根据dbf文件的数据恢复
- 并发测试工具(ubuntu 16.04)
- leapFTP 使用笔记
- annot keep settings in the secure 或WRITE_SETTINGS not granted
- 如何给华硕笔记本在光驱位加装另一块linux系统固态硬盘?
- 支付宝php40247,支付宝APP支付 显示 系统繁忙 请稍后再试 ALI40247
- TokenInsight 对话首席——隐私安全计算,价值几何?
- 三元组顺序表表示的稀疏矩阵转置(10分)
- RabbitMQ如何保证消息的可靠性
- IE 兼容性问题记录
- FortiGate抓包 Sniffer
热门文章
- 数据结构——带头结点双向循环链表
- arXiv journal 2022.0311
- 【理论学习】什么是技术栈?
- 牛客小白月赛61 F.选座椅(双指针)
- matlab 电路频率响应_你不知道的这12个细节,正毁掉你的电路...
- 一窥社交媒体中的档案学
- 社区升级改造,智慧社区解决方案解读
- 不能连接MySQL服务主机3306_解决centos的mysql服务3306端口无法远程连接10038问题
- [从头学绘画] 第16节 六十四式八卦掌 (33-40)
- 2018年8月草原计划