イントロダクション
前回は、写真などを表示しようと試みましたがImShowメソッドが使用できないということで、JavaFXを使用してやる方向にしました。
参考サイト;Java版OpenCVチュートリアル(英語です)
<工程>
- Eclipseのインストール
- OpenCVインストール
- JavaでOpenCVのライブラリを起動する(工程2の記事でやりました)
- JavaFXで写真を表示してみる(今回実行します)
画像(イメージ)表示の準備
こちらのサイトを参考にしてやります。
大雑把に上記の<工程>にあるようなことが書いてありました。今までに<工程>の1、2をやったので3からいきます。
<前提>
JavaFXでの画面作成のためにSceneBuiderをセットアップします。
JavaFXの実装
ソースコードはGITにあるものを参照(参考サイトより)
<今回の作成するアプリ構成>
ソースはGitにアップロードしてあります。
<Main.java>
import org.opencv.core.Core; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; import javafx.fxml.FXMLLoader; /** * OpenCVnogaの学習用のGUIを作成する。 * JavaFXで画面を作成している * 1.SceneBuilderでFXMLを出力 * 2.FXMLで取得したコンポーネントを表示する * * @author takunoji * 2018/11/18 */ public class Main extends Application { /** * このアプリケーション(Mainメソッド)が起動する前に * 起動(OpenCVのロード)する */ static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } /** * スーパクラスApplication)のstartメソッドを起動する * オーバーライドしているので、本当は親クラスのメソッドが起動するが * 子クラスで上書きするので、このクラスを起動した時にはこちらのメソッドが起動する。 */ @Override public void start(Stage primaryStage) { try { // FXMLをロード BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("OpenCvTest.fxml")); // 表示領域を作成する Scene scene = new Scene(root,400,400); // JavaFX用のCSSを適用する scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); // Sceneをステージに設定する primaryStage.setScene(scene); // 表示処理 primaryStage.show(); } catch(Exception e) { e.printStackTrace(); } } /** * メインメソッド。 * JavaFXの規定部品 * Application{@link #start(Stage)}を起動する * * @param args プログラム引数 */ public static void main(String[] args) { // スーパークラス(親)のメソッドを起動する launch(args); } }
<OpenCvController.java>
/** * FXMLで定義したコントローラクラス。 * 参考サイトを写経しようとしたが、ソースを少しいじる * 基本的には写経している。 * @see * @author takunoji * 2018/11/17 */ public class OpenCvController { /** FXML定義のボタン */ @FXML private Button button; /** FXML定義のImageViewer */ @FXML private ImageView currentFrame; /** ビデオキャプチャ */ private VideoCapture capture = new VideoCapture(); private ScheduledExecutorService timer; // a flag to change the button behavior private boolean cameraActive = false; // the id of the camera to be used private static int cameraId = 0; /** * ボタン押下時のアクション処理。 * "@FXML"アノテーションでFXMLとの同期を取っている * @param event ボタン押下のイベント */ @FXML protected void startCamera(ActionEvent event) { // カメラがアクティブ状態の時は停止する if (this.cameraActive) { this.cameraActive = false; this.button.setText("Start Camera"); this.stopAcquisition(); // 処理終了 return; } // カメラ this.capture.open(this.cameraId); // カメラが開いていない時 if (this.capture.isOpened() == false) { // エラーログを出力して処理を終了する System.err.println("Impossible to open the camera connection..."); return; } // カメラが正常に開いている時 this.cameraActive = true; // 匿名クラス Runnable frameGrabber = new Runnable() { @Override public void run() { Mat frame = grabFrame(); Image imageToShow = Utils.mat2Image(frame); updateImageView(currentFrame, imageToShow); } }; this.timer = Executors.newSingleThreadScheduledExecutor(); this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS); this.button.setText("Stop Camera"); } /** * 開いているビデオストリームからフレームを取得する * @return {@link Mat} */ private Mat grabFrame() { Mat frame = new Mat(); if (this.capture.isOpened()) { try { this.capture.read(frame); if (frame.empty() == false) { Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY); } } catch(Exception e) { System.err.println("Exception during the image elaboration: " + e); } } return frame; } private void stopAcquisition() { if (this.timer != null && this.timer.isShutdown() == false) { try { this.timer.shutdown(); this.timer.awaitTermination(33, TimeUnit.MICROSECONDS); } catch(Exception e) { // log any exception System.err.println("Exception in stopping the frame capture, trying to release the camera now... " + e); } } // @FIXME-[カメラを解放するだけで良い?] if (this.capture.isOpened()) { this.capture.release(); } } /** * Update the {@link ImageView} in the JavaFX main thread * * @param view * the {@link ImageView} to update * @param image * the {@link Image} to show */ private void updateImageView(ImageView view, Image image) { Utils.onFXThread(view.imageProperty(), image); } /** * On application close, stop the acquisition from the camera */ protected void setClosed() { this.stopAcquisition(); } }
ボタンを押下したらエラーが出た!
下のキャプチャのようにbottunの変数名とIDが違っていると次のようなエラーが出ます。
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
<BorderPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="zenryokuservice.opencv.fx.OpenCvController"> <center> <BorderPane prefHeight="522.0" prefWidth="410.0" BorderPane.alignment="CENTER"> <bottom> <Button fx:id="start_btn" mnemonicParsing="false" onAction="#startCamera" text="StartCamera" BorderPane.alignment="CENTER" /> </bottom> <center> <ImageView fx:id="currentFrame" fitHeight="248.0" fitWidth="234.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" /> </center> </BorderPane> </center> </BorderPane>
上のコード赤字の部分を変数名に合わせます。「button」に修正。
[rakuten ids="0909fd:10003689"]
<問題>
このVideoCaptureを起動し、閉じるときにメモリの使用量の部分(そのほかにもあるかも?)で問題があります。
ソースの方では、カメラを停止したときに「System.gc()」でメモリを解放するようにしたのですが、操作を早くするとクラッシュします。
この部分は、根深い問題なのでとりあえずは、飛ばしてOpenCVを学んでいくようにします。。。
エラーログ
# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007fffcfe13017, pid=1748, tid=0x000000000001560f # # JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode bsd-amd64 compressed oops) # Problematic frame: # C [libobjc.A.dylib+0x7017] objc_msgSend+0x17 # # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again # # An error report file with more information is saved as: # /XXX/git/OpenCvFX/OpeCvFX/hs_err_pid1748.log # # If you would like to submit a bug report, please visit: # http://bugreport.java.com/bugreport/crash.jsp # The crash happened outside the Java Virtual Machine in native code. # See problematic frame for where to report the bug. #
関連ページ一覧
<JavaFXの準備>