我有两个高速USB3相机(Ximea),并想编码一个应用程序的图像记录。帧速率在VGA分辨率下高达500fps,但我也想使用170fps的2Mpx分辨率。他们的.Net软件开发工具包告诉我,我应该简单地在循环中“获取”图像。我的问题是,我不知道如何在显示实时预览的同时获取并保存图像。每次我添加一些代码来实际更新picturebox时,帧速率都会急剧下降。
目前,我使用了一个录制函数,该函数是用
Task.Run(() => Record());在Record()中,我有一个获取位图的循环
while(record == true)
{
Camera.GetImage(out myImage, timeout); //From Ximea .Net SDK
Info = Camera.GetLastImageParams();
Timestamp = Info.GetTimeStamp();
ThreadPool.QueueUserWorkItem(state => SaveImage(myImage, filepath, Timestamp));
}由于SaveImage是
private void SaveImage(Bitmap myImage, string filepath, double Timestamp)
{
try
{
lock(myImage)
{
myImage.Save(filepath + Timestamp.ToString("0.00000") + ".tif");
}
}
catch{}
}如何在录制时显示实时预览,以及如何使整个代码更稳定(目前,由于Image.Save()调用中的“对象已在使用”-errors或“GDI+中的一般错误”,我使用try/catch语句跳过了一些丢弃的帧)?
发布于 2017-05-17 23:00:25
我相信您可以告诉Ximea API在传入队列中需要多少个图像缓冲区……适当使用XI_PRM_BUFFER_POLICY和XI_PRM_BUFFERS_QUEUE_SIZE,使队列长度稍长一些。然后,创建一个线程,该线程在激活时将图像从XI_IMG结构复制到您自己的缓冲区中。每隔n帧激活该线程(基于Ximea图像缓冲区队列的大小)...但是不要在实际调用xiGetImage的循环中进行任何内存复制。您可能应该在线程中阻塞以避免撕裂(因为如果您复制数据的速度不够快,Ximea代码可能会再次使用相同的缓冲区)……但是,您可以动态调整缓冲区的数量,以便在您拥有的时间内完成复制。此外,如果你正在做一些需要很长时间的事情,你可以考虑将图像数据复制到另一个缓冲区中。
伪代码(对不起,它是C语言):
// sync objects and a global image buffer pointer
CRITICAL_SECTION cs;
void *buf;
HANDLE ev;
int CopyImageThreadProc(...)
{
while (true)
{
if (WaitOnSingleObject(ev) == WAIT_OBJ_0)
{
EnterCriticalSection(cs);
// copy the image data at buf where ever you want
LeaveCriticalSection(cs);
}
}
}
int main(...)
{
// set up ximea api with appropriate buffering
// create event and critsec, start thread
while (!done)
{
XI_IMG img;
xiGetImage(dev, 10, &img);
// every 15 frames, tell your thread to go...
// if you find that the critsec is causing a hiccup, you can adjust this
// but remember to adjust the queue length, too
// if you change this to TRY entercriticalsection, you can determine that
if ((img.acq_nframe % 15) == 0)
{
EnterCriticalSection(cs);
buf = img.bp;
SetEvent(ev);
LeaveCriticalSection(cs);
}
}
// clean up
}发布于 2015-04-15 01:09:44
将每个捕获的帧添加到一个队列中,然后让一个工作线程获取这些图像,一次一个,并保存它们。尝试同时将多个映像写入磁盘的速度很可能会较慢。此外,一定要对任何GDI对象执行Dispose操作,否则很快就会遇到麻烦。我认为不这样做就是让你例外的原因。
至于显示图像,请确保您不是在尝试显示每个图像。你的监视器很可能运行在60赫兹,所以任何比这更快的都是浪费。我还怀疑(凭借GDI的性能),您甚至不一定能够做到这一点。所以我建议你有第二个队列来显示图像,如果你看到这个队列变得太大,你的程序将需要放慢一点速度,不要把那么多的帧推到队列中。
编辑:当然,正如@Franck提到的,如果你的磁盘跟不上,你的队列/缓冲区很快就会被填满。压缩图像可能会有所帮助,假设它们有适合压缩的内容,并且您的处理器可以跟上。
编辑:您需要的是生产者-消费者模式。有很多方法可以做到这一点,但其中一种可能是这样的:
// blocking collection
private BlockingCollection<Bitmap> m_Queue = ...
// camera thread
while( run )
{
var bitmap = GrabFrame();
m_Queue.Add( bitmap );
}
// worker thread
try
{
while( true )
{
// Take() will block if the queue is empty
var bitmap = m_Queue.Take();
bitmap.Save( ... );
bitmap.Dispose();
}
catch( InvalidOperationException )
{
// you'll end up here if you call `m_Queue.CompleteAdding()`
// (after the queue has been emptied, of course)
}至于显示图像,您可能会使用类似的东西,只是添加了一些代码来确定是否是时候推送新图像。
https://stackoverflow.com/questions/29632727
复制相似问题