Я полностью понимаю, что «преждевременная оптимизация — корень всех зол." Однако я дошел до того, что считаю, что оптимизация, которую я хочу сделать, больше не может считаться "преждевременной".
Я пишу приложение для Android, которое, помимо прочего, передает потоковое изображение в реальном времени с USB-устройства, выполняет некоторую обработку изображения с помощью OpenCV, преобразует кадр в растровое изображение, а затем отображает это растровое изображение в SurfaceView в режиме реального времени.
У меня все работает (прямая трансляция правильно отображается в SurfaceView), а инструмент «Профилировщик» Android Studio указывает, что я хорошо выполнил управление памятью (обратите внимание, что сборщик мусора не запускается один раз за 10 секунд, показанных ниже):
Однако, поскольку изображение, которое я транслирую, имеет низкое разрешение (320x180), когда я визуализирую его непосредственно в SurfaceView, оно на самом деле оказывается физически довольно маленьким на экране устройства.
Таким образом, я решил масштабировать растровое изображение до максимального размера, который поместился бы в SurfaceView, прежде чем отображать его на экране. Однако после этого я с ужасом обнаружил, что график использования памяти теперь выглядит ужасно! (Сборщик мусора выполняется более одного раза в секунду!)
Вот соответствующие фрагменты кода:
Перед масштабированием растрового изображения (очистить график памяти):
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() является виновником. Кто-нибудь знает другой способ сделать то же самое, который не дает сборщику мусора заработать свои деньги?
@KostyaVasilyev Ага, сработало! Тем не менее, мне все еще интересно, есть ли ошибка в методе Bitmap.createScaledBitmap(), которая привела к «утечке» памяти. (Да, я знаю, что на самом деле это не утечка, но вы поняли идею)
Что касается шаблонов использования памяти: вы создаете новое масштабированное растровое изображение каждый раз в цикле. Это регулярное выделение (довольно больших, как я полагаю) объемов памяти, вот и все.
@KostyaVasilyev Но я все еще делаю это, добавляя объекты OpenCV Mat в очередь 30 раз в секунду. Кроме того, вы можете видеть, что сборщик мусора вызывается, хотя я вручную освобождал память с помощью bitmap.recycle()




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