• OpenCV完美实现两张图片的全景拼接(详细教程)


    目录

    1,主要步骤

    1.1  导入需要的包和模块,并读取两张待拼接的图片,这里我们假设它们为 left.jpg 和 right.jpg。

    1.2  创建SIFT检测器

    1.3 创建一个基于 FLANN 的匹配器

    1.4  筛选过程删除掉一些不合适的匹配点,只保留最好的匹配点

    1.5透视变换

    1.6  消除重叠的效果,对两张图片进行加权处理

    2,代码展示

    3,效果展示


    应用场景主要有两个方面:

    1. 风景或建筑物的拍摄

    对于一些风景或建筑物的拍摄,有时候需要的画面宽度超出了单张图片所能提供的视野范围。这时可以通过拍摄多张图片并将它们拼接成一张更加宽阔的全景图来达到所需的效果。

    1. 科学研究

    在一些科学研究中,需要对一定的区域进行高精度测量,例如地形测量、海洋测量等。这时候就需要一些宽视野相机来实现拍摄。但是,由于一张图片所能覆盖的区域有限,因此通常还需要将多张图片拼接成一张更大的全景图像,方便科学家们进行研究和分析。

    1,主要步骤

    1. 读入待拼接的图片并调整大小;
    2. 使用 SIFT 或 SURF 算法提取图片的关键点和描述符;
    3. 使用基于 FLANN 的匹配器进行关键点匹配,并筛选出较好的匹配点;
    4. 计算视角变换矩阵,并使用透视变换对右边的图片进行变换;
    5. 消除重叠的效果,对两张图片进行加权处理;
    6. 输出拼接后的结果。

    1.1  导入需要的包和模块,并读取两张待拼接的图片,这里我们假设它们为 left.jpgright.jpg

    左视图:

    右视图:

    1.2  创建SIFT检测器

    cv2.xfeatures2d.SIFT_create() 创建一个 SIFT 检测器

    也可以选择使用 cv2.SIFT_create()

    不过前者是更新的版本,可能会更好一些

    然后,在两张图片上分别使用这个检测器进行关键点检测和特征提取,获得关键点集合和描述符集合。

    1. surf=cv2.xfeatures2d.SIFT_create()#可以改为SIFT
    2. #sift = cv2.SIFT_create()
    3. sift = cv2.xfeatures2d.SIFT_create()
    4. kp1,descrip1 = sift.detectAndCompute(imageA,None)
    5. kp2,descrip2 = sift.detectAndCompute(imageB,None)

    1.3 创建一个基于 FLANN 的匹配器

    调用 cv2.FlannBasedMatcher() 创建一个基于 FLANN 的匹配器,并使用 knnMatch() 处理两张图片的特征描述符,得到最佳匹配。

    1. indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
    2. searchParams = dict(checks=50)
    3. flann=cv2.FlannBasedMatcher(indexParams,searchParams)
    4. match=flann.knnMatch(descrip1,descrip2,k=2)
    5. good=[]

    1.4  筛选过程删除掉一些不合适的匹配点,只保留最好的匹配点

    1. for i,(m,n) in enumerate(match):
    2. if(m.distance<0.75*n.distance):
    3. good.append(m)

    1.5透视变换

    判断满足条件的匹配点数量是否大于阈值 MIN,如果大于,则进行视角变换矩阵的计算,将右边的图片 imageB 对其进行透视变换,得到 warpImg

    1. if len(good) > MIN:
    2. src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
    3. ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
    4. M,mask = cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
    5. warpImg = cv2.warpPerspective(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))
    6. direct=warpImg.copy()
    7. direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageA
    8. simple=time.time()
    9. show('res',warpImg)

    同时,将左边的图片覆盖在变换后的图片上,得到 direct。最后,显示结果。

    1. print(rows)
    2. print(cols)
    3. for col in range(0,cols):
    4. # 开始重叠的最左端
    5. if imageA[:, col].any() and warpImg[:, col].any():
    6. left = col
    7. print(left)
    8. break
    9. for col in range(cols-1, 0, -1):
    10. #重叠的最右一列
    11. if imageA[:, col].any() and warpImg[:, col].any():
    12. right = col
    13. print(right)
    14. break

    1.6  消除重叠的效果,对两张图片进行加权处理

    根据图片相对位置的不同,左边的图片和右边的图片有可能会在某些列出现重叠部分,为了消除这种不自然的效果,需要实现像素级的混合。首先找到左右图片开始重叠的位置和结束的位置,然后对两张图片进行加权处理,最后将加权后的图片输出。

    1. #加权处理
    2. res = np.zeros([rows, cols, 3], np.uint8)
    3. for row in range(0, rows):
    4. for col in range(0, cols):
    5. if not imageA[row, col].any(): # 如果没有原图,用旋转的填充
    6. res[row, col] = warpImg[row, col]
    7. elif not warpImg[row, col].any():
    8. res[row, col] = imageA[row, col]
    9. else:
    10. srcImgLen = float(abs(col - left))
    11. testImgLen = float(abs(col - right))
    12. alpha = srcImgLen / (srcImgLen + testImgLen)
    13. res[row, col] = np.clip(imageA[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)
    14. warpImg[0:imageA.shape[0], 0:imageA.shape[1]]=res
    15. show('res',warpImg)
    16. final=time.time()
    17. print(final-starttime)

    2,代码展示

    1. import cv2
    2. import numpy as np
    3. from matplotlib import pyplot as plt
    4. import time
    5. def show(name,img):
    6. cv2.imshow(name, img)
    7. cv2.waitKey(0)
    8. cv2.destroyAllWindows()
    9. MIN = 10
    10. FLANN_INDEX_KDTREE = 0
    11. starttime = time.time()
    12. img1 = cv2.imread('left.jpg') #query
    13. img2 = cv2.imread('right.jpg') #train
    14. imageA = cv2.resize(img1,(0,0),fx=0.2,fy=0.2)
    15. imageB = cv2.resize(img2,(0,0),fx=0.2,fy=0.2)
    16. surf=cv2.xfeatures2d.SIFT_create()#可以改为SIFT
    17. #sift = cv2.SIFT_create()
    18. sift = cv2.xfeatures2d.SIFT_create()
    19. kp1,descrip1 = sift.detectAndCompute(imageA,None)
    20. kp2,descrip2 = sift.detectAndCompute(imageB,None)
    21. #创建字典
    22. indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
    23. searchParams = dict(checks=50)
    24. flann=cv2.FlannBasedMatcher(indexParams,searchParams)
    25. match=flann.knnMatch(descrip1,descrip2,k=2)
    26. good=[]
    27. #过滤特征点
    28. for i,(m,n) in enumerate(match):
    29. if(m.distance<0.75*n.distance):
    30. good.append(m)
    31. # 当筛选后的匹配对大于10时,计算视角变换矩阵
    32. if len(good) > MIN:
    33. src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
    34. ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
    35. M,mask = cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
    36. warpImg = cv2.warpPerspective(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))
    37. direct=warpImg.copy()
    38. direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageA
    39. simple=time.time()
    40. show('res',warpImg)
    41. rows,cols=imageA.shape[:2]
    42. print(rows)
    43. print(cols)
    44. for col in range(0,cols):
    45. # 开始重叠的最左端
    46. if imageA[:, col].any() and warpImg[:, col].any():
    47. left = col
    48. print(left)
    49. break
    50. for col in range(cols-1, 0, -1):
    51. #重叠的最右一列
    52. if imageA[:, col].any() and warpImg[:, col].any():
    53. right = col
    54. print(right)
    55. break
    56. #加权处理
    57. res = np.zeros([rows, cols, 3], np.uint8)
    58. for row in range(0, rows):
    59. for col in range(0, cols):
    60. if not imageA[row, col].any(): # 如果没有原图,用旋转的填充
    61. res[row, col] = warpImg[row, col]
    62. elif not warpImg[row, col].any():
    63. res[row, col] = imageA[row, col]
    64. else:
    65. srcImgLen = float(abs(col - left))
    66. testImgLen = float(abs(col - right))
    67. alpha = srcImgLen / (srcImgLen + testImgLen)
    68. res[row, col] = np.clip(imageA[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)
    69. warpImg[0:imageA.shape[0], 0:imageA.shape[1]]=res
    70. show('res',warpImg)
    71. final=time.time()
    72. print(final-starttime)

    3,效果展示

  • 相关阅读:
    【鸿蒙软件开发】ArkTS基础组件之Marquee(文字跑马灯)、QRCode(二维码生成)
    Java-1201
    音视频从入门到精通——FFmpeg数据结构分析
    跨模态检索论文阅读:(PTP)Position-guided Text Prompt for Vision-Language Pre-training
    Jammy@Jetson Orin - Tensorflow & Keras Get Started: Concept
    如何结合phpstorm配置在docker中的xdebug
    SpringSecurity系列一:04 SpringSecurity 的默认用户是如何生成的?
    MySQL版数据库原理与应用期末复习重点(3)---画E-R图
    红黑树介绍
    Redis常用知识笔记(超全面!超详细!)收藏这一篇就够了
  • 原文地址:https://blog.csdn.net/qq_53545309/article/details/133848143