加具留矢流余

かぐるやるよ

pythonで色を連続的に変化させる方法

色をシームレスに変化させたい。虹色みたいに。ただそれだけ。

f:id:theflyingcat28:20200707003420g:plain
色を連続的に変化させるイメージ

と思っても、実は色についてほとんど知識が無いことに気がつく。RGBは分かるが、それを使ってどう変化させればいいかわからない。RGBのRの値だけを変えても色の赤みが薄くなるか濃くなるかだけで、虹色みたいに変化させるのは難しいのではと思った。

ja.wikipedia.org

困ったときのウィキペディア。世の中には皆がよく知るRGB以外にも色を表現する方法はあるようだ。というかペイントとかでお世話になっていた。 RGBは赤・緑・青の原色を混ぜ合わせることで色を表現する方法だが、HSV色空間は、Hue(色相)、Saturation(彩度)、Value(明度)の3つのパラメータで表現する方法だ。ウィキペディアを見て、色を連続的に変化させたい = 色相を変化させたい、とやりたいことを明確に言語化できた。

ウィキペディアを見るとHSVからRGBの変換方法まで記載されていたので、そのままhsvからRGBの変換関数を作成してみた。

def hsv_to_rgb(h, s, v):
    """ Convert hsv color code to rgb color code.
    Naive implementation of Wikipedia method.
    See https://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93
    Args:
        h (int): Hue 0 ~ 360
        s (int): Saturation 0 ~ 1
        v (int): Value  0 ~ 1
    """

    if s < 0 or 1 < s:
        raise ValueError("Saturation must be between 0 and 1")
    if v < 0 or 1 < v:
        raise ValueError("Value must be between 0 and 1")

    c = v * s
    h_dash = h / 60
    x = c * (1 - abs(h_dash % 2 - 1))
    rgb_dict = {
        0: (c, x, 0),
        1: (x, c, 0),
        2: (0, c, x),
        3: (0, x, c),
        4: (x, 0, c),
        5: (c, 0, x),
    }
    default = (0, 0, 0)
    rgb = [0, 0, 0]
    for i in range(len(rgb)):
        rgb[i] = (v - c + rgb_dict.get(int(h_dash), default)[i]) * 255
    rgb = map(int, rgb)
    return tuple(rgb)

あとはhを0~360までfor文で変化させたものをgifにすればトップの色が変化していく画像ができあがる。

for i in range(60):
    color = hsv_to_rgb(i*6, 1, 1)
    img = Image.new('RGB', (width, width), (0, 0, 0))
    draw = ImageDraw.Draw(img)
    draw.rectangle((0, 0, width, width), fill=color)
    images.append(img)

images[0].save("./color.gif", save_all=True, append_images=images[1:], optimize=False, duration=5, loop=0)

せっかくだからグラデーションにでもすればもっと見目麗しい画像になった気がする。 あとpillowは簡単にgifが出来て便利。(小並感)