Create video from array of pixel values in C++

痞子三分冷 提交于 2019-11-30 20:56:45

问题


Does anyone know of a method to save a sequence of pixel values, stored in an array to a video? Currently I'm using Cimg to visualise a simple n-body simulation, whilst I can save each iteration to an image file, this is very slow. Any suggestions on a similar library for handling video would be appreciated. Essentially, I just want to record what's displayed in the Cimg window I create to a video file. The program is written in C++, on linux, compiling with g++.

The fact that I can run the simulation and record it running with screen capturing software would seem to imply it's possible, but I'd like a tidier solution.

Cheers, Angus


回答1:


If saving image files is slow, you may have done something wrong. The cons of saving image files is disk space.

To produce video there are many choices:

  • produce image sequences, and encode using 3rd party external tools like ffmpeg.
  • use video lib, e.g. ffmpeg, libx264, gstreamer, DirectShow, etc.

On linux I strongly recommend libx264 or ffmpeg, they also provide example to save a video file from bitmap sequence.




回答2:


I was playing around doing this today, and thought I would share my results. You can output raw RGB video from CImg and then use ffmpeg to encode it up into video like this:

#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
   }
}

I guess I won't make it to Hollywood as the video is not very exciting!

Run the above code like this to make a video:

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

Note 1

The thing to realise is that CImg stores data in a planar configuration, which means all the red pixels first, then all the green ones directly afterwards and then all the blue ones straight after that - all without any padding or spaces.

Imagine a 4x4 image (with 16 pixels) in CImg:

RRRRRRRRRRRRRRRR GGGGGGGGGGGGGGGG BBBBBBBBBBBBBBBB

unlike regular RGB data, which would store the same image as:

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

So, you can either shuffle all the data around and reformat it and pass to ffmpeg as -pixel_fmt rgb24, or do as I did and output in CImg's planar format and choose a matching -pixel_fmt gbrp (where the p means "planar"). You just have to output the planes in the correct B,G,R order. See also Note 4.


Note 2

I chose to do 3 write()s, one for each colour plane, for the sake of clarity of demonstration, it would be more efficient to use a "gathered write" with writev(), so this:

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

would become something like (untested):

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

Note 3

I used the -c:v h264 -pix_fmt yuv420p to make the video compatible with Apple's QuickTime on my Mac, but you can change the output easily anyway - the harder part was getting the interface between CImg and fmpeg right.


Note 4

If you want to shuffle the data around and write it to ffmpeg non-planar (-pixel_fmt rgb), I did that originally and the code was like this:

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

In theory, you can do this with CImg's permute_axes() method, but I had no success.



来源:https://stackoverflow.com/questions/24228728/create-video-from-array-of-pixel-values-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!