2014年3月29日土曜日

Android NDK でSTLを使用する

Android NDKで cpp のコードを使用する際、そのままだと <string>などが使用できない。
STLを使用できるよう、設定を変更する。

行うことは簡単で、jniディレクトリにApplication.xmlを作成したら、以下の行を追加する。

APP_STL         := stlport_static

以上でOK。

2014年3月27日木曜日

物体認識エンジン完成!

数千分類可能な画像物体認識エンジンが完成した!
アルゴリズムから初めて1年以上…。

(目標となるエンジンがあって、それには分類数が全然及ばないけど)、
スマホからサーバに問い合わせるタイプで、LTEなら 2 秒くらいで返事がくる。
ちなみに物体認識のみの時間は 1 秒未満です。
要件とチューニングによっては 200 ミリ秒くらいに出来ます。

誰か使ってくれないかな。サーバ管理費だけで使用可能です。

数十万分類以上必要な場合は、それ相応のエンジンが必要だと思うので紹介できますが、
数百から数千くらいの分類で必要十分な場合は、是非テスト使用してくれる方を募集します。

ご連絡はこちら。
Twitter:masahiro
@masazdream


次は、Android上で検索まで動作するものをつくって、目標は Android Wear だな。

2014年3月25日火曜日

Android.mk を 書こう

端末上の画像処理とか数学計算とかAndroid Wearとかやりたい。
Androidアプリケーションはmakeするのにmkファイルを書く。

NDKはデフォルトで /jni/Android.mk を探しにいくようだ。どうりで設定ファイルらしき
ものが無かった。

jni 配下の別の場所にAndroid.mlを作成した場合は、以下の用に設定して、サブディレクトリ
を検索させる。

include $(call all-subdir-makefiles)

1つのAndroid.mkファイルに複数のモジュールを登録可能だし、
複数のAndroid.mlを使用することも可能。

ヘッダーリストや生成されたファイルの依存関係については、Android.mkに
記述しない。これは、NDKがアップデートされても、Android.mkはそのままで
良いというメリットにつながる。

以下、Native Support を Androidプロジェクトに設定すると、以下の用なmkファイルが
作成される。

  • LOCAL_PATH := $(call my-dir) ← Android.mkは必ずこの宣言からはじまる。my-dirはNDKコードで定義されたマクロであり、カレントディレクトリを示す。
  • include $(CLEAR_VARS) ← 必須項目。CLEAR_VARS は NDKシステムにより提供される変数である、特別なGNU Makefileを指している。LOCAL_PATHを除いたいくつかのLOCAL_~変数をクリアする。1つのGNU Makeによりパースされ、全変数はglobalとして扱われる。
  • LOCAL_MODULE    := IRClientAndroid ← 必須。一意のモジュール名。ホワイトスペースは使えない。libXXX.soのXXXに入る名前を決定する部分である。
  • LOCAL_SRC_FILES := IRClientAndroid.cpp ← モジュールに含めるC++ソースリストの定義。ヘッダファイルやインクルードファイルは含めないこと。
  • include $(BUILD_SHARED_LIBRARY) ← 動的共有ライブラリを作成する場合に指定。
Eclipseでやる場合、パスの設定がいろいろ必要なようだ。
次はそれについて。



2014年3月18日火曜日

virtualbox guest additionについて

OSのバージョンが新しくて、guest addition が古い時にエラーが起こることがある。

ちなみに、私は以下のエラーが起きた。

Building the OpenGL support module [失敗]

つまり、guset addtion の インストールが、新しいOSを考慮していないってこと。

一番良いのは、VirtualBoxと共にバージョンを上げること。

ちなみに、OpenGLがだめでも、一応startxすればGUIは起動する。

VirtualBoxのダウンロードサイトがSunのころと変わっているのでメモ

以下がVirtualBoxのダウンロードサイト。

http://download.virtualbox.org/virtualbox/

Sunのサイトはまだあるけど更新が止まってた。

CentOSのトラブルメモ

GUIのパッケージに不具合や依存関係が解決できないことがあった場合、GUI起動できなくなる。

まずは、singleユーザモードでログインする。

※参考
http://www.obenri.com/_operation/singleuser.html

起動時に e を入力そ、kernelの選択画面が出てきたら、末尾に sinble と追加して再起動。


次に、cuiモードに変更

 /etc/inittab を編集して、起動モードを 3 に変更して再起動。


これで、CUIモードで起動できます。

2014年3月16日日曜日

2014年3月15日土曜日

JNI (Java Native Interface) (5) 変数の設定、メソッド呼び出し

