opencv实战---调用stitcher类进行全景拼接和黑边处理

目录

stitcher优缺点:

相关库:

导入图片:

调用stitcher类进行拼接:

stitch:

全景拼接结果:

提取黑边轮廓:

copyMakeBorder:

threshold:

计算最大的轮廓边界:

findContours:

boundingRect:

cv2.rectangle:

寻找全景图内部最大的矩形区域:

cv2.countNonZero():

cv2.subtract():

寻找这个矩形框的轮廓,最后进行裁剪:

 完整代码:

注意:


stitcher优缺点:

优点:适应部分倾斜/尺度变换和畸变情形,拼接效果好,使用简单,可以一次拼接多张图片。

缺点:需要有足够的相同特征区域进行匹配,速度较慢(和图像大小有关)。

软件:pycharm     解释器:python3.7

相关库:

import cv2     # opencv-python==4.2.0.34
import os
import numpy as np    #numpy==1.21.6

导入图片:

mainFolder = 'images'
# 遍历images文件夹下的所有文件夹
myFolders = os.listdir(mainFolder)
# 输出所有子文件夹的名称
print(myFolders)

# 准备遍历每个子文件夹下的图片
for folder in myFolders:
    path = mainFolder + '/' + folder
    images = []
    # 遍历子文件夹下的图片名称
    myList = os.listdir(path)
    # 输出子文件内有多少张图片
    print(f'total number of images detected {len(myList)}')
    # 读取每张图片
    for imgName in myList:
        curImg = cv2.imread(f'{path}/{imgName}')
        curImg = cv2.resize(curImg,(0,0),None,0.2,0.2)  # 重新设置图片大小
        images.append(curImg)  # 将每个子文件夹下的图片存进images数组中

调用stitcher类进行拼接:

stitcher = cv2.Stitcher.create()  # 创建一个stitcher实例     
(status, result) = stitcher.stitch(images)   
    if (status == 0):       # 状态码为0的时候,进行处理
        print('Panorma Generated')
        cv2.imshow(folder, result)
    else:
        print('Panorma Generated Unsuccessful')
cv2.waitkey(0)

stitch:

作用:拼接指定的图像,images格式都为array。

输出:status退出状态码,为0代表正常;result为最终的全景图。

全景拼接结果:

原图:

 

 拼接后:

 由于两张原图的大小不一致,导致拼接图会出现黑边。接下来进行黑边处理步骤:

提取黑边轮廓:

stitched = cv2.copyMakeBorder(result, 10, 10, 10, 10, cv2.BORDER_CONSTANT, (0,0,0))
gray = cv2.cvtColor(stitched, cv2.COLOR_BGR2GRAY)    #图片灰度化
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]   

copyMakeBorder:

作用:扩充边界

cv2.copyMakeBorder(result, 10, 10, 10, 10, cv2.BORDER_CONSTANT, (0,0,0)):让全景图的上下左右分别扩充10像素的黑色边框。

threshold:

作用:灰度大于某个数值像素点保留

cv2.threshold(src, thresh, maxval, type[, dst]),返回值为retval, dst。

参数:src是灰度图像;thresh是阈值;maxval是最大值;type是定义如何处理数据与阈值的关系。

返回值:第一个返回的是输入的thresh值;第二个返回的是处理后的二值化图像。

在代码cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)中,如果像素值大于0的,则像素变为白色(255),其他像素值位置变为黑色(0)。图像像素值范围为[0,255],因此,除了黑色区域,其他部位全为白色。

计算最大的轮廓边界:

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
mask = np.zeros(thresh.shape, dtype="uint8") # 生成一个全黑的、大小和黑白图一致的蒙版
(x, y, w, h) = cv2.boundingRect(cnts[0])  
cv2.rectangle(mask, (x, y), (x + w, y + h), 255, -1)

findContours:

作用:寻找图像的轮廓

返回值:返回两个值,第一个为轮廓的点集,第二个是各层轮廓的索引。

cv2.RETR_EXTERNAL:表示只检测外轮廓 

cv2.CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

boundingRect:

作用:用一个最小的矩形,把找到的轮廓包起来

返回值:x,y是矩形左上点的坐标,w,h是矩形的宽和高

cv2.rectangle:

作用:画出矩形

(x,y):左上角的坐标

(x + w, y + h):右下角的坐标

-1:厚度-1像素将以指定的颜色填充矩形形状

结果:此边界框是整个全景图可以容纳的最小矩形区域。

寻找全景图内部最大的矩形区域:

这里巧妙的地方就在于创建了两个蒙版,循环使用腐蚀-像素相减,从而得到全景图内部最大的矩形区域。

  • minRect :将逐渐缩小,直到它可以放入全景图内部。
  • sub:将用于确定是否需要继续减小minRect的大小。
minRect = mask.copy()
sub = mask.copy()
while cv2.countNonZero(sub) > 0:
      minRect = cv2.erode(minRect, None)
      sub = cv2.subtract(minRect, thresh)

