こちらの続きとして 解任済み武将カード のリストから1単位を判定したい。
リストから1単位を判定できれば、あとは抽出して個別処理を行うだけになる。
最初は枠を判定して、そこからゴリゴリと座標計算して~みたいな事になるのかなぁと考えていた。
OpenCV ができる事を見ていると 輪郭検出 というワードがあり、調べてみると要件に非常に合っていたので、こちらで行う事にした。
キッカケとなったのは以下のサイト。
AVFoundation+OpenCVで矩形検出(「名刺撮影用カメラ」みたいなやつ作ってみました) | Developers.IO
サイトでは名刺を判定している。
これを 武将カード1単位 に置き換えできると思った。
画像処理に関しては右も左もわからないですが、処理に対して1つ1つ解説があって非常に助かりました。
感謝しかありません。
以下のサイトも非常に参考になった。
全体と各工程が非常にわかりやすく解説されている。
処理をする意図がわかると理解が進むので助かります。
サイトの処理を参考にしつつ以下を作成して実行。
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(); } }
以下、実行結果。
素晴らしい!
1単位を判定しライン赤色でカッコっています。
1単位は見えた、あとはそこを抽出するだけだ。
できた。
素晴らしい!
ただ課題もある。
以下は見切れているパターン。
これに関しては諦めるかも。
個別処理用に特殊な事をする事になりそう。
対応するコストが高い気がしている。
以下はヘッダが判定されている。
輪郭検出なので処理結果としては正しいんだけど 武将カード ではないので除外する必要がある。
ろ過作業みたいな事をしないといけない。
不要な情報の除去って輪郭検出の前と後、どちらでやるんだろうか。
状況にもよるんだろうけど、ここも考えていかないといけないね。