A real-time optical flow visualization app for Android that computes and displays motion vectors from the camera feed using native C++ implementation of the Lucas-Kanade algorithm.
- Real-time Optical Flow Computation: Calculates dense motion vectors from camera frames using Lucas-Kanade method
- Native C++ Implementation: High-performance optical flow calculations using JNI
- Dual Camera Support: Works with both front and back cameras with automatic orientation handling
- Visual Flow Representation: Color-coded vectors showing motion magnitude and direction
- Filtered out: Very low (< 1.0) or aberrant (> 6.0) motion
- White (60% opacity, 2px): Medium motion (1.0 - 3.0)
- White (100% opacity, 4px): High motion (> 3.0)
- Adaptive Resolution: Automatically handles different camera resolutions with proper viewport scaling
- Rotation Aware: Correctly transforms flow vectors for any device orientation with crop compensation
Optical Flow Visualization with Color-Coded Motion Vectors
Real-time optical flow overlay on live camera feed showing motion tracking:
without_camera_view.mp4
Isolated optical flow vectors demonstrating the algorithm's motion detection:
with-camera-view.mp4
- Language: Kotlin + C++
- UI Framework: Jetpack Compose
- Camera: CameraX API
- Native Layer: JNI + CMake
- Image Processing: YUV plane extraction and grayscale conversion
The app implements the Lucas-Kanade optical flow method:
- Sobel Gradient Calculation: Computes spatial derivatives (∂I/∂x, ∂I/∂y)
- Window-based Accumulation: Aggregates gradients over configurable windows
- Least Squares Solution: Solves the optical flow equation using the structure tensor
- Velocity Vector Output: Returns (u, v) motion vectors for each grid cell
Default Parameters:
- Cell Size: 12 pixels (grid spacing)
- Window Size: 5 pixels (neighborhood radius)
- Update Frequency: 10 Hz (100ms intervals)
OptiFluxApp/
├── app/src/main/
│ ├── cpp/ # Native C++ code
│ │ ├── jni_bridge.cpp # JNI interface
│ │ ├── optical_flow_calculator_core.cpp # Lucas-Kanade implementation
│ │ └── optical_flow_calculator_core.h
│ ├── java/com/acassion/optifluxapp/
│ │ ├── model/
│ │ │ └── OpticalFlowModel.kt # Data model for flow vectors
│ │ ├── native/
│ │ │ └── NativeOpticalFlowCalculator.kt # JNI bindings
│ │ ├── ui/screens/cameraview/
│ │ │ └── CameraViewScreen.kt # Main UI with Canvas rendering
│ │ ├── utils/
│ │ │ ├── OpticalFlowAnalyzer.kt # CameraX analyzer
│ │ │ └── Constants.kt
│ │ └── viewmodel/
│ │ └── CameraPreviewViewModel.kt # Camera and flow state management
│ └── res/ # Resources
- Android Studio: Arctic Fox or newer
- Android NDK: Version 21 or higher
- CMake: 3.22.1+
- Minimum SDK: 24 (Android 7.0)
- Target SDK: 34 (Android 14)
-
Clone the repository:
git clone <repository-url> cd OptiFluxApp
-
Open in Android Studio:
- File → Open → Select the project directory
-
Sync Gradle:
- Wait for Gradle sync to complete
- NDK and CMake will be configured automatically
-
Build and Run:
- Connect an Android device or start an emulator
- Click Run (▶) or use
Shift + F10
The app uses Kotlin DSL for Gradle configuration:
android {
compileSdk = 34
defaultConfig {
minSdk = 24
targetSdk = 34
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
}- Grant Camera Permission: On first launch, allow camera access
- View Optical Flow: Motion vectors appear as white lines overlaid on the camera feed
- Move Objects: Wave your hand or move objects to see flow vectors
- Thicker, brighter lines indicate faster motion
- Very slow or aberrant motion is automatically filtered out
- Camera Support: Works with both front (with mirroring) and back cameras
void computeFlow(
const uint8_t* previousImage,
int previousFrameWidth,
const uint8_t* currentImage,
int currentFrameWidth,
std::vector<OpticalFlowVector>& out
);- Processes grayscale YUV images
- Computes flow vectors on a grid
- Returns position (x, y), velocity (u, v), and magnitude
The app handles complex transformations to map flow vectors from camera space to screen space:
- Front Camera: Horizontal mirroring + rotation + crop compensation
- Back Camera: Rotation + crop compensation
- Supported Rotations: 0°, 90°, 180°, 270°
- Scale Modes: FILL_CENTER with automatic crop offset calculation for pixel-perfect alignment
- Backpressure Strategy:
STRATEGY_KEEP_ONLY_LATESTto drop frames under load - Native Processing: C++ for compute-intensive operations
- Efficient Buffer Management: Reuses ByteArrays for image data
- Adaptive Grid: Configurable cell size for performance tuning
- Outlier Filtering: Filters aberrant vectors (magnitude < 1.0 or > 6.0) to reduce visual noise
- Viewport Scaling: Proper coordinate transformation with crop compensation for accurate overlay alignment
- Jetpack Compose (UI)
- CameraX (Camera API)
- Kotlin Coroutines (Async operations)
- Accompanist Permissions (Permission handling)
- ReLinker (Native library loading)
- Android NDK (C++ compilation)
- CMake (Build system)
- Standard C++ libraries (math, algorithm, vector)
Edit Constants.kt to adjust optical flow parameters:
object Constants {
const val cellSize: Int = 15 // Grid spacing (pixels)
const val windowSize: Int = 5 // Neighborhood radius (pixels)
}Performance Tips:
- Increase
cellSizefor better performance but sparser flow - Increase
windowSizefor smoother but slower flow - Lower values = more vectors but higher CPU usage
- Outlier filtering automatically removes unreliable vectors (magnitude < 1.0 or > 6.0)
- Ensure NDK is installed:
Tools → SDK Manager → SDK Tools → NDK - Check CMakeLists.txt has no trailing commas in
add_library()
- Check Logcat for "OpticalFlowAnalyzer" and "CameraViewScreen" messages
- Verify camera resolution is logged correctly
- Ensure sufficient lighting and motion (magnitude must be between 1.0 and 6.0)
- Very low motion (< 1 pixel/frame) is automatically filtered out
- Check rotation value in logs (should show EffectiveFrame, Scale, and FrameCrop)
- Verify
isFrontCameraflag matches actual camera - Review coordinate transformation logic and crop compensation
- Ensure both Preview and ImageAnalysis use the same ResolutionSelector
This project is licensed under the MIT License - see the LICENSE file for details.
Alexis Cassion
- Lucas-Kanade optical flow algorithm
- Android CameraX team
- Jetpack Compose community