Shop OBEX P1 Docs P2 Docs Learn Events
RGB to Propeller color code conversion routine with LUT — Parallax Forums

RGB to Propeller color code conversion routine with LUT

digimorfdigimorf Posts: 74
edited 2012-04-02 14:35 in Propeller 1
Hi to everybody.
I was working on some graphic demos, and I wanted to try to build a converter from RGB to Propeller color code without using complex math.

I started from the color table of Hydra book, and checked the corresponding rgb value steps along the Hue value.
I saw there are common colors such as blue, magenta, red etc... that correspond to limit rgb vales:

R0 G0 B255, for Blue
R255 G0 B255, for Magenta
R255 G0 B0, for Red
...

These have to correspond to

Hue 0
Hue 3
Hue 5

So I've just calculated the inbetween steps and put into a lookup table.
To get luma I've considered that the color get bright when all components tend to reach 255, and black when they tend to 0
So I have calculated a middle value between each component and remapped to 6 luminosity values.

Maybe it's not so accurate right now, this is a first attempt so it will need to be fixed for sure, but maybe it's a good starting point to develop a fast conversion routine.

In attachment the functions involved, and the VERY basic use of this routine for you to try in your application :) .This means no graphic but data results on Propeller serial terminal :P

Enjoy!

color_rgb2prop_001 - Archive [Date 2012.03.27 Time 20.31].zip

