C++ プロジェクトのプロジェクト (opencv ライブラリとプロジェクトの依存ライブラリ移植を含む) を Android で使用できる so ライブラリにコンパイルし、Android Studio 上で so ライブラリを呼び出して使用します (血と涙の操作の概要)

概要

  最近、会社の先輩が書いた C++ アルゴリズム ライブラリを使用する必要がある Android プロジェクトを担当しましたが、当初は C++ プロジェクトが Android プロジェクトに移植できることを知らなかったので、単純にJava の C++ アルゴリズム ライブラリ。
  大量の情報を照会した結果、プロジェクト移植を通じて C++ プロジェクトを so ライブラリにコンパイルし、Android プロジェクトで呼び出すことができることがわかりました。インターネット上の情報は複雑ですが、ほとんどの回答で提案されている解決策の 1 つは、Android Studio で新しい C++ ネイティブ プロジェクトを作成し、cpp ディレクトリに cpp ファイルを作成して対応する関数を実装し、ネイティブで jni インターフェイスを実装することです。 cpp ファイル。jni インターフェイスで使用する必要がある対応する C++ 関数を、対応する Android プロジェクトに公開します。ファイル構造は次の図に示すとおりです。

ここに画像の説明を挿入します

   著者はこの方法を推奨しません。この方法の前提は、C++ プロジェクトが自分で作成され、内容と構造がすでに頭の中にあることです。そうでない場合は、コンパイルとパッケージ化のために C++ プロジェクトを cpp ディレクトリに直接コピーする必要があります。このライブラリは多くの頭痛の種を引き起こすものであり、初心者にとっては非常に不親切です。
   この記事では、Visual Studio 上のクロスプラットフォーム Android を通じて C++ プロジェクトを移植し、SO ライブラリにパッケージ化して Android Studio に呼び出す方法を紹介します。

事前に準備する


1: C++ プロジェクトの前処理 移植する
   C++ プロジェクトについては、まずプロジェクトが Windows と Linux のどちらに基づいて作成されているかを理解する必要があることに注意してください。Android は Linux カーネル (GNU コンポーネントを除く) をベースにした無料のオープン ソース オペレーティング システムであるため、C++ プロジェクトが Windows に基づいて作成されている場合は、C++ ソース コードにある程度の変更を加える必要がある場合があります。たとえば、次のようになります。 Windows で導入されたライブラリを使用するには、Linux でのライブラリに置き換える必要があります。そうしないと、Android ndk コンパイラが対応するライブラリを見つけられません。
ここに画像の説明を挿入します

   上の図に示すように、ifdef マクロ定義を使用すると、さまざまな処理操作を実行するために、現時点でプロジェクトが実行されているオペレーティング システムを条件付きで決定できます。
   C++ プロジェクトが非常に大きい場合は、プロジェクト全体を移植しないことをお勧めします。プロジェクトのコンテンツを移植するほど、問題が発生する可能性が高くなり、後で回復することが困難になります。Android プロジェクトが呼び出す必要があるアルゴリズム関数に対応する cpp ファイルを移植して使用することが最善です。

2:環境構築

今回の移植では、 Visual Studio 2019 に Android ndk-16b をインストールして動作させます。
  まず Visual Studio 2019 で VS インストーラーを開き
ここに画像の説明を挿入します

、C++ モバイル開発を選択します。VS
ここに画像の説明を挿入します

が NDK と SDK をダウンロードしていない場合は、VS で 3 を設定する必要があります
ここに画像の説明を挿入します
ここに画像の説明を挿入します

: Android プロジェクトの作成

ここに画像の説明を挿入します

作成後は次のようになります。
ここに画像の説明を挿入します

NDK バージョンにリダイレクトします。
ここに画像の説明を挿入します
プロジェクトのプロパティを設定します。
ここに画像の説明を挿入します

4:プロジェクトをインポートします。

プロジェクトをインポートする具体的な方法については、「プロジェクト メソッドのインポート」を参照してください。インポートされたプロジェクト ディレクトリは次のとおりです。
ここに画像の説明を挿入します


5: opencv ライブラリとプロジェクトの対応する依存ライブラリをリンクします。

  プロジェクトで opencv ライブラリが使用されている場合は、対応する OpenCV ライブラリを再リンクし、android バージョンの opencv をダウンロードする必要があります。私は opencv-3.4.15-android-sdk を使用します。
