Xamarin forms android image is not getting compressed - android

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.

Related

outofmemory exception for Large Bitmap

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"

resize image with activitytresult

how to resize image with my below code ? the code get image but I didnt how to resize it.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1 && resultCode == RESULT_OK && data != null && data.getData() != null) {
//file name
filePath = data.getData();
try {
// Bundle extras2 = data.getExtras();
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath);
// imageview.setImageBitmap(bitmap);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte imageInByte[] = stream.toByteArray();
Intent i = new Intent(this,
AddImage.class);
i.putExtra("image", imageInByte);
startActivity(i);
} catch (IOException e) {
e.printStackTrace();
}
}
}
I read a so queston using this way , but its different then my way
File dir=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
Bitmap b= BitmapFactory.decodeFile(PATH_ORIGINAL_IMAGE);
Bitmap out = Bitmap.createScaledBitmap(b, 320, 480, false);
File file = new File(dir, "resize.png");
FileOutputStream fOut;
try {
fOut = new FileOutputStream(file);
out.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
b.recycle();
out.recycle();
} catch (Exception e) {}
I would like to suggest you that first set the bitmap image to imageview and apply scale i.e., resize as per your requirement using setScaleType method.
ex:
imageview.setScaleType(ScaleType.CENTER);
(or)
If you want to try in same way then I think this link may be help full.
https://argillander.wordpress.com/2011/11/24/scale-image-into-imageview-then-resize-imageview-to-match-the-image/
To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to true in your BitmapFactory.Options object. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888). Here’s a method to calculate a sample size value that is a power of two based on a target width and height:
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;
}
Taken from here

Sampling a bitmap from url

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);

Read the bitmap from Parse on Android

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

Out of Memory Error - Bitmap Size

I want to display this image and upload it to a server.
I'm using the camera app to take a photo and returning the file path of the photo to an activity. I run into an Out of Memory error on some phones because I have a constant image size that I try and import between devices.
How can I pull in the largest image size possible to upload to the server while still working within the memory constraints of the phone?
Code Follows:
request to load Aync
GetBitmapTask GBT = new GetBitmapTask(dataType, path, 1920, 1920, loader);
GBT.addAsyncTaskListener(new AsyncTaskDone()
{
#Override
public void loaded(Object resp)
{
crop.setImageBitmap((Bitmap)resp);
crop.setScaleType(ScaleType.MATRIX);
}
#Override
public void error() {
}
});
GBT.execute();
The Async Task that throws the OOM error
public class GetBitmapTask extends AsyncTask<Void, Integer, Bitmap>
{
...
#Override
public Bitmap doInBackground(Void... params)
{
Bitmap r = null;
if (_dataType.equals("Unkown"))
{
Logger.e(getClass().getName(), "Error: Unkown File Type");
return null;
}
else if (_dataType.equals("File"))
{
Options options = new Options();
options.inJustDecodeBounds = true;
//Logger.i(getClass().getSimpleName(), _path.substring(7));
BitmapFactory.decodeFile(_path.substring(7), options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
Logger.i(getClass().getSimpleName(),
"height: " + options.outHeight +
"\nwidth: " + options.outWidth +
"\nmimetype: " + options.outMimeType +
"\nsample size: " + options.inSampleSize);
options.inJustDecodeBounds = false;
r = BitmapFactory.decodeFile(_path.substring(7), options);
}
else if (_dataType.equals("Http"))
{
r = _loader.downloadBitmap(_path, reqHeight);
Logger.i(getClass().getSimpleName(), "height: " + r.getHeight() +
"\nwidth: " + r.getWidth());
}
return r;
}
public static int calculateInSampleSize( 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;
while (height / inSampleSize > reqHeight || width / inSampleSize > reqWidth)
{
if (height > width)
{
inSampleSize = height / reqHeight;
if (((double)height % (double)reqHeight) != 0)
{
inSampleSize++;
}
}
else
{
inSampleSize = width / reqWidth;
if (((double)width % (double)reqWidth) != 0)
{
inSampleSize++;
}
}
}
return inSampleSize;
}
}
You can give the camera the uri pointing to file you want to save the image into:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mDefaultPhotoUri); // set path to image file
You can then upload that file to your server, so you have the full bitmap size. On the other hand, there is no need (and on many devices no way) to decode and show bitmap 1920x1920 (or similar) in UI, it's just too big.
Hope this helps.

Categories

Resources