I am using Camera intent to capture a photo on android, when intent from onActivityResult returns bitmap it has wrong orientation on some phones.
I know there are ways to fix this,but all the solutions I have seen talk about image stored in file.
What I am retrieving from intent is directly bitmap image. I want to know how I can get exif data of a bitmap and then correct its orientation. I repeat I have seen answers which deal with file and not bitmap, so please consider this before down voting.
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, Constants.CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
And result is as follows
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
How to get orientation and rotate it.
UPDATE
Exif is a file format that inserts some information data to JPEG.
https://www.media.mit.edu/pia/Research/deepview/exif.html
And Bitmap is data structior data holds row pixel data, no exif info.
So I think it is impossible to get exif info from Bitmap.
There is no method to get exif info.
https://developer.android.com/reference/android/graphics/Bitmap.html
ORIGINAL
I agree with #DzMobNadjib .
I think the info of rotation is only in exif.
To take exif, I recommend you to take following steps.
1. Start camera activity with file path.
See [Save the Full-size Photo] capture of this document.
You can start the camera activity with file path.The camera activity will save the image to the file path that you passed.
2. In 'onActivityResult', Follow this answer (as #DzMobNadjib suggested)
Your code will be like this:
(Sorry I'm not tested. Please read carefuly and follow the above answer)
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == Constants.CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
Uri uri = data.getData();
Bitmap bitmap = getAdjustedBitmap(uri);
}
}
}
private Bitmap getAdjustedBitmap(Uri uri) {
FileInputStream is = null;
try {
ExifInterface exif = new ExifInterface(uri.getPath());
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
Matrix matrix = new Matrix();
if (rotation != 0f) {
matrix.preRotate(rotationInDegrees);
}
is = new FileInputStream(new File(uri.getPath()));
Bitmap sourceBitmap = BitmapFactory.decodeStream(is);
int width = sourceBitmap.getWidth();
int height = sourceBitmap.getHeight();
return Bitmap.createBitmap(sourceBitmap, 0, 0, width, height, matrix, true);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
return null;
}
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) { return 180; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) { return 270; }
return 0;
}
Related
I am trying to develop app which save image to gallery.
I want to call the camera through intent, capture images, and save it locally in the gallery
but the issue is image quality is very poor. can anyone help me figure out why?
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
someActivityResultLauncher.launch(cameraIntent);}
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == RESULT_OK) {
bitmap = (Bitmap) Objects.requireNonNull(result.getData()).getExtras().get("data");
}
imageView.setImageBitmap(bitmap);
saveimage(bitmap);
}
private void saveimage(Bitmap bitmap){
Uri images;
ContentResolver contentResolver = getContentResolver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
images = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
}else {
images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis() +".jpg");
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "images/*");
Uri uri = contentResolver.insert(images, contentValues);
try {
OutputStream outputStream = contentResolver.openOutputStream(Objects.requireNonNull(uri));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
Objects.requireNonNull(outputStream);
//
}catch (Exception e){
//
e.printStackTrace();
}
}
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
if you get image from data directly, it's in bad quality
Your code is the legacy equivalent of using ActivityResultContracts.TakePicturePreview. It is specifically set up to return a low-resolution bitmap, suitable for a preview.
Instead, use ActivityResultContracts.TakePicture, and supply your destination Uri as part of the contract.
See this and this for more.
I am trying to implement the best approach to reduce a file, basicly my app is about plants, i need high quality images, so the first thing i did was use a external library to crop images in thumbnail(so i can mantain aspect ratio) i use 1:1 thumnbnail format.
Every time i take a picture and if everything is ok on activityResult, it calls the builded crop library so i can cut and get the uri, like this:
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
dateTimer = getTime();
Log.d("codig",String.valueOf(requestCode));
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Log.d("resultOK","resultOK");
CropImage.activity(data.getData())
.setAspectRatio(1,1)
.setInitialCropWindowPaddingRatio(0)
.setActivityTitle("Corte a foto")
.setActivityMenuIconColor(R.color.nephritis)
.setRequestedSize(300, 300, CropImageView.RequestSizeOptions.RESIZE_INSIDE)
.start(this);
}
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
CropImage.ActivityResult result = CropImage.getActivityResult(data);
if (resultCode == RESULT_OK) {
Uri uri = result.getUri();
Bitmap pic = null;
try {
pic = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
path = saveToInternalStorage(pic);
Log.d("caminho",path);
showDetailsDialog(data);
} else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
Exception error = result.getError();
Log.d("pict12",error.toString());
}
}
}
as you guys can see i have a setRequestSize method there, where i reduce the size of the image (this works, but just sometimes, the text i send as base64 is still to big.
i convert the uri to bitmap and save it on the device file, on next acitivty i get the file
private String saveToInternalStorage(Bitmap bitmapImage){
ContextWrapper cw = new ContextWrapper(getApplicationContext());
// path to /data/data/yourapp/app_data/imageDir
File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
// Create imageDir
File mypath=new File(directory,"captured.jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
// Use the compress method on the BitMap object to write image to the OutputStream
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return directory.getAbsolutePath();
}
so on next activity i get it like this:
private void loadImageFromStorage(String path)
{
try {
File f=new File(path, "captured.jpg");
Log.d("filehe",f.toString());
b = BitmapFactory.decodeStream(new FileInputStream(f));
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
i get the b(bitmap) and covert him to byte[] after to base64(what my server asks for)
byte[] capture = encodeImage(b);
String encodedImage = Base64.encodeToString(capture,1);
Log.d("encodedImage",encodedImage);
params.put("base64", encodedImage);
the problem here is that i need to preserve the quality of the image and i can't get the quality without increasing the heavyness on my server, can't i compress it somehow? i do it on the file but doesn't work
In my app i need to pick image from gallery to my ImageView. If I pick simple image, for example, downloaded from the Internet, it works fine. But if i pick photo made on my phone i get W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (4160x3120, max=4096x4096)
Using Glide to load the photo gives this
Caused by: java.lang.IllegalArgumentException: Unknown type class android.graphics.Bitmap. You must provide a Model of a type for which there is a registered ModelLoader, if you are using a custom model, you must first call Glide#register with a ModelLoaderFactory for your custom model class
This is my code which picks up an image and creates a bitmap out of it.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// When an Image is picked
if (requestCode == SELECT_PHOTO && resultCode == RESULT_OK
&& null != data) {
Uri imageUri = data.getData();
InputStream inputStream;
try {
inputStream = getContentResolver().openInputStream(imageUri);
Bitmap image = BitmapFactory.decodeStream(inputStream);
commentImage = (ImageView) findViewById(R.id.comment_image);
//Glide.with(this).load(image).asBitmap().override(4095, 4095).into(commentImage);
commentImage.setImageBitmap(image);
//commentImage.setImageBitmap(Bitmap.createScaledBitmap(image, 4095, 2048, false));
Toast.makeText(this, "SUCCESS", Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
// show a message to the user indictating that the image is unavailable.
Toast.makeText(this, "Unable to open image", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(this, "You haven't picked Image",
Toast.LENGTH_LONG).show();
}
}
How can I scale an Image in case if it is too big?
You can use the following to compress the bitmap, here bitmapImage is your bitmap object. :
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream); //90 refers to 90% quality, or 10% compression
byte[] data = byteArrayOutputStream.toByteArray();
Bitmap compressedBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
I have been trying for the past two or three days to build a program that allows the user to take a picture using the native camera or pick one from the gallery. After, getting a picture from either method, the picture is returned to the core libgdx project for processing. Picking from the gallery works like a charm. And the take a picture method, if returned as thumbnail it works perfectly.
However, I would like use a resized photo instead of a thumbnail. The application keeps crashing with "AndroidGraphics: deadlock kill" error. Code speaks louder than words.
public void takePicture() {
selectedImagePath = null;
selectedByteArray = null;
/* when thumbnail
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(intent.resolveActivity(getPackageManager())!=null) {
startActivityForResult(intent, SELECT_TAKEPICTURE_CODE);
}
*/
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = timeStamp + ".jpg";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
pictureImagePath = storageDir.getAbsolutePath() + "/" + imageFileName;
File file = new File(pictureImagePath);
Uri outputFileUri = Uri.fromFile(file);
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(cameraIntent, SELECT_TAKEPICTURE_CODE);
}
Handling the activity.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode==RESULT_CANCELED||data==null) {
didUserCancel = true;
}
/* Also for the thumbnail use
if (requestCode == SELECT_TAKEPICTURE_CODE&&resultCode==RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
selectedByteArray = convertBitmapToByteArray(imageBitmap);
}
*/
if (requestCode == SELECT_TAKEPICTURE_CODE&&resultCode==RESULT_OK) {
File imgFile = new File(pictureImagePath);
if (imgFile.exists()) {
pictureCaptured = true;
}
}
}
Three helper methods
private Bitmap scaleDownBitmap(Bitmap realImage, float maxImageSize, boolean filter) {
float ratio = Math.min( maxImageSize / (float)realImage.getWidth(),
maxImageSize / (float)realImage.getHeight());
int width = Math.round( ratio * (float)realImage.getWidth());
int height = Math.round( ratio * (float)realImage.getHeight());
Bitmap newBitmap = Bitmap.createScaledBitmap(realImage, width, height, filter);
return newBitmap;
}
private byte[] convertBitmapToByteArray(Bitmap bmp, int quality) {
if(quality>100)
quality = 100;
if(quality<0)
quality = 0;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, quality, stream);
byte[] byteArray = stream.toByteArray();
return byteArray;
}
#Override
public byte[] getCapturedPictureAsByteArray(float imageSize, boolean smoothFilter, int quality) {
Bitmap bitmap = getBitmapFromPath(pictureImagePath);
Bitmap scaledDownBitmap = scaleDownBitmap(bitmap, imageSize, smoothFilter);
selectedByteArray = convertBitmapToByteArray(scaledDownBitmap, quality);
return selectedByteArray;
}
Now for a rough version of the code on the core Game side:
if(gameMode==GameMode.setTextureFromCapturedPicture) {
float imageSize = 100; boolean smoothFilter = true; int quality = 10;
texture = byteArrayToTexture(rootApp.galleryOpener.getCapturedPictureAsByteArray(imageSize, smoothFilter, quality));
gameMode = GameMode.play;
}
For converting from byte[] to texture.
private Texture byteArrayToTexture(byte[] bytes) {
try {
Pixmap pmap =new Pixmap(bytes, 0, bytes.length);
Texture tex =new Texture(pmap);
return tex;
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
The error is I take a picture it takes me back to the libgdx's previous screen (in my case the menu screen). When I search the phone's Gallery I find that picture and can view it without issues. Furthermore, when I run my Game again, and chose that lastly picked picture, it works :s
The saddest part is that the error cannot be generated repeatedly. It just crashes. However, the most frequent errors I am getting are
com.*.*.game/Zygote: v2
com.*.*.game E/Zygote: accessInfo : 0
OR
com.*.*.game waiting for pause synchronization took too long; assuming deadlock and killing
The way I do it is this; Tell the intent where you want to store the image from the camera, otherwise nothing is stored:
File file = new File(Environment.getExternalStorageDirectory() + IMAGE_FOLDER + File.separator + "temp_image.jpg");
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
takePictureIntent.putExtra(PhotoStatics.OUTPUT_FORMAT, Bitmap.CompressFormat.JPEG.toString());
startActivityForResult(takePictureIntent, PhotoStatics.REQUEST_IMAGE_CAPTURE);
And then we listen for when the photo is done, and grab it from the place we told the camera to store the image...
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
//System.out.println("onActivityResult = " + requestCode + " " + resultCode + " " + intent.getAction());
// Photo incoming
if (requestCode == PhotoStatics.REQUEST_IMAGE_CAPTURE) {
if (resultCode == RESULT_OK) { // check if we have something to work with
File file = new File(Environment.getExternalStorageDirectory() + IMAGE_FOLDER + File.separator + "temp_image.jpg");
Uri image_uri = Uri.fromFile(file); // get where the temp is stored
Bitmap imageBitmap = BitmapFactory.decodeFile(image_uri.getPath());
}
}
}
Does this make sense to you?
// Static Globals
public static final String TEMP_IMAGE = "temp_image";
public static final String OUTPUT_FORMAT = "outputFormat";
public static final String IMAGE_FOLDER = "/these_pictures"; // don't forget the / at the beginning.
public static final int REQUEST_IMAGE_CAPTURE = 12; // just internal use.
public static final String IMAGE_TYPE = ".jpg";
I am new to Android. I have just created an AVD with 256 MB android-SDcard in it in Android 2.1. And I have inserted two images into it. I have done this using the DDMS perspective. And the images are now stored into a folder 100ANDRO in the DCIM folder of SDcard. Now I want to create an application that allows the user to select the images through browsing the folders and need to save the corresponding image to the database android-sqlite.
Can someone help me to find an appropriate method for this? Thanks in advance.
I have found one method for this.
I have created a button for UPLOAD and on the click action I have set like this.
upload.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, 1);
}
});
And I have overrided this method along with the same class as below.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
{
if (resultCode == RESULT_OK)
{
Uri photoUri = data.getData();
if (photoUri != null)
{
try {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(photoUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
imgView.setImageBitmap(bitmap);
int size = bitmap.getWidth() * bitmap.getHeight();
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
try {
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();}
String bb = out.toString();
byte[] x = out.toByteArray();
image_value.setTag(x);
image_value.setText(filePath);
}catch(Exception e)
{}
}
}
}
Here image_value represents a hidden text view in the xml file.
I have passed the value of the image location and bytes as text view's value and tag.
And later on I have saved this bytes into the database for later display. Its working fine.
Thanks to all.