Warum wird ein mit Kameraabsicht aufgenommenes Bild auf einigen Geräten unter Android gedreht?


376

Ich nehme ein Bild auf und setze es auf Bildansicht.

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

Das Problem ist jedoch, dass das Bild auf einigen Geräten jedes Mal gedreht wird, wenn es gedreht wird. Auf einem Samsung-Gerät funktioniert es beispielsweise gut, auf einem Sony Xperia wird das Bild jedoch um 90 Grad und auf dem Toshiba Thrive (Tablet) um 180 Grad gedreht.


1
Versuchen Sie dies in Ihrer Aktivität menifest android: configChanges = "Orientierung" android: screenOrientation = "Porträt"
Narendra Pal

@nick es funktioniert nicht, jetzt wird das Bild auf der Registerkarte um 90 Grad anstatt um 180 Grad gedreht
Shirish Herwade

1
Wie ich denke, wenn Sie die interne Absicht für den Umgang mit der Kamera-App verwenden, dreht sie das Bild. Dies hängt davon ab, wie Sie das Gerät halten, um das Bild aufzunehmen. Sie können den Benutzer also darauf beschränken, Bilder auf bestimmte Weise aufzunehmen. Dies bedeutet, dass der Benutzer Bilder immer aufnimmt, indem er das Gerät im Hoch- oder Querformat hält. Danach können Sie es in einen bestimmten Winkel ändern, um das gewünschte Bild zu erhalten. ODER EINE ANDERE OPTION, MACHEN SIE IHRE EIGENE KAMERA-APP.
Narendra Pal

@nick "Sie können den Benutzer auf bestimmte Weise auf das Aufnehmen von Bildern beschränken" bedeutet, dass dies mit der Einstellung von Ausrichtung = "Potrait" identisch ist. Und wie kann man "Danach kann man es in einen bestimmten Winkel ändern, um das gewünschte Bild zu erhalten" erreichen? Bitte können Sie einige nützliche Links geben
Shirish Herwade

3
Ich glaube, die Aufnahmeabsicht ruft immer die Standardkamera-App auf, die auf jedem Gerät eine bestimmte Ausrichtung und folglich eine feste Fotoausrichtung aufweist. Dies hängt nicht von der Art und Weise ab, wie der Benutzer das Gerät hält, oder von der Ausrichtung Ihrer Aktivität, die die Absicht ausgelöst hat.
Alex Cohn

Antworten:


440

Die meisten Telefonkameras sind im Querformat. Wenn Sie das Foto im Hochformat aufnehmen, werden die resultierenden Fotos um 90 Grad gedreht. In diesem Fall sollte die Kamerasoftware die Exif- Daten mit der Ausrichtung füllen, in der das Foto angezeigt werden soll.

Beachten Sie, dass die folgende Lösung von der Kamera-Software / dem Gerätehersteller abhängt, die bzw. der die Exif-Daten ausfüllt. Daher funktioniert sie in den meisten Fällen, ist jedoch keine 100% zuverlässige Lösung.

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

Hier ist die rotateImageMethode:

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

1
Aus den @ JasonRobinson-Codes lerne ich, wie man die tatsächliche Ausrichtung erhält, und durch die Kombination mit diesem Code verwalte ich die Ausrichtung erfolgreich.
Raditya Kurnianto

Die zweite exif.getAttributeIntVerwendungsoption ExifInterface.ORIENTATION_UNDEFINEDist nahezu identisch, da der zweite Parameter der Standardwert ist, falls die Funktion den Wert nicht bereitstellt.
Darpan

5
Dieser Code ist für ein Image, das bereits auf die Festplatte geschrieben wurde, oder? Mit dieser Methode erhalte ich keine Ergebnisse für Bitmaps, die auf die Festplatte geschrieben werden sollen.
Thracian

4
Es wird immer 0 Wert zurückgegeben. Bitte geben Sie an, wie Sie sich tatsächlich orientieren können.
Anurag Srivastava

3
Immer 0 bekommen, eine Idee warum?
Navya Ramesan

186

Durch die Kombination von Jason Robinson ‚s Antwort mit Felix ‘ s Antwort und füllt die fehlenden Teile, hier ist die letzte vollständige Lösung für dieses Problem , das die folgenden nach der Prüfung auf Android tun Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) und Android 5.0 ( Lollipop ).

Schritte

  1. Verkleinern Sie das Bild, wenn es größer als 1024 x 1024 ist.

  2. Drehen Sie das Bild nur dann in die richtige Ausrichtung , wenn es um 90, 180 oder 270 Grad gedreht wurde.

  3. Recyceln Sie das gedrehte Bild für Speicherzwecke.

