Open
Description
System Information
Kotlin user OpenCV android version: 4.11.0 Operating System / Platform: Android 12
Detailed description
I am trying to display JavaCameraView in my Android app with OpenCV but when I install the app for the very first time and grant camera permission it does not show up the camera but when I reopen the app then it works, here is the code
package com.codingwithnobody.myandroidprojectcameracv
import android.content.ContentValues
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.opencv.android.CameraActivity
import org.opencv.android.CameraBridgeViewBase
import org.opencv.android.JavaCameraView
import org.opencv.android.OpenCVLoader
import org.opencv.core.Mat
import org.opencv.core.MatOfByte
import org.opencv.core.Point
import org.opencv.core.Scalar
import org.opencv.imgcodecs.Imgcodecs
import org.opencv.imgproc.Imgproc
import java.io.ByteArrayInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.Collections
import androidx.core.app.ActivityCompat
class MainActivity : CameraActivity(), CameraBridgeViewBase.CvCameraViewListener2 {
private lateinit var cameraView: JavaCameraView
private lateinit var button: Button
private var shouldCapture = false
private val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = mutableListOf(android.Manifest.permission.CAMERA).apply {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}.toTypedArray()
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
baseContext, it) == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
Toast.makeText(this, "Permissions granted by the user.", Toast.LENGTH_LONG).show()
cameraView.visibility = View.VISIBLE
cameraView.enableView()
cameraView.setCvCameraViewListener(this@MainActivity)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
cameraView = findViewById(R.id.cameraView)
button = findViewById(R.id.capture)
if(OpenCVLoader.initLocal()){
ActivityCompat.requestPermissions(
this,
REQUIRED_PERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
}
button.setOnClickListener {
shouldCapture = true
}
}
override fun getCameraViewList(): MutableList<out CameraBridgeViewBase> {
return Collections.singletonList(cameraView)
}
override fun onPause() {
super.onPause()
cameraView.disableView()
}
override fun onResume() {
super.onResume()
cameraView.enableView()
}
override fun onCameraViewStarted(width: Int, height: Int) {
cameraView.setMaxFrameSize(width, height)
}
override fun onCameraViewStopped() {
}
private fun matToByteArray(mat: Mat, ext: String = ".png"): ByteArray {
val converted = Mat()
Imgproc.cvtColor(mat, converted, Imgproc.COLOR_RGBA2BGR)
val buf = MatOfByte()
Imgcodecs.imencode(ext, converted, buf)
return buf.toArray()
}
override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame?): Mat {
val rgba = inputFrame!!.rgba()
val center = Point(rgba.cols() / 2.0, rgba.rows() / 2.0)
Imgproc.circle(rgba, center, 100, Scalar(255.0, 0.0, 0.0, 255.0), 5)
if (shouldCapture) {
shouldCapture = false
val frame = rgba.clone()
CoroutineScope(Dispatchers.Main).launch {
val bytes = matToByteArray(frame, ".png")
try {
val uri = saveImageToExternalStorage(
bytes = bytes,
fileName = "opencv_${System.currentTimeMillis()}",
extension = "png",
mimeType = "image/png"
)
Toast.makeText(this@MainActivity, "Saved to: $uri", Toast.LENGTH_SHORT).show()
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(this@MainActivity, "Failed to save image", Toast.LENGTH_SHORT).show()
}
}
}
return rgba
}
private suspend fun saveImageToExternalStorage(
bytes: ByteArray,
fileName: String,
extension: String,
mimeType: String
): Uri = withContext(Dispatchers.IO) {
val cv = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, "$fileName.$extension")
put(MediaStore.Video.Media.MIME_TYPE, mimeType)
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
val resolver = contentResolver
val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
val uriSavedVideo = resolver.insert(collection, cv)
uriSavedVideo?.let { uri ->
resolver.openFileDescriptor(uri, "w")?.use { pfd ->
FileOutputStream(pfd.fileDescriptor).use { out ->
ByteArrayInputStream(bytes).use { inputStream ->
val buffer = ByteArray(8192)
var length: Int
while (inputStream.read(buffer).also { length = it } > 0) {
out.write(buffer, 0, length)
}
}
}
// Update the content values to mark the video as not pending
cv.clear()
resolver.update(uri, cv, null, null)
}
}
uriSavedVideo ?: throw IOException("Failed to create new MediaStore record.")
}
}
Here is the output video
Screen_recording_20250614_215336.mp4
Steps to reproduce
Clone this repo and open it in Android Studio
Issue submission checklist
- I report the issue, it's not a question
- I checked the problem with documentation, FAQ, open issues, forum.opencv.org, Stack Overflow, etc and have not found any solution
- I updated to the latest OpenCV version and the issue is still there
- There is reproducer code and related data files (videos, images, onnx, etc)