2

First time Android developer, I've been struggling for the past few months with OpenCV's JavaCameraView, which always displays a blank/black screen. I've followed lots of tutorials online, but following them to a tee has brought 0 results. I know for a fact that my code is correctly loading OpenCV and Logcat describes that the code tried starting the camera view, but to no avail. I need help. The problematic part of the code is this:

public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {
//Activate Camera
private BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback (this) {
    @Override
    public void onManagerConnected(int status) {
        if (status == BaseLoaderCallback.SUCCESS) {
            OpenCVCamView.enableView();                                //Enable Cam View
            Log.d(TAG, "Tried enabling Camera View!");
        } else { super.onManagerConnected(status); }
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "on Create");
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    setContentView(R.layout.activity_main);                                     //UI Layout loaded
    OpenCVCamView = findViewById(R.id.cam_view);                                    
    textView = findViewById(R.id.arduino_log);                                  
    textView.setVisibility(SurfaceView.INVISIBLE);                                
    OpenCVCamView.setVisibility(SurfaceView.VISIBLE);                              
    OpenCVCamView.setCvCameraViewListener(this);                                   
@Override
public void onCameraViewStarted(int width, int height) {
    Log.d(TAG, "on Camera view Started");
    rgba = new Mat (height, width, CvType.CV_8UC4);                         
    rgbaF = new Mat (height, width, CvType.CV_8UC4);                        
    rgbaT = new Mat (height, width, CvType.CV_8UC4);                        
    intMat = new Mat(height, width, CvType.CV_8UC4);                        
    gray = new Mat (height, width, CvType.CV_8UC1);                         
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    rgba = inputFrame.rgba();                                               
    Log.d(TAG, "on Camera Frame");
    Core.transpose(rgba,rgbaT);
    Imgproc.resize(rgbaT,rgbaF,rgbaF.size(),0,0,0);
    Core.flip(rgbaF,rgba,1);
    return rgba;
    }
}

My activity_main XML file is this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <org.opencv.android.JavaCameraView
        android:id="@+id/cam_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        opencv:camera_id="any"/>
        <TextView
        android:id="@+id/arduino_log"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="0dp"
        android:visibility="invisible" />
</RelativeLayout>

I'm using OpenCV version 4.3 for Android.

AshFL
  • 23
  • 3

1 Answers1

7

Found myself in the exact same rabbit hole today and then learned about the mOpenCvCameraView.setCameraPermissionGranted() method but wasn't too sure where to put it. Luckily there was already your question and another answer that describes how to request permissions for using a camera and when to call that method: Android OpenCV camera example is just showing black screen

This is my take on the subject:

class MainActivity : AppCompatActivity(), CameraBridgeViewBase.CvCameraViewListener2 {

    companion object {
        private const val REQUEST_CODE_CAMERA_PERMISSION = 101
    }

    private lateinit var mOpenCvCameraView: CameraBridgeViewBase
    private lateinit var mIntermediateMat: Mat
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // this is ViewBinding for Android
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // check for necessary camera permission
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            when {
                ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.CAMERA
                ) == PackageManager.PERMISSION_GRANTED -> {
                    // camera permission granted
                    activateOpenCVCameraView()
                }
                shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
                    // In an educational UI, explain to the user why your app requires this
                    // permission for a specific feature to behave as expected. In this UI,
                    // include a "cancel" or "no thanks" button that allows the user to
                    // continue using your app without granting the permission.
                }
                else -> {
                    // directly ask for the permission.
                    requestPermissions(
                        arrayOf(Manifest.permission.CAMERA),
                        REQUEST_CODE_CAMERA_PERMISSION
                    )
                }
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // overengineered check for if permission with our request code and permission name was granted
        if (requestCode == REQUEST_CODE_CAMERA_PERMISSION) {
            val indexOfCameraPermission = permissions.indexOf(Manifest.permission.CAMERA)
            if (indexOfCameraPermission != -1) {
                if (grantResults.isNotEmpty()) {
                    if (grantResults[indexOfCameraPermission] == PackageManager.PERMISSION_GRANTED) {
                        Toast.makeText(
                            applicationContext,
                            "Camera permission granted!",
                            Toast.LENGTH_LONG
                        ).show()
                        activateOpenCVCameraView()
                    } else {
                        Toast.makeText(
                            applicationContext,
                            "Camera permission is required to run this app!",
                            Toast.LENGTH_LONG
                        ).show()
                    }
                }
            }
        }
    }

    private fun activateOpenCVCameraView() {
        // everything needed to start a camera preview
        mOpenCvCameraView = binding.cameraView
        mOpenCvCameraView.setCameraPermissionGranted()
        mOpenCvCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_ANY)
        mOpenCvCameraView.visibility = SurfaceView.VISIBLE
        mOpenCvCameraView.setCvCameraViewListener(this)
        mOpenCvCameraView.enableView()
    }


    override fun onResume() {
        super.onResume()
        // there's no need to load the opencv library if there is no camera preview (I think that sounds reasonable (?))
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.CAMERA
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            if (!OpenCVLoader.initDebug()) {
                log("Internal OpenCV library not found. Using OpenCV Manager for initialization");
                OpenCVLoader.initAsync(
                    OpenCVLoader.OPENCV_VERSION_3_0_0, this,
                    mLoaderCallback
                )
            } else {
                log("OpenCV library found inside package. Using it!");
                mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
            }
        }
    }

    private val mLoaderCallback: BaseLoaderCallback = object : BaseLoaderCallback(this) {
        override fun onManagerConnected(status: Int) {
            when (status) {
                LoaderCallbackInterface.SUCCESS -> {
                    log("OpenCV loaded successfully")
                    activateOpenCVCameraView()
                }
                else -> super.onManagerConnected(status)
            }
        }
    }

    override fun onCameraViewStarted(width: Int, height: Int) {
        log("onCameraViewStarted")
        mIntermediateMat = Mat()
    }

    override fun onCameraViewStopped() {
        log("onCameraViewStopped")
        // Explicitly deallocate Mats
        mIntermediateMat.release()
    }

    override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame?): Mat {
        log("onCameraFrame")
        val rgba = inputFrame!!.rgba()

        return rgba
    }

    override fun onDestroy() {
        mOpenCvCameraView.disableView()
        super.onDestroy()
    }

    private fun log(message: String) {
        Log.i("MainActivity", message)
    }
}
rexxar
  • 1,671
  • 1
  • 21
  • 27
  • Thanks a lot! I too found by sheer luck the PermissionsGranted function, that did the trick. It's low-key annoying that most tutorials and docs omit this key part of the Android process. – AshFL May 29 '20 at 19:36
  • Cannot resolve method 'setCameraPermissionGranted' in 'CameraBridgeViewBase' im using opencv 341. please any solution? – Ambesh Tiwari Dec 08 '21 at 15:46
  • @AmbeshTiwari that permission helper method was added in OpenCV 4.x; [see this commit](https://github.com/opencv/opencv/commit/2734291b3533f2bb22d8e86442301b9fbf71d012#diff-aea4fe55ab1661b9857d13e39dc0e54e09c8c8310bfd95e9586b83a11454de83R229). not sure how it was handled before that version. – rexxar Jan 20 '22 at 14:22