ほぼリアルタイムでビデオを分析するAnalyze videos in near real time

この記事では、ライブ ビデオ ストリームから取得したフレームに対する分析を、Computer Vision API を使用してほぼリアルタイムで実行する方法を紹介します。This article demonstrates how to perform near real-time analysis on frames that are taken from a live video stream by using the Computer Vision API. そのような分析の基本的な要素は次のとおりです。The basic elements of such an analysis are:

  • ビデオ ソースからフレームを取得する。Acquiring frames from a video source.
  • 分析対象のフレームを選択する。Selecting which frames to analyze.
  • それらのフレームを API に送信する。Submitting these frames to the API.
  • API 呼び出しから返される各分析結果を使用する。Consuming each analysis result that's returned from the API call.

この記事のサンプルは C# で書かれています。The samples in this article are written in C#. このコードにアクセスするには、GitHub のビデオ フレーム分析のサンプル ページに移動してください。To access the code, go to the Video frame analysis sample page on GitHub.

ほぼリアルタイムで分析を実行するためのアプローチApproaches to running near real-time analysis

ビデオ ストリームに対してほぼリアルタイムで分析を実行するという課題は、さまざまな方法を使用して解決できます。You can solve the problem of running near real-time analysis on video streams by using a variety of approaches. この記事では、レベルの低いものから高いものの順に 3 つの方法を取り上げます。This article outlines three of them, in increasing levels of sophistication.

無限ループを設計するDesign an infinite loop

ほぼリアルタイムの分析向けの最も単純な設計は、無限ループです。The simplest design for near real-time analysis is an infinite loop. このループの繰り返すたびにフレームを取り込み、分析し、結果を使用します。In each iteration of this loop, you grab a frame, analyze it, and then consume the result:

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

軽量のクライアント側アルゴリズムで分析が構成されている場合は、このアプローチが適していると考えられます。If your analysis were to consist of a lightweight, client-side algorithm, this approach would be suitable. ただし、分析がクラウドで行われるときは、それに伴う待ち時間のために、API 呼び出しに数秒かかることがあります。However, when the analysis occurs in the cloud, the resulting latency means that an API call might take several seconds. その間は画像のキャプチャは行われず、スレッドは基本的に何もしません。During this time, you're not capturing images, and your thread is essentially doing nothing. 最大フレーム レートは、API 呼び出しの待ち時間によって制限されます。Your maximum frame rate is limited by the latency of the API calls.

API 呼び出しを並列実行できるようにするAllow the API calls to run in parallel

単純なシングルスレッドのループは、軽量のクライアント側アルゴリズムには適していますが、クラウドの API 呼び出しに伴う待ち時間には十分に対処できません。Although a simple, single-threaded loop makes sense for a lightweight, client-side algorithm, it doesn't fit well with the latency of a cloud API call. この問題の解決策は、実行時間の長い API 呼び出しを、フレームの取り込みと並列に実行できるようにすることです。The solution to this problem is to allow the long-running API call to run in parallel with the frame-grabbing. C# では、タスクベースの並列化を使用して実現できます。In C#, you could do this by using task-based parallelism. たとえば、次のコードを実行できます。For example, you can run the following code:

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

このアプローチでは、それぞれの分析を個別のタスクで起動します。With this approach, you launch each analysis in a separate task. それらのタスクをバックグラウンドで実行しながら、新しいフレームを取得し続けることができます。The task can run in the background while you continue grabbing new frames. このアプローチであれば、API 呼び出しから制御が戻るまで待機する間、メイン スレッドがブロックされることはありません。The approach avoids blocking the main thread as you wait for an API call to return. ただし、このアプローチは多少のデメリットを伴う場合があります。However, the approach can present certain disadvantages:

  • 単純なバージョンで得られているいくつかの保証は失われます。It costs you some of the guarantees that the simple version provided. つまり、複数の API 呼び出しが並列に行われると、その結果が間違った順序で返されることがあります。That is, multiple API calls might occur in parallel, and the results might get returned in the wrong order.
  • これにより、複数のスレッドが同時に ConsumeResult() 関数に入る可能性も生じます。関数がスレッドセーフでない場合は、それが危険である可能性があります。It could also cause multiple threads to enter the ConsumeResult() function simultaneously, which might be dangerous if the function isn't thread-safe.
  • 最後に、この単純なコードは作成されるタスクを追跡しないので、例外は自動的に表示されなくなります。Finally, this simple code doesn't keep track of the tasks that get created, so exceptions silently disappear. したがって、"consumer" スレッドを追加する必要があります。これは、分析タスクを追跡し、例外を発生させ、実行時間の長いタスクを中止し、正しい順序で 1 つずつ結果が使用されるようにします。Thus, you need to add a "consumer" thread that tracks the analysis tasks, raises exceptions, kills long-running tasks, and ensures that the results get consumed in the correct order, one at a time.

