Вызов API распознавания лиц без запуска веб-камеры - PullRequest
0 голосов
/ 24 сентября 2018

Я пытаюсь создать приложение, которое распознает лицо по веб-камере (просто верните ориентиры лица).Я написал код для веб-камеры и анализ лица по растровому изображению.Но когда я выполняю код ниже, веб-камера просто зависает.Как я могу использовать async / await, чтобы это исправить?Дополнительный вопрос был бы, как я могу вызывать метод AnalyzeFace только раз в 1 секунду?Я пока не знаю, как это сделать, поэтому мне нужны ваши советы.

FaceDetectionFromFrame обнаруживает лицо и рисует вокруг него прямоугольник

form.scanPictureBox.Image показывает текущий кадр в графическом окне

AnalyzeFace возвращает проанализированные свойства лица

Код обработки моего кадра:

private static void ProcessFrame(object sender, EventArgs e)
    {
        List<string> faceList = new List<string>();
        using (var imageFrame = capture.QueryFrame().ToImage<Bgr, Byte>())
        {
            FaceDetection.FaceDetectionFromFrame(imageFrame); // Face detection
            var form = FormFaceDetection.Current;
            form.scanPictureBox.Image = imageFrame.Bitmap;

            faceList.Add(FaceRecognition.AnalyzeFace(imageFrame.Bitmap));
        }
    }

1 Ответ

0 голосов
/ 26 сентября 2018

