Сортировка списка триплетов RGB в спектр

У меня есть список триплетов RGB, и я хотел бы построить их таким образом, чтобы они образовывали что-то вроде спектра.

я преобразовал их в HSV, которые люди, похоже, рекомендуют.

from PIL import Image, ImageDraw
import colorsys

def make_rainbow_rgb(colors, width, height):
    """colors is an array of RGB tuples, with values between 0 and 255"""

    img = Image.new("RGBA", (width, height))
    canvas = ImageDraw.Draw(img)

    def hsl(x):
        to_float = lambda x : x / 255.0
        (r, g, b) = map(to_float, x)
        h, s, l = colorsys.rgb_to_hsv(r,g,b)
        h = h if 0 < h else 1 # 0 -> 1
        return h, s, l

    rainbow = sorted(colors, key=hsl)

    dx = width / float(len(colors)) 
    x = 0
    y = height / 2.0
    for rgb in rainbow:
        canvas.line((x, y, x + dx, y), width=height, fill=rgb)
        x += dx
    img.show()

однако результат не очень похож на хороший радужный спектр. Я подозреваю, что мне нужно либо преобразовать в другое цветовое пространство, либо обработать триплет HSL по-другому.

this doesn't look like a spectrum

кто-нибудь знает, что мне нужно сделать для этого данные выглядят примерно как радуга?

обновление:

Я играл с кривыми Гильберта и вернулся к этой проблеме. Сортировка значений RGB (одинаковые цвета на обоих изображениях) по их положению вдоль кривой Гильберта дает интересный (если все еще не полностью удовлетворительный) результат:

RGB values sorted along a Hilbert curve.

5 ответов


вы пытаетесь преобразовать трехмерное пространство в одномерное пространство. Нет никакой гарантии, что вы можете сделать из него приятную радугу, как говорит Оли.

Что вы можете сделать, это "ведро" цветов в несколько разных категорий на основе насыщенности и значения/легкости, а затем сортировать в категориях, чтобы получить несколько независимых градиентов. Например, цвета высокой насыщенности сначала для классической радуги, затем цвета средней насыщенности с высоким значением (пастели), затем низкая насыщенность (серый).

альтернативно, если все, что вам нужно, это радуга, преобразовать в hsl, затем slam насыщенность 1.0 и значение 0.5, преобразовать обратно в rgb и визуализировать, что вместо исходного цвета.


предположительно вы сортируете по оттенку (т. е. H)? Это даст хороший результат, если S и L (или V) постоянны, но если они меняются независимо, то вы получите немного беспорядка!


интересный метод уменьшения размерности цветовых пространств использует заполнение пространства кривой Гильберта. Две соответствующие статьи:

Они оба рассматривают уменьшение 3d - > 2d, но промежуточный шаг отображения на кривую 1d может быть решением вашей проблемы.


вот некоторые радуги, которые я сделал недавно, вы можете изменить идею, чтобы сделать то, что вы хотите

from PIL import Image, ImageDraw  # pip install pillow
import numpy as np
from matplotlib import pyplot as plt

strip_h, strip_w = 100, 720
strip = 255*np.ones((strip_h,strip_w,3), dtype='uint8')
image_val = Image.fromarray(strip)
image_sat = Image.fromarray(strip)
draw0 = ImageDraw.Draw(image_val)
draw1 = ImageDraw.Draw(image_sat)
for y in range(strip_h):
    for x in range(strip_w):
        draw0.point([x, y], fill='hsl(%d,%d%%,%d%%)'%(x%360,y,50))
        draw1.point([x, y], fill='hsl(%d,%d%%,%d%%)'%(x%360,100,y))

plt.subplot(2,1,1)
plt.imshow(image_val)
plt.subplot(2,1,2)
plt.imshow(image_sat)
plt.show()

enter image description here


Это кажется неверным.

canvas.line((x, y, x + dx, y), width=height, fill=rgb)

попробуйте это.

canvas.rectangle([(x, y), (x+dx, y+height)], fill=rgb)