Bitmap memory management in Android - java

Hello im testing code for avoid memory crashes when loading a lot of bitmaps. And I have a little question, about the following code
Bitmap bmp;
long sizepreload, sizepostload,bmppixels;
sizepreload= Debug.getNativeHeapAllocatedSize();
bmp=getImageBitmapFromUrl("http://www.google.com/intl/en_com/images/srpr/logo2w.png");
sizepostload=Debug.getNativeHeapAllocatedSize();
bmppixels=bmp.getHeight()*bmp.getWidth();
Log.d("bmp test","pre="+sizepreload+"\n" +
"post="+sizepostload+"\n" +
"bmppixels="+bmppixels+"\n"+
"ratio="+(sizepostload-sizepreload)/bmppixels
);
The return of that snippet is:
pre=4260464
post=4333112
bmppixels=26125
ratio=2
So a bitmap takes 2 bytes per pixel. 16 bits per pixel is constant for all bitmaps or it is variable (depending of screen density or image quality). If its variable how can I get that ratio from the Bitmap class?
thanks in advance
Thats the code for download from a url:
public static Bitmap getImageBitmapFromUrl(String url){
URL aURL;
Bitmap result = null;
try {
aURL = new URL(url);
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
result= BitmapFactory.decodeStream(is);
is.close();
} catch (Exception e) {
result=null;
e.printStackTrace();
}
return result;
}

Bitmaps can be up to 4 bytes per pixel, this depends on the bitmap's format. It is not variable for the screen density. However when drawing the bitmap, it may need to be resized for the screen density, and this would affect your heap temporarily.

Related

How to reduce size (mb) of image picked from gallery [duplicate]

This question already has answers here:
Decrease image size without losing its quality in android
(6 answers)
Closed 4 years ago.
When the user selects an image from the gallery, I convert it into a base64 string. Then I use an API. The image size must be less than 2MB, so if the image from the gallery is bigger than 2MB, I need to reduce its size.
How should I do that?
That's my code:
// I pass the URI of the image to the method
#Override
protected Integer doInBackground(Uri... uris) {
InputStream inputStream = null;
try {
inputStream = getContentResolver().openInputStream(uris[0]);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
bytes = output.toByteArray();
String encodedImage = Base64.encodeToString(bytes, Base64.NO_WRAP);
You can create a Bitmap object using
BitmapFactory.decodeByteArray
Decode an immutable bitmap from the specified byte array.
and then you can use
bitmap.compress
Write a compressed version of the bitmap to the specified
outputstream. If this returns true, the bitmap can be reconstructed by
passing a corresponding inputstream to BitmapFactory.decodeStream().
Note: not all Formats support all bitmap configs directly, so it is
possible that the returned bitmap from BitmapFactory could be in a
different bitdepth, and/or may have lost per-pixel alpha (e.g. JPEG
only supports opaque pixels).
This method may take several seconds to complete, so it should only be
called from a worker thread.
and/or
Bitmap.createScaledBitmap
Creates a new bitmap, scaled from an existing bitmap, when possible.
If the specified width and height are the same as the current width
and height of the source bitmap, the source bitmap is returned and no
new bitmap is created.
In my project I created two utility functions that might help you understand how to use the functions I mentioned above to fit your needs
public static Bitmap base64ToBitmap(String encodedImage) {
byte[] decodedString = Base64.decode(encodedImage, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
}
public static String bitmapToBase64(Bitmap bitmap, int quality, int percentScale) {
if (quality < 0 || quality > 100) {
quality = IMAGE_QUALITY_DEFAULT_VALUE;
}
if (percentScale < 0 || percentScale > 100) {
percentScale = IMAGE_SCALE_DEFAULT_VALUE;
}
float scale = (float) percentScale / 100;
int width = Math.round(bitmap.getWidth() * scale);
int height = Math.round(bitmap.getHeight() * scale);
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
return Base64.encodeToString(byteArray, Base64.NO_WRAP);
}
One way to easily check the image and store it is according to the developer docs
https://developer.android.com/topic/performance/graphics/load-bitmap#java
Which gives a solution to your answer explained properly and another way is to change the size of the image which can be found here and then use it accordingly
https://developer.android.com/reference/android/graphics/drawable/ScaleDrawable

How to crop image to a set number of bytes?

I am coding an app where I take a picture and I pass the picture into an image recognition model. The problem is, the recognition only takes 150528 bytes, and the number of bytes in the image depends on the image quality. Is there a way in Java to crop the top and bottom of the image as evenly as possible depending on the quality to make it a set number of bytes each time?
This is my code for this part so far.
int width = 100;
int height = 200;
final ImageReader reader = ImageReader.newInstance(width,height,ImageFormat.JPEG,1);
List<Surface> outputSurface = new ArrayList<>(2);
outputSurface.add(reader.getSurface());
outputSurface.add(new Surface(textureView.getSurfaceTexture()));
As you can see, I'm restricting the size of the image captured, but the number of bytes depends on the quality so I need to actually just crop the image. How can I do this?
You can crop/ resize image with the following
byte[] thumb_byte_data;
Uri resultUri = ImageUri;
//getting imageUri and store in file. and compress to bitmap
File file_path = new File(resultUri.getPath());
try {
Bitmap thumb_bitmap = new Compressor(this)
.setMaxHeight(200)
.setMaxWidth(200)
.setQuality(75)
.compressToBitmap(file_path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thumb_bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
thumb_byte_data = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}

Getting a black preview and a image after onPictureTaken

I m trying to develop an app which would auto crop the edges of a card which is detected by using opencv.
Here in this function i am doing the following :
1) Converting the byte array (ie data) into a bitmap and then to a Mat
2) Cropping the required portion of the card and re sizing it
3) Converting the mat back into a byte array
4) Saving the modified byte array in a File(jpg)
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Mat mat=new Mat();
Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
Utils.bitmapToMat(bmp, mat); //converting a mat to bitmap
Mat matCrop = mat.submat((int)p1.y,(int)p2.y,(int)p3.x,(int)p4.x);//surely no issues here
Imgproc.resize(matCrop, matCrop, mat.size());
byte[] imageInBytes = new byte[(int)(matCrop.total() * matCrop.channels())];
mat.get(0, 0, imageInBytes);
data=imageInBytes;
try {
FileOutputStream fos = new FileOutputStream(mPictureFileName);
fos.write(data);
fos.close();
} catch (java.io.IOException e) {}
}
The picture gets saved in memory but the preview as well as the image both are blank . The saved file also takes about 3.5 Mb , What could be the reason for this ?
Thanks in advance.

