首页 > 解决方案 > 用 OpenCV + Python 拼接四个图像

问题描述

目标:

在过去的两周里,我一直在试图弄清楚如何转换以下图像:

在此处输入图像描述

看起来像这样的一个(可能不完全匹配,因为这张图片是在不同的时间拍摄的):

在此处输入图像描述

镜头校正(必要?):

我注意到的第一件事是,简单地切片图像并覆盖四个部分并不能完美地工作,因为某些线条的曲率不匹配。例如,中场线在第二个切片中向左弯曲,在第三个切片中向右弯曲。这种弯曲看起来像桶形失真,所以我尝试使用参数化镜头校正函数(将 k1、k2 和 k3 传递给 OpenCV)和lensfun。由于 lensfun 数据库不包括我的相机品牌或型号(它是 AXIS 相机),并且我不知道镜头的品牌或型号(它是作为相机的一部分制造的),我编写了一个小脚本来转储测试图像使用各种参数的各种镜头,然后浏览了数千个输出图像,直到我找到一个看起来有相对直线的图像:

在此处输入图像描述

此校正是使用“Samyang 12mm f/2.8 Fish-Eye ED AS NCS”镜头和 lensfun 中的“Canon EOS 10D”相机完成的。它可能并不完美,但我认为它已经足够接近第二步了。

一旦镜头畸变得到纠正,第二个问题是两个切片中的同一条线指向不同的方向,这应该通过简单的透视变换来纠正。因此,我开始了漫长的探索,以找出适合这种透视变换的参数。

失败的尝试:

1. 使用 SciPy

我首先编写了一个成本函数来判断给定参数集的“质量”(重叠像素应该匹配),然后应用 SciPy 的求解器来解决这个问题。我对我的成本函数进行了一些调整(应用高斯模糊,缩小图像,灰度缩放图像,使用 Sobel 算子获得渐变,在重叠后只查看“接缝”两侧的像素而不是整个重叠区域等),但它总是找不到一个好的解决方案。大多数时候,结果看起来都比原始相机图像差:

在此处输入图像描述

2. 使用数学

当失败时,我尝试应用数学来计算正确的透视变换。我知道相机的 FOV(来自规格表),我知道图像宽度和高度,我知道传感器尺寸(来自规格表),并使用量角器测量了镜头之间的角度。然后,我使用针孔模型计算了图像平面上点的预期 (x,y) 值以及校正它们所需的变换。结果看起来比 SciPy 好,但仍然令人沮丧。

在此处输入图像描述

3. 使用 OpenCV 的Stitcher

在此之后,我尝试使用 OpenCV 的内置Stitcher类。然而,由于图像之间的重叠不足,它未能将切片 2 和 3 拼接在一起(大约 10% 的时间它甚至无法将切片 1 和 2 拼接在一起,大概是因为 RANSAC 的不确定性)。即使它确实成功了,针迹也不是那么好:

在此处输入图像描述

4. 使用 ORB 和 OpenCV 的findHomography

最近我尝试使用带有掩码的 ORB(仅在重叠区域中寻找特征)和 OpenCV 的findHomography函数来创建自定义版本的缝合器。虽然比赛看起来很有希望,但最终的缝合仍然不是最理想的:

在此处输入图像描述

我开始怀疑我的方法(切片 -> 镜头正确 -> 透视变换 -> 叠加)有缺陷,并且有更好的方法来做到这一点。

5.更新ORB/findHomography

我更新了我的特征检测,以消除 Y 坐标差异很大的任何匹配项(例如,将桌子的白色与灯光的白色匹配)。这样做之后,我的匹配特征数量从 ~110 下降到 ~55,但单应性得到了显着改善。这是切片 1/2 和 2/3 的更新结果:

在此处输入图像描述 在此处输入图像描述

直到有人告诉我这一切都错了,我将继续采用以下附加步骤来执行此策略:

  1. 切片图像
  2. 镜头校正每一片
  3. 透视变换切片 2 或 3,使边线水平,中线垂直
  4. 使用 ORB + 匹配过滤 + findHomography 迭代对齐然后拼接相邻切片

最终,当一切都说完了,我想尝试计算从输入像素到输出像素的映射,这样我们就不会每帧都做所有这些复杂的工作(镜头校正、ORB、findHomography 等)。我们将为每个摄像机执行一次,将映射保存到某个文件中,然后我们可以使用实时将输入视频逐帧映射到输出视频cv2.remap

笔记:

我发布的第二张显示“预期输出”的图像直接来自有问题的相机。它可以配置为以 30 fps 的速度返回第一张图像,或以 10 fps 的速度返回第二张图像。我们希望在功能更强大的计算机上执行离机拼接,这样我们就可以获得 30 fps 但仍然有单张图像。

AXIS 提供了一个用于离机拼接的 SDK,但这个 SDK 仅适用于 Windows,我们的大部分技术堆栈都是 Linux,我们的大多数开发机器都是 Mac OS。我已经使用 Windows 计算机尝试查看他们提供的拼接 SDK,但是我没有运气让它编译和运行。他们的示例代码不断抛出错误,我从来没有运气让 Visual Studio 或 C++ 为我很好地发挥作用。

标签: pythonopencv

解决方案


我的建议是训练一个自动编码器。使用第一张图像作为输入,第二张图像作为输出,就像在去噪自动编码器中一样:

自动编码器

请注意,如果您在中间层创建的瓶颈太小,您可能会失去分辨率。

去噪

此外,变分自动编码器呈现一个潜在向量,但遵循相同的原理工作。

VAE

您可以调整此代码:

denoise = Sequential()
denoise.add(Convolution2D(20, 3,3,
                        border_mode='valid',
                        input_shape=input_shape))
denoise.add(BatchNormalization(mode=2))
denoise.add(Activation('relu'))
denoise.add(UpSampling2D(size=(2, 2)))
denoise.add(Convolution2D(20, 3, 3,
                            init='glorot_uniform'))
denoise.add(BatchNormalization(mode=2))
denoise.add(Activation('relu'))
denoise.add(Convolution2D(20, 3, 3,init='glorot_uniform'))
denoise.add(BatchNormalization(mode=2))
denoise.add(Activation('relu'))
denoise.add(MaxPooling2D(pool_size=(3,3)))
denoise.add(Convolution2D(4, 3, 3,init='glorot_uniform'))
denoise.add(BatchNormalization(mode=2))
denoise.add(Activation('relu'))
denoise.add(Reshape((28,28,1)))
sgd = SGD(lr=learning_rate,momentum=momentum, decay=decay_rate, nesterov=False)

denoise.compile(loss='mean_squared_error', optimizer=sgd,metrics = ['accuracy'])
denoise.summary()

denoise.fit(x_train_noisy, x_train,
                nb_epoch=50,
                batch_size=30,verbose=1)

推荐阅读