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.
Related
I have an Uri pointing to an image I have chosen. I want to store the image in a Mat variable, edit it and show it in an ImageView. Till now, I just want to load the image in the Mat variable, and show it, without edits. This is my code:
Uri uri = data.getData();
Mat image = Imgcodecs.imread(uri.getPath());
//several edits in future
Bitmap resultBitmap = Bitmap.createBitmap(image.rows(), image.cols(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(image, resultBitmap);
imageView.setImageBitmap(resultBitmap);
The line that gives the error is Bitmap resultBitmap = Bitmap.createBitmap(image.rows(), image.cols(),Bitmap.Config.ARGB_8888);, and says that image.rows() and image.cols() cannot be 0.
It's logic that if the dimensions are (0, 0) it gives an error, but I can't figure out why they are 0.
The problem was that I was passing a wrong URI to imread, which therefore failed and returned an empty Mat. As Dan MaĊĦek suggested I've called the empty method, that returned true, and I found that I was passing a wrong URI.
I'm running into issues with the images in my gallery being much much larger than I need them to be.
I've looked for ways to reduce their size before I actually pull them in, but I'm just not quite putting it together as most of what I'm finding deals with BitMap resources and not a BitMap that already exists in the gallery.
So, basically, I am getting the image like so,
imageBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
But before I actually assign it to my BitMap var over there I need to scale it down to a reasonable size for a phone.
Any help with understanding what is going on here better is appreciated.
getBitmap() is a weak convenience method. Its body is a whopping four lines of code:
public static final Bitmap getBitmap(ContentResolver cr, Uri url)
throws FileNotFoundException, IOException {
InputStream input = cr.openInputStream(url);
Bitmap bitmap = BitmapFactory.decodeStream(input);
input.close();
return bitmap;
}
This is why I don't bother teaching people about it.
IMHO, the best solution is for you to use one of the many image-loading libraries available for Android, such as Picasso. Most of the good ones can load from a Uri and handle your resizing as part of the operation, doing the heavy lifting on a background thread.
If, for whatever reason, you want to do all that work yourself, call BitmapFactory.decodeStream() with a BitmapFactory.Options object. In particular, set inSampleSize to indicate that you want the image to be resampled as part of reading it, so you wind up with a smaller Bitmap taking up less heap space.
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
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.
I have a method called loadFromCache and it returns a bitmap if it is found in the cache. Long story short I have it narrowed down to this method, returning null at the end if the try/catch fails.
FileInputStream fis = new FileInputStream(getCacheDir()+(""+position));
ObjectInputStream ois = new ObjectInputStream(fis);
Bitmap temp = (Bitmap)ois.readObject();
fis.close();
return temp;
I have previously tried the Bitmap.compress(...) methods to save bitmaps but they were a little slow for my needs... Yes the bitmap has been written to these positions, but I don't know if it (Bitmap) is serializable so is it actually saving? And yes I remembered to flush when I wrote the file.
How is Bitmap.compress() too slow for you? The only faster(?) way would be to write the bitmap unchanged to disk, see below.
Using MappedByteBuffer together with Bitmap.copyPixelsToBuffer() may work. I haven't tested this but it seems like it could work. Note that you most likely will have to store image dimensions yourself.
Sharing an experience I just had with Bitmap.compress being very slow:
My source image was a jpeg, and I was passing Bitmap.CompressFormat.PNG to bitmap.compress. This caused the compress operation to take 10-15 seconds.
Once I changed it to JPEG (such that the source and destination file remain the same image format) then the operation takes less than a second. Perhaps the original question came about through a similar means, and maybe someone else finds this helpful.