I'm having trouble converting bt.709 YUV (or YCrCb) to RGB. I have two equations.
This one gives accurate colors but leaves tiny artifacts on edges:
This one has no artifacts but the colors are inaccurate:Code:rf.f = (255/219)*y + (255/112)*v*(1-#Kr) - (255*16/219 + 255*128/112*(1-#Kr)) gf.f = (255/219)*y - (255/112)*u*(1-Kb)*Kb/Kg - (255/112)*v*(1-Kr)*Kr/Kg - (255*16/219 - 255/112*128*(1-Kb)*Kb/Kg - 255/112*128*(1-Kr)*Kr/Kg) bf.f = (255/219)*y + (255/112)*u*(1-Kb) - (255*16/219 + 255*128/112*(1-Kb))
Both come from here:Code:;rf.f = y + 2*(v-128)*(1-0.2126) ;gf.f = y - 2*(u-128)*(1-Kb)*Kb/Kg - 2*(v-128)*(1-Kr)*Kr/Kg ;bf.f = y + 2*(u-128)*(1-Kb)
http://avisynth.nl/index.php/Color_conversions
Working with Y,U,V and R,G,B in the 0 - 255 range. The video comes from a camcorder which outputs video in the 0 - 255 range.
Thoughts?
The colors are accurate and there are no artifacts when played back through VLC and SMPlayer
+ Reply to Thread
Results 1 to 30 of 152
-
-
Can you describe the "tiny artifacts on edges ?" , or post screenshot or sample ?
Can you reproduce the same edge artifact with the same equation (or actually, the implementation of the equation) in avisynth ?
If no, then there might be a problem with your implementation of the equation, or something else in the process (e.g. maybe source loading issue or other factor)
If yes, then look at the VLC or SMplayer code to see what they are using -
Hopefully you can see the file Artifact.jpg which I have attempted to upload.
Look at the blue box. Note the magenta-colored fringes on the edges. -
This lovely board software has shrunken the image so it might be hard to see. Below is a link to a full-size screen capture. See the magenta fringes at the edges of the blue patch.
http://www.chrisnology.info/photos/Artifact.JPG
It is possible to eliminate these fringes but then the colors become inaccurate, viz.:
Code:rf.f = y + 2*(v-128)*(1-0.2126) gf.f = y - 2*(u-128)*(1-Kb)*Kb/Kg - 2*(v-128)*(1-Kr)*Kr/Kg bf.f = y + 2*(u-128)*(1-Kb)
Code:y.a = PeekA(*pntr) u.a = frame((yCoord / 2) * (#W / 2) + (x / 2) + numpixels) v.a = frame((yCoord / 2) * (#W / 2) + (x / 2) + numpixels + (numpixels / 4))
Code:If rf > 255:rf = 255:EndIf :rd.a = rf If gf > 255:gf = 255:EndIf :gd.a = gf If bf > 255:bf = 255:EndIf :bd.a = bf
there might be a problem with your implementation of the equation
The code that gives me more accurate colors gives me
14-180-14
for green. The original bmp color is 16-180-16. I can live with that because there is rounding error, but we have these fringes.
The code that gives inaccurate colors gives:
26-171-26 for green.
I can't live with that.Last edited by chris319; 22nd Oct 2018 at 04:36.
-
To add to the confusion, I see the fringes only in the red channel. The green and blue channels have no such fringes.
Here is the offending code for the red channel:
Code:rf = (255/219)*y + (255/112)*v*(1-#Kr) - (255*16/219 + 255*128/112*(1-#Kr))
Last edited by chris319; 22nd Oct 2018 at 05:00.
-
Pandy: there is nothing on page 4 of the standard that would solve this dilemma.
-
An optimizing compiler may be treating calculations like (255/219) as integers, converting to (1) -- this is language and/or compiler dependent. Change all of them to floating point, eg. (255.0/219.0) to be sure. I suspect this isn't your problem because it would result in pretty large errors everywhere.
Also, you are using the limited range (Y=16-235, UV=16-240) conversion, not full range. The commented out equation you're not using is full range.
I would also check for underflow (<0) as well as overflow (>255).Last edited by jagabo; 22nd Oct 2018 at 09:22.
-
-
What a dumb guy I am!
Sometimes the simplest solution is the one most easily overlooked.
Code:If rf < 0:rf = 0:EndIf If gf < 0:gf = 0:EndIf If bf < 0:bf = 0:EndIf
Problem solved. I am happy to have accurate colors now, and no fringing.
Thank you to everyone who helped.
Regarding #Kr, in PureBasic the # means Kr has been defined as a constant. -
I clearly see proper equations - chapter 3 "Signal format" , page 4 (physical 6).
Those equations are OFFICIAL - there is no rounding in formal standard which assume truncating - this is one of common issues within video software - ROUNDING instead TRUNCATING - developers rarely following standards (as they are frequently unaware of standard existence - they mostly copying and pasting faulty code).
btw your problem is obviously NOT related to equations (unless you not using arithmetic with SATURATION) but to rescaling - don't use resizer which produce under and overshoots https://en.wikipedia.org/wiki/Overshoot_%28signal%29 . Usually pulses and steps are shaped with Gaussian, raised cosine or Hann, Blackman window function
This PDF may be nice to read...
Last edited by pandy; 23rd Oct 2018 at 03:23.
-
-
To be more precise - 8 bit from 10 and higher bits are truncated without rounding - so you can calculate for 10 bit (black level 64 and White level 940) and divide by 4 AND truncate/loose fractional part... trust me - this is how Hardware implementation looks - most of (if not all) open source encoders accept only 8 bit not 8.2 (10 bit) sample format.
-
Trust you? You lamented programmers not following the standard, and then you misrepresented it. No wonder there is such enduring confusion in implementations.
-
Page 4 of the standard has item numbers in the leftmost column. For clarity, please refer to those.
If you're referring to Item 3.4, you need E'R, E'G and E'B to calculate D'R, D'G and D'B. How am I going to calculate R, G and B given only Y, Cr and Cb?
Here is the code I am using now. Where do you see rounding or anything that is suboptimal? Better yet, why don't you rewrite my code if you think it can be written better. rf, gf, bf, #Kr, #Kg and #Kb are floats. y, u and V are 8-bit bytes output by the camcorder. I don't know why you're bringing 10-bits into the discussion because they don't come into play.
Code:;http://avisynth.nl/index.php/Color_conversions rf.f = (255/219)*y + (255/112)*v*(1-#Kr) - (255*16/219 + 255*128/112*(1-#Kr)) gf.f = (255/219)*y - (255/112)*u*(1-#Kb)*#Kb/#Kg - (255/112)*v*(1-#Kr)*#Kr/#Kg - (255*16/219 - 255/112*128*(1-#Kb)*#Kb/#Kg - 255/112*128*(1-#Kr)*#Kr/#Kg) bf.f = (255/219)*y + (255/112)*u*(1-#Kb) - (255*16/219 + 255*128/112*(1-#Kb)) If rf > 255: rf = 255:EndIf If gf > 255: gf = 255:EndIf If bf > 255: bf = 255:EndIf If rf < 0:rf = 0:EndIf If gf < 0:gf = 0:EndIf If bf < 0:bf = 0:EndIf
-
Do I have these matrices right?
See here:
https://en.wikipedia.org/wiki/YUV#HDTV_with_BT.709
I know very little about matrix notation so here goes:
R = 1 * Y' +1.28033 * v
G = 1 * Y' + -0.21482 * u + -0.38059 * v
B = 1 * Y' + 2.12798 + 0 * v
Where all except u and v are in the range 0 - 1.
At this point I'm just experimenting. I could make these INT's if need be.Last edited by chris319; 29th Oct 2018 at 22:19.
-
Actually I believe there was an error in the calculation of B. I omitted the multiplication by u.
Code:R = 1 * Y' +1.28033 * v G = 1 * Y' + -0.21482 * u + -0.38059 * v B = 1 * Y' + 2.12798 * u + 0 * v
Last edited by chris319; 30th Oct 2018 at 14:10.
-
lol - seem you trying to pin me but you have no clue how broadcast hardware works... for format 8.2 (so called 10 bit) fractional part is truncated, no rounding is performed - accordingly to you 50% grey will have 126 value but in real life it will have 125 - guess why...
Don't trust me i don't care - perform quick test: 50% grey (value 502 on 8.2), use SDI with 8 bit analyser - accordingly to equations 8 bit will be 126 value but on analyser you will see 125 - why? Are you able to explain this?
E'R, E'G and E'B are denominated ("analog") values from 0 to 1 - 50% red will be 0.5 etc - your glitch is related to lack of saturation arithmetic - you need to clamp your values between 0 and 1 (digital 1 and 254 or 0 and 255 etc).
Which is performed by this code (assumption is that you performing calculation with more than 8 bits - don't know what is default for your compiler - signed long?):
Code:If rf > 255: rf = 255:EndIf If gf > 255: gf = 255:EndIf If bf > 255: bf = 255:EndIf If rf < 0:rf = 0:EndIf If gf < 0:gf = 0:EndIf If bf < 0:bf = 0:EndIf
For example you may have pure chrominance signal without luminance (example of such signal is CCIR330 - if you try to convert CCIR330 to RGB without saturation arithmetic then for example Green will be significantly bellow zero (in real life you can't have negative Green light source).
I introduced 10 bit during discussion (or rather 8.2) as this is default digital video signal representation for professional signal sources and as i explained earlier: 8 bit in broadcast is produced by truncating (removing) from 8.2 its fractional part without additional error processing. Standard provide proper error processing and error is within +- 1/2 LSB but real life may be different than expected. Simply i saw your problem from different point than others in topic.
YCbCr to RGB equation is provided on page 3 (physical 5) in:
[Attachment 47062 - Click to enlarge]
General rule provide here: http://discoverybiz.net/enu0/faq/faq_YUV_YCbCr_YPbPr.htmlLast edited by pandy; 30th Oct 2018 at 17:12.
-
Nope - conversion from 8.2 to 8.0 is not specified and usually you have no resources to perform such processing. This is not about following spec's. There is many ways to deal with such things - for example introduce dither but this is completely different topic.
Btw - this is visible only when 10 bit source meet 8 bit receiver - usually broadcasters using 10 bit equipment (like video encoders) and customers don't care.
OP issue is lack of proper arithmetic - clamping will solve issue. Such problem is described by R&S paper. -
you need to clip your values between 0 and 1.
Today we're focused on this code from Wikipedia which is giving wrong colors:
Code:R = 1 * Y' +1.28033 * v G = 1 * Y' + -0.21482 * u + -0.38059 * v B = 1 * Y' + 2.12798 * u + 0 * v
-
You asked i replied even if this is week after (sorry but currently it is a bit hectic time for me and can't be on VH so frequently as before).
quantization range? coefficients from ITU standards are for 16,235 (64,940) - this one perhaps is for 0,255?
Are you able to use colour picker and read vales for RGB decoded correctly and not correctly (accordingly to you)?
Wiki doesn't provide source for those coefficients (this was my remark about developers) - some people simply don't care... -
Are you able to use colour picker and read values for RGB decoded correctly and not correctly
The coefficients are for BT.709. Follow the link. It explicitly says BT.709:
https://en.wikipedia.org/wiki/YUV#HDTV_with_BT.709
coefficients from ITU standards are for 16,235 (64,940) - this one perhaps is for 0,255Last edited by chris319; 30th Oct 2018 at 19:56.
-
Are you able to provide those values?
I prefer to follow more reputable sources than Wikipedia - that's why I've provided you standards. Perhaps my comment on developers was harsh but this is outcome of my experience - if some developer feel offended - apologies.
Performed quick test (color bar 100/75, Open Offce Calc) with your (wiki) coefficients and those coefficients are incorrect, they don't provide reversible RGB<>YCbCr for ITU 709 (limited quantization range)...
Attaching two files, xilinx app with detailed information about RGB <> YCbCr conversion (seems HW guys knows how to do things) and my test for conversion from RGB to YCbCr to RGB with your (wiki) incorrect coefficients.
Seem main coefficients for 709 are different for limited (16,235) Kr=0.2126, Kb=0.0722 and full (0,255) quantization range Kr=0.1819, Kb=0.0618.
-
Are you able to provide those values?
Performed quick test (color bar 100/75, Open Offce Calc) with your (wiki) coefficients and those coefficients are incorrect, they don't provide reversible RGB<>YCbCr for ITU 709 (limited quantization range)...
Could you please post some compilable code encompassing this theory in the form I am already using? Here again is the code I am using now and the source of it (avisynth). If this code is not optimal then I'm sure the avisynth folks would like to know about it.
Code:;http://avisynth.nl/index.php/Color_conversions rf.f = (255/219)*y + (255/112)*v*(1-#Kr) - (255*16/219 + 255*128/112*(1-#Kr)) gf.f = (255/219)*y - (255/112)*u*(1-#Kb)*#Kb/#Kg - (255/112)*v*(1-#Kr)*#Kr/#Kg - (255*16/219 - 255/112*128*(1-#Kb)*#Kb/#Kg - 255/112*128*(1-#Kr)*#Kr/#Kg) bf.f = (255/219)*y + (255/112)*u*(1-#Kb) - (255*16/219 + 255*128/112*(1-#Kb)) If rf > 255: rf = 255:EndIf If gf > 255: gf = 255:EndIf If bf > 255: bf = 255:EndIf If rf < 0:rf = 0:EndIf If gf < 0:gf = 0:EndIf If bf < 0:bf = 0:EndIf
Let us assume a data range of 0 - 255 (not confined to 16 - 235). -
Ok, so seem errors are related to encoding/decoding and you understand their nature thus not a problem.
Perfect then - seem here is no issue, wikipedia has some equations but those equations are incorrect (unknown purpose).
That's why i provided you ITU as reference not wikipedia (don't get me wrong - i like wikipedia however articles related to colour space and colour space conversions are not best ones).
I never said it is not optimal - i suggested to follow standard not relay on code that may have issues as frequently such equations are blindly (wiki code example - this can be OK but for special case not for regular use) copied.
I didn't check this code as one of things that hit me first was wrong signal naming convention - YUV is present ONLY in Composite video signal encoders/decoders - see no justification to use YUV as synonym for YCbCr or YPbPr except software PAL encoders/decoders which is not your case.
This code should work under some limitations - it expect Limited Quantization Range YCbCr (Y 16..235 ; Cb, Cr 16..240) and produce Full Quantization Range RGB output (0..255). It may be suboptimal when speed is your goal but i assume with modern CPU's capabilities this is not big issue.
Perhaps it is better (as code seem to use floats and provide sufficient precision) to remove scaling coefficients (255/219, 255/112) and rescale RGB components explicitly later but this is cosmetics (may improve code readability and increase flexibility- code should be able to deal with Full Quantization Range YCbCr data - be aware that in video world Full Scale YCbCr is usually 1..254 not 0..255 thus scaling coefficient like 255/254 may be required anyway if your intention is to get Full Scale RGB). -
Ok, so seem errors are related to encoding/decoding and you understand their nature thus not a problem.
This code should work under some limitations - it expect Limited Quantization Range YCbCr (Y 16..235 ; Cb, Cr 16..240)
How would you rewrite this code to handle 0 - 255? My math is not the greatest so I could use some help. The camcorder does put out 0 and 255 as it is intended for consumers, not for broadcast.
Code:rf = Int((255/219)*yf + (255/112)*Crf*(1-#Kr) - (255*16/219 + 255*128/112*(1-#Kr))+0.5) gf = Int((255/219)*yf - (255/112)*Cbf*(1-#Kb)*#Kb/#Kg - (255/112)*Crf*(1-#Kr)*#Kr/#Kg - (255*16/219 - 255/112*128*(1-#Kb)*#Kb/#Kg - 255/112*128*(1-#Kr)*#Kr/#Kg)+0.5) bf = Int((255/219)*yf + (255/112)*Cbf*(1-#Kb) - (255*16/219 + 255*128/112*(1-#Kb))+0.5)
Last edited by chris319; 2nd Nov 2018 at 23:53.
-
Don't get me wrong but colour space conversion is form of encoding and rounding itself is a form of error encoding - agree - semantics is important but encoder/decoder is black box with appropriate abstraction layer from your issue perspective...
Yes and this is clearly specified on provided by you Avisynth wiki
Such code is provided on same Avisynth page - yuv [0,255] <-> rgb [0,255] (0 <= [r,g,b] <= 255, 0 <= y <= 255, 0 <= [u,v] <= 255)
Code:r = y + 2*(v-128)*(1-Kr) g = y - 2*(u-128)*(1-Kb)*Kb/Kg - 2*(v-128)*(1-Kr)*Kr/Kg b = y + 2*(u-128)*(1-Kb)
You can also pre-process YCbCr and apply clamp before (limit Y between 16 and 235; Cb, Cr between 16, 240). -
Code:
r = y + 2*(v-128)*(1-Kr) g = y - 2*(u-128)*(1-Kb)*Kb/Kg - 2*(v-128)*(1-Kr)*Kr/Kg b = y + 2*(u-128)*(1-Kb)
I have reworked my test pattern to include patches of #FFFFFF and #000000. This code gives accurate colors over the range #000000 through #FFFFFF, so I think we're good.
https://youtu.be/69FU5Sjw0oc
Code:rf = (255/219)*y + (255/112)*v*(1-#Kr) - (255*16/219 + 255*128/112*(1-#Kr)) gf = (255/219)*y - (255/112)*u*(1-#Kb)*#Kb/#Kg - (255/112)*v*(1-#Kr)*#Kr/#Kg - (255*16/219 - 255/112*128*(1-#Kb)*#Kb/#Kg - 255/112*128*(1-#Kr)*#Kr/#Kg) bf = (255/219)*y + (255/112)*u*(1-#Kb) - (255*16/219 + 255*128/112*(1-#Kb)) If rf > 255: rf = 255:EndIf If gf > 255: gf = 255:EndIf If bf > 255: bf = 255:EndIf If rf < 0:rf = 0:EndIf If gf < 0:gf = 0:EndIf If bf < 0:bf = 0:EndIf
Similar Threads
-
Help Converting YUV to RGB
By chris319 in forum Video ConversionReplies: 7Last Post: 24th Sep 2018, 18:51 -
RGB to YUV to RGB
By chris319 in forum ProgrammingReplies: 70Last Post: 20th Feb 2017, 16:49 -
ffmpeg/x264 RGB to YUV
By SameSelf in forum Video ConversionReplies: 40Last Post: 14th Nov 2016, 18:40 -
YUV/ RGB problem in Avisynth
By spiritt in forum Newbie / General discussionsReplies: 9Last Post: 6th Sep 2015, 04:31 -
is this YUV or RGB?
By marcorocchini in forum Newbie / General discussionsReplies: 2Last Post: 20th Apr 2014, 10:21