Feature: Low Light Detection
Relevant source files
The following files were used as context for generating this wiki page:
- app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt
- app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt
- TODO.md
- ROADMAP.md
- app/src/main/java/com/musagenius/ocrapp/data/ocr/ImagePreprocessor.kt
- app/src/test/java/com/musagenius/ocrapp/domain/model/ImageQualityTest.kt
The Low Light Detection feature is a critical component of the camera system, designed to improve Optical Character Recognition (OCR) accuracy by assessing the lighting conditions of the camera preview in real-time. Its primary purpose is to analyze camera frames, determine if the lighting is sufficient for a quality scan, and provide feedback to the user, such as recommending the use of flash. This proactive quality check helps prevent poor captures that would lead to failed or inaccurate text extraction.
As outlined in the project's development plan, "Low-light detection and warning" is a core part of the image quality features for the CameraX integration. The system is built to be efficient, analyzing image luminance on a background thread to avoid blocking the UI. The result of the detection directly influences the CameraUiState, enabling the UI to display warnings or suggestions.
Sources: TODO.md, ROADMAP.md, app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt
Architecture and Core Components
The feature is encapsulated within the LowLightDetector class, which is designed as a thread-safe singleton to be used across the application.
Sources: app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:13
LowLightDetector Class
This diagram shows the structure of the LowLightDetector class, its key methods, and the nested LightingCondition enum.
The class has one primary public function, detectLightingCondition, which takes a CameraX ImageProxy object and returns a LightingCondition enum. The analysis is performed asynchronously on the Dispatchers.Default coroutine dispatcher to ensure the main thread is not blocked.
Sources: app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:35-37
Lighting Levels
The detection logic categorizes the ambient light into three distinct levels based on predefined brightness thresholds.
| Condition | Description | Brightness Threshold |
|---|---|---|
GOOD | Sufficient lighting for OCR. | >= 60f |
LOW | Suboptimal lighting; flash is recommended. | < 60f and >= 30f |
VERY_LOW | Very poor lighting; flash is strongly recommended. | < 30f |
Sources: app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:16-20, 40-43
The constants defining these thresholds are located in the LowLightDetector companion object.
| Constant | Value | Description |
|---|---|---|
VERY_LOW_LIGHT_THRESHOLD | 30f | The brightness value below which the condition is considered VERY_LOW. |
LOW_LIGHT_THRESHOLD | 60f | The brightness value below which the condition is considered LOW. |
Sources: app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:16-17
Detection Logic
The core of the feature is the calculateAverageBrightness method, which efficiently computes the average luminance of a camera frame.
This flowchart illustrates the process of analyzing an ImageProxy to determine the lighting condition.
Brightness Calculation
To maximize performance, the calculation relies on the following principles:
- Y-Plane Analysis: It directly uses the Y plane (luminance) from the
YUV_420_888image format, which is standard for camera previews. This avoids costly color space conversions. - Pixel Sampling: Instead of analyzing every pixel, it samples pixels at a regular interval defined by
SAMPLE_RATE = 8. This significantly reduces the number of computations per frame while still providing a reliable brightness average.
Sources: app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:23, 53-54, 66
The implementation iterates through the image buffer, respecting the rowStride and pixelStride to correctly access pixel data.
// app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:50-80
private fun calculateAverageBrightness(imageProxy: ImageProxy): Float {
// ...
val yPlane = imageProxy.planes[0]
val yBuffer = yPlane.buffer
// ...
val pixelStride = yPlane.pixelStride
val rowStride = yPlane.rowStride
val width = imageProxy.width
val height = imageProxy.height
var sum = 0L
var count = 0
// Sample pixels at regular intervals for performance
for (y in 0 until height step SAMPLE_RATE) {
for (x in 0 until width step SAMPLE_RATE) {
val index = y * rowStride + x * pixelStride
if (index < ySize) {
val luminance = yBuffer[index].toInt() and 0xFF
sum += luminance
count++
}
}
}
return if (count > 0) {
sum.toFloat() / count
} else {
LOW_LIGHT_THRESHOLD + 1 // Return safe value if no samples
}
}
Sources: app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt:50-80
UI Integration
The detected lighting condition is propagated to the UI layer through the CameraUiState. This state holds the current lightingCondition and a boolean showLowLightWarning to control the visibility of any warning messages.
State Management Flow
The following sequence diagram shows how a camera frame is analyzed and how the result updates the UI state. This flow is typically orchestrated by a ViewModel.
CameraUiState and CameraEvent
The CameraUiState data class includes fields specifically for managing lighting feedback:
lightingCondition: An enum of typeLowLightDetector.LightingConditionthat holds the latest assessment.showLowLightWarning: A boolean to control the display of a warning message to the user.
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt:18-19
The UI or an analyzer component communicates the detected condition to the ViewModel via the UpdateLightingCondition event.
// app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt:100
sealed class CameraEvent {
// ... other events
data class UpdateLightingCondition(val condition: LowLightDetector.LightingCondition) : CameraEvent()
// ... other events
}
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt:100
Conclusion
The Low Light Detection feature is a proactive and performance-conscious system that plays a vital role in the application's goal of achieving high OCR accuracy. By analyzing camera frames efficiently on a background thread and integrating seamlessly with the UI state management, it provides users with timely feedback to improve their image capture quality. This aligns with the project's roadmap to build robust image quality features directly into the camera experience, ultimately leading to more reliable text extraction.
Sources: ROADMAP.md, app/src/main/java/com/musagenius/ocrapp/data/camera/LowLightDetector.kt