« HSPでOpenCV試し Main WordPress: memolinkプラグイン... »

JavaでOpenCV

似たような記事が続きますが…(^^;。

JavaからOpenCVにアクセスするOPENCV \ libraryを使って顔認識を行なう。こちらはJNIの仕組みを使ったJava用のブリッジライブラリ。(基本的にはJavaをベースにしたビジュアル言語Processing(と言っていいのかしら…)用のライブラリとして作成されているらしい。アクセスURLにprocessingとあるのはこのため。このライブラリのタイトルはわかりにくい(というかせめて「OpenCV Java library」あたりにして欲しい…)が、ページ中の記述では「OpenCV Processing Library」となっている箇所もあるのでページタイトルで抜けてるだけかも)。

山本さんのところのプログラムでは、起動時の引数で指定された画像ファイルを読み込んで顔認識している。これはうまく動作するんだが、上の配布元ubaa.netで提供されているカメラ画像の取り込みサンプルを実行するとなぜか停止してしまう(jdk-1.6.0_11+Windows Vista Home SP1だから?)。ちらっと検索しつつ試していると、スレッドのrun()中で呼び出しているcv.read()で動作が停止する。JavaとOpenCV(あるいはブリッジライブラリ)のどちらが悪いのかわからんが同じような現象の人もいるらしい。

どうもキャプチャ開始のcv.capture()もrun()の中にないと、うまく動作しないようだ。ということで、スレッドを使う場合はcv.stop()へのアクセスなどもrun()(または、そこから呼ばれるメソッド)中にないとダメらしい。ここまで判明するのにえらい時間を食ってしまった。

作成した Java プログラムはこちら↓

  1.  import java.util.*;
  2.  import java.awt.*;
  3.  import java.awt.event.*;
  4.  import java.awt.image.MemoryImageSource;
  5.  import javax.swing.*;
  6.  import hypermedia.video.OpenCV;
  7.  
  8.  public class FaceDetection extends JFrame implements Runnable {
  9.          OpenCV cv;
  10.          boolean cv_thread = false;
  11.          Thread t;                    // 画像取り込みスレッド
  12.         
  13.          Image frame;                // 取り込み画像
  14.          Image b_img;                // ダブルバッファイメージ
  15.          Rectangle[] squares = new Rectangle[0]; // 検出された顔の領域情報
  16.  
  17.          final int FRAME_RATE  = 1000/30;        // 更新時間
  18.  
  19.          FaceDetection() {
  20.                  setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // 終了時の設定(dispose() が呼ばれる)
  21.  
  22.                  cv = new OpenCV();
  23.  
  24.                  // start running program
  25.                  t = new Thread( this );
  26.                  t.start();
  27.          }
  28.   
  29.          public void run() {
  30.                  // キャプチャ用意
  31.                  cv.capture( 320, 240 ); // ←cv.read()と同じメソッド内にないとダメ?(run() 内から呼び出せば別メソッドにあっても大丈夫らしい)
  32.  
  33.                  // 画面サイズ設定
  34.                  setBounds(50, 50, cv.width, cv.height );
  35.                  setVisible( true );
  36.  
  37.                  // ダブルバッファの生成
  38.                  b_img=createImage( cv.width, cv.height );
  39.                  Graphics b_g=b_img.getGraphics();
  40.  
  41.                  // 処理時間計測用
  42.                  long t_start, t_end, t_tpf=0;
  43.                  float t_fps=0;
  44.  
  45.                  // スレッド処理開始
  46.                  cv_thread=true;
  47.                  cv.cascade( "C:\\Program Files\\OpenCV\\data\\haarcascades\" + OpenCV.CASCADE_FRONTALFACE_ALT2 ); // 顔認識データ読み込み
  48.                  while( t!=null && cv!=null && cv_thread ) {
  49.                          t_start=(new Date()).getTime();
  50.                   
  51.                          cv.read(); // 画像取り込み
  52.                          frame = createImage(new MemoryImageSource( cv.width, cv.height, cv.pixels(), 0, cv.width)); // 取り込み画像の取得
  53.                          squares = cv.detect( 1.2f, 2, OpenCV.HAAR_DO_CANNY_PRUNING, 20, 20 ); // 顔検出と結果情報取得
  54.                          setTitle(String.format("%.1ffps (%dms): face %d", t_fps, t_tpf, squares.length));
  55.                   
  56.                          synchronized (b_img) { // 顔認識結果の描画
  57.                                  b_g.drawImage(frame, 0, 0, this);
  58.  
  59.                                  // draw squares
  60.                                  b_g.setColor( Color.RED );
  61.                                  for (Rectangle rect : squares) b_g.drawRect( rect.x, rect.y, rect.width, rect.height );
  62.                          }
  63.                          t_end=(new Date()).getTime();
  64.  
  65.                          t_tpf=t_end-t_start;
  66.                          t_fps=1000f/t_tpf;
  67.                   
  68.                          repaint(); // 再描画
  69.                                 
  70.                          try {t.sleep( FRAME_RATE );} catch(InterruptedException e) {}
  71.                  }
  72.                  cv.stop(); // ←cv.read()と同じメソッド内にないとダメ?
  73.                  cv=null;
  74.          }
  75.         
  76.          public void paint( Graphics g ) {
  77.                  if (b_img!=null) synchronized (b_img) {g.drawImage(b_img, 0, 0, this);}
  78.          }
  79.  
  80.          // ウィンドウ終了処理
  81.          public void dispose(){
  82.                  int count=0;
  83.           
  84.                  cv_thread=false; // cv_thread を false にして cv スレッドの終了を待つ
  85.                  while (cv!=null) {
  86.                          try {t.join(500);} catch(InterruptedException e) {}
  87.                          if (count==1) System.out.print("OpenCV closing.");
  88.                          if (count>1) System.out.print(".");
  89.                          count++;
  90.                  }
  91.                  if (count>1) System.out.println();
  92.                  System.exit(0);
  93.          }
  94.         
  95.          public static void main( String[] args ) {
  96.                  new FaceDetection();
  97.          }
  98.  }

他にも自前でJNIを使ってJavaからOpenCVを使おうとしている人もいる。

なお、今回のライブラリは、OpenCV-1.0用に作成されているとのことで、まだ最新の1.1aなどでは使用できないらしい。必要な処理が呼べない場合は、自前でJNI経由でOpenCVを使うのもいいかも?

JNIは Java Native Interface の略ですが、JNA (Java Native Access) というものもあり、さらにネイティブライブラリにアクセスしやすくなっているようです。遅いとの話もちらっと見かけましたが、JNIで必要だった途中のラッパーが不要になるようなので、こちらを試してみるのもいいかもしれません。

それと、Processingはデータ可視化で便利そうなので、機会があれば試しておきたい。JavaScriptからも使えるようになっているとのこと。

Leave a comment

Your comment