Hier ist der Codeteil:

Rufen Sie die folgende Methode mit dem aktuellen Contextund dem Bild auf URI, das Sie reparieren möchten

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

Hier ist die CalculateInSampleSizeMethode aus der oben genannten Quelle :

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

Dann kommt die Methode, die die aktuelle Bildausrichtung überprüft, um den Drehwinkel zu bestimmen

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

Schließlich die Rotationsmethode selbst

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

- Vergessen Sie nicht, für die Antworten der Jungs für ihre Bemühungen und Shirish Herwade zu stimmen , die diese hilfreiche Frage gestellt haben.


2
Es
erwacht

1
Die Methode rotateImageIfRequired () funktioniert sehr gut .. danke !!
Mapo

5
Funktioniert bei mir nicht Manchmal gibt mein Telefon Hoch-, manchmal Querformatfotos aus, aber die erkannte Ausrichtung beträgt immer 0 Grad.
Makalele

@Makalele Tritt dieses Problem auch beim Aufnehmen von Fotos und Anhängen über WhatsApp auf?
Manoj Perumarath

Ich benutze WhatsApp nicht, kann ich also nicht sagen, aber höchstwahrscheinlich ja. Das liegt daran, dass es sogar in der Foto-App (Google Stock Camera) passiert.
Makalele

45

Es ist einfach, die Bildausrichtung zu erkennen und die Bitmap zu ersetzen, indem Sie:

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

Um zu vermeiden, dass bei großen Bildern keine Erinnerungen mehr vorhanden sind, würde ich Ihnen empfehlen, das Bild mit folgenden Elementen neu zu skalieren:

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

Es ist nicht möglich, ExifInterface zu verwenden, um die Orientierung zu erhalten, da ein Problem mit dem Android-Betriebssystem vorliegt: https://code.google.com/p/android/issues/detail?id=19268

Und hier ist 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;
}

1
Was ist berechneInSampleSize Methode hier
Madhu Kotagiri

1
@madhukotagiri hier haben Sie ein Beispiel für die Implementierung für berechneInSampleSize: gist.github.com/anonymous/b7ea25fc2bbc54e43616
Felix

Danke Mann, du bist definitiv derjenige! Ich frage mich nur, wie nützlich die Größenänderung sein wird, wenn die Operation nur gelegentlich ausgeführt wird.
Marino

4
Der Parameter Uri selectedImage wird in der Methode getRotation (...) nicht verwendet. Wie brauchen wir es zu benutzen? Vielen Dank.
Valerybodak

1
Der Parameter 'selectedImage' scheint nirgendwo verwendet zu werden. Gibt es einen Grund, dort zu sein?
Alex

20

Einzeilige Lösung:

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

Oder

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

Dadurch wird die Drehung automatisch erkannt und das Bild in die richtige Ausrichtung gebracht

Picasso ist eine sehr leistungsstarke Bibliothek für die Verarbeitung von Bildern in Ihrer App. Dazu gehören: Komplexe Bildtransformationen mit minimalem Speicherbedarf.


1
Interessante Lösung
Bhavik Mehta

8
Es lädt nur das Bild in eine Ansicht, es gibt Ihnen keine Bitmap oder Datei, die Sie bearbeiten oder auf einen Server hochladen können.
Fehler

4
Das angezeigte Bild klickte so wie es ist. Es dreht sich nicht nach Bedarf.
Seema

