Welcome to Doom9's Forum, THE inplace to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. 
26th January 2024, 07:34  #1  Link 
Registered User
Join Date: Jul 2011
Posts: 1,121

Converting YUV to RGB formulas?
This is off topic as it's not really for avisynth development,
but hopefully it's close enough for a pass, otherwise I can be taken down of course. I'm trying to figure out the correct formula for converting YUV to RGB, both Rec601/Rec709 with Limited/Full range. The ones I have found doesn't seem to match what avisynth produces, apart from one which is this: Code:
R = 1.164 * (Y  16) + 1.793 * (U  128); G = 1.164 * (Y  16)  0.534 * (U  128)  0.213 * (V 128); B = 1.164 * (Y  16) + 2.115 * (V  128); This produces as far as i can tell the same results Rec709 Limited. But it doesn't really match the formula shown here: https://en.wikipedia.org/wiki/Y%E2%80%B2UV And i also don't know how to make it full range, except for the Y part. I tried looking at the avisynth code to figure this out, but it was harder than I thought. So was hoping to get some help here if possible 
26th January 2024, 08:26  #2  Link 
Registered User
Join Date: May 2006
Posts: 3,997

Your matrix above applies for Rec709 limited range Y'CbCr > Full range RGB conversion. (You may have mixed up U an V though, please double check)
U and V are from the analog realm; one should actually be using Cb and Cr in the digital realm. Sometimes interchangeably used, unfortunately. For conversions you may want to take a look here: https://web.archive.org/web/20120403...rmatConversion or check out the tool here: https://res18h39.netlify.app/color Last edited by Sharc; 26th January 2024 at 11:13. Reason: Link added 
26th January 2024, 13:57  #3  Link 
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,433

See also here: http://avisynth.nl/index.php/Color_conversions

26th January 2024, 15:41  #4  Link 
Registered User
Join Date: Jul 2018
Posts: 1,159

Correct equations are in the free available ITU documents (601/709/2020) .
AVS+ narrow range in current builds is not completely correct (waiting for pinterf patch merging). There possible very many forms of simplified equations for each use case and it is not easy to check the result. So if current ready to use simplified equations not provide correct/expected values  it is good to start from official documents from ITU. Simple Cform (reference) in the decodeYUVtoRGB plugin  https://github.com/DTL2020/ConvertYU...eYV12toRGB.cpp iY = *l_srcp_Y + Ybias; // Ybias = 16 iU = *l_srcp_U + UVbias; // UVbias=128 iV = *l_srcp_V + UVbias; // UVbias=128 int iR = iY + ((iV * Kr) >> 13); int iB = iY + ((iU * Kb) >> 13); int iG = iY  ((iU * Kgu) >> 13)  ((iV * Kgv) >> 13); and Kr, Kgu, Kgb, Kb /* Computing of (Kr, Kgu, Kgv, Kb) from (Yr and Yb) coefficients of YUV colour system: Yr Yg = 1.0  (Yr + Yb) Yb Kb = (1.0  Yb) * 2 Kr = (1.0  Yr) * 2 Kgu = (Yb * Kb) / Yg Kgv = (Yr * Kr) / Yg for Digital UV (601/709/2020): RatioUVtoY = 219.0/224.0 Kr,Kb,Kgu,Kgv *= RatioUVtoY */ if (Matrix == 0) // 601 { fYr = 0.299f; fYb = 0.114f; } else if (Matrix == 1) // 709 { fYr = 0.2126f; fYb = 0.0722f; } else if (Matrix == 2) // 2020 (ncl ?) { fYr = 0.2627f; fYb = 0.0593f; } float fKb = (1.0f  fYb) * 2.0f; float fKr = (1.0f  fYr) * 2.0f; float fYg = 1.0f  (fYr + fYb); Kr = (short)(fKr * fRatioUVtoY * UVgain * mulfac + 0.5f); Kb = (short)(fKb * fRatioUVtoY * UVgain * mulfac + 0.5f); Kgu = (short)(((fYb * fKb) / fYg) * fRatioUVtoY * UVgain * mulfac + 0.5f); Kgv = (short)(((fYr * fKr) / fYg) * fRatioUVtoY * UVgain * mulfac + 0.5f); UVgain=1.0, mulfac = double(1 << int_arith_shift); // integer aritmetic precision scale Last edited by DTL; 27th January 2024 at 22:28. 
27th January 2024, 08:21  #6  Link  
Registered User
Join Date: Jul 2011
Posts: 1,121

