¿Por qué una imagen capturada con la intención de la cámara se gira en algunos dispositivos en Android?

377

Estoy capturando una imagen y configurándola como vista de imagen.

public void captureImage() {

    Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
    File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
    imageUri = Uri.fromFile(filePhoto);
    MyApplicationGlobal.imageUri = imageUri.getPath();
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intentCamera, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intentFromCamera) {
    super.onActivityResult(requestCode, resultCode, intentFromCamera);

    if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {

        if (intentFromCamera != null) {
            Bundle extras = intentFromCamera.getExtras();
            if (extras.containsKey("data")) {
                bitmap = (Bitmap) extras.get("data");
            }
            else {
                bitmap = getBitmapFromUri();
            }
        }
        else {
            bitmap = getBitmapFromUri();
        }
        // imageView.setImageBitmap(bitmap);
        imageView.setImageURI(imageUri);
    }
    else {
    }
}

public Bitmap getBitmapFromUri() {

    getContentResolver().notifyChange(imageUri, null);
    ContentResolver cr = getContentResolver();
    Bitmap bitmap;

    try {
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
        return bitmap;
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

Pero el problema es que la imagen en algunos dispositivos cada vez que se gira. Por ejemplo, en un dispositivo Samsung funciona bien, pero en un Sony Xperia la imagen gira 90 grados y en Toshiba Thrive (tableta) 180 grados.

Shirish Herwade
fuente
1
intente esto en su manifiesto de actividad android: configChanges = "orientación" android: screenOrientation = "portrait"
Narendra Pal
@nick no funciona, ahora la imagen se gira a 90 grados en lugar de 180 grados en la pestaña
Shirish Herwade
1
como creo que cuando usas la intención interna para lidiar con la aplicación de la cámara, entonces gira la imagen. Esto depende de cómo sostengas el dispositivo para capturar la imagen. Por lo tanto, puede restringir al usuario para que tome una imagen de manera particular, lo que significa que el usuario siempre capturará la imagen sosteniendo el dispositivo en vertical u horizontal. Después de eso, puede cambiarlo a un ángulo específico para obtener la imagen que desee. O OTRA OPCIÓN, HAGA SU PROPIA APLICACIÓN DE CÁMARA.
Narendra Pal
@nick "puedes restringir al usuario para que tome imágenes de una manera particular" ¿significa que es lo mismo que configurar la orientación = "retrato"? ¿Y cómo lograr "Después de eso puede cambiarlo a un ángulo específico para obtener la imagen que desee"? ¿Puede darnos algunos enlaces útiles
Shirish Herwade
3
Creo que la intención de captura siempre muestra la aplicación de cámara predeterminada que tiene una orientación específica en cada dispositivo y, en consecuencia, una orientación fotográfica fija. No depende de la forma en que el usuario sostenga el dispositivo o la orientación de su actividad que invocó la intención.
Alex Cohn

Respuestas:

441

La mayoría de las cámaras de los teléfonos son horizontales, lo que significa que si toma la foto en vertical, las fotos resultantes se rotarán 90 grados. En este caso, el software de la cámara debe llenar los datos Exif con la orientación en la que se debe ver la foto.

Tenga en cuenta que la solución a continuación depende del fabricante del software / dispositivo de la cámara que complete los datos Exif, por lo que funcionará en la mayoría de los casos, pero no es una solución 100% confiable.

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

Aquí está el rotateImagemétodo:

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);
}
Jason Robinson
fuente
1
De los códigos de @JasonRobinson, aprendo cómo obtener la orientación real y, combinándolos con este código , manejo con éxito la orientación.
Raditya Kurnianto
La segunda opción de exif.getAttributeIntuso ExifInterface.ORIENTATION_UNDEFINEDes casi la misma, ya que el segundo parámetro es el valor predeterminado en caso de que la función no proporcione el valor.
Darpan
55
Este código es para una imagen ya escrita en el disco, ¿verdad? No obtengo resultados con este método para el mapa de bits que se va a escribir en el disco.
Tracio
44
Siempre me devuelve el valor 0. Indique cómo obtener orientación real.
Anurag Srivastava
3
Obteniendo 0 siempre, ¿alguna idea de por qué?
Navya Ramesan
186

