Come analizzare video in tempo realeHow to analyze videos in real time

Questa guida mostra come eseguire l'analisi near real time su fotogrammi acquisiti da un video in diretta streaming.This guide will demonstrate how to perform near-real-time analysis on frames taken from a live video stream. Le fasi fondamentali di questo tipo di sistema sono:The basic components in such a system are:

  • Acquisizione di fotogrammi da un'origine videoAcquire frames from a video source
  • Selezione dei fotogrammi da analizzareSelect which frames to analyze
  • Invio di tali fotogrammi all'APISubmit these frames to the API
  • Uso di ogni risultato dell'analisi che viene restituito dalla chiamata all'APIConsume each analysis result that is returned from the API call

Questi esempi sono scritti in C# e il codice è disponibile in GitHub all'indirizzo seguente: https://github.com/Microsoft/Cognitive-Samples-VideoFrameAnalysis.These samples are written in C# and the code can be found on GitHub here: https://github.com/Microsoft/Cognitive-Samples-VideoFrameAnalysis.

ApproccioThe Approach

Esistono molti modi di risolvere il problema dell'esecuzione di un'analisi near real time su video in streaming.There are multiple ways to solve the problem of running near-real-time analysis on video streams. Iniziamo delineando tre approcci con livelli di sofisticazione crescenti.We will start by outlining three approaches in increasing levels of sophistication.

Approccio sempliceA Simple Approach

Il progetto più semplice per un sistema di analisi near real time prevede un ciclo infinito in cui a ogni iterazione si acquisisce un fotogramma, lo si analizza e successivamente se ne usa il risultato:The simplest design for a near-real-time analysis system is an infinite loop, where in each iteration we 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);
    }
}

Se l'analisi consiste in un algoritmo lato client leggero, questo approccio è adeguato.If our analysis consisted of a lightweight client-side algorithm, this approach would be suitable. Quando invece l'analisi avviene nel cloud, la latenza interessata indica che una chiamata all'API può richiedere vari secondi, durante i quali non vengono acquisite immagini e il thread essenzialmente non genera alcun risultato.However, when our analysis is happening in the cloud, the latency involved means that an API call might take several seconds, during which time we are not capturing images, and our thread is essentially doing nothing. La frequenza massima dei fotogrammi è limitata dalla latenza delle chiamate all'API.Our maximum frame-rate is limited by the latency of the API calls.

Abbinamento in parallelo di chiamate all'APIParallelizing API Calls

Se l'uso di un ciclo con thread singolo semplice ha senso per un algoritmo semplificato su lato client, non è invece adatto in presenza della latenza insita nelle chiamata all'API cloud.While a simple single-threaded loop makes sense for a lightweight client-side algorithm, it doesn't fit well with the latency involved in cloud API calls. Per risolvere questo problema, le chiamate all'API a esecuzione prolungata devono essere eseguite in parallelo con l'acquisizione dei fotogrammi.The solution to this problem is to allow the long-running API calls to execute in parallel with the frame-grabbing. In C# è possibile applicare questa soluzione usando il parallelismo basato sulle attività, ad esempio:In C#, we could achieve this using Task-based parallelism, for example:

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

Questo approccio avvia ciascuna analisi in un'attività separata, che può essere eseguita in background mentre si continua ad acquisire nuovi fotogrammi.This approach launches each analysis in a separate Task, which can run in the background while we continue grabbing new frames. Questa soluzione evita il blocco del thread principale durante l'attesa dei risultati di una chiamata API, ma fa perdere alcune delle garanzie offerte dalla versione semplice. Possono infatti verificarsi più chiamate API in parallelo e i risultati potrebbero venire restituiti in un ordine non corretto.It avoids blocking the main thread while waiting for an API call to return, however we have lost some of the guarantees that the simple version provided -- multiple API calls might occur in parallel, and the results might get returned in the wrong order. Questo approccio può anche fare in modo che più thread entrino nella funzione ConsumeResult() simultaneamente. Questo comportamento può essere pericoloso, se la funzione non è thread-safe.This approach could also cause multiple threads to enter the ConsumeResult() function simultaneously, which could be dangerous, if the function is not thread-safe. Questo codice semplice, inoltre, non tiene traccia delle attività che vengono create, pertanto le eventuali eccezioni scompaiono silenziosamente.Finally, this simple code does not keep track of the Tasks that get created, so exceptions will silently disappear. L'ingrediente finale da aggiungere, quindi, è un thread "consumer" che tenga traccia delle attività di analisi, generi eccezioni, interrompa le attività a esecuzione prolungata e assicuri che i risultati vengano usati nell'ordine corretto e uno alla volta.Thus, the final ingredient for us to add is a "consumer" thread that will track the analysis tasks, raise exceptions, kill long-running tasks, and ensure the results get consumed in the correct order, one at a time.

