图像拼接—SIFT+Flann匹配+估计单应矩阵—(全景图panorama)

17 篇文章 1 订阅
6 篇文章 0 订阅

开发环境:python+opencv3
笔者拼接图像的步骤如下:
step1: 利用特征算子检测并描述,常见的特征算子在cv2.xfeatures2中都有,比如角点harris,斑点surf,sift等,这些算子的原理不再过多阐述。
step1:对于描述子,要进行匹配,在cv2中匹配可以是暴力匹配也可以选择flann,这是优化过的匹配算法,至于暴力匹配就是sift 的作者lowe 提出的NNDR方法,遍历所有点。
step3: 对于匹配点,我们首先要检查是否存在误点,可以利用基本矩阵(即极线约束的方式来剔除),在笔者源码中,没有这一步。对于提纯后的同名点,我们可以估计一个单应矩阵,即点对点的变换方式(如果h33=1),那么单应矩阵也可称为“投影矩阵”,根据《计算机视觉在多视图几何中的应用》那本书可以知道,投影矩阵具有保线性,即可以消除射影失真(影像上的平行线在看起来不平行,它们都有消影点(vanishing point))。单应矩阵常用语拼接。->基本矩阵和单应矩阵的区别见笔者之前的文章。
step 4: 利用opencv 中的warpPerspective 函数来实现全景图。
下面附上代码和效果图:(代码都加了注释)

# -*- coding: utf-8 -*-
#Panorama Stitcher 
import cv2
import numpy as np
class macthing(object):
    def matchIMG(self,im1,im2,kp1,kp2,des1,des2):
        FLANN_INDEX_KDTREE=0
        index_p=dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
        searth_p=dict(checks=50)
        flann=cv2.FlannBasedMatcher(index_p,searth_p)
        matches=flann.knnMatch(des1,des2,k=2)
        good =[]
        pts1=[]
        pts2=[]
        for i,(m,n) in enumerate(matches):
            if m.distance<0.6*n.distance:
                good.append(m)
                pts1.append(kp1[m.queryIdx].pt)
                pts2.append(kp2[m.trainIdx].pt)
        pts1=np.float32(pts1)
        pts2=np.float32(pts2)
        H,mask=cv2.findHomography(pts1,pts2,cv2.RANSAC,0.01)
        pts1_1=pts1[mask.ravel()==1]
        pts2_2=pts2[mask.ravel()==1]
        return pts1_1,pts2_2,H
    def appendimage(self,im1,im2):
        r1=im1.shape[0]
        r2=im2.shape[0]
        if r1<r2:
            img=np.zeros((r2-r1,im1.shape[1]),np.uint8)
            im1_1=np.vstack((im1,img))
            im3=np.hstack((im1_1,im2))
        else:
            img=np.zeros((r1-r2,im2.shape[1]),np.uint8)
            im2_2=np.vstack((im2,img))
            im3=np.hstack((im1,im2_2))
        return im3 
#    def panorama_get(self,im1,im2,H):
#        if im1.shape[0]>=im2.shape[0]:
#            result=cv2.warpPerspective(im1,H,(im1.shape[1]+im2.shape[1],im1.shape[0]))
#            result[0:im2.shape[0],0:im2.shape[1]]=im2
#        else:
#            result=cv2.warpPerspective(im1,H,(im1.shape[1]+im2.shape[1],im2.shape[0]))
#            result[0:im2.shape[0],0:im2.shape[1]]=im2
#        return result
    def panorama_get(self,im1,im2,H):
        h1,w1=im1.shape[:2]
        h2,w2=im2.shape[:2]
        pts1=np.float32([[0,0],[0,h1],[w1,h1],[w1,0]]).reshape(-1,1,2)#转为3维坐标
        pts2=np.float32([[0,0],[0,h2],[w2,h2],[w2,0]]).reshape(-1,1,2)
        pts1_=cv2.perspectiveTransform(pts1,H)#H:3*3 矩阵,所以pts1也该为3维坐标
        pts=np.concatenate((pts1_,pts2),axis=0)#列连接
          #np.min 是行优先
        [xmin,ymin]=np.int32(pts.min(axis=0).ravel()-0.5)
        [xmax,ymax]=np.int32(pts.max(axis=0).ravel()+0.5)
        t=[-xmin,-ymin]# 左加右减
        Ht = np.array([[1,0,t[0]],[0,1,t[1]],[0,0,1]]) #相当于一个向右平移
        result = cv2.warpPerspective(im1, Ht.dot(H), (xmax-xmin, ymax-ymin))#最后一个参数是输出图像的宽、高
        result[t[1]:h2+t[1],t[0]:w2+t[0]] = im2
        return result
