こんにちは、リコーの@roohii_3です。
最近は Python+OpenCV で画像処理をしていることが多いです。
昨年までは C++ メインで、とあるシステムの設計・開発をしてました。
※本記事の続編は以下に書きました↓
THETAプラグインで画像処理【FastCV画像処理編】
※[2019/01/08 追記] OpenCV編は以下↓
THETAの中でOpenCVを動かす
はじめに
2018年11月現在のRICOH THETAの最新機種、RICOH THETA Vでは「プラグイン」機能を使ってTHETAをカスタマイズすることができます。
さらに、「RICOH THETAプラグインパートナープログラム」に登録するとプラグインを作ることもできます。
パートナープログラムについては、記事の一番最後をご覧ください。
というわけで、 Android開発は初めて・”NDK”というキーワードも初耳 な私が、プラグイン開発にチャレンジしてみました。
日頃画像処理をすることが多いので、何らかの画像処理をするプラグインを作ってみたいと思います。
タイトルにあるように、画像処理ライブラリには FastCV を使いました。
内容が盛りだくさんなので、今回は 「FastCVの導入~簡単なAPIを使うまで」 を扱います。
実際にFastCVで画像処理する際の手順は、次回記事にまとめたいと思います。
FastCVとは
FastCV は、Qualcomm社 が提供している Computer Visionライブラリ です。
Qualcomm社が独自のライセンスで提供しています1。
画像処理に関わる演算がARMプロセッサ向けに最適化されており、Qualcomm製の Snapdragon プロセッサにおいては特に効果を発揮します。
FastCVを使うには、Android NDK(Native Development Kit)を用いてC/C++でプログラミングする必要があります。
なぜFastCV?
画像処理ライブラリといえば OpenCV を思い浮かべる方が多いかと思います。
"OpenCV for Android SDK"を用いればOpenCVをプラグインで使えないこともないのですが、RICOH THETA Vではメインプロセッサとして Snapdragon を採用しているため、ここでは FastCV を使ってみたいと思います。
※[2019/01/08 追記] OpenCV編を書きました↓
THETAの中でOpenCVを動かす
APIリファレンス
ドキュメントは Qualcomm FastCV Library にあります。
基本的な処理をはじめ、特徴点抽出 や クラスタリング、機械学習 などのモジュールも用意されているようですね。
環境
本記事で紹介する手順は、以下の環境で確認しています。
- RICOH THETA V firmware ver. 2.50.1
- RICOH THETA Plug-in SDK ver. 1.0.1
- Windows 7 Professional 64bit
- AndroidStudio ver. 3.2.1
- NDK ver. 18.1.5063045
- FastCV ver. 1.7.1
下準備
NDK開発環境を整え、FastCV SDKをインストールします。
開発環境の構築
下記リンク等を参考に、AndroidStudioの導入とプラグイン開発環境を整えておいてください。
- 【THETAプラグイン開発】THETAを開発者モードにする手順
- 【THETAプラグイン開発】THETAでRICOH THETA Plug-in SDKを動かす方法
- Ricoh THETA V Plugin Application Development Community Guide
- AndroidStudio
NDKのインストール
- AndroidStudioを起動し、メニューから”Tools > SDK Manager”を選択
- ”SDK Tools”タブを選択し、”NDK” にチェックを入れて"OK"をクリック
- 画面に従って進め、インストールが終わったら"Finish"をクリック
FastCV SDKのインストール
- Qualcomm developer networkの”Register”からアカウントを登録
- アカウント取得後、FastCV Computer Vision SDKから ”FastCV SDK Installer” をダウンロード
- ダウンロードしたInstallerを実行
私の場合、FastCV SDKは以下の場所にインストールされました。
C:/Android/Development/fastcv-android-1-7-1
インストーラ実行時に下記のエラーが出た場合
コマンドラインでJava VMを指定すれば、無事インストールできました2。コマンドラインへは以下のように入力します。
> fastcv-installer-android-1-7-1/fastcv-installer-android-1-7-1.exe LAX_VM "C:/Program Files(x86)/Java/jre1.8.0_171/bin/java.exe"
プロジェクトファイルの準備
RICOH THETA Plug-in SDKをベースにする方法を紹介します。
もちろん、一からプロジェクトファイルを作っても構いません。
なお、2. 名称変更は最悪行わなくても動きます。プラグインストアに出す場合は、必ず行うようにしてください。
※本記事で紹介している実装例では、プロジェクト名は"theta-plugin-fastcv-simple-sample"、パッケージ名は"fastcvsimplesample"にしています。
1. RICOH THETA Plug-in SDKのダウンロード
- RICOH THETA Plug-in SDK(AndroidStudioプロジェクト)をダウンロード
2. 名称変更
-
プロジェクトファイル名の変更
ダウンロードしたAndroidStudioプロジェクトの名称(ディレクトリ名)を任意のものにリネーム -
アプリケーションID・パッケージ名の変更
アプリケーションIDの設定等を参考に変更する -
プラグイン名の変更
プロジェクトツリーより "app > res > values > strings.xml" を開き、任意の名称に変更する
例)<string name="app_name">FastCV Simple Sample</string>
FastCVのインポートとビルド
作成したプロジェクトファイルにFastCVをインポートし、ビルドファイルを修正した後、プロジェクトをビルドします。
1. ライブラリファイルのコピー
FastCVのライブラリファイルを、今回作ったプロジェクトにコピー&ペーストする
ファイルのコピー元・先を以下にまとめる
コピー先のディレクトリは適宜作成する
ファイル名 | コピー元 | コピー先 |
---|---|---|
fastcv.h | ~/(FastCV SDK path)/inc/fastcv.h | ~/(Project path)/app/src/main/jni/fastcv/fastcv.h |
libfastcv.a (32bit) | ~/(FastCV SDK path)/lib/Android/lib32/libfastcv.a | ~/(Project path)/app/src/main/libs/armeabi-v7a/libfastcv.a |
libfastcv.a (64bit) | ~/(FastCV SDK path)/lib/Android/lib64/libfastcv.a | ~/(Project path)/app/src/main/libs/arm64-v8a/libfastcv.a |
2. Android.mkの編集
NDKのビルドファイルを修正する
ここでは、FastCVのサンプルプロジェクトのものを流用し、適宜修正していく
-
Android.mkのコピー
下記表を参考に、Android.mkをコピーしてプロジェクトに配置ファイル名 コピー元 コピー先 Android.mk ~/(FastCV SDK path)/samples/fastcorner/jni/Android.mk ~/(Project path)/app/src/main/jni/Android.mk -
AndroidStudioのプロジェクトツリーを ”Android” ビューにし、 ”app > cpp > Android.mk” が追加されたことを確認する
-
AndroidStudioでAndroid.mkを開き、以下の修正を加える3 4
-
include $(CLEAR_VARS)
を追加 -
LOCAL_MODULE := libfastcv
を追加 -
LOCAL_SRC_FILES := ../libs/$(TARGET_ARCH_ABI)/libfastcv.a
を追加 -
include $(PREBUILT_STATIC_LIBRARY)
を追加 -
LOCAL_LDLIBSから-lfastcv
をコメントアウト -
LOCAL_LDFLAGS := -Wl, --no-fix,cortex-a8
をコメントアウト -
LOCAL_MODULE :=
を任意の名称に変更 -
LOCAL_SRC_FILES :=
の値を削除 -
LOCAL_SHARED_LIBRARIES := liblog libGLESv2
をコメントアウト -
LOCAL_MODULE_OWNER :=
を任意の値に変更 -
LOCAL_JNI_SHARED_LIBRARIES :=
を任意の値に変更 -
LOCAL_PACKAGE_NAME :=
を任意の値に変更
記述例
Android.mkLOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libfastcv LOCAL_SRC_FILES := ../libs/$(TARGET_ARCH_ABI)/libfastcv.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_PRELINK_MODULE:= false # This variable determines the OpenGL ES API version to use: # If set to true, OpenGL ES 1.1 is used, otherwise OpenGL ES 2.0. USE_OPENGL_ES_1_1 := false # Set OpenGL ES version-specific settings. ifeq ($(USE_OPENGL_ES_1_1), true) OPENGLES_LIB := -lGLESv1_CM OPENGLES_DEF := -DUSE_OPENGL_ES_1_1 else OPENGLES_LIB := -lGLESv2 OPENGLES_DEF := -DUSE_OPENGL_ES_2_0 endif # An optional set of compiler flags that will be passed when building # C ***AND*** C++ source files. # # NOTE: flag "-Wno-write-strings" removes warning about deprecated conversion # from string constant to 'char*' LOCAL_CFLAGS := -Wno-write-strings $(OPENGLES_DEF) # The list of additional linker flags to be used when building your # module. This is useful to pass the name of specific system libraries # with the "-l" prefix. LOCAL_LDLIBS := \ -llog $(OPENGLES_LIB) # -lfastcv # LOCAL_LDFLAGS:= -Wl,--no-fix-cortex-a8 LOCAL_MODULE := fastcvsimplesample LOCAL_CFLAGS := -Werror LOCAL_SRC_FILES := \ LOCAL_STATIC_LIBRARIES := libfastcv # LOCAL_SHARED_LIBRARIES := liblog libGLESv2 LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/fastcv/inc \ LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/fastcv LOCAL_MODULE_OWNER := theta360 LOCAL_PROPRIETARY_MODULE := true include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_STATIC_JAVA_LIBRARIES := LOCAL_JNI_SHARED_LIBRARIES := libfastcvsimplesample LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := pluginapplication include $(BUILD_PACKAGE)
-
3. build.gradle(Module:app)の編集
-
"Gradle Scripts > build.gradle(Module: app)" を開く
-
"android > defaultConfig" に、以下のように "ndk" の値を追記する
build.gradleandroid { ... defaultConfig { ... ndk { moduleName "libfastcvsimplesample" abiFilters 'armeabi-v7a', 'arm64-v8a' } } }
-
エディタウィンドウ上部に表示された "Sync Now" をクリック
4. ネイティブライブラリとGradleのリンク
- プロジェクトツリーで ”app” を右クリックし、 "Link C++ Project with Gradle" を選択
- 開いたウィンドウのBuild Systemに "ndk-build" 、Project Pathに "Android.mk" のパスを指定し、OKをクリック
5. ビルド
FastCVアプリケーションの作成
実際にプロジェクト内でFastCV APIを使ってみます。
1. NDKソースファイルの追加
-
プロジェクトツリーから”(project) > app > src > main > jni”をたどって右クリックし、”New > C/C++ Source File” をクリック
-
適当なC++ファイル名を入力し、OKをクリック
-
同様に、”New > C/C++ Header File” からヘッダーファイルを作成する
"jni"下に作成したファイルが追加される
-
”Android.mk”を開き、
LOCAL_SRC_FILES
に追加した.cppファイル名を追記するAndroid.mkLOCAL_SRC_FILES := \ FastCVSample.cpp
-
エディタウィンドウ上部に表示された ”Sync Now” をクリック
2. 実装
Javaとネイティブ関数のやりとり
NDKについてはまだ勉強中なので詳細は触れませんが、Java ↔ ネイティブ(C++)のやり取りで用いる関数には、特殊なルールがあるようです。
戻り値や引数の型も特殊のものを使うようです5。
詳しくは、JNIの型とデータ構造等を参考にしてください。
ネイティブ側関数の命名規則6
JNIEXPORT [戻り値の型] JNICALL Java_[最上位ソースディレクトリからのJavaソースの相対パス]_[呼び出し元のJavaのクラス名]_[関数名](JNIEnv *, jobject, [引数], ...))
// 例
JNIEXPORT jstring Java_com_theta360_fastcvsimplesample_MainActivity_getFastCVVersion(JNIEnv * env, jobject obj);
Javaからのネイティブ関数の呼び出し
以下を行う必要があるようです6
- 起動時に
System.loadLibrary("[.soファイル名]")
を呼ぶ -
native
キーワードを付けて関数を宣言
// 例
static {
// Load JNI Library
Log.i(TAG, "Load JNI Library");
System.loadLibrary("fastcvsimplesample"); // <--
}
...
private void foo(){
// ネイティブ関数を呼ぶ
String versionText = "FastCV Version: " + getFastCVVersion(); // <--
Log.i(TAG, versionText);
}
}
...
// ネイティブ関数の定義
public native String getFastCVVersion(); // <--
FastCVの基本API
基本APIはMiscellaneousモジュールに定義されています。
FastCVを使う際には、以下の初期化・終了処理APIを呼ぶ必要があります。
- 初期化処理:
fcvSetOperationMode( fcvOperationMode mode );
- 引数にパフォーマンスレベルを設定するようです。
ドキュメント(fcvOperationMode項)にあるように、5段階の設定値が定義されています。
- 引数にパフォーマンスレベルを設定するようです。
- 終了処理 :
fcvCleanUp( void );
- バージョン取得:
fcvGetVersion( char *version, unsigned int versionLength);
- 引数に、バージョン情報を格納するための
ポインタ
、文字数
を渡します。
- 引数に、バージョン情報を格納するための
3. コード記述例
ネイティブ ヘッダファイル
#ifndef THETA_PLUGIN_FASTCV_SIMPLE_SAMPLE_FASTCVSAMPLE_H
#define THETA_PLUGIN_FASTCV_SIMPLE_SAMPLE_FASTCVSAMPLE_H
#include <jni.h>
// Declarations
extern "C" {
JNIEXPORT void JNICALL Java_com_theta360_fastcvsimplesample_MainActivity_initFastCV
(
JNIEnv* env,
jobject obj
);
JNIEXPORT void JNICALL Java_com_theta360_fastcvsimplesample_MainActivity_cleanupFastCV
(
JNIEnv * env,
jobject obj
);
JNIEXPORT jstring JNICALL Java_com_theta360_fastcvsimplesample_MainActivity_getFastCVVersion
(
JNIEnv* env,
jobject obj
);
};
#endif //THETA_PLUGIN_FASTCV_SIMPLE_SAMPLE_FASTCVSAMPLE_H
ネイティブ C++ファイル
#include <fastcv/fastcv.h>
#include "FastCVSample.h"
JNIEXPORT void JNICALL Java_com_theta360_fastcvsimplesample_MainActivity_initFastCV
(
JNIEnv* env,
jobject obj
)
{
fcvSetOperationMode( (fcvOperationMode) FASTCV_OP_PERFORMANCE );
return;
}
JNIEXPORT void JNICALL Java_com_theta360_fastcvsimplesample_MainActivity_cleanupFastCV
(
JNIEnv * env,
jobject obj
)
{
fcvCleanUp();
}
JNIEXPORT jstring JNICALL Java_com_theta360_fastcvsimplesample_MainActivity_getFastCVVersion
(
JNIEnv * env,
jobject obj
)
{
char sVersion[32];
fcvGetVersion(sVersion, 32);
return env->NewStringUTF(sVersion);
}
Javaファイル
RICOH THETA Plug-in SDKの "MainActivity.java" をベースに、[2. 実装]通りに実装していきます。
getFastCVVersion()
は onKeyDown()
内で呼ぶようにしています。これにより、THETAのシャッターボタンを押したときにバージョンが表示されるようにしています。
(省略)
public class MainActivity extends PluginActivity {
private static final String TAG = "FastCVSimpleSample";
static {
// Load JNI Library
Log.i(TAG, "Load JNI Library");
System.loadLibrary("fastcvsimplesample"); // <--
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set enable to close by pluginlibrary, If you set false, please call close() after finishing your end processing.
setAutoClose(true);
// Set a callback when a button operation event is acquired.
setKeyCallback(new KeyCallback() {
@Override
public void onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
// ネイティブ関数を呼ぶ
String versionText = "FastCV Version: " + getFastCVVersion(); // <--
Log.i(TAG, versionText);
}
}
@Override
public void onKeyUp(int keyCode, KeyEvent event) {
}
@Override
public void onKeyLongPress(int keyCode, KeyEvent event) {
}
});
}
@Override
protected void onResume()
{
super.onResume();
initFastCV(); // <--
}
@Override
protected void onPause() {
cleanupFastCV(); // <--
super.onPause();
}
// ネイティブ関数の定義
public native void initFastCV(); // <--
public native void cleanupFastCV(); // <--
public native String getFastCVVersion(); // <--
}
4. ビルド・実行
- メニューから "Run > Run'app'" をクリックしてビルド・実行する
- "Select Deployment Target"ウィンドウが出たらTHETA Vを選択し、OKをクリック
- ツールウィンドウの ”Build” より、ビルドが成功したことを確認する
[3. コード記述例]の通りにコード記述すると、シャッターボタンを押したときにFastCVのバージョンがLogcatに表示される。
※ Log.i()
で設定したTAG名
でフィルタリングすると見やすい
おわりに
今回は出来上がるものが地味な割に、結構大変な作業だったかと思います…。
次回記事では、実際に画像処理をする手順をご紹介する予定です。
※本記事の続編は以下です↓
THETAプラグインで画像処理【FastCV画像処理編】
RICOH THETAプラグインパートナープログラムについて
RICOH THETAプラグインをご存じない方はこちらをご覧ください。
RICOH THETAプラグイン開発者コミュニティの以下の記事もぜひご覧ください。
興味を持たれた方はtwitterのフォローとTHETAプラグイン開発コミュニティ(slack)への参加もよろしくおねがいします。