OpenCVをJavaから使う
2014 年 1 月 26 日追記; JavaCV のバージョンがあがって、使い方がもっとシンプルになりました。詳しくは新しい記事をご覧ください。
これまで、自前のライブラリで Web カメラから画像をとってきて、ARToolKitに渡してマーカー検出したりしていたのですが、画像処理関連の研究を始めたりして、そろそろ限界を感じるようになってきたので、Java からOpenCVの各機能が呼べるラッパーJavaCVを使ってみることにしました。
インストールおよび実行までの道のりが果てしなく遠い…かと思いきや、意外とすんなりできました。
64-bit Windows で JavaCV
- OpenCV のバイナリ版(OpenCV-*.exe, 自己展開 ZIP)をダウンロードして適当なディレクトリ(C:¥opencv など)に解凍
- Visual C++ 2010 のランタイムをインストール
- 環境変数 PATH を編集してパスを.¥build¥common¥tbb¥intel64¥vc10 と.¥build.¥x64¥vc10¥bin に通す
- JavaCV(javacv-*-bin.zip)をダウンロードして適当なディレクトリ(C:¥opencv¥javacv など)に解凍
- コンパイル、実行するときの Java のクラスパスに javacpp.jar, javacv.jar, javacv-windows-x86_64.jar を追加
64-bit Mac OS X で JavaCV
- MacPortsをインストール
- コマンドラインで sudo port install opencv +python27 を実行
- JavaCV(javacv-*-bin.zip)をダウンロードして適当なディレクトリ(/Users/*/lib/javacv など)に解凍
- コンパイル、実行するときの Java のクラスパスに javacpp.jar, javacv.jar, javacv-macosx-x86_64.jar を追加
以上です。
どちらの場合でも注意すべき内容としては、Java VM、OpenCV のビット数が合っていなくてはなりません。僕の環境では、古い研究プロジェクトを動かすため、Java VM が 32 ビットで起動するよう-d32 オプションをつけていました。ところが、OpenCV が 64 ビット版だったため、プログラムを走らせようとすると "java.lang.UnsatisfiedLinkError: Library jniopencv_core not found" というエラーが出てしばらく困りました。
JavaCV が正しくインストールできると、Web カメラから画像を読みとってウィンドウに表示するプログラムが、次のように簡単に書けます。
CaptureImage.java
import javax.swing.JFrame;
import com.googlecode.javacv.CanvasFrame;
import com.googlecode.javacv.OpenCVFrameGrabber;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
public class CaptureImage {
public static void main(String[] args) {
new CaptureImage().loop();
}
private void loop() {
CanvasFrame canvas = new CanvasFrame("Webcam");
canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// デフォルト(0)のWebカメラを使う
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);
try {
grabber.start();
// フレームレートを取得
double frameRate = grabber.getFrameRate();
long wait = (long) (1000 / (frameRate == 0 ? 10 : frameRate));
// 画像を取りつづける
while (true) {
Thread.sleep(wait);
IplImage image = grabber.grab();
// 取ってきた画像を画面に表示
if (image != null) { canvas.showImage(image); }
}
// 何かあったらエラーを吐いて終わる
} catch (Exception e) {
e.printStackTrace();
}
}
}