![]() |
OpenCV 4.12.0
开源计算机视觉
|
上一教程: 使用GDAL读取地理空间栅格文件
下一教程: 使用OpenCV创建视频
| 原始作者 | Bernát Gábor |
| 兼容性 | OpenCV >= 3.0 |
如今,拥有一个数字视频录制系统是很常见的。因此,您最终会遇到不再批量处理图像,而是处理视频流的情况。这些视频流可能有两种:实时图像馈送(网络摄像头)或预录制并存储在硬盘驱动器上的文件。幸运的是,OpenCV 以相同的方式处理这两种情况,使用相同的 C++ 类。因此,您将在本教程中学到:
为了展示如何使用 OpenCV,我创建了一个小程序,它读取两个视频文件并进行相似度检查。您可以使用此程序来检查新的视频压缩算法效果如何。这里有一个参考(原始)视频,例如 这个小Megamind剪辑 和 它的压缩版本。您还可以在 OpenCV 源代码库的 samples/data 文件夹中找到源代码和这些视频文件。
本质上,所有视频操作所需的功能都集成在 cv::VideoCapture C++ 类中。这本身是建立在 FFmpeg 开源库之上的。这是 OpenCV 的基本依赖项,所以您无需担心。视频由一系列图像组成,我们在文献中将其称为帧。对于视频文件,有一个帧率,它指定两帧之间的时间间隔。虽然视频摄像机通常每秒能数字化的帧数有限,但此属性不太重要,因为摄像机在任何时候都能看到世界的当前快照。
您需要做的第一件事是为 cv::VideoCapture 类分配其源。您可以通过 cv::VideoCapture::VideoCapture 或其 cv::VideoCapture::open 函数来完成。如果此参数是整数,则您会将该类绑定到摄像机(设备)。此处传递的数字是操作系统分配的设备 ID。如果您的系统连接了一个摄像机,其 ID 可能是零,后续的则会递增。如果传递给这些函数的参数是字符串,它将引用一个视频文件,字符串指向文件的位置和名称。例如,对于上面的源代码,一个有效的命令行是
我们进行相似度检查。这需要一个参考视频文件和一个测试用例视频文件。前两个参数指的是这个。这里我们使用相对地址。这意味着应用程序将在其当前工作目录中查找并打开视频文件夹,并尝试在其中找到Megamind.avi和Megamind_bug.avi。
要检查类与视频源的绑定是否成功,请使用 cv::VideoCapture::isOpened 函数
当对象析构函数被调用时,视频会自动关闭。但是,如果您想在此之前关闭它,您需要调用其 cv::VideoCapture::release 函数。视频的帧只是简单的图像。因此,我们只需要将它们从 cv::VideoCapture 对象中提取出来,并放入一个 Mat 对象中。视频流是顺序的。您可以使用 cv::VideoCapture::read 或重载的 >> 运算符来逐帧获取它们
如果无法获取帧(无论是视频流关闭还是到达视频文件末尾),上述读取操作将使 Mat 对象为空。我们可以通过一个简单的 if 语句来检查这一点
一个读取方法由帧抓取和对其应用的解码组成。您可以通过使用 cv::VideoCapture::grab 然后 cv::VideoCapture::retrieve 函数来显式调用这两个操作。
视频除了帧内容外,还附带许多信息。这些通常是数字,但在某些情况下可能是短字符序列(4字节或更少)。因此,为了获取这些信息,有一个名为 cv::VideoCapture::get 的通用函数,它返回包含这些属性的双精度值。使用位操作从双精度类型中解码字符,并在有效值仅为整数时进行转换。它的单个参数是查询属性的ID。例如,这里我们获取参考视频文件和测试用例视频文件中帧的大小;以及参考视频中的帧数。
当您处理视频时,您可能经常想自己控制这些值。为此,有一个 cv::VideoCapture::set 函数。它的第一个参数仍然是您要更改的属性名称,第二个参数是双精度类型,包含要设置的值。如果成功则返回true,否则返回false。很好的例子是在视频文件中跳转到给定时间或帧
有关您可以读取和更改的属性,请查看 cv::VideoCapture::get 和 cv::VideoCapture::set 函数的文档。
我们想要检查视频转换操作有多么难以察觉,因此我们需要一个系统来逐帧检查相似性或差异。最常用的算法是 PSNR(也称为峰值信噪比)。它最简单的定义始于均方误差。设有两个图像:I1 和 I2;二维尺寸为 i 和 j,由 c 个通道组成。
\[MSE = \frac{1}{c*i*j} \sum{(I_1-I_2)^2}\]
然后 PSNR 表示为
\[PSNR = 10 \cdot \log_{10} \left( \frac{MAX_I^2}{MSE} \right)\]
这里的 \(MAX_I\) 是像素的最大有效值。对于每个通道每个像素的简单单字节图像,这个值是 255。当两幅图像相同时,MSE 将为零,导致 PSNR 公式中出现无效的除零操作。在这种情况下,PSNR 是未定义的,我们需要单独处理这种情况。转换为对数刻度是因为像素值具有非常宽的动态范围。所有这些转换为 OpenCV 和一个函数看起来像:
对于视频压缩,典型的结果值在 30 到 50 之间,越高越好。如果图像显著不同,您会得到低得多的值,例如 15 等。这种相似性检查计算简单快速,但实际上可能与人眼感知不太一致。结构相似性算法旨在纠正这一点。
描述这些方法远远超出了本教程的目的。为此,我邀请您阅读介绍它的文章。尽管如此,通过查看下面的 OpenCV 实现,您可以很好地了解它。
这将返回图像每个通道的相似度指数。该值介于零和一之间,其中一表示完美匹配。不幸的是,多次高斯模糊的开销相当大,因此虽然 PSNR 可以在实时环境中(每秒 24 帧)工作,但要达到相似的性能结果,SSIM 需要显著更多的时间。
因此,教程开头提供的源代码将对每一帧执行 PSNR 测量,而 SSIM 仅在 PSNR 低于输入值时才执行。为了可视化目的,我们在 OpenCV 窗口中显示两幅图像,并将 PSNR 和 MSSIM 值打印到控制台。您应该会看到类似以下的内容:
您可以在此处的 YouTube 上观看此程序的运行时实例。