前回の記事でラズパイを購入して、まだBluetoothしか使っていない訳ですが、私は気付いていました。
そう、Raspbianにマインクラフトがインストールされていることを!
マイクラやってみたかったので、少し遊んでみます。
Minecraft Pi Edition
まとめサイトを見つけました。
https://matome.naver.jp/m/odai/2142545327953860701
Pi Editionの特徴は、以下のようです。
- 無料
- プログラミング機能あり
- クリエイティブモードのみ
- ワールドの広さに制限
- クラフト不可
サバイバルモードを有効にする非公式のパッチはあるみたいですね。
平面の広さが256×256しかないので、プログラミングして遊んで、本格的にやりたくなったらPC版を買う感じかな。
公式のドキュメントもプログラミングすること前提で書かれているみたい。
作るもの
256×256と聞いて思い浮かぶのは、地図タイルですね!(職業病)
https://en.m.wikipedia.org/wiki/Tiled_web_map
試しに、地図タイルを描画してみましょう。
使ったもの
- Minecraft Pi Edition v0.1.1
- Python 2.7.9
- python-opencv 2.4.9
Python3系が使えるかわからなかったので、Python2系にしました。
実装
OpenCVで画像を読み込んで各ピクセルのRGB値を調べ、その色に近いブロックを置いていきます。
OpenCVのインストール
以下の記事にある手順でインストールできました。
http://qiita.com/suppy193/items/91609e75789e9f458c39
$ sudo apt-get update
$ sudo apt-get install libopencv-dev
$ sudo apt-get install python-opencv
画像の読み込み&ピクセルのRGB値を取得
OpenCVを使えば簡単です。
import cv2
img = cv2.imread('image.png')
column = 10
row = 20
pixel = img[column, row]
print(pixel)
上記を実行すると、以下のような出力になります。
RGBではなく、BGRの順になっています。
>>>
[ 57 70 255]
近い色のブロックを置く
16色に塗り分けられるwool(羊毛)を使います。
各色のRGB値は、以下に記載されていました。
http://minecraft.gamepedia.com/Wool
ブロックを置くためのコードは以下のような感じです。
実行すると、プレイヤーの足元にあるブロックがオレンジになります。
from mcpi import minecraft
mc = minecraft.Minecraft.create()
x, y, z = mc.player.getPos()
wool = 35
selectedColor = 1 # Orange
mc.setBlock(x, y-1, z, wool, selectedColor)
各ピクセルにどの色を使うかですが、RGB値の差をnumpy.linalg.norm
で求めて、最も差が小さい色を使うことにします。
dist = numpy.linalg.norm(woolColor - pixel)
ループさせて画像を描画する
RGB値に近い色のブロックを置く処理をループさせ、画像の各ピクセルをブロックに変換します。
最終的なコード全文は、以下です。
from mcpi import minecraft
import cv2
import numpy
mc = minecraft.Minecraft.create()
wool = 35
# wool color BGR
woolColors = [
[221,221,221], # 0: White
[62,125,219], # 1: Orange
[188,80,179], # 2: Magenta
[201,138,107], # 3: Light blue
[39,166,177], # 4: Yellow
[56,174,65], # 5: Lime
[153,132,208], # 6: Pink
[64,64,64], # 7: Gray
[161,161,154], # 8: Light gray
[137,110,46], # 9: Cyan
[181,61,126], # 10: Purple
[141,56,46], # 11: Blue
[31,50,79], # 12: Brown
[27,70,53], # 13: Green
[48,52,150], # 14: Red
[22,22,25] # 15:Black
]
img = cv2.imread('image.png')
img = cv2.resize(img, (256, 256))
height, width = img.shape[:2]
x, y, z = mc.player.getPos()
for column in range(width):
for row in range(height):
pixel = img[column, row]
selectedColor = 0
minDist = None
for i, woolColor in enumerate(woolColors):
dist = numpy.linalg.norm(woolColor - pixel)
if minDist == None or minDist > dist:
minDist = dist
selectedColor = i
mc.setBlock(-153+row, y-1, -116+column, wool, selectedColor)
mc.postToChat("Create Image")
x,z座標はワールド端を基点にしているのですが、何故か[-153,-116]でした。
y座標はプレイヤーの位置から-1して、プレイヤーが立っている平面にブロックを並べています。
実行結果
Minecraft Pi Editionを起動した状態で、Pythonのスクリプトを実行します。
「F5」キーを押すか、"Run" > "Run Module"で実行できます。
65,536ピクセルを16色と比較しているため、結構時間が掛かります。
気長に数分待ってください。
元画像は、OpenStreetMapの地図タイル(渋谷駅周辺)を使ってみました。
© OpenStreetMap contributors
http://www.openstreetmap.org/copyright
カメラワークの都合上、全体を一度に見渡すことはできません。
アイコンの形はしっかり出てますが、注記の文字はがんばってなんとか読める程度まで崩れています。
また、淡いグレーや淡いブルーが白のブロックになってしまい、建物や敷地の境界が消えてしまったので、地図としてはちょっと微妙な結果になりました。
まとめ
シンプルなロジックで思ったよりも描画できていますが、いかんせん色数が足りない印象です。
wool以外のブロックも使って、不足している色を割り当てれば画質はよくなりそうですが、さらに処理時間が増えるので考えものです。
オマケ(本編)
256×256のサイズありきで地図タイルを試したんですが、縦横が同じピクセル数の画像であれば、OpenCVでresizeしてから処理すればいいんですよね。(コード例にも仕込んでます)
で、Twitterアイコン用に配布されている画像って、正方形で解像度も小さいじゃないですか。
適当に読み込んでみましょう。
こっちの方が全然上手く描けてるじゃん!
髪や肌など、色の淡い部分が白ブロックになってしまう傾向は変わりません。肌色や茶色に近いブロック(SandstoneやStaind Clay)を足すといいかもしれないですね。