Если вы не возражаете против использования сторонних библиотек, AOL cyclops-react lib, раскрытие :: Я являюсь автором, имеет класс ExceptionSoftener , который может помочь здесь .
s.filter(softenPredicate(a->a.isActive()));
Большинство телефонных камер - это пейзаж, а это означает, что если вы сделаете фотографию на портрете, полученные снимки будут повернуты на 90 градусов. В этом случае программное обеспечение камеры должно заполнить данные Exif с ориентацией, в которой должна просматриваться фотография.
Обратите внимание, что решение ниже зависит от производителя программного обеспечения камеры / устройства заполняя данные Exif, поэтому он будет работать в большинстве случаев, но он не является 100% -ным надежным решением.
ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
Bitmap rotatedBitmap = null;
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotatedBitmap = rotateImage(bitmap, 90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotatedBitmap = rotateImage(bitmap, 180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotatedBitmap = rotateImage(bitmap, 270);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
rotatedBitmap = bitmap;
}
Вот метод rotateImage
:
public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
matrix, true);
}
Я потратил много времени на поиск решения для этого. И, наконец, удалось это сделать. Не забывайте повышать голос @ Джейсона Робинсона, потому что мой основанный на его.
Итак, сначала вы знаете, что с Android 7.0 мы должны использовать FileProvider
и что-то, называемое ContentUri
, иначе вы получите раздражающую ошибку, пытающуюся вызвать ваш Intent
. Это пример кода:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriFromPath(context, "[Your path to save image]"));
startActivityForResult(intent, CAPTURE_IMAGE_RESULT);
Метод getUriFromPath(Context, String)
в пользовательской версии Android создает FileUri (file://...)
или ContentUri (content://...)
, и там это:
public Uri getUriFromPath(Context context, String destination) {
File file = new File(destination);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
} else {
return Uri.fromFile(file);
}
}
После onActivityResult
вы можете поймать это uri
, где изображение сохраняется камерой, но теперь вам нужно определить поворот камеры, здесь мы будем использовать moddified @Jason Robinson answer:
Сначала нам нужно создать ExifInterface
на основе Uri
@Nullable
public ExifInterface getExifInterface(Context context, Uri uri) {
try {
String path = uri.toString();
if (path.startsWith("file://")) {
return new ExifInterface(path);
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (path.startsWith("content://")) {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
return new ExifInterface(inputStream);
}
}
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
Выше код можно упростить, но я хочу показать все. Поэтому из FileUri
мы можем создать ExifInterface
на основе String path
, но из ContentUri
мы не можем, Android не поддерживает это.
В этом случае мы должны использовать другой конструктор на основе InputStream
. Помните, что этот конструктор недоступен по умолчанию, вам нужно добавить дополнительную библиотеку:
compile "com.android.support:exifinterface:XX.X.X"
Теперь мы можем использовать метод getExifInterface
для получения нашего угла:
public float getExifAngle(Context context, Uri uri) {
try {
ExifInterface exifInterface = getExifInterface(context, uri);
if(exifInterface == null) {
return -1f;
}
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90f;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180f;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270f;
case ExifInterface.ORIENTATION_NORMAL:
return 0f;
case ExifInterface.ORIENTATION_UNDEFINED:
return -1f;
default:
return -1f;
}
}
catch (Exception e) {
e.printStackTrace();
return -1f;
}
}
Теперь у вас есть Угол для правильного поворота изображения:).
Это, возможно, само собой разумеется, но всегда помните, что вы можете обрабатывать некоторые из этих проблем с обработкой изображений на своем сервере. Я использовал ответы, подобные тем, которые содержатся в этом потоке, чтобы обрабатывать немедленное отображение изображения. Однако мое приложение требует, чтобы изображения хранились на сервере (это, вероятно, является общим требованием, если вы хотите, чтобы изображение сохранялось, когда пользователи переключают телефоны).
Решения, содержащиеся во многих потоках, относящихся к этой теме, Обсудите отсутствие настойчивости данных EXIF, которые не выдержат сжатия изображения Bitmap, а это означает, что вам нужно будет поворачивать изображение каждый раз, когда ваш сервер загружает его. В качестве альтернативы вы можете отправить данные ориентации EXIF на свой сервер, а затем, если необходимо, повернуть изображение.
Мне было проще создать постоянное решение на сервере, потому что мне не пришлось беспокоиться о секретных файлах файлов Android.
Обычно рекомендуется решить проблему с помощью ExifInterface , как это делал Джейсон Робинсон. Если этот подход не работает, вы можете попытаться найти Ориентацию последнего снятого изображения ...
private int getImageOrientation(){
final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION };
final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageColumns, null, null, imageOrderBy);
if(cursor.moveToFirst()){
int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
cursor.close();
return orientation;
} else {
return 0;
}
}
В выбранном ответе используется наиболее распространенный метод ответа на этот и подобные вопросы. Однако он не работает с передней и задней камерами Samsung. Для тех, кто ищет решение, которое работает как на передней, так и на задней камерах для Samsung и других крупных производителей, этот ответ nvhausid является удивительным:
https://stackoverflow.com/a/18915443/ 6080472
Для тех, кто не хочет щелкнуть, соответствующее волшебство - использовать CameraInfo, а не полагаться на EXIF.
Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);
Полный код в ссылке.
// Try this way,hope this will help you to solve your problem...
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center">
<ImageView
android:id="@+id/imgFromCameraOrGallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/ic_launcher"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btnCamera"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Camera"/>
<Button
android:id="@+id/btnGallery"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:layout_height="wrap_content"
android:text="Gallery"/>
</LinearLayout>
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity {
private ImageView imgFromCameraOrGallery;
private Button btnCamera;
private Button btnGallery;
private String imgPath;
final private int PICK_IMAGE = 1;
final private int CAPTURE_IMAGE = 2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgFromCameraOrGallery = (ImageView) findViewById(R.id.imgFromCameraOrGallery);
btnCamera = (Button) findViewById(R.id.btnCamera);
btnGallery = (Button) findViewById(R.id.btnGallery);
btnCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, setImageUri());
startActivityForResult(intent, CAPTURE_IMAGE);
}
});
btnGallery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, ""), PICK_IMAGE);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == CAPTURE_IMAGE) {
setCapturedImage(getImagePath());
} else if (requestCode == PICK_IMAGE) {
imgFromCameraOrGallery.setImageBitmap(BitmapFactory.decodeFile(getAbsolutePath(data.getData())));
}
}
}
private String getRightAngleImage(String photoPath) {
try {
ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int degree = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
degree = 0;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
case ExifInterface.ORIENTATION_UNDEFINED:
degree = 0;
break;
default:
degree = 90;
}
return rotateImage(degree,photoPath);
} catch (Exception e) {
e.printStackTrace();
}
return photoPath;
}
private String rotateImage(int degree, String imagePath){
if(degree<=0){
return imagePath;
}
try{
Bitmap b= BitmapFactory.decodeFile(imagePath);
Matrix matrix = new Matrix();
if(b.getWidth()>b.getHeight()){
matrix.setRotate(degree);
b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
matrix, true);
}
FileOutputStream fOut = new FileOutputStream(imagePath);
String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
String imageType = imageName.substring(imageName.lastIndexOf(".") + 1);
FileOutputStream out = new FileOutputStream(imagePath);
if (imageType.equalsIgnoreCase("png")) {
b.compress(Bitmap.CompressFormat.PNG, 100, out);
}else if (imageType.equalsIgnoreCase("jpeg")|| imageType.equalsIgnoreCase("jpg")) {
b.compress(Bitmap.CompressFormat.JPEG, 100, out);
}
fOut.flush();
fOut.close();
b.recycle();
}catch (Exception e){
e.printStackTrace();
}
return imagePath;
}
private void setCapturedImage(final String imagePath){
new AsyncTask<Void,Void,String>(){
@Override
protected String doInBackground(Void... params) {
try {
return getRightAngleImage(imagePath);
}catch (Throwable e){
e.printStackTrace();
}
return imagePath;
}
@Override
protected void onPostExecute(String imagePath) {
super.onPostExecute(imagePath);
imgFromCameraOrGallery.setImageBitmap(decodeFile(imagePath));
}
}.execute();
}
public Bitmap decodeFile(String path) {
try {
// Decode deal_image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 1024;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(path, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public String getAbsolutePath(Uri uri) {
if(Build.VERSION.SDK_INT >= 19){
String id = "";
if(uri.getLastPathSegment().split(":").length > 1)
id = uri.getLastPathSegment().split(":")[1];
else if(uri.getLastPathSegment().split(":").length > 0)
id = uri.getLastPathSegment().split(":")[0];
if(id.length() > 0){
final String[] imageColumns = {MediaStore.Images.Media.DATA };
final String imageOrderBy = null;
Uri tempUri = getUri();
Cursor imageCursor = getContentResolver().query(tempUri, imageColumns, MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
if (imageCursor.moveToFirst()) {
return imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
}else{
return null;
}
}else{
return null;
}
}else{
String[] projection = { MediaStore.MediaColumns.DATA };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} else
return null;
}
}
private Uri getUri() {
String state = Environment.getExternalStorageState();
if(!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED))
return MediaStore.Images.Media.INTERNAL_CONTENT_URI;
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
public Uri setImageUri() {
Uri imgUri;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File file = new File(Environment.getExternalStorageDirectory() + "/DCIM/",getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis() + ".png");
imgUri = Uri.fromFile(file);
imgPath = file.getAbsolutePath();
}else {
File file = new File(getFilesDir() ,getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis()+ ".png");
imgUri = Uri.fromFile(file);
this.imgPath = file.getAbsolutePath();
}
return imgUri;
}
public String getImagePath() {
return imgPath;
}
}
Существует более простая команда для исправления этой ошибки.
Просто добавьте после yourImageView.setBitmap (bitmap); this yourImageView.setRotation (90);
Эта неподвижная шахта. Надеюсь, это поможет!
Самое простое решение для этой проблемы:
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));
Я сохраняю изображение в формате jpg.
Объединив Jason Robinson ответ с Felix 's ответ и заполнив недостающие части, здесь это окончательное решение для этой проблемы, которое будет выполнено после тестирования на Android Android & nbsp; 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) и Android 5.0 ( Lollipop ).
Шаги
Вот часть кода:
Вызовите следующий метод с текущим Context
и изображением URI
, которое вы хотите исправить
/**
* 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(context, img, selectedImage);
return img;
}
Вот метод CalculateInSampleSize
из предварительно упомянутого источника :
/**
* 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(Context context, Bitmap img, Uri selectedImage) throws IOException {
InputStream input = context.getContentResolver().openInputStream(selectedImage);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
ei = new ExifInterface(input);
else
ei = new ExifInterface(selectedImage.getPath());
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;
}
}
Наконец, метка вращения (/ g23)
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;
}
-Не забудьте проголосовать за ответы этих парней за свои усилия и Shirish Herwade , которые задали этот полезный вопрос.
Попробуйте следующее, если у вас возникла проблема с S4.
android:configChanges="locale|touchscreen|orientation|screenLayout|screenSize|keyboardHidden|uiMode"
rotateImageIfRequired(img, selectedImage);
вы передаете два параметра, тогда как в определении вашего метода вы определили три private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage)
, что я передаю в качестве контекста из своей активности?
– Romantic Electron
23 April 2015 в 05:42
К сожалению, ответ jason-robinson выше не работал для меня.
Хотя функция вращения работает отлично:
public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix,
true);
}
Мне нужно было сделать следующее, чтобы получить ориентацию, поскольку ориентация Exif всегда была 0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode,resultCode,data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
Uri selectedImage = data.getData();
String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
int orientation = -1;
if (cur != null && cur.moveToFirst()) {
orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
}
InputStream imageStream = getContentResolver().openInputStream(selectedImage);
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
switch(orientation) {
case 90:
bitmap = rotateImage(chosen_image_bitmap, 90);
break;
case 180:
bitmap = rotateImage(chosen_image_bitmap, 180);
break;
case 270:
bitmap = rotateImage(chosen_image_bitmap, 270);
break;
default:
break;
}
imageView.setImageBitmap(bitmap );
Ответ Джейсона Робинсона и Сами Эльтамави ответ превосходны.
Просто улучшение для завершения aproach, вы должны использовать совместимость ExifInterface.
com.android.support:exifinterface:${lastLibVersion}
Вы сможете создать экземпляр ExifInterface (pior API & lt; 24) с помощью InputStream
(из ContentResolver
) вместо путей uri, избегая «Исключенные файлы исключений» "
https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html
Однолинейное решение:
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Или
Picasso.with(context).load("file:" + photoPath).into(imageView);
Это автоматически определит вращение и поместит изображение в правильной ориентации
Picasso - очень мощный библиотека для обработки изображений в вашем приложении включает в себя: сложные преобразования изображений с минимальным использованием памяти.
exif.getAttributeInt
с использованиемExifInterface.ORIENTATION_UNDEFINED
почти такая же, как и второй параметр по умолчанию, если функция не может обеспечить значение. – Darpan 4 September 2015 в 10:32ExitInterface
поддерживает File и InputStream, поэтому вы можете создать InputStream . – Jason Robinson 3 April 2017 в 17:54