I use the following code to get a Blob back from my SQLite database and get back a bitmap. My problem is that the reconstructed bitmap is larger than the original picture (input). It seems that my BitmapFactory.Options isn't working, but I have no idea what is wrong, nor am I getting an error. What is wrong with this code?
byte[] blob = contact.getMP();
ByteArrayInputStream inputStream = new ByteArrayInputStream(blob);
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
bmpFactoryOptions.inScaled = false;
bmpFactoryOptions.outHeight = 240;
bmpFactoryOptions.outWidth = 320;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, bmpFactoryOptions);
try {
FileOutputStream out = new FileOutputStream("mnt/sdcard/test5.png"); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
}
When I check the output my 320 x 240 picture is 427 x 320. I don't want to use Bitmap.createScaledBitmap because it messes up the quality.
you have to pass to decodeStream in order to work
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, bmpFactoryOptions);
Related
I have a very large image on my device (not taken with device camera) I need to resize the image before sending it to server. So say it is 14mb originally and I want to reduce it to 2mb. I want to resize the image without losing quality at all. What I mean is that the server will allow for zooming into the photo. So I am thinking inDensity is important. Except I don’t understand how inDensity works in this regard. Will someone please explain how I can resize the photo to 2mb but keep such a high density that the image can be zoomed with high quality? Or is that not possible.
I already know how to resize images:
public static Bitmap resizeImage(String file, int reqHeight, int reqWidth) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, options);
options.inSampleSize = calculateInSampleSize(options, reqHeight, reqWidth);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(file, options);
}
Try this in your code:
Bitmap bmp = BitmapFactory.decodeFile(myImage)
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bmp.compress(CompressFormat.JPEG, 70, bos);
InputStream in = new ByteArrayInputStream(bos.toByteArray());
ContentBody image = new InputStreamBody(in, "image/jpeg", "filename");
It worked for me!
I'm trying to compress images selected by user from gallery for uploading. I saw that my camera pictures are over 5MB and I would like to compress them(same as facebook if possible). What I've been trying:
I let the user select the photo from gallery,get the uri and use this:
File file = new File(getRealPathFromURI(getActivity(), selectedImageUri));
long length = file.length();
Log.e("Filesize:", "Before: " + length);
if (file.getName().toLowerCase().endsWith("jpg")||file.getName().toLowerCase().endsWith("jpeg")){
Bitmap original;
try {
original = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), selectedImageUri);
length = sizeOf(original);
Log.e("Filesize:", "BeforeCompression: " + length);
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.JPEG, 50, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
length = sizeOf(decoded);
Log.e("Filesize:", "AfterCompression: " + length);
} catch (IOException e) {
e.printStackTrace();
Log.e("Filesize:", "Error: " + e);
}
I did this to test if it was working first, but what I get in the console is:
/name.company.newapp E/Filesize:: Before: 4970874
/name.company.newapp E/Filesize:: BeforeConversion: 63489024
/name.company.newapp E/Filesize:: AfterConversion: 63489024
The size doesn't change at all. Is this the right approach ?
This happens because you're actually getting the size of memory used by the Bitmap object by calling sizeOf(bitmap) and not the actual file size.
As you should know, a bitmap operates with the number of pixels in an image. Even though you compress the image using a JPEG compression, the image's width and height do not change. Thus the number of pixels do not change and thus the Bitmap's size (in memory) would not change too.
However, if you save the compressed bitmap to a location in your hard disk and use File.length() to calculate the size of the compressed image, then you will notice the difference.
Please check the size before decoding and after compression:
length = sizeOf(original);
Also i would recommend you to flush and close the outputstream:
out.flush();
out.close();
Hope i could help!
Edit:
Please try the following method to decode your bitmap:
public static Bitmap decodeFile(File f,int WIDTH,int HEIGHT){
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HEIGHT=HEIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HEIGHT)
scale*=2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
You can change the width and height of your picture to make it smaller.
I have primarily a question,Can I set a byte[] as image in ImageView without converting back to Bitmap?
The OOM error is thrown when I decode the byte[] to bitmap and I am seeing this happening for images of 300 KB even. I have opted to use BitmapFactory.options (inSampleSize) to scale the image to avoid the exception.
But this alters the dimension (width especially) of my image which looks so bad in my application. Is there anyway to fetch the original image from DB without scaling or altering the image (of course without the risk of OOM error)??
Any help is appreciated..!!
Thanks in Advance.
Code that troubles:
ByteArrayInputStream imageStream = new ByteArrayInputStream(imageByteArray);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap ImageBmp = BitmapFactory.decodeStream(imageStream, null, options);
options.inJustDecodeBounds = false;
//checking if the image is too large we can resize it small in order to avoid Out of Memory error from the decodeStream method.
if(showfullImage(options.outWidth) && !isExternal)
{
options.inSampleSize = 2;
}
imageStream.close();
imageStream = new ByteArrayInputStream(imageByteArray);
ImageBmp = BitmapFactory.decodeStream(imageStream, null, options);
return ImageBmp;
I am using Base64 Encoded String to convert image and then create it at Windows Server.
It is working fine in most of devices but It is giving error java.lang.OutOfMemoryError in android Version 2.3.5. I tried android:largeHeap="true" but it din't work.
Android Code:
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
Bitmap bitmap = drawable.getBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] data = baos.toByteArray();
strBase64 = Base64.encodeToString(data, Base64.DEFAULT);
I want to give crop image option to user and then store it at windows server. Is there any easy and better way for this ?
My code at asp.net:
public System.Drawing.Image Base64ToImage(string base64String)
{
byte[] imageBytes = Convert.FromBase64String(base64String);
using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
{
ms.Write(imageBytes, 0, imageBytes.Length);
System.Drawing.Image image = System.Drawing.Image.FromStream(ms, true);
return image;
}
}
System.Drawing.Image convertedImage = Base64ToImage(Photo);
convertedImage.Save(Server.MapPath("~\\images\\profileImg\\jeeten.jpg"), System.Drawing.Imaging.ImageFormat.Jpeg);
I tried some cropping image codes but It gave error : A generic error occurred in GDI+.
I would make these changes:
Instead of getting the image from the ImageView, consider using it's original source (file system? asset?). Then you dont have to re-compress the image.
Do not compress JPGs at 100% quality. There is large cost for un-noticeable image quality gains. If you need 100%, use PNG, otherwise use an 85% quality JPG.
You have several copies of the image in memory - in the drawable, in your byte array, in your base 64 string, etc. You can eliminate some of these.
Why convert to base 64? Just send the bytes to the server - Here's an example using PHP, but in .NET use HttpPostedFile to receive it.
Before Compressing the bitmap you can follow this:
//Put your image in file.
File file = new File("/mnt/sdcard/image.jpg");
//Pass your file in decodeImage method (your file, with its width and height as you want to display)
Bitmap bitmapImg=decodeImage(file,100,100);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmapImg.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
String encodedString = Base64.encodeToString(byteArray, Base64.DEFAULT);
//Body of decodeFile(File f,int WIDTH,int HIGHT)
public Bitmap decodeFile(File f,int WIDTH,int HIGHT)
{
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
b1= BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (Exception e) {}
return b1;
}
Take a picture with the normal smartphone camera.
Ok I've been Googling this for a while now, and everyone seems to use something like the following:
Bitmap bm = BitmapFactory.decodeStream(getContentResolver().openInputStream(fileUri));
ByteArrayOutputStream out = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 25, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
I use this to check the file size:
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
protected int sizeOf(Bitmap data) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) {
return data.getRowBytes() * data.getHeight();
} else {
return data.getByteCount();
}
}
The Bitmap is not getting any smaller, before and after:
Log.d("image", sizeOf(bm)+"");
Log.d("image", sizeOf(decoded)+"");
Results:
11-05 02:51:52.739: D/image(2558): 20155392
11-05 02:51:52.739: D/image(2558): 20155392
Pointers?
Answer:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 50;
Bitmap bmpSample = BitmapFactory.decodeFile(fileUri.getPath(), options);
Log.d("image", sizeOf(bmpPic)+"");
ByteArrayOutputStream out = new ByteArrayOutputStream();
bmSample.compress(Bitmap.CompressFormat.JPEG, 1, out);
byte[] byteArray = out.toByteArray();
Log.d("image", byteArray.length/1024+"");
The compress method, as mentioned in the documentation:
Write a compressed version of the bitmap to the specified outputstream. The bitmap can be reconstructed by passing a corresponding inputstream to BitmapFactory.decodeStream()
Thus the variable out now contains the compressed bitmap. Since you check the size after calling decodeStream the bitmap is decompressed and returned to you. Therefore the size is same.