yol*_*oli 9 python opencv image-processing computer-vision image-stitching
我有一个固定相机,可以快速拍摄连续移动的产品的照片,但处于相同角度(平移视角)的固定位置。我需要将所有图像拼接成全景图。我尝试过使用 Stitcher 类。它有效,但计算时间很长。我还尝试使用另一种方法,即使用 SIFT 检测器 FNNbasedMatcher,查找单应性,然后扭曲图像。如果我只使用两个图像,此方法效果很好。对于多个图像,它仍然无法正确缝合它们。有谁知道这种情况下最好、最快的图像拼接算法?
这是我使用 Stitcher 类的代码。
import time
import cv2
import os
import numpy as np
import sys
def main():
# read input images
imgs = []
path = 'pics_rotated/'
i = 0
for (root, dirs, files) in os.walk(path):
images = [f for f in files]
print(images)
for i in range(0,len(images)):
curImg = cv2.imread(path + images[i])
imgs.append(curImg)
stitcher = cv2.Stitcher.create(mode= 0)
status ,result = stitcher.stitch(imgs)
if status != cv2.Stitcher_OK:
print("Can't stitch images, error code = %d" % status)
sys.exit(-1)
cv2.imwrite("imagesout/output.jpg", result)
cv2.waitKey(0)
if __name__ == '__main__':
start = time.time()
main()
end = time.time()
print("Time --->>>>>", end - start)
cv2.destroyAllWindows()enter code here
Run Code Online (Sandbox Code Playgroud)
Bur*_*rak 24
尽管OpenCVStitcher类提供了很多执行拼接的方法和选项,但由于其复杂性,我发现很难使用它。因此,我会尽力提供最少且最快的方式来执行拼接。如果您想知道更复杂的方法(例如曝光补偿),我强烈建议您查看详细的示例代码。作为旁注,如果有人可以将以下函数转换为使用 Stitcher 类,我将不胜感激。
为了将多个图像组合成同一视角,需要进行以下操作:
什么是特点?
它们是可区分的部分,例如正方形的角,在图像中保留。为了获得这些特征点,提出了不同的算法,例如 Harris、ORB、SIFT、SURF 等。cv::Feature2d完整列表请参见。我将使用 SIFT,因为它准确且足够快。
特征由关键点(图像中的位置)和描述符组成,描述符是表示特征属性的一组数字(例如 128 维向量)。
找到图像中的不同点后,我们需要匹配相应的点对。看cv::DescriptionMatcher。我将使用基于 Flann 的描述符匹配器。
首先,我们初始化描述符和匹配器类。
descriptor = cv.SIFT.create()
matcher = cv.DescriptorMatcher.create(cv.DescriptorMatcher.FLANNBASED)
Run Code Online (Sandbox Code Playgroud)
然后,我们找到每张图像中的特征。
(kps, desc) = descriptor.detectAndCompute(image, mask=None)
Run Code Online (Sandbox Code Playgroud)
现在我们找到对应的点对。
if (desc1 is not None and desc2 is not None and len(desc1) >=2 and len(desc2) >= 2):
rawMatch = matcher->knnMatch(desc2, desc1, k=2)
matches = []
# ensure the distance is within a certain ratio of each other (i.e. Lowe's ratio test)
ratio = 0.75
for m in rawMatch:
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
matches.append((m[0].trainIdx, m[0].queryIdx))
Run Code Online (Sandbox Code Playgroud)
单应性是从一种视图到另一种视图的透视变换。一个视图中的平行线在另一个视图中可能不平行,就像通往日落的道路一样。我们需要至少有 4 个对应点对。越多意味着必须分解或消除冗余数据。
将初始视图中的点转换为其扭曲位置的单应性矩阵。它是通过直接线性变换算法计算的 3x3 矩阵。有 8 个 DoF,矩阵中最后一个元素为 1。
[pt2] = H * [pt1]
Run Code Online (Sandbox Code Playgroud)
现在我们有了对应的点匹配,我们计算单应性。我们处理冗余数据的方法是RANSAC,它随机选择4个点对并使用最佳拟合结果。请参阅cv::findHomography参考资料 了解更多选项。
if len(matches) > 4:
(H, status) = cv.findHomography(pts1, pts2, cv.RANSAC)
Run Code Online (Sandbox Code Playgroud)
通过计算单应性,我们知道源图像中的哪个点对应于目标图像中的哪个点。 为了不丢失源图像中的信息,我们需要将目标图像填充变换点落入负区域的量。 同时,我们需要跟踪原点的偏移量以拼接多个图像。
descriptor = cv.SIFT.create()
matcher = cv.DescriptorMatcher.create(cv.DescriptorMatcher.FLANNBASED)
Run Code Online (Sandbox Code Playgroud)
(kps, desc) = descriptor.detectAndCompute(image, mask=None)
Run Code Online (Sandbox Code Playgroud)
if (desc1 is not None and desc2 is not None and len(desc1) >=2 and len(desc2) >= 2):
rawMatch = matcher->knnMatch(desc2, desc1, k=2)
matches = []
# ensure the distance is within a certain ratio of each other (i.e. Lowe's ratio test)
ratio = 0.75
for m in rawMatch:
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
matches.append((m[0].trainIdx, m[0].queryIdx))
Run Code Online (Sandbox Code Playgroud)
[pt2] = H * [pt1]
Run Code Online (Sandbox Code Playgroud)
if len(matches) > 4:
(H, status) = cv.findHomography(pts1, pts2, cv.RANSAC)
Run Code Online (Sandbox Code Playgroud)
这是涉及曝光补偿等图像增强的步骤。为了简单起见,我们将使用均值混合。最简单的解决方案是覆盖目标图像中的现有数据,但平均操作对我们来说不是负担。
# find the ROI of a transformation result
def warpRect(rect, H):
x, y, w, h = rect
corners = [[x, y], [x, y + h - 1], [x + w - 1, y], [x + w - 1, y + h - 1]]
extremum = cv.transform(corners, H)
minx, miny = np.min(extremum[:,0]), np.min(extremum[:,1])
maxx, maxy = np.max(extremum[:,0]), np.max(extremum[:,1])
xo = int(np.floor(minx))
yo = int(np.floor(miny))
wo = int(np.ceil(maxx - minx))
ho = int(np.ceil(maxy - miny))
outrect = (xo, yo, wo, ho)
return outrect
Run Code Online (Sandbox Code Playgroud)
# homography matrix is translated to fit in the screen
def coverH(rect, H):
# obtain bounding box of the result
x, y, _, _ = warpRect(rect, H)
# shift amount to the first quadrant
xpos = int(-x if x < 0 else 0)
ypos = int(-y if y < 0 else 0)
# correct the homography matrix so that no point is thrown out
T = np.array([[1, 0, xpos], [0, 1, ypos], [0, 0, 1]])
H_corr = T.dot(H)
return (H_corr, (xpos, ypos))
Run Code Online (Sandbox Code Playgroud)
# pad image to cover ROI, return the shift amount of origin
def addBorder(img, rect):
x, y, w, h = rect
tl = (x, y)
br = (x + w, y + h)
top = int(-tl[1] if tl[1] < 0 else 0)
bottom = int(br[1] - img.shape[0] if br[1] > img.shape[0] else 0)
left = int(-tl[0] if tl[0] < 0 else 0)
right = int(br[0] - img.shape[1] if br[0] > img.shape[1] else 0)
img = cv.copyMakeBorder(img, top, bottom, left, right, cv.BORDER_CONSTANT, value=[0, 0, 0])
orig = (left, top)
return img, orig
Run Code Online (Sandbox Code Playgroud)
上述方法将先前组合的图像(称为全景)扭曲到随后的下一个图像上。然而,图案可能具有最佳缝合视图的连接点。
例如
1 2 3
4 5 6
Run Code Online (Sandbox Code Playgroud)
组合这些图像的最佳模式是
1 -> 2 <- 3
|
V
4 -> 5 <- 6
Run Code Online (Sandbox Code Playgroud)
1 & 2因此,我们需要最后一个函数来与2 & 3或1235与456节点结合5。
def size2rect(size):
return (0, 0, size[1], size[0])
Run Code Online (Sandbox Code Playgroud)
要进行快速演示,您可以运行GitHub 中的Python 代码。如果你想在C++中使用上述方法,你可以看看Stitch库。欢迎对这篇文章进行任何 PR 或编辑。
| 归档时间: |
|
| 查看次数: |
17351 次 |
| 最近记录: |