Simplification of the colors of a picture

I made my own WebSafe one. Seems to work.

Private Function NearestWebSafe(oneColorValue As Integer) As Integer
  
  Var near As Integer = oneColorValue \ 51
  If oneColorValue mod 51 > 25 Then near = near + 1 // the nearest is the next one above
  Return near * 51
  
End Function


Private Function WebSafeColor(c As Color) As Color
  
  Return Color.RGB(NearestWebSafe(c.Red), NearestWebSafe(c.Green), NearestWebSafe(c.Blue), c.Alpha)
  
End Function

A complete WebSafe Module with indexed palette handling

#tag Module
Protected Module WebSafeColors
	#tag Method, Flags = &h0
		Function GetWebSafeColor(idx As Integer) As Color
		  If not WebSafeTablesInited Then InitWebSafeTables()
		  If idx < 0 Then idx = 0
		  If idx > 215 Then idx = 215
		  Return WebSafeIndexToColorTable(idx)
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function GetWebSafeIndex(c As Color) As Integer

		  If not WebSafeTablesInited Then InitWebSafeTables()
		  Return WebSafeColorToIndexTable.Value(WebSafeColor(c.Red, c.Green, c.Blue, 0))
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h21
		Private Sub InitWebSafeTables()

		  WebSafeTablesInited = True
		  WebSafeColorToIndexTable = New Dictionary
		  For r As Integer = 0 to 255 step 51
		    For g As Integer = 0 to 255 step 51
		      For b As Integer = 0 to 255 step 51
		        Var aColor As Color = Color.RGB(r,g,b,0)
		        WebSafeIndexToColorTable.Add aColor
		        WebSafeColorToIndexTable.Value(aColor) = WebSafeIndexToColorTable.LastIndex
		      Next
		    Next
		  Next

		End Sub
	#tag EndMethod

	#tag Method, Flags = &h21
		Private Function NearestWebSafe(oneColorValue As Integer) As Integer
		  
		  Var near As Integer = oneColorValue \ 51
		  If oneColorValue mod 51 > 25 Then near = near + 1 // the nearest is the next one above
		  Return near * 51
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function WebSafeColor(c As Color) As Color
		  
		  Return Color.RGB(NearestWebSafe(c.Red), NearestWebSafe(c.Green), NearestWebSafe(c.Blue), c.Alpha)
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function WebSafeColor(r As Integer, g As Integer, b As Integer, a As Integer = 0) As Color
		  
		  Return Color.RGB(NearestWebSafe(r),NearestWebSafe(g),NearestWebSafe(b), a)
		  
		End Function
	#tag EndMethod


	#tag Property, Flags = &h21
		Private WebSafeColorToIndexTable As Dictionary
	#tag EndProperty

	#tag Property, Flags = &h21
		Private WebSafeIndexToColorTable() As Variant
	#tag EndProperty

	#tag Property, Flags = &h21
		Private WebSafeTablesInited As Boolean = False
	#tag EndProperty


	#tag ViewBehavior
		#tag ViewProperty
			Name="Name"
			Visible=true
			Group="ID"
			InitialValue=""
			Type="String"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Index"
			Visible=true
			Group="ID"
			InitialValue="-2147483648"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Super"
			Visible=true
			Group="ID"
			InitialValue=""
			Type="String"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Left"
			Visible=true
			Group="Position"
			InitialValue="0"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Top"
			Visible=true
			Group="Position"
			InitialValue="0"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
	#tag EndViewBehavior
End Module
#tag EndModule

This morning I revisited the problem, and simplified it to pure math instead of precalculated tables

