在Windows中使用OpenCV 2.4.4和FFmpeg

7
我知道有其他关于OpenCV中使用FFmpeg的问题,但它们大多数似乎已经过时了。
通过打开CMake中的makefile文件,我可以验证是否已开启WITH_FFMPEG标志。我的OpenCV构建输出文件夹包含一个bin文件夹,在其中有Debug和Release文件夹,每个文件夹都包含名为opencv_ffmpeg244.dll的.dll文件的副本。当我创建VideoWriter并验证dll的函数指针正确填充时,我可以进入OpenCV的源代码。看起来一切运作正常。
如果我使用CV_FOURCC_PROMPT的FOURCC代码,以下编解码器将正常工作:
- Microsoft Video 1 - Intel IYUV codec - Logitech Video (I420) - Cinepak Codec by Radius - Full Frames (Uncompressed)
以下编解码器不起作用(即产生0kb视频文件):
- Microsoft RLE
如果我的理解是正确的,使用FFMPEG应该允许使用大量新编解码器(如x264、DIVX、XVID等)进行视频编码。但是,这些编解码器都没有出现在提示框中。尝试使用宏CV_FOURCC(...)手动设置它们的FOURCC代码也不起作用。例如,使用这个:CV_FOURCC('X','2','6','4')会产生以下消息:
无法找到编解码器ID 28的编解码器:未找到编解码器
并生成大小为0kb的视频文件。
而使用这个:CV_FOURCC('X','V','I','D')则没有错误消息,并生成大小为6kb的视频文件,但无法在Windows Media Player或VLC中播放。
我尝试手动从Xvid.org下载Xvid编解码器。安装后,它出现在提示框的VFW选择中,并且编码器可以正常工作。所以这已经接近一个解决方案了,但是如果我尝试直接设置FOURCC代码,它仍然会失败!我每次都必须从提示框中选择它。FFmpeg难道不应该包含大量编解码器吗?如果是这样,为什么我要手动下载编解码器而不是使用内置的FFmpeg?
我错过了什么?有没有办法检查FFMPEG是否“启用”?似乎提示框中唯一可用的编解码器是VFW编解码器,而不是FFMPEG编解码器。dll已构建并位于可执行文件相同的文件夹中,但似乎根本没有使用它。

这里有很多相关的问题。希望能找到对OpenCV中FFmpeg实现和这些组件如何拼合有一定了解的专业人士。

3个回答

1
如何分别运行ffmpeg和您的应用程序,并使用管道数据传输图像?为了将视频输入到OpenCV程序中,
ffmpeg -i input.mp4 -vcodec mjpeg -f image2pipe -pix_fmt yuvj420p -r 10 -|program.exe

和记录等等

(保留原文中的HTML标签)
program.exe|ffmpeg -r 10 -vcodec mjpeg -f image2pipe -i - -vcodec h264 output.mp4

program.exe 应该能够从标准输入读取连接的 JPEG 图像,并将其写入标准输出,上述工作流程将正常工作。这里是一些代码,用于从标准输入读取并显示视频。

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;

#if defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
    || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
# include <io.h>
# include <fcntl.h>
# define SET_BINARY_MODE(handle) setmode(handle, O_BINARY)
#else
# define SET_BINARY_MODE(handle) ((void)0)
#endif

#define BUFSIZE 10240
int main ( int argc, char **argv )
{

    SET_BINARY_MODE(fileno(stdin));
    std::vector<char> data;
    bool skip=true;
    bool imgready=false;
    bool ff=false;
    int readbytes=-1;
    while (1)
    {   
        char ca[BUFSIZE];
        uchar c;
        if (readbytes!=0)
        {
            readbytes=read(fileno(stdin),ca,BUFSIZE);
            for(int i=0;i<readbytes;i++)
            {
                c=ca[i];
                if(ff && c==(uchar)0xd8)
                {
                    skip=false;
                    data.push_back((uchar)0xff);
                }
                if(ff && c==0xd9)
                {
                    imgready=true;
                    data.push_back((uchar)0xd9);
                    skip=true;
                }
                ff=c==0xff;
                if(!skip)
                {
                    data.push_back(c);
                }
                if(imgready)
                {
                    if(data.size()!=0)
                    {
                        cv::Mat data_mat(data);
                        cv::Mat frame(imdecode(data_mat,1));

                        imshow("frame",frame);
                        waitKey(1);
                    }else
                    {
                        printf("warning");
                    }
                    imgready=false;
                    skip=true;
                    data.clear();
                }
            }
        }
        else
        {
            throw std::string("zero byte read");
        }
    }
}    

编写输出类似于这样的内容应该可以正常工作。

void saveFramestdout(cv::Mat& frame,int compression)
{
    SET_BINARY_MODE(fileno(stdout));
    cv::Mat towrite;
    if(frame.type()==CV_8UC1)
    {
        cvtColor(frame,towrite,CV_GRAY2BGR);
    }else if(frame.type()==CV_32FC3)
    {
        double minVal, maxVal;
        minMaxLoc(frame, &minVal, &maxVal);
        frame.convertTo(towrite, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
    }
    else{
        towrite=frame;
    }
    std::vector<uchar> buffer;
    std::vector<int> param(2);
    param[0]=CV_IMWRITE_JPEG_QUALITY;
    param[1]=compression;//default(95) 0-100
    imencode(".jpg",towrite,buffer,param);
    uchar* a = &buffer[0];
    ::write(fileno(stdout),a,buffer.size());
}

上述方法存在的问题是对JPEG进行了多次编码/解码,可以通过与libjpeg-turbo链接来部分解决。或者可以尝试直接从ffmpeg和opencv传递原始数据。对于我的情况来说,这是完全可以接受的,因为大部分开销都在编码或视频处理中。

0

已经有所进展

我使用了CV_FOURCC('x','2','6','4'),虽然它选择了正确的编解码器,但我的默认设置是错误的,我不确定如何正确地设置它们。

[libx264 @ 0x7f9f0b022600] broken ffmpeg default settings detected

相关的C++源代码

VideoCapture cap(argv[1]); // open file
if(!cap.isOpened()) { // check if we succeeded
    cout << "failed to open file " << argv[1] << endl;
    return -1;
}
Mat raw;
VideoWriter out(argv[2], CV_FOURCC('x','2','6','4'), fpsOut, size, true );
for(;;) {
    bool bSuccess = cap.read(raw); // get a new frame from camera
    if (!bSuccess) {
        cout << "finished reading file" << endl;
        break;
    }
    out << raw;
}

0

你的设置失败是因为ffmpeg为你创建了一个XVID编解码器,但无法操作它。因此你会得到无效的文件。

幸运的是,XVID编解码器已经在VfW中注册,所以你可以简单地删除opencv_ffmpeg * .dll库来禁用它。一旦不使用ffmpeg,你的fourcc“xvid”将通过VfW机制触发正确的编解码器。


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