2

I am trying to use OpenCV library for my Android app (OpenCV 4.6.0). But the size of my app is really big after I added the library. I want to only include the part of the library that I use, which is imgproc and core (specifically Sobel edge detection). I have tried to edit the CMakeLists.txt (which is located on libcxx_helper) to this:

cmake_minimum_required(VERSION 3.6)

# dummy target to bring libc++_shared.so into packages
add_library(opencv_jni_shared STATIC dummy.cpp)

set(OpenCV_DIR D:/Project/AndroidStudioProject/SobelProject/sdk/native/jni)

find_package(OpenCV REQUIRED core imgproc)
add_executable(opencv_sobel dummy.cpp)
target_link_libraries(opencv_sobel core imgproc ${EXTERNAL_LIBS})

I expect there will be a libopencv_sobel.so generated from the above CMakeLists.txt, but nothing is generated. So, I load the library with this:

init {
    System.loadLibrary('opencv_java4')
}

Then I run the app. But the size of my app is still the same. I am unfamiliar with C++ and NDK.

Any help would be very appreciated.

EDIT:

I follow exactly the same step from the documentation @Rohit Bhati gave.

This is my Android.mk

include $(CLEAR_VARS)

OPENCV_INSTALL_MODULES:=on

include D:\Project\AndroidStudioProject\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk

OPENCV_CAMERA_MODULES:=off
OPENCV_LIB_TYPE:=STATIC

and this is my Application.mk

APP_STL := c++_static # I changed it from gnustl_static because it is not supported anymore
APP_CPPFLAGS := -frtti -fexceptions

APP_ABI := arm64-v8a

APP_PLATFORM := android-21

both located at {myProject}/app/jni then I ran the ndk-build. After it completes, a libopencv_java4.so file is generated inside the libs/armv64-v8 folder, but the size is still the same.

I look into the documentation but I didn't found how to specify what library I want to include. Also, what is the purpose of this code

OPENCV_CAMERA_MODULES:=off

because it doesn't reduce the .so generated file size

EDIT 2

I followed the step from the related link by @Rohit Bhati with a slight different because the steps seems old.

/d/Project/AndroidStudioProject/OpenCV-android-sdk/sdk/native/staticlibs/arm64-v8a
$ D:/Android/Sdk/ndk/25.0.8775105/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android21-clang++ -shared -o libopencv_java4.so --sysroot=D:/Android/Sdk/ndk/25.0.8775105/toolchains/llvm/prebuilt/windows-x86_64/sysroot -Wl,--whole-archive libopencv_core.a libopencv_imgproc.a -Wl,--no-whole-archive

$ D:/Android/Sdk/ndk/25.0.8775105/toolchains/llvm/prebuilt/windows-x86_64/bin/llvm-strip libopencv_java4.so

A new libopencv_java4.so file then generated with smaller size. Then, I copy it to opencv-sdk/native/libs/armv64-v8a replacing the old file (which is bigger one). But, I get this error

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "_ZN3tbb4task13note_affinityEt" referenced by "/data/app/id.indevelopment.edgepaint-pG-fG4NFi7Mj7szExjH9ww==/lib/arm64/libopencv_java4.so"...
        at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
        at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
        at java.lang.System.loadLibrary(System.java:1667)
        at id.indevelopment.edgepaint.detector.EdgeDetection.<init>(EdgeDetection.kt:17)
        at id.indevelopment.edgepaint.di.AppModuleKt$detectorModule$1$1.invoke(AppModule.kt:16)
        at id.indevelopment.edgepaint.di.AppModuleKt$detectorModule$1$1.invoke(AppModule.kt:16)
        at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:54)
        at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:38)
        at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:111)
        at org.koin.core.scope.Scope.resolveValue(Scope.kt:255)
        at org.koin.core.scope.Scope.resolveInstance(Scope.kt:242)
        at org.koin.core.scope.Scope.get(Scope.kt:205)
        at id.indevelopment.edgepaint.di.AppModuleKt$viewModelModule$1$1.invoke(AppModule.kt:22)
        at id.indevelopment.edgepaint.di.AppModuleKt$viewModelModule$1$1.invoke(AppModule.kt:11)
        at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:54)
        at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:38)
        at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:111)
        at org.koin.core.scope.Scope.resolveValue(Scope.kt:255)
        at org.koin.core.scope.Scope.resolveInstance(Scope.kt:242)
        at org.koin.core.scope.Scope.get(Scope.kt:205)
        at org.koin.androidx.viewmodel.factory.DefaultViewModelFactory.create(DefaultViewModelFactory.kt:13)
        at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:83)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35)
        at id.indevelopment.edgepaint.ui.main.MainActivity.getViewModel(MainActivity.kt:77)
        at id.indevelopment.edgepaint.ui.main.MainActivity.setUpViewModel(MainActivity.kt:152)
        at id.indevelopment.edgepaint.ui.main.MainActivity.onCreate(MainActivity.kt:112)
        at android.app.Activity.performCreate(Activity.java:7893)
        at android.app.Activity.performCreate(Activity.java:7880)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1306)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3310)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3484)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2068)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7551)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)

