文章目录
  • 基本相机移动
  • 区分动作的核心思想
  • 了解代码
  • 参考


基本相机移动

从我的非专业角度来看,尽管已知的摄像机运动有多种,但我们应该概述其中三种:

  • 一种是将摄像机安装在轨道上并移动——卡车、移动式摄影车、基座
  • 摄像机停留在同一位置并旋转——平移、倾斜
  • 改变变焦镜头的焦距——变焦

我们甚至可以更进一步,只保留其中两个:

  • 摄影机移动——卡车、移动摄影车、基座
  • 相机保持在同一位置 — 变焦、平移、倾斜

在这篇文章中,我们将使用平移和推车的示例来介绍区分移动的概念。对于其他组合(例如变焦和推车),可能需要采用不同的方法。

区分动作的核心思想

当我之前谈到不同的观点时,我的意思是在编写代码之前,我们需要弄清楚如何解释 panning 和 trucking 之间的区别。对于这两个,区别不是那么明显。

卡车运输时,场景中的所有物体都以相同的速度移动。然而,在平移时,距离摄像机较近的物体移动速度比距离摄像机较远的物体快。

因此,我们的想法是比较视频中不同物体的速度差异。以下是我逐步介绍的方法:

  • 计算一对帧上每个点的光流和平移值
  • 从排序后的翻译值两侧截断一些值——以消除异常值
  • 对其余值进行聚类(我使用 3 个聚类来将值彼此进一步分离)
  • 计算最小和最大聚类中心之间的差异
  • 如果差值大于某个阈值,视频就会发生平移,否则就会发生卡车移动。

它的效果出奇地好,但不幸的是也有其局限性。

视频中的某些物体在使用光流分析时可能非常棘手 - 例如天空或水。这些物体不会为算法提供要监控的精确点位置。

使用光流进行相机运动估计_数码相机

它还会受到视频中移动物体的影响,例如,如果视频中的人群四处奔跑或近景中只有一个人。因此,基本上,当有很多点以不同的速度向不同的方向移动时,它就会受到影响。

使用光流进行相机运动估计_数码相机_02

解决这一问题的方法之一是从帧中移除移动物体。方法是使用图像分割模型遮挡不需要的区域。也就是说,我们不考虑包含这些物体的区域的光流。

最后但并非最不重要的一点是,前景和背景之间的距离越远,算法的效果就越好。在靠近墙壁拍摄时,即使是人也很难确定其运动轨迹。在上面的例子中就很好地体现了这一点。

了解代码

OpenCV 视频读写入门指南

本节将对那些对想法的实现感兴趣的人和开始学习 OpenCV 的人有所帮助。本帖的实际代码可在此处找到。

该代码包含对帧中每个点的模式角和平移值的计算。这些值可用于确定摄像机运动的方向和速度。顺便说一下,在计算角度时,我们会记住原点的位置是在左下角(而不是 OpenCV 中的左上角)–这样似乎更方便。

假设我们要处理一段视频,并确定摄像机是如何移动的。我们想编写一段新视频,将算法的工作结果(或绘制光流本身)直接写入视频。

首先,我们需要截取一段视频,从中读取帧。

cap = cv.VideoCapture(os.path.join(path, filename))
  • 1.

此类将视频文件的路径或摄像机的 ID 作为输入。要从对象读取帧,我们使用以下命令:

ret, frame = cap.read()
  • 1.

该方法返回两个值。第一个是布尔值,如果读取成功则为 True(否则为 False),第二个是返回值(例如,如果相机已断开连接,或者视频文件中没有更多帧,则为 None)

我们还可以使用同一对象调用“get”方法。这个方法可以给我们提供很多信息。例如,我们可以提取原始视频的FPS值。

cap.get(cv.CAP_PROP_FPS)
  • 1.

以下是我们可以使用此方法提取的信息的完整列表。

outputStream = cv.VideoWriter(save_name, codec, frameRate, (int(cap.get(3)),int(cap.get(4))))
  • 1.

我们还需要事先初始化 VideoWriter 类对象。我们需要从原始视频中提取 fps 值和帧大小值。选择正确的编解码器可能有点棘手。如果您编写的视频是空的 - 您可以通过调整编解码器或输出视频格式进行实验。 这是有关保存视频的 OpenCV 教程。对我来说,工作的价值观如下:

fps = int(cap.get(cv.CAP_PROP_FPS))
codec = cv.VideoWriter_fourcc(*'XVID')
frame_size = (int(cap.get(3)),int(cap.get(4))
outputStream = cv.VideoWriter("video.avi", codec, fps, frame_size)
  • 1.
  • 2.
  • 3.
  • 4.

最后,我们发现了一些有趣的事情。有多种算法可用于计算光流。我们将使用 Gunnar Farneback 算法来计算密集光流。如果您想了解该算法如何工作的细节,可以阅读这篇文章。

flow = cv.calcOpticalFlowFarneback(img1, img2, None, 0.5, 3, 15, 3, 5, 1.2, 0)
  • 1.

该算法需要指定几个参数。您可以尝试选择适合您情况的值,也可以使用提供的一组值来更快地迭代整个过程。 这里是每个参数的描述。

您可以在此处找到其余代码。同时,这是两个不同视频的结果。条件几乎是理想的,因此算法正确运行也就不足为奇了。

参考

 https://medium.com/@ikunyankin/camera-motion-estimation-using-optical-flow-ce441d7ffec