Videók elemzése közel valós időben

Ez a cikk bemutatja, hogyan használhatja az Azure AI Vision API-t közel valós idejű elemzésre az élő videóstreamből vett kereteken. Az ilyen elemzés alapvető elemei a következők:

  • Képkockák beszerzése videóforrásból
  • Az elemezni kívánt keretek kiválasztása
  • A keretek beküldése az API-ba
  • Az API-hívásból visszaadott elemzési eredmények felhasználása

Tipp.

A cikkben szereplő minták C#-ban vannak megírva. A kód eléréséhez nyissa meg a Video frame analysis mintalapot a GitHubon.

Közel valós idejű elemzések futtatásának megközelítései

A videóstreamek közel valós idejű elemzésének különböző megközelítésekkel történő futtatásával megoldhatja a problémát. Ez a cikk háromat vázol fel, a kifinomultság növelésével.

1. módszer: Végtelen hurok tervezése

A közel valós idejű elemzés legegyszerűbb kialakítása egy végtelen ciklus. A ciklus minden iterációjában az alkalmazás lekéri a keretet, elemzi, majd feldolgozza az eredményt:

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

Ha az elemzés egy egyszerű, ügyféloldali algoritmusból állna, ez a megközelítés megfelelő lenne. Ha azonban az elemzés a felhőben történik, az eredményként kapott késés azt jelenti, hogy egy API-hívás több másodpercet is igénybe vehet. Ez idő alatt nem rögzít képeket, és a szál lényegében semmit sem tesz. A maximális képkockasebességet az API-hívások késése korlátozza.

2. módszer: Az API-hívások párhuzamos futtatásának engedélyezése

Bár egy egyszerű, egyszálas huroknak van értelme egy egyszerű, ügyféloldali algoritmushoz, nem illik jól a felhőalapú API-hívások késéséhez. A probléma megoldásának célja, hogy a hosszan futó API-hívás párhuzamosan fusson a keret megragadásával. A C#-ban ezt feladatalapú párhuzamosság használatával teheti meg. Futtathatja például a következő kódot:

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

Ezzel a módszerrel minden elemzést külön feladatban indít el. A feladat futhat a háttérben, miközben továbbra is új kereteket fog. A megközelítés nem blokkolja a fő szálat, miközben vár egy API-hívás visszatérésére. A megközelítés azonban bizonyos hátrányokat okozhat:

  • Ez az egyszerű verzió bizonyos garanciáiba kerül. Ez azt jelentheti, hogy több API-hívás is előfordulhat párhuzamosan, és előfordulhat, hogy az eredmények helytelen sorrendben lesznek visszaadva.
  • Azt is okozhatja, hogy több szál egyszerre lép be a ConsumeResult() függvénybe, ami veszélyes lehet, ha a függvény nem szálbiztos.
  • Végül ez az egyszerű kód nem követi nyomon a létrehozott feladatokat, így a kivételek csendben eltűnnek. Ezért hozzá kell adnia egy "fogyasztó" szálat, amely nyomon követi az elemzési feladatokat, kivételeket emel ki, megöli a hosszan futó tevékenységeket, és biztosítja, hogy az eredmények a megfelelő sorrendben, egyenként felhasználva lesznek.

3. módszer: Gyártói-fogyasztói rendszer tervezése

A "gyártó-fogyasztó" rendszer tervezéséhez egy olyan gyártói szálat kell létrehoznia, amely az előző szakasz végtelen ciklusához hasonlóan néz ki. Ezután ahelyett, hogy az elemzési eredményeket azonnal elérhetővé teszik, a gyártó egyszerűen egy üzenetsorba helyezi a feladatokat, hogy nyomon kövesse őket.

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

Emellett létre kell hoznia egy fogyasztói szálat is, amely leveszi a feladatokat az üzenetsorról, megvárja, amíg befejeződnek, és vagy megjeleníti az eredményt, vagy felveti a kidobott kivételt. Ezzel az üzenetsorsal garantálhatja, hogy az eredmények egyenként, a megfelelő sorrendben, a rendszer maximális keretsebességének korlátozása nélkül lesznek felhasználva.

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

A megoldás implementálása

Mintakód letöltése

Annak érdekében, hogy az alkalmazás a lehető leggyorsabban működjön, implementáltuk az előző szakaszban leírt rendszert. Célja, hogy elég rugalmas legyen ahhoz, hogy számos forgatókönyvet elférjen, miközben könnyen használható. A kód eléréséhez nyissa meg a Video frame analysis sample adattárat a GitHubon.

