Java OpenCV 〜学習準備のまとめ〜

こちらのサイトを参考にしてOpenCVの基本から学習していこうと思います。

理由は、輪郭取得もよくわからなかったからです。。。

インストールに関しては、こちらの記事に記載しています。
ライブラリのダウンロードはこちらです。

javacv1.5.3の場合

下のようなライブラリを読み込むコードがいらなくなったようです。

/** ネイティブライブラリを読み込む */
    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

これをつけたままだと下のようなエラーが出ます。

Exception in thread "JavaFX Application Thread" Exception in thread "main" java.lang.UnsatisfiedLinkError: no opencv_java430 in java.library.path
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
    at java.lang.Runtime.loadLibrary0(Runtime.java:871)
    at java.lang.System.loadLibrary(System.java:1124)
    at zenryokuservice.opencv.fx.Main.<clinit>(Main.java:46)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplicationWithArgs$2(LauncherImpl.java:352)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$7(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$5(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$6(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$3(WinApplication.java:177)
    at java.lang.Thread.run(Thread.java:748)
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.NullPointerException
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:383)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    ... 5 more

しかし、これは必要な処理で、設定方法が間違っていました。参考サイトはこちらです。

  1. Window -> Preference(設定)

  2. opencvXXX.jarとネイティブライブラリのフォルダを設定します。

学習準備

今までの記事でOpenCVを学習する準備をしていました。

  1. Java OpenCV 〜背景除去、輪郭を学習する準備1: SceneBuilder〜
  2. Java OpenCV 〜背景除去、輪郭を学習する準備2コントロラー追加〜
  3. Java OpenCV 〜背景除去、輪郭を学習する準備3:画像を表示する〜
  4. Java OpenCV 〜背景除去、輪郭を学習する準備4:コマンドで起動する実装〜

余談ですが、こちらのやってきたことをまとめると以下のようになります。

そして、ポリモーフィズムの実装については以下のような形になります。

CommandIF(インタフェース)を使用したポリモーフィズムの実行動画があります。

準備の概要

  1. SceneBuilderを用いて、簡単に画面の土台を作成する
  2. 作成した土台を元に、コントローラーを追加
  3. 起動確認を兼ねて画像を表示する
  4. コントローラーから、コマンド的に新規に作成するプログラムを起動できる仕組みを作る

プログラムの解説

githubに作成したプログラムをアップロードしてあります。

この階層にある「Main.java」クラスがこのアプリを起動するクラスになります。

Main.java

/** ネイティブライブラリを読み込む */
static {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
/**
 * メインメソッド。
 * @param args
 */
public static void main(String[] args) {
    // 親クラス(Superクラス)のメソッド起動
    launch();
}

/* (non-Javadoc)
 * @see javafx.application.Application#start(javafx.stage.Stage)
 */
@Override
public void start(Stage primaryStage) throws Exception {
    primaryStage.initStyle(StageStyle.TRANSPARENT);
    FXMLLoader loader = new FXMLLoader(ClassLoader.getSystemResource("TestingCv.fxml"));
    BorderPane root = (BorderPane) loader.load();
    Scene scene = new Scene(root, 800, 600);
    scene.setFill(null);
    scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());

    // 作成するクラス
    final TestingCvController controller = loader.getController();
    controller.setPane(root);
    primaryStage.setOnCloseRequest((new EventHandler<WindowEvent>() {
        public void handle(WindowEvent we) {
            controller.setClosed();
        }
    }));
    xPos = 200;
    yPos = 200;
    // キーアクションを追加する
    scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent event) {
            if (event.getEventType() == KeyEvent.KEY_PRESSED) {
                try {
                keyHandle(event.getCode(), root, primaryStage, controller);
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    });
    primaryStage.setTitle("Video Processing");
    primaryStage.setScene(scene);
    primaryStage.show();
}

Mainプログラムの主な部分を抜粋しました。
シンプルに、mainメソッドからアプリケーションを起動します。

/** ネイティブライブラリを読み込む */
static {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

このstaticで囲んだ部分がJavaを起動した時に(はじめに)読み込まれます。これで、OpenCVのライブラリを読み込みます。

そして、以下のコードでFXMLをロード(読み込み)して作成した画面の土台を取得。

FXMLLoader loader = new FXMLLoader(ClassLoader.getSystemResource("TestingCv.fxml"));
BorderPane root = (BorderPane) loader.load();
Scene scene = new Scene(root, 800, 600);
scene.setFill(null);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());

// 作成するクラス
final TestingCvController controller = loader.getController();
controller.setPane(root);
primaryStage.setOnCloseRequest((new EventHandler<WindowEvent>() {
    public void handle(WindowEvent we) {
        controller.setClosed();
    }
}));

今回使用したFXMLファイルはTestingCv.fxmlです。

読み込んだFXMLで定義しているコントローラーTestingCvControllerクラスを取り出しています。このコントローラで

final TestingCvController controller = loader.getController();

SceneBuilderで作成した土台にあるコンポーネントのオブジェクトをコントローラークラスで管理します。
具体的には。。。

コントローラー

SceneBuilderで作成した画面が下の画像です。

この土台には、下のようなコンポーネントを追加しています。

そして、洗濯している部分は。。。

上のようにフォーカスされます。

こんな感じで、追加したコンポーンネントがコントローラーで操作することができます。

TestingCvController.java

public class TestingCvController {

    @FXML
    private Canvas testCanvasBefore;
    @FXML
    private Canvas testCanvasAfter;

    @FXML
    private TextField input;

    private Properties prop;

    private CommandIF cmd;

    private Pane pane;

    /** コンストラクタ */
    public TestingCvController() {
        this.testCanvasBefore = new Canvas();
        this.testCanvasAfter = new Canvas();
//      this.testing = new LearnOpenCv();
        this.prop = new Properties();
        String propPath = "/command.properties";
        try {
            this.prop.load(this.getClass().getResourceAsStream(propPath));
        } catch (IOException e) {
            System.out.println(">>> Error! プロパティファイルの読み込みに失敗しました。" + propPath);
            e.printStackTrace();
        }
        // 確認
        System.out.println("プロパティ: " + prop.get("hello"));
    }

    /**
     * 画面のExecuteボタンを押下した時に起動する処理
     */
    @FXML
    protected void clickExecute() throws Exception {
        // 初期化する
        cmd = null;
        // 入力確認用
//      System.out.println(this.input.getText());
        String inputStr = this.input.getText();
        cmd = this.getCommand(inputStr);
        if (cmd == null) {
            // プロパティファイルに、コマンドがない
            System.out.println("コマンドがあません。" + this.input.getText());
        } else {
            // コマンド実行
            cmd.execute(this.pane);
        }
    }

    @FXML
    public void setClosed() {
        // 現状は空実装
    }

    /**
     * Clearボタンを押下した時の処理
     */
    @FXML
    public void clear() {
        System.out.println("Clear");
        // 描画したものをクリアする
        this.testCanvasBefore.getGraphicsContext2D().clearRect(0, 0, this.testCanvasBefore.getWidth(), this.testCanvasBefore.getHeight());
        // 描画したものをクリアする
        this.testCanvasAfter.getGraphicsContext2D().clearRect(0, 0, this.testCanvasAfter.getWidth(), this.testCanvasAfter.getHeight());
    }

    @FXML
    private void terminated() {
        System.exit(0);
    }

    public void setPane(Pane pane) {
        this.pane = pane;
    }
}

上のコードでcode>@FXMLアノテーションのついているフィールドは以下のものです。

@FXML
private Canvas testCanvasBefore;
@FXML
private Canvas testCanvasAfter;

@FXML
private TextField input;

TestingCV.fxml

<TextField fx:id="input" promptText="App No" />
<Canvas fx:id="testCanvasBefore" height="200.0" width="200.0" />
<Canvas fx:id="testCanvasAfter" height="200.0" width="200.0">

上記の部分が上のクラスにあるcode>@FXMLをつけたフィールド変数と対応しています。

同様に。。。

<Button mnemonicParsing="false" onAction="#clickExecute" text="Execute" />
<Button mnemonicParsing="false" onAction="#clear" text="Clear" />
<Button mnemonicParsing="false" onAction="#terminated" text="Exit" />

の部分はcode>@FXMLのついたメソッドに対応しています。

動かして見ると、下のような感じです。(コーディング〜なので最後の方に表示した画面があります)



コメントを残す