Progetto producer-consumerA Producer-Consumer Design

Nel sistema "producer-consumer" finale è presente un thread producer simile al ciclo infinito precedente.In our final "producer-consumer" system, we have a producer thread that looks similar to our previous infinite loop. Invece di usare i risultati dell'analisi man mano che diventano disponibili, il producer inserisce semplicemente le attività in una coda per tenerne traccia.However, instead of consuming analysis results as soon as they are available, the producer simply puts the tasks into 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);
    }
}

È inoltre presente un thread consumer che prende le attività dalla coda, attende che siano completate e visualizza il risultato o solleva l'eccezione che era stata generata.We also have a consumer thread, that is taking tasks off the queue, waiting for them to finish, and either displaying the result or raising the exception that was thrown. L'uso della coda assicura che i risultati vengano usati uno alla volta, nell'ordine corretto, senza limitare la frequenza dei fotogrammi massima del sistema.By using the queue, we can guarantee that 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();
 
    // Await 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);
    }
}

Implementazione della soluzioneImplementing the Solution

IntroduzioneGetting Started

Per fare in modo che l'app sia operativa il più rapidamente possibile, è stato implementato il sistema descritto in precedenza in modo che sia sufficientemente flessibile per implementare molti scenari e al contempo sia facile da usare.To get your app up and running as quickly as possible, we have implemented the system described above, intending it to be flexible enough to implement many scenarios, while being easy to use. Per accedere al codice, passare a https://github.com/Microsoft/Cognitive-Samples-VideoFrameAnalysis.To access the code, go to https://github.com/Microsoft/Cognitive-Samples-VideoFrameAnalysis.

La libreria contiene la classe FrameGrabber, che implementa il sistema producer-consumer discusso in precedenza per elaborare i fotogrammi di video ripresi da una webcam.The library contains the class FrameGrabber, which implements the producer-consumer system discussed above to process video frames from a webcam. L'utente può specificare il formato esatto della chiamata all'API e la classe usa gli eventi per indicare al codice chiamante quando viene acquisito un nuovo fotogramma o quando è disponibile un nuovo risultato dell'analisi.The user 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 a new analysis result is available.

Per illustrare alcune delle possibilità, sono disponibili due app di esempio che usano la libreria.To illustrate some of the possibilities, there are two sample apps that use the library. La prima è una semplice app console ed è una versione semplificata di quella riprodotta di seguito.The first is a simple console app, and a simplified version of this is reproduced below. Acquisisce i fotogrammi dalla webcam predefinita e li invia all'API Viso per il rilevamento del volto.It grabs frames from the default webcam, and submits them to the Face API for face detection.

using System;
using VideoFrameAnalyzer;
using Microsoft.ProjectOxford.Face;
using Microsoft.ProjectOxford.Face.Contract;
     
namespace VideoFrameConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create grabber, with analysis type Face[]. 
            FrameGrabber<Face[]> grabber = new FrameGrabber<Face[]>();
            
            // Create Face API Client. Insert your Face API key here.
            FaceServiceClient faceClient = new FaceServiceClient("<subscription key>");

            // Set up our Face API call.
            grabber.AnalysisFunction = async frame => return await faceClient.DetectAsync(frame.Image.ToMemoryStream(".jpg"));

            // Set up a listener for when we receive a new result from an API call. 
            grabber.NewResultAvailable += (s, e) =>
            {
                if (e.Analysis != null)
                    Console.WriteLine("New result received for frame acquired at {0}. {1} faces detected", e.Frame.Metadata.Timestamp, e.Analysis.Length);
            };
            
            // Tell grabber to call the Face API every 3 seconds.
            grabber.TriggerAnalysisOnInterval(TimeSpan.FromMilliseconds(3000));

            // Start running.
            grabber.StartProcessingCameraAsync().Wait();

            // Wait for keypress to stop
            Console.WriteLine("Press any key to stop...");
            Console.ReadKey();
            
            // Stop, blocking until done.
            grabber.StopProcessingAsync().Wait();
        }
    }
}

La seconda app di esempio è un po' più interessante e consente di scegliere quale API chiamare sui fotogrammi video.The second sample app is a bit more interesting, and allows you to choose which API to call on the video frames. Sul lato sinistro, l'app visualizza un'anteprima del video in diretta, mentre sul lato destro visualizza il risultato più recente dell'API sovrapposto al frame corrispondente.On the left-hand side, the app shows a preview of the live video, on the right-hand side it shows the most recent API result overlaid on the corresponding frame.

