I'm trying to auto-rotate pictures from system gallery intent, but ExifInterface can't get the path from picture uri.
It's crashing on this line:
img
Creating gallery intent:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getString(R.string.choose_image)), 1);
onActivityResult:
#Override
protected void onActivityResult(int RequestCode, int ResultCode, Intent I) {
super.onActivityResult(RequestCode, ResultCode, I);
if (RequestCode == 1 && ResultCode == RESULT_OK && I != null && I.getData() != null) {
uri = I.getData();
try {
ImageBitmap = handleSamplingAndRotationBitmap(Gallery.this, uri);
} catch (IOException e) {
e.printStackTrace();
}
SelectedImage.setImageBitmap(ImageBitmap);
detectImage();
} else {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}
Rotating:
/**
* This method is responsible for solving the rotation issue if exist. Also scale the images to
* 1024x1024 resolution
*
* #param context The current context
* #param selectedImage The Image URI
* #return Bitmap image results
* #throws IOException
*/
public static Bitmap handleSamplingAndRotationBitmap(Context context, Uri selectedImage)
throws IOException {
int MAX_HEIGHT = 1024;
int MAX_WIDTH = 1024;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
BitmapFactory.decodeStream(imageStream, null, options);
imageStream.close();
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
imageStream = context.getContentResolver().openInputStream(selectedImage);
Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);
img = rotateImageIfRequired(img, selectedImage);
return img;
}
/**
* Calculate an inSampleSize for use in a {#link BitmapFactory.Options} object when decoding
* bitmaps using the decode* methods from {#link BitmapFactory}. This implementation calculates
* the closest inSampleSize that will result in the final decoded bitmap having a width and
* height equal to or larger than the requested width and height. This implementation does not
* ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
* results in a larger bitmap which isn't as useful for caching purposes.
*
* #param options An options object with out* params already populated (run through a decode*
* method with inJustDecodeBounds==true
* #param reqWidth The requested width of the resulting bitmap
* #param reqHeight The requested height of the resulting bitmap
* #return The value to be used for inSampleSize
*/
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) {
// 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 = heightRatio < widthRatio ? heightRatio : widthRatio;
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down further
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
/**
* Rotate an image if required.
*
* #param img The image bitmap
* #param selectedImage Image URI
* #return The resulted Bitmap after manipulation
*/
private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException {
ExifInterface ei = new ExifInterface(selectedImage.getPath());//HERE IS PROBLEM
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
private static Bitmap rotateImage(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
This rotating was fully copied from another question, where it works. So i don`t know what is wrong. Uri have path inside, but getPath() can not find it...
Related
In my app user can take picture from camera intent , then I save this image as full size image
not thumbnail. Then what I want to do is use this saved image to compress it and send it over to server.
The image size from camera intent is around 7-8 MB and resolution of 5664x4248.
The requirements is to achieve is image of same size and quality of whatsapp which is 40-80KB
I tried different solution but I couldn't achieve the same good quality and size.
For this I used id.zelory:compressor:2.1.1 library
Any Idea?
Here I call this method after saving the image to resize it
private File customCompressImage (File imgFile) {
String destinationDirectoryPath= Environment.getExternalStorageDirectory().getPath() + "/Pictures/";
try {
return new CustomCompressor(context)
.setMaxWidth(612)
.setMaxHeight(816)
.setQuality(80)
.setCompressFormat(Bitmap.CompressFormat.JPEG)
.setDestinationDirectoryPath(destinationDirectoryPath)
.compressToFile(imgFile);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
CompressImage
static File compressImage(File imageFile, int reqWidth, int reqHeight, Bitmap.CompressFormat compressFormat, int quality, String destinationPath) throws IOException {
FileOutputStream fileOutputStream = null;
File file = new File(destinationPath).getParentFile();
if (!file.exists()) {
file.mkdirs();
}
try {
fileOutputStream = new FileOutputStream(destinationPath);
// write the compressed bitmap at the destination specified by destinationPath.
decodeSampledBitmapFromFile(imageFile, reqWidth, reqHeight).compress(compressFormat, quality, fileOutputStream);
} finally {
if (fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
}
return new File(destinationPath);
}
static Bitmap decodeSampledBitmapFromFile(File imageFile, int reqWidth, int reqHeight) throws IOException {
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap scaledBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
//check the rotation of the image and display it properly
ExifInterface exif;
exif = new ExifInterface(imageFile.getAbsolutePath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
Matrix matrix = new Matrix();
if (orientation == 6) {
matrix.postRotate(90);
} else if (orientation == 3) {
matrix.postRotate(180);
} else if (orientation == 8) {
matrix.postRotate(270);
}
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
return scaledBitmap;
}
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) {
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;
}
I tried passing different max width and height and quality and I never achived both small size and good quality
You can try this piece of code.
public static Bitmap getCompressed( Bitmap imageBitmap) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
return imageBitmap;
}
// 100 is quality,
change it according to your need
#End User shouldn't it be like this instead (that's at least what I get from reading the documentation):
public static Bitmap getCompressed(Bitmap imageBitmap) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (!imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out))
throw new IllegalStateException("Unable to compress image");
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
return BitmapFactory.decodeStream(in);
}
I'm trying to load an image from the internet using glide, but the size is too big for my application(The image size in dimensions 400X400 and the memory allocating is 352KB and the size of ImageView is same as the image dimensions),
I have tried to decode the bitmap after loading it and then apply it to the ImageView but it not working,
anyone can help me with this problem.
this is my Glide code:
Glide.with(this)
.asBitmap()
.load(url)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
int byteCount = resource.getAllocationByteCount();
int sizeInKB = (resource.getRowBytes() * resource.getHeight()) / 1024;
int sizeInMB = sizeInKB / 1024;
Glide.with(TestActivity.this)
.asBitmap()
.load(decodeSampledBitmapFromResource(resource, 400, 400))
.into(ss2);
sizeText.setText(sizeInKB + " KB");
}
});
and this is the code for decoding the 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;
}
public static Bitmap decodeSampledBitmapFromResource(Bitmap bitmap, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
ByteArrayOutputStream blob = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, blob);
byte[] bitmapData = blob.toByteArray();
BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bitmap.compress(Bitmap.CompressFormat.PNG, 0, blob);
byte[] bitmapData2 = blob.toByteArray();
return BitmapFactory.decodeByteArray(bitmapData2, 0, bitmapData2.length, options);
}
The size of an image depends of many things, one of them is it dimension and the quality; by default the quality format in Glide V4 is ARGB_8888 so if you are using it you can change it to RGB_565 that is smaller. You can found this and more information here.
In Picasso you can do it with .fit(), in Glide you have centerCrop() or fitCenter()
This methods resize the image to the container associated.
I am working on an android app, which has camera capture and photo uploading feature. If the device has a high resolution camera, the captured image size will be really large (1~3MB or more).
Since the app will need to upload this image to server, I will need to compress the image before uploading. If the camera captured a 1920x1080 full-res photo for example, the ideal output is to keep a 16:9 ratio of the image, compress it to be a 640x360 image to reduce some image quality and make it a smaller size in bytes.
Here is my code (referenced from google):
/**
* this class provide methods that can help compress the image size.
*
*/
public class ImageCompressHelper {
/**
* Calcuate how much to compress the image
* #param options
* #param reqWidth
* #param reqHeight
* #return
*/
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;
}
/**
* resize image to 480x800
* #param filePath
* #return
*/
public static Bitmap getSmallBitmap(String filePath) {
File file = new File(filePath);
long originalSize = file.length();
MyLogger.Verbose("Original image size is: " + originalSize + " bytes.");
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize based on a preset ratio
options.inSampleSize = calculateInSampleSize(options, 480, 800);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap compressedImage = BitmapFactory.decodeFile(filePath, options);
MyLogger.Verbose("Compressed image size is " + sizeOf(compressedImage) + " bytes");
return compressedImage;
}
The problem with the above code is:
It cannot keep the ratio, the code is forcing the image to resized to 480x800. if user captured a image in another ratio, the image will not look good after compress.
It doesn't functioning well. The code will always change the image size to 7990272byte no matter what the original file size is. If the original image size is pretty small already, it will make it big (my test result to take a picture of my wall, which is pretty much mono-colored):
Original image size is: 990092 bytes.
Compressed image size is 7990272 bytes
I am asking if there's suggestion of a better way to compress photo so it can be uploaded smoothly?
You need to decide on a limit for either your width or height (not both, obviously). Then replace those fixed image sizes with calculated ones, say:
int targetWidth = 640; // your arbitrary fixed limit
int targetHeight = (int) (originalHeight * targetWidth / (double) originalWidth); // casts to avoid truncating
(Add checks and calculation alternatives for landscape / portrait orientation, as needed.)
As #harism also commented: the large size you mentioned is the raw size of that 480x800 bitmap, not the file size, which should be a JPEG in your case. How are you going about saving that bitmap, BTW? Your code doesn't seem to contain the saving part.
See this question here for help on that, with the key being something like:
OutputStream imagefile = new FileOutputStream("/your/file/name.jpg");
// Write 'bitmap' to file using JPEG and 80% quality hint for JPEG:
bitmap.compress(CompressFormat.JPEG, 80, imagefile);
Firstly i check the size of image then i compress image according to size and get compressed bitmap then send that bitmap to server
For Compressed bitmap call below funtion we have to pass image path in below funtion
public Bitmap get_Picture_bitmap(String imagePath) {
long size_file = getFileSize(new File(imagePath));
size_file = (size_file) / 1000;// in Kb now
int ample_size = 1;
if (size_file <= 250) {
System.out.println("SSSSS1111= " + size_file);
ample_size = 2;
} else if (size_file > 251 && size_file < 1500) {
System.out.println("SSSSS2222= " + size_file);
ample_size = 4;
} else if (size_file >= 1500 && size_file < 3000) {
System.out.println("SSSSS3333= " + size_file);
ample_size = 8;
} else if (size_file >= 3000 && size_file <= 4500) {
System.out.println("SSSSS4444= " + size_file);
ample_size = 12;
} else if (size_file >= 4500) {
System.out.println("SSSSS4444= " + size_file);
ample_size = 16;
}
Bitmap bitmap = null;
BitmapFactory.Options bitoption = new BitmapFactory.Options();
bitoption.inSampleSize = ample_size;
Bitmap bitmapPhoto = BitmapFactory.decodeFile(imagePath, bitoption);
ExifInterface exif = null;
try {
exif = new ExifInterface(imagePath);
} catch (IOException e) {
// Auto-generated catch block
e.printStackTrace();
}
int orientation = exif
.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
Matrix matrix = new Matrix();
if ((orientation == 3)) {
matrix.postRotate(180);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
} else if (orientation == 6) {
matrix.postRotate(90);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
} else if (orientation == 8) {
matrix.postRotate(270);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
} else {
matrix.postRotate(0);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
}
return bitmap;
}
getFileSize funtion for getting the size of image
public long getFileSize(final File file) {
if (file == null || !file.exists())
return 0;
if (!file.isDirectory())
return file.length();
final List<File> dirs = new LinkedList<File>();
dirs.add(file);
long result = 0;
while (!dirs.isEmpty()) {
final File dir = dirs.remove(0);
if (!dir.exists())
continue;
final File[] listFiles = dir.listFiles();
if (listFiles == null || listFiles.length == 0)
continue;
for (final File child : listFiles) {
result += child.length();
if (child.isDirectory())
dirs.add(child);
}
}
return result;
}
I'm trying to create a resized Bitmap from an Uri.
While searching the web I saw couple of partial examples and only got confused.
What is the correct way to create this task?
Thanks.
There's a very nice resource article you should read:
http://developer.android.com/training/displaying-bitmaps/index.html
here some code, taken from this article:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
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) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger
// inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down
// further.
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
Matrix matrix = new Matrix();
matrix.postScale(xScale, yScale);
Bitmap newBitmap = Bitmap.createBitmap(oldBitmap, 0, 0, orgWidth, orgHeight, matrix, true);
where xScale and yScale should be negative numbers if you want to reduce the size else positive :)
I'm resizing an array of Bitmaps to a certain percentage of screen (So looks the same on all devices). Some of the bitmaps are sprites with + 256kb in size (explosions etc).
Obviously the VM is running out of memory once the bitmaps are converted twice, the bitmaps only convert at the beginning of the android application but it's still giving the error.
Can anyone tell me, is there a better, faster, more effcient way to return this peice of code as a bitmap.
Just out of curiosity are bitmap values passed by reference? (As in does the object parameter use the same line of memory for the same object?).
Anyhow's here is z code:
public Bitmap ResizeBitmap(Bitmap bitmap, float s_percentage, int frames, int viewport_width, int viewport_height)
{
float percentage = s_percentage / 100.0f;
float scale = viewport_width / 100 * percentage;
if(viewport_width < viewport_height)
{
scale = viewport_height / 100 * percentage;
}
int newWidth = (int) (bitmap.getWidth() * scale);
int newHeight = (int) (bitmap.getHeight() * scale);
if(newWidth <= 0 || newHeight <= 0)
{
// Extra check, for invalid width/height
Log.e("Function List, Resize Bitmap", "invalid dimension ("+newWidth+"x"+newHeight+")");
return bitmap;
}
//Round up to closet factor of total frames
int rW = (newWidth/frames)+1;
newWidth = rW*frames;
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
return newBitmap;
}
To be in VM budget try to scale down your Bitmap like this.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile( filename, options );
options.inJustDecodeBounds = false;
options.inSampleSize = 4;
bitmap = BitmapFactory.decodeFile( filename, options );
if ( bitmap != null ) {
bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
}
//Adjust SampleSize to values like 2, 4, 8 etc