プロデューサー/コンシューマー システムを設計するDesign a producer-consumer system

最後のアプローチは、"プロデューサー/コンシューマー" システムの設計です。この場合、前述した無限ループに似たプロデューサー スレッドを作成します。For your final approach, designing a "producer-consumer" system, you build a producer thread that looks similar to your previously mentioned infinite loop. ただし、プロデューサーは、利用できる状態になった分析結果をすぐに使用するのではなく、単にタスクをキュー入れて追跡します。However, instead of consuming the analysis results as soon as they're available, the producer simply places the tasks in a queue to keep track of them.

// 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);
    }
}

また、コンシューマー スレッドも作成します。これはタスクをキューから取り出し、タスクが完了するのを待ち、結果を表示するか、スローされた例外を発生させます。You also create a consumer thread, which takes tasks off the queue, waits for them to finish, and either displays the result or raises the exception that was thrown. キューの使用により、システムの最大フレーム レートを制限せずに一度に 1 つずつ正しい順序で結果が使用されることを保証できます。By using the queue, you can guarantee that the results get consumed one at a time, in the correct order, without limiting the maximum frame rate of the system.

// 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);
    }
}

ソリューションを実装するImplement the solution

すぐに使い始められるGet started quickly

お使いのアプリをできるだけ迅速に稼働させることができるように、前のセクションで説明したシステムを既に実装してあります。To help get your app up and running as quickly as possible, we've implemented the system that's described in the preceding section. 使いやすく、それでいて多くのシナリオに対応できるだけの柔軟性が考慮されています。It's intended to be flexible enough to accommodate many scenarios, while being easy to use. このコードにアクセスするには、GitHub のビデオ フレーム分析のサンプル ページに移動してください。To access the code, go to the Video frame analysis sample page on GitHub.

このライブラリには、FrameGrabber クラスが含まれています。Web カメラからのビデオ フレームを処理するために、前述したプロデューサー/コンシューマー システムがこのクラスに実装されています。The library contains the FrameGrabber class, which implements the previously discussed producer-consumer system to process video frames from a webcam. ユーザーは、正確なフォームの API 呼び出しを指定でき、クラスは、新しいフレームが獲得されたとき、または新しい分析結果が利用可能になったときに呼び出しコードに知らせるためにイベントを使用します。Users can specify the exact form of the API call, and the class uses events to let the calling code know when a new frame is acquired, or when a new analysis result is available.

いくつかの可能性を示すため、このライブラリを使用する 2 つのサンプル アプリを用意しました。To illustrate some of the possibilities, we've provided two sample apps that use the library.

1 つ目のサンプル アプリは単純なコンソール アプリで、既定の Web カメラからフレームを取得し、それらを顔検出のために Face API に送信します。The first sample app is a simple console app that grabs frames from the default webcam and then submits them to the Face API for face detection. 次のコードは、このアプリの簡略化バージョンを再現したものです。A simplified version of the app is reproduced in the following code:

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.microsoft.com";

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

            // Create Face API 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 API.
                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.
            grabber.StartProcessingCameraAsync().Wait();

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

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

2 つ目のサンプル アプリはさらに少し興味深いものです。The second sample app is a bit more interesting. ビデオ フレームに対してどの API を呼び出すかの選択が可能になっています。It allows you to choose which API to call on the video frames. 左側には、ライブ ビデオのプレビューが表示されます。On the left side, the app shows a preview of the live video. 右側には、対応するフレームに対する最新の API 結果がオーバーレイされます。On the right, it overlays the most recent API result on the corresponding frame.

