I don't know what to make of this.
RGB is converted to YUV and back without error with the luma coefficients given (3, 6, 1). However, these coefficients put Y, U and V out of the range of 10-bit integers.
These coefficients were chosen because they are close to the NTSC coefficients 30% red, 59% green, 11% blue.
Code:#include <stdio.h> #include <float.h> #define KR 3 #define KG 6 #define KB 1 unsigned short R, G, B, Rd, Gd, Bd; short Y,U,V; void rgb_to_yuv(void) {double fr, fg, fb, fy, fu, fv; fr = (double)R; fg = (double)G; fb = (double)B; fy = KR*fr + KG*fg + KB*fb; Y = (int)fy; fu = ((fb * KB) - fy); U = (int)fu; fv = (fr * KR) - fy; V = (int)fv; } void yuv_to_rgb(void) {double fy, fu, fv; fy = (double)Y; fu = (double)U; fv = (double)V; Rd = (int) ((fy+fv)/KR)+0.5; Gd = (int) (-(fy + fv + fu)/KG)+0.5; Bd = (int) ((fy + fu)/KB)+0.5; } int main(void) { unsigned long count, diff; count = 0; diff = 0; puts("Testing..."); for (R=0; R<=1023; R++) { for (G=0; G<=1023; G++) { for (B=0; B<=1023; B++) { count++; rgb_to_yuv(); yuv_to_rgb(); if (R!=Rd || G!=Gd || B!=Bd) { diff++; } } } } printf("\n%d colors tested\n", count); printf("%d colors differed\n", diff); }
Try StreamFab Downloader and download from Netflix, Amazon, Youtube! Or Try DVDFab and copy Blu-rays! or rip iTunes movies!
+ Reply to Thread
Results 1 to 30 of 71
Thread
-
-
Use floating point coefficients. Also U and V must be biased if they are to be stored in unsigned ints.
http://avisynth.nl/index.php/Color_conversionsCode://Rec.601 Kr=0.299 Kg=0.587 Kb=0.114 //Rec.709 Kr=0.2126 Kg=0.7152 Kb=0.0722 y = Kr*r + Kg*g + Kb*b v = 0.5*r - 0.5*g*Kg/(1-Kr) - 0.5*b*Kb/(1-Kr) + 128 u = -0.5*r*Kr/(1-Kb) - 0.5*g*Kg/(1-Kb) + 0.5*b + 128 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)
Last edited by raffriff42; 17th Nov 2016 at 06:14. Reason: code
-
Too many errors:
1073741824 colors tested
1073739073 colors differed
My original code had zero errors.
Code:#include <stdio.h> #include <float.h> #define Kr 0.3 #define Kg 0.59 #define Kb 0.11 unsigned short R, G, B, Rd, Gd, Bd; short Y,U,V; void rgb_to_yuv(void) {double fr, fg, fb, fy, fu, fv; fr = (double)R; fg = (double)G; fb = (double)B; fy = Kr*fr + Kg*fg + Kb*fb; fv = 0.5*fr - 0.5*fg*Kg/(1-Kr) - 0.5*fb*Kb/(1-Kr) + 128; fu = -0.5*fr*Kr/(1-Kb) - 0.5*fg*Kg/(1-Kb) + 0.5*fb + 128; Y = (int)fy+0.5; U = (int)fu+0.5; V = (int)fv+0.5; } void yuv_to_rgb(void) {double fy, fu, fv; fy = (double)Y; fu = (double)U; fv = (double)V; Rd = fy + 2*(fv-128)*(1-Kr); Gd = fy - 2*(fu-128)*(1-Kb)*Kb/Kg - 2*(fv-128)*(1-Kr)*Kr/Kg; Bd = fy + 2*(fu-128)*(1-Kb); } int main(void) { unsigned long count, diff; count = 0; diff = 0; puts("Testing..."); for (R=0; R<=1023; R++) { for (G=0; G<=1023; G++) { for (B=0; B<=1023; B++) { count++; rgb_to_yuv(); yuv_to_rgb(); if (R!=Rd || G!=Gd || B!=Bd) { diff++; } } } } printf("\n%d colors tested\n", count); printf("%d colors differed\n", diff); }
-
Error is error. Or maybe not.
Your calculations only test for exact matches, with no accounting for rounding. A more real-world test would include some range of tolerance.
But it has always been a given that round trip colorspace conversions are not lossless.
Scott -
I'm wondering if it need be so.
I wonder about these ever-changing luma coefficients. How finely do they need to slice them? Is there a discrenable difference among 0.11, 0.114 and 0.0593, especially with all the error that is introduced.
Here is a more recent version. Anyone may try compiling it and monkey around with it. One thing is that it has a 48-bit YUV payload rather than a 32-bit payload.
Code:#include <stdio.h> #include <float.h> //BT.601 coefficients #define Kr 0.299 #define Kg 0.587 #define Kb 0.114 //BT.2020 coefficients //#define Kr 0.2627 //#define Kg 0.678 //#define Kb 0.0593 //#KR = 0.299:#KG = 0.587:#KB = 0.114 ;REC.601 //2020 coefficients 0.2627 for red, 0.6780 for green, and 0.0593 for blue unsigned short R, G, B, Rd, Gd, Bd; float Y,U,V; short KR,KG,KB; void rgb_to_yuv(void) { Y = (KR*R + KG*G + KB*B); U = (B * KB) - Y; V = (R * KR) - Y; } void yuv_to_rgb(void) { Rd = ((Y+V)/KR); Gd = -(Y + U + V)/KG; Bd = ((Y+U)/KB); } int main(void) { KR= Kr*10; KG= Kg*10; KB= Kb*10; unsigned long count, diff; count = 0; diff = 0; puts("Testing..."); for (R=0; R<=1023; R++) { for (G=0; G<=1023; G++) { for (B=0; B<=1023; B++) { count++; rgb_to_yuv(); yuv_to_rgb(); //printf("R: %d \n",R); //printf("G: %d \n",G); //printf("B: %d \n",B); //printf("Kr: %f \n",Kr); if (R!=Rd || G!=Gd || B!=Bd) { diff++; } } } } printf("\n%d colors tested\n", count); printf("%d colors differed\n", diff); printf("Y: %f \n",Y); printf("U: %f \n",U); printf("V: %f \n",V); }
-
-
It may seem counter-intuitive, but it is good practice to add (usu 1/3LSB triangular or Gaussian) dithering to any conversion equation prior to rounding or truncating in order to maintain overall fidelity vs. quantization.
This is particularly true in audio & photo/video realms.
Scott -
If we're going to tolerate rounding error, why not simply round the coefficients to integers and do it all in integer arithmetic? That way, what comes out is what went in.
Here are the rounded coefficents as integers:
Code:601: 3,6,1 709: 2,7,1 NTSC: 3,6,1 2020: 3,7,1
-
If you're using integers and the same number of bits, yes. Look at the RGB cube inside the YUV cube.
https://software.intel.com/en-us/node/503873
With limited range rec.601 YUV the RGB cube only occupies about 1/6 of the volume of the YUV cube. So, for example, with 8 bit RGB you have 16 million different RGB values, but using an 8 bit YUV cube those 16 million RGB values map to only ~2.7 million different YUV values. Ie, on average, six different RGB values map to the same YUV value. Then when returning to RGB there's no way to tell which of those six RGB values was the original one.
About 2 more bits (on each axis) of YUV will let you encode all 16 million RGB values as different YUV values. So 8 bit RGB can be converted to 10 bit YUV, and back to 8 bit RGB without errors.Last edited by jagabo; 18th Nov 2016 at 21:32.
-
-
And by the way, if you use "random" coefficients (eg, 3, 6, 1) your YUV values will be different than the standard. Your YUV videos will not play back with the correct colors with other software.
-
So for 10-bit RGB we're looking at a 36-bit payload, or 5 bytes with room to spare.
if you use "random" coefficients (eg, 3, 6, 1) your YUV values will be different than the standard. Your YUV videos will not play back with the correct colors with other software.
Due to the rounding errors we've been discussing, they're not going to have perfect colors anyway, so choose your poison.
I wonder if it's realistic to be worried about multi-generation duplication, where the quality is slightly degraded with each successive generation due to cumulative encoding errors. I'm old enough to remember that this was a big concern with analog videotape.
I would love to see the whole deal rethought but it'll never happen. -
Your calculations only test for exact matches, with no accounting for rounding. A more real-world test would include some range of tolerance.Scott
I have changed the calculations of U and V from:
Code:U = (B-Y)/(1-Kb) = - R * Kr/(1-Kb) - G * Kg/(1-Kb) + B V = (R-Y)/(1-Kr) = R - G * Kg/(1-Kr) - B * Kb/(1-Kr)
Code:U = B*Kb - Y; V = R*Kr - Y;
Last edited by chris319; 6th Feb 2017 at 07:41.
-
Here is code which gives zero errors, i.e. a perfect round trip from RGB to YUV and back.
Y, U and V must be floats.
Code:#include <stdio.h> //BT.709 coefficients //#define Kr 0.2126 //#define Kg 0.7152 //#define Kb 0.0722 //BT.2020 coefficients #define Kr 0.2627 #define Kg 0.678 #define Kb 0.0593 unsigned short R, G, B, Rd, Gd, Bd; float Y,U,V; void rgb_to_yuv(void) { Y = Kr*R + Kg* G + Kb*B; V = R*Kr - Y; U = B*Kb - Y; } void yuv_to_rgb(void) { Rd = ((Y+V)/Kr)+0.5; Gd = (-(Y + U + V)/Kg)+0.5; Bd = ((Y+U)/Kb)+0.5; } int main(void) { unsigned long diff; diff = 0; puts("Testing..."); for (R=0; R<=255; R++) { for (G=0; G<=255; G++) { for (B=0; B<=255; B++) { rgb_to_yuv(); yuv_to_rgb(); if (R != Rd || G != Gd || B != Bd) { diff++; } } } } printf("%d colors differed\n", diff); }
Last edited by chris319; 6th Feb 2017 at 08:05.
-
You don't seem to understand the issue. There's no doubt you can convert 8 bit integer RGB to 32 bit floats and back to 8 bit integer RGB without errors. The issue is that you can't go from 8 bit integer RGB to 8 bit integer YUV and back to 8 bit integer RGB without errors.
-
Last edited by chris319; 6th Feb 2017 at 16:27.
-
1 or 2 units of R, G, and/or B. Look at the image in post #10. The entire RGB cube occupies only about 1/6 of the limited range YUV cube. I.e. 16 million RGB colors map to only ~3 million different YUV values. So on average, 6 different RGB triplets map to each YUV triplet. On conversion back from YUV to RGB there's no way of knowing which of the six triplets is correct.
-
In practice, it's not that bad:
Code:A=ColorbarsHD ## use any YV24 source here B=A.ConvertToRGB32(matrix="Rec709") \ .ConvertToYV24( matrix="Rec709") return Subtract(A, B) ## another test (view this frame-by-frame, not in real time) return Interleave( \ A.Subtitle("Original", align=5), \ B.Subtitle("Converted", align=5) \ )
Watch for subtle banding issues.
Converting through many round trips; what will happen? The answer may surprise you...Code:C=B.ConvertToRGB32(matrix="Rec709") \ .ConvertToYV24( matrix="Rec709") \ .ConvertToRGB32(matrix="Rec709") \ .ConvertToYV24( matrix="Rec709") \ .ConvertToRGB32(matrix="Rec709") \ .ConvertToYV24( matrix="Rec709") \ .ConvertToRGB32(matrix="Rec709") \ .ConvertToYV24( matrix="Rec709") return Subtract(B, C)
-
raffriff42: Why don't you write some compilable low-level code to demonstrate this, preferably in C? I have posted several examples so you could modify one of them.
-
It depends on the particular software. AviSynth is pretty good about avoiding incremental losses. Other software may not be. And one doesn't just convert back and forth for no reason. One does so because one needs to perform some operation in RGB or YUV that can't easily be done in the other. Consider this sequence:
Code:ConvertToYV24(matrix="rec709").ConvertToRGB(matrix="rec709").RGBAdjust(bb=1) ConvertToYV24(matrix="rec709").ConvertToRGB(matrix="rec709").RGBAdjust(bb=-1)
So the general rule is to keep YUV/RGB conversions to a minimum. -
-
You have some pretty definite ideas about how these things work. I've done the coding, so just plug in your equations
There are on-line C compilers so lack of a compiler is not an issue.
https://www.tutorialspoint.com/compile_c_online.php
You don't just plug numbers into what amounts to a black box and call it good.
one doesn't just convert back and forth for no reason.
Here is a weird phenomenon: different errors depending on the channel examined. Try uncommenting the different error test lines.
Code:#include <stdio.h> //BT.2020 coefficients #define Kr 0.2627 #define Kg 0.678 #define Kb 0.0593 unsigned short R, G, B, Rd, Gd, Bd, Y; short U; short V; void rgb_to_yuv(void) { //Y = ((Kr*R + Kg* G + Kb*B)+0.0); Y = (unsigned short)((Kr*R + Kg* G + Kb*B)+0.0); V = (short)(R*Kr - Y); U = (short)(B*Kb - Y); } void yuv_to_rgb(void) { Rd = ((Y+V)/Kr)+0; Gd = (-(Y + U + V)/Kg)+0; Bd = ((Y+U)/Kb)+0; } int main(void) { unsigned long diff; diff = 0; short err = 4; puts("Testing..."); for (R=0; R<=255; R++) { for (G=0; G<=255; G++) { for (B=0; B<=255; B++) { rgb_to_yuv(); yuv_to_rgb(); if (R-Rd > err|| Rd-R > err) //if (G-Gd > err|| Gd-G > err) //if (B-Bd > err|| Bd-B > err) {diff++;} } } } printf("%d colors differed\n", diff); }
-
Last edited by jagabo; 6th Feb 2017 at 22:28.
-
-
I posted ANSI C code to perform 8 bit RGB to 10 bit YUV and back here:
https://forum.videohelp.com/threads/376996-Lossless-Workflows-using-Premiere-Pro-Virtua...=1#post2446709
You can easily modify that for 8 bit YUV by removing the *4 scaling that was used to multiply 8 bit values to get 10 bit values, and the /4 scaling to convert 10 bit YUV to 8 bit YUV. -
I remember that code and was looking at it just today. It's quite complicated and I don't fully understand it. IIRC it works with BT.601 but I can't see how you would incorporate other color spaces such as BT.709 or BT.2020, plus I am not up to speed on matrix math.
I removed the *4 and /4 scaling and no joy.Last edited by chris319; 7th Feb 2017 at 04:50.
-
What do you mean "no joy"? The code didn't compile? You got erroneous results? In what way?
It's not written as matrix operations, it's just algebra, the same as your code except that it uses limited range rec.601 where black is at Y=16 and white = 235. And U and V range from 16 to 240. The 4.0 multiplier at the end converts to 10 bit limited range YUV where Y ranges from 64 to 940. Adding 0.5 before converting to integers gives rounding instead of truncation (for example, 0.666 rounds up to 1 rather than truncating to 0).
For example, in the line that starts
Code:fy = ( 0.256789 * fr
Code:fy = KR * fr
Code:KR * (235-16) / (255-0)
Similar Threads
-
ffmpeg/x264 RGB to YUV
By SameSelf in forum Video ConversionReplies: 40Last Post: 14th Nov 2016, 19:40 -
YUV/ RGB problem in Avisynth
By spiritt in forum Newbie / General discussionsReplies: 9Last Post: 6th Sep 2015, 05:31 -
is this YUV or RGB?
By marcorocchini in forum Newbie / General discussionsReplies: 2Last Post: 20th Apr 2014, 11:21 -
MENCODER: how to convert from YUV to RGB?
By marcorocchini in forum Newbie / General discussionsReplies: 1Last Post: 19th Dec 2013, 14:24 -
YUV 4:2:0 to RGB
By toshyuki in forum ProgrammingReplies: 15Last Post: 8th Oct 2012, 13:12