今度研究室でOpenCV勉強会というのをすることになったのですが、そのネタにと思い、id:yaneurao:20090802 を参考にはてなの画像認証をクラックしてみました。CAPTCHAの画像をHTMLを解析して自動収集するところから書き始めたりして、全然30分では終わらず3時間ぐらいかかりましたが、結果的にそこそこの精度で認識できました。
なお、一応断っておくと、今回の目的はあくまで画像認識であり、これを使って本当にスパムコメントを送ったりする意図はありません。とはいえ、こういうセキュリティに立ち向かうようなコードを書くのってなぜか楽しいし妙にがんばってしまいます。
はてなのCAPTCHAの場合だと、CAPTCHA画像に含まれる文字以外の雑音をほぼすべて除去できるため、雑音除去後の画像をそのままテンプレートとすれば限りなく100%に近い精度で認識可能だと思われますが、自分の大学院での研究と絡めて、今回の実装ではフォントからテンプレート画像を作成しました。いろいろ試した結果、Lucida Console でフォントサイズ24が一番適合率が良さそうでした。0を6と誤認識するのがまだ問題ですが、そのほかはほぼ問題なしです。
あまりコードを載せるとちょっとあれかもしれないので、テンプレートマッチングの部分だけ、こんな感じというのを載せておきます。
srcが原画像、templateがあらかじめフォントから作っておいた0〜9, a〜fの文字のテンプレート画像、posが左からの現在のマッチング位置を示す0~5の数値です。templateとposをforでまわしながらこのメソッドを呼んで、一番適合率が良いところを探索します。
/// <summary> /// テンプレートマッチング /// </summary> /// <param name="src"></param> /// <param name="template"></param> /// <param name="pos"></param> /// <returns></returns> double MatchTemplate(IplImage src, IplImage template, int pos) { CvRect roi = new CvRect((18 * (pos + 1)) - 15, 0, 30, src.Height); using (IplImage part = GetPartialImage(src, roi)) { CvSize dstSize = new CvSize(part.Width - template.Width + 1, part.Height - template.Height + 1); using (IplImage dst = new IplImage(dstSize, BitDepth.F32, 1)) { Cv.MatchTemplate(part, template, dst, MatchTemplateMethod.CCoeffNormed); double minVal, maxVal; Cv.MinMaxLoc(dst, out minVal, out maxVal); return maxVal; } } } /// <summary> /// 指定したROIの部分を切り取った画像を新たに生成する /// </summary> /// <param name="src"></param> /// <param name="roi"></param> /// <returns></returns> IplImage GetPartialImage(IplImage src, CvRect roi) { src.SetROI(roi); IplImage part = new IplImage(roi.Size, BitDepth.U8, 1); Cv.Copy(src, part); src.ResetROI(); return part; }
追記
0を6と誤認識する問題は、そもそも0のテンプレート画像を用意していなかったというひどいバグでした。なにはともあれ、これで100%の精度を達成できました。
OpenCV 2 プログラミングブック OpenCV 2.2/2.3対応
- 作者: OpenCV 2 プログラミングブック制作チーム
- 出版社/メーカー: マイナビ
- 発売日: 2011/12/27
- メディア: 単行本(ソフトカバー)
- 購入: 2人 クリック: 61回
- この商品を含むブログ (9件) を見る