PDA

View Full Version : RGB to Propeller color code conversion routine with LUT

digimorf
03-27-2012, 06:49 PM
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!

91061

Rayman
03-27-2012, 08:24 PM
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...

digimorf
03-27-2012, 09:19 PM
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... :)

Rayman
03-27-2012, 09:31 PM
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...

digimorf
03-28-2012, 09:46 AM
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! ;)

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)
03-28-2012, 03:36 PM
There are algos out there for converting RGB to HSV. Here's one, for example:

http://snipplr.com/view/48771/rgb2hsv/

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

digimorf
03-28-2012, 03:58 PM
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:
http://snipplr.com/view/48771/rgb2hsv/

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)
03-28-2012, 04:31 PM
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).

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

ericball
03-29-2012, 10:19 PM
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.

ericball
03-30-2012, 02:28 PM
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 )

Perry
03-30-2012, 05:33 PM
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_Acula
03-31-2012, 04:30 AM
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.

digimorf
04-02-2012, 09:35 PM
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.