I'm drawing an overlay on an image from the camera and saving the result to a file. To do this, I am passing a callback containing the code below to takePicture(). With larger image sizes, I am getting crashes with an OutOfMemoryError at the first line of the method.
Is there any way I can do this more efficiently? It seems that it's not possible to make a mutable Bitmap from the byte[], which doubles my memory usage immediately. If it can't be done this way at high resolutions, how can I produce an overlay on a large captured image without running out of memory?
public void onPictureTaken(byte[] rawPlainImage, Camera camera) {
Bitmap plainImage = BitmapFactory.decodeByteArray(rawPlainImage, 0, rawPlainImage.length);
plainImage = plainImage.copy(plainImage.getConfig(), true);
Canvas combinedImage = new Canvas(plainImage);
combinedImage.drawBitmap(mOverlay, mOverlayTransformation, null);
//Write plainImage (now modified) out to a file
plainImage.recycle();
}
You don't actually need to decode the image. Instead draw the overlay to the canvas, save the canvas as a bitmap, convert that bitmap to a byte array and then combine the byte array of the canvas and the bitmap and then save that.
Related
it is possible to save pictures from surfaceview? I want a costum camera(smaller than screen) I want the preview and take pictures.
I try a lot of code but nothing work properly
How to capture image from custom CameraView in Android?
Yes, it's possible and code by your link is correct in common case. But there is one more thing which you should check if you trying to convert data to specific format in callback:
public void onPictureTaken(byte[] arg0, Camera arg1)
You can check picture format of Camera arg with method:
public int getPictureFormat ()
This returns ImageFormat (like JPEG, NV21, YUV etc,) for camera in your device. You should use this value to format correctly byte data from onPictureTaken because BitmapFactory.decodeByteArray(arg0, 0, arg0.length) method can work correctly only with JPEG or PNG data.
From Android Developer BitmapFactory documentation:
Prior to KITKAT additional constraints apply: The image being decoded
(whether as a resource or as a stream) must be in jpeg or png format.
Only equal sized bitmaps are supported, with inSampleSize set to 1.
Additionally, the configuration of the reused bitmap will override the
setting of inPreferredConfig, if set.
Additionally I propose you to use TextureView instead of SurfaceView because in this case you can use simple way and get picture from TextureView by this method:
public Bitmap getBitmap ()
I'm using the Android Camera2 API to take still capture images and displaying them on a TextureView (for later image editing).
I have been scouring the web for a faster method to:
Decode Camera Image buffer into bitmap
Scale bitmap to size of screen and rotate it (since it comes in rotated 90 degrees)
Display it on a texture view
Currently I've managed an execution time of around 0.8s for the above, but this is too long for my particular application.
A few solutions I've considered were:
Simply taking a single frame of the preview (timing-wise this was fast, except that I had no control over auto flash)
Trying to get instead a YUV_420_888 formatted image and then somehow turning that into a bitmap (there's a lot of stuff online that might help but my initial attempts bore no fruit as of yet)
Simply sending a reduced quality image from the camera itself, but from what I've read it looks like the JPEG_QUALITY parameter in CaptureRequests does nothing! I've also tried setting BitmapFactory options inSampleSize but without any noticeable improvement in speed.
Finding some way to directly manipulate the jpeg byte array from image buffer to transform it and then converting to bitmap, all in one shot
For your reference, the following code takes the image buffer, decodes and transforms it, and displays it on the textureview:
Canvas canvas = mTextureView.lockCanvas();
// obtain image bytes (jpeg) from image in camera fragment
// mFragment.getImage() returns Image object
ByteBuffer buffer = mFragment.getImage().getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
// decoding process takes several hundred milliseconds
Bitmap src = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
mFragment.getImage().close();
// resize horizontally oriented images
if (src.getWidth() > src.getHeight()) {
// transformation matrix that scales and rotates
Matrix matrix = new Matrix();
if (CameraLayout.getFace() == CameraCharacteristics.LENS_FACING_FRONT) {
matrix.setScale(-1, 1);
}
matrix.postRotate(90);
matrix.postScale(((float) canvas.getWidth()) / src.getHeight(),
((float) canvas.getHeight()) / src.getWidth());
// bitmap creation process takes another several hundred millis!
Bitmap resizedBitmap = Bitmap.createBitmap(
src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
canvas.drawBitmap(resizedBitmap, 0, 0, null);
} else {
canvas.drawBitmap(src, 0, 0, null);
}
// post canvas to texture view
mTextureView.unlockCanvasAndPost(canvas);
This is my first question on stack overflow, so I apologize if I haven't quite followed common conventions.
Thanks in advance for any feedback.
If all you're doing with this is to draw it into a View, and won't be saving it, have you tried to simply request JPEGs that are lower resolution than maximum, and match the screen dimensions better?
Alternatively, if you need the full-size image, JPEG images typically contain a thumbnail - extracting that and displaying it is a lot faster than processing the full-resolution image.
In terms of your current code, if possible, you should avoid having to create a second Bitmap with the scaling. Could you instead place an ImageView on top of your TextureView when you want to display the image, and then rely on its built-in scaling?
Or use Canvas.concat(Matrix) instead of creating the intermediate Bitmap?
I am trying to write a method in android to save the whole webview as an image as following:
bitmap = Bitmap.createBitmap(webView.getWidth(),
Math.round(webView.getContentHeight() * webView.getScale()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
webView.draw(canvas);
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, JPEG_COMPRESSION, out);
However, the bitmap is too big (more than 100 MB) and takes too much memory. Does anyone know any better method to keep the memory usage low?
The problem is the WebView is small, and the generated JPEG file is small but the intermediate Bitmap which is not used any more in other places is huge.
Set JPEG_COMPRESSION = 0-100 as it is a quality paramater of bitmap 0 meaning compress for small size, 100 meaning compress for max quality.
You can also set bitmap height and width using
BitmapFactory.Options bop
bop.outWidth
bop.outHeight
I´m trying to merge 2 images, one is bitmap from camera, second one is .png file stored in drawables. What I did was that I used both images as bitmaps and I tried to merge them by using canvas, something like this:
Bitmap topImage = BitmapFactory.decodeFile("gui.png");
Bitmap bottomImage = BitmapFactory.decodeByteArray(arg0, 0, arg0.length);
Canvas canvas = new Canvas(bottomImage);
canvas.drawBitmap(topImage, 0, 0, null);
But I keep getting "Bitmap size exceeds VM budget" error all the time. I tried nearly everything, but still, it keeps throwing this error. Is there another way of merging 2 images? What i need to do is simple - I need to take photo and save it merged with that .PNG image stored in drawables. For example this app is very close to what i need - https://play.google.com/store/apps/details?id=com.hl2.hud&feature=search_result#?t=W251bGwsMSwyLDEsImNvbS5obDIuaHVkIl0.
Thanks :)
See the below code for combining two images.
This method returns combined bitmap
public Bitmap combineImages(Bitmap frame, Bitmap image) {
Bitmap cs = null;
Bitmap rs = null;
rs = Bitmap.createScaledBitmap(frame, image.getWidth() + 50,
image.getHeight() + 50, true);
cs = Bitmap.createBitmap(rs.getWidth(), rs.getHeight(),
Bitmap.Config.RGB_565);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(image, 25, 25, null);
comboImage.drawBitmap(rs, 0, 0, null);
if (rs != null) {
rs.recycle();
rs = null;
}
Runtime.getRuntime().gc();
return cs;
}
You can change height and width as per your requirements
Hope this will help...
How large are the images? I've only encountered this problem when trying to load large images into memory.
Is the byte array your decoding actually an image?
From a quick look at the android docs you can capture an image using the default camera app which may work in this situation.
http://developer.android.com/training/camera/photobasics.html
Also see this question: Capture Image from Camera and Display in Activity
Edit: You may also need to scale the image from the camera down if it is very large. See the end of the android page I linked to for details on that.
Image is always making problem for me always :(.
java.lang.OutOfMemoryError: bitmap size exceeds VM budget - android is the main problem even if i use very small image.
Please suggest me some tips in using Image. (link, tutorials, sample projects etc...)
Some of my quires are
Which should i prefer Drawable or bitmap
BitmapFactory.decodeFile or Drawable.createFromPath
BitmapFactory.decodeStream or Drawable.createFromStream
How to retain Drawable and Bitmap while rotating.
Is it a good idea to save or pass Drawable or Bitmap through intent
that all depends on what exactly you want to do with your image. bitmapfactory is for creating bitmaps. and drawable objects are used when you want to draw something :)
what do you want to do with your bitmap after you have created it in the memory?
as for rotating, you should take care of onRestoreInstanceState and onSaveInstanceState. something like this:
private static final String PHOTO_TAKEN = "photo_taken";
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState.getBoolean(PHOTO_TAKEN)) {
// do something if you have your pic here
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean(PHOTO_TAKEN, mPhotoTaken); // saving the state of the image (if the photo is taken or not in this case)
}
as for passing through extras... i bet it's a bad idea as you already getting outofmemory exception.... you can pass a reference to that image, like ID of path or something else.
UPDATE:
in one of the projects i was trying to show the image taken from the cam in an ImageView control ion my activity and got that outofmemory exception. the reason was that it was putting the whole big image into memory. the workaround was quite simple: i decreased the image size:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(mImagePath, options);
To avoid out of memory error we can use BitmapRegionDecoder ( to decode the original image which is larger than maximum texture limit 2048x2048 ) or we can decode it by specifying the coordinates from the original bitmap. (for e.g coordinates of each quarter)