Bagikan melalui


Menganalisis video hampir waktu nyata

Artikel ini menunjukkan cara menggunakan Azure AI Vision API untuk melakukan analisis mendekati real-time pada bingkai yang diambil dari streaming video langsung. Elemen dasar dari analisis tersebut adalah:

  • Memperoleh bingkai dari sumber video
  • Memilih bingkai mana yang akan dianalisis
  • Mengirimkan bingkai ini ke API
  • Mengkonsumsi setiap hasil analisis yang dikembalikan dari panggilan API

Petunjuk

Sampel dalam artikel ini ditulis dalam C#. Untuk mengakses kode, buka halaman Sampel analisis bingkai video di GitHub.

Pendekatan untuk pelaksanaan analisis mendekati real-time

Anda dapat menyelesaikan masalah menjalankan analisis mendekati real-time pada streaming video menggunakan berbagai pendekatan. Artikel ini menguraikan tiga di antaranya, dalam level kecanggihan yang makin tinggi.

Metode 1: Merancang perulangan tak terbatas

Desain paling sederhana untuk analisis mendekati real-time adalah perulangan tak terbatas. Dalam setiap perulangan ini, aplikasi mengambil bingkai, menganalisisnya, lalu memproses hasilnya:

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

Jika analisis Anda terdiri dari algoritma sisi klien yang ringan, pendekatan ini akan cocok. Namun, ketika analisis terjadi di cloud, latensi yang dihasilkan akan menyebabkan panggilan API mungkin memerlukan waktu beberapa detik. Selama waktu ini, Anda tidak menangkap gambar, dan utas Anda pada dasarnya tidak melakukan apa-apa. Kecepatan bingkai maksimum Anda dibatasi oleh latensi panggilan API.

Metode 2: Izinkan panggilan API berjalan secara paralel

Meskipun perulangan sederhana berulir tunggal masuk akal untuk algoritma sisi klien yang ringan, namun tidak cocok dengan latensi panggilan API di cloud. Solusi untuk masalah ini adalah mengizinkan panggilan API yang berjalan lama untuk berjalan secara paralel dengan pengambilan bingkai. Anda bisa melakukan ini di C# menggunakan paralelisme berbasis tugas. Misalnya, Anda dapat menjalankan kode berikut:

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

Dengan pendekatan ini, Anda meluncurkan setiap analisis dalam tugas terpisah. Tugas dapat berjalan di latar belakang saat Anda terus meraih bingkai baru. Pendekatan tersebut menghindari pemblokiran utas utama saat Anda menunggu panggilan API kembali. Namun, pendekatan ini juga dapat menghasilkan kerugian tertentu:

  • Anda kehilangan beberapa jaminan yang diberikan oleh versi sederhana. Artinya, beberapa panggilan API mungkin terjadi secara paralel, dan hasilnya mungkin dikembalikan dalam urutan yang salah.
  • Ini juga dapat menyebabkan beberapa utas memasuki fungsi ConsumeResult() secara bersamaan, yang mungkin berbahaya jika fungsinya tidak aman untuk utas.
  • Akhirnya, kode sederhana ini tidak melacak tugas yang dibuat, sehingga pengecualian menghilang tanpa disadari. Untuk itu, Anda perlu menambahkan utas "konsumen" yang melacak tugas analisis, menghasilkan pengecualian, menghentikan tugas yang berlangsung terlalu lama, dan memastikan bahwa hasilnya dikonsumsi dalam urutan yang benar, satu per satu.

Metode 3: Merancang sistem produsen-konsumen

Untuk merancang sistem "produsen-konsumen", Anda membangun thread produsen yang terlihat mirip dengan perulangan tak terbatas bagian sebelumnya. Kemudian, alih-alih mengkonsumsi hasil analisis segera setelah tersedia, produsen hanya menempatkan tugas dalam antrean untuk melacaknya.

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

Anda juga membuat utas konsumen yang mengambil tugas dari antrean, menunggu tugas selesai, dan menampilkan hasilnya atau memunculkan pengecualian yang dilemparkan. Dengan menggunakan antrean ini, Anda dapat menjamin bahwa hasilnya dikonsumsi satu per satu, dalam urutan yang benar, tanpa membatasi kecepatan bingkai maksimum sistem.

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

Terapkan solusi

Dapatkan sampel kode

Untuk membantu aplikasi Anda aktif dan berjalan secepat mungkin, kami telah menerapkan sistem yang dijelaskan di bagian sebelumnya. Ini dimaksudkan agar cukup fleksibel dalam mengakomodasi banyak skenario, sekaligus mudah digunakan. Untuk mengakses kode, buka repositori sampel analisis bingkai video di GitHub.

