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)

注意:

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

需要拼接的图像:

 

拼接后:

黑边处理后:

猜你喜欢

转载自blog.csdn.net/m0_57224196/article/details/127001458