VideoHelp Forum
+ Reply to Thread
Page 2 of 3
FirstFirst 1 2 3 LastLast
Results 31 to 60 of 71
Thread
  1. Removing the *4 and /4 scaling, here is what I get when testing from 0 to 255:

    Testing round trip...
    16777216 colors tested
    14133458 colors differed

    Testing from 16 to 235 gives:

    Testing round trip...
    10648000 colors tested
    8980712 colors differed
    Quote Quote  
  2. That's exactly what you expect to see. Like I said, roughly six RGB colors map to each legal YUV color when using 8 bit limited range YUV.

    Consider this one dimensional list of 12 numbers:

    Code:
    0 1 2 3 4 5 6 7 8 9 10 11
    Divide each by 6 then truncate the result (for example 11 / 6 = 1.8333... truncated to 1):

    Code:
    0 0 0 0 0 0 1 1 1 1 1 1
    They map to only two different values. Now convert them back by multiplying by 6:

    Code:
    0 0 0 0 0 0 6 6 6 6 6 6
    Again they map to only two different values, 0 and 6. Five out of every six are inaccurate.

    That's what's happening in the conversion of 8 bit RGB to 8 bit limited range YUV, except that it's happening in three dimensions, not one.
    Last edited by jagabo; 7th Feb 2017 at 10:47.
    Quote Quote  
  3. Agreed there will be some errors, but we still don't know for sure the magnitude of those errors. A few people have made educated guesses.
    Quote Quote  
  4. I worked it out in PureBasic. The errors are <= 17, or about 6.6% (17/255).

    Code:
    ;UPDATED 2/7/2017
    
    OpenConsole()
    
    ;BT.709 CONSTANTS
    ;#Kr = 0.2126: #Kg = 0.7152: #Kb = 0.0722
    
    ;REC 601 CONSTANTS
    ;#KR = 0.299: #KG = 0.587: #KB = 0.114
    
    ;NTSC CONSTANTS
    ;#KR = 3: #KG = 6: #KB = 1
    
    ;BT.2020 CONSTANTS
    #Kr= 0.2627 : #Kg= 0.678 : #Kb= 0.0593
    
    error = 0
    #err = 17
    
    Global Rd.w,Gd.w,Bd.w,R1.w,G1.w,B1.w
    Global U.b, V.b, Y.a
    
    Procedure rgb_to_yuv()
    Y = #Kr*R1 + #Kg*G1 + #Kb*B1
    U = (B1*#Kb - Y) / 2
    V = (R1*#Kr - Y) / 2
    EndProcedure
    
    Procedure yuv_to_rgb()
    Ut.w = U * 2
    Vt.w = V * 2
    Rd= ((Y+Vt)/#Kr) + 0.5
    Gd= (-(Y + Vt + Ut)/#Kg) + 0.5
    Bd= ((Y + Ut)/#Kb) + 0.5
    
    If Rd > 255: Rd = 255
    ElseIf Rd < 0: Rd = 0
    ElseIf Gd > 255: Gd = 255
    ElseIf Gd < 0: Gd = 0
    ElseIf Bd > 255: Bd = 255
    ElseIf Bd < 0: Bd = 0
    EndIf
    EndProcedure
    
    For R1 = 0 To 255
    For G1 = 0 To 255
    For B1 = 0 To 255
    
    rgb_to_yuv(): yuv_to_rgb()
    
    If R1-Rd > #err Or Rd-R1 > #err Or G1-Gd > #err Or Gd-G1 > #err Or B1-Bd > #err Or Bd-B1 > #err
    error + 1
    EndIf
    
    Next
    Next
    Next
    
    PrintN("Errors: "+Str(error))
    
    Repeat
    Delay(100)
    Until Inkey() <> ""
    Quote Quote  
  5. The errors drop to <= 9 if BT.601 coefficients are used, or about 3.5% (9/255).
    Quote Quote  
  6. Originally Posted by chris319 View Post
    Agreed there will be some errors, but we still don't know for sure the magnitude of those errors. A few people have made educated guesses.
    Of course we know what the errors are. All you have to do is print out the before and after RGB values. In my program the difference is never more than 2 units on each subchannel. The vast majority only differ by 1.
    Last edited by jagabo; 7th Feb 2017 at 13:49.
    Quote Quote  
  7. Originally Posted by jagabo View Post
    In my program the difference is never more than 2 units on each subchannel. The vast majority only differ by 1.
    Your program uses 16-bit ints for Y, U and V and gets the errors down to zero. The errors creep in when going from 16 to 8 bits for Y, U and V. As you said,

    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.
    Why don't you write this program out in compilable form using 8 bits for Y, U and V, including a main() function? I'm having trouble coverting your C. I can add the part that evaluates the magnitude of the errors.
    Last edited by chris319; 7th Feb 2017 at 16:06.
    Quote Quote  
  8. Originally Posted by chris319 View Post
    Originally Posted by jagabo View Post
    In my program the difference is never more than 2 units on each subchannel. The vast majority only differ by 1.
    Your program uses 16-bit ints for Y, U and V and gets the errors down to zero.
    Just because it uses16 bit shorts to hold the YUV values doesn't mean the YUV values are 16 bit. It uses only 10 of the 16 bits (ie, the values are always between 0 and 1023). If you remove the *4 and /4 (or change them to *1 and /1) then it only uses 8 of the 16 bits.

    At 10 bits the round trip is 100 percent lossless. At 8 bits about 5/6 of the RGB values differ from the original. Never by more that 2 units of R, G, and/or B.

    Originally Posted by chris319 View Post
    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.
    Why don't you write this program out in compilable form using 8 bits for Y, U and V, including a main() function? I'm having trouble coverting your C. I can add the part that evaluates the magnitude of the errors.
    Because it's already written. All you have to do is remove the *4 and /4.
    Last edited by jagabo; 7th Feb 2017 at 16:30.
    Quote Quote  
  9. Here is a program written in PureBasic which analyzes the errors in RGB/YUV round-trip conversions. It uses the avisynth code described here: http://avisynth.nl/index.php/Color_conversions

    The maximum error for each of the R, G and B channels is +/- 1 for any set of coefficients (601, 709, 2020, NTSC). This is not a count of the errors; it is the maximum deviation from the original RGB values.

    Good enough for me.

    Code:
    OpenConsole()
    
    ;BT.709 CONSTANTS
    #Kr = 0.2126: #Kg = 0.7152: #Kb = 0.0722
    
    ;REC 601 CONSTANTS
    ;The Cb And Cr samples are unsigned
    ;#KR = 0.299: #KG = 0.587: #KB = 0.114
    
    ;NTSC CONSTANTS
    ;#KR = 0.30: #KG = 0.59: #KB = 0.11
    
    ;BT.2020 CONSTANTS
    ;#Kr= 0.2627 : #Kg= 0.678 : #Kb= 0.0593
    
    Maxrederror = 0
    Maxgreenerror = 0
    Maxblueerror = 0
    
    Global Rd.a,Gd.a,Bd.a,R1.a,G1.a,B1.a
    Global U.a, V.a, Y.a
    
    Procedure rgb_to_yuv()
    ;avisynth code
    ;http://avisynth.nl/index.php/Color_conversions
    Y = #Kr*R1 + #Kg*G1 + #Kb*b1
    V = 0.5*R1 - 0.5*G1*#Kg/(1-#Kr) - 0.5*b1*#Kb/(1-#Kr) + 128
    U = -0.5*R1*#Kr/(1-#Kb) - 0.5*G1*#Kg/(1-#Kb) + 0.5*B1 + 128
    EndProcedure
    
    Procedure yuv_to_rgb()
    ;avisynth code
    ;http://avisynth.nl/index.php/Color_conversions
    Rd = y + 2*(v-128)*(1-#Kr)
    Gd = y - 2*(u-128)*(1-#Kb)*#Kb/#Kg - 2*(v-128)*(1-#Kr)*#Kr/#Kg
    Bd = y + 2*(u-128)*(1-#Kb)
    EndProcedure
    
    G1=0:B1=0
    For R1 = 1 To 254
    rgb_to_yuv(): yuv_to_rgb()
    If Rd-R1 > maxRedError: maxRedError = Rd-R1:EndIf
    If R1-Rd > maxRedError: maxRedError = R1-Rd:EndIf
    Next
    PrintN("Max red error: "+Str(maxRedError))
    PrintN(StrF(maxredError*100 / 235,2)+"%")
    Print(Chr(10))
    
    R1=0:B1=0
    For G1 = 1 To 254
    rgb_to_yuv(): yuv_to_rgb()
    If Gd-G1 > maxGreenError: maxGreenError = Gd-G1:EndIf
    If G1-Gd > maxGreenError: maxGreenError = G1-Gd:EndIf
    Next
    PrintN("Max green error: "+Str(maxGreenError))
    PrintN(StrF(maxgreenError*100 / 235,2)+"%")
    Print(Chr(10))
    
    R1=0:G1=0
    For B1 = 1 To 254
    rgb_to_yuv(): yuv_to_rgb()
    If Bd-B1 > maxBlueError: maxBlueError = Bd-B1:EndIf
    If B1-Bd > maxBlueError: maxBlueError = B1-Bd:EndIf
    Next
    PrintN("Max blue error: "+Str(maxBlueError))
    PrintN(StrF(maxblueError*100 / 235,2)+"%")
    
    Repeat
    Delay(100)
    Until Inkey() <> ""
    Quote Quote  
  10. You only tested pure reds, pure greens, and pure blues. Only 763 of the >16M colors.
    Quote Quote  
  11. Good catch. Take a look at this. Here are the results:

    Colors tested: 16387064

    Max red error: 1
    0.39%

    Max green error: 1
    0.39%

    Max blue error: 1
    0.39%

    Code:
    OpenConsole()
    
    ;BT.709 CONSTANTS
    #Kr = 0.2126: #Kg = 0.7152: #Kb = 0.0722
    
    ;REC 601 CONSTANTS
    ;The Cb And Cr samples are unsigned
    ;#KR = 0.299: #KG = 0.587: #KB = 0.114
    
    ;NTSC CONSTANTS
    ;#KR = 0.30: #KG = 0.59: #KB = 0.11
    
    ;BT.2020 CONSTANTS
    ;#Kr= 0.2627 : #Kg= 0.678 : #Kb= 0.0593
    
    Maxrederror = 0
    Maxgreenerror = 0
    Maxblueerror = 0
    
    Global Rd.a,Gd.a,Bd.a,R1.a,G1.a,B1.a
    Global U.a, V.a, Y.a
    
    Procedure rgb_to_yuv()
    ;avisynth code
    ;http://avisynth.nl/index.php/Color_conversions
    Y = #Kr*R1 + #Kg*G1 + #Kb*b1
    V = 0.5*R1 - 0.5*G1*#Kg/(1-#Kr) - 0.5*b1*#Kb/(1-#Kr) + 128
    U = -0.5*R1*#Kr/(1-#Kb) - 0.5*G1*#Kg/(1-#Kb) + 0.5*B1 + 128
    EndProcedure
    
    Procedure yuv_to_rgb()
    ;avisynth code
    ;http://avisynth.nl/index.php/Color_conversions
    Rd = y + 2*(v-128)*(1-#Kr)
    Gd = y - 2*(u-128)*(1-#Kb)*#Kb/#Kg - 2*(v-128)*(1-#Kr)*#Kr/#Kg
    Bd = y + 2*(u-128)*(1-#Kb)
    EndProcedure
    
    R1=1:G1=1:B1=1:count = 0
    
    For Rct = 1 To 254
    For Gct = 1 To 254
    For Bct = 1 To 254
    
    R1 = Rct: G1 = Gct: B1 = Bct
    rgb_to_yuv(): yuv_to_rgb()
    
    If Rd-R1 > maxRedError: maxRedError = Rd-R1:EndIf
    If R1-Rd > maxRedError: maxRedError = R1-Rd:EndIf
    
    If Gd-G1 > maxGreenError: maxGreenError = Gd-G1:EndIf
    If G1-Gd > maxGreenError: maxGreenError = G1-Gd:EndIf
    
    If Bd-B1 > maxBlueError: maxBlueError = Bd-B1:EndIf
    If B1-Bd > maxBlueError: maxBlueError = B1-Bd:EndIf
    
    count + 1
    
    Next
    Next
    Next
    
    PrintN("Colors tested: " + Str(count)+Chr(10))
    
    PrintN("Max red error: "+Str(maxRedError))
    PrintN(StrF(maxredError*100 / 254,2)+"%")
    Print(Chr(10))
    
    PrintN("Max green error: "+Str(maxGreenError))
    PrintN(StrF(maxgreenError*100 / 254,2)+"%")
    Print(Chr(10))
    
    PrintN("Max blue error: "+Str(maxBlueError))
    PrintN(StrF(maxblueError*100 / 254,2)+"%")
    
    Repeat: Delay(100): Until Inkey() <> ""
    Last edited by chris319; 17th Feb 2017 at 21:27.
    Quote Quote  
  12. I'm not real familiar with Visual Basic but shouldn't you explicitly cast you variables as integers?
    Quote Quote  
  13. Originally Posted by jagabo View Post
    I'm not real familiar with Visual Basic but shouldn't you explicitly cast you variables as integers?
    PureBasic, not Visual Basic.

    PureBasic is variable challenged. The only unsigned variables available are 8-bit bytes and 16-bit shorts. All other variables are signed. By default, unless otherwise declared, all variables are 64 bit signed because I'm using the 64-bit version of PB. So maxrederror, maxgreenerror, maxblue error are 64 bits signed. This won't affect the tally of errors, however.

    In the lines beginning with "Global" we have Rd.a, etc. The ".a" declares the variable Rd as an unsigned 8-bit byte. The "a" stands for "ascii" as in "ascii character" in the minds of the PureBasic developers. Many PureBasic users have clamored for a full range of unsigned variables but the PB developers can't figure it out.

    PB does some behind-the-scenes rounding when going from float to int. What I should do is rewrite this program in Pascal.
    Quote Quote  
  14. Same thing in Pascal, same result: +/- 1

    Pascal doesn't cut you any slack regarding variable types.

    Code:
    const
    Kr = 0.30; Kg = 0.59; Kb = 0.11;
    
    var
    Y,U,V, R1,G1,B1,Rd,Gd,Bd : byte;
    maxrederror : longword = 0;
    maxgreenerror : longword = 0;
    maxblueerror : longword = 0;
    count : longword = 0;
    
    procedure rgb_to_yuv;
    begin
    (*avisynth code
    http://avisynth.nl/index.php/Color_conversions*)
    Y := round(Kr*R1 + Kg*G1 + Kb*B1);
    V := round(0.5*R1 - 0.5*G1*Kg/(1-Kr) - 0.5*B1*Kb/(1-Kr) + 128);
    U := round(-0.5*R1*Kr/(1-Kb) - 0.5*G1*Kg/(1-Kb) + 0.5*B1 + 128);
    end;
    
    procedure yuv_to_rgb;
    begin
    (*avisynth code
    http://avisynth.nl/index.php/Color_conversions*)
    Rd := round(Y + 2*(v-128)*(1-Kr));
    Gd := round(Y - 2*(u-128)*(1-Kb)*Kb/Kg - 2*(v-128)*(1-Kr)*Kr/Kg);
    Bd := round(Y + 2*(u-128)*(1-Kb));
    end;
    
    begin (*main program*)
    
    for R1 := 1 to 254 do
    for G1 := 1 to 254 do
    for B1 := 1 to 254 do
    begin
    
    rgb_to_yuv; yuv_to_rgb; inc(count);
    
    If Rd-R1 > maxRedError then maxrederror := Rd-R1
    else if R1-Rd > maxRedError then maxrederror := R1-Rd;
    
    If Gd-G1 > maxGreenError then maxGreenerror := Gd-G1
    else if G1-Gd > maxGreenError then maxGreenerror := G1-Gd;
    
    If Bd-B1 > maxBlueError then maxBlueerror := Bd-B1
    else if B1-Bd > maxBlueError then maxBlueerror := B1-Bd;
    
    end;
    
    writeln('Colors tested:  ',count);
    writeln('Maximum red error:   ',maxrederror);
    writeln('Maximum green error: ',maxGreenerror);
    writeln('Maximum blue error:  ',maxBlueerror);
    
    end.
    Last edited by chris319; 18th Feb 2017 at 06:41.
    Quote Quote  
  15. Keep in mind that no production software would use floating point for this. They would use scaled integers or lookup tables.
    Quote Quote  
  16. Originally Posted by jagabo View Post
    Keep in mind that no production software would use floating point for this. They would use scaled integers or lookup tables.
    The coefficients are floating point. The rest are integers. Pascal won't compile the code unless the parts containing floating point (coefficients) are explicitly either rounded or truncated.

    If the video is transmitted, Y U and V are transmitted as unsigned bytes.
    Last edited by chris319; 18th Feb 2017 at 04:16.
    Quote Quote  
  17. Nobody would use floating point coefficients to do this in production code that works with large amounts of data. They would use scaled integers or table lookups. Integer math is on the order of 10 times faster than floating point. For example:

    Code:
    Y := round(Kr*R1 + Kg*G1 + Kb*B1);
    would be performed like this with pc.601 coefficients multiplied by 65536:

    Code:
    ;#KR = 0.299: #KG = 0.587: #KB = 0.114 each multiplied by 65536
    Y := (19595*R1 + 38470*G1 + 7471*B1) / 65536;
    That avoids any floating point math but loses a little accuracy.
    Quote Quote  
  18. Avisynth actually uses scaled integer math (convert.h)
    Code:
    inline void YUV2RGB(int y, int u, int v, BYTE* out) 
    {
      const int crv = int(1.596*65536+0.5);
      const int cgv = int(0.813*65536+0.5);
      const int cgu = int(0.391*65536+0.5);
      const int cbu = int(2.018*65536+0.5);
    
      int scaled_y = (y - 16) * int((255.0/219.0)*65536+0.5);
    
      out[0] = ScaledPixelClip(scaled_y + (u-128) * cbu);                 // blue
      out[1] = ScaledPixelClip(scaled_y - (u-128) * cgu - (v-128) * cgv); // green
      out[2] = ScaledPixelClip(scaled_y                 + (v-128) * crv); // red
    }
    
    //...
    
    inline int RGB2YUV(int rgb) 
    {
      const int cyb = int(0.114*219/255*65536+0.5);
      const int cyg = int(0.587*219/255*65536+0.5);
      const int cyr = int(0.299*219/255*65536+0.5);
    
      // y can't overflow
      int y = (cyb*(rgb&255) + cyg*((rgb>>8)&255) + cyr*((rgb>>16)&255) + 0x108000) >> 16;
      int scaled_y = (y - 16) * int(255.0/219.0*65536+0.5);
      int b_y = ((rgb&255) << 16) - scaled_y;
      int u = ScaledPixelClip((b_y >> 10) * int(1/2.018*1024+0.5) + 0x800000);
      int r_y = (rgb & 0xFF0000) - scaled_y;
      int v = ScaledPixelClip((r_y >> 10) * int(1/1.596*1024+0.5) + 0x800000);
      return ((y*256+u)*256+v) | (rgb & 0xff000000);
    }
    
    //...
    
    static __inline BYTE ScaledPixelClip(int i) {
      return PixelClip((i+32768) >> 16);
    }
    EDIT note, I don't fully understand this code, which is one reason I was reluctant to submit my own C++ implementation.
    Last edited by raffriff42; 18th Feb 2017 at 11:46.
    Quote Quote  
  19. raffriff: are you able to compile and execute this code? Could you add my checking code and test it for accuracy?
    Quote Quote  
  20. Originally Posted by chris319 View Post
    raffriff: are you able to compile and execute this code?
    The code snippet I posted will not compile. It is part of the AviSynth project with many unknown (to me) dependencies.

    I have never done it, but compiling the complete AviSynth project is doable with some work:
    http://avisynth.nl/index.php/Filter_SDK/Compile_AviSynth
    Quote Quote  
  21. It has this ScaledPixelClip() function which I don't understand, either.
    Last edited by chris319; 19th Feb 2017 at 01:59.
    Quote Quote  
  22. Oh, now I get it. (internal.h)
    Code:
    static __inline BYTE ScaledPixelClip(int i) {
      return PixelClip((i+32768) >> 16);
    }
    Okay, but what is PixelClip() doing?
    Code:
    class _PixelClip {
      enum { buffer=320 };
      BYTE clip[256+buffer*2];
    public:
      _PixelClip() {  
        memset(clip, 0, buffer);
        for (int i=0; i<256; ++i) clip[i+buffer] = (BYTE)i;
        memset(clip+buffer+256, 255, buffer);
      }
      BYTE operator()(int i) { return clip[i+buffer]; }
    };
    
    extern _PixelClip PixelClip;
    It looks to me like singleton object PixelClip contains an array of 256 BYTEs corresponding to as many ints:
    Code:
    for (int i=0; i<256; ++i) clip[i+buffer] = (BYTE)i;
    ...so calling
    Code:
    return PixelClip((i+32768) >> 16);
    in turn calls PixelClip's member function ()
    Code:
    BYTE operator()(int i) { return clip[i+buffer]; }
    And instead of simply doing something like the following 'normal' clipping/typecasting function:
    Code:
    static __inline BYTE PixelClip(int i) {
        return (i<=0) ? (BYTE)0 : (i>=255) ? (BYTE)255 : (BYTE)i;
    }
    They do all ...THAT? I don't get it. Is it supposed to be faster?

    I don't see any input validation either. Just access an array with a signed int!
    (EDIT there is a buffer of 320 bytes located in front of the array -- protection for out of range array access?)
    Last edited by raffriff42; 20th Feb 2017 at 09:32.
    Quote Quote  
  23. Alright, alright, I hadda do it
    Here's the Avisynth code, (using my PixelClip), called by your routine (ported to C++)
    Code:
    #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
    #include <windows.h>
    #include <limits.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /*
    https://forum.videohelp.com/threads/381298-RGB-to-YUV-to-RGB?p=2477791&viewfull=1#post2477791
    */
    static __inline BYTE PixelClip(int i) {
        return (i<=0) ? (BYTE)0 : (i>=255) ? (BYTE)255 : (BYTE)i;
    }
    
    /*
    http://avisynth2.cvs.sourceforge.net/viewvc/avisynth2/avisynth/src/internal.h?view=markup
    */
    
    static __inline BYTE ScaledPixelClip(int i) {
      return PixelClip((i+32768) >> 16);
    }
    
    /*
    http://avisynth2.cvs.sourceforge.net/viewvc/avisynth2/avisynth/src/convert/convert.h?view=markup
    */
    inline void YUV2RGB(int y, int u, int v, BYTE* out) 
    {
      const int crv = int(1.596*65536+0.5);
      const int cgv = int(0.813*65536+0.5);
      const int cgu = int(0.391*65536+0.5);
      const int cbu = int(2.018*65536+0.5);
    
      int scaled_y = (y - 16) * int((255.0/219.0)*65536+0.5);
    
      out[0] = ScaledPixelClip(scaled_y + (u-128) * cbu);                 // blue
      out[1] = ScaledPixelClip(scaled_y - (u-128) * cgu - (v-128) * cgv); // green
      out[2] = ScaledPixelClip(scaled_y                 + (v-128) * crv); // red
    }
    
    inline int RGB2YUV(int rgb) 
    {
      const int cyb = int(0.114*219/255*65536+0.5);
      const int cyg = int(0.587*219/255*65536+0.5);
      const int cyr = int(0.299*219/255*65536+0.5);
    
      // y can't overflow
      int y = (cyb*(rgb&255) + cyg*((rgb>>8)&255) + cyr*((rgb>>16)&255) + 0x108000) >> 16;
      int scaled_y = (y - 16) * int(255.0/219.0*65536+0.5);
      int b_y = ((rgb&255) << 16) - scaled_y;
      int u = ScaledPixelClip((b_y >> 10) * int(1/2.018*1024+0.5) + 0x800000);
      int r_y = (rgb & 0xFF0000) - scaled_y;
      int v = ScaledPixelClip((r_y >> 10) * int(1/1.596*1024+0.5) + 0x800000);
      return ((y*256+u)*256+v) | (rgb & 0xff000000);
    }
    
    
    /*
    https://forum.videohelp.com/threads/381298-RGB-to-YUV-to-RGB?p=2477697&viewfull=1#post2477697
    */
    
    BYTE Y,U,V, R1,G1,B1, Rd,Gd,Bd;
    LONG maxRedError,maxGreenerror,maxBlueError, count;
    
    void rgb_to_yuv() 
    {
    	int rgb = ((R1 & 0xff) << 16) + ((G1 & 0xff) << 8) + (B1 & 0xff);
    	int yuv = RGB2YUV(rgb);
    	Y = (yuv & 0xff0000) >> 16;
    	U = (yuv & 0xff00) >> 8;
    	V = (yuv & 0xff);
    }
    
    void yuv_to_rgb() 
    {
    	BYTE b[3];
    	YUV2RGB(Y, U, V, &b[0]);
    	Rd = b[2];
    	Gd = b[1];
    	Bd = b[0];
    }
    
    int main(int argc, char **argv) 
    {
    	maxRedError=0;
    	maxGreenerror=0;
    	maxBlueError=0;
    	count=0;
    
    	for (R1=1; R1<=254; R1++) 
    	{
    		for (G1=1; G1<=254; G1++) 
    		{
    			for (B1=1; B1<=254; B1++) 
    			{
    				rgb_to_yuv(); 
    				yuv_to_rgb(); 
    				count++;
    				
    				int t = abs(Rd-R1);
    				if (t > maxRedError)
    					maxRedError = t;
    				
    				t = abs(Gd-G1);
    				if (t > maxGreenerror)
    					maxGreenerror = t;
    
    				t = abs(Bd-B1);
    				if (t > maxBlueError)
    					maxBlueError = t;
    			}
    		}
    	}
    
    	printf("Colors tested: %d\n", count);
    	printf("Maximum red error:  %d\n", maxRedError);
    	printf("Maximum green error:  %d\n", maxGreenerror);
    	printf("Maximum blue error:  %d\n", maxBlueError);
    }
    
    // (end)
    Last edited by raffriff42; 16th Mar 2017 at 16:44. Reason: (fixed image link)
    Quote Quote  
  24. Note that raffriff42 program (and mine earlier) is using limited range YUV (commonly used in all consumer formats like DVD, Blu-ray, broadcast TV, etc.) whereas chris319's program is using full range YUV. Hence the larger errors in riffraff42's program.
    Quote Quote  
  25. Raffriff's error is only higher by 1 in the green channel (2 vs 1 in my code).

    Code:
    BYTE Y,U,V, R1,G1,B1, Rd,Gd,Bd;
    
    BYTE b[3];
    My code uses unsigned bytes. This would be hard to catch unless you've worked with PureBasic before. The ".a" declaration signifies an unsigned byte. What happens with your code if you make those bytes unsigned?

    Mine was never intended to be production code -- just testing.
    Last edited by chris319; 19th Feb 2017 at 16:26.
    Quote Quote  
  26. I am having no luck with the YCgCo code described here: https://www.microsoft.com/en-us/research/publication/ycocg-r-a-color-space-with-rgb-re...dynamic-range/

    Do either of you spot a problem with my encoding/decoding? I am able to get a passable monochrome image from it.

    Code:
    sub rgb_to_yuv ()
    Rf = R1 / 255
    Gf = G1 / 255
    Bf = B1 / 255
    
    Cof = Rf - Bf
    Cgf = Gf - (Rf + Bf) / 2 'Gf - 1
    Yf = Gf/2 + (Rf + Bf) / 4
    end sub
    
    sub yuv_to_rgb()
    dim as single temp
    Rf = Yf + Cof - Cgf
    Gf = Yf + Cgf
    Bf = Yf - Cof - Cgf
    Rd = Rf * 255
    Gd = Gf * 255
    Bd = Bf * 255
    Quote Quote  
  27. Originally Posted by chris319 View Post
    What happens with your code if you make those bytes unsigned?
    It works exactly the same. I didn't show the code but I verified the R, G, B, Y, U, and V primaries never exceeded their expected ranges. I used "int" because the original code I was working with could optionally test different bit depths. 8 bit, 9 bit, and 10 bit. Hence it needed to use a bigger type than "unsigned char".
    Quote Quote  
  28. My question was directed at raffriff
    Quote Quote  



Similar Threads

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