VideoHelp Forum




+ Reply to Thread
Results 1 to 23 of 23
  1. This is an exercise in converting between 10-bit RGB and YUV using BT.2020 coefficients.

    There is a tolerance of +/- 1 between the unconverted RGB value and the post-conversion RGB value to allow for rounding errors.

    I have cheated by multiplying the coefficients by 20 in order to bring the error count down, so these are not Kosher YUV values.

    It tests RGB values from 0 through 1023 and returns 0 errors, i.e. converted RGB +/- 1 of unconverted RGB.

    Feel free to play around with it. It is written in the C language.

    Code:
    #include "..\include\stdio.h"
    #include "..\include\float.h"
    
    //BT.709 CONSTANTS
    //#define KR 0.2126
    //#define KG 0.7152
    //#define KB 0.0722
    
    //BT.2020 CONSTANTS
    #define KR 0.2627
    #define KG 0.678
    #define KB 0.0593
    
    unsigned short nr,ng,nb,R,G,B;
    short Y,U,V; //16 bits
    /************************************************************************/
    //  Convert RGB to YUV
    /************************************************************************/
    void rgb_to_yuv(void)
    {
    double fr, fg, fb, fy, fu, fv;
    
    	fr = (double)R;
    	fg = (double)G;
    	fb = (double)B;
    
    fy = (KR*20)*fr + (KG*20)*fg + (KB*20)*fb;
    fu = (fb * KB*20) - fy;
    fv = (fr * KR*20) - fy;
    
    Y = (int)fy;
    U = (int)fu;
    V = (int)fv;
    
    //if (Y > 1023 || U > 1023 || V > 1023) {//printf("%d\n",Y);}
    
    }
    
    /************************************************************************/
    //  Convert YUV to RGB
    /************************************************************************/
    void yuv_to_rgb(void)
    {
    double fy, fu, fv;
    //CONVERT TO FLOAT FOR ROUNDING
    fy = (double)Y;
    fu = (double)U;
    fv = (double)V;
    
    nr = (int)(((fy+fv)/(KR*20))+0.5);
    ng = (int)((-(fy + fv + fu)/(KG*20))+0.5);
    nb = (int)(((fy+fu)/(KB*20))+0.5);
    
    }
    
    /************************************************************************/
    //  Test all 1023 RGB values through a round trip
    //  from RGB to YUV and back to RGB.
    /************************************************************************/
    void test_round_trip(void)
    {
    	int count, diff;
    	count = 0; diff =  0;
    
    	puts("\nTesting round trip...\n");
    
    	for (R=0; R<=1023; R++)
    	{
    		for (G=0; G<=1023; G++)
    		{
    			for (B=0; B<=1023; B++)
    			{
    				rgb_to_yuv();
    				yuv_to_rgb();
    
    //if ((R != nr) || (G != ng) || (B != nb))
    //if ( (R-nr>1) || (R-nr<-1) || (G-ng>1) || (G-ng<-1)||(B-nb>1) || (B-nb<-1) )
    
    int tol = 1;
    if( (R-nr>tol) || (R-nr<-tol) || (G-ng>tol) || (G-ng<-tol)||(B-nb>tol) || (B-nb<-tol) )
    				{
    //printf("%d, %d, %d -> %d, %d, %d\n", R, G, B, nr, ng, nb);
                  diff++;
    				}
    				count++;
            }
    		}
    	}
    	printf("%d colors tested\n", count);
    	printf("%d colors differed\n", diff);
    }
    /************************************************************************/
    
    int main(void) {
    test_round_trip();
    }
    Quote Quote  
  2. Originally Posted by chris319 View Post
    I have cheated by multiplying the coefficients by 20 in order to bring the error count down, so these are not Kosher YUV values.
    Then you are creating 15 bit values, not 10 bit.
    Quote Quote  
  3. Are you able to get it to work without that multiplication by 20?

    I'm not using the matrix math that you did in your program. I'm not familiar with using matrices.
    Quote Quote  
  4. Originally Posted by chris319 View Post
    Are you able to get it to work without that multiplication by 20?
    Of course not. The problem is the same as when using 8 bit: on average 6 RGB values map to the same YUV values. So you don't get anything near lossless conversion.

    If you want your YUV to be interchangeable with other software you need to stick with standards. Converting to 12 or 16 bit YUV would be a better choice.
    Quote Quote  
  5. you don't get anything near lossless conversion
    That's why I added "tolerance". I realize perfect conversion is not going to happen.

    Converting to 12 or 16 bit YUV would be a better choice.
    I'll give that a try.
    Quote Quote  
  6. Would multiplying by 32 give 16 bits of YUV?
    Last edited by chris319; 28th Jun 2016 at 20:08.
    Quote Quote  
  7. By the way, your code can be lossless if you round rather than truncate at the end of rgb_to_yuv(). Ie, change:
    Code:
    	Y = (int)fy;
    	U = (int)fu;
    	V = (int)fv;
    to:
    Code:
    	Y = (int)(fy+0.5);
    	if (fu>0.0)
    		U = (int)(fu+0.5);
    	else
    		U = (int)(fu-0.5);
    	if (fv>0.0)
    		V = (int)(fv+0.5);
    	else
    		V = (int)(fv-0.5);
    Quote Quote  
  8. Maybe it doesn't matter, but your conversions are missing:

    1. The scaling factors
    2. The offsets

    And,

    You should multiply by 2^(b-8) where b = 8, 10, 12 or however many bits.
    Quote Quote  
  9. Originally Posted by chris319 View Post
    Would multiplying by 32 give 16 bits of YUV?
    Check that. Multiply by 64?
    Quote Quote  
  10. On closer inspection, your conversion are just wrong. So I don't know what you are trying to do. I posted the correct conversions a while ago in my lossless thread.
    Quote Quote  
  11. Originally Posted by chris319 View Post
    Originally Posted by chris319 View Post
    Would multiplying by 32 give 16 bits of YUV?
    Check that. Multiply by 64?
    Yes. But for interchangeability with other software you need to do what SameSelf said.
    Quote Quote  
  12. I posted the correct conversions a while ago in my lossless thread.
    I remember that and I was unable to get them to work.

    So I don't know what you are trying to do.
    Convert RGB to YUV and back with as little error as possible. You're welcome to take a stab at rewriting the code. It is written in C but you may use Pascal, BASIC or whatever language you prefer.

    Here's what I've got now. Y, U and V are rounded, not truncated. It is not totally error-free but the errors are +/- 1.

    Code:
     
    #include <stdio.h>
    #include <float.h>
    
    //BT.709 CONSTANTS
    //#define KR 0.2126
    //#define KG 0.7152
    //#define KB 0.0722
    
    //BT.2020 CONSTANTS
    #define KR 0.2627
    #define KG 0.678
    #define KB 0.0593
    
    unsigned short nr,ng,nb,R,G,B;
    long Y,U,V; //16 bits
    /************************************************************************/
    //  Convert RGB to YUV
    /************************************************************************/
    void rgb_to_yuv(void)
    {
    double fr, fg, fb, fy, fu, fv;
    
    	fr = (double)R;
    	fg = (double)G;
    	fb = (double)B;
    
    fy = (KR*256)*fr + (KG*256)*fg + (KB*256)*fb;
    fu = (fb * KB*256) - fy;
    fv = (fr * KR*256) - fy;
    
    Y = (int)(fy+0.5);
    U = (int)(fu+0.5);
    V = (int)(fv+0.5);
    
    //if (Y > 1023 || U > 1023 || V > 1023) {//printf("%d\n",Y);}
    
    }
    
    /************************************************************************/
    //  Convert YUV to RGB
    /************************************************************************/
    void yuv_to_rgb(void)
    {
    double fy, fu, fv;
    //CONVERT TO FLOAT FOR ROUNDING
    fy = (double)Y;
    fu = (double)U;
    fv = (double)V;
    
    nr = (int)(((fy+fv)/(KR*256))+0.5);
    ng = (int)((-(fy + fv + fu)/(KG*256))+0.5);
    nb = (int)(((fy+fu)/(KB*256))+0.5);
    
    }
    
    /************************************************************************/
    //  Test all 1023 RGB values through a round trip
    //  from RGB to YUV and back to RGB.
    /************************************************************************/
    void test_round_trip(void)
    {
    	int count, diff;
    	count = 0; diff =  0;
    
    	puts("\nTesting round trip...\n");
    
    	for (R=0; R<=1023; R++)
    	{
    		for (G=0; G<=1023; G++)
    		{
    			for (B=0; B<=1023; B++)
    			{
    				rgb_to_yuv();
    				yuv_to_rgb();
    
    //if ((R != nr) || (G != ng) || (B != nb))
    if ( (R-nr>1) || (R-nr<-1) || (G-ng>1) || (G-ng<-1)||(B-nb>1) || (B-nb<-1) )
    
    //int tol = 1;
    //if( (R-nr>tol) || (R-nr<-tol) || (G-ng>tol) || (G-ng<-tol)||(B-nb>tol) || (B-nb<-tol) )
    				{
    //printf("%d, %d, %d -> %d, %d, %d\n", R, G, B, nr, ng, nb);
                  diff++;
    				}
    				count++;
            }
    		}
    	}
    	printf("%d colors tested\n", count);
    	printf("%d colors differed\n", diff);
    }
    /************************************************************************/
    
    int main(void) {
    test_round_trip();
    }
    Quote Quote  
  13. Multiplying by 256 gives 100% error free.

    Code:
     
    #include <stdio.h>
    #include <float.h>
    
    //BT.709 CONSTANTS
    //#define KR 0.2126
    //#define KG 0.7152
    //#define KB 0.0722
    
    //BT.2020 CONSTANTS
    #define KR 0.2627
    #define KG 0.678
    #define KB 0.0593
    
    unsigned short nr,ng,nb,R,G,B;
    long Y,U,V; //16 bits
    /************************************************************************/
    //  Convert RGB to YUV
    /************************************************************************/
    void rgb_to_yuv(void)
    {
    double fr, fg, fb, fy, fu, fv;
    
    	fr = (double)R;
    	fg = (double)G;
    	fb = (double)B;
    
    fy = (KR*256)*fr + (KG*256)*fg + (KB*256)*fb;
    fu = (fb * KB*256) - fy;
    fv = (fr * KR*256) - fy;
    
    Y = (int)(fy+0.5);
    U = (int)(fu+0.5);
    V = (int)(fv+0.5);
    
    //if (Y > 1023 || U > 1023 || V > 1023) {//printf("%d\n",Y);}
    
    }
    
    /************************************************************************/
    //  Convert YUV to RGB
    /************************************************************************/
    void yuv_to_rgb(void)
    {
    double fy, fu, fv;
    //CONVERT TO FLOAT FOR ROUNDING
    fy = (double)Y;
    fu = (double)U;
    fv = (double)V;
    
    nr = (int)(((fy+fv)/(KR*256))+0.5);
    ng = (int)((-(fy + fv + fu)/(KG*256))+0.5);
    nb = (int)(((fy+fu)/(KB*256))+0.5);
    
    }
    
    /************************************************************************/
    //  Test all 1023 RGB values through a round trip
    //  from RGB to YUV and back to RGB.
    /************************************************************************/
    void test_round_trip(void)
    {
    	int count, diff;
    	count = 0; diff =  0;
    
    	puts("\nTesting round trip...\n");
    
    	for (R=0; R<=1023; R++)
    	{
    		for (G=0; G<=1023; G++)
    		{
    			for (B=0; B<=1023; B++)
    			{
    				rgb_to_yuv();
    				yuv_to_rgb();
    
    if ((R != nr) || (G != ng) || (B != nb))
    //if ( (R-nr>1) || (R-nr<-1) || (G-ng>1) || (G-ng<-1)||(B-nb>1) || (B-nb<-1) )
    
    //int tol = 1;
    //if( (R-nr>tol) || (R-nr<-tol) || (G-ng>tol) || (G-ng<-tol)||(B-nb>tol) || (B-nb<-tol) )
    				{
    //printf("%d, %d, %d -> %d, %d, %d\n", R, G, B, nr, ng, nb);
                  diff++;
    				}
    				count++;
            }
    		}
    	}
    	printf("%d colors tested\n", count);
    	printf("%d colors differed\n", diff);
    }
    /************************************************************************/
    
    int main(void) {
    test_round_trip();
    }
    Quote Quote  
  14. sameself: You posted this a while back.

    Originally Posted by SameSelf View Post
    I am just going to modify your RGB > YUV portion. I am not fluent in whatever language that is and my changes are a little messy.

    Code:
    Y = (219/255)*(KR*fR + (1-KR-KB)*fG + KB*fB);
    U = (224/255)/2/(1-KB)*(fB - Y*(255/219));
    V = (224/255)/2/(1-KR)*(fR - Y*(255/219));
    I have to ask you where the numbers 219 and 224 come from. I assume 255 is (2^8) - 1 for 8-bit video. For 10-bit it would be 1023.

    Next we have to get from YUV to RGB.
    Quote Quote  
  15. Originally Posted by chris319 View Post
    I have to ask you where the numbers 219 and 224 come from.
    8 bit rec.601 and rec.709 define full black as Y=16 and full white as Y=235. So the valid range of Y is 235 - 16 = 219. In 8 bit RGB full black is 0,0,0 and full white is 255,255,255. So the range is 255 - 0 = 255. On conversion from RGB to Y the range has to be scaled from 255 units to 219 units, and 16 added to move everything up from 0 to 16.

    Similarly, the valid range of U and V is 16 to 240. 240 - 16 = 224.
    Quote Quote  
  16. 8 bit rec.601 and rec.709 define full black as Y=16 and full white as Y=235. So the valid range of Y is 235 - 16 = 219.
    Thank you for the explanation.

    In 8 bit RGB full black is 0,0,0 and full white is 255,255,255. So the range is 255 - 0 = 255.
    I was going from 0 - 1023 10-bit RGB and was not scaling to studio swing.

    I'm finding that for 100% accuracy from 0 through 1023 and multiplying the coefficients by 2^(16-8), Y, U and V must be 32-bit longs.

    If you want your YUV to be interchangeable with other software you need to stick with standards. Converting to 12 or 16 bit YUV would be a better choice.
    I'm finding that 14-bit YUV gives 100% accuracy from 0 - 255 without scaling to studio swing. The coefficients are thus multiplied by 64, and Y, U and V can be contained within 16-bit signed words.
    Last edited by chris319; 29th Jun 2016 at 14:05.
    Quote Quote  
  17. I would recommend going back and doing some reading of the definitions and conventions. You are really far afield from standard practice and what NLE's actually do. We established a while back that you need 10 bits in YUV space to losslessly round trip RGB24/32. I suppose you could generalize this by saying x bits in RGB require x+2 bits in YUV. Are you disputing that? Expanding out YUV to full range should not change that. But I don't know, nor am I willing to waste time testing it. At the end of the day, it all comes down to the definitions and conventions, and if you refuse to incorporate them into your conversions, then I am unable to help because that is what my models use.
    Quote Quote  
  18. Originally Posted by SameSelf View Post
    I don't know, nor am I willing to waste time testing it.
    I don't consider it a waste of time and I am testing it.
    Quote Quote  
  19. That is great. I am not trying to discourage you. Everyone has to follow their own path to enlightenment! I am sure, after you are done, that you will find the time and energy to conform your conversions to convention, and then we can all be on the same page.
    Quote Quote  
  20. In perusing the BT.709 spec I get the impression that the 16 - 235 clamping is performed in the RGB domain and not in the conversion to YUV. Am I incorrect on this? This explains why my YUV conversion code looks incomplete to some.

    It is also my impression that as long as Y, U and V are confined to 10 bits, i.e. the range 0 to 1023, there will be color errors and the implementors figure the user just lives with it. For perfect color it seems Y, U and V would have to occupy greater than 10 bits and this brings up bandwidth issues. For example, 20 bits each of Y, U and V would fit within 64 bits and this doubles the bandwidth of 3 x 10-bit words.

    Of course I could be completely wrong on all of this.

    The code below quantifies the color error. N.B.: This is all experimentation on my part, just goofin' around. It is not intended to be strictly standards compliant.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <float.h>
    
    //BT.709 CONSTANTS
    #define KR 0.2126*4
    #define KG 0.7152*4
    #define KB 0.0722*4
    
    //BT.2020 CONSTANTS
    //#define KR 0.2627
    //#define KG 0.678
    //#define KB 0.0593
    
    unsigned short nr,ng,nb,R,G,B;
    short Y,U,V, maxy=-256, maxu=-256, maxv=-256;
    double error=0;
    /************************************************************************/
    //  Convert an 8 bit RGB value to a 10 bit YUV value.
    /************************************************************************/
    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;
    fu = (fb * KB) - fy;
    fv = (fr * KR) - fy;
    
    Y = (int)(fy+0.5);
    U = (int)(fu+0.5);
    V = (int)(fv+0.5);
    if (Y > maxy){maxy = Y;}
    if (-U > maxu){maxu = -U;}
    if (-V > maxv){maxv = -V;}
    }
    
    /************************************************************************/
    //  Convert a 10 bit YUV value to an 8 bit RGB value.
    /************************************************************************/
    void yuv_to_rgb(void)
    {
    double fy, fu, fv;
    
    fy = (double)Y;
    fu = (double)U;
    fv = (double)V;
    
    nr = (int)(((fy+fv)/(KR))+0.5);
    ng = (int)((-(fy + fv + fu)/(KG))+0.5);
    nb = (int)(((fy+fu)/(KB))+0.5);
    
    }
    
    /************************************************************************/
    //  Test all 16 million 8 bit RGB values through a round trip
    //  from 8 bit RGB to 10 bit YUV and back to 8 bit RGB.
    /************************************************************************/
    void test_round_trip(void)
    {
    	int count, diff;
    	count = 0; diff =  0;
    
    	puts("\nTesting round trip...\n");
    
    	for (R=16; R<=235; R++)
    	{
    		for (G=16; G<=235; G++)
    		{
    			for (B=16; B<=235; B++)
    			{
    				rgb_to_yuv();
    				yuv_to_rgb();
    
    //if ((R != nr) || (G != ng) || (B != nb))
    
    int tol = 0;if((R-nr>tol) || (R-nr<-tol) || (G-ng>tol) || (G-ng<-tol)||(B-nb>tol) || (B-nb<-tol))
    
    //printf("%d, %d, %d -> %d, %d, %d\n", R, G, B, nr, ng, nb);
                  diff++;
    				}
    count++;
    error = error + abs(nr-R)+ abs(ng-G) + abs(nb-B);
            }
    		}
    
    	//printf("%d colors tested\n", count);
    	//printf("%d colors differed\n", diff);
    	printf("max y: %d max 1024\n", maxy);
    	printf("max u: %d max 1024\n", -maxu);
    	printf("max v: %d max 1024\n", -maxv);
    	printf("error: %.0f\n",error/(double)count+0.5);
    	printf("error pct: %.2f\n",(error/(double)count)/(235.0-16.0)*100);
    }
    /************************************************************************/
    
    int main(void) {
    test_round_trip();
    }
    Quote Quote  
  21. I am not sure how you got that "impression". Clamping is something that only exists in YUV space and is a carryover from the days (long before RGB existed) of analog broadcast signals that required headroom and footroom. But then people/programs/camcorders do all kinds of weird stuff, most notably superwhites on consumer cams. Spend some time reading about about why this exists. It might help you especially if you need to preserve details in the superwhite and superblack ranges.
    Quote Quote  
  22. This converts 8-bit RGB to 10-bit YUV in BT.709 space. Coefficients at http://www.poynton.com/PDFs/coloureq.pdf.

    Y values are in range between 64 and 940. U and V values are in range between 64 and 960.

    No errors in RGB range 0 to 255.

    Code:
    #include <stdio.h>
    #include <float.h>
    
    //BT.709 CONSTANTS
    #define KR 0.2126
    #define KG 0.7152
    #define KB 0.0722
    short maxy = 0, maxu = 0, maxv = 0, miny = 1024, minu = 1024, minv = 1024;
    /************************************************************************/
    //  Convert an 8 bit RGB value to a 10 bit YUV value BT.709
    /************************************************************************/
    
    void rgb_to_yuv(short R, short G, short B, short *Y, short *U, short *V)
    {
    	double fr, fg, fb, fy, fu, fv;
    
    	fr = (double)R;
    	fg = (double)G;
    	fb = (double)B;
    
    // high precision matrix at http://www.poynton.com/PDFs/coloureq.pdf
    fy = ((KR * fr + KG * fg + KB * fb)*4);
    *Y = (int)(fy+0.5);
    if (*Y > maxy) {maxy = *Y;}
    if (*Y < miny) {miny = *Y;}
    
    fu = (-0.1145 * R - 0.3855 * G + 0.5 * B) + 128;
    *U = (int)(fu * 4.0 + 0.5);
    if (*U > maxu) {maxu = *U;}
    if (*U < minu) {minu = *U;}
    
    fv = (0.5016 * R - 0.4556 * G - 0.0459 * B) + 128;
    *V = (int)(fv * 4.0 + 0.5);
     if (*V > maxv) {maxv = *V;}
    if (*V < minv) {minv = *Y;}
    
    }
    
    /************************************************************************/
    //  Convert a 10 bit YUV value to an 8 bit RGB value BT.709
    /************************************************************************/
    void yuv_to_rgb(short Y, short U, short V, short *R, short *G, short *B)
    {
    	double fr, fg, fb, fy, fu, fv;
    	fy = (double)Y / 4.0;
    	fu = (double)U / 4.0 - 128.0;
    	fv = (double)V / 4.0 - 128.0;
    
    // high precision values at http://www.poynton.com/PDFs/coloureq.pdf
    fr= (int)((fy + 1.5701 * fv)+0.5);
    fg= (int)((fy - 0.1870 * fu - 0.4664 * fv)+0.5);
    fb= (int)((fy + 1.8556 * fu)+0.5);
    
    	*R = (int)(fr + 0.5);
    	*G = (int)(fg + 0.5);
    	*B = (int)(fb + 0.5);
    }
    
    /************************************************************************/
    //  Test all 16 million 8 bit RGB values through a round trip
    //  from 8 bit RGB to 10 bit YUV and back to 8 bit RGB.
    /************************************************************************/
    
    void test_round_trip(void)
    {
    	short r, g, b, y, u, v, nr, ng, nb;
    	int count, diff;
    
    	printf("\nTesting round trip...\n");
    
    	count = 0;
    	diff = 0;
    
    	for (r=0; r<=255; r++)
    	{
    		for (g=0; g<=255; g++)
    		{
    			for (b=0; b<=255; b++)
    			{
    				rgb_to_yuv(r, g, b, &y, &u, &v);
    				yuv_to_rgb(y, u, v, &nr, &ng, &nb);
    				if ((r!=nr) || (g!=ng) || (b!=nb))
    				{
    					//printf("%d, %d, %d -> %d, %d, %d -> %d, %d, %d\n", r, g, b, y, u, v, nr, ng, nb);
    					diff++;
    				}
    				count++;
    			}
    		}
    	}
    	printf("%d colors tested\n", count);
    	printf("%d colors differed\n", diff);
    }
    
    /************************************************************************/
    int main(void) {
    test_round_trip();
    }
    Quote Quote  
  23. as a side note: may be some moderator could move this to the programming section?
    users currently on my ignore list: deadrats, Stears555, marcorocchini
    Quote Quote  



Similar Threads

Visit our sponsor! Try DVDFab and backup Blu-rays!