2 Answers2

1

Finally, I'm able to find a decent solution to my problem, which is to build the OpenCV from sources with only the library that I needed. I will share the solution here in case anyone came across a similar situation.

Python is required for this solution. The step is:

  1. Get the OpenCV latest sources (in zip) from the GitHub.

  2. Extract the zip file and open the folder (you should be at the root folder). Skip step 3 if you use imgcodecs.

  3. Now, from the root folder, go to modules/java/generator/android/java/org/opencv/android and open Utils.java on any text editor. Comment all code that uses Imgcodecs class including the import statement. If you don't comment it, you will get an error when running the command at step 5 as it is trying to find an imgcodecs module which we don't include.

  4. Back to the root folder and open a command prompt.

  5. Run python platforms/android/build_sdk.py {output_path} ./ --ndk_path={your_ndk_path} --sdk_path={your_sdk_path} --modules_list="core,imgproc,java" --no_samples_build --config=ndk-22.config.py

    On the modules_list, java is required to generate the java wrapper. There are some other options available which you can look at the build_sdk.py code

  6. Wait for the process to complete and you will get the sdk at {output_path}/OpenCV-android-sdk/sdk

Now, the only problem with this solution is it runs 2-3x slower compared to the (all modules) sdk that OpenCV provided here (which is the sdk that I use before).

Dharman
  • 30,962
  • 25
  • 85
  • 135
0

Use Static library linking which will only pull in the functionality you actually want to use in your project instead of the whole library. This link will be helpful Here

or else you can checkout the same sample project of static linking on Github. Hope it helps.

Rohit Bhati
  • 371
  • 2
  • 11
  • Hi, I have look into the documentation but still have some problems. You can look at the EDIT section of my question. – Bernhard Josephus Aug 07 '22 at 10:28
  • You didn't have to follow exactly same approach as article, it was example made for camra module. Wait i will shere a same project with you. – Rohit Bhati Aug 08 '22 at 03:02
  • Check this answer, maybe this will help with what you want, https://stackoverflow.com/questions/44962460/how-to-reduce-the-apk-size-of-android-app-which-need-opencv-lib-only-for-image – Rohit Bhati Aug 08 '22 at 03:12
  • I follow the steps from the link also but still failed. Please take a look at the EDIT 2 section I added for the error. – Bernhard Josephus Aug 08 '22 at 07:12
  • Can you shere full log cat output here. – Rohit Bhati Aug 08 '22 at 11:42
  • Well, I am not sure but I think this issue came because apparently Gradle creates its own version of 'Android.mk' and defaults to the same API level as you have in compileSdkVersion, not minSdkVersion. `Application.mk`, so first add 'Application.mk' file with the following if you don't have one: `APP_CFLAGS += -I$(LOCAL_PATH) APP_ABI := all APP_PLATFORM := android-19` now add this file path to your `build.gradle` file: `externalNativeBuild { ndkBuild { path 'src/main/jni/Application.mk' } }` – Rohit Bhati Aug 08 '22 at 12:49
  • and you can check out these two posts [link1](https://stackoverflow.com/questions/21096819/jni-and-gradle-in-android-studio) and [link2](https://stackoverflow.com/questions/28740315/android-ndk-getting-java-lang-unsatisfiedlinkerror-dlopen-failed-cannot-loca?rq=1). hope its help and if you get any error to feel free to write here again. – Rohit Bhati Aug 08 '22 at 12:50
  • I finally got the solution which I already posted the answer here. Thank you so much for all the help! – Bernhard Josephus Aug 12 '22 at 10:49
  • First congregation , and wow man!! you have done lots of work. – Rohit Bhati Aug 12 '22 at 11:58