1

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;
        }
    }
}
Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144
Tom White
  • 37
  • 2
  • 10
  • 1
    What is the specific issue that you are facing when implementing this in JS? Can you please post any of your attempts so far? – z33m Jan 21 '14 at 19:27
  • Here is all convertion function that I have tried, http://jsfiddle.net/QsL6b/ I all I get or empty canvas or image not draw if I convert it to base and set src in image. Thanks – Tom White Jan 22 '14 at 07:54

3 Answers3

2

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)

Ross
  • 14,266
  • 12
  • 60
  • 91
markE
  • 102,905
  • 11
  • 164
  • 176
0

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?

Community
  • 1
  • 1
CubanAzcuy
  • 125
  • 1
  • 13
  • Thanks @CubanAzcuy for respond. Yes, I have try some convert examples but have to luck to draw it, maybe I miss something. To draw then rgb byte is not issue, because I can also convert it to base64 and draw as image. Hope you will be success with convert. – Tom White Jan 21 '14 at 17:51
0

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.

  • I get white canvas anyway – Tom White Jan 22 '14 at 07:34
  • @TomWhite in what format is te YUV buffer? Is it "normalized" to 8:8:8 or is the data original 4:2:0? See this article for more details on how to deal with 420 raw buffers: https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion –  Jan 22 '14 at 07:39
  • also for encode data that they send me they use https://github.com/cisco/openh264 – Tom White Jan 22 '14 at 07:56