8

I want to crop image without getting OutOfMemory exception.
it means i have x, y, width and height of cropped image and want to crop original image without bringing it to memory.
Yes i know that BitmapRegionDecoder is good idea but maybe the cropped image would be too large for bringing it to memory.

In fact i don't want copped bitmap, just want to write cropped image from source file to destination file.


EDIT : I want to save cropped image not just showing it in an ImageView I want to save it in a new file without losing dimensions

This is the example
enter image description here

in this situation cropped image resolution is 20000x20000 and code below wont work cause of OOM:

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options);
mImageView.setImageBitmap(bitmap);

using inSampleSize to decrease the original picture size is good but the result i save is no longer 20000x20000.

How can i crop the 25000x25000 and save the 20000x20000 part of image in a file?

Sepehr Behroozi
  • 1,780
  • 1
  • 17
  • 32
  • why down vote? share your knowledge – Sepehr Behroozi Dec 27 '15 at 12:26
  • 1
    Stack Overflow is for programming questions. What is your question? If your question is "how do I do this?", please explain what you have tried and what specific problems you have encountered. – CommonsWare Dec 27 '15 at 12:58
  • yes! my question is how do i do this in android? i have tried all the answers about related questions in StackOverflow and every single answer was depends on `Bitmap` object that is a risk of OOM. I don't know which part of my question is vague @CommonsWare – Sepehr Behroozi Dec 27 '15 at 13:31
  • can you past some code? Do you use a backtask to do this? What device are you using? – Hugo Gresse Dec 30 '15 at 09:12
  • @Sepehr Behroozi the same was described here. Prease check it: http://stackoverflow.com/questions/3331527/android-resize-a-large-bitmap-file-to-scaled-output-file – ant Dec 30 '15 at 22:41
  • @ant thanks but convince me that this link you commented will gives us 20000x20000 cropped image. cause I think the opposite direction :) – Sepehr Behroozi Jan 02 '16 at 12:47
  • 1
    I think that its really tricky to do resize of huge images as you wish. Note that android is the system for small devices with limited memory. Even if you will use RGB_565 the size of 25000x25000 image will be more than 1.1GB in RAM. So all that I can suggest is try to look at some streams Java libs that may resize images without loading it in memory "on fly". Or try to look at NDK, RenderScript or OpenGL. – ant Jan 02 '16 at 12:58

4 Answers4

1

Did you checked BitmapRegionDecoder? It will extract a rectangle out of the original image.

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options);
mImageView.setImageBitmap(bitmap);

http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

Ziem
  • 6,579
  • 8
  • 53
  • 86
Ankit Aggarwal
  • 5,317
  • 2
  • 30
  • 53
  • problem is that maybe the rectangle would be too big and this line may throws OOM `Bitmap bitmap = bitmapRegionDecoder.decodeRegion(...` – Sepehr Behroozi Dec 30 '15 at 12:52
1

You can solve this using BitmapFactory. To determinate the original bitmap size without putting it in to memory, do the fallowing:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(..., options);

int originalImageWith = options.outWidth;
int originalImageHeight = options.outHeight;

Now you can use options.inSampleSize

If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1. Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2.

Now it's not a perfect solution but you can do math to find what is the closest factor of 2 that you can use on options.inSampleSize to save memory.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
Bitmap bitmap = BitmapFactory.decodeResource(..., options);
Ziem
  • 6,579
  • 8
  • 53
  • 86
Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
1

Simply put, it requires lots of low level programming and optimizations.

as you can see, lots of answers in this region are pointing to generic concepts of bitmap compression, etc which are indeed applicable in most issues but not specifically yours.

Also BitmapRegionDecoder as suggested in answers won’t work well. It sure prevents loading the whole bitmap in RAM but what about the cropped image? after cropping an image it gives you a giant bitmap which no matter what, gives you an OOM.

Because your problem as you described, needs Bitmaps to get written or get read from disk just as they get written or read from memory; something called a BufferedBitmap (or so) which efficiently handles the memory it requires by saving little chunks of a bitmap to disk and using them later, thus, avoiding OOM.

Any other solution which wants to tackle the problem with scaling only do half of the work. why? because cropped image itself can be too big for memory (as you said).

However, solving the problem by scaling isn’t that bad, if you don’t care about the quality of the cropped image compared to the quality user had seen when she was cropping it. that’s what the Google Photos do, it simply reduces the quality of cropped image, very simple!

I haven’t seen any BufferedBitmap classes around (but if there are, it would be awesome). They sure become handy for solving similar problems.

You can check Telegram messaging app which comes with an open-source implementation of image cropping facilities; you guess right, it handles all the similar nasty works with good old C... Hence, we might conclude that a good global solution (or better said, ONE OF THE SEVERAL APPLICABLE SOLUTIONS) appears to be low-level programming to handle disk and memory yourself.

I know my answer failed to give any copy-paste-ish solution to your problem but at least I hope it gave you some ideas my friend.

Dusk
  • 1,729
  • 10
  • 17
0

BitmapRegionDecoder is the good way to crop big or large Images, but it's available from API 10 and above.

There is a class called BitmapRegionDecoder which might help you, but it's available from API 10 and above.

If you can't use it :

Many image formats are compressed and therefore require some sort of loading into memory.

You will need to read about the best image format that fits your needs, and then read it by yourself, using only the memory that you need.

a little easier task would be to do it all in JNI, so that even though you will use a lot of memory, at least your app won't get into OOM so soon since it won't be constrained to the max heap size that is imposed on normal apps.

Of course, since android is open source, you can try to use the BitmapRegionDecoder and use it for any device.

Reference : Crop image without loading into memory

Or you can find some other way on below that might be helpful to you:

Bitmap/Canvas use and the NDK

Community
  • 1
  • 1
KishuDroid
  • 5,411
  • 4
  • 30
  • 47