Reputation: 37
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
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
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
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