月で絵を描きたいだと？それならpythonに任せなさい🌝

# 月で絵を描く
　最近、月の絵文字で絵を描くツイートがバズっていました。

月の絵文字とは以下の8種類です。

🌑
🌒
🌓
🌔
🌘
🌗
🌖
🌕

　これらの絵文字の濃淡を利用することで何かのロゴとか、オルガさんの「止まるんじゃねぇぞ...」とかを描くというわけです。

　***これは自動化できる。。。***

　そう思った私は早速emacsとterminalを立ち上げ、コーディングを開始しました。思っていたより完成は早く、朝の10時にコーディングを開始しお昼12時半には完成していたと思います。それでは手法とその結果を見ていきましょう。


# 手法
**流れ**
1. 画像を読み込む
2. 画像をグレースケールにする
3. 画質を落とす
4. 前処理
5. 画像の一部と月行列でアダマール積を取る(詳細は後で)
6. 5の最大値を取る月行列に対応する月を採用する
7. 5と6を画像の隅々にわたるまで繰り返す


## 1.画像を読み込む

※pythonのバージョンは3.6.4です。

```python
from PIL import Image

img = Image.open(img_file)
```
たったこれだけ。

## 2.画像をグレースケールにする

```python
img = img.convert('L')
```
たったこれだけ。
python最高。

## 3.画質を落とす

```python
    width = 300
    height = int(width*(img.height/img.width))
    height -= height%4

    img = img.resize((width, height))
```
さて、少し煩雑なコードになってしまいました。

正確に説明することが難しいので、やりたいことがわかってくれればそれでいいです。

やりたいことは比をできるだけ保ちながら、縦と横のピクセル数を**4の倍数にしたい**ということ。理由は、この後定義する月行列にあります。

### ◆月行列
　当然のことながら、月を何らかの数字に置き換えないとコンピュータは演算することができません。そこで私は次のような月行列を定義しました。

行列*A*を4x4の正方行列とし、

