OpenCVとjavaCVで画像比較を目論む 1
自分で画像比較用のロジックをちまちま作っていたのだけど(色ヒストグラム系)、常識的に考えて自分で作るものではなかろうということで、いまさらながらOpenCVに手を出しました。
が、ネット上に転がっているのは、顔認識系、物体検知系がほとんどで単純な画像比較がなかったので、そのメモです。
あと、最初に断っておくと、まだ未完成で実用には耐えません。
インストール
下記の3点をインストールします。
- OpenCV本体 http://sourceforge.net/projects/opencvlibrary/files/opencv-doc/Tutorials,%20Presentations/
- JavaCV http://code.google.com/p/javacv/
- Java Native Access 3.2.7 https://jna.dev.java.net/servlets/ProjectDocumentList?folderID=7408&expandFolder=7408&folderID=0
コード
まず、よく使うっぽいimport文
これからのコードは全部、下記importがあることが前提になってます。
import static com.googlecode.javacv.jna.cxcore.*; import static com.googlecode.javacv.jna.cv.*; import static com.googlecode.javacv.jna.highgui.*; import static com.googlecode.javacv.jna.cvaux.*; import com.googlecode.javacv.CanvasFrame; import com.googlecode.javacv.jna.cxcore.CvMat; import com.googlecode.javacv.jna.cxcore.CvPoint2D32f; import com.googlecode.javacv.jna.cxcore.CvRect; import com.googlecode.javacv.jna.cxcore.IplImage; import com.googlecode.javacv.jna.cxcore.CvSize.ByValue; import com.sun.jna.ptr.DoubleByReference;
で、基本となるイメージのロード
public static IplImage loadImage(String path) { IplImage image = cvLoadImage(path, CV_LOAD_IMAGE_COLOR); if (image == null) { throw new IllegalArgumentException(path); } return image; }
イメージの表示。
public static void showImg(IplImage img, int time, String name) { CanvasFrame canvas = new CanvasFrame(name); canvas.showImage(img); sleep((long) time); canvas.dispose(); }
イメージのリサイズ。
public static IplImage resize(IplImage image, float f) { // TODO 元画像からチャネルの取り方がわからない・・・ IplImage cvCreateImage = cvCreateImage(new ByValue( (int) (image.width * f), (int) (image.height * f)), image.depth, 3); cvResize(image, cvCreateImage, 0); return cvCreateImage; }
で、肝心の画像比較。
一致部分の検出をそこを枠でくくるコード。
public static IplImage matchTemplateAndRectangle(IplImage image, IplImage template) { DoubleByReference min_val = new DoubleByReference(); DoubleByReference max_val = new DoubleByReference(); CvPoint min_loc = new CvPoint(); CvPoint max_loc = new CvPoint(); ByValue size = cvSize(image.width - template.width + 1, image.height - template.height + 1); IplImage dest_img = cvCreateImage(size, IPL_DEPTH_32F, 1); cvMatchTemplate(image, template, dest_img, CV_TM_CCORR_NORMED); cvMinMaxLoc(dest_img, min_val, max_val, min_loc, max_loc, null); IplImage clone = image.clone(); cvRectangle(clone, max_loc.byValue(), cvPoint(max_loc.x + cut.width, max_loc.y + cut.height), CV_RGB(255, 0, 0), 3, 1, 0); return clone; }
結果
問題
上記だけ見ますと問題なく抽出されているように見えますが、実は実用には大きな問題有ります。
上記のコードで比較できるのは、「比較画像」が「対象画像」より切り出されたものである必要がある模様です。
自炊した書籍の表紙と、アマゾンより取得した表紙画像では全然マッチングできませんでした。
今後はヒストグラム生成して比較するタイプのメソッドを試す予定です。
tips
JavaCVは、JavadocがないためC++等のOpenCVのドキュメントを睨みながら使い方を推測することになります。
ってことで、参考にした中でよかったサイトの紹介です。
下記のサイトが一番目的別のサンプルが充実していてよかったです。