Issue
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)
Solution
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:
Get the OpenCV latest sources (in zip) from the GitHub.
Extract the zip file and open the folder (you should be at the root folder). Skip step 3 if you use
imgcodecs
.Now, from the root folder, go to
modules/java/generator/android/java/org/opencv/android
and openUtils.java
on any text editor. Comment all code that usesImgcodecs
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 animgcodecs
module which we don't include.Back to the root folder and open a command prompt.
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 thebuild_sdk.py
codeWait 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).
I will update it here if I found any solution regarding the speeds or if anyone have the solution, please kindly share it.
Answered By - Bernhard Josephus
Answer Checked By - Katrina (JavaFixing Volunteer)