dyne / frei0r

A large collection of free and portable video plugins
https://frei0r.dyne.org/
GNU General Public License v2.0
419 stars 90 forks source link

How to convert an input RGBA frame into an RGB one? #116

Closed adinapoli closed 2 years ago

adinapoli commented 2 years ago

Hello @ddennedy ,

apologies for opening an issue, it just seemed quicker and with better support for posting code snippets 😉 . I am trying to do something conceptually simple: I need to take the input frame that is addressed by the uint32_t *in pointer in the update callback and drop the alpha out of it.

Bigger context: I need to call a (Python) library (via the C interpreter) that accepts only images without the alpha channel or, to be more precise, a numpy array as a matrix of triples (R,G,B). My cunning plan was to mostly copy what the cartoon filter was doing and take from there. In particular, I thought about the following:

This seems to "kinda work", but I get a grayscale image, and by dumping the numpy matrix on stdout is clear I am doing something silly. Super stripped down version of the code:

#define RED(n)  ((n>>16) & 0x000000FF)
#define GREEN(n) ((n>>8) & 0x000000FF)
#define BLUE(n)  (n & 0x000000FF)
#define RGB(r,g,b) ((r<<16) + (g <<8) + (b))

...

// "in" is the pointer to the input frame I am given from frei0r;
// "geo" is a simple data structure copied from the cartoon filter, host the screen size
// yprecal is taken from cartoon and is populate in the constructor to be essentially double the height.

memset((void*)frameBuffer, 0xFF, sizeof(char) * (geo->w * geo->h * 3));

int32_t pixel;

for (x=(int)0;x<geo->w;x++) {
    for (y=(int)0;y<geo->h;y++) {

              char r,g,b;

              pixel = *(in+x+yprecal[y]);
              r     = RED(pixel);
              g     = GREEN(pixel);
              b     = BLUE(pixel);

              *(frameBuffer+x+yprecal[y])     = 0xED;
              *(frameBuffer+x+yprecal[y] + 1) = 0xBD;
              *(frameBuffer+x+yprecal[y] + 2) = 0x13;

    }
}
...

// inverting the process, later on

for (x=(int)0;x<geo->w;x++) {
    for (y=(int)0;y<geo->h;y++) {

                  char r,g,b;
                  int32_t rgba;

                  r = *(frameBuffer+x+yprecal[y]);
                  g = *(frameBuffer+x+yprecal[y] + 1);
                  b = *(frameBuffer+x+yprecal[y] + 2);

                  rgba = RGB(r,g,b);

                  *(out+x+yprecal[y]) = rgba;

    }
}

I am opening this ticket as I would like to understand where is the hole in my reasoning and why this mysterious yprecal array even work. As said it was shamelessly stolen by cartoon and is initialised like this in the constructor:

      if ( geo->size > 0 ) {
          frameBuffer = (char*)malloc(sizeof(char) * (geo->w * geo->h * 3));

          yprecal = (int*)malloc(geo->h*2*sizeof(int));
      }
      for(c=0;c<geo->h*2;c++)
          yprecal[c] = geo->w*c;

It seems some way of mapping between "screen space" and some other kind of coordinate system, but I am afraid this is beyond my level of ffmpeg/frei0r expertise.

Thanks in advance 😉

adinapoli commented 2 years ago

Ok, I have managed to get it to work. In case somebody finds it useful, my approach was mostly correct but I wrongly assumed that loop was iterating by rows, whereas the scan was done by columns (silly me), and this is exactly what yprecal buys us, the ability to precalculate the next pixel columwise.

All good, closing it.