Хотя вы и не сказали этого, мне кажется, что `ProcessFrame - это функция, которая вызывается всякий раз, когда камера хочет уведомить вас о том, что новый кадр доступен для обработки.

Видимо, обрабатывает этот новыйкадр занимает значительное время.Может даже случиться, что новое изображение уже захвачено, когда предыдущее еще не обработано.

async-await не поможет вам в этом: вам нужно будет захватить изображения как можно быстрее и заказать другой поток для обработки извлеченного кадра.Вы должны вернуться из обработчика событий как можно скорее, предпочтительно до обработки кадра.Другое вероятное требование заключается в том, что захваченные кадры должны обрабатываться и отображаться в том порядке, в котором они были захвачены.

Ниже я расскажу вам больше об асинхронном ожидании.Сначала я предложу решение вашей проблемы с камерой.

Решение вашей проблемы заключается в шаблоне дизайна производителя-потребителя .Производитель производит данные, которые должны быть обработаны потребителем.Данные могут быть получены быстрее или медленнее, чем они могут быть обработаны потребителем.Если новые данные доступны до того, как ранее произведенные данные были обработаны потребителем, производитель должен сохранить произведенные данные где-нибудь и продолжить производство.

Всякий раз, когда потребитель обработал произведенные данные, он проверяет, есть ли еще произведенные данныеи начинает его обработку.

Это продолжается до тех пор, пока производитель не сообщит потребителю, что данные больше не будут производиться.

Этот шаблон для потребителя продукта реализован со всей безопасностью многопоточности в Параллельная библиотека задач MSDN (TPL) .Это можно загрузить как пакет nuget: Microsoft Tpl Dataflow

Вам нужны два потока: производитель и потребитель.Производитель создает изображения максимально быстро.Полученные изображения сохраняются в BufferBlock<Frame>.

. Другой поток будет использовать созданные кадры изображений.

// the buffer to save frames that need to be processed:
private readonly BufferBlock<ImageFrame> buffer = new BufferBlock<ImageFrame>();

// event handler to be called whenever the camera has an image
// similar like your ProcessFrame
public async void OnImageAvailableAsync(object sender, EventArgs e)
{
    // the sender is your camera who reports that an image can be grabbed:
    ImageGrabber imageGrabber = (ImageGrabber)sender;

    // grab the image:
    ImageFrame grabbedFrame = imageGrabber.QueryFrame();

    // save it on the buffer for processing:
    await this.buffer.SendAsync(grabbedFrame);

    // finished producing the image frame
}

Потребитель:

// this task will process grabbed images that are in the buffer
// until there are no more images to process
public async Task ProcessGrabbedImagesAsync()
{
    // wait for data in the buffer
    // stop waiting if no data is expected anymore
    while (await buffer.OutpubAvailableAsync())
    {
        // The producer put some data in the buffer.
        // Fetch it and process it.
        // This may take some time, which is no problem. If the producer has new frames
        // they will be saved in the buffer
        FaceDetection.FaceDetectionFromFrame(imageFrame); // Face detection
        var form = FormFaceDetection.Current;
        form.scanPictureBox.Image = imageFrame.Bitmap;

        faceList.Add(FaceRecognition.AnalyzeFace(imageFrame.Bitmap));
    }
}

Использование:

// Start a consumer task:
Task taskConsumer = task.Run( () => ProcessGrabbedImagesAsync());

// subscribe to the camera's event:
camera.EventImageAvailable += OnImageAvailableAsync;
camera.StartImageGrabbing();

// free to do other things

Вам понадобятся операторы, чтобы прекратить захват изображения

camera.StopImageGrabbing();
// unsubscribe:
camera.EventImageAvailable -= OnImageAvailableAsync;    
// notify that no images will be produced:
buffer.Complete();

// await until the consumer is finished processing all produced images:
await taskConsumer;

Об async-await

async-await имеет смысл только в том случае, если ваш процесс должен бездействоватькакой-то другой процесс, который нужно завершить, например, когда он ожидает завершения запроса к базе данных, или файла, который нужно записать, или некоторой информации, которую нужно извлечь из Интернета.В течение этого времени ваш процесс обычно будет просто бездействовать, пока другой процесс не завершится.

Чтобы сделать функцию асинхронной:

  • объявить ее асинхронной
  • return Task вместо void и Task<TResult> вместо TResult
  • единственное исключение: обработчики событий возвращают void вместо Task: никто не ждет завершения обработчика событий
  • Внутри вызова асинхронных функцийдругие асинхронные функции.
  • Если вам пока не нужен результат асинхронной функции, но вы можете выполнить другие действия до завершения асинхронной функции.пока не ждите этого, запомните возвращенный Task
  • await возвращенный Task<TResult> непосредственно перед тем, как вам понадобится результат асинхронной функции.
  • Возвращение await Task<TResult> is TResult;await Task имеет возврат void.
  • Хорошая практика: создать как асинхронную, так и не асинхронную функцию

На первый взгляд кажется, что выполнение вашего процесса решит проблемуВаша проблема.

private async void ProcessFrameAsync(object sender, EventArgs e)
{   // async event handlers return void instead of Task

    var grabbedImage = await camera.FetchImageAsync();

    // or if your camera has no async function:
    await Task.Run( () => camera.FetchImage());

    // this might be a length process:
    ProcessImaged(grabbedImage); 
    ShowImage(grabbedImage);
}

Если новое изображение доступно до того, как предыдущий будет полностью обработан, тогда обработчик события вызывается снова.Если обработка 2-го изображения выполняется быстрее, чем обработка первого изображения, то она отображается перед показом первого изображения.

Кроме того, вам нужно позаботиться о том, чтобы эти два процесса не мешали.

Следовательно, в вашем случае не рекомендуется делать асинхронный обработчик событий.

Делать обработчики событий асинхронными, только если вы уверены, что событие завершенодо следующего события

Если захват изображения был сделан не с помощью событий, а напрямую, запросив у камеры новое изображение, async-await поможет:

async Task GrabAndProcessImages(CancellationToken token)
{
     // grab the first image:
     var grabbedImage = await camera.GrabImageAsync(token);
     while (!token.CancellationRequested)
     {
          // start grabbing the next image, do not wait for it yet
          var taskGrabImage = camera.GrabImageAsync(token);

          // because I'm not awaiting, I'm free to do other things
          // like processing the last grabbed image:
          ProcessImage(grabbedImage);

          // await for the next image:
          grabbedImage = await taskGrabImage;
     }
}

Использование:

using(var cancellationTokenSource = new cancellationTokenSource())
{
     Task taskProcessImages = grabAndProcessImages(cancellationTokenSource.Token);

     // because I did not await, I'm free to do other things,
     DoSomeThingElse();

     // To stop grabbing images: cancel the cancellationTokenSource:
     cancellationTokenSource.Cancel();

     // or if you want to be sure that it grabbed for at least 30 seconds:
     cancellationTokeSource.CanceAfter(TimeSpan.FromSeconds(30));

     // still free to do something else,
     DoSomeThingElse();

     // before returning: await until the image grabbing task completes:
     await taskProcessImages;

     // if here, you are certain that processing images is completed
}
...