Setting-up Dlib and OpenCV for Android

A step-by-step guide on how to integrate natively Dlib and (or) OpenCV for enhanced Android applications.

Android with OpenCV and Dlib

In this guide I’ll show how to integrate Dlib 19.16 and OpenCV 4.0.1 with Android. If you need only one of them you can skip the steps related to a specific library.

I’m currently using Android Studio 3.2. Kotlin support is not necessary, so you can freely use Java (along C++, anyway). But supporting Kotlin since the beginning of the project could be handy for the future.

0. Prerequisites

In order to build native code and library, three tools are necessary: LLDB, CMake, NDK. All of them can be downloaded from Android Studio at Tools > SDK Manager > SDK Tools.

I also made a little script (available here) that automates the entire process by:

  • Compiling Dlib,
  • Copying the generated .so files for Dlib and OpenCV to your project.

Anyway, if your are curious the procedure is explained on chapter 2.

1. Project Creation

Downloading the needed libraries:

We’ll need them later.

Now lets open Android Studio and create a new project, with:

  • C++ support, and
  • Kotlin support (optional)

Click next, select the minimum API level you need (mine is 16) and then select empty activity.

Once you get to this point remember to select C++11, that’s fundamental!. The exception and runtime info support is also optional.

2. Compiling Dlib (optional step)

Unlike OpenCV, Dlib is provided only with its source and header files. So, we have to compile it by ourselves. This can be done (quite easily) with the android-cmake tool (located within the android sdk folder) in conjunction with ninja (Windows) or make (Linux).

At this point, we have to locate (or set environment variables) for:

  • android-cmake executable (mine is at ..\Android\sdk\cmake\3.10.2.4988404\bin\cmake.exe),
  • ndk-bundle location (mine is at ..\Android\sdk\ndk-bundle),
  • android.toolchain.cmake file (mine is at ..\Android\sdk\ndk-bundle\build\cmake\android.toolchain.cmake).

We open a terminal and type:

# I assume that android-cmake is set on $PATH:
cd \path\to\dlib\
mkdir build
cd build
# compile:
cmake -DBUILD_SHARED_LIBS=1
-DCMAKE_TOOLCHAIN_FILE=android\toolchain\path
-DANDROID_NDK=ndk-bundle\path
-GNinja # only for windows users!
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_CXX_FLAGS=-std=c++11 -frtti -fexceptions
-DANDROID_ABI=abi-you-want-to-support
-DANDROID_PLATFORM=android-16 # >= 16 supported
-DANDROID_TOOLCHAIN=clang
-DANDROID_STL=c++_shared
-DANDROID_CPP_FEATURES=rtti exceptions
"..\path\to\dlib\CMakeLists.txt"
cmake --build . # done

You can compile native code for different platforms (or ABI) except for armeabi that is not supported anymore. Thus, the most common are:

  • armeabi-v7a
  • arm64-v8a
  • x86
  • x86_64.

The code above will compile the entire Dlib for a chosen ABI. It generates a shared library named libdlib.so located inside build\dlib\.

More informations about: ABIs, STL, flags, …, can be found here.

Shrinking the library size

After building the library we can notice that the generated libdlib.so are around 20MB in size, that’s too much!

The generated library is non-stripped. Thus, by stripping it we can effectively reduce its size.

In order to perform string-stripping a proper tool is needed. Fortunately this tool came along with the android sdk. Mine (I’m using Windows — for linux user just replace windows-x86_64 with linux-x86_64) are located at \Android\sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin.

Notice that there is a specific strip tool for a particular ABI.

  • armeabi-v7a: requires the arm-linux-androideabi-strip,
  • arm64-v8a: requires the aarch64-linux-android-strip and,
  • x86, x86_64: either needs the x86_64-linux-android-strip.

For example, if you want to strip a libdlib.so for the ABI arm64-v8a you just need to type into the terminal:

# stripping libdlib.so for arm64-v8a:
$> cd path-to-stripper\
$> aarch64-linux-android-strip.exe --strip-unneeded library-path\libdlib.so

For a more complete reference, check this article.

3. Launching the Script

The setup script I prepared is made of three main functions:

  • Compile-Dlib that’s self-explanatory,
  • Dlib-Setup that copies every libdlib.so to your Android project,
  • Opencv-Setup that copies every libopencv_java4.so to your project.

Inside the script, I defined several variables for each part of the process. Before executing it, is necessary to edit them all. The variables are:

  • AndroidCmake: location of the android-cmake executable,
  • NDK: location of the Android ndk-bundle,
  • TOOLCHAIN: location of the android.toolchain.cmake file,
  • ABIs: a list of the ABI you want to support,
  • PROJECT_PATH: the path of the Android project you want to configure.

Now it’s time to grab your powerful script and execute it!

On my Windows machine, I had trouble executing the script on my PowerShell. I discover that typing powershell -ExecutionPolicy ByPass -File setup.ps1 solves the problem.

4. The CMakeLists

Now we have all the necessary files and libs in the right place (our project). The next step is to let CMake wire everything together by writing a CMakeLists file. Here it is:

Please note: according to the script I’ve made, in order to let it work correctly, the CMakeLists.txt should be stored inside the app folder of your android project. If you want you can save it to another path, but editing the script is required.

5. The build.gradle

The last file we need is the build.gradle (app):

(Bonus) Add Java Support for OpenCV

From Android Studio go to File > New > Import Module and select the path of your opencv module, like so:

Then is necessary to specify that the opencv module is a dependency for the app module. So, going to File > Project Structure click on app (under modules) and open the Dependencies panel. Press the green plus button (on the right top) and select module dependency:

Next, is necessary to edit the Opencv build.gradle (located inside the opencv module in the root path of the project) by:

  • Setting the minSdkVersion equal to the value of the minSdkVersion defined in the app’s build.gradle.
  • Changing the value of the variable res.srcDirs to ['res'].
Change the path of the opencv resource directory

Sync the project.

Finally, in your MainActivity (or main entry point of the application) put a static (Java) or init (Kotlin) block with:

// Kotlin:
init {
System.loadLibrary("native-lib") // or whatever name you choose

// prints initialization stuff, useful to know if opencv works
OpenCVLoader.initDebug()
}

Conclusions

I hope the guide will help you to overcome all the difficulties related to native code integration.

If you need an advanced application (as reference for Dlib and OpenCV usage) you can find the mine here.

The compile-copy-stuff script is, again, here.

And, Dlib models can be found here (mine) and here (official).

That’s all folks! 😄

PhD student in Data Science and Computation @Alma Mater Studiorum