Mediante la combinación de Jason Robinson 's respuesta con Félix ' s respuesta y llenar las partes que faltan, aquí está la solución completa definitiva para este problema que va a hacer la siguiente después de probarlo en Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) y Android 5.0 ( Lollipop ).

Pasos

  1. Reduzca la imagen si era más grande que 1024x1024.

  2. Gire la imagen a la orientación correcta solo si gira 90, 180 o 270 grados.

  3. Reciclar la imagen girada para fines de memoria.

Aquí está la parte del código:

Llame al siguiente método con la imagen actual Contexty la URIque desea corregir

/**
 * 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;
}

Aquí está el CalculateInSampleSizemétodo de la fuente mencionada anteriormente :

/**
  * 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;
}

Luego viene el método que verificará la orientación de la imagen actual para decidir el ángulo de rotación

 /**
 * 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;
    }
}

Finalmente el método de rotación en sí

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

-No olvides votar por las respuestas de esos chicos por sus esfuerzos y Shirish Herwade, quien hizo esta útil pregunta.

Sami Eltamawy
fuente
2
Me
despierta
1
el método rotateImageIfRequired () funciona muy bien ... ¡gracias!
mapo
55
No funciona para mi A veces mi teléfono muestra retratos, a veces fotos de paisajes, pero la orientación detectada siempre es de 0 grados.
Makalele
@Makalele ¿Este problema también ocurre al tomar fotos y adjuntarlas a través de WhatsApp?
Manoj Perumarath
No uso WhatsApp, así que no puedo decirlo, pero probablemente sí. Eso es porque incluso sucede en la aplicación de fotos de archivo (Google Stock Camera).
Makalele
45

Es fácil detectar la orientación de la imagen y reemplazar el mapa de bits usando:

 /**
 * Rotate an image if required.
 * @param img
 * @param selectedImage
 * @return
 */
private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage) {

    // Detect rotation
    int rotation = getRotation(context, selectedImage);
    if (rotation != 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
    else{
        return img;
    }
}

/**
 * Get the rotation of the last image added.
 * @param context
 * @param selectedImage
 * @return
 */
private static int getRotation(Context context,Uri selectedImage) {

    int rotation = 0;
    ContentResolver content = context.getContentResolver();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                       new String[] { "orientation", "date_added" },
                                       null, null, "date_added desc");

    if (mediaCursor != null && mediaCursor.getCount() != 0) {
        while(mediaCursor.moveToNext()){
            rotation = mediaCursor.getInt(0);
            break;
        }
    }
    mediaCursor.close();
    return rotation;
}

Para evitar quedarse sin recuerdos con imágenes grandes, te recomiendo cambiar el tamaño de la imagen usando:

private static final int MAX_HEIGHT = 1024;
private static final int MAX_WIDTH = 1024;
public static Bitmap decodeSampledBitmap(Context context, Uri selectedImage)
    throws IOException {

    // 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;
}

No es posible usar ExifInterface para obtener la orientación debido a un problema con el sistema operativo Android: https://code.google.com/p/android/issues/detail?id=19268

Y aqui esta 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
 */
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 = 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;
}
Felix
fuente
1
¿cuál es el método calculateInSampleSize aquí
Madhu Kotagiri
1
@madhukotagiri aquí tiene un ejemplo de implementación para CalculateInSampleSize
Felix
Gracias hombre, definitivamente eres el indicado. Me pregunto cuánto será útil el cambio de tamaño, si la operación se realiza solo ocasionalmente.
Marino
44
El parámetro Uri selectImage no se usa en el método getRotation (...). ¿Cómo necesitamos usarlo? Gracias.
valerybodak
1
El parámetro 'selectedImage' no parece usarse en ningún lado. ¿Alguna razón para estar allí?
Alex
20

Solución de una línea:

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

O

Picasso.with(context).load("file:" + photoPath).into(imageView);

Esto detectará automáticamente la rotación y colocará la imagen en la orientación correcta

Picasso es una biblioteca muy poderosa para manejar imágenes en su aplicación que incluye: Transformaciones complejas de imágenes con un uso mínimo de memoria.

