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.
Minták integrálása a kódbázisba
A minta használatának megkezdéséhez tegye a következőket:
- Hozzon létre egy Azure-fiókot. Ha már van ilyen, ugorjon a következő lépésre.
- 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.
- Klónozza a Cognitive-Samples-VideoFrameAnalysis GitHub-adattárat.
- Nyissa meg a mintát a Visual Studio 2015-ben vagy újabb verziójában, majd hozza létre és futtassa a mintaalkalmazásokat:
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.