あなたの小さなホームプロジェクトでは、Viola-Jones顔検索アルゴリズムが使用され、モデルのJavaソースコードはここからcode.google.com/p/jviolajonesから少し修正されて取得されました。2つのクラスが追加されました:PointとRectangle。 Android向けOpenCVを使用しなかった理由を明確にします-別のライブラリアプリケーションをインストールする必要がありますが、これは非常に不便であり、実験により警告なしにクラッシュしたことが示されました。最も単純な完成した実装を採用することにしました。
アルゴリズムの速度は悲惨な結果を示しました。私の壊れた古いGT-I9300Iの400 x 300の写真-54秒、avd(仮想デバイス)、さらに長い-250秒。
多くの場合、JavaとC ++でのコードのパフォーマンスに関する議論に出くわしました。Javaが遅れているように見えたり、場合によっては逆に、1つのループを持つコードの小さなセクションがあったりします。 このアルゴリズムは、ソースからわかるように、ネストされた6サイクルのオーダーの、やや複雑です。 したがって、C ++で書き直した私たち自身の経験を試すことを決定しました。 私が読んだすべての記事で、速度が最大20パーセント増加するという印象を受けましたが、結局は間違っていました。
当然、次のタスクが発生しました-入力データの転送方法と出力データの受信方法、およびコードの書き換え方法。 Detectorは、コンストラクターのxmlからjavaにモデルの塗りつぶしを残すことにしました。もちろん、すぐにいっぱいになりますが、C ++でxmlを操作するのは非常に怖いので、そのままにしました。 私の職業はjavaに関連しており、研究所でC \ C ++に連絡し、古いプロジェクトで少し働いていました。 したがって、私は少しのドキュメントを勉強し、記事を読んで、いくつかのコーンを埋めなければなりませんでした。
ロジックの書き換え。 ここには特別な問題はなく、メソッドが採用されました-クラスをコピーすることなく、日食が赤いhatchで強調表示されています。 すべてのArrayListを配列に変換しましたが、幸いなことに、サイズは変更されませんでした。
ネイティブコードを呼び出すための環境設定については説明しません。このトピックに関する記事はたくさんあります。
データを転送する方法。 単純型-int、float、booleanでは、すべてが単純で明確です。 一次元では、それも簡単なようです:
JNIEXPORT jint JNICALL Java_com_example_Computations_intFromJni(JNIEnv* env, jobject thiz, jintArray arr) { jsize d = env->GetArrayLength(arr); jboolean j; int * p = env->GetIntArrayElements(arr, &j); ... }
二次元ではもう少し複雑です:
JNIEXPORT jint JNICALL Java_com_example_Computations_findFaces(JNIEnv* env, jobject thiz, jobjectArray image) { int width = env -> GetArrayLength(image); jboolean j2; jintArray dim= (jintArray)env->GetObjectArrayElement(image, 0); int height = env -> GetArrayLength(dim); int **imageLocal; imageLocal = new int*[width]; for (int i = 0; i < width; i++) { jintArray oneDim= (jintArray)env->GetObjectArrayElement(image, i); int *element = env->GetIntArrayElements(oneDim, &j2); imageLocal[i] = new int[height]; for(int j=0; j < height; ++j) { imageLocal[i][j]= element[j]; } } ... }
さらに、リスト型があるフィールドの集まりを持つオブジェクトを渡す方法を見てみましょう。 オブジェクトフィールドを取得するには、次の構成が適用されます。
jclass clsDetector = env->GetObjectClass(objDetector); jfieldID sizeFieldId = env->GetFieldID(clsDetector, "size", "Ldetection/Point;"); jobject pointObj = env->GetObjectField(objDetector, sizeFieldId);
シートの場合、2つのgetおよびsizeメソッドが必要です。
jfieldID stagesFieldId = env->GetFieldID(clsDetector, "stages", "Ljava/util/List;"); jobject stagesList = env->GetObjectField(detectorJObj, stagesFieldId); jclass listClass = env->FindClass( "java/util/List" ); jmethodID getMethodIDList = env->GetMethodID( listClass, "get", "(I)Ljava/lang/Object;" ); jmethodID sizeMethodIDList = env->GetMethodID( listClass, "size", "()I" ); int listStagesCount = (int)env->CallIntMethod( stagesList, sizeMethodIDList ); for( int i=0; i < listStagesCount; ++i ) { jobject stage = env->CallObjectMethod( stagesList, getMethodIDList, i); ...
データを取得する方法を学びました。 エラーが発生しました-ローカル参照テーブルが512エントリをオーバーフローしました。 すべてのローカルjclassおよびjobjectリンクを削除する必要があることがわかりました。これは次のように行われます。
env->DeleteLocalRef(jcls); env->DeleteLocalRef(jobj);
また、配列についても:
env->ReleaseIntArrayElements(oneDim, element, JNI_ABORT); env->DeleteLocalRef(oneDim);
結果を返します。 タスクを簡素化するために、結果をRectangleの配列として返しました。
jclass cls = env->FindClass("detection/Rectangle"); jobjectArray jobAr =env->NewObjectArray(faces->currIndex, cls, NULL); jmethodID constructor = env->GetMethodID(cls, "<init>", "(IIII)V"); for (int i = 0; i < faces->currIndex; i++) { Rectangle* re = faces->rects[i]; jobject object = env->NewObject(cls, constructor, re->x, re->y, re->width, re->height); env->SetObjectArrayElement(jobAr, i, object); } return jobAr;
厳soleな瞬間-同じ写真での検索-14秒、つまり 4倍高速、他の写真でも同様の結果。 仮想アンドロイドでは、132秒対300秒。 しかし、私たちが知っているように、1つの実験の結果を使用することは不可能です、数回繰り返す必要があります、私は1枚の写真、処理時間を秒単位で与えます。
仮想デバイス | cppを使用する仮想デバイス | 私の銀河の電話 | CPPを使用した銀河の電話 |
---|---|---|---|
238 | 132 | 84 | 14 |
318 | 137 | 54 | 14 |
472 | 135 | 54 | 14 |
264 | 150 | 54 | 14 |
266 | 138 | 54 | 14 |
262 | 129 | 53 | 14 |
そして結論として、私は注意します。 書き換えが大きな加速をもたらしたという事実にもかかわらず、完璧にはまだ多くの制限があります。マルチスレッドを使用することができます。これは近い将来に研究する予定です。 そして、アルゴリズムを調整することがおそらく最も難しい部分です。
更新 ソース を投稿しました。 次のように使用します。
Detector detector = Detector.create(inputHaas); List<Rectangle> res = detector.getFaces(background_image, 1.2f, 1.1f, .05f, 2, true, useCpp);
inputHaasはモデルストリーム、つまり 元のアルゴリズムからのhaarcascade_frontalface_default.xmlファイル、useCpp-C ++を使用するかどうか。 C ++ソースでは、メモリを解放しません。 急いで書いた。