読者です 読者をやめる 読者になる 読者になる

catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

MTGの公式APIを叩いてみる

カードゲームのイラストを自動判別しようと試行錯誤しています。かたりぃなです。 今回はMagicTheGatheringのカードの自動判別の前に、データ集めに挑戦します。

どうやってデータを集めるか?

DeepLearningとか機械学習だの前に、まず学習させるデータが必要です。 学習用のデータは実際にカメラで撮影したもののほうが程よくノイズが乗って学習にはよさそうな気がしますが、そのためにはそういうデータを収集するアプリが必要です。

学習済みモデルをアプリで利用したいのに、学習するためのアプリを作る必要があって、鶏が先か卵が先かみたいな不毛な話になってしまいます。

仕方ないので試しに手作業で実際のカードをスキャナで取りんでみましたが、一枚あたり一分くらいです。一つのシリーズに200から300種類のカードがあるので、とりあえずは300分ですね。

こんなの手作業でやる気は起きません。そもそもすべてのカード揃ってるわけないですし、人力でカードの束からサーチしたりと手間かかりすぎます。

クローラーを書く?

少し試しましたが、面倒すぎます。スクレイピングした結果のデータを目視確認してラベル付けする必要もありますし、そんなの手作業でやっていてはお話になりません。

目視確認の代わりにDeepLearningでモデルを作ってそれを使えば……と思いましたが、またしても鶏と卵になってしまいます。

公式APIがあった

発売元から公式のAPIが提供されていました。こいつを使えばなんとかなりそうです。 https://docs.magicthegathering.io/

試しにREST-APIを叩いてみる

よくあるWebAPIの使い方と同じです。

カードに関するクエリであればHTTPRequestとして示されている

https://api.magicthegathering.io/v1/cards

のURLをベースとし、?の後ろにクエリを付けてあげればOKです。

名前検索するならname=“検索したいカード名"とか。

たとえば"nissa"を含むカード一覧を取得する例はこうなります。 https://api.magicthegathering.io/v1/cards?name=nissa ブラウザで開くとJSON文字列が並びます。

画像をとってくる

↑の結果得られたJSONには「名前に"nissa"を含むカード」がリストアップされています。このcards配列から適当な"imageUrl"要素を抽出すると、こんなURLがとれます。

http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=190411&type=card

ここにアクセスすると、確かにそれらしいイラストが取得できました。

Windows上から叩きたい

Linuxならcurlwgetですが、windows上で自動化するにはPowerShellから叩きたいところです。

というわけでInvoke-RestMethodというのを使えば一発でした。

先ほどのimageUrlの指す画像を保存するにはPowershellから

> Invoke-RestMethod -Uri "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=190411&type=card" -OutFile test.jpg

できました。 これで理論上はMTG公式APIが提供している全てのカードの絵柄は、自動で収集できるということになります。

Pythonで自動化する

よくよく考えたらPowerShell書かなくても公式が出してる各プログラミング言語の実装を使えば済みますね。

公式のsdkPython版を落としてきて、適当に叩いていきます。 叩き方がわかればあとはスクリプト書いてぐるぐるまわせばいいので。

その1, 準備

まずはローカルでテストしやすいように全カードの情報をとります。 10分くらいかかるので待ちましょう。

import mtgsdk
cards = Card.all()

毎回待つの嫌なので、ローカルファイルに保存します

import pickle
base_file = open('cardlist.bin', 'wb')
pickle.dump(cards, base_file)
close(base_file)

80MBくらいのファイルになりました。

このデータを読み込むときは

import pickle
base_file = open('cardlist.bin', 'rb')
cards = pickle.load(base_file)

で読めます。webからのダウンロードは時間かかりますが、これなら数秒で終わります。

その2, 中身を確認

上記で取得してきたデータの中身を見てみます。

len(cards)
>>> 32768
dir(cards[0])
>>> 長いので省略。アトリビュートが表示される
cards[0].name
>>> 'Air Elemental'
cards[0].image_url
>>> 'http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=94&type=card'

要素が32768ていうのが何かの上限に引っかかってそうな値(216 signed?)で嫌な感じですが、URLも取れてるので先に進みます。

これでカード名をもとにローカルに保存するファイル名が生成できそうです。(この例だと"Air Elemental.jpg"とか。)

その3, Pythonからイメージをダウンロード

urllibというライブラリを叩くだけでした。

import urllib
card = cards[0] # 実験しやすいように先頭のカードの情報をとっておく
image_request = urllib.request.urlopen(card.image_url)
image_data = image_request.read()

これでimage_dataにダウンロードしたイメージのバイナリが格納されます。 あとはfileにwriteしてあげればOKです。

その4, スクリプトでカードイメージを全部ダウンロードする

先の全カードのダウンロードでは嫌な数字が見えているので、特定のエキスパンションだけダウンロードします。

いきなり数万枚もの画像分類モデルを作るよりは、少しずつ積み重ねていったほうが色々と見えそうというのもあります。

というわけで「カラデシュ」という弾の(略称=kld)カードをすべてダウンロードします。

from mtgsdk import Card
from mtgsdk import Set
import urllib

ktk_cards = Card.where(set='kld').all()   # ここで与えるパラメータset=にはエキスパンションの略称。

print("card num = {}", len(ktk_cards) )
for card in ktk_cards:
    image_url_request = urllib.request.urlopen(card.image_url)
    image_data = image_url_request.read()
    out_file_name = card.name +".jpg"
    print("{}".format(out_file_name) )
    out_file = open(out_file_name, "wb")
    out_file.write(image_data)
    out_file.close()

できました! ただし、同じカード名のものが重複してしまうとうまく保存できません。 土地カード(forestとかswampとか)は同一カード名で複数の絵柄があります。これは今後の課題ということで。

同様にしてset=のところを'ktk'とかにすれば「タルキール覇王譚」が取れます。

感想と今後の展望

実際にカードの属性見て気づいたのですが、カードを分類するための情報ってカード名だけじゃないんですよね。 学習モデルに突っ込むことを考えると、カード名は人間がつけた分類ラベルであって、そのラベルに「人間が見て意味を解釈できる文字列」が関連付けられているだけのことです。

次のステップでは今回の画像セットを水増しして、学習モデルに入力していきたいと思います。 HoloLens側のアプリも作っていきたいですね。

それでは今回はこれくらいで。