OpenCV应用 – 安卓相机在10秒后崩溃

huangapple go评论73阅读模式
英文:

OpenCV application - Android Camera crashes after 10 seconds

问题

package com.example.cv;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.widget.Toast;

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;

    private static final int MY_CAMERA_REQUEST_CODE = 100;

    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);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED)  {
            Log.d(TAG, "Permissions granted");
            javaCameraView.setCameraPermissionGranted();
            javaCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_BACK);
            javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
            javaCameraView.setCvCameraViewListener(this);
        } else {
            Log.d(TAG, "Permission prompt");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_CAMERA_REQUEST_CODE);
        }
    }

    @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);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == MY_CAMERA_REQUEST_CODE) {
            // camera can be turned on
            Toast.makeText(this, "camera permission granted", Toast.LENGTH_LONG).show();
            javaCameraView.setCameraPermissionGranted();
            javaCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_FRONT);
            javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
            javaCameraView.setCvCameraViewListener(this);
        } else {
            //camera will stay off
            Toast.makeText(this, "camera permission denied", Toast.LENGTH_LONG).show();
        }
    }
}
<!-- AndroidManifest.xml -->
<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>
<!-- activity_main.xml -->
<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>
英文:

Background

In this application, I was able to launch my camera preview that was developed in Android Studio with an aim to incorporate OpenCV into it. However, I face an error; the camera preview only lasts around 10 seconds until it crashes and the app shuts down. Would there be any way to resolve this? Here is my code;

MainActivity.java

package com.example.cv;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.widget.Toast;
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 = &quot;MainActivity&quot;;
JavaCameraView javaCameraView;
Mat mRGBA, mRGBAT;
private static final int MY_CAMERA_REQUEST_CODE = 100;
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, &quot;OpenCV is Configured or Connected successfully.&quot;);
}
else
{
Log.d(TAG, &quot;OpenCV not Working or Loaded.&quot;);
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED)  {
Log.d(TAG, &quot;Permissions granted&quot;);
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_BACK);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
} else {
Log.d(TAG, &quot;Permission prompt&quot;);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_CAMERA_REQUEST_CODE);
}
}
@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, &quot;OpenCV is Configured or Connected successfully.&quot;);
baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
}
else
{
Log.d(TAG, &quot;OpenCV not Working or Loaded.&quot;);
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_CAMERA_REQUEST_CODE) {
// camera can be turned on
Toast.makeText(this, &quot;camera permission granted&quot;, Toast.LENGTH_LONG).show();
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_FRONT);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
} else {
//camera will stay off
Toast.makeText(this, &quot;camera permission denied&quot;, Toast.LENGTH_LONG).show();
}
}
}

AndroidManifest.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
package=&quot;com.example.cv&quot;&gt;
&lt;uses-permission android:name=&quot;android.permission.CAMERA&quot;/&gt;
&lt;uses-feature android:name=&quot;android.hardware.camera&quot;/&gt;
&lt;uses-feature android:name=&quot;android.hardware.camera.autofocus&quot;/&gt;
&lt;uses-feature android:name=&quot;android.hardware.camera.front&quot;/&gt;
&lt;uses-feature android:name=&quot;android.hardware.camera.front.autofocus&quot;/&gt;
&lt;application
android:allowBackup=&quot;true&quot;
android:icon=&quot;@mipmap/ic_launcher&quot;
android:label=&quot;@string/app_name&quot;
android:roundIcon=&quot;@mipmap/ic_launcher_round&quot;
android:supportsRtl=&quot;true&quot;
android:theme=&quot;@style/AppTheme&quot;&gt;
&lt;activity android:name=&quot;.MainActivity&quot;&gt;
&lt;intent-filter&gt;
&lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
&lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
&lt;/intent-filter&gt;
&lt;/activity&gt;
&lt;/application&gt;
&lt;/manifest&gt;

activity_main.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;RelativeLayout
xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
tools:context=&quot;.MainActivity&quot;&gt;
&lt;org.opencv.android.JavaCameraView
android:id=&quot;@+id/my_camera_view&quot;
android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;fill_parent&quot;
/&gt;
&lt;/RelativeLayout&gt;

答案1

得分: 2

我在你的问题上进行了进一步的调查,以下是一些考虑事项:

1 - 我在这个之前的答案的评论中已经建议过你,一旦在onCameraFrame()内不再需要矩阵,就释放它们。忘记这一点可能会引发垃圾收集问题,就像这个答案或者这里中所概述的那样。你可以通过使用Mat.release()方法显式地触发这个过程,就像用户在相同的答案中所做的那样。你尝试过吗?根据我的测试,这样可以延长摄像头的使用时间,尽管还没有修复这个 bug<br/>
2 - 而从这个答案可以了解到,多次进行转置可能会产生其他问题。让我们看看你的代码:

mRGBAT = mRGBA.t();
Core.flip(mRGBA.t(), mRGBAT, 1);

这明显是我们可以改进的地方。你首先将mRGBA.t()赋值给mRGBAT,然后在调用Core.flip()时又要求重新计算mRGBA的转置矩阵。结果已经存储在mRGBAT中,因此没有必要再执行这样的操作。你可以像这样修复这个问题:

mRGBAT = mRGBA.t();
Core.flip(mRGBAT, mRGBAT, 1);

3 - Mat.t() 和 Core.transpose() 没有真正的区别,但前者是对后者的封装,我们可以直接使用后者。

完整的代码片段如下,将解决你的问题:

Mat mRGBA, mRGBAT, dst;

@Override
public void onCameraViewStarted(int width, int height)
{
    mRGBAT = new Mat();
    dst = new Mat();
}

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
{
    mRGBA = inputFrame.rgba();
    Core.transpose(mRGBA, mRGBAT);
    Core.flip(mRGBAT, mRGBAT, 1);
    Imgproc.resize(mRGBAT, dst, mRGBA.size());
    mRGBA.release();
    mRGBAT.release();
    return dst;
}
英文:

I did some further investigation on your issue and here are a few considerations:

1 - I had already suggested you in the comments of this previous answer to release the matrices inside onCameraFrame() as soon as they are not needed anymore. Forgetting this can generate garbage collection issues, as outlined in this answer or here. You can explicitly invoke this process to happen by using the Mat.release() method, as the user does in the same answer. Did you try it? from my tests it was extending the 10 seconds of camera life, despite not yet fixing the bug<br/>
2 - from this answer instead, we can understand that transposing too many times may create other troubles. Let's give a look at your code:

mRGBAT = mRGBA.t();
Core.flip(mRGBA.t(), mRGBAT, 1);

This definitely looks like something we can improve. You are first assigning mRGBA.t() to mRGBAT but then you ask again to calculate the transpose matrix for mRGBA when calling Core.flip(). The result is already stored in mRGBAT, thus there is no need to perform such operation again. Then you can fix this issue like this:

mRGBAT = mRGBA.t();
Core.flip(mRGBAT, mRGBAT, 1);

3 - there is no real difference between Mat.t() and Core.transpose() but the first is a wrapper around the second and we can directly use the latter

The entire snippet that will solve your issue is the following:

Mat mRGBA, mRGBAT, dst;
@Override
public void onCameraViewStarted(int width, int height)
{
mRGBAT = new Mat();
dst = new Mat();
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
{
mRGBA = inputFrame.rgba();
Core.transpose(mRGBA, mRGBAT);
Core.flip(mRGBAT, mRGBAT, 1);
Imgproc.resize(mRGBAT, dst, mRGBA.size());
mRGBA.release();
mRGBAT.release();
return dst;
}

huangapple
  • 本文由 发表于 2020年10月23日 02:50:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/64488713.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定