Corredor

ウェブ、プログラミングの勉強メモ。

JavaFX + OpenCV でウェブカメラを扱う GUI アプリを作り Gradle でセットアップした

Gradle を使ってセットアップした Java プロジェクトにて、JavaFX と OpenCV を組み合わせた GUI アプリを作ってみた。

何ができるアプリか

今回作成したアプリは、ウェブカメラのプレビューが確認できるだけのアプリ。

JavaFX を使用して GUI ウィンドウを開き、PC に接続されているウェブカメラデバイスを特定して OpenCV の機能で映像をキャプチャする。キャプチャした映像からコマ画像を切り取り、JavaFX のウィンドウに描画しているというワケ。

このような Java アプリを Gradle でセットアップしていて、最終的にはアプリを単一 JAR ファイルにビルドしたりもできる。

ソースコード

ソースコード全量は以下。

github.com

コレを見て理解いただけるようなら、以降の説明は要らないかな。

動作確認環境

動作確認を行った環境は以下のとおり。

  • OS : Windows 10・MacOS Catalina・Ubuntu 18.04
  • JDK : v1.8.0_65
    • Java8 は JavaFX が同梱されている最後の Oracle JDK なので、簡単にするためコレを採用した
  • OpenCV : v3.2.0
    • OS 別の導入方法は以前の記事でも紹介したので参考にされたし

JDK 1.8.0_65 をダウンロードする

動作確認に使用した JDK は以下からダウンロードできる。

OS 別に以下のファイルをダウンロードしインストールした。

  • Windows : jdk-8u65-windows-x64.exe
  • MacOS : jdk-8u65-macosx-x64.dmg
  • Ubuntu : jdk-8u65-linux-x64.tar.gz

OpenCV をセットアップする

  • Windows は公式の opencv-3.2.0-vc14.exe を解凍し任意の場所に配置する
  • MacOS・Ubuntu ではソースコードからビルドして入手する
  • いずれも、JAR ファイル opencv-320.jar が入手できるはずなので、コレを ./lib/ ディレクトリ配下にコピーする
  • ネイティブライブラリは適宜 PATH を通しておくか、実行時にオプション引数で指定することになる

使い方

前述のリポジトリを git clone してもらったら、./lib/ ディレクトリ配下に opencv-320.jar を配置してもらう。ファイル名は同じでも OS により内容が若干異なるようなので、自分のマシンにインストールした OpenCV から取得するのが確実。

  • lib-mac/
  • lib-ubuntu/
  • lib-windows/

配下にある JAR は、手元で動作確認した時のファイルを参考までに格納しただけ。

OpenCV は JAR ファイルだけでなく、ネイティブライブラリも必要になる。ネイティブライブラリはパスを通してやれば動作するので、特にプロジェクトディレクトリ配下にファイルをコピーしたりする必要はない。build.gradle を確認してもらい、必要に応じて OpenCV ネイティブライブラリへのパスを設定すれば OK。

設定ができたら、

$ ./gradlew run

でアプリが起動できる。

JavaFX によるウィンドウが開き、「Start」「Stop」というボタンが見えていれば OK。

  • UnsatisfiedLinkError といったエラーが出てウィンドウが起動できなかった場合は、OpenCV のネイティブライブラリが見つけられなかった場合なので、build.gradle の設定を確認してもらったり、.dll or .dylib or .so ファイルの配置場所などを確認してもらいたい。

ウィンドウが開いたら、「Start」ボタンを押す。するとマシンに接続されているウェブカメラがキャプチャされ、プレビューが表示されるはずだ。

  • ウェブカメラが接続されていなかったり、上手くキャプチャが開始できなかった場合はワーニングが表示されるはず。
  • RootController.java にて VideoCapture#open(0) と実装している。引数の 0 はカメラデバイスの ID で、0 がマシンに最初に接続されているデフォルトのカメラとなる。Linux なんかだと /dev/video0 などで番号が確認できる。

内部的には OpenCV の Mat という形式で映像からフレーム (コマ画像) を取得しており、コレを JavaFX で使う Image 形式に変換して描画している。Mat イメージを取得した後、Image に変換するまでの間にアレコレ処理をすれば、キャプチャした画像に文字列や図形を描画してみせたり、画像をグレースケールに加工したりなどができるワケだ。

「Stop」ボタンを押せば、ウェブカメラのプレビューが閉じられる。