Quote:
Thought Y'CbCr was "Component Video" and YUV was, well the normal stuff, both Digital and Analogue where it's Y, U and V. @DTV Will try it out, was more steps there than "usual", will try to find the ITU documentations as well for the equations. Last edited by zerowalker; 27th January 2024 at 08:24. 

27th January 2024, 12:31  #7  Link  
Registered User
Join Date: May 2006
Posts: 3,997

Quote:
The symbols U and V are sometimes used quite loosely. Avisynth adopted U and V for the digital realm (probably for convenience and historic reasons) rather than Cb and Cr. For the discussion here you can assume U=Cb and V=Cr. 

27th January 2024, 18:04  #8  Link 
Registered User
Join Date: Jul 2018
Posts: 1,159

"Thought Y'CbCr was "Component Video""
Most important difference between Analog (abstract YUV or defined in 0..XXX mV voltage range typically with zero black and zero UV for math zero value) and Digital YUV (CbCr for Coded BY and RY?) in storage and transfer domains data form. The 601/709/2020 recs are about digital exchange formats for motion pictures  not only abstract YUVdescriptions. Analog YUV looks like exist in the equal scale Y and UV signals (as provided from RGB>YUV matrix). Digital YUV uses quantization and some Digital YUV systems uses nonequal scale for Y and UV data in the output (storage and transfer/distribution/interfacing) form. For performance of processing in lowest possibe steps number real YUV<>RGB equations are significantly compacted. Real steps are: 1. Decode YUV from Digital YUV storage domain into abstract YUV 0..1 float domain. 2. Apply YUV>RGB matrix conversion in abstract 0..1 float domain. 3. Convert (encode) RGB into output digital domain (or other required at output). Some designers with good understanding in math can warp 1+2+3 in the single step processing by some compute engine. It make best processing performance but may suffer from issues if something important is missing. And also harder or impossible to debug in steps. The 601/709/2020 recs define Digital (quantized form) YUV domain as having nonequal scale for Y and UV data (219 for Y and 224 for UV/CbCr). It may be designed to have a bit less quantization noise in UV lowbit data with natural colour gamuts for these systems (or for YUV as having larger colour gamut in compare with RGB). So the very typical error for convertor designers is missing Y and UV input data normalization to equal scale. It is 219/224 ratio in the math displayed. The resulted error is not very big but visible even in 8bit samples. Last edited by DTL; 27th January 2024 at 22:30. 
29th January 2024, 07:32  #9  Link 
Registered User
Join Date: Jul 2011
Posts: 1,121

Thanks for the explanation
When doing the y offset (Y16) in float (01). Isn't it (Y(16/255))? Cause when I do this Rec709 is valid, but Rec601 is wrong by a tiny amount, but (Y(16/256)) makes it valid. (valid as in, identical to avisynth). EDIT: Okay even PC.601 becomes a tiny bit wrong. EDIT 2: Okay it seems this happens even with ffmpeg, is it cause some small math error in avisynths conversion? Last edited by zerowalker; 29th January 2024 at 16:58. 
29th January 2024, 17:20  #10  Link 
Registered User
Join Date: Jul 2018
Posts: 1,159