JNIを使って Javaの変数フィールド を扱う方法を見ていきましょう。

javahで生成された関数プロトタイプは以下の様でした。

JNIEXPORT void JNICALL
Java_jp_sprix_jni_ImageRecognize_queryImage(JNIEnv *env, jobject obj){

すべての関数プロトタイプは JNIEnvポインタ が最初の引数として宣言されます。
C++ネイティブコードは、JNIEnvポインタ で自分のメソッド(this)と同じように使うことができます。C++オブジェクトは、同様の方法で自分自身のメソッドへのポインタを保持しているので、このように使うことができます。

では、Javaのデータフィールドを操作してみましょう。

オブジェクトのデータフィールドにネイティブからアクセスする

以下のようなJavaコードがあったとしましょう。

privateなint型の変数と、同じ型ですがstaticな変数を宣言しています。
それぞれの値をsetするnativeメソッドを作っておきましょう。

javahで生成した関数プロトタイプが以下です。

関数プロトタイプの実装は以下のようにできます。

これらからわかることを整理してみましょう。

  • staticとそうでないデータフィールドにアクセスするには、それぞれ違い関数集合がある
  • staticなデータフィールドを参照する場合、jclass への参照が必要
  • 非静的なデータフィールドを参照する場合、jobject への参照が必要
  • データフィールドを操作するJNI関数は、アクセスするデータ型を名前に含む


次は、Javaオブジェクトのデータフィールドを取得してみましょう。

JNI (Java Native Interface) (4) nativeメソッドのロードと呼び出すについて

nativeメソッドは、通常のJavaメソッドと同じように呼び出せます。

ただし、以下の点に注意しましょう。

  • System.loadLibrary を呼び出す。
  • nativeメソッドを宣言しているクラスの中に、staticな初期化ブロックを置いて呼び出すのが一般的
  • staticな初期化ブロックのコードは、クラスを初期化するときに実行される
  • クラスは初めてアクティブに使われたとき (= クラスが定義したメソッドを呼び出す)に初期化される
  • ロードしていなかったり、失敗している場合にはJVMが「java.lang.UnsatisfiedLnkError」を投げる(getMessageで見つからなかったnativeメソッド名を取得できる)
  • 共有オブジェクトライブラリのファイルは、環境変数LD_LIBRARY_PATHに指定したディレクトリの中にあることが必要
  • LD_LIBRARY_PATHは、「;」で区切って複数のディレクトリを指定可能。以下で確認する
% echo $LD_LIBRARY_PATH
  • ネイティブライブラリのロードを頑丈にするには、以下のようにします
次に、ロードに関するよくある間違いを見てみましょう。

検索パスのミス

  • ロードに失敗すると、最初にnativeメソッドを呼び出そうとしたとき、java.lang.UnsatisfiedLinkErrorが投げられます。
  • mainでロードしている場合、ロードにトライしたとき、上記と同様なことが起こります。

ライブラリ名の間違い

  •  上記と同じエラーが起きます。
  • javaデバッガで実行するとき、Nativeライブラリの名前に_gを要求することがある。デバッグでのみ問題がおこるときは、[ライブラリ名]_g.so や [ライブラリ名]_g.dll を準備してみるとよいです。

シグネチャの不一致

  •  nativeメソッドの仮引数の個数と型が、関数プロトタイプと一致しない場合、java.lang.UnsatisfiedLinkErrorが投げられる。nativeメソッドを変更したら、必ずjavahでヘッダを再生成することを進めます。package変更によっても起こります。

java.lang.UnsatisfiedLinkError が投げられたとき、上記を見直してみるとよいでしょう。

次は、変数とメソッド呼び出し についてです。


2014年3月14日金曜日

JNI (Java Native Interface) (3) Servletから呼び出す

既にプラットフォーム上で動作できているならば、同じプラットフォーム上の tomcat servlet から呼び出すことは難しくありません。

ただし、Pure Java で 書いたモジュールのように、tomcatとJVMがあればどこでもというわけにはいきません。

同じプラットフォーム上に限定すれば、以下の注意をすれば javaで作成したWEBアプリケーションから nativeコード を呼び出すことが可能です。


  1. Servletクラス(もしくはその先でnativeライブラリを使用するクラス)では、nativeライブラリのロードを忘れないようにしましょう。
  2. jniライブラリは、jarファイルにして WEB-INFのlib配下に設置します。


後は、LD_LIBRARY_PATH に 使用する動的ライブラリを入れて、環境変数か実行時にオプションで指定すれば動作します。




JNI (Java Native Interface) (2) 最も簡単なコードを書いてみる

ここでは、最も簡単なコードを書いてみましょう。
世界が広がる「Hello World!」です。

ただ実行するだけでなく、出来るだけ簡単に build 出来る環境も作成しましょう。

流れ


Java と c や c++ の両方の経験があれば、難しい処理は出てきません。

Java だけしか経験が無い場合、少し戸惑うかもしれませんが、知らなかったり、わからない部分は c や c++ のコードを実行するのに必要な作業と思えばよいと思います。

まず、Javaのコードを書きます。
その際、メソッドに nativeキーワード を指定して、nativeメソッド宣言します。
Javaクラスにはnativeメソッドの本体は定義しないので注意してください。

javah 使って、nativeメソッド毎の関数プロトタイプが記述された ヘッダファイル を生成します。

その関数プロトタイプの記述に従って、c や cpp で本体の関数を記述します。

関数プロトタイプには、入力(仮引数) と 出力(戻り値) を定義するので、native関数はそれに従って記述する必要があります。

本体のnative関数は、プラットフォーム固有のコンパイラとリンカを使用してコンパイラします。ここは、プラットフォーム毎に手順が違うと思います。

JVMは、それぞれの関数プロトタイプに定義された呼び出しプロトコルを使って、nativeメソッドを実行します。

これが、一連の流れです。
ここでは、native メソッドのプログラムは C や C++ で考えることにします。

ネイティブコードへのインターフェース記述

Javaコード と native のコードを統合するときの課題は、Javaのnativeメソッドの仮引数を、既存のAPIの仮引数にマッピングすることです。

一般の解決法は以下の 2 つです。

  1. 従来のデータを Java で設計し、そのデータを扱えるうまいメソッドを定義する
  2. 既存のインターフェースを、自分の Java のnativeメソッド宣言に直接マップ
どちらも難しそうです。
後者は、Cの構造体をJavaのオブジェクトで置き換えるために適用できる方法ですが、後で詳しく見ていくことにします。


順番に見ていきましょう。


Javaコードの生成

Javaクラスに、nativeメソッドを定義します。
例えば以下の様な感じです。





javac でクラスファイルを生成

IDEを使用していれば、ここは意識しないところですね。

簡単に以下の様にコマンドすれば出来ます。

% javac NativeSampleMethod.java

javahでインクルードするヘッダファイルを生成

nativeメソッドの宣言が入ったJavaクラスファイルを作成したら、対応する ANSI C関数プロトタイプを生成します。

native宣言の入ったすべてのクラスファイルで、javah を走らせます。

Javaクラスファイルの場所は、環境変数 CLASSPATH、javahのオプション -classpath で指定します。

% javah -jni NativeSampleMethod

生成されたヘッダファイルは、nativeメソッドを実装したソースファイルにインクルードが出来る。

ヘッダファイルの名前は、入力したクラスの完全修飾された名前で決まります。つまり、パッケージ名までを含んだ一意な名前になります。


例えば以下のようになります。


これはjp.sprix.jniパッケージの配下にjavaファイルを置いている場合です。

次に、nativeコードを記述します。


以下の3つの制限に従っていることに注意しましょう。

  1. nativeメソッドの実装関数は、最初の引数が JNIEnvへのポインタ、2番目がオブジェクト参照
  2. 入力引数の型はJNIが定義する
  3. 戻り値の型はJNIが定義する
  4. 関数プロトタイプが含む ヘッダファイルをインクルードしなければならない

ライブラリのビルド

プラットフォーム固有になる部分です。

Linuxであれば .so 、Windowsであれば .dll をビルドします。


  1. nativeコードをコンパイルし、jni.hをインクルードするためにふさわしい場所はすべて指定
  2. nativeライブラリをビルド


今回は、ant contrib を使用して、ビルドしましょう。
詳細な方法は、以前のブログに書いています。

http://masabloggers.blogspot.jp/2013/05/jni-javacpp.html



今回必要になるbuild.xmlは以下です。


この構成に必要となる構造は以下です。

src -  javaパッケージ、javaソースコード、ANSI 関数プロトタイプヘッダファイル格納
dist - 動的ライブラリの格納ディレクトリ
jnih - ANSI 関数プロトタイプヘッダファイル、native実装ファイル
build.xml

build.xmlファイルを読めば、大体のディレクトリ構造はわかると思います。
また、gccコンパイルに必要なインクルードディレクトリやリンカーの設定もしてあります。

これでbuildしたxmlファイルは、実行時に以下のjava オプションでnativeライブラリの場所を指定すます。

LD_LIBRARY_PATH

以上が、Hello World と ビルド方法についてです。


次は、起きやすい問題やその対処について書こうと思います。

JNI経由でJavaからOpenCVを使ったCppを呼び出す

前回JNIについて書いたのは結構前でした。

JNI JavaからCppを実行する
http://masabloggers.blogspot.jp/search/label/JNI

※参考ページ
http://stbr.no-ip.org/daizu/2009/11/jni-ant.html

この時に作成したbuild.xmlを改良して、OpenCVを使ったCppのコードを
コンパイル出来るようにするぜ。

後、うまくOpenCVの実行ファイルを使えるようにするぜ。

というのが目標です。

Cppコードのコンパイル


まずは ANT build.xml でOpenCVを使ったコンパイルが出来るようにしましょう。

今回は、OpenCV2.4.6 を使います。

OpenCVダウンロード
http://masabloggers.blogspot.jp/search/label/JNI


以下に展開した前提で進めます。

/opt/opencv-2.4.6
gcc のコンパイルは、ノード cc で実行します。

OpenCVを使用したコードで必要なのは、主に以下だと思います。

  • ヘッダファイルのincludeディレクトリ
  • リンクするライブラリ
ノードccの子供に、以下のノードを設定できます。

  • includepath
  • libset

以下のように書けます。


注意したいのは、ライブラリを指定する時に、先頭の lib と 拡張子の .so を除いて記述するところです。libsには私が必要だったライブラリしか指定していないので、必要に合わせて変更します。

OpenCVのインストール時、オプションを指定しないとすべて /usr/local/ に配置されるので、ディレクトリを指定したい場合は注意。




JNI (Java Native Interface) (1) JNIの概要

JNIについて、ネットを探してもあまり良い日本語の情報が見つからなかったので、
まとめてみようと思いました。

参考にしている書籍は以下の本なので、若干古い情報もあるかもしれないが、
コードは動作したもののみを載せます。

http://www.amazon.co.jp/dp/4894710803

JNIを使う理由


・PureJavaでは実現できないパフォーマンスが必要なとき
・過去のアプリケーションライブラリを有効活用したいとき

パフォーマンスの問題は、JVMの成熟やJIT(ジャストインタイム)コンパイラによって
改善に向かっていますが、すべてを解決してくれるわけではありません。

JNI以外の手法(JRI、RNI)も存在するが、AndroidアプリケーションがNDK(JNI)をサポート
したためか、2014年現在、存在感を失っています。


Javaアプリケーションがネイティブメソッドを実行するということ


ホストのハードウェア上で直接、ネイティブコードが走ります。つまり、

JavaコードはJavaバイトコード(classファイル)にコンパイルされ、JVMがバイトコードと
ホストハードウエアの間を媒介する形で実行しますが、ネイティブコードはJVMの管理外で
走るということです。

ネイティブコードはJVMが媒介しないで実行可能な、プラットフォーム固有のオブジェクトコードです。


ネイティブコードとは

一般にダイナミック(動的)ロードライブラリの中に入れます。

ライブラリを作成する場合は、ホストマシンのネイティブコンパイラとリンカを
使用してライブラリをビルドします。
ネイティブコンパイラが作成するオブジェクトコードはプラットフォーム固有なので、
ライブラリも同様です。


再ビルドは必要ない(バイナリ互換性がある)


JNIのAPI(アプリケーションプログラミングインターフェイス)の規則に従って作成された
バイナリオブジェクトがプラットフォームX上のライブラリに入っているならば、
ライブラリを再ビルドすることなく、JNIをサポートしたJVMを介して使用することが出来ます。


JNIの特徴


・Javaオブジェクトの中身が見えない方法で参照を提供する
・JVMとネイティブコードの間に関数テーブルを配置し、JVMのデータにアクセスする場合は必ずその関数を経由する
・ネイティブの型集合を定義し、Javaの型からプラットフォーム固有な型への一様なマッピングを提示する
・メモリの喪失への対応(ユーザが連続したメモリ領域を期待する場合のために、オブジェクトのメモリの扱い方についてJVMに柔軟性をもたせる。文字列や数値配列データを返すJNI関数は、ネイティブコードがメモリ領域を使用している間にメモリ管理システムがその領域を移動しないように、メモリ領域をロックする可能性がある)

コードが無いとイメージしづらい部分もあると思います。
それらは後で実際にコードを動作させて見てみようと思います。

さらに詳細な特徴もあります。メソッドの呼び出しやオブジェクトの生成、変数へのアクセス、例外処理、ネイティブメモリの管理モニタ、クラスのロード、JVMの生成 などにJNIの特徴・良い点があります。

次に続く。