使用C ++中的像素值数组创建视频

时间:2014-06-15 10:32:55

标签: c++ video cimg

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

我可以运行模拟并使用屏幕捕获软件记录它运行这一事实似乎暗示它是可能的,但我想要一个更整洁的解决方案。

干杯, 安格斯

2 个答案:

答案 0 :(得分:0)

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

制作视频有很多选择:

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

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

答案 1 :(得分:0)

我今天正在玩这个,并且认为我会分享我的结果。您可以从 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
   }
}

我想我不会去好莱坞,因为视频不是很令人兴奋!

enter image description here

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

./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表示“plane”)。您只需要以正确的B,G,R顺序输出平面。另请参阅注释4

注2

我选择为每个颜色平面做3 write()个,为了演示的清晰起见,使用“聚集写入”与{更高效{1}},所以这个:

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);  

注3

我使用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); 在Mac上使视频与Apple的 QuickTime 兼容,但无论如何都可以轻松更改输出 - 更难的部分是获取之间的界面CImg fmpeg 对。

注4

如果您要将数据随机播放并将其写入 ffmpeg 非平面(-c:v h264 -pix_fmt yuv420p),我最初是这样做的,代码是这样的:

-pixel_fmt rgb

理论上,您可以使用 CImg // 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); 方法执行此操作,但我没有成功。