在OpenCV 2.4.9中检测错误帧

8
我知道标题有点模糊,但我不确定如何描述它。
这是一个关于在CentOS上使用ffmpeg和OpenCV 2.4.9的问题。我的工作是开发一个简单的运动检测系统,通过IP摄像头( h264 )来实现视频流的监控。
偶尔视频流会出现"坏帧"(参见下面的pic-bad.png链接),问题在于这些帧与之前的帧差别很大,即使没有实际运动,也会触发"运动"事件。
下面的图片将解释问题:
好的一帧(捕捉到了运动): Good Frame 坏的一帧(没有运动,只是一个破碎的帧): Bad Frame 这些坏帧是随机抓取的,我想可以通过分析(循环)从某个位置向下的像素是否都相同来制作一个坏帧检测器,但我想知道是否有任何其他更有效,更符合规范的方法来检测这些类型的坏帧和跳过它们。
谢谢!
编辑更新:
该帧是通过C ++运动检测程序使用cvQueryFrame(camera);来抓取的,因此我不直接与ffmpeg进行交互,而是由OpenCV在后台处理。我正在使用从git源编译的最新版本的ffmpeg。所有库也都是最新的(h264等,昨天都下载和编译的)。数据来自RTSP流(ffserver)。我已经测试了多个摄像头(dahua 1-3 MP型号),帧故障在所有摄像头上都很持久,尽管它并不连续发生,只是偶尔会发生(例如:每10分钟一次)。

可能是ffmpeg编解码器的问题。但您可以在录制的视频上进行测试。请参见此处 - thedarkside ofthemoon
帧是通过C++运动检测程序使用cvQueryFrame(camera);抓取的,因此我不直接与ffmpeg进行交互,OpenCV在后端完成。我正在使用从git源编译的最新版本的ffmpeg。所有库也都是最新的。数据来自RTSP流。我已经在多个摄像头(大华)上进行了测试,帧故障非常持久,尽管它不会连续发生,只是偶尔发生(例如:每10分钟一次)。 - user3630380
cvQueryFrame(camera); 我认为这是 C 版本的,但你能发一些代码吗?也许你应该在 for 循环的末尾加上 cv::waitKey(10);,因为它可能太快了(也许...) - thedarkside ofthemoon
我在代码中加了waitKey(50)的等待代码。我之前也看到过这种情况,但是当视频以约30fps的速度播放时,每秒钟能看到30帧画面,如果只是单独一帧这样的情况并不是很严重。但是,在进行帧分析时就不同了。我有点惊讶没有人发现ffmpeg的这种行为。我99%确定这是由ffmpeg方面的某些解码问题引起的,我只是好奇是否有任何方法可以防止它或自动检测并删除它。 - user3630380
当我尝试保存图像并强制停止应用程序时,我曾经遇到过这种情况。然后我得到了一张不完整的图片。如果还没有更新ffmpeg,请尝试更新。 - thedarkside ofthemoon
显示剩余2条评论
3个回答

4
我首先想到的是通过计算有效帧的示例和我们正在检查的帧之间不同像素的数量来检查它们之间的不同。将此数字除以面积,我们得到百分比,用于测量不同之处。我猜在0.5以上,我们可以说测试的帧无效,因为它与有效帧的示例差异太大。
这种假设只适用于静态相机(不移动)和可以在其前面移动的物体不在最短距离(取决于焦距,但如果您拥有例如广角镜头,则物体不应该出现在距相机不到30厘米的地方,以防止物体“从无处跳入画面并使其大小超过50%的画面区域”)。
这里有一个OpenCV函数,可以实现我所说的功能。实际上,如果您认为运动变化会更快,可以将不同系数调整得更大。请注意,第一个参数应是有效帧的示例。
bool IsBadFrame(const cv::Mat &goodFrame, const cv::Mat &nextFrame) {
    // assert(goodFrame.size() == nextFrame.size())

    cv::Mat g, g2;
    cv::cvtColor(goodFrame, g, CV_BGR2GRAY);
    cv::cvtColor(nextFrame, g2, CV_BGR2GRAY);

    cv::Mat diff = g2 != g;

    float similarity = (float)cv::countNonZero(diff) / (goodFrame.size().height * goodFrame.size().width);

    return similarity > 0.5f;
}

1
这种方法可能有效,但我还没有测试过(我不在有代码的机器前),但这里有一个问题...在这种情况下,C++应用程序+OpenCV的目的是检测运动。通过比较当前帧与上一帧的变化来检测运动(已经完成),这就是为什么我们在当前没有任何运动时看到“运动”的原因...现在,如果一辆汽车停在摄像头前面,它将导致像素/颜色等的巨大变化,但我认为它也会触发上面的“IsBadFrame”函数。 - user3630380

1

您没有提到您是使用ffmpeg命令行还是库,但在后一种情况下,您可以检查坏帧标志(我忘记了它的确切描述),并简单地忽略那些帧。


在帖子编辑中添加。它是通过C++和OpenCV实现的,而不是直接通过ffmpeg实现的。 - user3630380

0

移除waitKey(50)或将其更改为waitKey(1)。我认为opencv不会生成新线程来执行捕获。因此,当有暂停时,它会混淆缓冲区管理例程,导致坏帧..也许?

我有大华相机,并观察到较高的延迟会导致出现坏帧。使用waitKey(1)可以完全消除这些问题。暂停不一定需要来自waitKey。如果调用例程需要足够长的时间,则也会导致这样的暂停并导致坏帧。

这意味着连续帧抓取之间应该有最小的暂停时间。解决方案是使用两个线程分别执行捕获和处理。


啊,非常有趣。我明天会试一下。谢谢分享!我会更新帖子的结果。 - user3630380
@user3630380:减少等待时间有用吗?你最终采取了什么解决方案?谢谢。 - saurabheights

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接