Sample down Image from Inputstream without decoding to Bitmap

I am trying to find a solution to be able to download an image from Url into the Filesystem and resize it without the need to decode it to a bitmap first.
The only solution I could find yet was the approach to decode it to a bitmap with specific SampleSize. However, the problem here is that a lot of memory is consumed when creating the bitmap which would limit the maximum size of the image depending on the free memory available on the device. Another Problem is that it takes a lot of time to decode it to a Bitmap and also to compress it back to an output stream.
My current code looks like this (options1 contains the original dimensions, calculateSampleSizeForImageSize() calculates the sampleSize for smaller required Imagesize):
if (maximumImageSize < Runtime.getRuntime().maxMemory() / 4f) {
//enough memory free to reduce size
//reduce size
BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inSampleSize = calculateSampleSizeForImageSize(options1, maximumImageSize);
options2.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options2);
try {
//write reduced size bitmap to picture file
os = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
bitmap.recycle();
return 0;
} catch (IOException e) {
Log.d(TAG, "failed to fetch image", e);
throw new IOException(e);
} finally {
closeStream(os);
closeStream(is);
}
So is there a way to bypass the bitmap and directly write the input stream in reduced size to the output stream?

Base64 encoded bitmap returns lengthy string

I am using the following code for encoding my bitmap:
public static String convertBitmapToBase64String(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String encodedImage = null;
try {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] byteArrayImage = baos.toByteArray();
encodedImage = Base64
.encodeToString(byteArrayImage, Base64.DEFAULT);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return encodedImage;
}
But this method returns a very long string and therefore I can't upload my image to the server.
Please help.
Thanks in advance.
There is nothing base64 can do for the actual size. The encoding will inevitably increase the size by a fixed percentage (about 37% IIRC).
The only thing you can do is make the image data smaller. You are using an unreasonable quality setting here:
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
100% quality JPEG is overkill, 83-90% is more reasonable for most images. If you need lossless image quality, try PNG. JPEG isn't designed for 100% quality, the resulting file will very likely be larger than if you used a lossless image format in the first place.
if your image is really too big you can always use Bitmap.createScaledBitmap() to resize it smaller

Categories

Resources