Pythonでマインクラフト内にピクセルアートを作成

#はじめに
Pythonからマインクラフト動かせる！ということで、
好きな画像をマイクラ内でピクセルアート化できるプログラムの作成をしました。
実際にアイコンをピクセルアート化すると以下のようになります。
<img width=400 alt="2021-12-13_17.28.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/7123120f-c431-c525-3e75-26a211a9bc39.png">
<img width=400 alt="2021-12-13_17.35.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/45ec6109-eb39-88cc-5f82-1630fdafc47f.png">

#環境
Pythonからマインクラフトを動かすための環境構築は以下を参考にしました。
[マインクラフトでpythonプログラミングをするための5つのステップ]
(https://www.timeless-education.com/five-steps-to-start-python-programming-in-minecraft-2607.html)

実行環境は以下になります。
Mincraft JE 1.12.2
Forge 1.12.2
Raspberry Jam Mod 0.92
Python 3.9.9

#画像からピクセルアートに変換
Pythonでの画像の操作はPillow,numpyを使います。

```Python:pic2dot.py
import numpy as np
from PIL import Image
```

###1.指定のピクセル数に変換
長辺が指定のピクセル数になるように元画像をリサイズします。
輝度の調整については後に記述します。

```Python:pic2dot.py
def load_picture(file_path, map_size, brightness):
    im = Image.open(file_path) #画像読み込み
    #輝度の調整
    con = ImageEnhance.Brightness(im)
    im = con.enhance(brightness)
    #長辺がmap_sizeになるように変換
    if im.size[0] > im.size[1]:
        resize_rate = map_size / im.size[0]
    else:
        resize_rate = map_size / im.size[1]
    #リサイズ
    im = im.resize((round(resize_rate*im.size[0]), round(resize_rate*im.size[1])))
    return im
```

###2.使用するマインクラフトのブロック
リサイズしたピクセルの色に対応するマイクラ内のブロックを決めていきます。
今回は羊毛16色と土、金ブロック、本棚、砂岩の計20種類を使っています。
羊毛以外のブロックは肌色、茶色系統を追加するためです。
それぞれのブロックの色はMinecraft Wikiを参照しました。
[地図アイテムフォーマット](https://minecraft.fandom.com/ja/wiki/%E5%9C%B0%E5%9B%B3%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)

###3.各ピクセルをブロックに置き換え
各ピクセルに近い色のブロックを選んでいきます。
色の類似はXYZ色空間のユークリッド距離で評価します。
各ピクセルと20種類のブロックの色の距離を計算し、一番近いブロックを当てはめていきます。
具体的な変換方法は以下を参考にしました。

[CIE XYZ表色系（９）：　XYZ-RGB の変換式 と カラートライアングル](http://www.enjoy.ne.jp/~k-ichikawa/CIEXYZ_RGB.html)

```Python:pic2dot.py
#CIE-XYZで各ピクセルの色に対応するblock_idに変更 im:リサイズ後の画像
def to_color(im):
    #各ブロックのXYZ色空間の一覧読み込み
    block_list = np.loadtxt(f"color_list/color_srgbxyz.csv", delimiter=",", skiprows=1)
    #RGB画像をXYZ色空間に変換
    rgb2xyz = (
            0.4124, 0.3576, 0.1805, 0,
            0.2126, 0.7152, 0.0722, 0,
            0.0193, 0.1192, 0.9505, 0
        )
    im_array = np.array(im.convert("RGB", rgb2xyz))
    #結果を格納するarray
    out = np.zeros(im.size, dtype=int).T
    #各ピクセルとブロックの色の距離算出して一番距離が近いブロックを当てはめていく
    for i in range(im.size[1]):
        for j in range(im.size[0]):
            dist = 100000
            for k in range(len(block_list)):
                dist_ = np.linalg.norm(im_array[i,j]-block_list[k,1:]) #ピクセルとブロックの色の距離算出
                if dist_ < dist:
                    out[i,j] = block_list[k,0]
                    dist = dist_
    out = Image.fromarray(out)
    return out
```

color_srgbxyz.csvにブロックidとXYZ色空間に変換した数値を記載。
羊毛のようにブロックidと種類のidを持つ場合は併記します。
後で100で割った商とあまりでブロックidと種類のidに分解します。

```color_srgbxyz.csv
id,x,y,z
3500,242.3775,255,277.695      #羊毛:白
3501,143.6991,140.4342,67.7827 #羊毛:オレンジ
...
47,114.5236,120.709,85.3807    #本棚
3,115.1493,115.6188,89.0956    #土
```

###4.ブロックを配置
マインクラフトにアクセスしてブロックを配置していきます。

```Python:minecraft_pixel_art_for_color.py
#マインクラフトとの接続
mc = minecraft.Minecraft()

#ブロックの配置
for i in range(map_size):
    for j in range(map_size):
        #余白は白のウール
        if im[i,j] == 255:
            mc.setBlock(corner[0] + j, corner[1], corner[2] + i, block.WOOL.id, 0)
        #ウールなどブロックidと種類idを持つブロックの設置
        elif im[i, j] > 1000:
            id1, id2 = divmod(im[i, j], 100) #100で割った商がブロックid、あまりが種類id
            mc.setBlock(corner[0] + j, corner[1], corner[2] + i, int(id1), int(id2))
        #その他ブロックの設置
        else:
            mc.setBlock(corner[0] + j, corner[1], corner[2] + i, int(im[i, j]))
```

#輝度について
マイクラでピクセルアートを作成したとき、よく画像が暗くなる印象を受けます。
そこで輝度を調節することでピクセルアート化したときに明るく見やすくします。
輝度1だと暗く輝度1.2で地図の画像がいい感じ、輝度2でピクセルアートがきれいにできている印象を受けます。
<img width=400 alt="lena.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/f0c4cc17-5113-88de-bf02-c3a4ee1e1ce6.jpeg">
　　　　　　　　　　　元画像
<img width=400 alt="2021-12-13_20.35.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/302c655c-e7ad-eeb6-31f6-37242333c9e0.png">
　　　　　　　　　　　輝度1
<img width=400 alt="2021-12-13_20.36.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/cdeaeed5-c6c0-86c0-fcb4-c4c127940edb.png">
　　　　　　　　　　　輝度1.2
<img width=400 alt="2021-12-13_20.36.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/6c6c051f-b775-f083-4d2c-e062ee28aa7e.png">
　　　　　　　　　　　輝度2


#終わりに
pythonで画像を加工し、マインクラフト内でピクセルアート化する方法を紹介しました。
githubにコードをあげているので、よろしければご利用ください。
https://github.com/konishi0125/minecraft_pixel_art