使用OpenCV和Python拼接四张图像

3

目标:

在过去的两个星期里,我一直在努力想方设法将以下图像转换为:

enter image description here

对于长成这样的(可能与实际情况不完全匹配,因为此图像是在不同时间拍摄的):

enter image description here

镜头校正(必要性):

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

enter image description here

这个修正是使用“Samyang 12mm f/2.8 Fish-Eye ED AS NCS”镜头和“Canon EOS 10D”相机在lensfun中完成的。它可能不是完美的,但我认为足够接近了,可以继续进行第二步。
一旦镜头畸变被纠正,第二个问题就是同一行在两个切片中指向不同方向,应该通过简单的透视变换来纠正。所以我开始长时间寻找透视变换的正确参数。
失败的尝试:
1. 使用SciPy
我首先编写了一个成本函数来判断给定参数集的“质量”(重叠像素应该匹配),并应用SciPy的求解器来找出它。我对成本函数进行了几次调整(应用高斯模糊、缩小图像、灰度化图像、使用Sobel算子获得梯度、只查看“接缝”两侧的像素而不是整个重叠区域等),但它总是无法找到一个好的解决方案。大多数情况下,结果看起来比原始相机图像更糟糕:

enter image description here

2. 使用数学

当尝试失败后,我尝试应用数学来计算适当的透视变换。我知道相机的视场角(从规格说明书中得知),我知道图像的宽度和高度,我知道传感器的大小(从规格说明书中得知),并使用量角器测量了镜头之间的角度。使用针孔模型,我计算出了图像平面上点的预期(x,y)值以及需要进行的变换来进行纠正。结果看起来比SciPy好,但仍然不太理想。

enter image description here

3. 使用OpenCV的Stitcher

接下来,我尝试使用OpenCV内置的Stitcher类。然而,由于图像之间的重叠区域不足,它无法将第2和第3张图像拼接在一起(即使有10%的时间它甚至不能将第1和第2张图像拼接在一起,可能是由于RANSAC的非确定性本质)。即使成功拼接,拼接效果也不太好:

enter image description here

4. 使用ORB和OpenCV的findHomography

最近,我尝试使用带有掩码的ORB(仅在重叠区域寻找特征)和OpenCV的findHomography函数创建一个自定义版本的拼接器。虽然匹配看起来很有前途,但拼接的结果仍然不够理想:

enter image description here

我开始怀疑我的方法论(切片 -> 镜头校正 -> 透视变换 -> 叠加)存在缺陷,有更好的方法来完成这个任务。
5. ORB更新 / findHomography 我更新了我的特征检测,消除了Y坐标差异显著的匹配项(例如将桌子的白色与灯光的白色匹配)。在这样做之后,我的匹配特征数量从约110个下降到约55个,但是单应性显著改善。以下是使用更新后的1/2和2/3切片拼接的结果:

enter image description here enter image description here

除非有人可以告诉我我正在错误地进行这个过程,否则我将继续使用以下添加步骤的策略:

  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++为我工作。
1个回答

1

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

Autoencoder

请注意,如果在中间层创建瓶颈过小,则可能会损失分辨率。

Denoise

此外,变分自编码器提供了一个潜在向量,但是它们遵循相同的原理进行工作。

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)

我会阅读一些有关自编码器的资料,特别是与神经网络相关的OpenCV方法(BatchNormalization、Activation、UpSampling2D等),但我有两个问题。第一个问题是速度问题。如果神经网络只是输入一张图像并输出另一张图像(而不是获取输入像素到输出像素的映射),那么我无法构建一个着色器来提高性能。第二个问题是我没有办法获得精确的输入图像/输出图像对。我可以改变相机模式,但未来的图像与过去的图像数据不同。 - stevendesu
关于像素,取决于神经网络卷积和卷积核大小的复杂度。如果您没有精确的输入/输出,可以进行数据增强,添加噪声、高斯滤波等处理。 - razimbres

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接