Tom White
Tom White

Reputation: 37

Javascript convert YUV to RGB

I have YUV image byte array(UINtArray) in js, is it possible to convert it to rgb? or how to draw it on canvas?

here is link where it using webGL canvas, by filling texture/ But I need to do that using 2D context canvas. I know that it may be slower, but anyway. https://github.com/mbebenita/Broadway/blob/master/Player/canvas.js

var lumaSize = width * height,
    chromaSize = lumaSize >> 2;
webGLCanvas.YTexture.fill(buffer.subarray(0, lumaSize));
webGLCanvas.UTexture.fill(buffer.subarray(lumaSize, lumaSize + chromaSize));
webGLCanvas.VTexture.fill(buffer.subarray(lumaSize + chromaSize, lumaSize + 2 * chromaSize));
webGLCanvas.drawScene();

Update: image YUV bytes is also I420 format as I see

Update2: I have found example in c++ that I think will work, but can you help me to convert it to js?

static int getR(int y, int u, int v)
{
    return clamp(y + (int) (1.402f * (v - 128)));
}

static int getG(int y, int u, int v)
{
    return clamp(y - (int) (0.344f * (u - 128) + 0.714f * (v - 128)));
}

static int getB(int y, int u, int v)
{
    return clamp(y + (int) (1.772f * (u - 128)));
}

static int getY(int r, int g, int b)
{
    return (int) (0.299f * r + 0.587f * g + 0.114f * b);
}

static int getU(int r, int g, int b)
{
    return (int) (-0.169f * r - 0.331f * g + 0.499f * b + 128);
}

static int getV(int r, int g, int b)
{
    return (int) (0.499f * r - 0.418f * g - 0.0813f * b + 128);
}

static int clamp(int value)
{
    return Math::Min(Math::Max(value, 0), 255);
}

static void ConvertI420ToRGB24(SSourcePicture *yuvImage, unsigned char *rgbData)
{
    int yPadding = yuvImage->iStride[0] - yuvImage->iPicWidth;
    int uPadding = yuvImage->iStride[1] - (yuvImage->iPicWidth / 2);
    int vPadding = yuvImage->iStride[2] - (yuvImage->iPicWidth / 2);

    int yi = 0;
    int ui = 0;
    int vi = 0;
    int rgbi = 0;
    for (int ypos = 0; ypos < yuvImage->iPicHeight; ypos++)
    {
        for (int xpos = 0; xpos < yuvImage->iPicWidth; xpos++)
        {
            int y = yuvImage->pData[0][yi] & 0xFF;
            int u = yuvImage->pData[1][ui] & 0xFF;
            int v = yuvImage->pData[2][vi] & 0xFF;

            int r = getR(y, u, v);
            int g = getG(y, u, v);
            int b = getB(y, u, v);

            if (BitConverter::IsLittleEndian)
            {
                rgbData[rgbi++] = (unsigned char) b;
                rgbData[rgbi++] = (unsigned char) g;
                rgbData[rgbi++] = (unsigned char) r;
            }
            else
            {
                rgbData[rgbi++] = (unsigned char) r;
                rgbData[rgbi++] = (unsigned char) g;
                rgbData[rgbi++] = (unsigned char) b;
            }

            yi++;
            if (xpos % 2 == 1)
            {
                ui++;
                vi++;
            }
        }

        yi += yPadding;

        if (ypos % 2 == 0)
        {
            ui -= (yuvImage->iPicWidth / 2);
            vi -= (yuvImage->iPicWidth / 2);
        }
        else
        {
            ui += uPadding;
            vi += vPadding;
        }
    }
}

Upvotes: 1

Views: 7182

Answers (3)

markE
markE

Reputation: 105015

Here's JS code to convert yuv to rgb.

function yuv2rgb(y,u,v){
  y=parseInt(y);
  u=parseInt(u);
  v=parseInt(v);
  r=clamp(Math.floor(y+1.4075*(v-128)),0,255);
  g=clamp(Math.floor(y-0.3455*(u-128)-(0.7169*(v-128))),0,255);
  b=clamp(Math.floor(y+1.7790*(u-128)),0,255);
  return({r:r,g:g,b:b});
}

function clamp(n,low,high){
    if(n<low){return(low);}
    if(n>high){return(high);}
}

You can use var imgData=ctx.createImageData(canvas.width,canvas.height) to create a "from-scratch" pixel array that you can feed rgb values into.

After you build your pixel array you can draw it to the canvas with ctx.putImageData(imgData)

Upvotes: 2

user1693593
user1693593

Reputation:

Assuming your YUV array is 3 bytes per pixel you can copy and convert the buffer to canvas like this:

var width = 800,    /// the dimension of the YUV buffer
    height = 600,
    canvas = document.getElementById('myCanvasId'),
    ctx = canvas.getContext('2d'),

    idata,          /// image data for canvas
    buffer,         /// the buffer itself
    len,            /// cached length of buffer
    yuvBuffer = ... /// your YUV buffer here

    i = 0,          /// counter for canvas buffer
    p = 0,          /// counter for YUV buffer
    y, u, v;        /// cache the YUV bytes

/// make sure canvas is of same size as YUV image:
canvas.width = width;
canvas.height = height;

/// create an image buffer for canvas to copy to
idata = ctx.createImageData(width, height);
buffer = idata.data;
len = buffer.length;

Now we can iterate the buffers and convert as we go:

/// iterate buffers
for(; i < len; i += 4, p += 3) {

    /// get YUV bytes
    y = yuvBuffer[p];
    u = yuvBuffer[p + 1] - 128;
    v = yuvBuffer[p + 2] - 128;

    /// convert to RGB and copy to canvas buffer
    buffer[i]     = y + v * 1.402 + 0.5;
    buffer[i + 1] = y - u * 0.344 - v * 0.714 + 0.5;
    buffer[i + 2] = y + u * 1.772 + 0.5;
}

/// update canvas with converted buffer
ctx.putImageData(idata, 0, 0);

We don't need to clamp the values as the buffer for canvas is by default Uint8ClampedArray so the browser will do the clamping for us and save us a few CPU cycles. Just add 0.5 for rounding.

If your YUV buffer is raw 4:2:0 format then it is a little more tedious as you have to take interleave etc. into consideration. But for the YUV component value itself you can use the conversion method above to get RGB.

Hope this helps.

Upvotes: 0

CubanAzcuy
CubanAzcuy

Reputation: 125

I don't know much about your topic I'll try to figure out more later, but I found two userful link that may help.

Should Tell You How to Convert YUV image to RGB (This is in Java I will try to convert this later): http://www.41post.com/3470/programming/android-retrieving-the-camera-preview-as-a-pixel-array

Should Tell You How to Draw the RGB Image: getPixel from HTML Canvas?

Upvotes: 0

Related Questions