Android ライブラリのヘッダー ファイル ディレクトリとライブラリ ファイル ディレクトリのリンク インポートについて簡単に説明します。ヘッダー ファイルは、
\opencv-3.4.15-android-sdk\OpenCV-android-sdk\sdk\native\jni\include にあります。ライブラリファイルは、\opencv-3.4.15-android-sdk\OpenCV-android-sdk\sdk\native\3rdparty\libs \ opencv-3.4.15-android-sdk\OpenCV-android-sdk\sdk\
ここに画像の説明を挿入します
にあります。ネイティブ\libs \opencv-3.4 .15-android-sdk\OpenCV-android-sdk\sdk\native\staticlibs   上の図の最初の追加ライブラリ ディレクトリは、C++ プロジェクトが依存する必要があるライブラリ ファイル libusb です。 USB インターフェイスを呼び出して対応する操作を実行するため、対応するライブラリ ファイルを追加のライブラリ ディレクトリに追加する必要があります。ここでは、コンパイルする必要があるアーキテクチャ タイプに応じて、さまざまなバージョンのライブラリ ファイルをリンクします。ここでリンクしているのは、armeabi-v7a バージョンです。このバージョンは比較的一般的です。2010 年以降のほとんどの Android デバイスは、この CPU アーキテクチャを使用しています。最後のステップは追加のライブラリを追加することです:   追加の依存関係は、上記の追加ライブラリ ディレクトリで使用する必要がある .so ダイナミック ライブラリ ファイルと .a スタティック ライブラリ ファイルをリンクすることです。Android プロジェクトではサードパーティの dll と lib を使用できないことに注意してください。 Windows のライブラリ ファイル。すべて使用する必要があります。Android 側の .so および .a ファイルに置き換えてください。



ここに画像の説明を挿入します



ここに画像の説明を挿入します

   追加の依存関係を追加するための命名規則は libopencv_java3.so です。追加の依存関係を追加する場合は、-l+lib を削除した後のファイル名に変更します。サフィックス名を追加する必要はありません。静的ライブラリを追加する場合も同様です。
ここに画像の説明を挿入します

ダイナミックライブラリファイル

ここに画像の説明を挿入します

静的ライブラリファイル
完整的opencv库添加到附加依赖项列表:

-lopencv_java4
-lopencv_calib3d
-lopencv_core
-lopencv_dnn
-lopencv_features2d
-lopencv_flann
-lopencv_highgui
-lopencv_imgcodecs
-lopencv_imgproc
-lopencv_ml
-lopencv_objdetect
-lopencv_photo
-lopencv_stitching
-lopencv_video
-lopencv_videoio
-lcpufeatures
-lIlmImf
-littnotify
-llibjasper
-llibjpeg-turbo
-llibpng
-llibprotobuf
-llibtiff
-llibwebp
-lquirc
-ltbb
-ltegra_hal
-lz
-ldl
-lm
-llog

  C++ プロジェクトが他のライブラリに依存している場合は、依存ライブラリの対応するバージョンを見つけて、それを上記の方法に従って追加のライブラリ ディレクトリと追加の依存関係に追加することを忘れないでください。たとえば、プロジェクトで libusb ライブラリを使用し、 C++ プロジェクト so ライブラリの armeabi-v7a バージョンの場合は、プロジェクトに libusb.h を追加し、追加のライブラリ ディレクトリと追加の依存関係に libusb so ライブラリの amreabi-v7a バージョンを追加する必要があります。これは、ライブラリのリンクを真に完了し、その関数を呼び出すことができる唯一の方法です。


コンパイル操作

  以下に示すように、コンパイルする so ライブラリのバージョンを変更します。ここでは、so ライブラリの armeabi-v7a バージョンである ARM バージョンを選択しました。
ここに画像の説明を挿入します
  

  コンパイルの開始時に C++ 設定でいくつかの問題が発生するため、構成をいくつか変更する必要があります。
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
  コンパイル後、コンソールに上記の表示が表示され、コンパイルが成功したことが示されます。その他の問題が発生した場合は、コメント領域に投稿してください。
  

so の関数はエクスポートされ、Android で呼び出されます。


1: エクスポート関数の作成

  新しく作成された Android プロジェクト内に cpp ファイルが自動的に生成されます。このファイルでは、JNIEXPORT jfloat JNICALL Java_com_jniexample_JNIInterface_*** (JNIEnv env、jclass type、jfloatArray buf) メソッドが、呼び出すために Java にエクスポートされるメソッドです。ファイルの内容は次のとおりです。

