Por que uma imagem capturada usando a intenção da câmera é rotacionada em alguns dispositivos no Android?

376

Estou capturando uma imagem e configurando-a para visualização de imagem.

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

Mas o problema é que a imagem em alguns dispositivos toda vez que é rotacionada. Por exemplo, em um dispositivo Samsung, ele funciona bem, mas em um Sony Xperia a imagem é girada em 90 graus e no Toshiba Thrive (tablet) em 180 graus.

Shirish Herwade
fonte
11
tente isso em sua atividade android manifesto: configChanges = "orientação" android: screenOrientation = "portrait"
Narendra Pal
@ Nick Ele não funciona, agora a imagem fica girado a 90 graus, em vez de 180 graus no guia
Shirish Herwade
11
Como eu acho que quando você usa a intenção interna para lidar com o aplicativo da câmera, ele gira a imagem. Isso depende de como você segura o dispositivo para capturar a imagem. Portanto, você pode restringir o usuário a tirar uma foto de maneira particular, pois o usuário sempre capturará a imagem mantendo o dispositivo em retrato ou paisagem. Depois disso, você pode alterá-lo para um ângulo específico para obter a imagem desejada. OU OUTRAS OPÇÕES, FAÇA SEU APLICATIVO DE CÂMERA.
Narendra Pal
@nick "você pode restringir o usuário a tirar uma imagem de uma maneira particular" significa que é o mesmo que definir a orientação = "potrait"? E como "Depois disso, você pode alterá-lo para um ângulo específico para obter a imagem que deseja" alcançar? Por favor, você pode dar alguns links úteis
Shirish Herwade
3
Acredito que a intenção de captura sempre traga o aplicativo de câmera padrão, que possui orientação específica em cada dispositivo e, conseqüentemente, orientação fotográfica fixa. Não depende da maneira como o usuário mantém o dispositivo ou da orientação da sua atividade que invocou a intenção.
Alex Cohn

Respostas:

440

A maioria das câmeras de telefone é horizontal, ou seja, se você tirar a foto em retrato, as fotos resultantes serão giradas 90 graus. Nesse caso, o software da câmera deve preencher os dados Exif com a orientação em que a foto deve ser exibida.

Observe que a solução abaixo depende do fabricante do software / dispositivo da câmera que preenche os dados Exif; portanto, ele funciona na maioria dos casos, mas não é uma solução 100% confiável.

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

Aqui está o 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
fonte
11
Com o código @JasonRobinson, eu aprendo como obter a orientação real e, combinando com esse código, gerencio a orientação com sucesso.
Raditya Kurnianto
A segunda opção de exif.getAttributeIntuso ExifInterface.ORIENTATION_UNDEFINEDé quase a mesma, pois o segundo parâmetro é o valor padrão, caso a função falhe em fornecer o valor.
Darpan 4/09/15
5
Esse código é para uma imagem já gravada no disco, certo? Não obtive resultados usando esse método para o bitmap prestes a ser gravado no disco.
tracio
4
É sempre me retornar 0 valor. Por favor, diga como obter orientação real.
Anurag Srivastava
3
Obtendo 0 sempre, alguma idéia do porquê?
Navya Ramesan 23/05/19
186

Ao combinar Jason Robinson 's resposta com Felix ' s resposta e preencher as partes em falta, aqui é a solução completa final para esta questão que irá fazer o seguinte após testá-lo no Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) e Android 5.0 ( pirulito ).

Passos

  1. Diminua a imagem se ela for maior que 1024x1024.

  2. Gire a imagem para a orientação correta somente se estiver girando 90, 180 ou 270 graus.

  3. Recicle a imagem girada para fins de memória.

Aqui está a parte do código:

Chame o método a seguir com a atual Contexte a imagem URIque deseja corrigir

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

Aqui está o CalculateInSampleSizemétodo da fonte 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;
}

