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.
Related
I'm trying to build an image steganography android app to hide information in jpeg images.
I'm encoding the information in the DCT Coefficients of the image.
I'm using OpenCv to perform the following steps :
Load image bitmap and get image mat from it.
Convert image from RGB to YCrCb
Divide the image into blocks of 8 x 8 blocks.
Center pixel values around 0 [0-255 => -128-127]
Apply DCT transformation to get DCT coefficients for the block.
Quantization [divide the DCT coefficients with quant matrix]
Now substitute the LSBs of the non 0/1/DC coefficients with our secret message.
inverse quantization
inverse DCT
[0-255 <= -128-127]
YCbCr to RGB
Get bitmap from image mat
Save the bitmap as jpeg.
The idea is that since I'm just extracting and repacking a jpeg image using jpeg compression algo, the jpeg compression algorithm should not destroy the data in the DCT coefficients (a lot of jpeg there :P)
Problem :
The data I just saved in the LSB of DCT coefficients gets destroyed int the 12th step. I want a way to save the image without its data being destroyed (which should be theoretically possible since I have followed the jpeg compression algorithm only.
Somethings I have tried :
this is the normal method, which leads to dataloss.
I used this method. It does not destroys the data, but it increases the file size by around 6 times and the final file received is not jpeg, it is pure bytes (which can be viewed in image viewer if the extension is set to jpeg or png)
I thought that the jpeg image I used might have a different level of compression as compared to the one while saving the jpeg image by android. So I used the image saved by the app to hide data. Even in that a lot of data is lost(less compared to before).
saving the image as png. This works, no data is lost and dct algorithm can be applied to extract the hidden information. But the point was to get a jpeg image as output, bcz jpeg is the most common image extension out there.
how to compress GIF image before upload it to the server ?, i tried some android libraries to compress gif image but it convert it to png image. is there any way to compress gif image ?
im using a bitmap in my projects.
Bitmap
Example code:
ImageView carView = (ImageView) v.findViewById(R.id.imagen_cr7);
byte[] decodedString = Base64.decode(picture, Base64.NO_WRAP);
InputStream input=new ByteArrayInputStream(decodedString);
Bitmap ext_pic = BitmapFactory.decodeStream(input);
carView.setImageBitmap(ext_pic);
GIF is a lossless image compression format: it is set up to reproduce the image exactly.
As a consequence, there is no "image quality" slider (as in JPEG encoders); although a GIF will likely be much smaller than an uncompressed format (such as many camera RAW or common TIFF options), there is a limit to how far it can go.
Also, you should know that GIF is limited to 8 bits per pixel (so it is most appropriate for line art, not photo-like images). If your source image is a full 24 bits, it must be dithered to fit into a 256-entry color palette. So, although the GIF format itself is lossless, the image processing required to use it in the first place may be lossy.
There are a number of things you can do to reduce the size of your image file:
You can choose a lossy format (such as JPEG), which will allow much greater compression. Note that JPEG works well on photo-like images, but not so well on line art. Also, (although your question explicitly rejects it) PNG may be a reasonable option, as it (losslessly) supports 24-bpp images.
As mentioned in a comment, you can try reducing the resolution of your image, and shipping the reduced version. If you can't generate a smaller image to start with, image resizing typically works well on photo-like images, and there are nonlinear resizing filters available that are specifically intended to handle line art.
If a full-resolution GIF is mandatory for your application, you may be able to generate an image that is more compressible by the GIF format. GIF compresses solid blocks of color extremely well -- but does less well on dithered or noisy images (such as you might get from converting a 24-bpp image to GIF format).
Since you have not given any information on your requirements (what kind of image you have, where you got it, and what you need it for), it is hard to come up with specific advice.
However, there is a good chance that your GIF has far more resolution than you need for your particular application (leading to option #2).
JPEG is a compressed format.
Does BitmapFactory.decodeFile("path to a JPEG file") decompress the JPEG image when decoding it as a Bitmap in the memory?
And when I use Bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) on the resulting bitmap from the decoded JPEG file, then the size of the compressed image is more than the size of the original image. Can anybody exactly explain this phenomenon?
Does BitmapFactory.decodeFile("path to a JPEG file") decompress the JPEG image when decoding it as a Bitmap in the memory?
Yes.
And when I use Bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) on the resulting bitmap from the decoded JPEG file, then the size of the compressed image is more than the size of the original image
That is very possible. With a quality level of 100, I would consider it likely, though perhaps not assured.
Can anybody exactly explain this phenomenon?
There is no requirement that they be the same for a JPEG. In fact, it will be almost random chance if they are the same.
Suppose we start with an in-memory image (Boriginal, where B is short for bitmap). We then compress that image to a JPEG (Joriginal). JPEG incorporates a lossy compression algorithm, to achieve better compression on real-world images (e.g., photos) by taking into account the fact that human eyes cannot discern small amounts of change.
Suppose we then decode Joriginal back into an in-memory bitmap (Breloaded). Breloaded will not be the same image as Boriginal, because the JPEG compression will have changed the image. How close Breloaded is to Boriginal will depend on a variety of factors, partly tied to the image itself, and partly tied to the quality level used when saving the JPEG (the 100 in your code). This quality level ranges from 0 to 100, with 100 meaning highest quality.
If we then compress Breloaded to a second JPEG (Jreloaded), the new JPEG will not be the same as the original JPEG (Joriginal). Partly, that is because the source bitmap changed, per the previous paragraph. Partly, that is because we might not choose the same quality level as we did with the original compression work.
In your case, you did not create Joriginal. You do not necessarily know what quality level was used (that information might be stored in the JPEG header; I forget). But because Breloaded will be different that the original bitmap (wherever it came from), when you compress the bitmap to Jreloaded, it is going to be different than Joriginal. On the whole, whether it is larger or smaller is difficult to say in the abstract. However, since you are choosing a quality level of 100, and Joriginal might well have been compressed with a lower quality level, your compressed image very easily could be larger.
This has nothing to do with Android. This is purely a function of how JPEG works. If you have further questions about JPEG itself, you may wish to read more about JPEG and ask questions on some site that has something to do with image formats.
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.
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.