在不触发网络摄像头的情况下调用人脸识别 API
Call face recognition API without triggering webcam
我正在尝试制作一个应用程序,它可以从网络摄像头识别人脸(只是 return 人脸的标志)。我已经为网络摄像头编写了代码,并从位图中进行面部分析。但是当我执行下面的代码时,网络摄像头就冻结了。我如何使用 async/await 来解决这个问题?另一个问题是,我怎样才能让我每 1 秒调用一次 AnalyzeFace
方法?我真的不知道该怎么做,所以我需要你的建议。
FaceDetectionFromFrame
检测到人脸并在其周围绘制一个矩形
form.scanPictureBox.Image
在图片框中显示当前帧
AnalyzeFace
returns 分析了面部的属性
我的帧处理代码:
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));
}
}
虽然你没有这么说,但在我看来,`ProcessFrame 是一个函数,每当相机想要通知你有新的 Frame 可供处理时调用。
显然处理这个新帧需要相当长的时间。甚至可能是在前一张图像尚未处理时已经抓取了一张新图像。
async-await 不会帮助你:你必须尽快抓取图像并命令另一个线程来处理获取的帧。您应该尽快从事件处理程序中 return ,最好是在处理帧之前。另一个可能的要求是抓取的帧应该按照抓取的顺序进行处理和显示。
下面我会告诉你更多关于async-await的知识。首先我会为你的相机问题提出一个解决方案。
您的问题的解决方案在 producer-consumer design pattern 中。生产者生产必须由消费者处理的数据。数据的生成速度可能比消费者处理数据的速度更快或更慢。如果在先前生产的数据被消费者处理之前有新数据可用,生产者应该将生产的数据保存在某个地方并继续生产。
每当消费者处理完生产数据时,它会检查是否有更多生产数据并开始处理它。
这一直持续到生产者通知消费者不再生产数据为止。
此生产者-消费者模式在 MSDN Task Parallel Library (TPL). This is downloadable as a nuget package: 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 仅在您的进程必须空闲地等待其他进程完成时才有意义,例如,当它正在等待数据库查询完成,或要写入的文件,或等待某些信息时从互联网上获取。在此期间,您的进程通常会空闲地等待,直到另一个进程完成。
要使函数异步:
- 将其声明为异步
- return
Task
而不是 void 和 Task<TResult>
而不是 TResult
- 唯一例外:事件处理程序 return 无效而不是任务:没有人等待事件处理程序完成
- 在你的异步函数中调用其他异步函数。
- 如果您还不需要异步函数的结果,但可以在异步函数完成之前做其他事情。不要等待它,记住 returned
Task
await
returned Task<TResult>
就在您需要异步函数的结果之前。
await Task<TResult>
的return为TResult
; await Task
有空 return.
- 好的做法:同时创建一个异步函数和一个非异步函数
乍一看,制作您的过程似乎可以解决您的问题。
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);
}
如果在上一个图像处理完成之前有新图像可用,则再次调用事件处理程序。如果第二张图片的处理速度比第一张图片的处理速度快,那么它会在第一张图片显示之前显示。
此外,您还必须注意这两个过程不会相互干扰。
因此,在您的情况下,使事件处理程序异步并不是一个好主意。
Only make event handlers async if you are certain that the event is completed before the next event is raised
如果图像抓取不是通过事件完成,而是直接向相机请求新图像,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
}
我正在尝试制作一个应用程序,它可以从网络摄像头识别人脸(只是 return 人脸的标志)。我已经为网络摄像头编写了代码,并从位图中进行面部分析。但是当我执行下面的代码时,网络摄像头就冻结了。我如何使用 async/await 来解决这个问题?另一个问题是,我怎样才能让我每 1 秒调用一次 AnalyzeFace
方法?我真的不知道该怎么做,所以我需要你的建议。
FaceDetectionFromFrame
检测到人脸并在其周围绘制一个矩形
form.scanPictureBox.Image
在图片框中显示当前帧
AnalyzeFace
returns 分析了面部的属性
我的帧处理代码:
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));
}
}
虽然你没有这么说,但在我看来,`ProcessFrame 是一个函数,每当相机想要通知你有新的 Frame 可供处理时调用。
显然处理这个新帧需要相当长的时间。甚至可能是在前一张图像尚未处理时已经抓取了一张新图像。
async-await 不会帮助你:你必须尽快抓取图像并命令另一个线程来处理获取的帧。您应该尽快从事件处理程序中 return ,最好是在处理帧之前。另一个可能的要求是抓取的帧应该按照抓取的顺序进行处理和显示。
下面我会告诉你更多关于async-await的知识。首先我会为你的相机问题提出一个解决方案。
您的问题的解决方案在 producer-consumer design pattern 中。生产者生产必须由消费者处理的数据。数据的生成速度可能比消费者处理数据的速度更快或更慢。如果在先前生产的数据被消费者处理之前有新数据可用,生产者应该将生产的数据保存在某个地方并继续生产。
每当消费者处理完生产数据时,它会检查是否有更多生产数据并开始处理它。
这一直持续到生产者通知消费者不再生产数据为止。
此生产者-消费者模式在 MSDN Task Parallel Library (TPL). This is downloadable as a nuget package: 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 仅在您的进程必须空闲地等待其他进程完成时才有意义,例如,当它正在等待数据库查询完成,或要写入的文件,或等待某些信息时从互联网上获取。在此期间,您的进程通常会空闲地等待,直到另一个进程完成。
要使函数异步:
- 将其声明为异步
- return
Task
而不是 void 和Task<TResult>
而不是TResult
- 唯一例外:事件处理程序 return 无效而不是任务:没有人等待事件处理程序完成
- 在你的异步函数中调用其他异步函数。
- 如果您还不需要异步函数的结果,但可以在异步函数完成之前做其他事情。不要等待它,记住 returned
Task
await
returnedTask<TResult>
就在您需要异步函数的结果之前。await Task<TResult>
的return为TResult
;await Task
有空 return.- 好的做法:同时创建一个异步函数和一个非异步函数
乍一看,制作您的过程似乎可以解决您的问题。
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);
}
如果在上一个图像处理完成之前有新图像可用,则再次调用事件处理程序。如果第二张图片的处理速度比第一张图片的处理速度快,那么它会在第一张图片显示之前显示。
此外,您还必须注意这两个过程不会相互干扰。
因此,在您的情况下,使事件处理程序异步并不是一个好主意。
Only make event handlers async if you are certain that the event is completed before the next event is raised
如果图像抓取不是通过事件完成,而是直接向相机请求新图像,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
}