Converting YUV to RGB
charleyshf
Posts: 165
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:
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.
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) h := F32.FAdd(c,g) 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.
Comments
Try fixed point math, like this:
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
Fixed point is the same as integer, but with a fixed scaling factor, i.e.
What I am doing is this: http://www.fourcc.org/fccyvrgb.php
Is there a better reference to fixed point math anywhere?
Wikipedia is typically a good place to start: http://en.wikipedia.org/wiki/Fixed-point_arithmetic
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.