if __name__=="__main__":
    M=macthing()
    im1_=cv2.imread(r"C:\Users\Y\Desktop\3.jpg")
    im2_=cv2.imread(r"C:\Users\Y\Desktop\4.jpg")
    im1=cv2.cvtColor(im1_,cv2.COLOR_BGR2GRAY)
    im2=cv2.cvtColor(im2_,cv2.COLOR_BGR2GRAY)
    sift=cv2.xfeatures2d.SIFT_create()
    kp1,des1=sift.detectAndCompute(im1,None)
    kp2,des2=sift.detectAndCompute(im2,None)
    pts1_1,pts2_2,H=M.matchIMG(im1,im2,kp1,kp2,des1,des2)
    im3=M.appendimage(im1,im2)
    pts2_new=pts2_2.copy()
    for i in range(len(pts2_2)):
        pts2_new[i,0]=pts2_new[i,0]+np.float32(im1.shape[1])
    for i in range(len(pts1_1)):
        cv2.line(im3,tuple(pts1_1[i]),tuple(pts2_new[i]),255,2)
#    cv  cv2\Y\Desktop\45.jpg",result)
    cv2.namedWindow("panorma ,cv2.WINDOW_NORMAL)
    cv2.imshow("panorama",result),cv2.waitKey(0)

_]这里写图片描述_]![这里写图片描述UI

这里提供一个基于OpenCV实现的sift+FLANN+ransac+加权融合算法的代码,代码中使用了两幅图像进行拼接。需要注意的是,这段代码仅供参考,具体实现需要根据实际情况进行调整和改进。 ```python import cv2 import numpy as np # 读入两幅图片 img1 = cv2.imread('img1.jpg') img2 = cv2.imread('img2.jpg') # 获取图片大小 rows1, cols1 = img1.shape[:2] rows2, cols2 = img2.shape[:2] # 使用sift算法提取特征点和特征描述子 sift = cv2.xfeatures2d.SIFT_create() keypoints1, descriptors1 = sift.detectAndCompute(img1, None) keypoints2, descriptors2 = sift.detectAndCompute(img2, None) # 使用FLANN算法进行特征点匹配 FLANN_INDEX_KDTREE = 0 index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(descriptors1, descriptors2, k=2) # 进行ransac过滤 good_matches = [] for m, n in matches: if m.distance < 0.7 * n.distance: good_matches.append(m) src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) # 计算拼接后的图像大小 h, w = img1.shape[:2] pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2) dst = cv2.perspectiveTransform(pts, M) dst += (cols1, 0) # 进行图像拼接 result = np.zeros((max(rows1, rows2), cols1 + cols2, 3), dtype=np.uint8) result[:rows1, :cols1, :] = img1 result[:rows2, cols1:, :] = img2 # 加权融合 for i in range(dst.shape[0]): x, y = dst[i][0] x, y = int(x), int(y) if 0 <= x < cols1 + cols2 and 0 <= y < max(rows1, rows2): if x < cols1: result[y, x, :] = img1[y, x, :] elif x >= cols1: result[y, x, :] = img2[y, x - cols1, :] else: result[y, x, :] = (img1[y, x, :] + img2[y, x - cols1, :]) / 2 # 显示拼接后的图像 cv2.imshow('result', result) cv2.waitKey(0) cv2.destroyAllWindows() ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值