opencv Stitcher多图拼接

举报
风吹稻花香 发表于 2021/06/05 22:37:05 2021/06/05
【摘要】   Opencv使用Stitcher类图像拼接生成全景图像   Opencv中自带的Stitcher类可以实现全景图像,效果不错。下边的例子是Opencv Samples中的stitching.cpp的简化,源文件可以在这个路径里找到: \opencv\sources\samples\cpp\stitching.cpp ? 1 2 3 4 5 ...

 

Opencv使用Stitcher类图像拼接生成全景图像

 

Opencv中自带的Stitcher类可以实现全景图像,效果不错。下边的例子是Opencv Samples中的stitching.cpp的简化,源文件可以在这个路径里找到:
\opencv\sources\samples\cpp\stitching.cpp

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

#include <fstream>

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/stitching/stitcher.hpp"

#include <iostream>

  

using namespace cv;

using namespace std;

  

vector<Mat> imgs; //保存拼接的原始图像向量

  

//导入所有原始拼接图像函数

void parseCmdArgs(int argc, char** argv);

  

int main(int argc, char* argv[])

{

  //导入拼接图像

  parseCmdArgs(argc, argv);  

  Mat pano;

  Stitcher stitcher = Stitcher::createDefault(false);

  Stitcher::Status status = stitcher.stitch(imgs, pano);//拼接

  if (status != Stitcher::OK) //判断拼接是否成功

  {

    cout << "Can't stitch images, error code = " << int(status) << endl;

    return -1;

  }

  namedWindow("全景拼接",0);

  imshow("全景拼接",pano);

  imwrite("D:\\全景拼接.jpg",pano);

  waitKey();  

  return 0;

}

  

//导入所有原始拼接图像函数

void parseCmdArgs(int argc, char** argv)

{

  for(int i=1;i<argc;i++)

  {

    Mat img = imread(argv[i]);

    if (img.empty())

    {

      cout << "Can't read image '" << argv[i] << "'\n";

    }

    imgs.push_back(img);

  }

}

图1:

图2:

图3:

图4:

图5:

5个图片的拍摄角度合起来在180°左右,没有经过压缩的,下载下来可以直接测试使用,传入顺序随意,Stitcher会自动排列。全景拼接效果很赞:

 

下面是python的:

一、拼接介绍

在同一位置拍摄的两幅或多幅图像是单应性相关的。我们可以使用该约束将很多图像拼接起来,拼成一幅大的图像来创建全景图像
全景图像拼接最重要的两个步骤就是:

(一)特征点匹配
这部分主要采用SIFT算法实现,之前的博客有介绍就不再详细介绍了,主要是为了找到两幅图像相同的特征点并将其进行匹配。
(二)图片匹配
图片匹配就是找到图像之间所有重叠的部分,将其拼接后就能得到一幅全景图。

基本原理

1.单应性矩阵
定义:在计算机视觉领域,空间同一平面的任意两幅图像被单应矩阵联系着(假设在针孔相机模型中),即一个相机拍摄空间同一平面的两张图像,这两张图像之间的映射关系可以用单应矩阵表示。
在两视几何中,也可以这样理解,两架相机拍同一空间上得到两幅图像A、B,其中图像A到图像B存在一种变换,而且这种变换是一一对应的关系,这个变换矩阵用单应矩阵表示。OpenCV中可以用函数findHomography计算得到单应矩阵H。
要实现两张图片的简单拼接,其实只需找出两张图片中相似的点 (至少四个,因为 homography 矩阵的计算需要至少四个点), 计算一张图片可以变换到另一张图片的变换矩阵 (homography 单应性矩阵),用这个矩阵把那张图片变换后放到另一张图片相应的位置 ( 就是相当于把两张图片中定好的四个相似的点給重合在一起)。如此,就可以实现简单的全景拼接。当然,因为拼合之后图片会重叠在一起,所以需要重新计算图片重叠部分的像素值,否则结果会很难看。

2.RANSAC算法
RANSAC(Random Sample Consensus)即随机采样一致性,该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵,RANSAC的基本思想在于,找到正确数据点的同时摒弃噪声点。