Comments

  • RaymanRayman Posts: 14,844
    edited 2012-03-27 13:24
    digimorf, Is this to convert from a 8-bit Propeller TV driver color to a 24-bit RGB color?
    If so, this is an interesting approach.
    I'm using a 256 long look-up table to do this with values obtained from a screen capture.
    But, maybe this is a better way...
  • digimorfdigimorf Posts: 74
    edited 2012-03-27 14:19
    Rayman wrote: »
    digimorf, Is this to convert from a 8-bit Propeller TV driver color to a 24-bit RGB color?
    If so, this is an interesting approach.
    I'm using a 256 long look-up table to do this with values obtained from a screen capture.
    But, maybe this is a better way...

    I thought to use this to convert from rgb to propeller color format

    so, from R0-255, G0-255, B0-255 I can have Hue << 4 | 8 | luma.

    But with some work I think that the inverse way can be obtained... :)
  • RaymanRayman Posts: 14,844
    edited 2012-03-27 14:31
    Ok, that is useful too... For my "TV Bitmap Viewer", I used that same table and then did a closest match search to find the value.
    But, maybe your way could be better and faster...
  • digimorfdigimorf Posts: 74
    edited 2012-03-28 02:46
    Rayman wrote: »
    Ok, that is useful too... For my "TV Bitmap Viewer", I used that same table and then did a closest match search to find the value.

    Oh really ? Great! ;)
    Rayman wrote: »
    But, maybe your way could be better and faster...

    For now spin version is just for experiments and tests, but my intention is to optimize this into ASM. Of course the use of Lookup tablescan speed up things, especially for frame buffer based graphics.

    I think about real-time dithering, color cycling effects, overlaying etc...

    I really hope to achieve a smart and fast way to convert RGB to Hue/luma.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-03-28 08:36
    There are algos out there for converting RGB to HSV. Here's one, for example:

    Once you've got the hue (H), it can be scaled to go from 0-15 and rotated mod 16 to align with the Prop's colorburst offset. Saturation (S) needs to be either of three values in the Prop, so a threshold will work there. (For the high saturation setting, the colorburst offset has to be rotated yet again by 8.) And value (V) can be rescaled to cover the Prop's intensity gamut.

    The only question, in my mind, is whether Hue and the colorburst offset are linear w.r.t. each other. If not, you will have to convert RGB to YIQ first and use an arctan on the I and Q coordinates to get the colorburst offset.

    -Phil
  • digimorfdigimorf Posts: 74
    edited 2012-03-28 08:58
    There are algos out there for converting RGB to HSV.

    Yes I saw, but many are quite different each other, they use different way to obtain H, S, V
    Here's one, for example:

    This can be useful, thanks.
    Once you've got the hue (H), it can be scaled to go from 0-15 and rotated mod 16 to align with the Prop's colorburst offset. Saturation (S) needs to be either of three values in the Prop, so a threshold will work there. (For the high saturation setting, the colorburst offset has to be rotated yet again by 8.) And value (V) can be rescaled to cover the Prop's intensity gamut.

    I was trying to figure out how the saturation is involved in Propeller single color
    The only question, in my mind, is whether Hue and the colorburst offset are linear w.r.t. each other. If not, you will have to convert RGB to YIQ first and use an arctan on the I and Q coordinates to get the colorburst offset.

    -Phil

    Uhm, I wanted to find some way to not use trigonometric functions, that's why I've thought about a lookup table.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-03-28 09:31
    digimorf wrote:
    I was trying to figure out how the saturation is involved in Propeller single color
    The Propeller video circuitry gives you three choices
    • Zero saturation for the handful of gray values.
    • Low saturation, where the chroma amplitude is one.
    • Supersaturation, which occurs at maximum intensity, where the chroma causes the intensity to overflow and roll over zero (sync level).
    digimorf wrote:
    Uhm, I wanted to find some way to not use trigonometric functions, ...
    It's unlikely that you'd have to resort to this, since I will bet that there's a one-to-one mapping between hue and chroma phase.

    -Phil
  • ericballericball Posts: 774
    edited 2012-03-29 15:19
    Hmm.. thinking about this, ignoring clipping, the Parallax color gamut can be thought of as two elliptical cylinders around the line from black to white.
    ' first calculate YUV from 24 bit RGB
    Y = 0.299 * R / 255 + 0.587 * G / 255 + 0.114 * B / 255
    U = 0.492111 * ( B / 255 - Y )
    V = 0.877283 * ( R / 255 - Y )
    ' next calculate the color saturation
    S = sqrt( U * U + V * V )
    ' finally determine the color code
    if S < 0.1  ' greys only
      C = round( Y * 5 + 2 )
    elseif S < 0.5  ' low saturation
      C = round( arctan( U, V ) / (2*pi) * 16 ) << 4 + $08 + round( Y * 5 + 2 )
    else
      if Y < 0.4  ' two high saturations
        C = round( arctan( -U, -V ) / (2*pi) * 16 ) << 4 + $0F
      else
        C = round( arctan( -U, -V ) / (2*pi) * 16 ) << 4 + $08
    
    Note: although the math is correct, the heuristics may not be correct for all RGB values.

    Also, don't despair this is going to require floating point, square root and arctangent. You are mapping a 24 bit color space down to less than 7 bits. The intent is to get reasonably close for the majority of the values. There are some colors the Propeller simply is not going to be able to create. So it may be sufficient to approximate. IIRC sqrt( U*U + V*V ) can be approximated as max(abs(U),abs(V)) + min(abs(U),abs(V))/2 and for the arctan you are simply deciding which of the 16 hues will be used.
  • ericballericball Posts: 774
    edited 2012-03-30 07:28
    Approximation attempt #1:
    Y = 3 * R + 6 * G + B
    U = ( 10 * B - Y ) << 2
    V = 7 * ( 10 * R - Y )
    S = max(abs(U),abs(V)) + min(abs(U),abs(V))>>1
    if S < 2048  ' greys only
      C = ( Y + 1280 ) >> 9
    elseif S < 10,240  ' low saturation
      C = hue( U, V )<< 4 + $08 + ( Y + 1280 ) >> 9
    else
      if Y < 1024  ' two high saturations
        C = hue( -U, -V )<< 4 + $0F
      else
        C = hue( -U, -V )<< 4 + $08
    
    where hue(x,y) is an approximation of round( arctan(x, y) / (2*pi) * 16 )
    
  • PerryPerry Posts: 253
    edited 2012-03-30 10:33
    ericball wrote: »
    Approximation attempt #1:
    where hue(x,y) is an approximation of round( arctan( -U, -V ) / (2*pi) * 16 )
    

    I have been working with Phil's color capture code quite extensively to analize the color I/Q and produce NTSC outouts
    I am using 32 instead of 16 in the table to get better approximation to 33 degree rotation !

    Perry
    CON
      iq_side  = 32 ' 32 '64  '  size of angle to hue look up table
      iq_shift = 5 ' 5 '  6   ' 
    
    VAR
      byte  hues[iq_side*iq_side]
    
    PUB hue_main
    
    ' code is used like this to get NTSC hue approximation 
    
          make_hues ' build table
    
          is := ((( chromai * burstq - chromaq * bursti)~> 6 ) + constant(iq_side/2))   #> 0 <# constant(iq_side-1)     '  fit hue angle to hues table
          qs := (((-chromaq * burstq - chromai * bursti)~> 6 ) + constant(iq_side/2))   #> 0 <# constant(iq_side-1)
          hue := hues[is + (qs << iq_shift )] ~> 1 '&$F
    
    PUB make_hues  | ib,qb,hue
      '  code to build hue table. 
    
      fm.start
    
      repeat ib from 0 to constant(iq_side-1)     
    '    pst.char(13)
    '    pst.str(string("Byte "))
        repeat qb from 0 to constant(iq_side-1)     
          hue := ((fm.FRound(fm.Fmul(fm.ATan2(fm.FFloat(ib-constant(iq_side/2)), fm.FFloat(qb-constant(iq_side/2))),572.9578))+1800)/113)' &$1F'113 '235 '225
    '      if qb<>0
    '        pst.char(",")
          byte[@hues+ib + qb<<iq_shift ] := ((hue+3)//32) 
    '      pst.dec(byte[@hues+ib + qb<<iq_shift ])
    '      if ||(ib- constant(iq_side/2))<1
    '        if ||(qb- constant(iq_side/2))<1
    '           hues[ib + qb<<iq_shift ] := $30
    '      saturation := ^^((ib-constant(iq_side/2))*(ib-constant(iq_side/2))+(qb-constant(iq_side/2))*(qb-constant(iq_side/2)))
    '      plot (ib -constant(iq_side/2) + constant(biWidth/2), -qb +constant(iq_side/2) + constant(biHeight/2), hues[ib + qb<<iq_shift ]<<11 | $0800 |  (saturation<<3 #> 1 <# 157) )
    '      plot ( ib -constant(iq_side/2) + constant(biWidth/2), -qb +constant(iq_side/2) + constant(biHeight/2),hues[ib + qb<<iq_shift ],(saturation~>1) )
    '  hues[constant((iq_side/2))+constant(iq_side/2)<<iq_shift] := $30 
    '
      fm.stop
    
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-03-30 21:30
    I've found a lot of the mathematical formulas don't work very well. In particular, there are a whole lot of light yellows that the propeller does not display. The closest yellow is a dark gold. Attached is a palette that was matched manually with a TV display. It is quite different to the original mathematical one based on even steps with HSL.

    Down the bottom are some pictures using various algorithms. You can see the difference between the 'closest color' matches vs Floyd Steinberg. If you give the FS algorithm the actual palette it can work out the lighter yellows by, for example, dithering a dark yellow and a white pixel alternately.

    There are also a large number of colors the propeller cannot display, eg all the grays with a little bit of color added. Again, dithering is a good workaround for this.

    Sing out if you would like this vb.net code.
    975 x 645 - 116K
    Pics.jpg 116.3K
  • digimorfdigimorf Posts: 74
    edited 2012-04-02 14:35
    Dr_Acula wrote: »
    I've found a lot of the mathematical formulas don't work very well. In particular, there are a whole lot of light yellows that the propeller does not display. The closest yellow is a dark gold. Attached is a palette that was matched manually with a TV display. It is quite different to the original mathematical one based on even steps with HSL.

    Right, looking at a propeller palette, the way saturation is treated in a different way and luma can't be managed as many formulas says.

    For example searching the web for formulas, I've found one where the luma is equal to the maximum value between R, G, B, but:

    For example, If I have R=255, G=0, B=0 ( a full saturated Red ) in Propeller I should have something like $56 or $57 ( Hue = 5, Luma = max ) , but if you see this color on a tv driver it is a light red instead.

    That's why in my example I've took a medium value between the max and the min value in the RGB given range.

    Of course the approach depends on the application where the color conversion has to be used, personally I'm working on a different approach without complex formulas and extra cogs to convert a value.
Sign In or Register to comment.