近乎实时地分析视频

本文介绍如何使用 Azure AI 视觉 API 近乎实时地分析实时视频流中的帧。 此分析的基本元素包括:

  • 从视频源获取帧
  • 选择要分析的帧
  • 将这些帧发送到 API
  • 使用 API 返回的每个分析结果

小窍门

本文中的示例以 C# 编写。 若要访问代码,请转到 GitHub 上的 视频帧分析示例 页。

进行近实时分析的方法

可以使用多种方法解决在视频流上运行近实时分析的问题。 本文概述了其中三个,按复杂程度递增排列。

方法 1:设计无限循环

近实时分析的最简单设计是无限循环。 在此循环的每个迭代中,应用程序检索一个帧、分析它,然后处理结果:

while (true)
{
    Frame f = GrabFrame();
    if (ShouldAnalyze(f))
    {
        AnalysisResult r = await Analyze(f);
        ConsumeResult(r);
    }
}

如果分析由轻型客户端算法组成,则此方法适用。 但是,当分析发生在云中时,生成的延迟意味着 API 调用可能需要几秒钟时间。 在此期间,你不会捕获图像,线程实际上没有执行任何操作。 最大帧速率受 API 调用延迟的限制。

方法 2:允许 API 调用并行运行

尽管一个简单的单线程循环对于轻型客户端算法有意义,但它与云 API 调用的延迟不匹配。 解决此问题的解决方法是允许长时间运行的 API 调用与帧抓取并行运行。 在 C# 中,可以使用基于任务的并行度执行此作。 例如,可以运行以下代码:

