OpenCV:VideoCapture :: get(CV_CAP_PROP_FPS)返回0 FPS

13
我正在尝试从相机中获取fps,以便可以将其传递给VideoWriter以输出视频。然而,当我从相机调用VideoCapture :: get(CV_CAP_PROP_FPS)时,我得到了0 fps。如果我硬编码它,我的视频可能会太慢或太快。
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <stdlib.h>

using namespace std;
using namespace cv;

int main(int argc, char *argv[])
{
    cv::VideoCapture cap;
    int key = 0;

    if(argc > 1){
        cap.open(string(argv[1]));
    }
    else
    {
        cap.open(CV_CAP_ANY);
    }
    if(!cap.isOpened())
    {
        printf("Error: could not load a camera or video.\n");
    }

    Mat frame;
    cap >> frame;
    waitKey(5);

    namedWindow("video", 1);
    double fps = cap.get(CV_CAP_PROP_FPS);
    CvSize size = cvSize((int)cap.get(CV_CAP_PROP_FRAME_WIDTH),(int)cap.get(CV_CAP_PROP_FRAME_HEIGHT));

    int codec = CV_FOURCC('M', 'J', 'P', 'G');
    if(!codec){ waitKey(0); return 0; }
    std::cout << "CODEC: " << codec << std::endl;
    std::cout << "FPS: " << fps << std::endl;
    VideoWriter v("Hello.avi",-1,fps,size);
    while(key != 'q'){
        cap >> frame;
        if(!frame.data)
        {
            printf("Error: no frame data.\n");
            break;
        }
        if(frame.empty()){ break; }
        v << frame;
        imshow("video", frame);
        key = waitKey(5);
    }
    return(0);
}

我该如何让VideoCapture::get(CV_CAP_PROP_FPS)返回正确的帧率,或为所有网络摄像头通用的VideoWriter提供一个可行的帧率?

4个回答

4

据我所知,CV_CAP_PROP_FPS仅适用于视频。如果您想从网络摄像头捕获视频数据,则必须自行正确计时。例如,使用定时器每40毫秒从网络摄像头捕获一帧,然后保存为25fps视频。


那个提议几乎肯定会产生一个卡顿的视频,因为视频中捕获的fps与网络摄像头的捕获fps不匹配。 - Jose Gómez
你能详细说明一下吗?为什么会产生卡顿的视频? - littleimp
3
网络摄像头采集视频时使用固定或定义好的帧率。对于给定的分辨率,捕获速度可以是15帧每秒、25帧每秒、60帧每秒等,这取决于摄像头的速度、链路带宽、驱动程序和设置。如果网络摄像头的帧率与你捕获的帧率不匹配,有时你可能会重复捕获同一帧,有时也可能会跳帧,因此运动看起来就不太流畅。 - Jose Gómez
2
获得平滑运动的关键是设置所需的FPS(如果驱动程序允许,请参见我的回答https://dev59.com/lmIk5IYBdhLWcg3wBaD7#22920585),并以网络摄像头提供的帧速率捕捉(或其除数,例如60-> 30,50-> 25,30-> 15)。 - Jose Gómez
好的,谢谢您的解释。但是我仍然需要手动计时捕获,对吗? - littleimp
我所做的是请求“期望”的帧率。但有时网络摄像头驱动程序会覆盖此设置并选择其他帧率。因此,您必须找出网络摄像头实际输出的帧率,因为您希望输出视频与源帧率匹配;否则,视频将播放得太快或太慢。 - Jose Gómez

2
你可以使用VideoCapture::set(CV_CAP_PROP_FPS)来设置摄像头的期望帧率,但是出于某些原因你不能使用get。
请注意,由于摄像头的限制,有时驱动程序会选择与您请求的不同的FPS。 我的解决方法:在几秒钟内捕获帧(我的测试中以4为佳,初始延迟为0.5秒),并估算相机输出的fps。

1

我从未观察到CV_CAP_PROP_FPS起作用。 我尝试使用各种OpenCV 2.4.x(当前为2.4.11)和文件输入。

在一个场景中,我通过直接使用libavformat(来自ffmpeg)获取帧速率来解决问题,然后可以在其他OpenCV代码中使用:

static double get_frame_rate(const char *filePath) {
    AVFormatContext *gFormatCtx = avformat_alloc_context();
    av_register_all();

    if (avformat_open_input(&gFormatCtx, filePath, NULL, NULL) != 0) {
        return -1;
    } else if (avformat_find_stream_info(gFormatCtx, NULL) < 0) {
        return -1;
    } 

    for (int i = 0; i < gFormatCtx->nb_streams; i++) {
        if (gFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            AVRational rate = gFormatCtx->streams[i]->avg_frame_rate;
            return (double)av_q2d(rate);
        }
    }

    return -1;
}

除此之外,无疑是获得平均每秒帧数最慢且肯定可行的方法之一,就是逐帧进行操作,并将当前帧数除以当前时间:

for (;;) {
    currentFrame = cap.get(CV_CAP_PROP_POS_FRAMES);
    currentTime = cap.get(CV_CAP_PROP_POS_MSEC);
    fps = currentFrame / (currentTime / 1000);

    # ... code ...
    # stop this loop when you're satisfied ...
}

你可能只会选择后者,如果其他直接查找fps的方法失败了,并且没有更好的方法来概括获取总体持续时间和帧数信息。上面的示例适用于文件--要适应相机,可以使用自开始捕获以来的经过的墙钟时间,而不是获取CV_CAP_PROP_POS_MSEC。然后,会话的平均fps将是经过的墙钟时间除以当前帧数。

有用的信息。然而,问题涉及使用(web)摄像头作为输入,而不是使用视频文件。 - Jose Gómez
@JoseGómez最后一段没有让你满意,是吗? - Jameson
是的,我错过了那个。你提出的听起来很合理。基本上,你建议计算有效帧率,对吗?这是我最终采取的类似方法。请注意,我必须在初始延迟后才开始计算帧速率,以使数字准确(可能由于某些初始化)。看起来很奇怪,为了获得从网络摄像头设置的当前帧速率这样简单的东西,人们必须经历如此多的麻烦。 - Jose Gómez
通常最长的等待时间是等待第一帧。我在几个网络摄像头上进行了测量,如果我们测量实时时间(而不是墙上时间 - CPU时间),从第一帧之后开始,间隔非常一致。还要注意,有时 cap.set(CV_CAP_PROP_FPS, /*someting*/) 会起作用。例如,在使用V4L2的Linux上,但仅当相机可以物理地提供那么多FPS时。 - Tomasz Gandor

-2

要从网络摄像头获取实时视频,请使用cap.get(cv2.CAP_PROP_FPS)


1
这就是 OP 所做的。这将返回 0。 - Tomasz Gandor

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