"When doing the y offset (Y16) in float (01).
Isn't it (Y(16/255))?" 0..1 range in float is 16 to 235 code values in 8bit Digital YUV (scaled + shifted/offsetted). 23519=219. Typically we subtract 16 and maps result of 0..219 to 0..1 float (so divide by 219.0). The range above 235 code values in storage Digital YUV 8bit is for superwhites (above nominal 100% white for SDR). If you first convert 0..255 integer to float 0.0..255.0 it maybe also easy to subtract 16.0 and divide result to 219.0 (for performance  multiply to 1/219.0 constant). You will got black and nominal white in 0.0..1.0 range and negative underblacks and 1.0+ superwhites. For UV it is subtract 128 and divide by 224. See https://www.itu.int/dms_pubrec/itur...3I!!PDFE.pdf 2.5.3 and 2.5.4 "it seems this happens even with ffmpeg, is it cause some small math error in avisynths conversion?" AVS and ffmpeg may use integer computing with less precision for performance. Try ConvertBits(32) before ConvertToRGB and check output float32 RGB values. See also some posts around https://forum.doom9.org/showthread.p...20#post1987720 about YUV to RGB decoding in AVS. Some +1 rounding/computing errors still visible in 8bits. As fix maybe used 10bits (12..16) computing and rounding to 8bits with ConvertBits(8). So it is possible to get exactly 16/180 8bit RGB narrow code values for all colours of ColorBarsHD YUV output. Last edited by DTL; 29th January 2024 at 17:49. 
24th April 2024, 02:59  #11  Link 
Registered User
Join Date: Apr 2018
Posts: 39

