Python+OpenCVで200色の白を表現してみた

息抜き記事です。n番煎じ。

# はじめに

ア〇ミカ女史によると、白は200色あるそうです。

Pythonで（約）200色の白を1枚の画像に描写して、それぞれの味わいを語り合いましょう！

# コンピュータにおける色の表現の基礎

コンピュータでは、色を`(赤,緑,青)`の組み合わせで表現することが一般的です。この表現方法を**RGB**と呼びます。

これは、人間が判別できる多くの色を赤（**R**ed）緑（**G**reen）青（**B**lue）の3色の組み合わせで表すことができる、いわゆる「光の三原色」に基づいています。

https://ja.wikipedia.org/wiki/RGB

また、各色は一般的に0～255の範囲で表現されます。0のときはまったく光っておらず、255のとき最高に光っているとイメージしてください。

0～255を16進数で表現すると、0x00～0xFFと表せます。HTML/CSSなどでよく見る`#55C500`といったカラーコードも、RGBの表記法の1つです。この場合前から2桁ずつ、0x55=85が赤の強さ、0xC5=197が緑の強さ、0x00=0が青の強さを指しています。

Qiitaではカラーコードの右に小さく色を表示してくれますが、Googleで検索して確認することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550728/82071af7-fce5-92bc-2ef6-d1019265acd2.png)

この色は、Qiitaのロゴカラーですね！

# 「200色の白」ってなんやねん

それでは、コンピュータで「白」をを表現してみましょう。先ほどのWikipediaで「光の三原色」のイラストを見てみると、最高に光っている3色の光が交わる真ん中が白になっているとわかります。白は`(255,255,255)`、`#FFFFFF`と表すことができます。

しかし、果たして`(255,255,255)`だけが白なのでしょうか？ちょっといたずらして、赤を弱めてしまいましょう。`(254,255,255)`（`#FEFFFF`）ではどうでしょうか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550728/30efb220-9b07-1df9-58aa-474aedc3d9de.png)

...ほとんど違いがわからない白色です。それじゃあもう少し大胆に、それぞれの光を同じだけ弱めてみましょう。5ずつ弱めて、`(250,250,250)`（`#FAFAFA`）ではどうでしょうか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550728/46b465d7-6fc3-2f67-8dd6-a3bed2c04358.png)

私の液晶ではちょっぴりくすんだ印象はありますが、まだまだ白と言えそうです。つまり「255から少しいじったぐらいなら、私たちは白色と認識することができる」ということがわかります。

# 200色の白を作るための値の範囲

では200色の白を作るためには、各色をどれくらい広げる必要があるのか考えてみます。

各色`255`～`255`の場合、表現できる白色は`(255,255,255)`の1色です。

`254`～`255`の場合は、
`(254,254,254)`、`(254,254,255)`、`(254,255,254)`、`(254,255,255)`、`(255,254,254)`、`(255,254,255)`、`(255,255,254)`、`(255,255,255)`、すなわち`2 ** 3 = 8`色の白色を表現できます（カラーコードで見れば、それぞれちゃんと白に見えることがわかるでしょう）。

この調子で、200色の白を表現するための範囲を考えます。それぞれの色がn階調表現できるとして、`n ** 3 >= 200`より`n >= 200 ** (1/3)`、nは整数なので6階調、各色`250`～`255`もあれば200色の白を表現することができるようです。先ほど`(250,250,250)`が白色に見えましたから、大丈夫そうですね。

# 画像にしてみる

PythonとOpenCVを使って、`6 ** 3 = 216`色の白色を1枚の画像に並べましょう。

OpenCVの導入などについては詳しくは説明しませんが、以下のコマンドを実行するとOpenCVをインストールできます。

```
pip install opencv-python
```

こちらが実際のコードです。

```various-white.py
import numpy as np
import cv2

def main():
    
    SIDE_LENGTH = 32
    image = np.zeros((480, 480, 3), dtype=np.uint8)

    row = 0
    column = 0
    for r in range(250, 256):
        for g in range(250, 256):
            for b in range(250, 256):
                cv2.rectangle(image, (column * SIDE_LENGTH, row * SIDE_LENGTH), (column * SIDE_LENGTH + SIDE_LENGTH, row * SIDE_LENGTH + SIDE_LENGTH), (b, g, r), -1)
                column += 1
                if column == 15:
                    row += 1
                    column = 0
    
    cv2.imwrite("result.png", image)
   
if __name__ == '__main__':
    main()
```

空の画像を用意して、それぞれの色を32 * 32ずつ塗るコードです。216個の白色を並べるための正方形の一辺は、`x ** 2 >= 216`より`x >= sqrt(216)`なので`15 * 32 = 480`ピクセルとわかります。あとは15個並べたら改行するだけです。

![various-white.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550728/124756e2-b1f4-7c0a-84c1-e690e17bba9a.png)

すべて白色に見えるのではないでしょうか。白は本当に200色あったんだ...！

# ？？？「白ってn色あんねん」

偽アン〇カが現れて適当な数字を言っても確認できるように、任意の数に対応しましょう。とはいっても、ここまで各数字の導出過程を書いてきたので、これらをソースコードに起こすだけです。

16777216色の白があると主張するアンミカには、こちらのコードで対抗しましょう。色数が多すぎてメモリ使用量がすさまじいことになるため、色を塗るサイズは1ピクセルにしています。

```various-white.py
import math

import numpy as np
import cv2

def main():

    NUM_WHITE = 16777216
    COLOR_RANGE = range(256 - math.ceil(NUM_WHITE ** (1 / 3)), 256)
    NUM_WHITE_ACTUAL = len(COLOR_RANGE) ** 3
    
    SIDE_LENGTH = 1
    SIDE_COUNT = math.ceil(math.sqrt(NUM_WHITE_ACTUAL))
    SIDE_RESULT = SIDE_COUNT * SIDE_LENGTH
    image = np.zeros((SIDE_RESULT, SIDE_RESULT, 3), dtype=np.uint8)

    row = 0
    column = 0
    for r in COLOR_RANGE:
        for g in COLOR_RANGE:
            for b in COLOR_RANGE:
                cv2.rectangle(image, (column * SIDE_LENGTH, row * SIDE_LENGTH), (column * SIDE_LENGTH + SIDE_LENGTH, row * SIDE_LENGTH + SIDE_LENGTH), (b, g, r), -1)
                column += 1
                if column == SIDE_COUNT:
                    row += 1
                    column = 0
    
    cv2.imwrite("result.png", image)
   
if __name__ == '__main__':
    main()
```

![various-white.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550728/05c9504a-1b63-05b6-334e-56cc3434ca63.png)

こちらが16777216の「白」です。`#000000`から`#FFFFFF`まであります！

おあとがよろしいようで...