JavaCV を使用して 三国志大戦4 解任済み武将カード の1単位を判定する

kameyatakefumi.hatenablog.com

こちらの続きとして 解任済み武将カード のリストから1単位を判定したい。
リストから1単位を判定できれば、あとは抽出して個別処理を行うだけになる。

最初は枠を判定して、そこからゴリゴリと座標計算して~みたいな事になるのかなぁと考えていた。
OpenCV ができる事を見ていると 輪郭検出 というワードがあり、調べてみると要件に非常に合っていたので、こちらで行う事にした。

キッカケとなったのは以下のサイト。

AVFoundation+OpenCVで矩形検出(「名刺撮影用カメラ」みたいなやつ作ってみました) | Developers.IO

サイトでは名刺を判定している。
これを 武将カード1単位 に置き換えできると思った。
画像処理に関しては右も左もわからないですが、処理に対して1つ1つ解説があって非常に助かりました。
感謝しかありません。

以下のサイトも非常に参考になった。

機械学習のためのOpenCV入門 - Qiita

全体と各工程が非常にわかりやすく解説されている。
処理をする意図がわかると理解が進むので助かります。

サイトの処理を参考にしつつ以下を作成して実行。

public class FindContours {

    public static void main(String[] args) {

        // 画像を読込
        Mat mat = imread("{画像パス}");

        // グレースケール化
        Mat gray = new Mat(mat.size(), CV_8UC1);
        cvtColor(mat, gray, COLOR_BGR2GRAY);

        // しきい値処理
        threshold(gray, gray, 0, 255, CV_THRESH_OTSU);

        // 輪郭検出
        MatVector contours = new MatVector();
        Mat hierarchy = new Mat();
        findContours(gray, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_TC89_L1);

        List<Mat> mats = new ArrayList<>();
        int max_level = 0;
        for (int i = 0; i < contours.size(); i++) {

            // ある程度の面積が有るものだけに絞る
            double area = contourArea(contours.get(i), false);
            if (area > 15000) {

                //輪郭を直線近似する
                Mat approx = new Mat();
                approxPolyDP(contours.get(i), approx, 0.01 * arcLength(contours.get(i), true), true);

                // 矩形のみ取得
                if (approx.size().area() == 4) {

                    // 1単位を収集
                    Rect rect = boundingRect(approx);
                    mats.add(new Mat(mat, rect));

                    // 輪郭を塗る
                    drawContours(mat, contours, i, Scalar.RED, 2, CV_AA, hierarchy, max_level, new Point());
                }
            }
        }

        imshow("Result", mat);
        waitKey(0);
        destroyAllWindows();
    }

}

以下、実行結果。
f:id:kameya_takefumi:20171101134526p:plain
素晴らしい!
1単位を判定しライン赤色でカッコっています。

1単位は見えた、あとはそこを抽出するだけだ。
f:id:kameya_takefumi:20171101135059p:plain
できた。
素晴らしい!

ただ課題もある。

以下は見切れているパターン。
f:id:kameya_takefumi:20171101140057p:plain
これに関しては諦めるかも。
個別処理用に特殊な事をする事になりそう。
対応するコストが高い気がしている。

以下はヘッダが判定されている。
f:id:kameya_takefumi:20171101140307p:plain
輪郭検出なので処理結果としては正しいんだけど 武将カード ではないので除外する必要がある。
ろ過作業みたいな事をしないといけない。

不要な情報の除去って輪郭検出の前と後、どちらでやるんだろうか。
状況にもよるんだろうけど、ここも考えていかないといけないね。