#tag Module
Protected Module WebSafeColors
	#tag Method, Flags = &h21
		Private Function NearestWebSafe(oneColorValue As Integer) As Integer
		  
		  Var near As Integer = oneColorValue \ 51
		  If oneColorValue mod 51 > 25 Then near = near + 1 // the nearest is the next one above
		  Return near * 51
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function WebSafeColor(c As Color) As Color
		  
		  Return Color.RGB(NearestWebSafe(c.Red), NearestWebSafe(c.Green), NearestWebSafe(c.Blue), c.Alpha)
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function WebSafeColor(idx As Integer) As Color

		  Var r,g,b As Integer
		  
		  If idx < 0 Then idx = 0
		  If idx > 215 Then idx = 215
		  
		  // Find a WebSafe RGB based on its index and an imaginary color distribution
		  b = idx mod 6 * 51
		  idx = idx \ 6
		  g = idx mod 6 * 51
		  r = idx \ 6 * 51
		  
		  Return Color.RGB(r, g, b)
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function WebSafeColor(r As Integer, g As Integer, b As Integer, a As Integer = 0) As Color
		  
		  Return Color.RGB(NearestWebSafe(r),NearestWebSafe(g),NearestWebSafe(b), a)
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function WebSafeColorIndex(c As Color) As Integer

		  // Calculate a virtual index for a Web Safe color (Palette of 216 colors)
		  Return NearestWebSafe(c.Red) \ 51 * 36 + NearestWebSafe(c.Green) \ 51 * 6 + NearestWebSafe(c.Blue) \ 51
		  
		End Function
	#tag EndMethod


	#tag ViewBehavior
		#tag ViewProperty
			Name="Name"
			Visible=true
			Group="ID"
			InitialValue=""
			Type="String"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Index"
			Visible=true
			Group="ID"
			InitialValue="-2147483648"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Super"
			Visible=true
			Group="ID"
			InitialValue=""
			Type="String"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Left"
			Visible=true
			Group="Position"
			InitialValue="0"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Top"
			Visible=true
			Group="Position"
			InitialValue="0"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
	#tag EndViewBehavior
End Module
#tag EndModule

Thank you Rick,
I will try that.
I am not familiar with this kind of representations like for example #tag Method, Flags = &h0 .
What is their meaning?

No idea of the meaning but Xojo inserts such annotations for their use, probably to supply missing features/infos that could be expressed/inferred entirely using proper readable idiomatic content from the language.

I wrote the module, exported it as text, and pasted above as Xojo wants.

Just copy and paste that code into a text file named websafecolors.xojo_code and save it.
Then create a desktop app and drag such file to its navigator. Say yes/ok to the “import thing”. A module with the proper methods will show up. :wink:

What I think I need to represent what I intended idiomatically could be only something like:

Public Module WebSafeColors

		// Find nearest web safe value for a channel
		Private Function NearestWebSafe(oneColorValue As Integer) As Integer
		  
		  Var near As Integer = oneColorValue \ 51
		  If oneColorValue mod 51 > 25 Then near = near + 1 // the nearest is the next one above
		  Return near * 51
		  
		End Function

		// receive a color, return the nearest web safe of it
		Function WebSafeColor(c As Color) As Color
		  
		  Return Color.RGB(NearestWebSafe(c.Red), NearestWebSafe(c.Green), NearestWebSafe(c.Blue), c.Alpha)
		  
		End Function

		// Receive an index (0 to 215) and return the equivalent web safe color
		Function WebSafeColor(idx As Integer) As Color

		  Var r,g,b As Integer
		  
		  If idx < 0 Then idx = 0
		  If idx > 215 Then idx = 215
		  
		  // Find a WebSafe RGB based on its index and a virtual web safe color distribution
		  b = idx mod 6 * 51
		  idx = idx \ 6
		  g = idx mod 6 * 51
		  r = idx \ 6 * 51
		  
		  Return Color.RGB(r, g, b)
		  
		End Function

		// compose a web safe color passing each RGB channel value
		Function WebSafeColor(r As Integer, g As Integer, b As Integer, a As Integer = 0) As Color
		  
		  Return Color.RGB(NearestWebSafe(r),NearestWebSafe(g),NearestWebSafe(b), a)
		  
		End Function

		// Receive a color and return the one byte value (0to 215) equivalent 
		Function WebSafeColorIndex(c As Color) As Integer

		  // Calculate a virtual index for a Web Safe color (Palette of 216 colors)
		  Return NearestWebSafe(c.Red) \ 51 * 36 + NearestWebSafe(c.Green) \ 51 * 6 + NearestWebSafe(c.Blue) \ 51
		  
		End Function