Formulas for rec 601:
R /= 255; G /= 255; B /= 255; Y = R * .299 + G * .587 + B * .114; U = (B  Y) / 1.772 + .5; V = (R  Y) / 1.402 + .5; Here you can do Y,U,V *= 255 to get 8 bit YUV, and round to int. Clamping to 0,255 is not needed. I'v checked all results will be in 0...255 range. and back to RGB: U = 0.5; V = 0.5; R = Y + 1.402 * V; G = Y  0.114 * 1.772 / 0.587 * U  0.299 * 1.402 / 0.587 * V; B = Y + 1.772 * U; Here you need to do R,G,B *= 255, round to int. If you will not convert from float to int between conversions then they will be lossless. But be sure to use rounding before converting to int (like Math.Round in C#). Don't use just float to int conversion without rounding. WRONG: int r = (int)R; CORRECT: int r = (int)Math.Round(R); Then your conversions will be lossless (checked). Rec 709: Y = 0.2126 * R + 0.7152 * G + 0.0722 * B; U = (B  Y) / 1.8556 + .5; V = (R  Y) / 1.5748 + .5; R = Y + 1.5748 * V; G = Y  0.0722 * 1.8556 / 0.7152 * U  0.2126 * 1.5748 / 0.7152 * V; B = Y + 1.8556 * U; Rec 2020: Y = 0.2627 * R + 0.6780 * G + 0.0593 * B; U = (B  Y) / 1.8814 + .5; V = (R  Y) / 1.4746 + .5; R = Y + 1.4746 * V; G = Y  0.0593 * 1.8814 / 0.6780 * U  0.2627 * 1.4746 / 0.6780 * V; B = Y + 1.8814 * U; All formulas were tested. When you convert float value to INT you need to use clamping (ensure that value don't go over limit). For example 8 bit limits = 0255, 10 bit limits 0  1023. In normal cases value will not go over limit. But I'v found one source where it happens. Like there is Y = 233 U = 130 V = 142 This makes R to be > 255. I don't know how it happend that source has such values. Maybe there was some other formula used. Or maybe it was just a bug in conversion software that created source. With clamping it looks normal. Last edited by Argaricolm; 28th May 2024 at 22:00. 
28th May 2024, 22:16  #12  Link 
Registered User
Join Date: Apr 2018
Posts: 39

Also I see that there is a question how to convert result to full range.
As I'v found the documented correct way to do it is: Convert YUV to RGB floats (without converting them to ints). Then you will have limited RGB but within range 0.0  1.0. And now you need to rerange it. You can do it this way for each channel (R,G,B): 8 bit: float lowlimit = 16.0 / 255.0; float range = 219.0 / 255.0; float R = (R  lowlimit) / range; 10 bit: float lowlimit = 64.0 / 1023.0; float range = 879.0 / 1023.0; float R = (R  lowlimit) / range; Then also documentation suggests to do this (midtones correction): if (R <= 1.0 && R >= 0.018) 1.099 * pow(R, 0.45)  0.099; else if (R < 0.018 R cf >= 0) R *= 4.5; Then you can use formulas above to convert back to YUV (YUV will be full range). Don't forget to set "Full range" in encoder settings so it will set such flag. Or most hardware will treat your YUV video as limited range (in staxrip you can set it in VUI settings). Last edited by Argaricolm; 28th May 2024 at 22:21. 
28th May 2024, 22:56  #13  Link  
Registered User
Join Date: Jul 2018
Posts: 1,159

Quote:
The possible solution is to keep RGB in float so you will not get integer overflow. But you need to remember this RGB have out of range values and if you need integer RGB output  you need to apply some limiter. If you read ITU docs this issue also described. And the limiter may be mot very simple RGB clamp to min..max but recommended to make more advanced like attempt to keep hue if possible and degrade saturation first. So as some advanced YUV>RGB converter it is possible to design not simple clampmode but more smart HSB/HSL domain aware transform keeping more important colour data if possible and make less degradation to image. The order of degragation (errors) is about 1. Saturation (less important ?) 2. Brightness 3. Hue (most important ?). So the possible algorithm: If (out of range YUV to RGB triplet detected) > convert to HSB (?) and use some iterative process to get valid range RGB triplet with minimized deltaHue and deltaBrightness errors (with some (more ?) weight to deltaHue ?). It may be some time and compute complex but will give better result in compare with simple clamp to min..max. Also this smart RGB limiter may be stand alone plugin so you can make YUV to RGB unlimited in RGBPS and process with smartRGB limiter to go to integer RGB. Quote:
To convert 0..1.0 float to full integer RGB you simply multiply to 2^N where n is number of bits in the ingeter encoding. So black will be 0 and nominal white will be 255 in 8bit or 1023 in 10bit. And you will lost both underblacks and overwhites. Last edited by DTL; 28th May 2024 at 23:05. 

29th May 2024, 12:07  #14  Link  
Formerly davidh*****
Join Date: Jan 2004
Posts: 2,512

Quote:


29th May 2024, 14:12  #15  Link 
Registered User
Join Date: Jul 2018
Posts: 1,159

Yes  to get 255 from 1.0f it is multiplication to (2^8)1 = 255. I am typically bad at +1 errors in many cases. Sorry.
About rounder for float it is typically a good working addition of +0.5f (half of LSB value). So to round 254.8f to 255 integers it is typically passed 255.2+0.5f=255.7f to engine converting float to int. It looks like 'rounding to the nearest'. Though it is only my current ideas  the industry documents have precomputed tables for 10 (and sometimes 8) bit YUV and RGB values for colour bars test pattern so any math may be checked with these tables. Also some 'losses' check is for given 180/16 narrow RGB  conversion to (10bit ?) YUV and back to RGB must also return 180/16 RGB. It is for 180/16 RGB 8bit colour bars: ConvertBits(10) ConvertToYUV() // in 10bits or more ConvertToRGB() // in 10bits or more ConvertBits(8) The industry standards typically allow to get 180/16 RGB from 10bit YUV. But with 8bit RGB<>YUV conversion will not be lossless and give about +1 LSB error. 
29th May 2024, 21:20  #16  Link  
Registered User
Join Date: Apr 2018
Posts: 39

Quote:
Its an OETF function. As I understand from docs its real light > electronic signal transfer. There is EOTF function that is reverse of OETF. But strange thing is that OETF makes image light and EOTF makes it very dark. Quite interesting results with OETF used after full range... 

Thread Tools  Search this Thread 
Display Modes  

