I fully understand that "premature optimization is the root of all evil." However, I have gotten to a point where I believe that the optimization I wish to do can no longer be considered "premature".
I'm writing an Android application that, among other things, streams a live image feed from a USB device, performs some image processing with OpenCV, converts the frame into a bitmap, and then renders that bitmap to a SurfaceView in real time.
I got everything working, (the live feed is correctly rendered to the SurfaceView) and Android Studio's "Profiler" tool indicates that I've done my memory management well (notice that the garbage collector does not run once in the 10 seconds shown below):
However, because the image I'm streaming is low resolution (320x180), when I render it directly to the SurfaceView, it actually ends up being physically pretty small on the device's screen.
Thus, I decided to scale the bitmap to the maximum size that would fit in the SurfaceView before rendering it to the screen. However, after doing this, I was horrified to find that the memory usage graph now looks horrendous! (The garbage collector runs more than once per second!)
Here are the relevant snippets of code:
Before scaling the bitmap (clean memory graph):
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);
After scaling the bitmap (horrendous memory graph):
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()
method:
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);
}
}
So it seems that Bitmap.createScaledBitmap()
is the culprit. Does anyone know another method to accomplish the same that doesn't give the garbage collector a run for its money?
Instead of scaling the bitmap itself like you're doing - can't you scale while drawing?
I'm looking at canvas.drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) - note that it takes both source and destination rectangles.
You'll want to set the source to your (small) image dimensions and the dest to the dimensions of your surface.