AndroidプラットフォームでのJavaからC ++への書き換え

AndroidプラットフォームでjavaからC ++に書き換えた経験と、その結果を共有したいと思います。



あなたの小さなホームプロジェクトでは、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 ++ソースでは、メモリを解放しません。 急いで書いた。



All Articles