1

Background

I had started out this project on Android Studio with the intent of creating a OpenCV application that could process frames using the camera on my OnePlus android device. After running the application, I was gratified to see it finally launch on my device. However, the application shows up with a black screen where the camera preview should be. Here is my code for my MainActivity, activity_main, and AndroidManifest files:

EDIT: While the application launched on my device, I did give the application permission to use camera

MainActivity.java

package com.example.cv;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2
{
    private static String TAG = "MainActivity";
    JavaCameraView javaCameraView;
    Mat mRGBA, mRGBAT;


    BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(MainActivity.this) {
        @Override
        public void onManagerConnected(int status)
        {
            if (status == BaseLoaderCallback.SUCCESS) {
                javaCameraView.enableView();
            } else {
                super.onManagerConnected(status);
            }
        }
    };

    static
    {
        if (OpenCVLoader.initDebug())
        {
            Log.d(TAG, "OpenCV is Configured or Connected successfully.");
        }
        else
        {
            Log.d(TAG, "OpenCV not Working or Loaded.");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
        javaCameraView.setVisibility(SurfaceView.VISIBLE);
        javaCameraView.setCvCameraViewListener(MainActivity.this);

    }

    @Override
    public void onCameraViewStarted(int width, int height)
    {
        mRGBA = new Mat(height, width, CvType.CV_8UC4);
    }

    @Override
    public void onCameraViewStopped()
    {
        mRGBA.release();
    }

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
    {
        mRGBA = inputFrame.rgba();
        mRGBAT = mRGBA.t();
        Core.flip(mRGBA.t(), mRGBAT, 1);
        Imgproc.resize(mRGBAT, mRGBAT, mRGBA.size());
        return mRGBAT;
    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (javaCameraView != null)
        {
            javaCameraView.disableView();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (javaCameraView != null)
        {
            javaCameraView.disableView();
        }
    }


    @Override
    protected void onResume() {
        super.onResume();

        if (OpenCVLoader.initDebug())
        {
            Log.d(TAG, "OpenCV is Configured or Connected successfully.");
            baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
        }
        else
        {
            Log.d(TAG, "OpenCV not Working or Loaded.");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <org.opencv.android.JavaCameraView
        android:id="@+id/my_camera_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</RelativeLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cv">


    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera"/>
    <uses-feature android:name="android.hardware.camera.autofocus"/>
    <uses-feature android:name="android.hardware.camera.front"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Antonino
  • 3,178
  • 3
  • 24
  • 39
RunMildew
  • 67
  • 1
  • 13

1 Answers1

1

The issue you are facing is actually with the camera permission check

Starting from Android Marshmallow, the CAMERA permission is considered a dangerous one and the user must explicitly agree on its usage at runtime, clicking on the system dialog

camera permission request

This wouldn't happen for "normal permissions" [e.g. INTERNET] that are granted by default. If you are interested in which ones are dangerous and which not you can check for the one of interest directly in the Android documentation

What's happening with your initial code is that you are mentioning you will require the camera permission in your manifest file and then you are enabling it from the Android Settings [toggling the Camera slider]. But then, when you start the app there is nothing in the code that goes and checks that toggle. Then you get a black screen because Android assumes that the user has not given explicit consent

This link from the Android documentation should help you understand more. Here the snippet that will make your code work. In a nutshell, with onCreate() you go and check if the user has already granted the permission and if not you will ask it. In onRequestPermissionResult() there is the check we were talking about before. If the user agreed the camera will start, otherwise it won't

private static final int MY_CAMERA_REQUEST_CODE = 100;
int activeCamera = CameraBridgeViewBase.CAMERA_ID_FRONT;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);

    // checking if the permission has already been granted
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED) {
        Log.d(TAG, "Permissions granted");
        initializeCamera(javaCameraView, activeCamera);
    } else {
        // prompt system dialog
        Log.d(TAG, "Permission prompt");
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_CAMERA_REQUEST_CODE);
        }
}

// callback to be executed after the user has givenapproval or rejection via system prompt
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_CAMERA_REQUEST_CODE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // camera can be turned on
            Toast.makeText(this, "camera permission granted", Toast.LENGTH_LONG).show();
            initializeCamera(javaCameraView, activeCamera);
        } else {
            // camera will stay off
            Toast.makeText(this, "camera permission denied", Toast.LENGTH_LONG).show();
        }
    }
}

private void initializeCamera(JavaCameraView javaCameraView, int activeCamera){
    javaCameraView.setCameraPermissionGranted();
    javaCameraView.setCameraIndex(activeCamera);
    javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
    javaCameraView.setCvCameraViewListener(this);
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Antonino
  • 3,178
  • 3
  • 24
  • 39
  • Thank you so much! This was extremely helpful and the camera is up and running! However, I do have a follow-up, which might prompt me to make another question thread; after the camera starts, it stays on for around 10 seconds, and then the app crashes. Would there be any way to resolve this? – RunMildew Oct 19 '20 at 21:29
  • @RunMildew ciao! the issue you are having is with the processing happening in `onCameraFrame()` that after a few seconds becomes unsustainable. This one you are asking is a memory management issue though - totally unrelated to the black screen at inizialization time - so I would suggest you to open a new one. Also, since now it seems you are able to get the camera stream please mark this as the answer to your question, thanks! – Antonino Oct 20 '20 at 01:17
  • You're very welcome! I'm really new to software development, so hopefully one of the final questions I'll be asking is; would I have to create a new OnCameraFrame( ) method or class in order to make this sustainable? and would I just be copying and pasting the same `OnCameraFrame( )` that I had made prior? – RunMildew Oct 20 '20 at 11:47
  • @RunMildew no problem, we all started from zero so I perfectly understand ;) no, what I was recommending you before is to make the elaboration happening inside `onCameraFrame()` a little bit lighter. Just change its content to make it work smarter. For example in [this question](https://stackoverflow.com/a/39922008/4092588) the user had a very similar problem and was suggested already to release the matrices after usage. That's a good start I guess... ciao! – Antonino Oct 20 '20 at 12:28
  • Thank you for getting back to me! This will be the final question; So would I be required to replace the code that i already have on `OnCameraFrame()` with the one provided in the link? or would I be merely adding it onto it? Thank you so much in advance with this one! – RunMildew Oct 20 '20 at 23:31
  • simply speaking: you have to replace the code in `onCameraFrame()`. The link I gave you is a test that in your shoes I would start doing. Check if clearing the matrices as they do in that answer with `.release()` is enough and then you move on from there. Most probably you will need to slightly adapt any solution that you will find because you have your specific frame transformation to accomplish. Have a check even here on StackOverflow, I think there may be more people who faced a similar freeze. Good luck mate! – Antonino Oct 21 '20 at 00:23