前回の続き
前回(https://qiita.com/tanakah/items/ee41c592cc4f2caaf56d) のツールでは、
ゲーム画像のチャット文字に特定文字列が表示されたかどうか(及びその位置)の検出方法として、
2値化した画像のパターン(高さ16ドットの列を2バイト=16bitで符号化)の辞書比較による探索でやっていました。
ゲーム画像ということで、フォント種類、文字幅が固定され、ノイズもないという緩い条件なので、
高速で確実に検出することができています。
ただ、この方法だと上下に1ドットでも探索ラインがずれると検出できないのですよね。
あらかじめ探索開始ラインを調整しておかないといけない。
そこでもうちょっと画像処理らしくやってみたいなと思っていました。
パターンマッチングへの拡張
要するに、今までは特定ラインだけ走査して相関値を求めていたのを、複数ラインに拡張すればいいだけです。
(時間がかかるから手抜きしてました^^;)
例えば5×5のガウシアンフィルタで画像全体を走査するように、
相関値を求めるフィルタで画像を走査して画素値を逐一求める処理します。
で、このような空間フィルタリングは周波数領域での演算と同等なわけで
(というか、周波数領域で意味を持った処理を時間領域に変換することによって、フィルタサイズに応じた係数が決まる)
フーリエ変換を使った演算で位置検出できることを確認してみたくなりました。
テスト
OpenCV2.4系でざっくり書いてみました。
IplImage *image_co, *image_re, *image_im;
IplImage *dict_co, *dict_re, *dict_im;
IplImage *temp_re, *temp_im;
IplImage *disp_gr;
...
//// DFT(1)
cvDFT(image_co, image_co, CV_DXT_FORWARD, dft_size);
{
cvSplit(image_co, image_re, image_im, NULL, NULL);
//
cvSplit(image_co, temp_re, temp_im, NULL, NULL);
cvPow (temp_re, temp_re, 2.0); cvPow(temp_im, temp_im, 2.0);
cvAdd(temp_re, temp_im, temp_re, NULL); cvPow(temp_re, temp_re, 0.5);
// normalize
cvDiv(image_re, temp_re, image_re); cvDiv(image_im, temp_re, image_im);
cvMerge(image_re, image_im, NULL, NULL, image_co);
}
//// DFT(2)
cvDFT(dict_co, dict_co, CV_DXT_FORWARD, dft_size);
{
cvSplit(dict_co, dict_re, dict_im, NULL, NULL);
//
cvSplit(dict_co, temp_re, temp_im, NULL, NULL);
cvPow (temp_re, temp_re, 2.0); cvPow(temp_im, temp_im, 2.0);
cvAdd(temp_re, temp_im, temp_re, NULL); cvPow(temp_re, temp_re, 0.5);
// normalize
cvDiv(dict_re, temp_re, dict_re); cvDiv(dict_im, temp_re, dict_im);
cvMerge(dict_re, dict_im, NULL, NULL, image_co);
}
cvMulSpectrums(image_co, dict_co, image_co, CV_DXT_MUL_CONJ);
//// IDFT
cvDFT(image_co, image_co, CV_DXT_INVERSE);
cvSplit(image_co, image_re, image_im, NULL, NULL);
mycvSwapQuadrant(image_re, image_re);
//// convert scale
double re_min=0.0, re_max=1.0;
CvPoint maxloc, minloc;
cvMinMaxLoc(image_re, &re_min, &re_max, &minloc, &maxloc, NULL);
Printf("maxpoint(%d,%d)\n", maxloc.x, maxloc.y);
cvConvertScale(image_re, disp_gr, 255.0/(re_max-re_min), 255.0*(-re_min)/(re_max-re_min));
結果
IDFT(DFT×DFT) 画像 (わかりにくいですが、"おそれ"の位置に輝点)
検出条件を絞っていったときにこういう方法もあるわな、と覚えておくことにします。
(ちなみに、入力画像の太字の"おそれ"が検出できないように、この方法だと拡大縮小には対応しないんですよね、
対応するためにはLogPolar変換使ったりするんでしょうけど、まぁそこまでいいわっていう)