Depois, vem o método que verifica a orientação atual da imagem para decidir o ângulo de rotação

 /**
 * 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, o próprio método de rotação

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

-Não se esqueça de votar nas respostas desses caras por seus esforços e em Shirish Herwade, que fez essa pergunta útil.

Sami Eltamawy
fonte
2
Sua Woking para mim Perfectly.Thank você
Shohel Rana
11
o método rotateImageIfRequired () funciona muito bem .. obrigado !!
mapo
5
Não funciona para mim. Às vezes, meu telefone dá retrato, às vezes fotos em paisagem, mas a orientação detectada é sempre 0 graus.
Makalele
@ Makalele Esse problema também ocorre ao tirar fotos e anexar através do WhatsApp?
Manoj Perumarath 13/02/19
Eu não uso o WhatsApp, então não posso dizer, mas provavelmente sim. Isso ocorre até no aplicativo de fotos (Google Stock Camera).
Makalele 13/02/19
45

É fácil detectar a orientação da imagem e substituir o bitmap 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 a falta de memórias com imagens grandes, recomendo que você redimensione a imagem 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;
}

Não é possível usar o ExifInterface para obter a orientação devido a um problema no sistema operacional Android: https://code.google.com/p/android/issues/detail?id=19268

E aqui está 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
fonte
11
o que é o método calculateInSampleSize aqui
madhu Kotagiri
11
@madhukotagiri aqui você tem um exemplo de implementantion para calculateInSampleSize: gist.github.com/anonymous/b7ea25fc2bbc54e43616
Felix
Obrigado cara, você é definitivamente o cara! Só estou me perguntando quanto o redimensionamento será útil, se a operação for realizada apenas ocasionalmente.
Marino
4
O parâmetro Uri selectedImage não é usado no método getRotation (...). Como precisamos usá-lo? Obrigado.
Valerybodak
11
O parâmetro 'selectedImage' não parece ser usado em lugar algum. Alguma razão para estar lá?
1128 Alex
20

Solução de uma linha:

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

Ou

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

Isso detecta automaticamente a rotação e coloca a imagem na orientação correta

O Picasso é uma biblioteca muito poderosa para manipular imagens no seu aplicativo: Transformações complexas de imagens com uso mínimo de memória.

voytez
fonte
11
Solução interessante
Bhavik Mehta 16/05
8
Ele apenas carrega a imagem em uma visualização, não fornece um bitmap ou um arquivo que você pode manipular ou enviar para um servidor.
flawyte
4
Sua imagem exibida clicou como está. Não está girando conforme necessário.
seema
11
@Flawyte, você pode fazer isso carregando o arquivo no destino em vez de visualizar com o retorno de chamada que retorna bitmap cortado / redimensionado: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Into (target); where target = new Target () {Substitui public void onBitmapLoaded (bitmap de bitmap, Picasso.LoadedFrom from) {
voytez
O problema que eu ainda estou enfrentando é que leva alguns segundos imagem do monitor para
Anu
12

Passei muito tempo procurando soluções para isso. E finalmente conseguiu fazer isso. Não esqueça de votar em @Jason Robinson, porque o meu é baseado no dele.

Então, primeiro, você deve saber que, desde o Android 7.0, precisamos usar FileProvideralgo chamado ContentUri, caso contrário, você receberá um erro irritante ao tentar invocar o seu Intent. Este é um código de exemplo:

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)baseado na versão do usuário do Android create FileUri (file://...)ou ContentUri (content://...)e lá 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);
    }
}

Depois de onActivityResultcapturar o local urionde a imagem é salva pela câmera, mas agora você precisa detectar a rotação da câmera, aqui usaremos a resposta modificada de @Jason Robinson:

Primeiro precisamos criar com ExifInterfacebase emUri

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

O código acima pode ser simplificado, mas quero mostrar tudo. Então FileUri, podemos criar com ExifInterfacebase String path, mas ContentUrinão podemos, o Android não suporta isso.

Nesse caso, temos que usar outro construtor baseado em InputStream. Lembre-se de que este construtor não está disponível por padrão, você deve adicionar uma biblioteca adicional:

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

Agora podemos usar o getExifInterfacemétodo para obter nosso â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;
    }
}

Agora você tem Angle para girar corretamente sua imagem :).

Artur Szymański
fonte
2
implementação 'androidx.exifinterface: exifinterface: XXX' É para aqueles que estão usando o androidx. obrigado por sua postagem
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
fonte
Perfect Solution Haresh Bhai
Sagar Pithiya
9

Você pode ler a orientação do sensor da câmera conforme indicado pelo Google na documentação: 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 amostra:

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
fonte
6

Jason Robinson resposta e Sami Eltamawy resposta são excelentes.

Apenas uma melhoria para concluir a abordagem, você deve usar compat ExifInterface.

com.android.support:exifinterface:${lastLibVersion}

Você poderá instanciar o ExifInterface (pior API <24) com caminhos InputStream(from ContentResolver) em vez de uri, evitando "Arquivo não encontrado exceções"

https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html

Ricard
fonte
4

Normalmente, é recomendável resolver o problema com o ExifInterface , como sugeriu @Jason Robinson. Se essa abordagem não funcionar, tente procurar a orientação da última imagem tirada ...

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
fonte
11
Eu acho que esse código só detecta em que grau a rotação ocorreu. Agora eu sou capaz de fazer isso, mas não consigo na próxima tarefa, ou seja, girar a imagem.
Shirish Herwade
Você está certo, mas não pediu a rotação neste Thread, então vamos mantê-lo limpo;) É por isso que eu coloco minha resposta para o seu problema de rotação no seu outro Thread ... Espero que ajude, ele funciona para eu: stackoverflow.com/questions/14123809/…
Chris Conway
4

Infelizmente, a resposta @ jason-robinson acima não funcionou para mim.

Embora a função de rotação funcione perfeitamente:

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

Eu tive que fazer o seguinte para obter a orientação, pois a orientação Exif sempre foi 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
fonte
11
alwasys 0, samsung 7
djdance
2

Melhor tentar tirar a foto em uma orientação específica.

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

Para obter melhores resultados, forneça orientação de paisagem na atividade de visualização de câmera.

Siva
fonte
desculpe, não funciona De fato, na guia, sempre que terminar a execução de onActivityResult, estranhamente onCreate é chamado.
Shirish Herwade
11
Lamentamos, mas o problema é como é
Shirish Herwade
1

A resposta selecionada usa o método mais comum respondido a esta e a perguntas semelhantes. No entanto, ele não funciona com as câmeras frontal e traseira da Samsung. Para quem procura uma solução que funcione nas câmeras frontal e traseira da Samsung e de outros grandes fabricantes, a resposta da nvhausid é impressionante:

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

Para aqueles que não querem clicar, a mágica relevante é usar o CameraInfo em vez de confiar no 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 no link.

D. Scott
fonte
não, rotação incorreta em diferentes ângulos (smasung s7). Quero dizer, galeria do curso
djdance
1

Talvez isso seja óbvio, mas lembre-se sempre de que você pode lidar com alguns desses problemas de manipulação de imagem no servidor. Usei respostas como as contidas neste tópico para lidar com a exibição imediata da imagem. No entanto, meu aplicativo exige que as imagens sejam armazenadas no servidor (esse é provavelmente um requisito comum se você deseja que a imagem persista enquanto os usuários trocam de telefone).

As soluções contidas em muitos dos tópicos relacionados a este tópico não discutem a falta de persistência dos dados EXIF ​​que não sobrevivem à compactação de imagem do Bitmap, o que significa que você precisará girar a imagem cada vez que o servidor a carregar. Como alternativa, você pode enviar os dados de orientação EXIF ​​para o servidor e, em seguida, girar a imagem para lá, se necessário.

Era mais fácil para mim criar uma solução permanente em um servidor porque não precisava me preocupar com os caminhos de arquivos clandestinos do Android.

Braden Holt
fonte
Você pode girá-lo uma vez no momento da captura da imagem e salvá-lo dessa maneira para que nunca precise ser girado novamente?
Jk7
Sim, você pode e esse foi realmente o processo que acabei implementando no final. Estava com problemas para obter o caminho do arquivo da imagem no telefone Android que me permitia fazer isso. Esta é a resposta que ajudou: stackoverflow.com/a/36714242/5443056
Braden Holt
1

A solução mais simples para esse problema:

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

Estou salvando a imagem em formato jpg.

DNB
fonte
0

Aqui está a Xamarin.Androidversão:

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

Então 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;
}

Da resposta 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
fonte
0

Se você estiver usando o Fresco, poderá usar isto -

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

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

Isso gira automaticamente as imagens com base nos dados Exif.

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

Ritesh Chandnani
fonte
0

O código abaixo trabalhou comigo, ele obteve o bitmap do fileUri e, se necessário, corrige a rotação:

    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
fonte
0

Tem uma resposta para esse problema sem usar o ExifInterface . Podemos obter a rotação da câmera frontal ou traseira, independentemente do que você estiver usando. Ao criar o Bitmap, podemos girar o bitmap usando Matrix.postRotate (degree)

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

Depois de calcular a rotação, você pode girar seu bitmap como abaixo:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm deve ser seu bitmap.

Se você quiser saber a rotação da sua câmera frontal, basta alterar Camera.CameraInfo.CAMERA_FACING_BACK para Camera.CameraInfo.CAMERA_FACING_FRONT acima.

Eu espero que isso ajude.

Om Prakash Agrahari
fonte
11
Resposta horrível, mas eu acidentalmente votei. Este código assume que todas as imagens da sua galeria são feitas com sua câmera. Este não é o caso
Zun
-1

Criei uma função de extensão Kotlin que simplifica a operação para desenvolvedores Kotlin com base na resposta de @Jason Robinson. Espero que ajude.

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
fonte
11
impressionante, mas sofre do mesmo problema que todas as soluções, como extensão ou função - não funciona no Android 10.
Lior Iluz 07/01
-2

Existe um comando mais simples para corrigir esse erro.

Basta adicionar depois do yourImageView.setBitmap (bitmap); this yourImageView.setRotation (90);

Este meu fixo. Espero que ajude !

JG
fonte
6
Como o OP afirmou, alguns dispositivos não giram a imagem, alguns giram em 90 graus, outros 180, etc. Portanto, sempre girá-lo 90 seria incorreto em alguns casos.
Jk7
-8

isso funcionou para mim

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);
zaheer
fonte
lol que aberração é essa. Como diabos você sabe foto tirada pela câmera é -90/90 / 0 / ... Usuário pode estar a tirar fotos como a paisagem e não importa o que você vai girar que ... lmao
Alex
Nesse caso, funcionou para mim, pois no meu caso o usuário sempre tirará a foto com o telefone na vertical.
Christian Eduardo Galdamez