首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用实时预览保存保存的高帧率摄像头图像

如何使用实时预览保存保存的高帧率摄像头图像
EN

Stack Overflow用户
提问于 2015-04-15 00:33:42
回答 2查看 1.1K关注 0票数 0

我有两个高速USB3相机(Ximea),并想编码一个应用程序的图像记录。帧速率在VGA分辨率下高达500fps,但我也想使用170fps的2Mpx分辨率。他们的.Net软件开发工具包告诉我,我应该简单地在循环中“获取”图像。我的问题是,我不知道如何在显示实时预览的同时获取并保存图像。每次我添加一些代码来实际更新picturebox时,帧速率都会急剧下降。

目前,我使用了一个录制函数,该函数是用

代码语言:javascript
复制
Task.Run(() => Record());

在Record()中,我有一个获取位图的循环

代码语言:javascript
复制
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是

代码语言:javascript
复制
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语句跳过了一些丢弃的帧)?

EN

回答 2

Stack Overflow用户

发布于 2017-05-17 23:00:25

我相信您可以告诉Ximea API在传入队列中需要多少个图像缓冲区……适当使用XI_PRM_BUFFER_POLICY和XI_PRM_BUFFERS_QUEUE_SIZE,使队列长度稍长一些。然后,创建一个线程,该线程在激活时将图像从XI_IMG结构复制到您自己的缓冲区中。每隔n帧激活该线程(基于Ximea图像缓冲区队列的大小)...但是不要在实际调用xiGetImage的循环中进行任何内存复制。您可能应该在线程中阻塞以避免撕裂(因为如果您复制数据的速度不够快,Ximea代码可能会再次使用相同的缓冲区)……但是,您可以动态调整缓冲区的数量,以便在您拥有的时间内完成复制。此外,如果你正在做一些需要很长时间的事情,你可以考虑将图像数据复制到另一个缓冲区中。

伪代码(对不起,它是C语言):

代码语言:javascript
复制
// 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
}
票数 1
EN

Stack Overflow用户

发布于 2015-04-15 01:09:44

将每个捕获的帧添加到一个队列中,然后让一个工作线程获取这些图像,一次一个,并保存它们。尝试同时将多个映像写入磁盘的速度很可能会较慢。此外,一定要对任何GDI对象执行Dispose操作,否则很快就会遇到麻烦。我认为不这样做就是让你例外的原因。

至于显示图像,请确保您不是在尝试显示每个图像。你的监视器很可能运行在60赫兹,所以任何比这更快的都是浪费。我还怀疑(凭借GDI的性能),您甚至不一定能够做到这一点。所以我建议你有第二个队列来显示图像,如果你看到这个队列变得太大,你的程序将需要放慢一点速度,不要把那么多的帧推到队列中。

编辑:当然,正如@Franck提到的,如果你的磁盘跟不上,你的队列/缓冲区很快就会被填满。压缩图像可能会有所帮助,假设它们有适合压缩的内容,并且您的处理器可以跟上。

编辑:您需要的是生产者-消费者模式。有很多方法可以做到这一点,但其中一种可能是这样的:

代码语言:javascript
复制
// 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)
}

至于显示图像,您可能会使用类似的东西,只是添加了一些代码来确定是否是时候推送新图像。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29632727

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档