End Module

But this way Xojo probably will complain about something.

Excuse me, I was already wrong in the value of the sky blue color (192,255,255) but it is also true for the light yellow.
Rick, unless I’m mistaken if you take the color (242,255,255) you get white, right?

You have an explanation about RGB colors there:

If you want to experiment colors and run macOS, fire TextEdit, then Cmd-C and you will get the Color selector. Then you can play with the values and see the colors.

Same can be done with GIMP.

On Windows, I suppose you can get the “same” Color selector in Paint (the bitmap one, I do not know for Paint3D; must have it too…)

Yep. The nearest color of the WebSafe 216 colors palette to this almost white blue is white.

image

image

Émile,
My problem is not to understand the colors, I have all the possibilities that I could discover, RGB, HSV, XYZ, CMYK and the distances between all these colors but my problem is to be able to simplify the colors compared to a palette when the colors are very light.

If you want to map to the specific web safe colors, then you will always lose some light colors to White.
A routine that simplifies the colors this way would represent the pale blue as a dithered area of a darker blue, plus white

If you just want fewer colors , that would be a different problem.
What exactly do you want to achieve?

Jeff,
If you look at my second image with a list of sky blue colors, I just want to simplify those colors to RGB(192, 255,255) and not white as they are closer to white than sky blue.

So you want a (weird to me) personalized palette full of very specific non-linear exceptions causing more distortions than correctness. You need to create your own algorithm with such exceptions and adding the desired color to the palette (192,255,255, that is not Web Safe) and map all those desired exception ranges to such color.
That’s is not generally useful, but things like that are kind of used for image filters, probably such filter will add a blueish bias to the image.

I’m not sure which image you mean.
Are you always working with the same image?

If the source images always have the same set of distinct RGB values, you can easily set up a lookup table using a database or dictionary.

mylookups.value(RGB(192, 205,241) ) = RGB(192, 255,255)

and then recover them afterwards.

Again: what do you want to achieve?

Why do I keep asking that?

Consider this:
Someone keeps asking ‘how do I make a winch which is 20 miles in length’
So we get lots of suggestions about tying cables together, tensile strength, etc etc

But if the answer to ‘what do you want to DO?’ is: ‘I want my car to be at the seaside’, then a better answer is ‘turn the engine on and drive there’

In simple terms, what do you want to do?
(i.e not ‘how do you think it should be achieved’)

1 Like

No, the image can be arbitrary, of any size and up to 255x255x255 colors.
Myself I understand what I wrote in my first post, I want to reduce the number of colors of an image to that of a defined palette. For each pixel we look for the closest color in the palette. But for certain colors like very light sky blue, the simplification gives us white.

And that may be just correct. As very very dark blueish gray, almost black, will end up just black.

Yes. It will.
So you either need to increase the number of colors,
or dither,
or accept that the nearest color to very pale blue is white.

There is no ‘solution’ to your question, because what you do, is the cause of what you see.

The palette shown in the first post contains quite a few near duplicates, which suggests that a linear approach is not optimal. I would experiment with some non linear mappings to try to resolve this.

Doing this leaves 192 as 192. I don’t understand how you’re getting a different value.

Using a fixed linear palette does the dirt fast job (save just the pixels mapped and discard the palette as it is always the same), but for “optimal results” one must use adaptative palettes and use color quantization algorithms to analyze images and extract the optimal palette for it and now we need both infos, the extracted palette and the pixels mapped to it. Octree Quantization and NeuQuant comes to mind, but the with it comes complexity too…