JavaFX + OpenCV でウェブカメラを扱う GUI アプリを作り Gradle でセットアップした
Gradle を使ってセットアップした Java プロジェクトにて、JavaFX と OpenCV を組み合わせた GUI アプリを作ってみた。
目次
何ができるアプリか
今回作成したアプリは、ウェブカメラのプレビューが確認できるだけのアプリ。
JavaFX を使用して GUI ウィンドウを開き、PC に接続されているウェブカメラデバイスを特定して OpenCV の機能で映像をキャプチャする。キャプチャした映像からコマ画像を切り取り、JavaFX のウィンドウに描画しているというワケ。
このような Java アプリを Gradle でセットアップしていて、最終的にはアプリを単一 JAR ファイルにビルドしたりもできる。
ソースコード
ソースコード全量は以下。
コレを見て理解いただけるようなら、以降の説明は要らないかな。
動作確認環境
動作確認を行った環境は以下のとおり。
- 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
}
}
- 参考 : Going Native with Gradle · cjstehno/coffeaelectronica Wiki · GitHub
- 参考 : JavaFX 11 : Create a jar file with Gradle - Stack Overflow … 同様に
isLinux()
・isMacOsX()
なども確認できる
この文字は、エンコーディング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 を意識せず実装できていてよきよき。