voytez
fuente
1
Solución interesante
Bhavik Mehta
8
Simplemente carga la imagen en una vista, no le proporciona un mapa de bits o un archivo que pueda manipular o cargar en un servidor.
flawyte
44
Su imagen de visualización hizo clic como está. No está girando como se requiere.
seema
1
@Flawyte puede hacerlo cargando el archivo en el destino en lugar de verlo con la devolución de llamada que devuelve un mapa de bits recortado / redimensionado: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Into (target); where target = new Target () {Override public void onBitmapLoaded (Bitmap bitmap, Picasso.LoadedFrom from) {
voytez
el problema que sigo enfrentando es que toma unos segundos mostrar la imagen
Anu
12

He pasado mucho tiempo buscando una solución para esto. Y finalmente logró hacer esto. No olvides votar a @Jason Robinson porque mi se basa en la suya.

Entonces, lo primero que debe saber es que desde Android 7.0 tenemos que usar FileProvidery algo llamado ContentUri, de lo contrario obtendrá un error molesto al intentar invocar su Intent. Este es un código de muestra:

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

Método getUriFromPath(Context, String)basado en la versión de usuario de Android crear FileUri (file://...)o ContentUri (content://...)y ahí está:

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

Después de onActivityResultque pueda captar eso uridonde la imagen se guarda con la cámara, pero ahora tiene que detectar la rotación de la cámara, aquí usaremos la respuesta modificada de @Jason Robinson:

Primero necesitamos crear ExifInterfacebasado enUri

@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;
}

El código anterior se puede simplificar, pero quiero mostrarlo todo. Entonces, desde FileUrique podemos crear en ExifInterfacebase a String path, pero desde ContentUrique no podemos, Android no es compatible con eso.

En ese caso tenemos que usar otro constructor basado en InputStream. Recuerde que este constructor no está disponible por defecto, debe agregar una biblioteca adicional:

compile "com.android.support:exifinterface:XX.X.X"

Ahora podemos usar el getExifInterfacemétodo para obtener nuestro ángulo:

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

Ahora tiene Ángulo para rotar adecuadamente su imagen :).

Artur Szymański
fuente
2
implementación 'androidx.exifinterface: exifinterface: XXX' Esto es para aquellos que usan androidx. gracias por tu publicación
Doongsil
11
// 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;
    }
}
Haresh Chhelana
fuente
Solución perfecta Haresh Bhai
Sagar Pithiya
9

Puede leer la orientación del sensor de la cámara como lo indica Google en la documentación: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html

SENSOR_ORIENTATION

Added in API level 21
Key<Integer> SENSOR_ORIENTATION
Clockwise angle through which the output image needs to be rotated to be upright on the device screen in its native orientation.

Also defines the direction of rolling shutter readout, which is from top to bottom in the sensor's coordinate system.

Units: Degrees of clockwise rotation; always a multiple of 90

Range of valid values:
0, 90, 180, 270

This key is available on all devices.

Código de muestra:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
int orientation = 0;
try {
    String cameraId = manager.getCameraIdList()[0];
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
    orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
}
catch (Exception e)
{
}
Stephen Shi
fuente
4

Normalmente se recomienda resolver el problema con ExifInterface , como sugirió @Jason Robinson. Si este enfoque no funciona, puede intentar buscar la Orientación de la última imagen tomada ...

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;
    }
}
Chris Conway
fuente
1
Creo que este código solo detecta en qué grado se produjo la rotación. Ahora puedo hacer eso, pero no puedo en la siguiente tarea, es decir, rotar la imagen.
Shirish Herwade
Tienes razón, pero no pediste la rotación en este hilo, así que mantengámoslo limpio;) Es por eso que puse mi respuesta a tu problema de rotación en tu otro hilo ... Espero que eso ayude, funciona yo: stackoverflow.com/questions/14123809/…
Chris Conway
4

Lamentablemente, la respuesta de @ jason-robinson anterior no funcionó para mí.

Aunque la función de rotación funciona perfectamente:

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

Tuve que hacer lo siguiente para obtener la orientación ya que la orientación Exif siempre fue 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 );
rharvey
fuente
1
alwasys 0, samsung 7
djdance
2

Mejor intente tomar la foto en una orientación específica.

android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden"

Para obtener mejores resultados, brinde orientación horizontal en la actividad de vista de cámara.

Siva
fuente
lo siento, no funciona De hecho, en la pestaña, cada vez que finaliza la ejecución de onActivityResult, extrañamente se llama a onCreate.
Shirish Herwade
1
lo siento, el problema es como es
Shirish Herwade
1

