Slim OpenCV

Scanly ships a self-built OpenCV 5.0.0 that contains only the modules the app uses, instead of the full org.opencv:opencv:5.0.0.1 Maven AAR. Each APK gets about 32 MB smaller (both ABIs), and the app's results and speed stay the same.

Official 5.0.0.1Slim 5.0.0
libopencv_java5.so arm64-v8a33.4 MB14.3 MB
libopencv_java5.so armeabi-v7a22.3 MB9.3 MB
GitHub release APK (both ABIs)146.2 MB113.9 MB

What is in it

  • Modules: core, imgproc, geometry (imgproc needs it, and Indra calls Geometry.getRotationMatrix2D), flann (geometry needs it), java, imgcodecs and videoio. OpenCV's Android Java classes always include org.opencv.android.Utils (imports Imgcodecs) and NativeCameraView (imports videoio), so they do not compile without those two.
  • Codecs: every image codec in imgcodecs is off. The app never encodes or decodes images through OpenCV. videoio on Android is only the system MediaNDK backend.
  • Same as the official build: source tag, NDK 27.2.12479018, CMake 3.22.1, compiler flags and -O3, CPU baseline and dispatch (arm64: NEON FP16 baseline, NEON_DOTPROD / NEON_FP16 / NEON_BF16 dispatch), KleidiCV 26.03 and carotene acceleration, TBB 2022.1, ITT and 16 KB page alignment. Compare third_party/opencv/build-info-<abi>.txt (OpenCV's getBuildInformation() for the slim library) with the official library's build information.
  • Left out: dnn, features, calib, objdetect, photo, stitching, video, ptcloud, stereo and highgui. The app uses none of them; the Java classes of those modules are absent.

Building it

app/build.gradle.kts uses third_party/opencv/opencv-5.0.0-slim.aar when it exists and falls back to the Maven artifact otherwise, which has the same API. Release builds stop with an error without the slim AAR, so a release never ships the full library by accident. To build a release with the full library on purpose (for an A/B comparison), move the AAR aside and pass -Pscanly.allowFullOpenCv=true.

Requirements: Android SDK with ndk/27.2.12479018 and cmake/3.22.1, JDK 17 in JAVA_HOME (OpenCV's Gradle projects use Kotlin 1.8.20, which rejects newer JVM targets), Python 3.8+ and internet access (OpenCV source, KleidiCV, TBB, Gradle). From the repo root:

$env:JAVA_HOME = "<path to JDK 17>"
python scripts/opencv/build_slim_opencv.py --sdk "$env:LOCALAPPDATA/Android/Sdk" --work C:/ocvb

The script runs OpenCV's own platforms/android/build_sdk.py and build_java_shared_aar.py (the scripts behind the official AAR) with scripts/opencv/slim.config.py. A full build takes about 40 minutes. It works on Windows, Linux and macOS; on Windows it starts Gradle through gradlew.bat, which is the only change to how OpenCV's scripts run.

Checking a new build

OpenCvParityHarnessTest (androidTest) runs every OpenCV code path in the app on fixed generated pages: all filter presets at three intensities, the adjustment sliders, signature extraction, the image analysis and Indra preprocessing. It logs a SHA-256 of every output and median timings to logcat (tag OcvParity). Run it once with each library on the same phone and diff the HASH lines; see the test's KDoc for the commands. It only runs when the opencvParity instrumentation argument is true.

Results for the first slim build, on a Galaxy S20 FE (Android 13):

  • All 344 output hashes were identical between the official and the slim library in every run (the official library itself is deterministic run to run).
  • Total time of the 18 timed operations, alternating official and slim APKs: −0.1%, −0.6% and −0.4% for the slim library in the three pairs the phone did not start throttling in.

Upgrading OpenCV

Change OPENCV_VERSION and NDK_VERSION in scripts/opencv/build_slim_opencv.py to match the new official release's build information, rebuild, compare build-info-<abi>.txt with the official library, and run the parity harness against the official AAR before switching.

View source on GitHub · Markdown