目录
stitcher优缺点:
相关库:
导入图片:
调用stitcher类进行拼接:
stitch:
全景拼接结果:
提取黑边轮廓:
copyMakeBorder:
threshold:
计算最大的轮廓边界:
findContours:
boundingRect:
cv2.rectangle:
寻找全景图内部最大的矩形区域:
cv2.countNonZero():
cv2.subtract():
寻找这个矩形框的轮廓,最后进行裁剪:
完整代码:
注意:
优点:适应部分倾斜/尺度变换和畸变情形,拼接效果好,使用简单,可以一次拼接多张图片。
缺点:需要有足够的相同特征区域进行匹配,速度较慢(和图像大小有关)。
软件: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 = 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)
作用:拼接指定的图像,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]
作用:扩充边界
cv2.copyMakeBorder(result, 10, 10, 10, 10, cv2.BORDER_CONSTANT, (0,0,0)):让全景图的上下左右分别扩充10像素的黑色边框。
作用:灰度大于某个数值像素点保留
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)
作用:寻找图像的轮廓
返回值:返回两个值,第一个为轮廓的点集,第二个是各层轮廓的索引。
cv2.RETR_EXTERNAL:表示只检测外轮廓
cv2.CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
作用:用一个最小的矩形,把找到的轮廓包起来
返回值:x,y是矩形左上点的坐标,w,h是矩形的宽和高
作用:画出矩形
(x,y):左上角的坐标
(x + w, y + h):右下角的坐标
-1:厚度-1像素将以指定的颜色填充矩形形状
结果:此边界框是整个全景图可以容纳的最小矩形区域。
这里巧妙的地方就在于创建了两个蒙版,循环使用腐蚀-像素相减,从而得到全景图内部最大的矩形区域。
minRect = mask.copy()
sub = mask.copy()
while cv2.countNonZero(sub) > 0:
minRect = cv2.erode(minRect, None)
sub = cv2.subtract(minRect, thresh)
作用:返回灰度值不为0的像素数,用来判断图像是否全黑。
作用:两个图像像素相减。
灰度图像素值范围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)
如果原始图像本身就有黑色区域,此黑边处理的效果会不理想。
需要拼接的图像:
拼接后:
黑边处理后: