アウトプットブログ

主にプログラミングで学んだことをアウトプットします。

ぷよぷよの画像を読み込んでフィールド情報を取得する機能を作ってみた

以前作成した画像分類のモデルとホモグラフィ変換を用いて、ぷよぷよのゲーム画像をアップロードしてフィールド情報を取得する機能をアプリに追加します。


▼過去記事 画像分類
畳み込みニューラルネットワークを用いた画像分類 - アウトプットブログ


▼過去記事 ホモグラフィ変換
ホモグラフィ変換の実装 - アウトプットブログ


フレームワーク

サーバ側でpythonを実行したかったのでpythonフレームワーク選定。
調べた結果構築が一番シンプルに思えた、flaskにしました。

処理

ブラウザ側から
・INPUTとなる画像(base64エンコードした値)
・ホモグラフィ変換用の四隅の点
を受け取り、
 ↓
モグラフィ変換を行い、
 ↓
以前作成したモデルに画像情報を与えてぷよぷよのフィールド情報を予測します。


メイン処理。

def convert_image_to_field(base64bin, p1, p2, p3, p4):
    """
    画像(base64)をフィールド(文字列)に変換
    @param base64bin: 画像(base64)
    @param p1: 左上の座標
    @param p2: 右上の座標
    @param p3: 右下の座標
    @param p4: 左下の座標
    @return: フィールド(文字列)
    """
    # base64→ndarray
    img = base64_to_ndarry(base64bin)

    # ホモグラフィ変換
    h_img = homography(img, p1, p2, p3, p4, 120, 240)

    # 6*12分割
    img_arr = split_grid(h_img, 6, 12)

    # AIで予測
    field_str = predict(img_arr)

    return field_str


モグラフィ変換処理。
以前はjavaでゴリゴリ実装したけどライブラリを使用すると数行で書けてしまう、超便利。

def homography(img, p1, p2, p3, p4, width, height):
    """
    ホモグラフィ変換
    @param img: 画像
    @param p1: 左上の座標
    @param p2: 右上の座標
    @param p3: 右下の座標
    @param p4: 右上の座標
    @param width: 変換後の幅
    @param height: 変換後の高さ
    @return: 変換後の画像
    """
    src = np.float32([p1, p2, p3, p4])
    dst = np.float32([[0, 0], [width, 0], [width, height], [0, height]])

    M = cv2.getPerspectiveTransform(src, dst)
    output = cv2.warpPerspective(img, M, (width, height))

    return output


AI処理。
前回作成したモデルを読み込み、画像をもとにぷよの色を予測。
予測結果をなんやかんや加工して文字列化、返却。

def predict(img_arr):
    """
    画像を元にフィールドを予測
    @param img_arr: 画像
    @return: フィールド予測結果
    """
    # 空の配列作成
    field_arr = ["0"] * 72

    # 作成済モデルの読み込み   // TODO サーバ起動時に読み込んで使いまわしたい
    path = os.path.join(os.getcwd(), "kerasmodel", "puyo_image_classification_model.hdf5")
    model = load_model(path)

    # モデルで予測
    X = img_arr.astype("float32") / 255.0
    predicts = model.predict(X)

    for i, p in enumerate(predicts):
        # 画像は12段目→1段目の順になっているので、1段目→12段目になるようにする
        x = i % 6
        y = 11 - int(i / 6)
        ii = x + y * 6

        color = p.argmax()
        if color == 6: color = 9    # おじゃまは6で返ってくるので9に変更
        field_arr[ii] = str(color)

    return "".join(field_arr)

実際に動かしてみた



期待通りに動きました。

あとがき

期待通りには動きました。
が、ただちょっとでもトリミング枠がずれると・・・



正しい結果になりません。
このあたりはもっと調べて知識つけないとだめそうですね・・・
フィールドの範囲を自動で判定してくれるようなプログラムが作れたらいいですが。


ぷよぷよのwebアプリも中途半端なまま放置しているので、一旦納得するところまで作り上げたいなあ~。