Modify Hue on an image

BlitzMax Forums/BlitzMax Programming/Modify Hue on an image

kronholm(Posted 2007) [#1]
Is is possible somehow?

Reason I'm asking is I have 1 player image with animation. This player is blue per default, and has white eyes. I wish to have him appear red in the game. Thus I cannot use setcolor before I draw the player to make him red, because his eyes would become reddish as well (and it would be best if he was grayscale to begin with I guess).

But if I was able to modify the hue value, I could easily make him red, purple, green, whatever, without the white eyes changing.


Gabriel(Posted 2007) [#2]
There's some code ( not BMax, but should be easy enough to follow ) here to convert back and forth between RGB and HSV, so I guess you could convert to HSV, change the hue and convert back to RGB?

http://www.cs.rit.edu/~ncs/color/t_convert.html


Beaker(Posted 2007) [#3]
Or you could draw the eyes on afterwards.


kronholm(Posted 2007) [#4]
I'd rather start the game out by converting/calculating them, rather than having to issue two draw commands instead of one. Thanks gabriel, looking into it now!


Derron(Posted 2007) [#5]
Just make the parts of the image grey which have to be colorized...
If the eyes are totally white (255,255,255) and the pupils black (0,0,0) there will be no problems... or just make them like (255,255,250) - so you will not see a difference to perfect white.

The function below will colorize all grey parts to the given rgb-values. You can easily change it to color pixmaps and so on.

Function ARGB_Alpha:Int(ARGB:Int)
 Return Int((ARGB & $FF000000:Int) / $1000000:Int)
End Function

Function ARGB_Red:Int(ARGB:Int)
 Return Int((ARGB & $00FF0000:Int) / $10000:Int)
End Function

Function ARGB_Green:Int(ARGB:Int)
 Return Int((ARGB & $0000FF00:Int) / $100:Int)
End Function

Function ARGB_Blue:Int(ARGB:Int)
 Return (ARGB & $000000FF:Int)
End Function

Function ARGB_Color:Int(alpha:Int,red:Int,green:Int,blue:Int)
 Return (Int(alpha*$1000000)+Int(blue*$10000)+Int(green*$100)+Int(RED))
End Function 
             
'returns true if the given pixel is monochrome (grey)						   
Function isMonochrome(argb:Int)
Try
	Local alpha:Int = ARGB_Alpha(argb)
	Local red:Int = ARGB_Red(argb)
	Local green:Int = ARGB_Green(argb)
	Local blue:Int = ARGB_Blue(argb)
	                                                        '250
	If (red = green) And (red = blue)And(alpha <> 0)And(red<250) Then Return green
	Return 0
Catch a$
	Print "ismonochrome exception:"+a$
EndTry

End Function
			   
'colorizes an TImage (may be an AnimImage when given cell_width and height)
Function ColorizeTImage:TImage(_image:TImage, r:Int,g:Int,b:Int, cell_width:Int=0,cell_height:Int=0,first_cell:Int=0,cell_count:Int=1, flag:Int=0)
If _image <> Null
  Local mypixmap:TPixmap=LockImage(_image)
  Local mypixmap2:TPixmap = TPixmap.Create(_image.width,_image.height, mypixmap.format,1)
  mypixmap2 = mypixmap.Copy()
  Local mypixelptr2:Int Ptr = Int Ptr(mypixmap2.PixelPtr(0,0))
  Local mypixelptr2backup:Int Ptr = mypixelptr2
  For Local my_x=0 To ((mypixmap2.width)*(mypixmap2.height))
 '   If Mypixelptr2[0] = Null Then Exit
     Local colortone:Int = isMonochrome(mypixelptr2[0])
     If colortone > 0 And mypixelptr2[0] <> 0          
       mypixelptr2[0] = ARGB_Color(ARGB_Alpha(mypixelptr2[0]),Int(colortone*r/255), Int(colortone*g/255), Int(colortone*b/255))
     EndIf
     mypixelptr2:+1
     If mypixelptr2 = mypixelptr2backup+(mypixmap2.pitch Shr 2)
         mypixelptr2backup=mypixelptr2
     EndIf
  Next        
  UnlockImage(_image)
  If cell_width > 0 And cell_count > 0 Then Return LoadAnimImage(mypixmap2, cell_width, cell_height, first_cell,cell_count, flag)
  Return LoadImage(mypixmap2)
EndIf
End Function



Hope this works for you.

bye
mB


Ghost Dancer(Posted 2007) [#6]
I wrote an HSV library a few years back which should you to do what you want. However, it was for Blitz Plus, but it should be easy to convert. Have a look on my old website, it may be of some use to you:

[url]http://www.aurora-soft.co.uk/blitz/colourSpace.asp[/url]


kronholm(Posted 2007) [#7]
Hey ghost dancer, thanks man, it's exactly what I need. Now only if I could get it working ;). I'm not too sharp on BlitzPlus (never used it), but I tried to convert it to bmax anyway. It runs but gives a weird problem, noted in my comments in the first few lines.

I think I'm either calling your functions incorrectly, or I misunderstood something about the temp pointer in rgb2hsv(). I'm still trying to learn about pointers, so it's pretty exotic for me :p

Can you see what I did wrong you think?

Strict


Local myrgb = rgb(100,150,200)
Print "Red color of 'rgb' should be 100, it is: "+getred(myrgb)		'prints out: 255
Local myhsv:hsv = New hsv
myhsv = rgb2hsv(myrgb,myhsv)
Print "HSV values from myrgb: "+myhsv.h+" "+myhsv.s+" "+myhsv.v		'prints out: -1.#IND0000 0.000000000 1.00000000
Local newrgb = hsv2rgb(myhsv.h,myhsv.s,myhsv.v)
Print "myrgb converted to hsv, and back to rgb: "+newrgb			'prints out: 16777215



'-----------begin ghost dancer's code, converted to bmax-------------------

'create a custom Type For HSV colour
Type hsv
	Field h#, s#, v#
End Type



'-------------------------------------------------------------------
Function getRed(rgb)
'-------------------------------------------------------------------
'Get red component of RGB colour value
'
'Parameters:
'rgb	- RGB value that you want the red component from
'
'Return value:
'red value (0 To 255)
'-------------------------------------------------------------------
	rgb = rgb Shr 16 And $ff
	Return rgb' Shr 16 And $ff
End Function


'-------------------------------------------------------------------
Function getGreen(rgb)
'-------------------------------------------------------------------
'Get green component of RGB colour value
'
'Parameters:
'rgb	- RGB value that you want the green component from
'
';Return value:
'green value (0 To 255)
'-------------------------------------------------------------------

	Return (rgb Shr 8) And $ff
End Function


'-------------------------------------------------------------------
Function getBlue(rgb)
'-------------------------------------------------------------------
'Get blue component of rgb colour value
'
'Parameters:
'rgb	- RGB value that you want the blue component from
'
'Return value:
'blue value (0 To 255)
'-------------------------------------------------------------------
	Return rgb And $ff
End Function


'-------------------------------------------------------------------
Function rgb(r, g, b)
'-------------------------------------------------------------------
'Convert R,G,B components To single RGB value
'
'Parameters:
'r	- red value
'g	- green value
'b	- blue value
'
'Return value:
'rgb value
'-------------------------------------------------------------------

	Return (r Shl 16) + (g Shl 8) + b
End Function


'-------------------------------------------------------------------
Function rgb2hsv:hsv(rgb, temphsv:hsv)
'-------------------------------------------------------------------
'Convert RGB To HSV
'
'Parameters:
'rgb			- RGB value that you want convert
'temphsv.hsv	- Added in V2.01, it is required To prevent the
'				  memory leakage problem. This needs To be the same
'				  hsv Type pointer that the Function returns to.
'				  e.g.	myhsv.hsv = New hsv
'				  		myhsv = rgb2hsv($ff0000, myhsv)
'
'Return value:
'custom hsv Type defined in this library
'-------------------------------------------------------------------

'	;RGB components in  range 0 To 1
	Local r# = getRed(rgb) / 255.0
	Local g# = getGreen(rgb) / 255.0
	Local b# = getBlue(rgb) / 255.0

'	;Min value	
	Local minVal#
	If r < g Then minVal# = r Else minVal# = g
	If b < minVal Then minVal = b

'	;Max value
	Local maxVal#		
	If r > g Then maxVal# = r Else maxVal# = g
	If b > maxVal Then maxVal = b

'	;calculate difference
	Local diff# = maxVal - minVal
	
	temphsv.v = maxVal
	
	If maxVal = 0 Then
		temphsv.s = 0
		temphsv.h = -1		';h is undefined
	Else
		temphsv.s = diff / maxVal
	
		If r = maxVal Then
			temphsv.h = (g - b) / diff
		ElseIf g = maxVal Then
			temphsv.h = 2 + (b - r) / diff
		Else
			temphsv.h = 4 + (r - g) / diff
		EndIf
	
		temphsv.h = temphsv.h * 60
		If temphsv.h < 0 Then temphsv.h = temphsv.h + 360
	EndIf

	Return temphsv
End Function


';-------------------------------------------------------------------
Function hsv2rgb(h#, s#, v#)
'-------------------------------------------------------------------
'Convert HSV To RGB
'
'Parameters:
'h#	- hue (in degrees, 0 To 350)
's#	- saturation (0.0 To 1.0)
'v#	- value (brightness, 0.0 To 1.0)
'
'Return value:
'rgb value
'-------------------------------------------------------------------
	Local r#,g#,b#,f#,p#,q#,t#,i#

	If s = 0 Then
'		;saturation of 0 = grey
		r# = v ; g# = v ; b# = v
	Else
		h = h / 60
		i = Floor(h)
		f# = h - i
		p# = v * (1 - s)
		q# = v * (1 - s * f)
		t# = v * (1 - s * (1 - f))

		Select i
		Case 0
			r# = v
			g# = t
			b# = p
		Case 1
			r# = q
			g# = v
			b# = p
		Case 2
			r# = p
			g# = v
			b# = t
		Case 3
			r# = p
			g# = q
			b# = v
		Case 4
			r# = t
			g# = p
			b# = v
		Default
			r# = v
			g# = p
			b# = q
		End Select		
	EndIf

	r = r * 255
	g = g * 255
	b = b * 255

	Return rgb(r, g, b)
End Function 



Ghost Dancer(Posted 2007) [#8]
I'm guessing it's the bit shifting since Blitz Plus didn't have alpha. Can't really test it at the moment but I'll try and have a proper look later for you.


kronholm(Posted 2007) [#9]
Thank you very much for taking the time Ghost Dancer, I really appreciate it :)


Ghost Dancer(Posted 2007) [#10]
OK, you were using And instead of &. So, this is what it should look like:

'-------------------------------------------------------------------
Function getRed(rgb)
'-------------------------------------------------------------------
'Get red component of RGB colour value
'
'Parameters:
'rgb	- RGB value that you want the red component from
'
'Return value:
'red value (0 To 255)
'-------------------------------------------------------------------
	Return rgb Shr 16 & $ff
End Function


'-------------------------------------------------------------------
Function getGreen(rgb)
'-------------------------------------------------------------------
'Get green component of RGB colour value
'
'Parameters:
'rgb	- RGB value that you want the green component from
'
';Return value:
'green value (0 To 255)
'-------------------------------------------------------------------

	Return (rgb Shr 8) & $ff
End Function


'-------------------------------------------------------------------
Function getBlue(rgb)
'-------------------------------------------------------------------
'Get blue component of rgb colour value
'
'Parameters:
'rgb	- RGB value that you want the blue component from
'
'Return value:
'blue value (0 To 255)
'-------------------------------------------------------------------
	Return rgb & $ff
End Function



kronholm(Posted 2007) [#11]
Thank you!

I got it working now, sort of. I merged some of your code with Michael's code to rebuild an image, while changing the hue value inbetween. However I can't seem to understand how the "formula" is for blitzmax's pixmaps are.. They have alpha, and your code, ghost dancer, does not take that in account.. I'll have to know the formula to be able to rewrite it so it does :)


Ghost Dancer(Posted 2007) [#12]
I think my code will basically ignore any alpha information, so if you don't need the alpha it shouldn't be a problem.

If you do need the alpha info, then that would have to be accounted for in the rgb <-> hsv conversions. Off the top of my head, I'm guessing it would just alter the saturation and/or the value (brightness) in the hsv model.


kronholm(Posted 2007) [#13]
Nah I just get a black image if I don't account for alpha >.< No idea what the formula is so I can include alpha in your rbg2hsv and hsv2rgb?


Derron(Posted 2007) [#14]
If you read the pixel-color first... then just read its alpha-value and set again after your hsv-conversion.



bye
MB


kronholm(Posted 2007) [#15]
Is this what you mean michael? I took out argb values in seperate variables from the pixmap, then converted rgb to hsv and modified hue, then converted back to rgb and added alpha again. It did change the color, but the eyes (about 200 to 255 rgb..white) would somehow end up being black ?


Derron(Posted 2007) [#16]
When some pixels went black, you forgot to read their intensity... if you modify the hue, the saturation has to be constant (the intensity of the color) - otherwise it would get black.

if you use:
read pixel
read alpha
convert rgb 2 hsv
modify hue
convert hsv 2 rgb
reset alpha
write pixel

it would be a nice overhead, but would have to function I think.


But if you just use my functions, you would not have to change the hue, for just being able to colorize grey parts... everything with r=g=b could be colorized and to leave "grey looking parts" you will have to change the parts color to eg. r-4,g,b (so it is not grey any longer).


bye
MB