![texclip20180614162253.png](https://qiita-image-store.s3.amazonaws.com/0/221435/e66751e9-fc0d-e6e9-38b8-c954808d3eed.png)

![texclip20180614162223.png](https://qiita-image-store.s3.amazonaws.com/0/221435/22690221-23e9-a070-a34d-7f8bb085247c.png)

明るければ1、暗ければ-1とする。

例えば、

🌒に対応する月行列は、
![texclip20180614162914.png](https://qiita-image-store.s3.amazonaws.com/0/221435/e29b3123-3493-c87e-4c84-e8223e3e0506.png)

🌗に対応する月行列は、
![texclip20180614163002.png](https://qiita-image-store.s3.amazonaws.com/0/221435/16e0a08e-d658-d681-d90e-dd6d580bd498.png)


みたいな感じ。

これを事前に8種類分用意しておく。

```python

    tsuki_0 = np.matrix([[-1, -1, -1, -1],
                        [-1, -1, -1, -1],
                        [-1, -1, -1, -1],
                        [-1, -1, -1, -1]])

    tsuki_1 = np.matrix([[-1, -1, -1, 1],
                        [-1, -1, -1, 1],
                        [-1, -1, -1, 1],
                        [-1, -1, -1, 1]])

    tsuki_2 = np.matrix([[-1, -1, 1, 1],
                        [-1, -1, 1, 1],
                        [-1, -1, 1, 1],
                        [-1, -1, 1, 1]])

    tsuki_3 = np.matrix([[-1, 1, 1, 1],
                        [-1, 1, 1, 1],
                        [-1, 1, 1, 1],
                        [-1, 1, 1, 1]])

    tsuki_4 = np.matrix([[1, -1, -1, -1],
                        [1, -1, -1, -1],
                        [1, -1, -1, -1],
                        [1, -1, -1, -1]])

    tsuki_5 = np.matrix([[1, 1, -1, -1],
                        [1, 1, -1, -1],
                        [1, 1, -1, -1],
                        [1, 1, -1, -1]])

    tsuki_6 = np.matrix([[1, 1, 1, -1],
                        [1, 1, 1, -1],
                        [1, 1, 1, -1],
                        [1, 1, 1, -1]])

    tsuki_7 = np.matrix([[1, 1, 1, 1],
                        [1, 1, 1, 1],
                        [1, 1, 1, 1],
                        [1, 1, 1, 1]])
```
これら月行列を用いて画像から月描画を行います。なので、画質を4の倍数に落とした方が後々得するのです。

(説明適当ですみません)

そして、月行列のindexを上から0~7としました。index→絵文字とする関数も作っておきます。

```python
def index2tsuki(index):
    if index==0:
        return emoji.emojize(':new_moon:', use_aliases=True)
    if index==1:
        return emoji.emojize(':waxing_crescent_moon:', use_aliases=True)
    if index==2:
        return emoji.emojize(':first_quarter_moon:', use_aliases=True)
    if index==3:
        return emoji.emojize(':waxing_gibbous_moon:', use_aliases=True)
    if index==4:
        return emoji.emojize(':waning_crescent_moon:', use_aliases=True)
    if index==5:
        return emoji.emojize(':last_quarter_moon:', use_aliases=True)
    if index==6:
        return emoji.emojize(':waning_gibbous_moon:', use_aliases=True)
    else:
        return emoji.emojize(':full_moon:', use_aliases=True)
```

pythonでは絵文字を扱う時にはemojiというライブラリを使用するのが手っ取り早いかと思います。pipなどお手元のパッケージインストーラーでインストールしましょう。

話を戻します。

## 4.前処理
```python
    img = np.matrix(img)
    img = (img/128.) - 1.
```

これに関してもやりたいことだけ理解してください。
やりたいことは画像のピクセルに格納されている数値を0~255から-1~1に変換したいということです。あんまり脳みそ使わずに実験的に書いたので、細かいことは放っておいて下さい。。。

さて、ここまでで月描画における準備が完了しました。これらのコードを用いて月描画を行います。上までのコードを全て、`utils.py`というファイルに関数群としてまとめておきます。

```python
# -*- coding: utf-8 -*- #

import emoji
import numpy as np
from PIL import Image

def load_tsuki_matrixs():
    """
    月行列を返す関数

    Args:
        無し
    Return:
        月行列8個, (numpyの行列のリスト)
    """
    tsuki_0 = np.matrix([[-1, -1, -1, -1],
                        [-1, -1, -1, -1],
                        [-1, -1, -1, -1],
                        [-1, -1, -1, -1]])

    tsuki_1 = np.matrix([[-1, -1, -1, 1],
                        [-1, -1, -1, 1],
                        [-1, -1, -1, 1],
                        [-1, -1, -1, 1]])

    tsuki_2 = np.matrix([[-1, -1, 1, 1],
                        [-1, -1, 1, 1],
                        [-1, -1, 1, 1],
                        [-1, -1, 1, 1]])

    tsuki_3 = np.matrix([[-1, 1, 1, 1],
                        [-1, 1, 1, 1],
                        [-1, 1, 1, 1],
                        [-1, 1, 1, 1]])

    tsuki_4 = np.matrix([[1, -1, -1, -1],
                        [1, -1, -1, -1],
                        [1, -1, -1, -1],
                        [1, -1, -1, -1]])

    tsuki_5 = np.matrix([[1, 1, -1, -1],
                        [1, 1, -1, -1],
                        [1, 1, -1, -1],
                        [1, 1, -1, -1]])

    tsuki_6 = np.matrix([[1, 1, 1, -1],
                        [1, 1, 1, -1],
                        [1, 1, 1, -1],
                        [1, 1, 1, -1]])

    tsuki_7 = np.matrix([[1, 1, 1, 1],
                        [1, 1, 1, 1],
                        [1, 1, 1, 1],
                        [1, 1, 1, 1]])

    return [tsuki_0, tsuki_1, tsuki_2, tsuki_3,
            tsuki_4, tsuki_5, tsuki_6, tsuki_7]



def index2tsuki(index):
    """
    indexから絵文字対応する絵文字を返す関数
    Args:
        index
    Return:
        絵文字
    """
    if index==0:
        return emoji.emojize(':new_moon:', use_aliases=True)
    if index==1:
        return emoji.emojize(':waxing_crescent_moon:', use_aliases=True)
    if index==2:
        return emoji.emojize(':first_quarter_moon:', use_aliases=True)
    if index==3:
        return emoji.emojize(':waxing_gibbous_moon:', use_aliases=True)
    if index==4:
        return emoji.emojize(':waning_crescent_moon:', use_aliases=True)
    if index==5:
        return emoji.emojize(':last_quarter_moon:', use_aliases=True)
    if index==6:
        return emoji.emojize(':waning_gibbous_moon:', use_aliases=True)
    else:
        return emoji.emojize(':full_moon:', use_aliases=True)


def preprocess(path, col):
    """
    画像の前処理。グレースケール、画質下げ、その他前処理。
    Args:
        path: 画像のパス
        col: 月の列の数
    Return:
        img: 前処理が完了したnumpyの行列
    """
    img = Image.open(path)
    img = img.convert('L')

    width = col*4
    height = int(width*(img.height/img.width))
    height -= height%4

    img = img.resize((width, height))
    img = np.matrix(img)
    img = (img/128.) - 1.

    return img
```

この関数群を`main.py`から呼び出す形式をとります。

## 5.画像の一部と月行列でアダマール積を取る
**ここが今回の月描画の肝です。**

アダマール積とは、下のような要素ごとの掛け算のことです。(Wikiより)
<img width="625" alt="スクリーンショット 2018-06-14 16.44.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/221435/198eb2fe-5cad-8772-579f-986ad9dfa668.png">



```python
if __name__ == "__main__":
    tsuki_matrixs = load_tsuki_matrix()
    img = preprocess(IMAGE_URL)
```

先ほど定義したutils.pyの中の関数を用いて`tsuki_matrix`(8つの行列)と`img`(1つの行列)を用意します。ここで重要なことは、どの行列も**-1~1の範囲で、明るければ1に近く、暗ければ-1に近い**という性質を持つ行列であるということです。つまりアダマール積と取り、その合計を取ると、互いの類似度をうまく測ることができます。`tsuki_matrix`は4x4の行列なので、画像内の4x4の部分のみと類似度を測り、最も類似している月を採用します。それを画像内全ての位置で繰り返せばいいのです。


```python
for n, tk in enumerate(tsuki_matrixs):
    hadamard = np.multiply(img[row:row+4, col:col+4], tk)
```

## 6. 5の最大値をとる月行列に対応する月を採用する


```python
max = -10000
max_tk = 0

for n, tk in enumerate(tsuki_matrixs):
    hadamard = np.multiply(img[row:row+4, col:col+4], tk)

    if max < hadamard.sum():
        max_index = n
        max = hadamard.sum()

tsuki_list.append(index2tsuki(max_index))
```

これで、画像内の4x4ピクセル部分の月が決定します。あとはこれを画像内全体で繰り返せばいいだけです。

## 7. 5と6を画像の隅々にわたるまで繰り返す

```python
# -*- coding: utf-8 -*- #

import sys
import numpy as np
from PIL import Image

from utils import load_tsuki_matrix, preprocess, index2tsuki

IMAGE_URL = "./images/画像名"

if __name__ == "__main__":
    tsuki_matrixs = load_tsuki_matrix()
    img = preprocess(IMAGE_URL)

    tsuki_list = []

    for i in range(int(np.shape(img)[0]/4)):
        for j in range(int(np.shape(img)[1]/4)):
            row = 4*i
            col = 4*j
            max = -10000
            max_tk = 0

            for n, tk in enumerate(tsuki_matrixs):
                hadamard = np.multiply(img[row:row+4, col:col+4], tk)

                if max < hadamard.sum():
                    max_index = n
                    max = hadamard.sum()

            tsuki_list.append(index2tsuki(max_index))
        tsuki_list.append('\n')



    for i in tsuki_list:
        sys.stdout.write(i)
```

これで全てのコーティングは終了です。あとは、ソースコード内の`画像名`の部分をお好みの画像名に変えてあげることで、python君がせっせこせっせこ月描画を始めてくれます。皆さんも是非試してみてください。

[ソースコードはこちら](https://github.com/wataoka/tsuki)

# 結果
オルガさん
<img width="1051" alt="スクリーンショット 2018-06-14 15.49.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/221435/eb951a11-43b6-a5b2-ebfb-883bfe6a0112.png">

安倍さん
<img width="1404" alt="abesan.png" src="https://qiita-image-store.s3.amazonaws.com/0/221435/2f23ae6e-8131-1996-4f61-1e11b60b60a7.png">

僕
<img width="1048" alt="スクリーンショット 2018-06-14 16.58.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/221435/bb6c774b-5095-a505-6d9c-bfa44f004847.png">


なかなかの描画性能じゃないでしょうか。

ちなみに僕のツイートは十数リツイートとそんなにバズりませんでした。(他のバズってる月ツイートよりめちゃくちゃ高画質やのに!!!)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">月で安倍晋三作った <a href="https://t.co/4ohnJtNzTk">pic.twitter.com/4ohnJtNzTk</a></p>&mdash; 綿岡晃輝 (@koki_wataoka) <a href="https://twitter.com/koki_wataoka/status/1007151811692670976?ref_src=twsrc%5Etfw">2018年6月14日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

最後まで読んでいただきありがとうございました。🌚


# 自己紹介

冒頭に書くと邪魔になるので最後にひっそりと自己紹介させてください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/221435/16727490-2b41-4318-cc25-e2601479e77a.jpeg" width=30%>

|名前|綿岡晃輝|
|:--|:--|
|職業|大学4年生|
|分野|機械学習, 深層学習, 音声処理|
|Twitter|[@Wataoka_Koki](https://twitter.com/Wataoka_Koki)|