JAR ファイルにビルドする時は、

$ ./gradlew build

コマンドで ./build/libs/ 配下に JAR ファイルが生成される。

OpenCV のネイティブライブラリを同梱したりはしていないので、実行時に環境変数なりオプション引数なりで指定してやる必要がある。

# Windows の場合の例
$ java -Djava.library.path='C:\PATH\TO\opencv\build\java\x64' -jar ./build/libs/practice-javafx-opencv.jar

覚えたこと

Gradle・JavaFX・OpenCV と、個人的には経験のなかった技術スタックなので、色々と覚えることがあった。

Gradle + JavaFX までのところは以前 Tips 記事を書いたモノが流用できている。

OpenCV を使うにあたって、もしくは Windows で発生したエラー対応のため、調整したことがある。

$ ./gradlew run 実行時に OpenCV のパスを通す

MacOS や Linux の場合はソースコードからビルドするので、PATH の通った場所に OpenCV のネイティブライブラリが格納されるっぽいのだが、Windows では自分で PATH を通さないといけない。

環境変数 PATH で通しても良いが、build.gradle 内で設定する方法があったので書いておいた。以下は ./gradlew run 実行時に、Windows OS でのみ、Java 実行オプションを設定するというモノ。

run {
  // For Windows : Set OpenCV Native Library Path
  if(org.gradle.internal.os.OperatingSystem.current().isWindows()) {
    systemProperty "java.library.path", file("C:\\PATH\\TO\\opencv\\build\\java\\x64").absolutePath
  }
}

この文字は、エンコーディングMS932にマップできません エラーを回避する

Windows 環境の GitBash で ./gradlew run を実行したところ、

この文字は、エンコーディングMS932にマップできません

とかいうエラーが出た。

個人的には、MacOS などでは

export _JAVA_OPTIONS='-Dfile.encoding=UTF-8'

などと環境変数で設定しているので、Windows で試すまで気付かなかった。

コレも build.gradle にてエンコーディングを指定してやれば良い。

tasks.withType(JavaCompile) {
  // For Windows : Set Character Encoding
  options.encoding = 'UTF-8'
}

VSCode で OpenCV の自動補完が効かない

VSCode に導入した Java Extension Pack は、独自に .classpath ファイルを作り、コイツによって自動補完を実現している。Gradle の動作としては .classpath は不要なのだが、VSCode での開発状は .classpath ファイルをうまく設定してやらないと、自動補完が効かない。

OpenCV についても、./lib/opencv-320.jar を格納した後、.classpath ファイルに次のような記述をしてやらないと、import などの自動補完がされなくて手間なので、忘れず設定しておく。

<classpath>
  <!-- …中略… -->
  
  <!-- Enable To Reference OpenCV Library on VSCode -->
  <classpathentry kind="lib" exported="true" path="lib/opencv-320.jar">
    <accessrules>
      <accessible kind="accessible" pattern="**" />
    </accessrules>
  </classpathentry>
</classpath>

accessrules が大事。

以上

OpenCV って結構導入のハードル高いなーというのが率直な思い。Gradle も、Eclipse や Maven でチクチクやってた依存解決を別の DSL で覚え直さないといけない感じで、まぁまぁ面倒クセェなーと。npm (Node.js) のパッケージ管理システムが一番気楽だなーと思った。

しかし一方、ウェブカメラのキャプチャを高速に行いたかったりすると、やはりこうしたネイティブライブラリを組み合わせないといけないので、今回やってみた次第。

今回実装した範囲だと、ウェブカメラの映像をキャプチャしてそれをそのまま表示しているだけだが、Mat イメージが拾えているので、ココから映像をあれやこれや加工していく基礎部分が出来たと思うので、もう少し OpenCV を勉強してみる。

JavaFX は FXML での記述の他、独自の CSS も組み合わせられるので、SPA・フロントエンド開発に近い感覚でコンポーネントを作れると思った。FXML を使わず全てを Java 側で実装することもできて、コレをやると jQuery で HTML ソースをブチ込んでいた頃を思い出す。笑

いずれもクセがあるが、まぁまぁやりたいことはできた感。JavaFX なら OS 問わず GUI が構築できるし、裏で使う OpenCV も各 OS で動かせるようになったので、アプリとしては OS を意識せず実装できていてよきよき。

Javaで始めるOpenCV3プログラミング

Javaで始めるOpenCV3プログラミング