将一系列RGB三元组排序为光谱。

19

我有一组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 有人知道我需要做什么才能让这些数据看起来像一个彩虹吗?
更新:
我在玩Hilbert曲线,并重新审视了这个问题。通过沿着Hilbert曲线对RGB值(两个图像中的相同颜色)进行排序,可以得到一个有趣的(尽管仍然不完全令人满意)结果: RGB values sorted along a Hilbert curve.

请阅读以下内容,然后重新考虑你正在做的事情:http://www.brucelindbloom.com/index.html?Equations.html。光谱是单一波长值,不是HSL三元组或任何类似的东西。这个方程并不简单,所有RGB颜色都不在光谱上。此外,这可能会有所帮助:http://www.pfk.ff.vu.lt/cie/1931CIE_explanation.htm。你真的需要重新思考你正在做的事情。 - S.Lott
3
@S.Lott -- 是的,我知道从技术上讲这是一个愚蠢的任务。我并不需要与物理光有关的任何东西,只需要展示出最令人愉悦的渐变(即最小化感知不连续性)的东西。 - Jason Sundram
更新--我决定在两个轴上绘制颜色,色调和饱和度,使亮度自然变化。结果看起来不错(但需要更多的空间)。 - Jason Sundram
如果你正在寻找真实的光谱颜色,请特别查看 R、G、B 图表,并尝试以类似方式对颜色进行排序。然后按强度重新排序“相同”的颜色,这样你就不会有太多的凸起... - Spektre
5个回答

12

您正在尝试将三维空间转换为一维空间。就像Oli所说的那样,不能保证可以制作出令人愉悦的彩虹。

您可以根据饱和度和明度/亮度将颜色分成几个不同的类别,并在类别内进行排序,以获得几个独立的渐变。例如,对于经典彩虹,首先是高饱和度的颜色,然后是中等饱和度高价值/亮度的颜色(柔和色),然后是低饱和度的颜色(灰色)。

或者,如果您只关心彩虹,可以将其转换为HSL格式,然后将饱和度设置为1.0,将亮度/明度设置为0.5,再将其转换回RGB格式,并以此渲染,而不是使用原始颜色。


我喜欢分离彩虹的想法。我会尝试一下。谢谢。 - Jason Sundram
正如rephorm在他的答案中所说,您可以使用Hibert曲线将3D空间降至2D。 - gagarine

6

假设您是按色调(即H)排序的?如果SL(或V)保持不变,那么这将给出一个不错的结果,但如果它们独立变化,那么你会得到一些混乱!


是的,我正在按h、s、l排序,而最后两个的可变性导致了一些混乱。我在想是否有一些h、s和l的函数可以制作出漂亮的东西。 - Jason Sundram
@Jason:一般来说,不行。正如@Russell在他的回答中所说,你试图将一个三维空间折叠成一条一维线。除非你实际修改输入颜色,否则无法避免不连续性。 - Oliver Charlesworth

4

这是我最近制作的一些彩虹,您可以修改这个想法来做您想要的事情。

这段话与IT技术无关。如果您有其他需要翻译的内容,请再次提出。
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
这段代码可以用一系列漂亮的颜色填充空间。我的问题是我有一个颜色列表,我想要将它们变成彩虹色。有点相反的问题。不过还是谢谢你提供的代码。 - Jason Sundram

4

一种有趣的降低色彩空间维度的方法是使用填充Hilbert曲线。两篇相关文章如下:

它们都考虑了3D -> 2D的缩减,但映射到1D曲线的中间步骤可能是解决问题的方法。


1
感谢提供这2个非常有趣的链接! - Jason Sundram
使用希尔伯特曲线对图像像素进行排序的另一个示例:http://www.poeschko.com/2011/09/extreme-geeky-tidying-up/ - gagarine

1

这似乎不正确。

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

试试这个。

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

1
它们都产生相同的结果,尽管我承认使用“矩形”来绘制矩形比画一个粗线条更有意义。 - Jason Sundram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接