2015-04-14 2 views
0

У меня есть две высокоскоростные камеры USB3 (Ximea) и вы хотите закодировать приложение для записи изображений. Фрейдеры до 500 кадров в секунду при разрешении VGA, но я также хочу использовать разрешение 2Mpx со скоростью 170 кадров в секунду. Их. SDK SDK говорит мне, что я должен просто «получить» изображения в цикле. Моя проблема в том, что я понятия не имею, как получить изображения и сохранить их, показывая предварительный просмотр в реальном времени. Каждый раз, когда я добавляю некоторый код для фактического обновления фрейма, частота кадров резко падает.Как сохранить фотографии с высокой частотой кадров с предварительным просмотром в реальном времени

В данный момент я использовать функцию записи, которая вызывается с

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{} 
} 

Как показать предварительный просмотр в реальном времени во время записи и как я могу сделать весь код mo (на данный момент есть некоторые отброшенные кадры из-за «объекта, который уже используется» - или «общая ошибка в GDI +» в вызове Image.Save(), который я пропускаю с помощью инструкции try/catch)?

+0

Вы знаете сохранение на жесткий диск - не лучшая идея. 2 mpx - это примерно 2,5 мб файлов. Вы хотите 170 изображений в секунду, поэтому хотите записать 425 МБ в секунду на ваш жесткий диск и по-прежнему сможете загрузить его в ящик для изображений. Если у вас SSD, у вас может быть небольшая скорость записи. Едва ешьте 300 мб в секунду. Вместо этого вы хотите использовать буфер. Рам гораздо быстрее читает и пишет. – Franck

+0

@Franck в моем случае Я сохраняю на SSD или Ram Disk - поэтому скорость записи должна быть достаточно быстрой. Кроме того, изображения монохромные, поэтому не такие большие - 2Mpx tifs 700KB с этой камерой. –

ответ

0

Добавить каждый захваченный кадр в очередь, затем иметь рабочий поток, который принимает эти изображения, по одному и сохраняет их. Попытка написать несколько изображений на диск в то же время, скорее всего, будет медленнее. Кроме того, всегда Dispose любых объектов GDI или вы столкнетесь с проблемой очень быстро. Я бы не думал, что это то, что дает вам исключения.

Что касается отображения изображений, убедитесь, что вы не пытаетесь отобразить каждое изображение. Ваш монитор, скорее всего, работает на частоте 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) 
} 

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

+0

К сожалению, я понятия не имею, как это сделать - некоторые Pseudo Code будут действительно оценены. Ошибка GDI обычно исходит из отметки времени, которая уже сохранена, - не знаю, как это возможно в очереди, но, по-видимому, иногда изображение с той же самой отметкой времени уже существует. –

+0

Тогда я добавлю! – Chris

+0

Крис прав насчет частоты обновления, 170 кадров в секунду бесполезно, если на экране нет не менее 170 Гц, и я думаю, что после 120-х годов он прыгнет до 300 Гц. И тратить так много денег на такой монитор, чтобы отображать очень маленькое видео, - странный выбор. – Franck

1

Я считаю, что вы можете сообщить API Ximea, сколько буферов изображений вы хотите во входящей очереди ... используйте 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 
} 
Смежные вопросы