【问题标题】:Create video from array of pixel values in C++从 C++ 中的像素值数组创建视频
【发布时间】:2014-06-15 10:32:55
【问题描述】:

有谁知道将像素值序列保存到视频的数组中的方法?目前我正在使用 Cimg 来可视化一个简单的 n 体模拟,虽然我可以将每次迭代保存到一个图像文件中,但这非常慢。任何有关处理视频的类似库的建议将不胜感激。本质上,我只想将我创建的 Cimg 窗口中显示的内容记录到视频文件中。程序用C++编写,在linux上,用g++编译。

我可以运行模拟并使用屏幕捕捉软件记录它的运行情况,这似乎意味着这是可能的,但我想要一个更简洁的解决方案。

【问题讨论】:

    标签: c++ video cimg


    【解决方案1】:

    我今天正在玩这个,并认为我会分享我的结果。您可以从 CImg 输出原始 RGB 视频,然后使用 ffmpeg 将其编码为如下视频:

    #include <iostream>
    #include "CImg.h"
    
    using namespace std;
    using namespace cimg_library;
    
    int main()
    {
       const unsigned int width=1024;
       const unsigned int height=768;
    
       // Basic frame we will draw in
       CImg<unsigned char> image(width,height,1,3);
    
       unsigned char magenta[] = {255,0,255};
    
       // We are going to output 300 frames of 1024x768 RGB raw video
       // ... making a 10s long video at 30fps
       int radius=100;
       int cx=100;
       int cy=100;
       for(int frame=0;frame<300;frame++){
          // Start with black - it shows fewer stains ;-)
          image.fill(0);
          image.draw_circle(cx,cy,radius,magenta);
    
          // Move and re-colour circle
          cx+=2; cy++; if(magenta[1]!=255){magenta[1]++;}
    
          // Output to ffmpeg to make video, in planar GBR format
          // i.e. run program like this
          // ./main | ffmpeg -y -f rawvideo -pixel_format gbrp -video_size 1024x768 -i - -c:v h264 -pix_fmt yuv420p video.mov
          char* s=reinterpret_cast<char*>(image.data()+(width*height));   // Get start of G plane
          std::cout.write(s,width*height);                                // Output it
          s=reinterpret_cast<char*>(image.data()+2*(width*height));       // Get start of B plane
          std::cout.write(s,width*height);                                // Output it
          s=reinterpret_cast<char*>(image.data());                        // Get start of R plane
          std::cout.write(s,width*height);                                // Output it
       }
    }
    

    我想我不会去好莱坞,因为视频不是很精彩!

    像这样运行上面的代码来制作视频:

    ./main | ffmpeg -y -f rawvideo -pixel_format gbrp -video_size 1024x768 -i - -c:v h264 -pix_fmt yuv420p video.mov
    

    注 1

    要意识到的是,CImg 以平面配置存储数据,这意味着首先是所有红色像素,然后是所有绿色像素,然后是所有蓝色像素 - 所有没有任何填充或空格。

    想象一下 CImg 中的 4x4 图像(16 像素):

    RRRRRRRRRRRRRRRR GGGGGGGGGGGGGGGG BBBBBBBBBBBBBBBB
    

    与常规 RGB 数据不同,后者将存储与以下相同的图像:

    RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB 
    

    因此,您可以将所有数据打乱并重新格式化并以-pixel_fmt rgb24 的形式传递给 ffmpeg,或者像我一样在 CImg 中输出平面格式并选择匹配的-pixel_fmt gbrp(其中p 表示“平面”)。您只需要以正确的 B、G、R 顺序输出平面。另请参阅注 4


    注2

    我选择了 3 个write()s,每个颜色平面一个,为了演示的清晰,使用 "gathered write"writev() 会更有效,所以这个:

    char* s=reinterpret_cast<char*>(image.data()+(width*height));   // Get start of G plane
    std::cout.write(s,width*height);                                // Output it
    s=reinterpret_cast<char*>(image.data()+2*(width*height));       // Get start of B plane
    std::cout.write(s,width*height);                                // Output it
    s=reinterpret_cast<char*>(image.data());                        // Get start of R plane
    std::cout.write(s,width*height);  
    

    会变成这样:

    struct iovec iov[3];
    ssize_t nwritten;
    
    iov[0].iov_base = reinterpret_cast<char*>(image.data()+(width*height))
    iov[0].iov_len  = width*height;
    iov[1].iov_base = reinterpret_cast<char*>(image.data()+2*(width*height));
    iov[1].iov_len  = width*height;
    iov[2].iov_base = reinterpret_cast<char*>(image.data());  
    iov[2].iov_len  = width*height;
    
    nwritten = writev(STDOUT_FILENO,iov,3);
    

    注3

    我使用-c:v h264 -pix_fmt yuv420p 在我的 Mac 上使视频与 Apple 的 QuickTime 兼容,但无论如何您都可以轻松更改输出 - 更难的部分是获得 CImg 和 fmpeg 对。


    注4

    如果你想打乱数据并将其写入ffmpeg非平面(-pixel_fmt rgb),我最初是这样做的,代码是这样的:

    // Outside main loop
    unsigned char* BIP = new unsigned char[width*height*3];
    unsigned char *d,*r,*g,*b;
    
    ...
    ...
    
    // Now output it...
    // ... remember CImg is band-interleaved by plane  RRRRRR GGGGGG BBBBBB
    // ... not band-interleaved by pixel RGB RGB RGB RGB
    r=image.data();       // Start of R plane in CImg image
    g=r+(width*height);   // Start of G plane in CImg image
    b=g+(width*height);   // Start of B plane in CImg image
    d=BIP;                // Destination buffer in RGB order
    for(int i=0;i<width*height;i++){
       *d++=*r++;
       *d++=*g++;
       *d++=*b++;
    }
    // Output to ffmpeg to make video, i.e. run program like this
    // ./main | ffmpeg -y -f rawvideo -pixel_format rgb24 -video_size 1024x768 -i - -c:v h264 -pix_fmt yuv420p video.mov
    std::cout.write(reinterpret_cast<char*>(BIP),width*height*3);
    

    理论上,你可以用CImgpermute_axes()方法做到这一点,但我没有成功。

    【讨论】:

      【解决方案2】:

      如果保存图像文件很慢,您可能做错了什么。保存图像文件的缺点是磁盘空间。

      制作视频有很多选择:

      • 生成图像序列,并使用 ffmpeg 等第三方外部工具进行编码。
      • 使用视频库,例如ffmpeg、libx264、gstreamer、DirectShow 等

      在 linux 上我强烈推荐 libx264 或 ffmpeg,它们还提供了从位图序列中保存视频文件的示例。

      【讨论】:

        猜你喜欢
        • 2013-01-03
        • 1970-01-01
        • 1970-01-01
        • 2012-02-17
        • 1970-01-01
        • 1970-01-01
        • 2012-08-15
        • 2013-04-19
        • 1970-01-01
        相关资源
        最近更新 更多