使用Visual Studio 2017创建C#项目处理视频文件

在本文中,将探讨如何使用Visual Studio2017和C#创建一个项目,该项目利用两个库来处理视频文件。将展示如何循环遍历从视频文件捕获的帧。请参考第一部分观看演示视频并找到关于示例项目的更多信息(所有有趣的内容都在Program.cs文件中 - 请保持此文件在单独的标签页中打开,因为本文将展示其片段)...

步骤1:从文件捕获视频

在任何处理之前,需要从视频文件中获取一帧。这可以通过使用VideoCapture类来轻松完成(许多教程提到Capture类,但在最近的Emgu版本中不可用)。检查示例项目中的Main方法:

private const string BackgroundFrameWindowName = "Background Frame"; private static Mat backgroundFrame = new Mat(); static void Main(string[] args) { string videoFile = @"PUT A PATH TO VIDEO FILE HERE!"; using (var capture = new VideoCapture(videoFile)) { if (capture.IsOpened) { backgroundFrame = capture.QueryFrame(); CvInvoke.Imshow(BackgroundFrameWindowName, backgroundFrame); VideoProcessingLoop(capture, backgroundFrame); } else { Console.WriteLine($"Unable to open {videoFile}"); } } }

VideoCapture类有四个构造函数版本。使用的重载版本接受一个string参数,即视频文件或视频流的路径。其他版本允许连接到摄像头。如果正确设计程序,从文件输入切换到网络摄像头可能就像改变新的VideoCapture调用一样简单!

一旦创建了VideoCapture实例,可以通过访问IsOpened属性来确认是否成功打开(也许路径是错误的,或者缺少编解码器?)。VideoCapture提供了几种获取帧的方法,但发现最方便的方法是调用QueryFrame方法。此方法返回Mat类实例(已经从第一部分知道了),并移动到下一帧。如果找不到下一帧,则返回null。可以利用这个事实轻松地循环遍历视频。

无人机检测项目基于找到背景帧和其他帧之间的差异。假设可以将从视频中获得的第一帧视为背景,因此在创建VideoCapture对象后立即调用QueryFrame:

backgroundFrame = capture.QueryFrame();

加载背景后,可以通过调用Imshow方法(也知道它来自第一部分)来检查它的外观:

CvInvoke.Imshow(BackgroundFrameWindowName, backgroundFrame);

在视频中总是像减去帧一样容易找到(有意义的!)差异吗?不,不是的。首先,背景可能不是静态的(想象一下无人机在被风吹动的树前飞行,或者房间的光线发生了显著变化)。第二个挑战可能来自相机的移动。拥有固定的背景和相机位置使无人机检测任务对于初学者的OpenCV教程来说足够简单,而且并不完全不现实。视频检测/识别通常用于完全受控的环境,如工厂的一部分... OpenCV能够处理更复杂的场景 - 可以阅读有关背景减法技术和光流的更多信息...

知道可以使用QueryFrame获取单个帧图像(Mat实例)并前进到下一帧,知道QueryFrame返回null如果它不能再进一步。让利用这些知识构建一个循环遍历帧的方法:

private static void VideoProcessingLoop(VideoCapture capture, Mat backgroundFrame) { var stopwatch = new Stopwatch(); int frameNumber = 1; while (true) { var rawFrame = capture.QueryFrame(); if (rawFrame != null) { frameNumber++; stopwatch.Restart(); ProcessFrame(backgroundFrame, Threshold, ErodeIterations, DilateIterations); stopwatch.Stop(); WriteFrameInfo(stopwatch.ElapsedMilliseconds, frameNumber); ShowWindowsWithImageProcessingStages(); int key = CvInvoke.WaitKey(0); if (key == 27) Environment.Exit(0); } else { capture.SetCaptureProperty(CapProp.PosFrames, 0); frameNumber = 0; } } }

在每次循环迭代中,从视频文件中捕获一帧。然后将其传递给ProcessFrame方法,该方法执行图像差异、噪声去除、轮廓检测和绘制(将在下一篇文章中详细讨论)... ProcessFrame调用被System.Diagnostics.Stopwatch使用所包围 - 这样,可以测量视频处理性能。笔记本电脑处理每帧只需要大约1.5ms - 告诉过OpenCV很快!

如果QueryFrame返回null,则程序通过调用VideoCapture实例上的SetCaptureProperty方法返回到第一帧(视频将再次处理)。WriteFrameInfo在帧的左上角放置文本,包含有关其编号和处理时间的信息。ShowWindowsWithImageProcessingStages确保可以看到当前(原始)帧、背景帧、中间帧和最终帧在单独的窗口中... 这两种方法将在下一篇文章中展示。

while循环将无限旋转,除非通过在显示帧的任何窗口中按下Escape键来停止程序执行(不是控制台窗口!)。如果将0作为WaitKey参数传递,那么程序将等待直到按下某个键。这让可以想看每帧多久就看多久。如果向WaitKey传递其他数字,那么程序将等待直到按下键或延迟到期。可能会使用它来自动以指定的帧速率播放视频:

int fps = (int)capture.GetCaptureProperty(CapProp.Fps); int key = CvInvoke.WaitKey(1000 / fps);
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485