Reputation: 1
I have small set of color data that I want to investigate. It is in the form of a list of RGB data.
[(255, 255, 255), (124, 144, 231), ...]
The image uses a restricted palette, and I would like to see how these colors are "distributed" by plotting them along the color wheel. As alternative, I tried histogram of individual channels, but this did not give me the insight I am interested in.
I googled and learned that HSL color more accurately maps to color wheel. However, after converting to HSL, I'm still confused. 3 pieces of data still make up the color: Hue, saturation, and luminosity. How do you map 3 piece of data onto a 2d plane?
I read about polar coordinates here: https://www.mathsisfun.com/polar-cartesian-coordinates.html. I try ignoring luminosity and plotting by treating HSL data as Polar coordinate (hue = angle, saturation = length of radius (scaled by some factor))
def polar2cartesian(hsl):
color_circle_radius = 100
radius = hsl.saturation * color_circle_radius
x = radius * math.cos(math.radians(hsl.hue))
y = radius * math.sin(math.radians(hsl.hue))
return x, y
...
for hsl in colors:
x, y = polar2cartesian(hsl)
im.point(x, y, hsl.to_rgb())
This is not correct result. As it shows same red color hue in multiple places like this example:
What is the correct way to translate from RGB to a position on color wheel?
Upvotes: 0
Views: 4093
Reputation: 1228
The problem of mapping a 3D (H, S, V) colour onto a 2D plane is a tough one to solve objectively, so I thought I'd give a crack at it and come up with results that I find pleasing.
My approach is as follows:
Now, in code: (Entire file)
Create a table to store the largest value in every particular position
highest_value = numpy.zeros((image_size, image_size))
Convert RGB to HSV
def rgb_to_point(rgb):
hsv = colorsys.rgb_to_hsv(*rgb)
Convert that to a vector
rads = math.tau * hsv[0] - math.pi
mag = hsv[1] * (image_size/2) - 1
Convert that to a point on our image
x = int(math.cos(rads) * mag + (image_size/2))
y = int(math.sin(rads) * mag + (image_size/2))
If the value is higher, return the point, otherwise None
if(hsv[2] > highest_value[x][y]):
highest_value[x][y] = hsv[2]
return (x, y)
I called all that the rgb_to_point
function, now we will use it for every pixel in our image:
for pixel in img.getdata():
c = rgb_to_point(pixel)
if(c):
imgo.putpixel(c, pixel)
if(c)
determines whether the value was higher, since c is None
when it wasn't.
Here's the results:
Note: Part of the reason I am dealing with value like this is because the alternatives I thought of were not as good. Ignoring value completely lead to darker pixels turning up on the output image, which usually lead to an ugly wheel. Turning the value up to 1 for every output pixel lead to very generic looking wheels that didn't really give a good idea of the original input image.
Upvotes: 10