Hey guys,
I’m going to share here my personal write-up of the process I followed to develop natively (C++) using OpenCV, then use this code to build an Android app from Unity. I use MacOS. Big disclaimer: I don’t have a background in software development so I can’t guarantee the correctness of any of this. All I know it’s that it’s working for me right now I also recommend you have a read of all the resources I link, they helped me a lot.
The process Thomas shared is valid to compile a native (C++) OpenCV code into a shared library to be read from the Unity Editor and in a Windows machine, although it can be easily adapted for MacOS, but that’s a different story. If you want something that works on Android, you instead need to cross compile your code in the form of an .so library.
Note: In this SO post (Answer by Bilelmnasser) there’s a brief explanation of how the libraries need to be loaded from Unity according to the target platform. And here’s the information Unity provides regarding developing native plugins for Android.
The main thing to understand (sorry if this is too basic for you) is that when one downloads the regular OpenCV version, this is meant to be compiled for your machine’s OS (Windows, Linux, MacOS). For example in my case (MacOS) this means that the compilation results in a set of .dylib libraries that are installed in /usr/local/include and /usr/local/lib, and can be used as explained by Thomas in his tutorials. If instead we want to be able to run OpenCV from an Android platform, we need to use OpenCV4Android, which allows development of OpenCV code for Android, both natively in C++ and in Java (by using the provided wrapper). OpenCV4Android can be downloaded from their website (I specifically downloaded this package - 3.2.0 version).
If like me you’re interested in developing C++ OpenCV code, you can follow the process I’m about to share. If instead you want to use Java OpenCV, this SO post (don’t read the verified answer, but instead the one that follows) contains a brilliant step by step for setting up Android Studio for developing Java OpenCV. I tried it and it works.
Android allows the use of native code in its apps by means of their NDK toolset. This toolset is available to download on its own, here (and then it can be used in any IDE or through command line), or it can be downloaded through the SDK manager inside Android Studio, as explained here. I chose to use the NDK within Android Studio as it seems to be the recommended way. This NDK essentially allows you to cross compile native code that can be run on an Android platform, which is exactly what we need.
OpenCV offers very messy tutorials of how to use OpenCV4Android. Available resources are gathered on this page. The main problem is that the tutorials are outdated. In the case of developing in Java it’s all based on the deprecated ADT plugin for Eclipse, which gave GUI-based access to many of the command-line Android SDK tools. On the other hand, in the case of native OpenCV development, these tutorials offer instructions on how to use the NDK within Eclipse and use ndk-build, which is slowly being replaced by CMake (I think NDK support is now directly built in CMake, see here). I think these instructions are still valid, but I preferred to opt for the “recommended” way of using the NDK directly from Android Studio and using CMake.
The key things that are inside OpenCV4Android folder (info from this tutorial from OpenCV’s website):
⇒ sdk/java folder is key if you’re going to use Java OpenCV code, since it contains the Java wrapper. It can be found in sdk/java/src/org/opencv. We’re not going to use the contents of this folder since we’re going to use C++ instead.
⇒ sdk/native folder contains OpenCV C++ headers (for JNI code) and native Android libraries (*.so and *.a) for ARM-v5, ARM-v7a and x86 architectures. Header files are in sdk/native/jni/include and libraries in sdk/native/libs.
Assuming you have now have OpenCV4Android and Android Studio + NDK, now we can move on to how I’ve done the whole process in Android Studio. Reading these instructions really helped me to figure this out.
⇒ Create project with C/C++ support
⇒ Check “Phone and tablet”, leave the recommended minimum SDK version.
⇒ Include an Empty Activity (this will create files necessary for the C++ functionality)
⇒ Keep the rest of the options as they come by default.
⇒ Android Studio has now created a project folder. Inside it, in app/src/main/cpp/, Android Studio will have created a file called native-lib.cpp, which contains an example function called stringfromJNI() that we can ignore. However, you can write your C++ OpenCV code in this file.
⇒ In the app folder inside your project folder, a file called CMakeLists.txt will be created. This is the file with all the instructions on how to compile your native C++ code. Leave it as it is right now, we’ll be modifying it in a bit.
⇒ Copy the libraries of OpenCV4Android folder, contained in sdk/native/libs (all folders for different architectures) in app/main inside your Android Studio project. Change the name of the parent folder from libs to jniLibs. Delete all static libraries (.a) from all folders, only leaving libopencv_java3.so in each of the architecture’s folders. The other ones are not needed.
⇒ Copy the header files in OpenCV4Android (sdk/native/jni/include) to a folder in app/src/main/cpp. I opted to name the parent folder include as well. I think this folder could be anywhere, the only important thing is that it needs to be in a location accessible by Android Studio.
⇒ Edit the CMakeLists.txt file so that 1) it imports the OpenCV library (libopencv_java3.so) as a shared library; 2) it adds the library as a target; 3) it includes the path to OpenCV’s header files. This is a copy of the file I’m using, which does all of this.
For more information about using CMake with Android Studio, read the
Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
Creates and names a library, sets it as either STATIC
or SHARED, and provides the relative paths to its source code.
You can define multiple libraries, and CMake builds them for you.
Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
Sets the library as a shared library.
SHARED
Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
add_library( test-lib SHARED IMPORTED )
set_target_properties(test-lib PROPERTIES IMPORTED_LOCATION
{CMAKE_SOURCE_DIR}/src/main/jniLibs/{ANDROID_ABI}/libopencv_java3.so)
include_directories(src/main/cpp/include)
Searches for a specified prebuilt library and stores the path as a
variable. Because CMake includes system libraries in the search path by
default, you only need to specify the name of the public NDK library
you want to add. CMake verifies that the library exists before
completing its build.
find_library( # Sets the name of the path variable.
log-lib
Specifies the name of the NDK library that
you want CMake to locate.
log )
Specifies libraries CMake should link to your target library. You
can link multiple libraries, such as libraries you define in this
build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib test-lib
Links the target library to the log library
included in the NDK.
${log-lib} )
Note: You can ignore the log-lib stuff, it’s just there because it was in the sample of Android studio.
⇒ Now the project is ready to be built in Android Studio: Build->Make project. This will generate a shared library (.so file, in my case called libnative-lib.so) for our native code for each architecture in jniLibs. The generated libraries can be found in the folder app/build/intermediates/cmake/debug/obj.
⇒ Now in Unity! Create a folder called Plugins inside the Assets folder. Then another called Android inside Plugins, and another called libs inside Android. Copy the folders “x86” and “armeabi-v7a” from app/build/intermediates/cmake/debug/obj. These are the processor architectures that Android supports (ARMv7 and x86), see here for more info. Android also supports MIPS but it’s the least popular and not supported by Unity. Also, the 64 counterparts of ARM and x86 are not supported by Unity either. When we later build the app in Unity, it generates a FAT APK by default, which works in both architectures. This setting can be changed in Build Settings->Player Settings ->Android->Other settings->Device filter. IMPORTANT EDIT: Also copy inside the corresponding architecture folder in Plugins the file libopencv_java3.so that can be found in OpenCV4Android/OpenCV-android-sdk/sdk/native/libs.
Once you’ve copied your .so in this folder, Unity will treat them as plugins → see here for details on the Plugin inspector and the different file extensions that are treated as plugins by Unity. The folder structure is very important as detailed in the Deployment section of this article, so Unity picks up the right library according to the target architecture (mmm).
Important resources provided by Unity:
=> Finally, to use the C++ functionality from Unity, you can follow Thomas’ tutorials, in particular Part 3.
Note: If you don’t need to develop natively, I believe there’s also the possibility to use OpenCV directly from Unity with Emgu CV, which is a cross platform .Net wrapper to OpenCV, allowing OpenCV functions to be called from .NET compatible languages such as C#, VB, VC++, IronPython etc. To make this work follow this SO post (haven’t tried it).
Let me know if this works for you!