La respuesta seleccionada utiliza el método más común respondido a esta y preguntas similares. Sin embargo, no funciona con las cámaras frontal y posterior de Samsung. Para aquellos que buscan una solución que funcione tanto en cámaras frontales como traseras para Samsung y otros fabricantes importantes, esta respuesta de nvhausid es increíble:

https://stackoverflow.com/a/18915443/6080472

Para aquellos que no quieren hacer clic, la magia relevante es usar CameraInfo en lugar de confiar en 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);

Código completo en el enlace.

D. Scott
fuente
no, rotación incorrecta en diferentes ángulos (smasung s7). Me refiero a la galería, por supuesto
djdance
1

Tal vez esto sea evidente, pero siempre recuerde que puede manejar algunos de estos problemas de manejo de imágenes en su servidor. Utilicé respuestas como las contenidas en este hilo para manejar la visualización inmediata de la imagen. Sin embargo, mi aplicación requiere que las imágenes se almacenen en el servidor (este es probablemente un requisito común si desea que la imagen persista mientras los usuarios cambian de teléfono).

Las soluciones contenidas en muchos de los hilos relacionados con este tema no discuten la falta de persistencia de los datos EXIF ​​que no sobreviven a la compresión de imagen del mapa de bits, lo que significa que deberá rotar la imagen cada vez que su servidor la cargue. Alternativamente, puede enviar los datos de orientación EXIF ​​a su servidor y luego rotar la imagen allí si es necesario.

Fue más fácil para mí crear una solución permanente en un servidor porque no tenía que preocuparme por las rutas clandestinas de archivos de Android.

Braden Holt
fuente
¿Puede rotarlo una vez en el momento de captura de la imagen y guardarlo de esa manera para que nunca más tenga que rotarlo?
jk7
Sí puedes y ese es el proceso que terminé implementando al final. Estaba teniendo problemas para obtener la ruta del archivo de la imagen en el teléfono Android que me permitiría hacer eso. Esta es la respuesta que ayudó: stackoverflow.com/a/36714242/5443056
Braden Holt
1

La solución más simple para este problema:

captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
                   characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));

Estoy guardando la imagen en formato jpg.

DNB
fuente
0

Aquí está la Xamarin.Androidversión:

De la respuesta de @Jason Robinson :

Bitmap rotate(Bitmap bitmap, int angle)
{
    var matrix = new Matrix();
    matrix.PostRotate(angle);

    return Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
}

Bitmap rotateIfRequired(Bitmap bitmap, string imagePath)
{
    var ei = new ExifInterface(imagePath);
    var orientation = ei.GetAttributeInt(ExifInterface.TagOrientation, (int)Android.Media.Orientation.Undefined);

    switch (orientation)
    {
        case (int)Android.Media.Orientation.Rotate90: return rotate(bitmap, 90);
        case (int)Android.Media.Orientation.Rotate180: return rotate(bitmap, 180);
        case (int)Android.Media.Orientation.Rotate270: return rotate(bitmap, 270);
        default: return bitmap;
    }
}

Entonces calculateInSampleSizemétodo:

int calculateInSampleSize(BitmapFactory.Options options, int reqW, int reqH)
{
    float h = options.OutHeight;
    float w = options.OutWidth;
    var inSampleSize = 1;

    if (h > reqH || w > reqW)
    {
        if (reqH == 0) inSampleSize = (int)Math.Floor(w / reqW);
        else if (reqW == 0) inSampleSize = (int)Math.Floor(h / reqH);
        else
        {
            var hRatio = (int)Math.Floor(h / reqH);
            var wRatio = (int)Math.Floor(w / reqW);
            inSampleSize = false ? Math.Max(hRatio, wRatio) : Math.Min(hRatio, wRatio);
        }
    }

    return inSampleSize;
}

De la respuesta de @Sami Eltamawy :

Bitmap handleSamplingAndRotationBitmap(string imagePath)
{
    var maxHeight = 1024;
    var maxWidth = 1024;

    var options = new BitmapFactory.Options();
    options.InJustDecodeBounds = true;
    BitmapFactory.DecodeFile(imagePath, options);

    options.InSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);

    options.InJustDecodeBounds = false;

    var bitmap = BitmapFactory.DecodeFile(imagePath, options);

    bitmap = rotateIfRequired(bitmap, imagePath);

    return bitmap;
}
Mehdi Dehghani
fuente
0

