[英]Sending frame by VideoWriter; can't catching it again (OpenCV 3.1, c++)
[英]C++ OpenCV VideoWriter frame rate synchronization
我即将从视频采集卡中捕获帧。 这些帧被处理并写入硬盘。 整个设置是在一个多线程环境中,所以抓取器将图像写入队列,在另一个线程中,图像被处理,另一个写入硬盘。 如果根据处理器的定义图像是好的,图像就会被写入硬盘。 如果连续 10 张图像“坏”,则文件完成。 如果有 9 个或更少的“坏”图像,则所有图像都将写入下一个好图像,因此文件编写者会得到通知。
这里的问题是,如果我不这样做,而是在处理后直接写入每个文件,视频文件很好,但是写入了9个“坏”图像。 如果我按照上面描述的方式进行操作,则视频的速度/帧速率不合适。 这个描述有点奇怪,所以这里只是一个简化的例子,这样你就可以看到问题了:
void FrameWriter::writeFrameLoop() {
string path = getPath();
cv::Size2i size(1350, 1080);
cv::VideoWriter videoWriter(path, fourcc, 30, size);
while (this->isRunning) {
while (!this->frames.empty()) {
usleep(100000); // this effects the speed/frame
videoWriter.write(this->pop());
}
std::this_thread::sleep_for(10ms);
}
videoWriter.release();
}
这个例子很简单,在这里我用睡眠“阻塞”了写入过程,记住这是一个不同的线程。 这意味着捕获停止后,文件写入需要更长的时间。 但我希望这不会影响视频本身,因为帧率为 30 并且图像的顺序仍然相同。 但是对我来说,当我不及时调用“videoWriter.write”时,它似乎会影响视频文件。 在这种情况下,视频比预期的要快得多。 我以为只有配置的 30 帧和写入图像的数量会影响视频速度,但事实并非如此。 任何人都可以帮助我了解这里发生了什么吗?
我在 Ubuntu 18.04 上使用 openCV 4.4.0。 感谢您的帮助。 BR迈克尔
我想我知道快速播放结果视频的原因。
在构造函数cv::VideoWriter videoWriter(path, fourcc, 30, size);
您将生成的视频的帧速率 (FPS) 设置为 30。这意味着 CV 库期望write()
函数为生成的视频流的每 1 秒write()
30 帧。
同样对于 CV 库,使用新帧调用write()
速度没有区别,您可以每秒调用 5 次或 10 次甚至 1000 次。 唯一重要的是您必须为视频的每一秒提供 30 帧,而提供这 30 帧的速度并不重要。
我的意思是,您的所有sleep(...)
功能对于 CV VideoWriter 类都无关紧要。 并且对于所有视频渲染/转换库始终如此。 所以暂停线程根本不会改变任何东西。
但是在您的情况下,您是说您从采集卡视频卡的实时视频数据中每秒抓取 10 帧。 这意味着您的 FPS 真的是每秒 10 帧。 因此,为了正确解决您的任务,接下来应该做的事情是:
sleep()
。 它根本不需要。 并且不会改变 VideoWriter 的行为。cv::VideoWriter videoWriter(path, fourcc, 30, size);
值 30 到 10。这已经可以解决您的任务,但您必须确保每秒抓取 10 帧,不多不少。 那么您的视频将是一个正确播放(正确速度)的视频,帧速率为每秒 10 帧。 这是最简单的解决方案。 视频不需要为 30 FPS 以后才能正确播放,10 FPS 的视频以后任何播放器都可以正确播放。videWriter.write(...)
三次(在一个小循环中),调用这个write
没有任何暂停(如sleep
)。 然后,您生成的视频每秒将精确到 30 帧。 我想你只是想念了解CV::VideoWriter
是如何工作的。 您认为write()
实时渲染生成的视频,这意味着如果您将其提供 10 帧但恰好在一秒内,那么它应该渲染正确的视频速度。 但是这个作者不是实时渲染视频,这意味着它只是假设通过的 10 帧仅构成结果视频的 1/3 秒,因此它期望在 1 秒内写入 30 帧。
如果您的相机无法提供恒定的帧率,比如说 30 帧,您还可以考虑将帧率限制为例如 25,并测量自写入最后一帧以来经过的时间。 您还可以将帧率更改为任意值,只要相机能够提供即可。 一个实现示例:
m_fps = 25;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point now;
while (1) {
now = std::chrono::steady_clock::now();
long duration = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count();
if ((double)duration > (1e9 / m_fps)) {
start = std::chrono::steady_clock::now();
m_duration += duration;
// Capture frame-by-frame
if(m_cap->grab()) { m_cap->retrieve(m_frame); }
// write frame
m_writer->write(m_frame);
cv::waitKey(1);
}
}
我想出了我的问题,是我,而不是 OpenCV。 由于多线程环境,我将图像 (cv::Mat) 写入队列。 由于有很多转换 YUV -> RGB -> BGR -> Crop 等。我期待我放入队列的 cv::Mat 对象是一个深拷贝。 而不是这个, cv::Mat 总是同一个对象。 这意味着,即使我的队列中已经有 20 个 cv::Mat 条目,它们都是相同的,并且当有新图像时它们都“改变”了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.