This is my code to load Bitmap into ImageView from filepath retrieved from another activity.
I can get file, but Bitmap is always null.
I have tried with 250kb image code works fine but it does not work with 1.5MB images.How to resolve this issue?
Logcat message:
skia: --- SkImageDecoder::Factory returned null
Choreographer: Skipped 855 frames! `The application may be doing too much work on its main thread`.
code
Bundle extras = getIntent().getExtras();
if (extras != null) {
String imagepath = extras.getString("FILEPATH1");
File imgFile = new File(imagepath);
if(imgFile.exists()){
Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
imgCaptured.setImageBitmap(bitmap);
}
}
}
As Javadocs say:
Returns: the resulting decoded bitmap, or null if it could not be decoded.
So, if you successfully can load a small bitmap from the same file but larger file fails, it is a strong indicator to the size being a problem. Most likely decoding the 1.5M JPEG file would result in a bitmap that is over 10M in size. Your phone can not load an image that big.
BTW, you can estimate the uncompressed size of the image by multiplying the width and height and multiplying that by 4 (one byte per channel: red, green, blue, alpha).
For example, a 2.6M JPEG that has 4128x2322 pixels takes about 38340000bytes (38M) when uncompressed.
This may help: Handling large Bitmaps
Related
I need to compress an image to send it to my server. I am trying to do it this way:
private Bitmap compressImage(Bitmap bitmapImg){
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmapImg.compress(Bitmap.CompressFormat.JPEG, 50, out);
Bitmap compressed = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
return compressed;
}
But when I compare the Byte count of the original Bitmap object and the compressed one, I get the same number:
Log.e("UNCOMPRESSED", Integer.toString(mBitmapImg.getByteCount()));
E/UNCOMPRESSED: 23970816
Log.e("COMPRESSED", Integer.toString(compressedBitmapImg.getByteCount()));
E/COMPRESSED: 23970816
How can I fix this to have a smaller file?
But when I compare the Byte count of the original Bitmap object and the compressed one, I get the same number:
The size of a Bitmap in memory is based only on its resolution (width and height in pixels) and bit depth (the number of bytes per pixel, for controlling how many colors can be used per pixel).
How can I fix this to have a smaller file?
You do not have a file. You have a Bitmap object in memory. An image file is usually stored in a compressed form. In particular, this is true for JPEG, PNG, WebP, and GIF, the four major image formats used in Android. So, for example, out.toByteArray() will be smaller than 23,970,816 bytes.
Moreover, you are not sending a Bitmap to the server. You are sending an image to the server. You need to read the documentation for the server, or talk to the server developers, to determine what image format(s) they support and how to send the image to the server (ideally, something efficient like an HTTP PUT).
If you want to reduce the in-memory size of the Bitmap, scale it to a lower-resolution image (e.g., via createScaledBitmap()).
You can change your bitmap format to RGB_565 from ARGB_8888. That'll reduce your bitmap's memory footprint to half, but, would lead to loss of quality as well. Unfortunately, that's the most you can do with Bitmap.
Having said that, the compression method that you're using should work fine for most situations. It's also the advocated method for a number of platforms. An example for Firebase is this.
I am using EasyImage library to take pictures.
I then convert those Files to Bitmap and then I convert Bitmap to Base64 and upload it to server.
I know, it's not a good way to do it, but that's how I currently do it.
When a picture is taken:
#Override
public void onImagePicked(File imageFile, EasyImage.ImageSource source) {
uploadImage(imageFile);
}
This is the first line inside "uploadImage" method:
Image image = new Image(LoginManager.getInstance(getApplicationContext()).getUsername(), file);
This is the constructor:
public Image(String userName, File imageFile) {
this.userName = userName;
this.imageFile = imageFile;
createBase64(getBitmap());
}
Inside "getBitmap" is where the problem begins. These 2 lines in particular:
bitmap = BitmapFactory.decodeFile(imageFile.getPath());
bitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, false);
imageFile is never null.
I checked with debugger at least a 100 times and it is never null. It also always has path.
getPath() is never null.
However, it still often fails to create bitmap.
Sometimes it is successful and everything is OK, but most of the times, the bitmap is null.
I don't know why.
File (the picture taken) is always created successfully and is never null, but it just fails to create the Bitmap for some reason.
From the documentation
Returns
the resulting decoded bitmap, or null if it could not be decoded.
This can have various reasons, most of the times the bitmap is too large and the space could not be allocated.
Check that the path to the image exists and that you have read / write permissions to the URI specified.
If you do have access but it is still failing you should add BitmapFactory.Options to the method call and set inSampleSize to load a smaller version of the image.
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
Also you should always check for null returned when working with bitmaps, since memory can always be an issue.
I have a problem in saving images in my app.
The process is simple:
1 - I read the image into a bitmap for display
2 - Save the bitmap as a new file
At this point, I do not understand why the final file has a size much larger than the original image. For example, a 170 kb image is saved in a new file 1 mb.
I know the difference in the management of compression between PNG and JPG, but I expect that if I read a PNG file, and save it in the same format, the size remains the same.
Thank you all for the help.
Edit
This is my code for saving method:
Bitmap resized = Bitmap.createScaledBitmap(original, generateWidth, generateHeight, false);
FileOutputStream out = new FileOutputStream(tempResizedFile.getPath());
resized.compress(generateType.CompressFormat(),
qualityBar.getProgress(), out);
out.close();
I just added support for album art in my Android application and I've encountered a problem where displaying the album art in a layout causes the application memory to spike and the playback service is eventually killed to free up memory. I believe the issue is that I'm adding the extracted album art to the layout without compressing it. This results in a large image having to be cached in memory. The code I'm using to make the Bitmap is:
byte [] blob = mCursor.getBlob(mCursor.getColumnIndexOrThrow(Media.MediaColumns.PICTURE));
if (blob != null) {
return BitmapFactory.decodeByteArray(blob, 0, blob.length);
}
Is it possible to uniformly scale/compress these Bitmaps to reduce their memory footprint. Also, is there a way to compress directly using the byte array (rather then an inputstream).
Try this
Options opt = new Options();
opt.inSampleSize = 2;
if (blob != null) {
return BitmapFactory.decodeByteArray(blob, 0, length, opt)
}
More info about this
public int inSampleSize
Added in API level 1
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 will try to fulfill this request, but the resulting bitmap may have different dimensions that precisely what has been requested. Also, powers of 2 are often faster/easier for the decoder to honor.
By following this link, I have written the following code to show a large image bitmap from sdcard.
try {
InputStream lStreamToImage = context.getContentResolver().openInputStream(Uri.parse(imagePath));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
options.inSampleSize = 8; //Decrease the size of decoded image
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(lStreamToImage, null, options);
} catch(Exception e){}
image.setImageBitmap(bitmap);
But it is not returning the bitmap(I mean it returns null). In logcat it is showing the below message repeatedly
08-02 17:21:04.389: D/skia(19359): --- SkImageDecoder::Factory returned null
If I will comment the options.inJustDecodeBounds line and rerun it, it works fine but slowly. The developer guide link I provided above says to use inJustDecodeBounds to load bitmaps efficiently.
Please tell me where I am doing wrong.
inJustDecodeBounds does not load bitmaps. That's the point of it. It loads the dimensions of the bitmap without loading the actual bitmap so you can do any pre-processing or checking on the bitmap before you actually load it. This is helpful is you, say, were having memory issues and you needed to check if loading a bitmap would crash you program.
The reason your bitmap might be loading slowly is because it's probably very large and SD cards are very slow.
EDIT:
From the documentation:
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
Edit 2:
Looking at your code with the example provided by Google, it looks like you are doing relatively the same thing. The reason it's returning null is possibly your InputStream has been modified in the first decoding and thus not starting at the beginning of the bitmap's memory address (they use a resource ID rather than InputStream.
From the code you supplied here, here's what I've figured. You are ALWAYS setting a sample size to 8 regardless of what the first decoding gives you. The reason Google decodes the first time is to figure out what the actual size of the bitmap is versus what they want. They determine that the bitmap is ZxZ dimensions and they want YxY dimensions, so they calculate the samplesize that they should use from the second decoding. You are not doing this. You are simply retrieving the dimensions of the bitmap and not using them. THEN, you set the sample size to a hard-coded 8, swapping it to a hard-coded ARGB_4444 bitmap, then decoding the full bitmap in to memory. In other words, these three lines are not being used:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
Setting inJustDecodeBounds merely gives you the bitmap's dimensions without putting the bitmap in to memory. It doesn't make it more efficient. It's meant to allow you to load bitmaps in a smaller memory space if they are too big because you can pre-decide what size it should be without decoding the whole thing).
The reason decoding the bitmap is slow might merely be a CPU thing. Depending on the size of your bitmap, you're loading the bitmap from an InputStream from the SDcard which is a slow operation in itself.