1
@Flawyte Sie können dies tun, indem Sie eine Datei in das Ziel laden, anstatt sie mit einem Rückruf anzuzeigen, der eine zugeschnittene / geänderte Bitmap zurückgibt: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). In (target); where target = new Target () {Öffentliche Leere überschreiben onBitmapLoaded (Bitmap-Bitmap, Picasso.LoadedFrom from) {
voytez

Das Problem, mit dem ich immer noch konfrontiert bin, ist, dass es einige Sekunden dauert, um das Bild anzuzeigen
Anu

12

Ich habe viel Zeit damit verbracht, nach einer Lösung dafür zu suchen. Und das endlich geschafft. Vergiss nicht, die Antwort von @Jason Robinson zu verbessern, da meine auf seiner basiert.

Als erstes sollten Sie wissen, dass wir seit Android 7.0 verwenden müssen FileProviderund etwas namens ContentUri, sonst erhalten Sie einen nervigen Fehler beim Versuch, Ihre aufzurufen Intent. Dies ist ein Beispielcode:

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

Methodenbasis getUriFromPath(Context, String)auf User-Version von Android erstellen FileUri (file://...)oder ContentUri (content://...)und da ist es:

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

Nachdem onActivityResultSie festgestellt haben, uriwo das Bild von der Kamera gespeichert wurde, aber jetzt die Kameradrehung erkennen müssen, verwenden wir hier die modifizierte @ Jason Robinson-Antwort:

Zuerst müssen wir ExifInterfacebasierend auf erstellenUri

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

Der obige Code kann vereinfacht werden, aber ich möchte alles zeigen. Also FileUrikönnen wir ExifInterfacebasierend auf erstellen String path, aber von können ContentUriwir nicht, Android unterstützt das nicht.

In diesem Fall müssen wir einen anderen Konstruktor verwenden, der auf basiert InputStream. Denken Sie daran, dass dieser Konstruktor standardmäßig nicht verfügbar ist. Sie müssen eine zusätzliche Bibliothek hinzufügen:

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

Jetzt können wir die getExifInterfaceMethode verwenden, um unseren Winkel zu ermitteln:

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

Jetzt haben Sie den Winkel, um Ihr Bild richtig zu drehen :).


2
Implementierung 'androidx.exifinterface: exifinterface: XXX' Dies ist für diejenigen, die androidx verwenden. Vielen Dank für Ihre Veröffentlichung
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;
    }
}

Perfekte Lösung Haresh Bhai
Sagar Pithiya

9

Sie können einfach die Ausrichtung des Kamerasensors lesen, wie von Google in der Dokumentation angegeben: 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.

Beispielcode:

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

6

Jason Robinson Antwort und Sami Eltamawy Antwort ist ausgezeichnet.

Nur eine Verbesserung, um den Ansatz zu vervollständigen, sollten Sie kompatibles ExifInterface verwenden.

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

Sie können das ExifInterface (pior API <24) mit InputStream(from ContentResolver) anstelle von uri-Pfaden instanziieren, wobei "Ausnahmen für Datei nicht gefunden" vermieden werden.

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


4

Normalerweise wird empfohlen, das Problem mit dem ExifInterface zu lösen , wie es @Jason Robinson vorgeschlagen hat. Wenn dieser Ansatz nicht funktioniert, können Sie versuchen, die Ausrichtung des zuletzt aufgenommenen Bildes nachzuschlagen ...

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

1
Ich denke, dieser Code erkennt nur, in welchem ​​Grad Rotation aufgetreten ist. Jetzt kann ich das tun, aber bei der nächsten Aufgabe, dh beim Drehen des Bildes, nicht.
Shirish Herwade

Du hast recht, aber du hast nicht nach der Rotation in diesem Thread gefragt, also lass es uns sauber halten;) Deshalb habe ich meine Antwort auf dein Rotationsproblem in deinen anderen Thread eingefügt ... Ich hoffe, das hilft, es funktioniert für ich: stackoverflow.com/questions/14123809/…
Chris Conway

4

Leider hat die Antwort von @ jason-robinson oben bei mir nicht funktioniert.

Obwohl die Drehfunktion perfekt funktioniert:

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

Ich musste Folgendes tun, um die Ausrichtung zu erhalten, da die Exif-Ausrichtung immer 0 war

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

1
alwasys 0, samsung 7
djdance

2

Versuchen Sie besser, das Bild in einer bestimmten Ausrichtung aufzunehmen.

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

Für beste Ergebnisse geben Sie die Querformatorientierung in der Kameraansicht an.


Entschuldigung, es funktioniert nicht. Tatsächlich wird onCreate jedes Mal nach Abschluss der Ausführung von onActivityResult auf der Registerkarte seltsamerweise aufgerufen.
Shirish Herwade

1
Entschuldigung, das Problem ist wie es ist
Shirish Herwade



1

Die ausgewählte Antwort verwendet die häufigste Methode, die auf diese und ähnliche Fragen beantwortet wird. Es funktioniert jedoch nicht mit Vorder- und Rückkameras von Samsung. Für diejenigen, die nach einer Lösung suchen, die sowohl für Vorder- als auch für Rückkameras von Samsung und anderen großen Herstellern funktioniert, ist diese Antwort von nvhausid fantastisch:

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

Für diejenigen, die sich nicht durchklicken möchten, ist die relevante Magie, die CameraInfo zu verwenden, anstatt sich auf EXIF ​​zu verlassen.

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

Vollständiger Code im Link.


nein, falsche drehung in verschiedenen winkeln (smasung s7). Ich meine Galerie natürlich
DJDance

1

