前回投稿からかなり間が空いてしまいましたが、勉強の区切りがついたのと、投稿できるネタが出来ました。今回は、JavaからJNIを介してC++をCallする、Sampleプログラムを作ってみた。
いろんな環境
・プラットフォーム:Ubuntu,Fedora,Windows
・コンパイラ :MSVC(cl.exe,link.exe),g++,clang
でインプリメントを試してみたのですが、MSVCのCLI環境で動作したのでこれを紹介します。
今回の投稿までに書籍とググりとChatGPT達のお力を色々とお借りしました。この過程でその素晴らしさを痛感しましたし、噂ほどでもないことに落胆もしました。ChatGPT達に質問すれば、三者三様の統一感のない回答が返って来ます。しかし、そこから問題解決に向けての探検が始まりました。問題解決にに向けてChatGPT達は、まるで共同開発者同様に振舞ってくれます。議論を重ねて問題解決に至るのです(質問を重ね、自分自身で本当に正しい答えを導き出す)。今まで一人でコツコツ調べるスタイルから共同開発者と議論しながらのスタイルへ変化しました。ワークスタイルが激変したと言っても良いかもしれません。
あくまで私見ですが、今まで以上の成果レベルを上げることが出来たと思っています。
【参考書籍】
「JNI JavaNativeInterfaceプログラミング」
1.プログラム概要
(1)Main.javaからAClassWithNativeMethods.javaを介してtheNativeMethod.cをコールしている。
(2)theNativeMethod.cでは、Sample.avi( 動画ファイル)を再生する。
※動画ファイルは、各自準備されたい。
2.事前準備
(1) Opencvがインストールされていること。(今回は4.8.1 for windowsを使用)
(2) Visual Studio 2022がインストールされていること。(今回は2022をを使用)
(3)Javaがインストールされていること。
注意事項
・jni.h、jni_md.hのインクルードはjavaのJDKのものを使うこと(AndroidのNDKのものを使わないこと)
・windowsでの共有ライブラリは、.dll、linuxでは、.soとなる
3.ビルド手順
(1) nativeメソッドを宣言するJavaクラスファイルAClassWithNativeMethods.javaを生成したらそのメソッドのためのANCI C関数プロトタイプAClassWithNativeMethods.hを生成
PS > javac -h Header AClassWithNativeMethods.java
(Headerディレクトリ配下に、AClassWithNativeMethods.hを生成)
(2) コンパイル
コンパイル環境としては、ターミナルでVisual Studio 2022 Developer Command Promptを開いてソースファイル等のあるディレクトリに移動して実行すれば、MSVCのパスなどの環境ができているので簡単です。(3)項も同様。
PS > javac Main.java
PS > javac AClassWithNativeMethods.java
PS > cl /I C:\Progra~1\Java\jdk-21\include /I C:\Progra~1\Java\jdk-21\include\win32 /I C:\opencv\opencv481\build\include /c /EHsc theNativeMethod.cpp
【参考】
上記コマンドラインから”/EHsc”を外すと、以下warningが出る
C:\Progra~1\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\ostream(419): warning C4530: C++ 例外処理を使っていますが、アン ワインド セマンティクスは有効にはなりません。/EHsc を指定してください。
(3) リンク
PS > link /DLL /OUT:libNative.dll theNativeMethod.obj /LIBPATH:C:\opencv\opencv481\build\x64\vc16\lib opencv_world481.lib
4.プログラムの実行
PS > java Main
※Sample.aviの動画が再生されます。
5.ソース一覧
AClassWithNativeMethods.h
/* DO NOT EDIT THIS FILE – it is machine generated */
#include <jni.h>
/* Header for class AClassWithNativeMethods */
#ifndef _Included_AClassWithNativeMethods
#define _Included_AClassWithNativeMethods
#ifdef __cplusplus
extern “C” {
#endif
/*
* Class: AClassWithNativeMethods
* Method: theNativeMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_AClassWithNativeMethods_theNativeMethod(
JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
theNativeMethod.cpp
#include <stdio.h>
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include “jniAClassWithNativeMethods.h”
using namespace std;
using namespace cv;
JNIEXPORT void JNICALL Java_AClassWithNativeMethods_theNativeMethod
(JNIEnv* env, jobject thisObj) {
string filepath = “Sample.avi”;// 動画ファイルのパスの文字列を格納するオブジェクトを宣言する
VideoCapture video;// 動画ファイルを取り込むためのオブジェクトを宣言する
video.open(filepath);// 動画ファイルを開く
if (video.isOpened() == false) {
return; // 動画ファイルが開けなかったときは終了する
}
Mat image;// 画像を格納するオブジェクトを宣言する
//動画を再生する
while (1) {
video >> image; // videoからimageへ1フレームを取り込む
if (image.empty() == true)
break; // 画像が読み込めなかったとき、無限ループを抜ける
imshow(“showing”, image); // ウィンドウに動画を表示する
int key = waitKeyEx(20); // キー入力を待つ
if (key == ‘q’)
break; //qを押すと終了
}
return;
}
AClassWithNativeMethods.java
public class AClassWithNativeMethods {
public native void theNativeMethod();
public void aJavaMethod() {
theNativeMethod();
}
}
Main.java
public class Main {
static {
System.loadLibrary(“libNative”);
}
public static void main(String[] args) {
AClassWithNativeMethods c = new AClassWithNativeMethods();
c.aJavaMethod();
}
}