Videoları neredeyse gerçek zamanlı olarak analiz etme

Bu makalede, canlı video akışından alınan kareler üzerinde neredeyse gerçek zamanlı analiz gerçekleştirmek için Azure AI Vision API'sinin nasıl kullanılacağı gösterilmektedir. Böyle bir analizin temel öğeleri şunlardır:

  • Video kaynağından kare alma
  • Analiz etmek istediğiniz çerçeveleri seçme
  • Bu çerçeveler API'ye gönderiliyor
  • API çağrısından döndürülen her analiz sonucunu kullanma

İpucu

Bu makaledeki örnekler C# dilinde yazılmıştır. Koda erişmek için GitHub'da Video çerçeve analizi örnek sayfasına gidin.

Gerçek zamanlıya yakın analiz çalıştırma yaklaşımları

Çeşitli yaklaşımlar kullanarak video akışlarında neredeyse gerçek zamanlı analiz çalıştırma sorununu çözebilirsiniz. Bu makalede, artan gelişmişlik düzeylerinde bunlardan üçü özetlenmiştir.

Yöntem 1: Sonsuz döngü tasarlama

Neredeyse gerçek zamanlı analiz için en basit tasarım sonsuz bir döngüdür. Bu döngünün her yinelemesinde uygulama bir çerçeve alır, analiz eder ve ardından sonucu işler:

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

Analiziniz basit ve istemci tarafı bir algoritmadan oluşacaksa bu yaklaşım uygun olacaktır. Ancak analiz bulutta gerçekleştiğinde ortaya çıkan gecikme süresi bir API çağrısının birkaç saniye sürebileceği anlamına gelir. Bu süre boyunca görüntüleri yakalamazsınız ve yazışmanız temelde hiçbir şey yapmıyor. Maksimum kare hızınız, API çağrılarının gecikme süresiyle sınırlıdır.

Yöntem 2: API çağrılarının paralel olarak çalışmasına izin verme

Basit, tek iş parçacıklı bir döngü basit ve istemci tarafı bir algoritma için anlamlı olsa da, bulut API çağrısının gecikme süresine uygun değildir. Bu sorunun çözümü, uzun süre çalışan API çağrısının çerçeve yakalama ile paralel olarak çalışmasına izin vermektir. C# dilinde, bunu görev tabanlı paralellik kullanarak yapabilirsiniz. Örneğin, aşağıdaki kodu çalıştırabilirsiniz:

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

Bu yaklaşımla, her analizi ayrı bir görevde başlatırsınız. Yeni çerçeveleri yakalamaya devam ederken görev arka planda çalıştırılabilir. Bu yaklaşım, bir API çağrısının döndürülmesini beklerken ana iş parçacığını engellemeyi önler. Ancak, yaklaşım belirli dezavantajları sunabilir:

  • Basit sürümün sağladığı garantilerden bazılarına mal olur. Başka bir ifadeyle, birden çok API çağrısı paralel olarak gerçekleşebilir ve sonuçlar yanlış sırada döndürülebilir.
  • Ayrıca, birden çok iş parçacığının Aynı anda ConsumeResult() işlevine girmesine neden olabilir; bu, işlev iş parçacığı güvenli değilse tehlikeli olabilir.
  • Son olarak, bu basit kod oluşturulan görevleri izlemez, bu nedenle özel durumlar sessizce kaybolur. Bu nedenle, analiz görevlerini izleyen, özel durumlar oluşturan, uzun süre çalışan görevleri öldüren ve sonuçların birer birer doğru sırada tüketilmesini sağlayan bir "tüketici" iş parçacığı eklemeniz gerekir.

Yöntem 3: Üretici-tüketici sistemi tasarlama

"Üretici-tüketici" sistemi tasarlamak için, önceki bölümün sonsuz döngüsüne benzer bir üretici iş parçacığı oluşturursunuz. Ardından üretici, analiz sonuçlarını kullanılabilir oldukları anda kullanmak yerine görevleri takip etmek için bir kuyruğa yerleştirir.

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

Ayrıca kuyruktan görevleri alan, bunların tamamlanmasını bekleyen ve sonucu görüntüleyen veya oluşan özel durumu tetikleyen bir tüketici iş parçacığı da oluşturursunuz. Bu kuyruğu kullanarak, sistemin en yüksek kare hızını sınırlamadan sonuçların doğru sırayla birer birer tüketildiğini garanti edebilirsiniz.

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

Çözümü uygulama

Örnek kodu alma

Uygulamanızın mümkün olan en kısa sürede çalışmaya başlamasını sağlamak için önceki bölümde açıklanan sistemi uyguladık. Kullanımı kolay olmakla birlikte birçok senaryoya uyum sağlamak için yeterince esnek olması amaçlanmıştır. Koda erişmek için GitHub'da Video kare analizi örnek deposuna gidin.