Dies ist vielleicht selbstverständlich, aber denken Sie immer daran, dass Sie einige dieser Probleme bei der Bildbearbeitung auf Ihrem Server lösen können. Ich habe Antworten wie die in diesem Thread enthaltenen verwendet, um die sofortige Anzeige des Bildes zu ermöglichen. Für meine Anwendung müssen jedoch Bilder auf dem Server gespeichert werden (dies ist wahrscheinlich eine häufige Anforderung, wenn das Bild erhalten bleiben soll, wenn Benutzer das Telefon wechseln).

Die in vielen Threads zu diesem Thema enthaltenen Lösungen behandeln nicht die mangelnde Persistenz der EXIF-Daten, die die Bildkomprimierung der Bitmap nicht überlebt. Dies bedeutet, dass Sie das Bild jedes Mal drehen müssen, wenn Ihr Server es lädt. Alternativ können Sie die EXIF-Orientierungsdaten an Ihren Server senden und das Bild dort bei Bedarf drehen.

Es war einfacher für mich, eine dauerhafte Lösung auf einem Server zu erstellen, da ich mich nicht um die geheimen Dateipfade von Android kümmern musste.


Können Sie es einmal zur Bildaufnahmezeit drehen und auf diese Weise speichern, damit es nie wieder gedreht werden muss?
7.

Ja, das kannst du und das ist eigentlich der Prozess, den ich am Ende implementiert habe. Ich hatte Probleme, den Dateipfad vom Bild auf dem Android-Telefon abzurufen, der mir dies ermöglichen würde. Dies ist die Antwort, die geholfen hat: stackoverflow.com/a/36714242/5443056
Braden Holt

1

Die einfachste Lösung für dieses Problem:

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

Ich speichere das Bild im JPG-Format.


0

Hier ist Xamarin.Android Version:

Aus der Antwort von @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;
    }
}

Dann calculateInSampleSizeMethode:

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

Aus der Antwort von @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;
}

0

Wenn Sie Fresco verwenden, können Sie dies verwenden -

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

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

Dadurch werden die Bilder automatisch basierend auf Exif-Daten gedreht.

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


0

Der folgende Code hat bei mir funktioniert, er hat die Bitmap aus der DateiUri erhalten und bei Bedarf die Rotationskorrektur durchgeführt:

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

0

Ich habe eine Antwort auf dieses Problem erhalten, ohne ExifInterface zu verwenden . Wir können die Drehung der Kamera entweder auf der Vorder- oder auf der Rückkamera abrufen, je nachdem, was Sie verwenden. Während der Erstellung der Bitmap können wir die Bitmap mit Matrix.postRotate (Grad) drehen.

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

Nach der Berechnung der Drehung können Sie Ihre Bitmap wie folgt drehen:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm sollte Ihre Bitmap sein.

Wenn Sie die Drehung Ihrer Frontkamera wissen möchten, ändern Sie oben einfach Camera.CameraInfo.CAMERA_FACING_BACK in Camera.CameraInfo.CAMERA_FACING_FRONT .

Ich hoffe das hilft.


1
Schreckliche Antwort, aber ich habe versehentlich gestimmt. Dieser Code setzt voraus, dass jedes Bild aus Ihrer Galerie mit Ihrer Kamera aufgenommen wurde. Dies ist nicht der Fall
Zun

-1

Ich habe eine Kotlin-Erweiterungsfunktion erstellt, die den Betrieb für Kotlin-Entwickler basierend auf der Antwort von @Jason Robinson vereinfacht. Ich hoffe, es hilft.

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

1
Genial, leidet aber unter dem gleichen Problem wie alle Lösungen, als Erweiterung oder Funktion - funktioniert nicht unter Android 10.
Lior Iluz

-2

Es gibt einen einfacheren Befehl, um diesen Fehler zu beheben.

Fügen Sie einfach nach yourImageView.setBitmap (Bitmap) hinzu. this yourImageView.setRotation (90);

Diese feste Mine. Ich hoffe es hilft !


6
Wie im OP angegeben, drehen einige Geräte das Bild nicht, andere drehen es um 90 Grad, andere um 180 usw. Es wäre also in einigen Fällen falsch, es immer um 90 zu drehen.
7.

-8

das hat bei mir funktioniert

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);

lol was zum Teufel ist das? Wie zum Teufel würden Sie wissen, dass das mit der Kamera aufgenommene Foto -90 / 90/0
Alex

In diesem Fall hat es bei mir funktioniert, da in meinem Fall der Benutzer das Bild immer mit dem Telefon in vertikaler Richtung aufnimmt.
Christian Eduardo Galdamez
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.