使用OpenCV + Python拼接四个图像

时间:2019-11-14 16:16:40

标签: python opencv

目标:

在过去的两个星期中,我一直在尝试找出如何转换以下图像:

enter image description here

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

enter image description here

镜头校正(必要吗?):

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

enter image description here

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

一旦校正了镜头畸变,第二个问题是两个切片中的同一条线指向不同的方向,应使用简单的透视变换对其进行校正。因此,我开始了漫长的探索,以找出用于此透视变换的适当参数。

尝试失败:

1。使用SciPy

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

enter image description here

2。使用数学

当失败时,我尝试将数学应用于 compute 正确的透视变换。我知道相机的FOV(根据规格表),我知道图像的宽度和高度,我知道传感器的尺寸(根据规格表),并使用量角器测量了镜头之间的角度。然后,我使用pinhole model计算了图像平面上点的预期(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函数来创建自定义版本的Stitcher。尽管比赛看起来很有希望,但最终的缝制仍然不是最佳的:

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 个答案:

答案 0 :(得分:1)

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

Autoencoder

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

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)