A production-shaped OpenCV 4 camera template for Android. Camera2 preview, a native C++/JNI bridge, and every feature in its own pluggable file.
A barebones but complete starting point for OpenCV 4.x on Android. It wires up a live
Camera2 preview through OpenCV's
JavaCamera2View, adds an NDK bridge so the same frames can be
processed in C++ via JNI, and keeps every
user-facing capability in its own file.
The last part is the point: the app doubles as a template. Each feature is a CameraFeature
registered on one line, so the project can be reduced to a bare preview or extended without
touching unrelated code.
Three apps with the same UI, one per integration style. Everything shared lives in the
camera-ui library: the camera view, the bottom bar, the dialogs and the feature framework. Each
app supplies only its own effect implementations, which is the part that differs by language.
| Module | Language | Effects implemented in | Effect count |
|---|---|---|---|
app/ |
Kotlin | Mat extension functions over the OpenCV Java bindings |
17 |
java/ |
Java | OpenCV Java bindings, no Kotlin in this app's sources | 11 |
cpp/ |
Kotlin plus C++ | Native cv:: calls over JNI, only the Mat address crosses |
11 |
app/ additionally has Haar cascade face detection. Build any of them with
./gradlew :app:installDebug, :java:installDebug or :cpp:installDebug.
Each app is a thin subclass of CameraScreenActivity that names its effects:
class MainActivity : CameraScreenActivity() {
override fun createFeatures(): List<CameraFeature> =
listOf(
FilterPicker(this, KotlinEffects.all()),
FaceDetection(this),
) + defaultFeatures()
}defaultFeatures() supplies torch, canvas scaling, the FPS overlay, resolution selection, shutter
feedback, EXIF orientation and rotating control icons, so a new example gets the whole UI for free.
| Feature | File | Notes |
|---|---|---|
| Effect picker | features/FilterPicker.kt |
16 OpenCV effects, "No Effect" default, applied per frame |
| Face detection | features/FaceDetection.kt |
Haar cascade, boxes drawn on the frame |
| Resolution picker | features/ResolutionPicker.kt |
Every supported size, with aspect ratio and megapixels |
| Canvas fit / fill | features/CanvasScaleControl.kt |
Letterbox or centre-crop, no camera restart |
| Flash torch | features/TorchControl.kt |
Availability-checked per camera |
| FPS overlay | features/FpsOverlayControl.kt |
Tinted chip, toggleable |
| Shutter flash | features/ShutterEffect.kt |
Flash plus the system shutter sound |
| Photo orientation | features/PhotoOrientation.kt |
Writes the EXIF orientation tag |
| Rotating controls | features/ControlsRotator.kt |
Icons rotate, the bar stays at the bottom |
Also included: front/back switching and capture to MediaStore.
Features are registered in a single list in MainActivity:
private val features: List<CameraFeature> by lazy {
listOf(
FilterPicker(this),
FaceDetection(this),
ResolutionPicker(this, binding.CvCamera),
// ...
)
}The list order is also the frame-processing order, so effects run before face boxes are drawn on
top. A new capability means a new file implementing CameraFeature plus one line here. Dropping a
capability means deleting its file and its line.
CameraFeature provides attach/detach, onResume/onPause, onMenuItemSelected,
processFrame, onCameraSwitched, onPhotoCaptureStarted and onPhotoSaved, all optional.
-
Install JDK 21.
-
Clone the repository, then fetch the OpenCV SDK:
./setupOpenCV_4x.sh
This downloads the OpenCV 4.14.0 Android SDK, verifies its SHA-256, extracts it to
opencvsdk4140/, and applies the patches inpatches/. Re-running it on a prepared SDK is a no-op.To do it by hand instead: download the OpenCV 4.14.0 Android release, unzip it beside the project, rename
OpenCV-android-sdktoopencvsdk4140, and adjust the path insettings.gradle.ktsif you put it elsewhere. -
Sync Gradle and run on a device:
./gradlew installDebug
The Android emulator's virtualscene back camera renders a 3D room, which is enough to exercise
the preview, effects and capture:
emulator -avd <name> -camera-back virtualscene -no-snapshot-load
./gradlew installDebug
adb shell pm grant com.os.cvCamera android.permission.CAMERA
adb shell am start -n com.os.cvCamera/.MainActivity-no-snapshot-load matters: loading a snapshot written by a different GPU backend crashes the
emulator with "change of renderer detected".
./gradlew spotlessApply # format (ktlint)
./gradlew spotlessCheck assembleDebug \
:app:lintDebug :app:testDebugUnitTest # what CI runs- Version catalog: every dependency and SDK level lives in
gradle/libs.versions.toml. - Kotlin DSL: all build scripts are
.kts. - Spotless + ktlint: enforced in CI.
- Timber: logging, debug builds only.
- Conventional commits: enforced by convention;
cliff.tomldrives the changelog.
CvCheckUnitTest asserts the linked OpenCV version, which is the guard that the SDK swap in
setupOpenCV_4x.sh actually took effect.
Kotlin, OpenCV 4, Android, Android Studio, Camera2, NDK, JNI, C++, Version Catalog, Kotlin DSL, Spotless, Material 3