Сортировка списка RGB-триплетов в спектр - PullRequest
16 голосов
/ 02 июня 2011

У меня есть список 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 ]

11 голосов
/ 02 июня 2011

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

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

В качестве альтернативы, если все, что вас волнует, это радуга,преобразовать в hsl, затем увеличить насыщенность до 1,0 и значение до 0,5, преобразовать обратно в rgb и визуализировать его вместо исходного цвета.

6 голосов
/ 02 июня 2011

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

4 голосов
/ 07 апреля 2012

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

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

3 голосов
/ 02 июня 2011

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

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

1 голос
/ 02 июня 2011

Это кажется неправильным.

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

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

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