Kitaplık, bir web kamerasından FrameGrabber video çerçevelerini işlemek için üretici-tüketici sistemini uygulayan sınıfını içerir. Kullanıcılar API çağrısının tam biçimini belirtebilir ve sınıfı, çağrı kodunun yeni bir çerçevenin ne zaman alınacağını veya yeni bir analiz sonucunun kullanılabilir olduğunu bildirmek için olayları kullanır.

Örnek uygulamaları görüntüleme

Bazı olasılıkları göstermek için kitaplığı kullanan iki örnek uygulama sağladık.

İlk örnek uygulama, çerçeveleri varsayılan web kamerasından alan ve ardından yüz algılama için Yüz Tanıma hizmetine gönderen basit bir konsol uygulamasıdır. Uygulamanın basitleştirilmiş bir sürümü aşağıdaki kodda gösterilir:

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

İkinci örnek uygulama daha fazla işlevsellik sunar. Video karelerinde hangi API'yi çağırabileceğinizi seçmenize olanak tanır. Sol tarafta, uygulama canlı videonun önizlemesini gösterir. Sağ tarafta, ilgili çerçevede en son API sonucunu yer alır.

Çoğu modda, soldaki canlı video ile sağ taraftaki görselleştirilmiş analiz arasında görünür bir gecikme vardır. Bu gecikme, API çağrısı yapmak için geçen süredir. Azure AI hizmetlerine görüntü göndermeden önce OpenCV kullanarak istemci bilgisayarda yerel olarak yüz algılama gerçekleştiren modda bir özel durum EmotionsWithClientFaceDetect vardır.

Bu yaklaşımı kullanarak algılanan yüzü hemen görselleştirebilirsiniz. Ardından API çağrısı döndürdüğünde öznitelikleri daha sonra güncelleştirebilirsiniz. Bu, "karma" bir yaklaşım olasılığını gösterir. Başka bir ifadeyle, istemcide bazı basit işlemler gerçekleştirilebilir ve daha sonra azure yapay zeka hizmetleri API'leri gerektiğinde bu işlemeyi daha gelişmiş analizle artırmak için kullanılabilir.

The LiveCameraSample app displaying an image with tags

Örnekleri kod tabanınızla tümleştirme

Bu örneği kullanmaya başlamak için aşağıdakileri yapın:

  1. Azure hesabı oluşturun. Zaten varsa, sonraki adıma atlayabilirsiniz.
  2. Anahtarınızı ve uç noktanızı almak için Azure portalında Azure AI Vision ve Yüz Tanıma için kaynaklar oluşturun. Kurulum sırasında ücretsiz katmanı (F0) seçtiğinizden emin olun.
    • Azure AI Vision
    • Yüz Tanıma Kaynaklar dağıtıldıktan sonra, her kaynak için anahtarınızı ve uç noktanızı toplamak için Kaynağa git'i seçin.
  3. Cognitive-Samples-VideoFrameAnalysis GitHub deposunu kopyalayın.
  4. Visual Studio 2015 veya sonraki sürümlerinde örneği açın ve örnek uygulamaları derleyip çalıştırın:
    • BasicConsoleSample için Yüz Tanıma anahtarı doğrudan BasicConsoleSample/Program.cs içinde sabit kodlanır.
    • Live Kamera Sample için, uygulamanın Ayarlar bölmesine anahtarları girin. Anahtarlar oturumlar arasında kullanıcı verileri olarak kalıcıdır.

Örnekleri tümleştirmeye hazır olduğunuzda kendi projelerinizden VideoFrameAnalyzer kitaplığına başvurun.

VideoFrameAnalyzer'ın görüntü, ses, video ve metin anlama özellikleri Azure AI hizmetlerini kullanır. Microsoft yüklediğiniz görüntüleri, sesleri, videoları ve diğer verileri (bu uygulama aracılığıyla) alır ve bunları hizmet geliştirme amacıyla kullanabilir. Uygulamanızın azure yapay zeka hizmetlerine verilerini gönderdiği kişileri koruma konusunda yardımınızı istiyoruz.

Sonraki adımlar

Bu makalede, Yüz Tanıma ve Azure AI Vision hizmetlerini kullanarak canlı video akışlarında neredeyse gerçek zamanlı analiz çalıştırmayı öğrendiniz. Ayrıca, kullanmaya başlamak için örnek kodumuzu nasıl kullanabileceğinizi de öğrendiniz.

GitHub deposunda geri bildirim ve öneriler sağlamaktan çekinmeyin. Daha kapsamlı API geri bildirimi sağlamak için UserVoice sitemize gidin.