cv2.countNonZero():

作用:返回灰度值不为0的像素数,用来判断图像是否全黑。

cv2.subtract():

作用:两个图像像素相减。

灰度图像素值范围0-255,如果两个数相减得到负数的话,会直接将其置为0;如果两个数相加,结果超过了255的话,则直接置为255。

minRect最终结果图:

寻找这个矩形框的轮廓,最后进行裁剪:

cnts = cv2.findContours(minRect.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0]
(x, y, w, h) = cv2.boundingRect(cnts[0])

# ROI裁剪
stitched = stitched[y:y + h, x:x + w]

结果:

拼接图:

黑边处理后的图:

 完整代码:

import cv2
import os
import numpy as np

mainFolder = 'images'
myFolders = os.listdir(mainFolder)
print(myFolders)

for folder in myFolders:
    path = mainFolder + '/' + folder
    images = []
    myList = os.listdir(path)
    print(f'total number of images detected {len(myList)}')
    for imgName in myList:
        curImg = cv2.imread(f'{path}/{imgName}')
        curImg = cv2.resize(curImg,(0,0),None,0.2,0.2)
        images.append(curImg)

    stitcher = cv2.Stitcher.create()       # 创建一个stitcher实例
    (status, result) = stitcher.stitch(images)  
    if (status == 0):       # 状态码为0的时候,进行处理
        print('Panorma Generated')
        # cv2.imshow(folder, result)

        # 黑边处理
        stitched = cv2.copyMakeBorder(result, 10, 10, 10, 10, cv2.BORDER_CONSTANT, (0,0,0))
        gray = cv2.cvtColor(stitched, cv2.COLOR_BGR2GRAY)    #图片灰度化
        thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]    

        # 计算最大轮廓边界
        cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
        mask = np.zeros(thresh.shape, dtype="uint8")
        (x, y, w, h) = cv2.boundingRect(cnts[0])  # 取出list中的轮廓二值图,类型为numpy.ndarray
        cv2.rectangle(mask, (x, y), (x + w, y + h), 255, -1)

        # 腐蚀处理,直到minRect的像素值都为0
        minRect = mask.copy()
        sub = mask.copy()
        while cv2.countNonZero(sub) > 0:
            minRect = cv2.erode(minRect, None)
            sub = cv2.subtract(minRect, thresh)
        cv2.imshow('minRect', minRect)

        # 寻找这个矩形框的轮廓
        cnts = cv2.findContours(minRect.copy(), cv2.RETR_EXTERNAL,
                                cv2.CHAIN_APPROX_SIMPLE)[0]
        (x, y, w, h) = cv2.boundingRect(cnts[0])

        # ROI裁剪
        stitched = stitched[y:y + h, x:x + w]

        cv2.imshow(f'ori {folder}', result)
        cv2.imshow(folder, stitched)
    else:
        print('Panorma Generated Unsuccessful')
cv2.waitKey(0)

注意:

如果原始图像本身就有黑色区域,此黑边处理的效果会不理想。

需要拼接的图像:

 

拼接后:

黑边处理后:

 

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
全景图像拼接是将多张拍摄自同一地点、视角不同的照片拼接成一张大的全景图像。OpenCV是一个非常强大的计算机视觉库,可以用来实现全景图像拼接。 下面是实现全景图像拼接的基本步骤: 1. 加载图片。使用OpenCV的cv2.imread()函数加载图片。 2. 特征点检测。使用OpenCV的SIFT、SURF、ORB等算法检测每张图片的特征点。 3. 特征点匹配。使用OpenCV的FLANN或者Brute-Force算法对特征点进行匹配。 4. 计算单应性矩阵。使用OpenCV的findHomography函数计算单应性矩阵,将当前图片与上一张图片进行拼接。 5. 图像拼接。使用OpenCV的warpPerspective函数将当前图片进行透视变换,然后将图片拼接到上一张图片上。 6. 重复步骤2-5,直到所有图片拼接完成。 下面是一个基于OpenCV实现全景图像拼接的示例代码: ```python import cv2 import numpy as np # 加载图片 img1 = cv2.imread('img1.jpg') img2 = cv2.imread('img2.jpg') # 特征点检测 sift = cv2.xfeatures2d.SIFT_create() kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # 特征点匹配 bf = cv2.BFMatcher() matches = bf.knnMatch(des1, des2, k=2) good_matches = [] for m, n in matches: if m.distance < 0.75 * n.distance: good_matches.append(m) src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) # 计算单应性矩阵 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) # 图像拼接 result = cv2.warpPerspective(img1, M, (img1.shape[1] + img2.shape[1], img1.shape[0])) result[0:img2.shape[0], 0:img2.shape[1]] = img2 cv2.imshow('result', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这段代码实现了两张图片的拼接。你可以使用这个基本的框架,将多张图片进行拼接,从而实现全景图像拼接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值