libopencv_java4.soは(多くの場合)無駄を含んでいる
OpenCVはコンパイル済みのAndroid用ビルドが公式で配布されています。
https://github.com/opencv/opencv/releases
opencv-4.0.1-android-sdk.zipをダウンロードし、共有ライブラリlibopencv_java4.soとjavaファイル群をAndroidプロジェクトの所定の位置に置くと、AndroidでOpenCVの処理をJava/Kotlinから使うことができます。
しかし共有ライブラリlibopencv_java4.soは多くの場合無駄を含んでいます。
nmコマンドでJNI関数のシンボルを確認してみます。
wget https://github.com/opencv/opencv/releases/download/4.0.1/opencv-4.0.1-android-sdk.zip
unzip opencv-4.0.1-android-sdk.zip
cd OpenCV-android-sdk/
nm -D sdk/native/libs/arm64-v8a/libopencv_java4.so | less
シンボル名のJava_org_opencv_の次に注目してください。
前略
00000000001f0918 T Java_org_opencv_calib3d_StereoSGBM_setUniquenessRatio_10
中略
000000000018e4bc T Java_org_opencv_core_Algorithm_clear_10
中略
00000000001c9828 T Java_org_opencv_dnn_DictValue_DictValue_10
中略
00000000001e8830 T Java_org_opencv_features2d_AKAZE_create_10
中略
00000000001d746c T Java_org_opencv_imgcodecs_Imgcodecs_haveImageReader_10
中略
00000000001a50d8 T Java_org_opencv_imgproc_CLAHE_apply_10
中略
00000000001bb888 T Java_org_opencv_ml_ANN_1MLP_create_10
中略
000000000020fb5c T Java_org_opencv_objdetect_BaseCascadeClassifier_delete
中略
00000000001c7034 T Java_org_opencv_photo_AlignExposures_delete
中略
000000000021db0c T Java_org_opencv_video_BackgroundSubtractorKNN_delete
中略
00000000001da178 T Java_org_opencv_videoio_VideoCapture_VideoCapture_10
以下略
それら、calib3d、core、dnn、features2d等はそれぞれOpenCVのモジュールに相当します。多くの場合、持っているモジュールをすべて使うことは無いと思います。私はcore、imgcodecs、imgprocしか使いません。今回はその3つだけを持つlibopencv_java4.soを自分でビルドします。
2022年版を書きました。
この記事のやり方は2022年の最新のOpenCV、Android SDK、Android NDKではできなかったので、新たにDockerを使用したビルド方法の記事を書きました。
【2022年版】OpenCV for Androidのlibopencv_java4.soのバイナリサイズを減らし、APKのサイズを減らす
以降は2022年版をご参照ください。
ビルド環境の構築
Android NDKをAndroid Studioからインストールします。
Android SDK、Android NDKのパスを環境変数に設定します。
export ANDROID_SDK=$HOME/Library/Android/sdk
export ANDROID_NDK=$ANDROID_SDK/ndk-bundle/
Python3をpyenv等でセットアップします。
cmake、ninja、ccacheをインストールします。
# Macの場合
brew install cmake ninja ccache
# Ubuntuの場合はaptコマンド
ビルドスクリプトの編集
ソースコードをダウンロード、展開し、ビルドスクリプトを編集します。
wget https://github.com/opencv/opencv/archive/4.0.1.tar.gz
tar -xvzf 4.0.1.tar.gz
cd opencv-4.0.1/platforms/
# エディタで android/build_sdk.py を開く
build_library関数のcmake_varsを編集します。このスクリプトは内部でcmakeコマンドを呼んでいて、この編集はcmakeの引数を変更するものになります。
def build_library(self, abi, do_install):
cmd = ["cmake", "-GNinja"]
cmake_vars = dict(
CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
INSTALL_CREATE_DISTRIB="ON",
WITH_OPENCL="OFF",
WITH_IPP=("ON" if abi.haveIPP() else "OFF"),
WITH_TBB="ON",
BUILD_EXAMPLES="OFF",
BUILD_TESTS="OFF",
BUILD_PERF_TESTS="OFF",
BUILD_DOCS="OFF",
BUILD_ANDROID_EXAMPLES="OFF", # ビルドに失敗するのでOFF
INSTALL_ANDROID_EXAMPLES="OFF", # ビルドに失敗するのでOFF
BUILD_LIST="core,imgcodecs,imgproc,java" # 追加する
)
用途に応じてBUILD_LISTを変更します。例えば局所特徴量が必要な場合はfeatures2dを追加します。
あとはこのスクリプトを実行するだけです。ビルドが始まります。
android/build_sdk.py
生成物
OpenCV-android-sdkディレクトリにlibopencv_java4.soとjavaファイル群が作成されました。
find OpenCV-android-sdk/sdk/native/libs -type f
OpenCV-android-sdk/sdk/native/libs/armeabi-v7a/libopencv_java4.so
OpenCV-android-sdk/sdk/native/libs/x86/libopencv_java4.so
OpenCV-android-sdk/sdk/native/libs/arm64-v8a/libopencv_java4.so
OpenCV-android-sdk/sdk/native/libs/x86_64/libopencv_java4.so
find OpenCV-android-sdk/sdk/java/src -type f
OpenCV-android-sdk/sdk/java/src/org/opencv/core/Scalar.java
OpenCV-android-sdk/sdk/java/src/org/opencv/core/MatOfFloat6.java
OpenCV-android-sdk/sdk/java/src/org/opencv/core/Size.java
以下略
シンボルを確認すると、確かにcore、imgcodecs、imgprocしか含まれていません。
nm -D OpenCV-android-sdk/sdk/native/libs/arm64-v8a/libopencv_java4.so | less
前略
00000000000fc2c4 T Java_org_opencv_core_Algorithm_clear_10
中略
0000000000116cb8 T Java_org_opencv_imgcodecs_Imgcodecs_haveImageReader_10
中略
0000000000114044 T Java_org_opencv_imgproc_CLAHE_apply_10
以下略
削減した容量
libopencv_java4.soに含めるモジュールをcore,imgcodecs,imgprocだけにすると、以下のようにバイナリサイズを削減できました。
ABI | 元のファイルサイズ | 今回のファイルサイズ |
---|---|---|
armeabi-v7a | 9MB | 5MB |
arm64-v8a | 17MB | 9MB |
追記 ABIがx86とx86_64のsoファイルの容量を削減する
build_sdk.pyをもう1行変更しないと、ABIがx86とx86_64の容量が大きいままでした。
def haveIPP(self):
return False #self.name == "x86" or self.name == "x86_64"
ABI | haveIPP関数そのまま | haveIPP関数の戻り値False |
---|---|---|
x86 | 25M | 10M |
x86_64 | 39M | 11M |