Nella maggior parte delle modalità, vi sarà un ritardo visibile tra il video in diretta a sinistra e l'analisi visualizzata a destra.In most modes, there will be a visible delay between the live video on the left, and the visualized analysis on the right. Il ritardo è il tempo necessario per effettuare la chiamata all'API.This delay is the time taken to make the API call. La modalità "EmotionsWithClientFaceDetect" fa eccezione. Questa modalità esegue il rilevamento del viso localmente nel computer client usando OpenCV, prima di inviare le immagini a Servizi cognitivi.The exception to this is in the "EmotionsWithClientFaceDetect" mode, which performs face detection locally on the client computer using OpenCV, before submitting any images to Cognitive Services. In questo modo è possibile visualizzare immediatamente il viso rilevato e quindi aggiornare le emozioni in un secondo momento dopo che la chiamata all'API ha prodotto il risultato.By doing this, we can visualize the detected face immediately, and then update the emotions later once the API call returns. Ciò dimostra la possibilità di un approccio "ibrido", in cui è possibile eseguire una semplice elaborazione nel client e successivamente usare le API Servizi cognitivi per potenziare l'elaborazione con questa analisi più avanzata quando necessario.This demonstrates the possibility of a "hybrid" approach, where some simple processing can be performed on the client, and then Cognitive Services APIs can be used to augment this with more advanced analysis when necessary.

Screenshot dell'app LiveCameraSample che mostra un'immagine con tag visualizzati

Integrazione nella codebaseIntegrating into your codebase

Per iniziare a usare questo esempio, seguire questa procedura:To get started with this sample, follow these steps:

  1. Ottenere le chiavi API per le API Visione da Sottoscrizioni.Get API keys for the Vision APIs from Subscriptions. Per l'analisi dei fotogrammi video, le API applicabili sono:For video frame analysis, the applicable APIs are:

  2. Clonare il repository di GitHub Cognitive-Samples-VideoFrameAnalysisClone the Cognitive-Samples-VideoFrameAnalysis GitHub repo

  3. Aprire l'esempio in Visual Studio 2015, compilare ed eseguire le applicazioni di esempio:Open the sample in Visual Studio 2015, build and run the sample applications:

    • Per BasicConsoleSample, la chiave dell'API Viso è hardcoded direttamente in BasicConsoleSample/Program.cs.For BasicConsoleSample, the Face API key is hard-coded directly in BasicConsoleSample/Program.cs.
    • Per LiveCameraSample, le chiavi devono essere immesse nel riquadro Impostazioni dell'app.For LiveCameraSample, the keys should be entered into the Settings pane of the app. Queste chiavi saranno persistenti tra le sessioni come i dati dell'utente.They will be persisted across sessions as user data.

Quando si è pronti per l'integrazione, fare semplicemente riferimento alla libreria VideoFrameAnalyzer dai propri progetti.When you're ready to integrate, simply reference the VideoFrameAnalyzer library from your own projects.

Le funzionalità di riconoscimento delle immagini, della voce, dei video o del testo di VideoFrameAnalyzer usano Servizi cognitivi di Azure.The image, voice, video or text understanding capabilities of VideoFrameAnalyzer uses Azure Cognitive Services. Microsoft riceve le immagini, l'audio, i video e altri dati che vengono caricati (tramite questa app) e può usarli per migliorare i servizi.Microsoft will receive the images, audio, video, and other data that you upload (via this app) and may use them for service improvement purposes. Gli sviluppatori devono contribuire a proteggere i dati degli utenti inviati dall'app a Servizi cognitivi di Azure.We ask for your help in protecting the people whose data your app sends to Azure Cognitive Services.

SummarySummary

In questa guida è stato appreso come eseguire l'analisi near real time di video in diretta streaming usando le API Viso, Visione artificiale ed Emozioni e come usare il codice di esempio per iniziare.In this guide, you learned how to run near-real-time analysis on live video streams using the Face, Computer Vision, and Emotion APIs, and how you can use our sample code to get started. È possibile iniziare a creare la propria app con chiavi API gratuite nella pagina di accesso a Servizi cognitivi di Azure.You can get started building your app with free API keys at the Azure Cognitive Services sign-up page.

È gradito ricevere commenti e suggerimenti nel repository GitHub o, per commenti più approfonditi sulle API, nel  sito UserVoice.Please feel free to provide feedback and suggestions in the GitHub repository, or for more broad API feedback, on our UserVoice site.