Si está usando Fresco, puede usar esto:

final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setRotationOptions(RotationOptions.autoRotate())
.build();

mSimpleDraweeView.setController(
Fresco.newDraweeControllerBuilder()
    .setImageRequest(imageRequest)
    .build());

Esto gira automáticamente las imágenes según los datos Exif.

Fuente: https://frescolib.org/docs/rotation.html

Ritesh Chandnani
fuente
0

El siguiente código funcionó conmigo, obtuvo el mapa de bits del archivoUri, y corrige la rotación si es necesario:

    private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
        val bitmap = when {
            Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
                this.contentResolver,
                selectedPhotoUri
            )
            else -> {
                val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
                ImageDecoder.decodeBitmap(source)
            }
        }

        // If the image is rotated, fix it
        return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
            ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
            ExifInterface.ORIENTATION_ROTATE_90 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(90F) }, true)
            ExifInterface.ORIENTATION_ROTATE_180 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(180F) }, true)
            ExifInterface.ORIENTATION_ROTATE_270 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(270F) }, true)
            else -> bitmap
        } 
    }
Hasan A Yousef
fuente
0

Tengo una respuesta para este problema sin usar ExifInterface . Podemos obtener la rotación de la cámara, ya sea la cámara frontal o la cámara trasera, lo que sea que esté utilizando y, al crear el mapa de bits, podemos rotar el mapa de bits con Matrix.postRotate (grado)

public int getRotationDegree() {
    int degree = 0;

    for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(i, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            degree = info.orientation;

            return degree;
        }
    }

    return degree;
}

Después de calcular la rotación, puede girar su mapa de bits como a continuación:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

 Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);

Herare bm debería ser tu mapa de bits.

Si desea conocer la rotación de su cámara frontal, simplemente cambie Camera.CameraInfo.CAMERA_FACING_BACK a Camera.CameraInfo.CAMERA_FACING_FRONT arriba.

Espero que esto ayude.

Om Prakash Agrahari
fuente
1
Respuesta horrible pero accidentalmente voté. Este código asume que cada imagen de su galería está hecha con su cámara. Este no es el caso
Zun
-1

Creé una función de extensión de Kotlin que simplifica la operación para los desarrolladores de Kotlin basada en la respuesta de @Jason Robinson. Espero que ayude.

fun Bitmap.fixRotation(uri: Uri): Bitmap? {

    val ei = ExifInterface(uri.path)

    val orientation: Int = ei.getAttributeInt(
        ExifInterface.TAG_ORIENTATION,
        ExifInterface.ORIENTATION_UNDEFINED
    )

    return when (orientation) {
        ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage( 90f)
        ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage( 180f)
        ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage( 270f)
        ExifInterface.ORIENTATION_NORMAL -> this
        else -> this
    }
}

fun Bitmap.rotateImage(angle: Float): Bitmap? {
    val matrix = Matrix()
    matrix.postRotate(angle)
    return Bitmap.createBitmap(
        this, 0, 0, width, height,
        matrix, true
    )
}
seyfullah.bilgin
fuente
1
impresionante, pero sufre el mismo problema que todas las soluciones, como extensión o función: no funciona en Android 10.
Lior Iluz
-2

Hay un comando más simple para corregir este error.

Simplemente agregue después de yourImageView.setBitmap (mapa de bits); this yourImageView.setRotation (90);

Esto solucionó el mío. Espero eso ayude !

JG
fuente
66
Como indicó el OP, algunos dispositivos no giran la imagen, algunos la giran 90 grados, otros 180, etc. Por lo tanto, rotarlo 90 sería incorrecto en algunos casos.
jk7
-8

esto funcionó para mí

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);
zaheer
fuente
jajaja qué demonios es esto. ¿Cómo demonios sabrías que la foto tomada por la cámara es -90 / 90/0 / ... El usuario puede tomar una foto como paisaje y no importa lo que vayas a rotar? ... lmao
Alex
En este caso, funcionó para mí, ya que en mi caso el usuario siempre tomará la foto con el teléfono en vertical.
Christian Eduardo Galdamez