Более оптимизированная для памяти версия Bitmap.createScaledBitmap?

Я полностью понимаю, что «преждевременная оптимизация — корень всех зол." Однако я дошел до того, что считаю, что оптимизация, которую я хочу сделать, больше не может считаться "преждевременной".

Я пишу приложение для Android, которое, помимо прочего, передает потоковое изображение в реальном времени с USB-устройства, выполняет некоторую обработку изображения с помощью OpenCV, преобразует кадр в растровое изображение, а затем отображает это растровое изображение в SurfaceView в режиме реального времени.

У меня все работает (прямая трансляция правильно отображается в SurfaceView), а инструмент «Профилировщик» Android Studio указывает, что я хорошо выполнил управление памятью (обратите внимание, что сборщик мусора не запускается один раз за 10 секунд, показанных ниже):

Более оптимизированная для памяти версия Bitmap.createScaledBitmap?

Однако, поскольку изображение, которое я транслирую, имеет низкое разрешение (320x180), когда я визуализирую его непосредственно в SurfaceView, оно на самом деле оказывается физически довольно маленьким на экране устройства.

Таким образом, я решил масштабировать растровое изображение до максимального размера, который поместился бы в SurfaceView, прежде чем отображать его на экране. Однако после этого я с ужасом обнаружил, что график использования памяти теперь выглядит ужасно! (Сборщик мусора выполняется более одного раза в секунду!)

Более оптимизированная для памяти версия Bitmap.createScaledBitmap?

Вот соответствующие фрагменты кода:

  • Перед масштабированием растрового изображения (очистить график памяти):

    canvas = getHolder().lockCanvas();
    openCvMat = frameQueue.take();
    Utils.matToBitmap(openCvMat, bitmapFromMat);
    mat.release();
    canvas.drawColor(Color.BLACK);
    canvas.drawBitmap(bitmapFromMat, 0, 0, null);
    getHolder().unlockCanvasAndPost(canvas);
    
  • После масштабирования растрового изображения (ужасный график памяти):

    canvas = getHolder().lockCanvas();
    openCvMat = frameQueue.take();
    Utils.matToBitmap(openCvMat, bitmapFromMat);
    mat.release();
    canvas.drawColor(Color.BLACK);
    Bitmap scaled = scaleBitmapToViewport(bitmapFromMat);
    canvas.drawBitmap(scaled, 0, 0, null);
    scaled.recycle();
    getHolder().unlockCanvasAndPost(canvas);
    
  • scaleBitmapToViewport() метод:

    private Bitmap scaleBitmapToViewport(Bitmap bitmap)
    {
        //Landscape
        if ((canvas.getHeight() * aspectRatio) < canvas.getWidth())
        {
            return Bitmap.createScaledBitmap(bitmap, (int) Math.round(canvas.getHeight() * aspectRatio), canvas.getHeight(), true);
        }
        else //Portrait
        {
            return Bitmap.createScaledBitmap(bitmap, canvas.getWidth(), (int) Math.round(canvas.getWidth() / aspectRatio), true);
        }
    }
    

Итак, кажется, что Bitmap.createScaledBitmap() является виновником. Кто-нибудь знает другой способ сделать то же самое, который не дает сборщику мусора заработать свои деньги?

Вместо масштабирования самого растрового изображения, как вы делаете, вы не можете масштабировать во время рисования? developer.android.com/reference/android/graphics/… Я смотрю на canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) — обратите внимание, что он принимает как исходный, так и целевой прямоугольники. Вы захотите установить источник в соответствии с вашими (маленькими) размерами изображения, а цель - размерами вашей поверхности.

Kostya Vasilyev 10.07.2019 06:39

@KostyaVasilyev Ага, сработало! Тем не менее, мне все еще интересно, есть ли ошибка в методе Bitmap.createScaledBitmap(), которая привела к «утечке» памяти. (Да, я знаю, что на самом деле это не утечка, но вы поняли идею)

You'reAGitForNotUsingGit 10.07.2019 15:57

Что касается шаблонов использования памяти: вы создаете новое масштабированное растровое изображение каждый раз в цикле. Это регулярное выделение (довольно больших, как я полагаю) объемов памяти, вот и все.

Kostya Vasilyev 10.07.2019 19:09

@KostyaVasilyev Но я все еще делаю это, добавляя объекты OpenCV Mat в очередь 30 раз в секунду. Кроме того, вы можете видеть, что сборщик мусора вызывается, хотя я вручную освобождал память с помощью bitmap.recycle()

You'reAGitForNotUsingGit 10.07.2019 19:47
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо того, чтобы масштабировать само растровое изображение, как вы делаете, вы не можете масштабировать во время рисования?

Я смотрю на холст.drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) — обратите внимание, что он принимает как исходный, так и целевой прямоугольники.

Вы захотите установить источник в соответствии с вашими (маленькими) размерами изображения, а цель - размерами вашей поверхности.

Другие вопросы по теме