#include "opencvTest2.h"
#include <jni.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "opencvTest2", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "opencvTest2", __VA_ARGS__))

using namespace cv;
using namespace std;
//这里的extern就是将你需要使用到的c++算法中的函数进行导出进行使用。
extern float TestOpencv(float* buf, int len);
extern float TestMath();
extern int ScanIDCardMoveToReadPosition(int timeout);
extern "C" {
    
    
	/*此简单函数返回平台 ABI,此动态本地库为此平台 ABI 进行编译。*/
	const char * opencvTest2::getPlatformABI()
	{
    
    
	#if defined(__arm__)
	#if defined(__ARM_ARCH_7A__)
	#if defined(__ARM_NEON__)
		#define ABI "armeabi-v7a/NEON"
	#else
		#define ABI "armeabi-v7a"
	#endif
	#else
		#define ABI "armeabi"
	#endif
	#elif defined(__i386__)
		#define ABI "x86"
	#else
		#define ABI "unknown"
	#endif
		LOGI("This dynamic shared library is compiled with ABI: %s", ABI);
		return "This native library is compiled with ABI: %s" ABI ".";
	}

	//C++导出给Java类使用的命名规范
	//Java_packagename_classname_functionname
	//第一个传参总是JNIEnv* env
	//第二个传参 如果是static成员函数就是jclass type,
	//		    如果是非static成员函数就是jobject thiz,
	//第三个传参才是真正的参数
	JNIEXPORT jfloat JNICALL
		Java_com_jniexample_JNIInterface_CVTestSum(JNIEnv* env, jclass type, jfloatArray buf) //这个用来导出给Java使用
	{
    
    
		auto len = env->GetArrayLength(buf);
		jboolean notcopy = JNI_FALSE;
		float* fptr = env->GetFloatArrayElements(buf, &notcopy);//从Java内存转换到native指针
		return TestOpencv(fptr, len);
	}
	JNIEXPORT jfloat JNICALL
		Java_com_jniexample_JNIInterface_TestSum(JNIEnv* env, jclass type, jfloatArray buf)//这个用来导出给Java使用
	{
    
    
		auto len = env->GetArrayLength(buf);
		jboolean notcopy = JNI_FALSE;
		float* fptr = env->GetFloatArrayElements(buf, &notcopy);
		float sum = 0;
		for (size_t i = 0; i < len; i++)
		{
    
    
			sum += fptr[i];
		}
		return sum;
	}
	//测试动态库是否可以使用
	JNIEXPORT jfloat JNICALL
		Java_com_jniexample_JNIInterface_TestMath(JNIEnv* env, jclass type)//这个用来导出给Java使用
	{
    
    
		return TestMath();
	}
	//ScanIDCardMoveToReadPosition测试c++项目中的函数能否使用
	JNIEXPORT jint JNICALL
		Java_com_jniexample_JNIInterface_ScanIDCardMoveToReadPosition(JNIEnv* env, jclass type, jint timeout)//这个用来导出给Java使用
	{
    
    
		int i= ScanIDCardMoveToReadPosition(timeout);
		return i;
	}

	void opencvTest2()
	{
    
    
	}
	opencvTest2::opencvTest2()
	{
    
    
	}
	opencvTest2::~opencvTest2()
	{
    
    
	}
}


2: Android プロジェクトにインポート.
  Android のルート ディレクトリに libs フォルダーを作成し、ダイナミック ライブラリの下に対応するバージョンのフォルダーを作成して、下図のダイナミック ライブラリを保存します。
ここに画像の説明を挿入します
ここに画像の説明を挿入します

Android ディレクトリの build.gradle を変更します。
ここに画像の説明を挿入します

  上の図の青いフォルダー java に Java クラスを追加します。この Java クラスを作成するためのパッケージ パスとクラス名は、手順 1 で記述した JNIEXPORT メソッドの Java_com_jniexample_JNIInterface 名に従って作成する必要があることに注意してください。そうしないと、ライブラリは作成できません。エクスポート方法。
ここに画像の説明を挿入します


  このクラスは、.so でエクスポートされた関数 Java_com_jniexample_JNIInterface_CVTestSum および Java_com_jniexample_JNIInterface_TestSum とのインターフェイスとして使用されます。
したがって、次のステップは、これら 2 つの関数のエクスポートを作成することです。
ここに画像の説明を挿入します

これらをプロジェクトで使用し、
ここに画像の説明を挿入します

正常に呼び出して結果を出力します。
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/ccjjjjdff/article/details/129746119