ほとんどのモードで、左のライブ ビデオと右に表示された分析との間に視覚的な遅延が生じます。In most modes, there's a visible delay between the live video on the left and the visualized analysis on the right. この遅延は、API 呼び出しにかかる時間です。This delay is the time that it takes to make the API call. これに該当しないのは、"EmotionsWithClientFaceDetect" モードになっているときで、このモードでは、Azure Cognitive Services に画像を送信する前に、OpenCV を使用してクライアント コンピューターでローカルに顔検出が実行されます。An exception is in the "EmotionsWithClientFaceDetect" mode, which performs face detection locally on the client computer by using OpenCV before it submits any images to Azure Cognitive Services.

このアプローチを使用することで、検出した顔をすぐに表示できます。By using this approach, you can visualize the detected face immediately. その後 API 呼び出しから制御が戻ったときに感情を更新できます。You can then update the emotions later, after the API call returns. これは "ハイブリッド" アプローチの可能性を示しています。This demonstrates the possibility of a "hybrid" approach. つまり、クライアント上で単純な処理をいくつか実行してから、必要に応じて Cognitive Services APIs を使用し、より高度な分析によってこの処理を強化することができます。That is, some simple processing can be performed on the client, and then Cognitive Services APIs can be used to augment this processing with more advanced analysis when necessary.

タグ付きの画像が表示された LiveCameraSample アプリ

サンプルをコードベースに統合するIntegrate the samples into your codebase

このサンプルの使用を開始するには、次の手順を実行します。To get started with this sample, do the following:

  1. Vision API の API キーをサブスクリプションから取得します。Get API keys for the Vision APIs from Subscriptions. ビデオ フレーム分析の場合、適用可能な API は次のとおりです。For video frame analysis, the applicable APIs are:

  2. Cognitive-Samples-VideoFrameAnalysis GitHub リポジトリを複製します。Clone the Cognitive-Samples-VideoFrameAnalysis GitHub repo.

  3. Visual Studio 2015 以降でサンプルを開き、サンプル アプリケーションをビルドして実行します。Open the sample in Visual Studio 2015 or later, and then build and run the sample applications:

    • BasicConsoleSample の場合、Face API キーは、BasicConsoleSample/Program.cs 内に直接ハードコードされています。For BasicConsoleSample, the Face API key is hard-coded directly in BasicConsoleSample/Program.cs.
    • LiveCameraSample の場合、アプリの設定ウィンドウにキーを入力します。For LiveCameraSample, enter the keys in the Settings pane of the app. これらのキーは、セッションを移動してもユーザー データとして残されます。The keys are persisted across sessions as user data.

サンプルを統合する準備ができたら、自分のプロジェクトから VideoFrameAnalyzer ライブラリを参照します。When you're ready to integrate the samples, reference the VideoFrameAnalyzer library from your own projects.

VideoFrameAnalyzer の画像、音声、ビデオ、またはテキストの解釈機能は、Azure Cognitive Services を使用しています。The image-, voice-, video-, and text-understanding capabilities of VideoFrameAnalyzer use Azure Cognitive Services. Microsoft は、開発者が (このアプリから) アップロードする画像、音声、ビデオ、およびその他のデータを受け取り、サービス向上の目的でそれらを使用する場合があります。Microsoft receives the images, audio, video, and other data that you upload (via this app) and might use them for service-improvement purposes. アプリによって Azure Cognitive Services に送信されるデータの所有者の保護にご協力をお願いします。We ask for your help in protecting the people whose data your app sends to Azure Cognitive Services.

まとめSummary

このガイドでは、Face API と Computer Vision API を使用してライブ ビデオ ストリームでほぼリアルタイムの分析を実行する方法を学習しました。In this article, you learned how to run near real-time analysis on live video streams by using the Face API and the Computer Vision API. また、サンプル コードを実際に使ってみる方法についても学習しました。You also learned how you can use our sample code to get started. 無料の API キーを使用して、アプリのビルドを開始するには、Azure Cognitive Services のサインアップ ページにアクセスしてください。To get started building your app by using free API keys, go to the Azure Cognitive Services sign-up page.

フィードバックや提案は、GitHub リポジトリからお気軽にお寄せください。Feel free to provide feedback and suggestions in the GitHub repository. API に関するより幅広いフィードバックについては、UserVoice サイトにアクセスしてください。To provide broader API feedback, go to our UserVoice site.