ALL,
I have a following problem. Here is the code:
class ParseCommunicator
{
private static int width = -1;
private static int height = -1;
#SuppressWarnings("deprecation")
public void getPhoto(Device dev) throws ParseException, IOException
{
InputStream stream = null;
Bitmap bmp = null;
BitmapFactory.Options opts = new BitmapFactory.Options();
Rect rect = new Rect();
WindowManager wm = (WindowManager) context.getSystemService( Context.WINDOW_SERVICE );
Display display = wm.getDefaultDisplay();
width = display.getWidth();
height = display.getHeight();
try
{
stream = new URL( dev.getBitmapURL() ).openStream();
opts.inJustDecodeBounds = true;
bmp = BitmapFactory.decodeStream( stream, rect, opts );
int bmpHeight = opts.outHeight;
int bmpWidth = opts.outWidth;
int inSampleSize = 1;
int reqHeight = height / 3 ;
int reqWidth = width / 2;
if( bmpHeight > reqHeight || bmpWidth > reqWidth )
{
int halfHeight = bmpHeight / 2;
int halfWidth = bmpWidth / 2;
while( ( halfHeight / inSampleSize ) > reqHeight && ( halfWidth / inSampleSize ) > reqWidth )
inSampleSize *= 2;
}
opts.inSampleSize = inSampleSize;
opts.inJustDecodeBounds = false;
bmp = BitmapFactory.decodeStream( stream, rect, opts );
}
catch( MalformedURLException e )
{
e.printStackTrace();
}
finally
{
if( stream != null )
stream.close();
}
if( bmp != null )
dev.setPhoto( bmp );
}
This code should get the bitmap (stored as png) from the Parse interface. When running the code it does not give me a bitmap, it has NULL in that object. No exception is thrown.
Trying to debug I found out following:
If I take out sampling, the bitmap is read without any issues, i.e. commenting the lines:
opts.inJustDecodeBounds = true;
...............
opts.inJustDecodeBounds = false;
will produce the bitmap I am looking for.
Those bitmaps will be later on used in a grid view.
The URL it is reading from is correct as I can get the bitmap without any issues without sampling.
The same sampling code works fine when I try to sample the picture taken from the Android Gallery and put it on the ImageView.
Could someone spot what is going on?
I'm doing my testing on the LG Android phone with Android 2.2.
Thank you in advance.
The problem is you are using the same inputstream twice in BitmapFactory.decodeStream().
Try calling reset() before you want to load the bitmap "for real"
// First make sure you are using a BufferedInputStream
InputStream bis = new BufferedInputStream(stream)
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bis.reset();
Here's a nice explanation from another user: https://stackoverflow.com/a/11214451/833647
Related
I have an activity where the user can open the camera like so
getPictureUri(createImageFromFile = true)?.let {
photoUri = it
openCameraActivity(REQUEST_IMAGE_CAPTURE, it)
} ?: photoViewModel.onRequestUriError()
openCamera is an Activity Extension that looks like this
fun Activity.openCameraActivity(requestCode: Int, pictureUri: Uri) {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
takePictureIntent.resolveActivity(packageManager)?.also {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri)
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(takePictureIntent, requestCode)
}
}
}
We write the image to a file when we get it back, and store the photoUri in the app somewhere. It works great! The issue is that some users have super high megapixel cameras, like 64mp, and when they take a picture it tries to display in the UI and it crashes.
FATAL EXCEPTION: main
Process: co.app.staging, PID: 27859
java.lang.RuntimeException: Canvas: trying to draw too large(256576512bytes) bitmap.
Is there a way I can limit the camera resolution on this? I could turn the quality down but that seems like a bandaid at best. What are some decent solutions for this?
I tried this way to draw bitmap on canvas from URI, try this:
(Explanation given below code)
private void drawBitmapUriOnCanvas(Uri selectedImage) {
if (selectedImage != null) {
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream(getContentResolver().openInputStream(
selectedImage), null, bmpFactoryOptions);
bmpFactoryOptions.inSampleSize = 2;
bmpFactoryOptions.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory
.decodeStream(getContentResolver().openInputStream(
selectedImage), null, bmpFactoryOptions);
Bitmap alteredBitmap;
if (bmp != null) {
if (getDeviceRam(this) <= 1024) {
bmp = getResizedBitmap(bmp, displayWidth);
}
bmp = checkAndRotateBitmap(bmp);
alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp
.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(alteredBitmap);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
Matrix matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
(now alteredBitmap is processed bitmap and you can use it)
}
} catch (FileNotFoundException e) {
Toast.makeText(this, "Error reading Image Metadata", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}
Other included methods:
public static long getDeviceRam(Context context) {
ActivityManager actManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
actManager.getMemoryInfo(memInfo);
long totalMemory = memInfo.totalMem;
long fileSizeInKB = totalMemory / 1024;
// Convert the KB to MegaBytes (1 MB = 1024 KBytes)
return fileSizeInKB / 1024;
}
public static Bitmap getResizedBitmap(Bitmap image, int maxSize) {
int width = image.getWidth();
int height = image.getHeight();
float bitmapRatio = (float) width / (float) height;
if (bitmapRatio > 1) {
width = maxSize;
height = (int) (width / bitmapRatio);
} else {
height = maxSize;
width = (int) (height * bitmapRatio);
}
return Bitmap.createScaledBitmap(image, width, height, true);
}
Here, first of all i decoded image uri using BitmapFactory, then i checked device ram and it it was of 1GB then i resize the bitmap other wise original bitmap will be ok for next step,
And next was simply drawing bitmap on canvas and you will get alteredBitmap will is processed bitmap and it will be easy to use.
If this will not work then there is another option for completing your requirement and it was given below:
Bitmap bitmap = getBitmapFromGallery(selectedImage.getPath(), 800, 800);
private Bitmap getBitmapFromGallery(String path, int width, int height) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
// if you use image from camera then in some cases it will rotated automatically like it was captured landscape but displays portrait then you have to use method `checkAndRotateBitmap()` which was also given below
// bitmap = checkAndRotateBitmap(bitmap);
return bitmap;
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
private Bitmap checkAndRotateBitmap(Bitmap bmp) {
try {
ExifInterface ei = new ExifInterface(imgPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
Bitmap rotatedBitmap;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotatedBitmap = Helper.rotateImage(bmp, 90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotatedBitmap = Helper.rotateImage(bmp, 180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotatedBitmap = Helper.rotateImage(bmp, 270);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
rotatedBitmap = bmp;
}
return rotatedBitmap;
} catch (IOException e) {
e.printStackTrace();
return bmp;
}
}
EDIT
for more information on how getBitmapFromGallery() method works, you should have look at https://medium.com/android-news/loading-large-bitmaps-efficiently-in-android-66826cd4ad53
That's all my friend, now try one of the methods for your requirement and let me know if this will work or not, also help me improve my answer if anything was wrong..Thank you!!
I found a lot of documentation on how to load large Bitmaps and avoid outofmemory exception. but the problem is that I have to take the image from my MediaStore.Images.media so the classical
decodeFile(path,options) indicated in the google documentation does not work to me
As you can see below I decommented the line // Bitmap photo= Mediastore.Images, that is the one that triggers the out of memory. on the other side adding
the line Bitmap bm=BitmapFactory.decodeFile(selectedImageToUri,options) returns null, although the compiler can see both the path in selectedImageToUri (that indicates the content provider where the pics are) than the options value, that I set to 8, because I want to subscale all the images
My question is how can I insert in bm the bitmap that is referring to the image selected by the user in the gallery. in the line BitMap photo does not return null and work really well, but I decommented because after I change a couple of images gives me outofmemory exception.
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable final Bundle savedInstanceState) {
if (flagVariable) {
if (selectedImageToUri != null) {
// BitMap photo = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), Uri.parse(selectedImageToUri));
final BitmapFactory.Options options= new BitmapFactory.Options();
options.inSampleSize=8;
Bitmap bm = BitmapFactory.decodeFile(selectedImageToUri, options);
pic = new BitmapDrawable(bm);
getActivity().getWindow().setBackgroundDrawable(pic);
} else {
getDefaultImageBackground(inflater, container);
}
hiddenList = inflater.inflate(R.layout.fragment_as_list_layout_temp, container, false);
} else {
getDefaultImageBackground(inflater, container);
}
listView = (ListView) hiddenList.findViewById(R.id.list_hidden);
MediaStore.getBitmap is just a simple convienence method, it looks like this:
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;
}
You can create your own method based on this that takes the options and calls a different overload on BitmapFactory:
public static final Bitmap getBitmap(ContentResolver cr,
Uri url,
BitmapFactory.Options options)
throws FileNotFoundException, IOException {
InputStream input = cr.openInputStream(url);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, options);
input.close();
return bitmap;
}
Usage:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap bm = getBitmap(getActivity().getContentResolver(),
Uri.parse(selectedImageToUri),
options);
I spent a lot of time on this problem, but no one will give me exact answer and finally i solved it. First create method and provide Image URI as argument, and this will return bitmap basically here i calculated image size on bases of, we can manage memory as well as image and get exact image in bitmap form.
you can even display 5000×8000 and 12MiB picture without any error code is tested just copy paste in your class and enjoy.
Use
Bitmap mBitmap = getPhoto(MYIMAGEURI);
Provide URI to method and get Bitmap
Bitmap getPhoto(Uri selectedImage) {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
Bitmap photoBitmap = null;
InputStream inputStream = null;
try {
inputStream = getContentResolver().openInputStream(selectedImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, bitmapOptions);
int imageWidth = bitmapOptions.outWidth;
int imageHeight = bitmapOptions.outHeight;
#SuppressWarnings("unused")
InputStream is = null;
try {
is = getContentResolver().openInputStream(selectedImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
float scale = 1.0f;
if (imageWidth < imageHeight) {
if (imageHeight > width * 1.0f) {
scale = width * 1.0f / (imageHeight * 1.0f);
}
} else {
if (imageWidth > width * 1.0f) {
scale = width * 1.0f / (imageWidth * 1.0f);
}
}
photoBitmap = decodeSampledBitmapFromResource(this,
selectedImage, (int) (imageWidth * scale),
(int) (imageHeight * scale));
return photoBitmap;
}
Decode Bitmap Sample using image size
public static Bitmap decodeSampledBitmapFromResource(Context context,
Uri uri, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream is = null;
try {
is = context.getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
BitmapFactory.decodeStream(is, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode editBitmap with inSampleSize set
options.inJustDecodeBounds = false;
InputStream inputs = null;
try {
inputs = context.getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return BitmapFactory.decodeStream(inputs, null, options);
}
Calculate Sample Size
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = Math.min(heightRatio, widthRatio);
// inSampleSize = heightRatio < widthRatio ? heightRatio :
// widthRatio;
}
return inSampleSize;
}
Or may be possible to solved using one line of code in manifiest.xml
is in application tag use this
android:largeHeap="true"
I'm working on xamarin forms project. I'm taking images from gallery and uploading those to server. My back-end is parse backend where we can not upload files having size more than 1MB. So, I'm trying to compress the image so that every time the image size is less than 1MB.
Mentioned below is my code :-
protected override async void OnActivityResult (int requestCode, Result resultCode, Intent intent)
{
if (resultCode == Result.Canceled)
return;
try {
var mediafile = await intent.GetMediaFileExtraAsync (Forms.Context);
// get byte[] from file stream
byte[] byteData = ReadFully (mediafile.GetStream ());
byte[] resizedImage = ResizeAndCompressImage (byteData, 60, 60, mediafile);
var imageStream = new ByteArrayContent (resizedImage);
imageStream.Headers.ContentDisposition = new ContentDispositionHeaderValue ("attachment") {
FileName = Guid.NewGuid () + ".Png"
};
var multi = new MultipartContent ();
multi.Add (imageStream);
HealthcareProfessionalDataClass lDataClass = HealthcareProfessionalDataClass.Instance;
lDataClass.Thumbnail = multi;
App.mByteArrayOfImage = byteData;
MessagingCenter.Send<IPictureTaker,string> (this, "picturetaken", mediafile.Path);
} catch (InvocationTargetException e) {
e.PrintStackTrace ();
} catch (Java.Lang.Exception e) {
e.PrintStackTrace ();
}
}
public static byte[] ReadFully (System.IO.Stream input)
{
using (var ms = new MemoryStream ()) {
input.CopyTo (ms);
return ms.ToArray ();
}
}
public static byte[] ResizeAndCompressImage (byte[] imageData, float width, float height, MediaFile file)
{
try {
// Load the bitmap
var options = new BitmapFactory.Options ();
options.InJustDecodeBounds = true;
options.InMutable = true;
BitmapFactory.DecodeFile (file.Path, options);
// Calculate inSampleSize
options.InSampleSize = calculateInSampleSize (options, (int)width, (int)height);
// Decode bitmap with inSampleSize set
options.InJustDecodeBounds = false;
var originalBitMap = BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length, options);
var resizedBitMap = Bitmap.CreateScaledBitmap (originalBitMap, (int)width, (int)height, false);
if (originalBitMap != null) {
originalBitMap.Recycle ();
originalBitMap = null;
}
using (var ms = new MemoryStream ()) {
resizedBitMap.Compress (Bitmap.CompressFormat.Png, 0, ms);
if (resizedBitMap != null) {
resizedBitMap.Recycle ();
resizedBitMap = null;
}
return ms.ToArray ();
}
} catch (Java.Lang.Exception e) {
e.PrintStackTrace ();
return null;
}
}
public static int calculateInSampleSize (BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 16;
if (height > reqHeight || width > reqWidth) {
int halfHeight = height / 2;
int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
But the problem is my image is not getting compressed.I'm not able to upload an image having size = 2MB and I want to upload images having size at-least 30 MB. Also I've observed that calculateInSampleSize always returns 16 as inSampleSize which is default one.
Please let me know if there's any issue in my code.
This seems like a very complicated and convoluted way of doing it. Here's a more concise sample that should help you resize your images:
protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
var stream = this.Resize(data.Data, 60, 60);
// Send the stream to Parse
}
private Stream Resize(Android.Net.Uri uri, float maxWidth, float maxHeight)
{
var scale = 1;
using (var rawStream = this.ContentResolver.OpenInputStream(f))
using (var options = new BitmapFactory.Options { InJustDecodeBounds = true })
{
BitmapFactory.DecodeStream(rawStream, null, options);
while(options.OutWidth / scale / 2 > maxWidth ||
options.OutHeight / scale / 2 > maxHeight)
{
scale *= 2;
}
}
using (var options = new BitmapFactory.Options { InSampleSize = scale })
using (var bitmap = f.GetBitmap(options))
{
var memoryStream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 0, memoryStream);
memoryStream.Position = 0;
return memoryStream;
}
}
Regarding why you are seeing InSampleSize = 16, my guess is that your image's height or width are less than 1920 (which is 60 * 2 * 16) and since you are using && in the while loop, the greater check for that side fails and thus, you never enter the while body.
Additionally, if you are looking to create smaller images, compressing them as Jpeg is a much better approach than using png's.
I am trying to reduce the size of bitmap from a url. I saw many posts, but all were about sampling a local file. I want to sample the image at url. Here is my code:
public Bitmap getScaledFromUrl(String url) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize = 1 / 10;
try {
return BitmapFactory.decodeStream((InputStream) new URL(url)
.getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
Is this approach correct? I am getting out of memory crashes in my app at this function. Any ideas?
This works. I found it at http://blog.vandzi.com/2013/01/get-scaled-image-from-url-in-android.html . Use the following snippet of code, pass params as you like.
private static Bitmap getScaledBitmapFromUrl(String imageUrl, int requiredWidth, int requiredHeight) throws IOException {
URL url = new URL(imageUrl);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(url.openConnection().getInputStream(), null, options);
options.inSampleSize = calculateInSampleSize(options, requiredWidth, requiredHeight);
options.inJustDecodeBounds = false;
//don't use same inputstream object as in decodestream above. It will not work because
//decode stream edit input stream. So if you create
//InputStream is =url.openConnection().getInputStream(); and you use this in decodeStream
//above and bellow it will not work!
Bitmap bm = BitmapFactory.decodeStream(url.openConnection().getInputStream(), null, options);
return bm;
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
It's really flexible.. I think you should try it out.
You are using it wrong. You are asking to make the picture 10 times bigger :) You should give the command in normal numbers, not fraction. For example:
final BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inSampleSize = 8;
b = BitmapFactory.decodeFile(image, options2);
with this configuration you obtain 8 times smaller picture than the original.
UPDATE: To load image from internet add this class to the project and do the following:
ImageLoader loader = new ImageLoader(context);
Bitmap image = loader.getBitmap(URL);
in my App I need to scale an image, this works with normaly. But when a big pictured will be
loaded then the decodeStream method returns null. Currently it does not work when the image was bigger than 6 MB (5616x3744)
Does someone know why this happens and what can i do to fix this?
public static Bitmap scaleImage(final String filepath, int height, int width) throws IOException{
if (filepath == null){
Log.e(TAG, "cannot create Thumbnail without file");
return null;
}
File f = new File(filepath);
InputStream is = new FileInputStream(f);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
is.close();
final int imageHeight = options.outHeight;
final int imageWidth = options.outWidth;
String imageType = options.outMimeType;
final int thumbnailHeight = height;
final int thumbnailWidth = width;
int heightRatio = Math.round((float) imageHeight) / Math.round((float) thumbnailHeight);
int widthRatio = Math.round((float) imageWidth) / Math.round((float) thumbnailWidth);
int inSampleSize = 0;
if (heightRatio < widthRatio){
inSampleSize = heightRatio ;
}
else{
inSampleSize = widthRatio;
}
options.inSampleSize = inSampleSize;
// Decode bitmap with inSampleSize set
int retrySteps = 0;
InputStream is2 = new FileInputStream(f);
Bitmap bmp = null;
for (int i = 0; i < 5; i++){
options.inJustDecodeBounds = false;
try {
bmp = BitmapFactory.decodeStream(is2, null, options);
if (bmp != null){
i = 10;
}
else{
System.gc();
SystemClock.sleep(i * 1000);
options.inSampleSize = inSampleSize + 1;
}
} catch (OutOfMemoryError e) {
System.gc();
SystemClock.sleep(i * 1000);
}
}
is2.close();
return bmp;
thanks