KamVision is an Open-Source library which provides easy access to the complex Camera 2 API (Strategic choice for low-level control in contrast to CameraX) in Jetpack Compose. You can use internal elements like Preview and Frame Capturing as a one-line method call in your projects.
| Category | Details |
|---|---|
| Core Technologies | Kotlin, Java, Coroutines, Flow, MVVM (Model-View-ViewModel), Jetpack Compose, Camera 2 |
| Design Patterns | Singleton, Builder, Facade |
| Preview and Image Handling | Camera 2 Api, AndroidView for preview start/stop, Bitmap handling for single and multiple frames |
The core of this library is provided by Camera2 Api & Jetpack Compose .
- Low-Level Control: Camera2 API provides direct, in-depth control over the camera hardware. You can manipulate almost every aspect of the camera pipeline, including:
- Manual Exposure Control: Set ISO, shutter speed, and aperture (if supported by hardware) precisely. This is crucial for creative photography and specific lighting conditions.
- Manual Focus: Achieve precise focus control, including setting specific focus distances.
- RAW Capture: Access uncompressed RAW sensor data (DNG format), which offers the most flexibility for post-processing.
- Advanced Capture Request Parameters: Control a vast array of parameters for each capture, such as white balance, color correction, noise reduction, edge enhancement, tone mapping, and more, using CaptureRequest.Builder.
- Stream Configuration: Define custom output streams and formats for different use cases (e.g., high-resolution still image, real-time preview, video recording, image analysis).
- Metadata Access: Get detailed metadata for each frame, including sensor timestamps, lens characteristics, and capture settings.
- Multi-camera Control: For devices with multiple cameras, Camera2 gives you fine-grained control over which camera to use and how to combine their outputs.
- Flexibility for Complex Use Cases: If you're building something beyond standard photo/video capture, like custom computer vision pipelines, augmented reality applications, or specialized imaging tools, Camera2's granular control is essential
The 👎 of Camera2 api:
- It is more complex and time-consuming to implement, but I've already done it for you ;)
- Start Camera Preview with only one line function call
- Stop Camera Preview
- Switch between front and back Camera lenses
- Single frame capturing
- Multiple frame capturing based on a user-defined number
- Full compatibility with Jetpack Compose
- Fluent UI
- Optimized with coroutines
🎯 Download the demonstration of the library usage in a sample Android app KamVision-Showcase.apk
KamVision is available on JitPack.io/KamVision. To integrate it into your Android project, follow these steps:
- Add the JitPack repository to your project-level
settings.gradle.ktsfile:
// settings.gradle.kts
pluginManagement {
repositories {
// ... other repositories
maven { url = uri("[https://jitpack.io](https://jitpack.io)") }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
// ... other repositories
maven { url = uri("[https://jitpack.io](https://jitpack.io)") }
}
}
- Add the dependency to your module-level
build.gradle.kts(e.g.,app/build.gradle.kts):
// app/build.gradle.kts
dependencies {
implementation("com.github.kamyab9k:KamVision:LATEST_TAG")
}
- Replace
LATEST_TAGwith the actual version tag you want to use (e.g.,v1.0.1).
KamVision makes integrating the Camera2 API into your Jetpack Compose applications simple. Follow these three simple steps to get your camera preview up and running quickly.
Note: This is just a simple setup to get started; you can alter or implement the UI and other parameters based on your project or needs!
- Request Camera Permissions:
class MainActivity : ComponentActivity() {
private var cameraPermissionGranted = mutableStateOf(false)
private val requestCameraPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
cameraPermissionGranted.value = isGranted
if (!isGranted) {
// Optionally, inform the user that permission is needed
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val permissionGranted by remember { cameraPermissionGranted }
LaunchedEffect(Unit) {
// Check initial permission status
cameraPermissionGranted.value = isCameraPermissionGranted()
if (!cameraPermissionGranted.value) {
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
if (permissionGranted) {
CameraPreview()
} else {
// Optional: Show a message to the user while waiting for permission
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("Camera permission is required to use this app.")
}
}
}
}
private fun isCameraPermissionGranted(): Boolean {
return ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
}
}- Integrate KamVision's Camera Preview
@Composable
fun CameraPreview() {
val coroutineScope =
rememberCoroutineScope()
var cameraService: CameraService? by remember { mutableStateOf(null) }
var textureView: TextureView? by remember { mutableStateOf(null) }
Box(modifier = Modifier.fillMaxSize()) {
AndroidView(
factory = { ctx ->
TextureView(ctx).also {
textureView = it
}
},
modifier = Modifier.fillMaxSize(),
update = { tv ->
if (cameraService == null) {
cameraService = CameraService.Builder(
context = tv.context,
textureView = tv,
scope = coroutineScope
)
.setFrontCamera(false)
.setAutoBrightnessMode(true)
.setAutoFocusMode(true)
.setFlashMode(2)
.build()
cameraService?.startPreview()
}
cameraService?.captureFrame(50)
cameraService?.getCapturedFrames { frames: List<Bitmap> ->
println("Hi $frames")
}
}
)
Button(
onClick = {
coroutineScope.launch {
cameraService?.switchCamera()
}
},
modifier = Modifier.align(Alignment.BottomCenter)
) {
Text("Switch Camera")
}
}
DisposableEffect(Unit) {
onDispose {
cameraService?.stopCameraPreview()
}
}
}- Leverage KamVision Functions:
...
update = { tv ->
if (cameraService == null) {
cameraService = CameraService.Builder(
context = tv.context,
textureView = tv,
scope = coroutineScope
)
.setFrontCamera(false) // KamVision parameter: 'true' for front camera, 'false' for back camera
.setAutoBrightnessMode(true) // KamVision parameter: 'true' to enable auto brightness adjustment, 'false' otherwise
.setAutoFocusMode(true) // KamVision parameter: 'true' to enable auto focus, 'false' otherwise
.setFlashMode(2) // KamVision parameter: Sets the flash mode (0: OFF, 1: Single Flash, 2: Torch)
.build() // Builds the CameraService instance with the specified configurations
cameraService?.startPreview() // KamVision function: Starts the camera preview
}
cameraService?.captureFrame(50) // KamVision function: Captures the specified number of frames (e.g., 50 frames)
cameraService?.getCapturedFrames { frames: List<Bitmap> -> // KamVision function: Retrieves the list of captured frames
println("Hi $frames")
}
}
)
Button(
onClick = {
coroutineScope.launch {
cameraService?.switchCamera() // KamVision function: Asynchronously switches between front and back cameras
}
},
modifier = Modifier.align(Alignment.BottomCenter)
) {
Text("Switch Camera")
}
}
DisposableEffect(Unit) {
onDispose {
cameraService?.stopCameraPreview()
}
}
}There are several functions you can call with KamVision.
| Function | Example Usage |
| • startPreview() | cameraService?.startPreview() |
| Starts the camera preview. | |
| • stopPreview() | cameraService?.stopPreview() |
| Stops the camera preview. | |
| • captureFrame(Frames: Int) | cameraService?.captureFrame(15) |
| Capture frames and the number is given in parameter . | |
| • getCapturedFrames(frames: List) | cameraService?.getCapturedFrames { frames: List ->} |
| Returns the list of captured frames in Bitmap format. | |
In addition to the builder functions, KamVision receives some configurations. For example:
// shows a Preview in Jetpack Compose AndroidView
Box(modifier = Modifier.fillMaxSize()) {
AndroidView(
factory = { ctx ->
// Create the TextureView
TextureView(ctx).also {
textureView = it
}
},
modifier = Modifier.fillMaxSize(),
update = { textureView ->
if (cameraService == null) {
cameraService = CameraService.Builder(
context = textureView.context,
textureView = textureView,
scope = coroutineScope
).build()Read more about these three important topics to better understand the functionality of this Library:
- Video capturing & more
- Adding more parameters
Copyright 2024 Mohammad Khosravi
Licensed under the Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
