Help us understand the problem. What is going on with this article?

THETAプラグインで画像処理【FastCV導入編】

More than 1 year has passed since last update.

こんにちは、リコーの@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 にあります。
基本的な処理をはじめ、特徴点抽出クラスタリング機械学習 などのモジュールも用意されているようですね。
modules.png

環境

本記事で紹介する手順は、以下の環境で確認しています。

  • 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の導入とプラグイン開発環境を整えておいてください。

NDKのインストール

  1. AndroidStudioを起動し、メニューから”Tools > SDK Manager”を選択
  2. ”SDK Tools”タブを選択し、”NDK” にチェックを入れて"OK"をクリック ndk_install.png
  3. 画面に従って進め、インストールが終わったら"Finish"をクリック

FastCV SDKのインストール

  1. Qualcomm developer networkの”Register”からアカウントを登録
  2. アカウント取得後、FastCV Computer Vision SDKから ”FastCV SDK Installer” をダウンロード
  3. ダウンロードしたInstallerを実行

私の場合、FastCV SDKは以下の場所にインストールされました。

C:/Android/Development/fastcv-android-1-7-1


インストーラ実行時に下記のエラーが出た場合

fastcv_install_error.png

コマンドラインで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のダウンロード

  1. RICOH THETA Plug-in SDK(AndroidStudioプロジェクト)をダウンロード

2. 名称変更

  1. プロジェクトファイル名の変更
    ダウンロードしたAndroidStudioプロジェクトの名称(ディレクトリ名)を任意のものにリネーム
  2. アプリケーションID・パッケージ名の変更
    アプリケーションIDの設定等を参考に変更する
  3. プラグイン名の変更
    プロジェクトツリーより "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のサンプルプロジェクトのものを流用し、適宜修正していく

  1. Android.mkのコピー
    下記表を参考に、Android.mkをコピーしてプロジェクトに配置

    ファイル名 コピー元 コピー先
    Android.mk ~/(FastCV SDK path)/samples/fastcorner/jni/Android.mk ~/(Project path)/app/src/main/jni/Android.mk
  2. AndroidStudioのプロジェクトツリーを ”Android” ビューにし、 ”app > cpp > Android.mk” が追加されたことを確認する
    AS_Androidmk.png

  3. 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.mk
    LOCAL_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)の編集

  1. "Gradle Scripts > build.gradle(Module: app)" を開く
  2. "android > defaultConfig" に、以下のように "ndk" の値を追記する

    build.gradle
    android {
        ...
        defaultConfig {
            ...
            ndk {
                moduleName "libfastcvsimplesample"
                abiFilters 'armeabi-v7a', 'arm64-v8a'
            }
        }
    }
    
  3. エディタウィンドウ上部に表示された "Sync Now" をクリック

4. ネイティブライブラリとGradleのリンク

  1. プロジェクトツリーで ”app” を右クリックし、 "Link C++ Project with Gradle" を選択
  2. 開いたウィンドウのBuild Systemに "ndk-build" 、Project Pathに "Android.mk" のパスを指定し、OKをクリック
    AS_ndkLink.png

5. ビルド

  1. メニューから "Build > Build APK(s)" をクリック
  2. ツールウィンドウの ”Build” より、ビルドが成功したことを確認する AS_build.png

FastCVアプリケーションの作成

実際にプロジェクト内でFastCV APIを使ってみます。

1. NDKソースファイルの追加

  1. ツールウィンドウバーから ”Project” を選択し、ビューを ”Project” モードにする AS_ProjectView.png
  2. プロジェクトツリーから”(project) > app > src > main > jni”をたどって右クリックし、”New > C/C++ Source File” をクリック
  3. 適当なC++ファイル名を入力し、OKをクリック
  4. 同様に、”New > C/C++ Header File” からヘッダーファイルを作成する
    "jni"下に作成したファイルが追加される
    AS_AddCFile.png
  5. ”Android.mk”を開き、LOCAL_SRC_FILESに追加した.cppファイル名を追記する

    Android.mk
    LOCAL_SRC_FILES := \
        FastCVSample.cpp
    
  6. エディタウィンドウ上部に表示された ”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キーワードを付けて関数を宣言
MainActivity.java
// 例

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 );
  • 終了処理 : fcvCleanUp( void );
  • バージョン取得: fcvGetVersion( char *version, unsigned int versionLength);
    • 引数に、バージョン情報を格納するためのポインタ文字数を渡します。

3. コード記述例

ネイティブ ヘッダファイル
FastCVSample.h
#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++ファイル
FastCVSample.cpp
#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のシャッターボタンを押したときにバージョンが表示されるようにしています。

MainActivity.java
(省略) 

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. ビルド・実行

  1. メニューから "Run > Run'app'" をクリックしてビルド・実行する
  2. "Select Deployment Target"ウィンドウが出たらTHETA Vを選択し、OKをクリック
  3. ツールウィンドウの ”Build” より、ビルドが成功したことを確認する

[3. コード記述例]の通りにコード記述すると、シャッターボタンを押したときにFastCVのバージョンがLogcatに表示される。
Log.i()で設定したTAG名でフィルタリングすると見やすい
logcat.png

おわりに

今回は出来上がるものが地味な割に、結構大変な作業だったかと思います…。
次回記事では、実際に画像処理をする手順をご紹介する予定です。
※本記事の続編は以下です↓
THETAプラグインで画像処理【FastCV画像処理編】

RICOH THETAプラグインパートナープログラムについて

RICOH THETAプラグインをご存じない方はこちらをご覧ください。
RICOH THETAプラグイン開発者コミュニティの以下の記事もぜひご覧ください。

興味を持たれた方はtwitterのフォローとTHETAプラグイン開発コミュニティ(slack)への参加もよろしくおねがいします。

roohii_3
theta-plugin
株式会社リコーの技術者有志による、RICOH THETAプラグイン技術情報の提供を目的としたコミュニティです。
https://api.ricoh/products/theta-plugin/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away