将RGB三元组列表排序为频谱

时间:2011-06-01 23:43:46

标签: python python-imaging-library color-space spectrum hsv

我有一个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()

然而,结果看起来并不像一个漂亮的彩虹-y光谱。我怀疑我需要转换为不同的颜色空间或以不同的方式处理HSL三元组。

this doesn't look like a spectrum

有谁知道我需要做些什么来使这些数据看起来像彩虹一样?

更新:

我正在玩希尔伯特曲线并重新审视这个问题。按照希尔伯特曲线的位置对RGB值(两个图像中的相同颜色)进行排序会产生一个有趣的(如果仍然不完全令人满意)结果:

RGB values sorted along a Hilbert curve.

5 个答案:

答案 0 :(得分:11)

您正在尝试将三维空间转换为一维空间。正如奥利所说,无法保证你能从中获得令人愉悦的彩虹。

你可以做的是根据饱和度和值/亮度将颜色“桶”成几个不同的类别,然后在类别中进行排序,以获得几个独立的渐变。例如,高饱和度颜色首先是经典彩虹,然后是中饱和度高值颜色(粉彩),然后是低饱和度(灰色)。

或者,如果你关心的只是彩虹,转换为hsl,然后将饱和度甩到1.0并将值甩到0.5,转换回rgb并渲染而不是原始颜色。

答案 1 :(得分:6)

据推测你是按色调排序(即 H )?如果 S L (或 V )是常量,那么这将得到一个很好的结果,但如果它们是独立变化的,那么你将得到一个有点乱!

答案 2 :(得分:4)

一种减少颜色空间维度的有趣方法是使用空间填充Hilbert curve。两篇相关文章是:

他们都考虑3d - &gt;减少2d,但映射到1d曲线的中间步骤可以解决您的问题。

答案 3 :(得分:3)

以下是我最近制作的一些彩虹,你可以修改想法来做你想做的事情

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

答案 4 :(得分:1)

这似乎不正确。

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

试试这个。

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