A
Note the code I present in this answer does not pretend to be a solution, but only to serve to explore some ideas.InitialThe first thing that occurred to me was to convert each RGB color to HSV representation (Hue, Saturation, Value). In this representation, the "Hue" component tells you what color it is (that value is a real number between 0 and 360 that represents an angle, which is the orientation of an imaginary vector in the color space). The "S" component is the "Saturation" that tells you how much white it has added that color. It is an integer between 0 and 1, being 0 saturation minimum (white) and 1 maximum (white, pure color). Finally the V is the "value" of luminosity that comes to say how much black is mixed with the color (0 is all black, 1 is nothing black, "pure").I thought that by transforming each color in this way and simply ordering the resulting trios, they would already be grouped by "chromatic similarity", as we would be ordering first by Hue (color) and in the case of the same color, by saturation and then by brightness.Unfortunately the results are not what I expected. The following python code serves to generate a lot of random RGB hexademic values:import random
def color_aleatorio():
r, g, b = random.randint(0,255), random.randint(0,255), random.randint(0,255)
return "#{:02x}{:02x}{:02x}".format(r,g,b)
mil_colores = [color_aleatorio() for _ in range(1000)]
Some elements of the resulting list:['#6264c0',
'#b37f4d',
'#116dcc',
'#a8ce4b',
'#7ea6f8',
'#a94f89',
'#83be23',
'#6c8052',
'#d87e04',
'#d107f2'
...]
The graphic representation of this "paleta" of colors, in the same random order in which they were generated would be as follows: This representation I got with this code, in case someone wants to replicate it:import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors
def mostrar_colores(colores):
data = matplotlib.colors.to_rgba_array(colores)
plt.imshow(np.array(data).reshape((20,50,4)))
plt.grid(False)
mostrar_colores(mil_colores)
The following function implements the conversion of one of these values to the HSV form:import matplotlib
def rgb2hsv(hex_rgb):
r, g, b, a = matplotlib.colors.to_rgba(hex_rgb)
r, g, b = r/255.0, g/255.0, b/255.0
mx = max(r, g, b)
mn = min(r, g, b)
df = mx-mn
if mx == mn:
h = 0
elif mx == r:
h = (60 * ((g-b)/df) + 360) % 360
elif mx == g:
h = (60 * ((b-r)/df) + 120) % 360
elif mx == b:
h = (60 * ((r-g)/df) + 240) % 360
if mx == 0:
s = 0
else:
s = df/mx
v = mx
return h, s, v
Now we can ask Python to order the sequence mil_colores using the previous function as a management key:colores_ordenados = sorted(mil_colores, key=rgb2hsv)
The first ten elements already ordered would be these:['#d4d4d4',
'#efc7c7',
'#df2b2a',
'#c1201f',
'#a30907',
'#f09b99',
'#9a2623',
'#efcecd',
'#753836',
'#e30b01',
...
Which graphically gives this result, a little disappointing: We can see that, yes... more or less have been grouped by colorful, the "arbow strip" with the orange above, the violet down... but within each strip the order seems a little random and it is because values with a very close HUE, but different values or saturations end very close.Second ideaThen I realized what we want is that next colors Finish together. What forces us to define the concept of distance between colors.A color at the end is only a point in a space of three coordinates: R, G, B. Therefore we can use the euclide distance between them (raiz of the sum of squares of distances between each coordinate).Although the distance is now well defined, achieving an ordination in which the near colors end together, is in the background another form of the problem of the traveler. I mean, you have to find a tour of all points that minimizes the distance traveled. That tour would be the desired color management.Since that problem is NP-duro, I have given up implementing it.Update. Reflections and HeuristicsAfter thinking more, I came to the conclusion that the ordination "for distances" is actually only one more way to order them, and not necessarily the best, since the concept of "best" is not well defined here. In the ordination for "proximity" there may appear separate colors that in another ordination it would be considered that they should go together, for example, a very dark red could appear "with blacks" instead of "with reds". What's better?I think the problem in the background cannot be solved because it is not well defined. The set of input colors can actually be mathematically understood as a set of points in a 3-dimensional space (which would be its components, either RGB or HSV). By asking for an ordination, in the background we want to move that into a single dimension. So it's a problem projection.It is somewhat equivalent to the problem of mapping on a plane the surface of the Earth sphere. There is no single solution, and therefore there are different cartographic projections, depending on which aspect we give priority. A projections are useful because they make navigation easier, keeping the angúlos constant. Others keep the surfaces of the countries constant and are more useful to make an idea of their relative size, etc.With the color ordinances something similar happens. If all the colors were "puros" (in HSV they would have S=1 and V=1) their ordination would be simple, based only on their "Hue", and would be like this: But if we also have "pastel" colors (in which saturation goes down and therefore approach the white), or "dark" colors (in which the brightness goes down and therefore approach the black), there is no longer where to color them in the impeccable ordination by freshly seen hue.This box of paints shows how the manufacturer has solved the problem: He made two drawers. The top for the saturated colors (ordinated by hue) and the bottom for the pastel tones (ordinated also by hue). The "dark" colors have intercalated them according to their hue. For example a dark blue at the end of the blues. That "break" the beautiful rainbow because behind that dark blue comes a green-blue more clarit. In addition, it has only considered two levels of saturation (the two drawers). There could be many more...Is this ordination better or worse than another? It depends again on what is intended. The artist may want to have the paintings together with the same hue and cares little if the "rgb-distance" between the colors matches or not the distance between paintings in his box.In short There is no defined criterion to prefer management over another.All that said, I present another heuristic to order the thousand random colors that is based on:Separate very dark colors and leave them for the endSeparate the remaining colors by their saturation, putting at first the most saturated and then the "all pastel".Sort each sequence by its hue, but by reducing the number of possible hues, of infinite (because the hue is a real that varies continuously between 0 and 360) to only 15 categories (an arbitrary value based a little on easily discernible hues), and to equal hue, for its shine.This is the algorithm (python):def ordenacion(hex_rgb):
h,s,v = rgb2hsv(hex_rgb)
return v<0.002, s<0.7, int(h/360*15), v
colores_ordenados = sorted(mil_colores, key=ordenacion)
And this is the result: If instead of a thousand colors I use ten thousand, the result is more impressive, as it passes: to this: To see that the algorithm also scales for few colors, these ones proposed by Alvaro:c = [ "#fff000", "#238923", "#aaaaa0", "#ff2300", "#2ff014", "#010203" ]
And this is the ordination that results:['#ff2300', '#fff000', '#2ff014', '#238923', '#aaaaa0', '#010203']