3.利用RANSAC算法求解单应性矩阵
虽然SIFT是具有很强稳健性的描述子,当这方法仍远非完美,还会存在一些错误的匹配。而单应性矩阵需要选取4对特征点计算,万一选中了不正确的匹配点,那么计算的单应性矩阵肯定是不正确的。因此,为了提高计算结果的鲁棒性,我们下一步就是要把这些不正确的匹配点给剔除掉,获得正确的单应性矩阵。在这里使用了RANSAC算法:随机抽取不同的4对特征匹配坐标(在图1中随机抽取4个特征坐标,以及这4个特征坐标在图2中匹配的4个特征坐标,组成4对特征匹配坐标),利用这4对特征匹配坐标计算出矩阵H1(3x3的一个矩阵,图2经过矩阵变换后,可以把图2映射到图1的坐标空间中,再将图2进行简单的平移即可与图1实现无缝拼接),再将图2中所有特征匹配点经过该透视矩阵H1映射到图1的坐标空间,然后与图1匹配点实际坐标求欧氏距离(就是为了验证计算出来的这个H1矩阵是否满足绝大多数特征匹配点);之后重复上面内容,再随机抽取不同的四组特征匹配坐标,再计算透视矩阵H2,再求欧式距离,如此重复多次。最后以欧式距离最小的那个透视矩阵(表示这个特征矩阵H满足最多的特征匹配点,它最优秀)作为最终计算结果。
4.图片融合
在用计算出的变换矩阵对其中一张图做变换,然后把变换的图片与另一张图片重叠在一起,并重新计算重叠区域新的像素值。对于计算重叠区域的像素值,其实可以有多种方法去实现一个好的融合效果,这里就用最简单粗暴的但效果也不错的方式。直白来说就是实现一个图像的线性渐变,对于重叠的区域,靠近左边的部分,让左边图像内容显示的多一些,靠近右边的部分,让右边图像的内容显示的多一些。用公式表示就是,假设 alpha 表示像素点横坐标到左右重叠区域边界横坐标的距离,新的像素值就为 newpixel = 左图像素值 × (1 - alpha) + 右图像素值 × alpha 。这样就可以实现一个简单的融合效果。

2代码及运行结果


  
  1. from pylab import *
  2. from numpy import *
  3. from PIL import Image
  4. # If you have PCV installed, these imports should work
  5. from PCV.geometry import homography, warp
  6. from PCV.localdescriptors import sift
  7. """
  8. This is the panorama example from section 3.3.
  9. """
  10. # set paths to data folder
  11. featname = ['C:\\Users\DELL\Desktop\PCV\jmu\panorama/z0'+str(i+1)+'.sift' for i in range(5)]
  12. imname = ['C:\\Users\DELL\Desktop\PCV\jmu\panorama/z0'+str(i+1)+'.jpg' for i in range(5)]
  13. # extract features and match
  14. l = {}
  15. d = {}
  16. for i in range(5):
  17. sift.process_image(imname[i],featname[i])
  18. l[i],d[i] = sift.read_features_from_file(featname[i])
  19. matches = {}
  20. for i in range(4):
  21. matches[i] = sift.match(d[i+1],d[i])
  22. # visualize the matches (Figure 3-11 in the book)
  23. '''
  24. for i in range(4):
  25. im1 = array(Image.open(imname[i]))
  26. im2 = array(Image.open(imname[i+1]))
  27. figure()
  28. sift.plot_matches(im2,im1,l[i+1],l[i],matches[i],show_below=True)
  29. '''
  30. # function to convert the matches to hom. points
  31. def convert_points(j):
  32. ndx = matches[j].nonzero()[0]
  33. fp = homography.make_homog(l[j+1][ndx,:2].T)
  34. ndx2 = [int(matches[j][i]) for i in ndx]
  35. tp = homography.make_homog(l[j][ndx2,:2].T)
  36. # switch x and y - TODO this should move elsewhere
  37. fp = vstack([fp[1],fp[0],fp[2]])
  38. tp = vstack([tp[1],tp[0],tp[2]])
  39. return fp,tp
  40. # estimate the homographies
  41. model = homography.RansacModel()
  42. fp,tp = convert_points(1)
  43. H_12 = homography.H_from_ransac(fp,tp,model)[0] #im 1 to 2
  44. fp,tp = convert_points(0)
  45. H_01 = homography.H_from_ransac(fp,tp,model)[0] #im 0 to 1
  46. tp,fp = convert_points(2) #NB: reverse order
  47. H_32 = homography.H_from_ransac(fp,tp,model)[0] #im 3 to 2
  48. tp,fp = convert_points(3) #NB: reverse order
  49. H_43 = homography.H_from_ransac(fp,tp,model)[0] #im 4 to 3
  50. # warp the images
  51. delta = 500 # for padding and translation
  52. im1 = array(Image.open(imname[1]), "uint8")
  53. im2 = array(Image.open(imname[2]), "uint8")
  54. im_12 = warp.panorama(H_12,im1,im2,delta,delta)
  55. im1 = array(Image.open(imname[0]), "f")
  56. im_02 = warp.panorama(dot(H_12,H_01),im1,im_12,delta,delta)
  57. im1 = array(Image.open(imname[3]), "f")
  58. im_32 = warp.panorama(H_32,im1,im_02,delta,delta)
  59. im1 = array(Image.open(imname[4]), "f")
  60. im_42 = warp.panorama(dot(H_32,H_43),im1,im_32,delta,2*delta)
  61. figure()
  62. imshow(array(im_42, "uint8"))
  63. axis('off')
  64. show()

在这里插入图片描述
在这里插入图片描述
因为相机和光照强度的差异,会造成一幅图像内部,以及图像之间亮度的不均匀,拼接后的图像会出现明暗交替,这样给观察造成极大的不便。室外场景的拼接效果比室内的要好很多,多图拼接对图片的要求也比较高,差异性大或太小(几乎相同)的拼接效果都很差。

文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/95182542

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。