My application uses a SurfaceView to show an image to the user, and have them manipulate stickers on top of the image that has been taken. To reduce memory usage, I have scaled all of these bitmaps to fit within the screen. Now I want to save the image that the user just put all of the stickers on, and I would like to save it with the resolution of the original image. How would a go about doing this without loading the full-size images into memory and risking an OutOfMemoryError? I do not know where to start with this, it seems like an impossible task with the given tools.
In case if you want to save the bitmap without downsizing it. There are two options.
Use largeHeap=true in your Application tag (Manifest).
If you can afford a bit of loss in quality! Compress the bitmap using.
Sample Code
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 70, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
Below is the link to the documentation of compress method of Bitmap class.
http://developer.android.com/reference/android/graphics/Bitmap.html#compress(android.graphics.Bitmap.CompressFormat, int, java.io.OutputStream)
I would do the following: You have to decouple the editing and the composition. In editing you save what the user wants in some POJOs ( e.g. sticker number A at position X,Y with rotation R )
Then in the composition-step you work with the Hi-Res images - but only one by one to save memory.
In the worst case you have to work with tiles if you still hit memory constraints.
Related
Whenever we need to rotate and resize a huge image file on Android (no bitmap allocated yet), the usual steps would be to first load the image into a resized bitmap, then do the rotation on another newly created bitmap.
If I do the rotation first I'll probably end with the dreaded OutOfMemoryError. This is quite logical and I'm OK with that.
Problem is, both approaches include having two bitmaps at once in memory at some point, and I'm on a tight memory budget here, even after implementing bitmap scaling as the official docs say (https://developer.android.com/topic/performance/graphics/load-bitmap). A typical implementation looks like this:
Bitmap bitmapResized = resizeBitmap(fSource, nTargetWidth, nTargetHeight);
Bitmap bitmapRotated = rotateBitmap(bitmapResized, nOrientation);
[recycle both bitmaps here]
Is there any way to do both steps at once in a memory lightweight fashion?
I have read about renderscript but I wasn't able to find code that does not take an already allocated bitmap.
What im trying to do is to save an edited bitmap that is composed by 2 bitmaps overlayed. My application allows the user to draw on top of a picture and save it.
My problem is: when i save the result image, it gets smaller, even setting the quality to 100. So, if the user saves and edit the image multiple times, the image will get smaller and smaller.
I save the bitmap with this:
result.compress(Bitmap.CompressFormat.JPEG, 100, fos);
I debugged the code, and at this point the width and height are fine, but after saving, the image shrinks.
I've researched for questions about this, but the ones i've found had no answers that could help me.
What i need is a way to save a bitmap without shrinking it, but i need it to be in image format, like JPG, PNG, etc.
Thanks in advance.
I guess is not at the time when you are saving the image, the issue is when you are editing the image (creating layers). Check that mechanism, maybe you are setting the image size there.
I am developing an image manipulation application which has to be able to work on large images e.g. those taken by a camera, the approach I'm taking is to split the source image into multiple suitably sized tiles so that the tiles can be loaded individually into memory as a Bitmap without exceeding the dreaded VM limit, next image manipulation is performed on a tile by tile basis, this is all well and good but until I want to stitch these tiles back to a final jpeg image.
I'm looking for a built in sdk api or free ware solution that can perform the following:
Open jpeg output file as output stream
Stream RGB pixels from bitmap (tile 1) to jpeg output stream
Stream RGB pixels from bitmap (tile 2) to jpeg output stream
etc.. for all tiles
Close jpeg output stream
Any ideas or pointers other than writing my own jpeg encoder?
What about this:
FileOutputStream outFile = new FileOutputStream(output_file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outFile);
outFile.flush();
outFile.close();
You can adjust the quality setting (100). See the docs
You'll still need to decide how to slice it. But if you're taking the image on the device, why would it be a problem loading the whole image if it didn't cause a memory problem in the first place?
Stitching Tiles back together: Here's one simple approach used by slippy maps the I know works.
Cut tiles in standard size (say 256x256)
Assign each tile an x,y value - this could be a directory structure or filename convention: i.e. "0_0.jpg"
Tile names (numbers) are related to their top/left pixel position.
Each tile number can be calculated by the following:
tileX = floor(pixelX / 256)
tileY = floor(pixelY / 256)
So the tile at pixels 0,0 = tile (0,0), the tile at pixels 256,0 is tile (1,0), etc...
Your stitching approach has two major issues and will ultimately fail. If you ask the system to decode a JPEG file in parts, the decode time will end up being close to N times longer (N = number of parts). The next problem is that when trying to save the image you will run out of memory. JPEG images need to be compressed in one shot. If your entire uncompressed image can't fit in memory, then you won't be able to use the technique you're using.
The hard truth is that, with Android as it is currently designed, you must use native code to hold on to the bitmap in order to manage an image larger than the VM memory limit. It's not that difficult, but it does require a re-design of your app.
I am joining two images using the code below but it throws an OutOfMemory error my images are around 1MB each.
private Bitmap overlayMark(String first, String second)
{
Bitmap bmp1, bmp2;
bmp1 = BitmapFactory.decodeFile(first);
bmp2 = BitmapFactory.decodeFile(second);
if (bmp1 == null || bmp2 == null)
return bmp1;
int height = bmp1.getHeight();
if (height < bmp2.getHeight())
height = bmp2.getHeight();
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth() + bmp2.getWidth(), height,
Bitmap.Config.ARGB_8888);// Out of memory
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, 0, 0, null);
canvas.drawBitmap(bmp2, bmp1.getWidth(), 0, null);
bmp1.recycle();
bmp2.recycle();
return bmOverlay;
}
Update: I tried below two answers but it still not allwoing me to create bitmap of such big size the problem is that the resultant bitmap is too large in size around 2400x3200 so its going out of memory.
How can I join large images without running out of memory?
Without loading the image into memory, you CAN get the size of the image, using inJustDecodeBounds. The Bitmap returns null, but all the parameters are set. You can scale down the image accordingly.
If your JPEG images are 1 MiB each, conversion to a BMP will take a lot of memory indeed. You can easily calculate its BMP equivalent by the dimensions of the image. Conversion of such a large image is expected to crash indeed. Android limits its apps to 16 MiB VM only.
Also use RGB_565 instead of ARGB_8888.
So your only solution is:
(a) To use BitmapFactory.Options.inSampleSize to scale down the image
or
(b) Use Android NDK where the 16 MiB limit isn't there.
I use this simple rule of the thumb:
the heavy lifting (both memory/CPU) is done on the server.
So write some servlet that takes the image, resizes it to a specified dimension (probably reduces the pixel depth too) and returns the result.
Piece of cake and it works on any mobile device you need.
Good luck!
I think a solution sort of like Sumon suggests might work.
Figure out the size of the final
image based on what will fit on the
screen.
Get the size of the first image using
the inJustDecodeBounds technique.
Figure out the size of the first
image in the final image. Calculate
re-sizing parameters.
Resize image, loading into memory.
Write resized image back to disk.
Recycle the bitmap. (This will help
when resizing the 2nd image)
Repeat for the second image, only you
can skip the writing to disk part.
Load first image.
If you only need to display, then just do that. If not then you can combine into a single bitmap at this point and write to disk. If this is the case, it may be difficult because you wil have essentially 2x the screen size in memory. In that case I would recommend resizing smaller. If you can't go smaller, then you will have to go the NDK route, thought I'm not sure how much that will help. Here's an amusing intro to the NDK and JNI. Finally, I would highly recommend developing this using a phone running Android 2.3+ since its use of heap-allocated bitmaps will make debugging much easier. More about those here.
It's not necessary that the space taken by in-memory representation of bitmaps correspond closely with file size. So even if you have 3mb memory available to jvm, you might still get OutOfMemoryException.
Your code is creating three in-memory images simultaneously. If you can find the size of both images without reading the complete files, you can modify the code in a way to have only one of the source images in memory at a time. If even that doesn't prove to be sufficient you might need some sort of streaming method of reading the images.
you may get some idea from here.
Are you trying to display this super large image or are you just trying to save it?
If your trying to display it. Cut the images into tiles. Then only display the tiles that are being viewed. If the user zooms out you need to reduce the size of the bitmap before showing the whole thing.
If your trying to save it, try saving it in sections to the same file by cutting the image up.
Loading 2 1m files in memory then creating a 2m file leaves you with 4M in memory for your images alone. Dynamically loading and unloading the memory solves this issue similar to tiles on Google maps or dynamic zooming in other map oriented solutions.
If you need to return that huge 2400x3200 bitmap as your result, there is no way to actually realize this goal. The reason is that 2400*3200*4 bytes ~ 30 Mb! How can you hope to implement this method, when even you can't even fit the return value into your limited heap space (ie 16Mb)?
And even if you used 16-bit color, it would still fail because you would end up using about 15MB, which would not leave you enough space for the language run time.
I am using the Camera activity to capture the JPEG image. After image is saved and the controll is returned back to my application activity I would like to to determine the captured JPEG image dimensions without loading the whole image into memory via Bitmap object - can it be easily done somehow?
Is there any class, which reads the JPEG header and give me such information? I would like to avoid OOM conditions - but might be it is not a problem to load the whole image into memory - is it?
After knowing the dimensions I would like to use BitmapFactory to scale the image.
Thanks a lot
Kind Regards,
STeN
Perhaps a work-around (see 2nd approach) by setting the quality?
bmp.compress(CompressFormat.JPEG, 75,
pfos);
You would have to do a trial run to see what size the quality gets you though...
*The first approach creates a log file to get the width and height but not sure if you wanted the extra step of looking at a log file.
You can use the ExifInterface class to read the image width and height.