Pustaka berisi kelas FrameGrabber, yang mengoperasikan sistem produsen-konsumen untuk memproses frame video dari webcam. Pengguna dapat menentukan bentuk panggilan API yang tepat, dan kelas menggunakan peristiwa untuk memberi tahu kode panggilan saat bingkai baru diperoleh, atau ketika hasil analisis baru tersedia.

Lihat implementasi sampel

Untuk mengilustrasikan beberapa kemungkinan, telah tersedia dua aplikasi sampel yang menggunakan pustaka.

Sampel pertama adalah aplikasi konsol sederhana yang mengambil bingkai dari webcam default, lalu mengirimkannya ke layanan Face untuk pendeteksian wajah. Versi aplikasi yang disederhanakan diwakili dalam kode berikut:

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

Aplikasi sampel kedua menawarkan lebih banyak fungsionalitas. Aplikasi ini memungkinkan Anda untuk memilih API mana yang akan dipanggil pada bingkai video. Di sisi kiri, aplikasi menampilkan pratinjau video secara langsung. Di sebelah kanan, ia menampilkan hasil API terbaru pada bingkai terkait.

Di sebagian besar mode, ada sedikit penundaan antara video langsung di sebelah kiri dan visualisasi analisis di sebelah kanan. Penundaan ini adalah waktu yang diperlukan untuk melakukan panggilan API. Pengecualian ada dalam EmotionsWithClientFaceDetect mode , yang melakukan deteksi wajah secara lokal di komputer klien dengan menggunakan OpenCV sebelum mengirimkan gambar apa pun ke layanan Azure AI.

Melalui pendekatan ini, Anda dapat segera memvisualisasikan wajah yang terdeteksi. Anda kemudian dapat memperbarui atribut nanti, setelah panggilan API kembali. Ini menunjukkan kemungkinan pendekatan "hibrida". Artinya, beberapa pemrosesan sederhana dapat dilakukan pada klien, dan kemudian API layanan Azure AI dapat digunakan untuk menambah pemrosesan ini dengan analisis yang lebih canggih jika diperlukan.

Aplikasi LiveCameraSample menampilkan gambar dengan tag

Mengintegrasikan sampel ke dalam basis kode Anda

Untuk memulainya dengan contoh ini, lakukan hal berikut:

  1. Buat akun Azure. Jika sudah memilikinya, Anda dapat melompat ke langkah berikutnya.
  2. Buat sumber daya untuk Azure AI Vision dan Face di portal Azure untuk mendapatkan kunci dan titik akhir Anda. Pastikan untuk memilih tingkat gratis (F0) selama pengaturan.
    • Azure AI Vision
    • Face Setelah sumber daya disebarkan, pilih Pergi ke sumber daya untuk mengumpulkan kunci dan titik akhir Anda untuk setiap sumber daya.
  3. Klon repositori GitHub Cognitive-Samples-VideoFrameAnalysis.
  4. Buka sampel di Visual Studio 2015 atau yang lebih baru, lalu buat dan jalankan aplikasi sampel:
    • Untuk BasicConsoleSample, kunci Face dikodekan langsung secara permanen di BasicConsoleSample/Program.cs.
    • Untuk LiveCameraSample, masukkan kunci di panel Pengaturan aplikasi. Kunci dipertahankan di seluruh sesi sebagai data pengguna.

Saat Anda siap untuk mengintegrasikan sampel, referensikan pustaka VideoFrameAnalyzer dari proyek Anda sendiri.

Kemampuan pemahaman gambar, suara, video, dan teks VideoFrameAnalyzer menggunakan layanan Azure AI. Microsoft menerima gambar, audio, video, dan data lain yang Anda unggah (melalui aplikasi ini) dan mungkin menggunakannya untuk tujuan peningkatan layanan. Kami meminta bantuan Anda dalam melindungi orang-orang yang datanya dikirim aplikasi Anda ke layanan Azure AI.

Langkah berikutnya

Dalam artikel ini, Anda mempelajari cara menjalankan analisis mendekati real-time pada streaming video langsung dengan menggunakan layanan Face dan Azure AI Vision. Anda juga mempelajari bagaimana cara menggunakan kode sampel kami untuk memulai.

Jangan ragu untuk memberikan umpan balik dan saran di repositori GitHub. Untuk memberikan umpan balik API yang lebih luas, buka situs UserVoice kami.