PDA

View Full Version : Converting YUV to RGB

charleyshf
10-23-2011, 04:10 AM
Hi!

I've been trying to convert YUV to RGB this week, and so far the formula I have been using is:

R_mean = 1.164*(Y_mean - 16) + 1.596*(V_mean - 128);
G_mean = 1.164*(Y_mean - 16) - 0.813*(V_mean - 128) - 0.391*(U_mean - 128);
B_mean = 1.164*(Y_mean - 16) + 2.018*(U_mean - 128);

I have been able to convert red so far, but I am hoping there is an easier/better way, I have been using the Float32Full.spin, FloatString.spin, and pst.spin objects thus far along with my code, and this is what I am using to convert to RED:

PUB YUVtoRGB |a,b,c,d,e,f,g,h
a := F32.FFloat(resultvar[0])
b := F32.FFloat(16)
c := F32.FMul (1.164, (F32.FSub (a, b)))
d := F32.FFloat(resultvar[2])
e := F32.FFloat(128)
f := F32.FSub(d, e)
g := F32.FMul(1.596, f)
PST.Str(String(pst#NL, "R = "))
PST.Str(FSTRING.FloatToString(h))

I know the code could be "much" better but after seeing all the different calculations that can be done to go from YUV to RGB via google this week, I had to break it down as simple as possible. I do need to convert to RGB. I would appreciate any advise.

AntoineDoinel
10-23-2011, 05:12 AM
Hi!

I've been trying to convert YUV to RGB this week, and so far the formula I have been using is:

R_mean = 1.164*(Y_mean - 16) + 1.596*(V_mean - 128);
G_mean = 1.164*(Y_mean - 16) - 0.813*(V_mean - 128) - 0.391*(U_mean - 128);
B_mean = 1.164*(Y_mean - 16) + 2.018*(U_mean - 128);

Try fixed point math, like this:

Y_temp = 4768 * (Y_mean - 16);
R_mean = (Y_temp + 6537 * (V_mean - 128)) >> 12;
G_mean = (Y_temp - 3330 * (V_mean - 128) - 1602 * (U_mean - 128)) >> 12;
B_mean = (Y_temp + 8266 * (U_mean - 128)) >> 12;

charleyshf
10-24-2011, 12:40 PM
Thank you...

Boy that looks a lot easier to deal with verses what I have currently. Unfortunately I am lost when it comes to fixed point math on the prop, not something I have really dealt with. Over the weekend I googled around about fixed point math and it just made things worse. From what you have it looks like you take for example 1.164 and multiply it by 4096 giving the result of 4767.744 or 4768 if you round up. I am going to take a guess here but if I wanted more accuracy would I be able to say take 1.164 and mutiply it by 8192?

I am still searching around trying to get a better idea of fixed point math, maybe I am just making too much of it, of I just think I am clueless.

Thank You

ericball
10-24-2011, 03:13 PM
First, what do you mean by YUV? If you're talking NTSC then

Y = 0.299R + 0.587G + 0.114B
U = 0.492 (B-Y)
V = 0.877 (R-Y)
and R,G,B are gamma corrected, and Y is the same range/scale as RGB and colorburst phase is -U. Inverting the matrix is left as an exercise for the user.

Fixed point is the same as integer, but with a fixed scaling factor, i.e.

integer = fixed * scaling
Ai = Af * scaling
Bi = Bf * scaling
Cf = Af + Bf
Cf = Ai / scaling + Bi / scaling = (Ai + Bi) / scaling
Ci = Ai + Bi
Cf = Af * Bf
Cf = Ai / scaling * Bi / scaling = (Ai * Bi) / (scaling * scaling)

charleyshf
10-24-2011, 06:00 PM
Hello,
What I am doing is this: http://www.fourcc.org/fccyvrgb.php

Is there a better reference to fixed point math anywhere?

ericball
10-24-2011, 08:20 PM
What I am doing is this: http://www.fourcc.org/fccyvrgb.php
As you can see from that page, you need to know what your source data used as "YUV" so you get the right scaling factors.
Wikipedia is typically a good place to start: http://en.wikipedia.org/wiki/Fixed-point_arithmetic

charleyshf
10-25-2011, 01:23 PM
Thank you for that link, still trying to get a better grip on fixed point math with the prop, the values I am working with are from 0 - 255 for either YUV or RGB, I think i've just over complicated this and that's why I am getting stuck

Dave Hein
10-25-2011, 02:08 PM
Fixed point (or integer) math is fairly straightforward. Spin performs math using 32-bit signed integers. They have a range of approximately +/- 2 billion. Any computations that are done must stay within that range or you will get an overflow, which will give you an erroneous result.

Also, since the computations are done with integers, a divide will only produce the integer part of the result, and the fractional part will be discarded. Fractional results can be obtained by pre-scaling the numerator before dividing. A larger scaling factor will produce higher precision, but if you scale too high you will get overflows performing multiplication.

YUV and RGB values are normally treated as unsigned 8-bit integers, which have a range from 0 to 255. This can be scaled by as much as 8 million without getting an overflow, but you normally don't need that much precision. Scaling by 1000 should be more than enough precision. If we scale by 1000, your original equations would be as follows.

R_mean = 1164*(Y_mean - 16) + 1596*(V_mean - 128);
G_mean = 1164*(Y_mean - 16) - 813*(V_mean - 128) - 391*(U_mean - 128);
B_mean = 1164*(Y_mean - 16) + 2018*(U_mean - 128);

The result is still scaled by 1000, so you would need to divide by 1000 to get back to the 8-bit range.

R_mean /= 1000
G_mean /= 1000
B_mean /= 1000

Dividing by 1000 will discard the fractional part. If you want slightly improved precsion you could round to the nearest integer by adding one-half of the scaling factor before dividing. Rounding would be done as follows.

R_mean := (R_mean + 500) / 1000
G_mean := (G_mean + 500) / 1000
B_mean := (B_mean + 500) / 1000

Keep in mind that the resulting values may be slightly beyond the 0 to 255 range. If you want to ensure that the results are within the 8-bit range you will have to limit them to 0 and 255.

It's more efficient to use a scaling factor that is a power of 2, such as 1024 ro 2048, because scaling can be done with shifts instead of multiplies and divides.

charleyshf
10-25-2011, 07:32 PM
I just wanted to say thank you to everyone who helped me understand this, I finally got it after realizing I was making things more complicated than they needed to be.