|
|
| Fredrik |
| Posted: Dec 12 2002, 11:54 AM |
 |
|
Unregistered

|
When converting from RGB (0-255) to YUV, the Y value is surely between 0 and 255. But what about U and V, for example when converting R255 G255 B0 to YUV, U is surely negative. I have to correct these values somehow, don't I? How is it done?
I use
Y = 0.299R + ... U = B - Y V = R - Y
|
 |
| fccHandler |
| Posted: Dec 12 2002, 07:53 PM |
 |
|
Administrator n00b
  
Group: Moderators
Posts: 3961
Member No.: 280
Joined: 13-September 02

|
You add 128 to U and V, clamp to 0-255, and store. When converting back to RGB, you subtract 128.
-------------------- May the FOURCC be with you... |
 |
| Fredrik |
| Posted: Dec 12 2002, 11:48 PM |
 |
|
Unregistered

|
Hello,
I am currently rewriting my cartoon cleaner filter to work in YUV colorspace (mostly because I want to get rid of the chroma noise). After searching the internet quite a long time I found the following conversion formulas:
RGB -> YUV ---------- Y = 0.2990R + 0.5870G + 0.1440B U = -0.1687R - 0.3313G + 0.5000G V = 0.5000R - 0.4187G - 0.0813B
YUV -> RGB ---------- R = Y + 1.402V G = Y - 0.34414U - 0.71414V B = Y + 1.772U
Using these formulas on 0-255 rgb values produces 0-255 for Y and (-127.5)-(+127.5) for both U and V. I decided to multiply by 4096*0,299 etc. and then divide by 256 (shift right by 8), so my factors look like this:
unsigned __int64 rgb2y = 0x000004c9096401d3; unsigned __int64 rgb2u = 0x0000fd4dfab30800; unsigned __int64 rgb2v = 0x00000800f94dfeb3;
unsigned __int64 yuv2r = 0x000010000000166f; unsigned __int64 yuv2g = 0x00001000fa7ff493; unsigned __int64 yuv2b = 0x000010001c5a0000;
The first thing I tried was converting RGB->YUV and then, without any filtering, convert back YUV->RGB which resulted in a lot of garbage. See http://www.geocities.com/ackehurst/ for the pics.
I'm pretty sure this is a rounding problem. Can it be solved by changing the above tables? As a temporary solution, I decided to push any rgb value which is zero to one. The new picture is almost identical to the original (despite some rgb values being one off, I checked it with Paint Shop Pro).
To sum it up, my question is: how do other rgb<->yuv converters handle these rounding problems? I don't want to limit this filter to p3/athlon users just because of one stupid pmaxub-opcode...
|
 |
| phaeron |
| Posted: Dec 13 2002, 12:59 AM |
 |
|

Virtualdub Developer
  
Group: Administrator
Posts: 7773
Member No.: 61
Joined: 30-July 02

|
Looks like you've either got an overflow problem or have signed/unsigned values mixed up somewhere -- clipping isn't ordinarily a problem in MMX because it's automatically done by the pack instructions.
As for doing the zero push without MMX2:
| CODE | pxor mm1, mm1 pcmpeqw mm1, mm0 psubw mm0, mm1
|
Before you go through all the work of a full YUV solution, though, note that [Y, R-Y, G-Y, B-Y] space is easier to compute and is equivalent for linear processing. |
 |
| Fredrik |
| Posted: Dec 13 2002, 02:53 AM |
 |
|
Unregistered

|
| QUOTE (phaeron @ Dec 12 2002, 06:59 PM) | clipping isn't ordinarily a problem in MMX because it's automatically done by the pack instructions.
|
Thank you very much, I did not think of packuswb, I just por'd the values together since I assumed they'd be between 0 and 255 anyway... d'oh! Now it works fine.
Maybe you can give me some other hints on my mmx code? The arranging of the r, g and b values seems rather complicated, but I could not solve it better (yet).
esi points to an array of yuv qwords, edi points to the destination buffer, this is just the code for a single line
yuv2rgb:
movd mm6, [esi] // mm6 = | 0 | 0 |16U|16V| movd mm5, [esi+4] // mm5 = | 0 | 0 | 0 |16Y|
pslld mm5, 12 // mm5 = | 0 | Y | 0 | movq mm0, [yuv2r]
pmaddwd mm0, mm6 // mm0 = | 0 |R-Y|0.r| movq mm1, [yuv2g]
pmaddwd mm1, mm6 // mm1 = | 0 |G-Y|0.g| movq mm2, [yuv2b]
pmaddwd mm2, mm6 // mm2 = | 0 |B-Y|0.b| paddd mm0, mm5 // mm0 = | 0 | R |0.r|
paddd mm1, mm5 // mm1 = | 0 | G |0.g| paddd mm2, mm5 // mm2 = | 0 | B |0.b|
psrlq mm0, 16 // mm0 = | 0 | 0 | R | pand mm1, [gmask] // mm1 = | 0 | G | 0 |
psllq mm0, 32 // mm0 = | 0 | R | 0 | 0 | psrld mm2, 16 // mm2 = | 0 | 0 | B |
por mm1, mm2 // mm1 = | 0 | G | B | add esi, 8
por mm0, mm1 // mm0 = | 0 | R | G | B | add edi, 4
packuswb mm0, mm7 // mm0 = |0|0|0|0|0|r|g|b| dec ecx
movd [edi-4], mm0 jnz yuv2rgb
Argl, just paste the code in your fav text editor, I just don't get it properly arranged here.
And concerning chroma noise reduction: is it sufficient to just average between two frames, or should I consider saving more frames like temporal smoother does?
|
 |
| phaeron |
| Posted: Dec 13 2002, 04:43 AM |
 |
|

Virtualdub Developer
  
Group: Administrator
Posts: 7773
Member No.: 61
Joined: 30-July 02

|
There isn't a whole lot of room to manuever in that code, but if you swap U and V in your scanline format, you can generate R-Y and B-Y at the same time with pmulhw and then use punpcklwd to squish G-Y in the middle. It may not be better, however, since you will have to broadcast Y and that is annoying without pshufw. If you have pshufw then there's no point as you can simply use brute force matrix multiplication.
The way I typically attack this problem is to generate RGB from four pixels in parallel, pack them down to bytes, then reinterleave in two stages (RBRB + xGxG -> xRGBxRGB). This ends up putting a lot of pressure on the shifter, though, and you still have accuracy issues to deal with. In your case it's not quite so bad since you don't have a bias and scale factor on Y to deal with. |
 |
|