A kódtár tartalmazza az FrameGrabber osztályt, amely megvalósítja a producer-fogyasztói rendszert a videoképek webkamerából történő feldolgozásához. A felhasználók megadhatja az API-hívás pontos formáját, és az osztály események használatával tudatja a hívó kóddal, hogy mikor szerez be új keretet, vagy mikor érhető el új elemzési eredmény.

Minta implementációk megtekintése

A lehetőségek egy részének szemléltetéséhez két mintaalkalmazást biztosítottunk, amelyek a kódtárat használják.

Az első mintaalkalmazás egy egyszerű konzolalkalmazás, amely megragadja az alapértelmezett webkamerából származó kereteket, majd elküldi őket a Face szolgáltatásnak arcfelismerés céljából. Az alkalmazás egyszerűsített verziója a következő kódban jelenik meg:

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

A második mintaalkalmazás további funkciókat kínál. Ez lehetővé teszi, hogy eldöntse, melyik API-t hívja meg a videókereteken. A bal oldalon az alkalmazás az élő videó előnézetét jeleníti meg. A jobb oldalon átfedi a legújabb API-eredményt a megfelelő kereten.

A legtöbb módban látható késés tapasztalható a bal oldali élő videó és a jobb oldali vizualizációs elemzés között. Ez a késés az API-hívás indításához szükséges idő. Kivételt képez az a mód, amely helyileg végzi el az EmotionsWithClientFaceDetect arcfelismerést az ügyfélszámítógépen az OpenCV használatával, mielőtt képeket küld az Azure AI-szolgáltatásoknak.

Ezzel a módszerrel azonnal megjelenítheti az észlelt arcot. Ezután később frissítheti az attribútumokat az API-hívás visszatérése után. Ez a "hibrid" megközelítés lehetőségét mutatja be. Ez azt jelent, hogy néhány egyszerű feldolgozás elvégezhető az ügyfélen, majd az Azure AI-szolgáltatások API-ival bővítheti ezt a feldolgozást, ha szükséges, fejlettebb elemzéssel.

The LiveCameraSample app displaying an image with tags

Minták integrálása a kódbázisba

A minta használatának megkezdéséhez tegye a következőket:

  1. Hozzon létre egy Azure-fiókot. Ha már van ilyen, ugorjon a következő lépésre.
  2. Erőforrások létrehozása az Azure AI Vision és a Face számára az Azure Portalon a kulcs és a végpont lekéréséhez. A beállítás során mindenképpen válassza ki az ingyenes szintet (F0).
    • Azure AI Vision
    • Az erőforrások üzembe helyezése után válassza az Ugrás az erőforrásra lehetőséget az egyes erőforrások kulcsának és végpontjának gyűjtéséhez.
  3. Klónozza a Cognitive-Samples-VideoFrameAnalysis GitHub-adattárat.
  4. Nyissa meg a mintát a Visual Studio 2015-ben vagy újabb verziójában, majd hozza létre és futtassa a mintaalkalmazásokat:
    • A BasicConsoleSample esetében a Face billentyű közvetlenül a BasicConsoleSample/Program.cs-ban van kódolva.
    • Live Kamera Sample esetén adja meg a kulcsokat az alkalmazás Gépház paneljén. A kulcsok a munkamenetek során felhasználói adatokként maradnak meg.

Ha készen áll a minták integrálására, hivatkozzon a VideoFrameAnalyzer könyvtárra a saját projektjeiből.

A VideoFrameAnalyzer kép-, hang-, video- és szövegértési képességei Azure AI-szolgáltatásokat használnak. A Microsoft megkapja a feltöltött képeket, hangokat, videókat és egyéb adatokat (az alkalmazáson keresztül), és szolgáltatásfejlesztési célokra használhatja őket. Segítséget kérünk azoknak a személyeknek a védelméhez, akiket az alkalmazás az Azure AI-szolgáltatásoknak küld.

Következő lépések

Ebben a cikkben megtanulta, hogyan futtathat közel valós idejű elemzést élő videóstreameken a Face és az Azure AI Vision szolgáltatással. Azt is megtanulta, hogyan használhatja a mintakódot az első lépésekhez.

Nyugodtan küldhet visszajelzést és javaslatokat a GitHub-adattárban. Ha szélesebb körű API-visszajelzést szeretne nyújtani, lépjen a UserVoice webhelyre.