OutOfMemoryError генерируется из-за предварительного просмотра камеры

Мне удалось открыть камеру Android с помощью opencv. но когда я использую код, который фиксирует ориентацию камеры — «см. код, указанный ниже в методе onCameraFrame (..)», — приложение вылетает через несколько секунд, и logcat генерирует сообщения, размещенные в belwo, в «разделе logcat».

Чтобы решить эту проблему:

  1. я пытался использовать SystemClock.sleep, чтобы приложение задержалось на некоторое время, но это не было хорошим решением, потому что оно задерживает предварительный просмотр камеры

  2. я попытался максимально уменьшить размер кадра, поэтому я установил его на 320x240, используя mOpenCvCameraView.setMaxFrameSize(320, 240) «упомянутый ниже в разделе кода». Но этому решению удалось продлить предварительный просмотр камеры на несколько минут, но в конце концов приложение также вышло из строя.

Подскажите, пожалуйста, какое правильное решение в такой ситуации и как этого избежать?

Логкат:

10-07 14:42:43.445 30510-31656/com.example.bak.opencvcamera_00 E/cv::error(): OpenCV Error: Insufficient memory (Failed to allocate 307200 bytes) in void* cv::OutOfMemoryError(size_t), file /home/maksim/workspace/android-pack/opencv/modules/core/src/alloc.cpp, line 52
10-07 14:42:43.445 30510-31656/com.example.bak.opencvcamera_00 E/cv::error(): OpenCV Error: Assertion failed (u != 0) in void cv::Mat::create(int, const int*, int), file /home/maksim/workspace/android-pack/opencv/modules/core/src/matrix.cpp, line 411
10-07 14:42:43.450 30510-31656/com.example.bak.opencvcamera_00 E/org.opencv.core.Mat: Mat::n_1t() caught cv::Exception: /home/maksim/workspace/android-pack/opencv/modules/core/src/matrix.cpp:411: error: (-215) u != 0 in function void cv::Mat::create(int, const int*, int)
10-07 14:42:43.450 30510-31656/com.example.bak.opencvcamera_00 E/AndroidRuntime: FATAL EXCEPTION: Thread-9334
Process: com.example.bak.opencvcamera_00, PID: 30510
CvException [org.opencv.core.CvException: cv::Exception: /home/maksim/workspace/android-pack/opencv/modules/core/src/matrix.cpp:411: error: (-215) u != 0 in function void cv::Mat::create(int, const int*, int)
at org.opencv.core.Mat.n_t(Native Method)
at org.opencv.core.Mat.t(Mat.java:852)
at com.example.bak.opencvcamera_00.MainActivity.onCameraFrame(MainActivity.java:109)
at org.opencv.android.CameraBridgeViewBase.deliverAndDrawFrame(CameraBridgeViewBase.java:391)
at org.opencv.android.JavaCameraView$CameraWorker.run(JavaCameraView.java:350)

код:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    setContentView(R.layout.activity_main);
    mOpenCvCameraView = (JavaCameraView) findViewById(R.id.surfaceView);
    mOpenCvCameraView.setOnTouchListener(this);
    mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
    mOpenCvCameraView.setCvCameraViewListener(this);
    mOpenCvCameraView.setMaxFrameSize(320, 240);
}
...
...
...
@Override
public Mat onCameraFrame(Mat inputFrame) {
    mRgbaT = inputFrame.t();
    Core.flip(inputFrame.t(), mRgbaT, 1);
    Imgproc.resize(mRgbaT, mRgbaT, inputFrame.size());
    return mRgbaT;
}

обновить:

Я изменил приведенный ниже метод таким образом, что mRgbaT объявляется как поле, и я очищаю его содержимое после захвата нового кадра... но проблема остается.

@Override
public Mat onCameraFrame(Mat inputFrame) {

    if (mRgbaT != null) {
        mRgbaT.release();
    }

    mRgbaT = inputFrame.t();
    Core.flip(inputFrame.t(), mRgbaT, 1);
    Imgproc.resize(mRgbaT, mRgbaT, inputFrame.size());
    return mRgbaT;
}

person user2121    schedule 07.10.2016    source источник


Ответы (3)


По моему опыту, OpenCV4Android очень, очень плохо справляется со сбором мусора в матрицах, поэтому я бы сохранял каждую матрицу в отдельной переменной и очищал их вручную.

@Override
public Mat onCameraFrame(Mat inputFrame) {

    Matrix mat1 = new Mat();
    Matrix mat2 = new Mat();

    Core.flip(inputFrame.t(), mat1, 1);
    Imgproc.resize(mat1, mat2, inputFrame.size());

    mat1.release();
    inputFrame.release();

    return mat2;
}

Если вы используете OpenCV4Android в виде исходного кода, я бы также предложил добавить modified.release(); здесь

person jereksel    schedule 07.10.2016
comment
не могли бы вы сказать мне, что такое модифицированный .релиз ?? - person user2121; 07.10.2016
comment
Это матрица кадра, снятого с фотоаппарата. Однако он преобразуется в растровое изображение с помощью matToBitmap, поэтому эта матрица больше не нужна. - person jereksel; 07.10.2016
comment
не могли бы вы объяснить мне разницу между вашим кодом и моим, который опубликован выше?! Спасибо - person user2121; 08.10.2016
comment
Конечно, в первую очередь вы инициализируете mRBA с матрицей кадра, которая не нужна, потому что мы помещаем туда перевернутую матрицу в следующем операторе. Затем вы используете ту же матрицу в качестве ввода и вывода при изменении размера (в этом нет ничего плохого, но, как я уже сказал, GC довольно плох в Android OpenCV). В моем коде каждая матрица инициализируется только один раз и не изменяется в течение всего срока ее службы. - person jereksel; 08.10.2016
comment
Нет, потому что мы возвращаем mat2, поэтому мы не можем его выпустить. - person jereksel; 08.10.2016

Я решил это, избегая выполнения операции транспонирования матрицы дважды, как показано ниже в коде:

@Override
public Mat onCameraFrame(Mat inputFrame) {
    if (mRgbaT != null) {
        mRgbaT.release();
    }

    mRgbaT = inputFrame.t();
    Core.flip(mRgbaT, mRgbaT, 1);
    Imgproc.resize(mRgbaT, mRgbaT, inputFrame.size());
    return mRgbaT;
}
person user2121    schedule 10.10.2016

В вашем файле cpp должен существовать цикл, который непрерывно захватывает изображения при каждом вызове. Когда вы уменьшаете размер изображения до более низкого разрешения, приложение работает немного дольше, но все равно вызывает утечку памяти и сбои.

Вы можете сделать подход, который будет очищать каждое изображение после обработки и захватывать новое, чтобы вы не могли в конечном итоге использовать все пространство ОЗУ. Отсылка к этой ссылке.

person Sudip Podder    schedule 07.10.2016
comment
выполните задачу выпуска в вашем файле С++. см. ошибку, она относится к вашему файлу cpp. - person Sudip Podder; 07.10.2016