while (true)
{
    Frame f = GrabFrame();
    if (ShouldAnalyze(f))
    {
        var t = Task.Run(async () =>
        {
            AnalysisResult r = await Analyze(f);
            ConsumeResult(r);
        }
    }
}

使用此方法,可以在单独的任务中启动每个分析。 程序可以在后台运行,同时你可以继续获取新帧。 此方法避免在等待 API 调用返回时阻止主线程。 但是,此方法可能会存在某些缺点:

  • 你失去了简单版本提供的一些保证。 也就是说,可能会并行执行多个 API 调用,结果可能会按错误顺序返回。
  • 它可能导致多个线程同时输入 ConsumeResult() 函数,如果函数不是线程安全的,则这可能会很危险。
  • 最后,此简单代码不会跟踪创建的任务,因此异常会静默消失。 因此,你需要添加一个跟踪分析任务的“消费者”线程,处理异常,终止长时间运行的任务,并确保结果按正确顺序逐个被消费。

方法 3:设计生成者-使用者系统

若要设计“生成方-使用者”系统,请生成类似于上一部分的无限循环的生成者线程。 然后,生产者不是在分析结果刚一出来就使用它们,而是简单地将任务放入队列中以进行跟踪。

// Queue that will contain the API call tasks.
var taskQueue = new BlockingCollection<Task<ResultWrapper>>();

// Producer thread.
while (true)
{
    // Grab a frame.
    Frame f = GrabFrame();

    // Decide whether to analyze the frame.
    if (ShouldAnalyze(f))
    {
        // Start a task that will run in parallel with this thread.
        var analysisTask = Task.Run(async () =>
        {
            // Put the frame, and the result/exception into a wrapper object.
            var output = new ResultWrapper(f);
            try
            {
                output.Analysis = await Analyze(f);
            }
            catch (Exception e)
            {
                output.Exception = e;
            }
            return output;
        }

        // Push the task onto the queue.
        taskQueue.Add(analysisTask);
    }
}

此外,创建一个消费者线程,该线程从队列中获取任务,等待任务完成,并显示结果或抛出抛出的异常。 使用此队列,可以确保结果按正确的顺序逐个处理,并且不会影响系统的最大帧率。

// Consumer thread.
while (true)
{
    // Get the oldest task.
    Task<ResultWrapper> analysisTask = taskQueue.Take();
 
    // Wait until the task is completed.
    var output = await analysisTask;

    // Consume the exception or result.
    if (output.Exception != null)
    {
        throw output.Exception;
    }
    else
    {
        ConsumeResult(output.Analysis);
    }
}

实施解决方案

获取示例代码

为了帮助你尽快运行应用,我们实现了上一部分所述的系统。 它旨在足够灵活,以适应许多方案,同时易于使用。 若要访问代码,请转到 GitHub 上的 视频帧分析示例 存储库。

该库包含类 FrameGrabber ,该类实现生成者-使用者系统以处理来自网络摄像头的视频帧。 用户可以指定 API 调用的确切形式,类使用事件让调用代码知道何时获取新帧或新的分析结果可用。

查看示例实现

为了说明一些可能性,我们提供了两个使用库的示例应用。

第一个示例应用是一个简单的控制台应用,它从默认网络摄像头中获取帧,然后将其提交到人脸服务进行人脸检测。 应用的简化版本在以下代码中表示:

using System;
using System.Linq;
using Microsoft.Azure.CognitiveServices.Vision.Face;
using Microsoft.Azure.CognitiveServices.Vision.Face.Models;
using VideoFrameAnalyzer;

namespace BasicConsoleSample
{
    internal class Program
    {
        const string ApiKey = "<your API key>";
        const string Endpoint = "https://<your API region>.api.cognitive.azure.cn";

        private static async Task Main(string[] args)
        {
            // Create grabber.
            FrameGrabber<DetectedFace[]> grabber = new FrameGrabber<DetectedFace[]>();

            // Create Face Client.
            FaceClient faceClient = new FaceClient(new ApiKeyServiceClientCredentials(ApiKey))
            {
                Endpoint = Endpoint
            };

            // Set up a listener for when we acquire a new frame.
            grabber.NewFrameProvided += (s, e) =>
            {
                Console.WriteLine($"New frame acquired at {e.Frame.Metadata.Timestamp}");
            };

            // Set up a Face API call.
            grabber.AnalysisFunction = async frame =>
            {
                Console.WriteLine($"Submitting frame acquired at {frame.Metadata.Timestamp}");
                // Encode image and submit to Face service.
                return (await faceClient.Face.DetectWithStreamAsync(frame.Image.ToMemoryStream(".jpg"))).ToArray();
            };

            // Set up a listener for when we receive a new result from an API call.
            grabber.NewResultAvailable += (s, e) =>
            {
                if (e.TimedOut)
                    Console.WriteLine("API call timed out.");
                else if (e.Exception != null)
                    Console.WriteLine("API call threw an exception.");
                else
                    Console.WriteLine($"New result received for frame acquired at {e.Frame.Metadata.Timestamp}. {e.Analysis.Length} faces detected");
            };

            // Tell grabber when to call the API.
            // See also TriggerAnalysisOnPredicate
            grabber.TriggerAnalysisOnInterval(TimeSpan.FromMilliseconds(3000));

            // Start running in the background.
            await grabber.StartProcessingCameraAsync();

            // Wait for key press to stop.
            Console.WriteLine("Press any key to stop...");
            Console.ReadKey();

            // Stop, blocking until done.
            await grabber.StopProcessingAsync();
        }
    }
}

第二个示例应用提供了更多功能。 这使您可以选择要调用以处理视频帧的 API。 在左侧,应用显示实时视频的预览。 在右侧,它将最新的 API 结果叠加在相应的框架上。

在大多数模式下,左侧实时视频与右侧可视化分析之间存在明显的延迟。 此延迟是进行 API 调用所需的时间。 一个例外是在 EmotionsWithClientFaceDetect 模式中,该模式在将任何图像提交到 Azure AI 服务之前,会在客户端计算机上使用 OpenCV 本地执行人脸检测。

使用此方法可以立即可视化检测到的人脸。 然后,可以在 API 调用返回后更新属性。 此方法演示了“混合”方法的可能性。 可以在客户端上执行一些简单的处理,然后 Azure AI 服务 API 可以在必要时通过更高级的分析来扩充此处理。

显示带有标记的图像的 LiveCameraSample 应用

将示例集成到代码库中

若要开始使用此示例,请完成以下步骤:

  1. 创建 Azure 帐户。 如果已有帐户,请转到下一步。
  2. 在 Azure 门户中为 Azure AI 视觉和人脸创建资源以获取密钥和终结点。 请确保在安装过程中选择免费层(F0)。
    • Azure AI 视觉
    • Face 在门户部署资源后,选择转到资源以获取每个资源的密钥和终结点。
  3. 克隆 Cognitive-Samples-VideoFrameAnalysis GitHub 存储库。
  4. 在 Visual Studio 2015 或更高版本中打开示例,然后生成并运行示例应用程序:
    • 对于 BasicConsoleSample,直接在 BasicConsoleSample/Program.cs中硬编码人脸键。
    • 对于 LiveCameraSample,请在应用的 “设置” 窗格中输入密钥。 应用会将密钥在多个会话期间保留为用户数据。

准备好集成示例时,请从自己的项目中引用 VideoFrameAnalyzer 库。

VideoFrameAnalyzer 的图像、语音、视频和文本理解功能使用 Azure AI 服务。 Microsoft接收通过此应用上传的图像、音频、视频和其他数据,并可能将其用于服务改进目的。 我们请求你帮助保护通过应用程序发送到 Azure AI 服务的人员及其数据。

后续步骤

本文介绍了如何使用人脸和 Azure AI 视觉服务对实时视频流运行近实时分析。

随时在 GitHub 存储库中提供反馈和建议。 若要提供更广泛的 API 反馈,请转到 我们的 UserVoice 站点。