我已经写了一个C#程序,它通过相机制造商的专有应用程序接口从一个专门的相机捕获视频。我可以通过FileStream对象将捕获的帧写入磁盘,但当涉及到帧率时,我受制于相机和磁盘I/O。
确保以所需的帧率写入磁盘的最佳方法是什么?是否有某种算法可以计算实时平均帧率,然后添加/丢弃帧以保持特定的期望帧率?
发布于 2013-08-15 09:08:26
在这种缺乏信息的情况下,很难说出任何事情。
格式是什么?有压缩吗?
摄像头API是如何发送帧的?它们是否计时,所以摄像头将发送您要求的帧速率?如果是这样的话,您实际上是在处理I/O速度。
如果您需要高质量,并且在没有压缩的情况下写入,您可以尝试一些无损压缩算法来平衡处理和驱动器I/O。如果驱动器I/O处的瓶颈很高,您可以获得如此高的速度。
对于框架,有一些方法可以实现这一点。正常情况下,帧有时间戳,您应该搜索时间戳,并丢弃距离其他帧太近的帧。假设你想要60fps,那么帧之间的间隔是1000/ 60 =16ms,如果你得到的帧有时间戳,比如说最后一帧后13ms,你可以丢弃它,不写入磁盘。
发布于 2022-01-10 00:39:40
在理想的情况下,您应该检查第一秒,它给出了您的系统支持的每秒帧数。
假设你的相机拍摄速度是60帧/秒,但你的电脑只能处理45帧/秒。你要做的就是每秒跳过15帧才能跟上。到目前为止,这已经很简单了。
这种基本情况下的数学运算是:
60 / 15 = 4因此,每四个传入帧跳过一帧,就像这样(保留标记有X的帧,跳过其他帧):
000000000011111111112222222222333333333344444444445555555555
012345678901234567890123456789012345678901234567890123456789
XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX当然,你很可能不会得到这样一个清晰的案例。数学仍然是一样的,只是你最终会在看起来不同的时间点跳过帧:
// simple algorithm based on fps from source & destination
// which fails to keep up long term
double camera_fps = 60.0;
double io_fps = camera_fps;
for(;;)
{
double frames_step = camera_fps / io_fps;
double start_time = time(); // assuming you get at least ms precision
double frame = 0.0;
for(int i(0); i < camera_fps; ++i)
{
buffer frame_data = capture_frame();
if(i == (int)frame) // <- part of the magic happens here
{
save_frame(frame_data);
}
frame += frames_step;
}
double end_time = time();
double new_fps = camera_fps / (end_time - start_time);
if(new_fps < io_fps)
{
io_fps = new_fps;
}
}如此算法所示,您希望随着时间的推移调整fps。在第一秒内,由于各种原因,它可能会给你一个无效的结果。例如,对磁盘的写入可能会被缓冲,所以速度非常快,看起来就像你可以支持60fps。稍后,fps会变慢,您可能会发现最大I/O速度为57fps。
这个基本算法的一个问题是,你可以很容易地减少帧的数量,使其在1秒内工作,但它只会减少fps (即,我只在new_fps较小时更新io_fps )。如果您找到了io_fps的正确数字,就可以了。这是因为当(end_time - start_time)恰好为1秒时,new_fps将为1.0,这意味着您没有花费太多时间来捕获和保存传入的帧。
这个问题的解决方案是对save_frame()函数进行计时。如果内部循环中的总帧数小于1秒,则可以增加可以保存的帧数。如果你可以使用两个线程,这将会工作得更好。一个线程读取帧,将帧推入内存中的FIFO,另一个线程从该FIFO中检索帧。这意味着捕获一帧的时间量不会(很大程度上)影响保存帧所需的时间。
bool stop = false;
// capture
for(;;)
{
buffer frame_data = capture_frame();
fifo.push(frame_data);
if(stop)
{
break;
}
}
// save to disk
double expected_time_to_save_one_frame = 1.0 / camera_fps;
double next_start_time = time();
for(;;)
{
buffer frame_data = fifo.pop();
double start_time = next_start_time;
save_frame(frame_data);
next_start_time = time();
double end_time = time();
if(start_time - end_time > expected_time_to_save_one_frame)
{
fifo.pop(); // skip one frame
}
}这只是伪代码,我可能犯了一些错误。它还期望start_time和end_time之间的间隔不会超过一帧(即,如果您有这样一种情况,即捕获速度为60 fps,而I/O支持不到30 fps,则通常必须连续跳过两帧)。
对于正在压缩帧的人,请记住,一次调用save_frame()的时间会有很大不同。有时,框架可以很容易地压缩(没有水平移动),有时它真的很慢。这就是这种活力可以提供极大帮助的地方。假设在记录过程中没有发生太多其他事情,那么一旦达到支持的最大速度,磁盘I/O应该不会有太大变化。
重要说明:这些算法假设相机fps是固定的;这可能不是很正确;您也可以对相机计时并相应地调整camera_fps参数(这意味着expected_time_to_save_one_frame变量也可以随时间变化)。
https://stackoverflow.com/questions/18244394
复制相似问题