2014-12-16 19 views
11

Ich entwickle eine Kamera-Anwendung für Android API 16 bis 21, die Haupt- und einzigen Zweck ist, Porträt Foto zu nehmen. Ich kann Bilder mit mehreren Geräten aufnehmen (Nexus 4, Nexus 5, HTC ...) und sie korrekt ausrichten (das bedeutet, dass meine Vorschau dem aufgenommenen Bild in Größe und Ausrichtung entspricht).Android Kamera/Bildausrichtung Probleme mit Samsung Galaxy S3, S4, S5

Allerdings habe ich meine Anwendung auf mehreren anderen Geräten getestet und einige von ihnen geben mir viel Ärger: Samsung Galaxy S3/S4/S5.

Auf diesen drei Geräten wird die Vorschau korrekt angezeigt, jedoch sind die von der Methode onPictureTaken(final byte[] jpeg, Camera camera) zurückgegebenen Bilder immer seitwärts.

Dies ist die Bitmap von byte[] jpeg und angezeigt in der Image meines Benutzer erstellt, kurz bevor es auf die Festplatte zu speichern:

preview

Und hier ist das Bild einmal gespeichert auf der Festplatte:

disk

Wie Sie sehen können, wird das Bild in der Vorschau komplett gestreckt und nach dem Speichern auf der Festplatte falsch gedreht.

Hier ist meine CameraPreview Klasse (I verschleierte andere Methoden, da sie nichts mit Kameraparametern zu tun hatten):

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback 
{ 
    private SurfaceHolder surfaceHolder; 
    private Camera camera; 

    // Removed unnecessary code 

    public void surfaceCreated(SurfaceHolder holder) 
    { 
     camera.setPreviewDisplay(holder); 
     setCameraParameters(); 
     camera.startPreview(); 
    } 

    private void setCameraParameters() 
    { 
     Camera.Parameters parameters = camera.getParameters(); 
     Camera.CameraInfo info = new Camera.CameraInfo(); 
     Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info); 

     DisplayMetrics metrics = new DisplayMetrics(); 
     WindowManager windowManager = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 
     windowManager.getDefaultDisplay().getMetrics(metrics); 
     int rotation = windowManager.getDefaultDisplay().getRotation(); 
     int degrees = 0; 
     switch (rotation) 
     { 
      case Surface.ROTATION_0: 
       degrees = 0; 
       break; 
      case Surface.ROTATION_90: 
       degrees = 90; 
       break; 
      case Surface.ROTATION_180: 
       degrees = 180; 
       break; 
      case Surface.ROTATION_270: 
       degrees = 270; 
       break; 
     } 
     int rotate = (info.orientation - degrees + 360) % 360; 
     parameters.setRotation(rotate); 

     // Save Parameters 
     camera.setDisplayOrientation(90); 
     camera.setParameters(parameters); 
    } 
} 

Wie kommt es genau dieses Stück Code für andere Geräte außer Samsung gearbeitet?

Ich habe versucht, Antworten auf die folgenden SO-Beiträge zu finden, aber nichts konnte mir bisher helfen: this one und this other one.

EDIT

Antwort Joey Chong Implementierung tut ändert nichts:

public void onPictureTaken(final byte[] data, Camera camera) 
{ 
    try 
    { 
     File pictureFile = new File(...); 
     Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length); 
     FileOutputStream fos = new FileOutputStream(pictureFile); 
     realImage.compress(Bitmap.CompressFormat.JPEG, 100, fos); 

     int orientation = -1; 
     ExifInterface exif = new ExifInterface(pictureFile.toString()); 
     int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); 

     switch (exifOrientation) 
     { 
      case ExifInterface.ORIENTATION_ROTATE_270: 
       orientation = 270; 
       break; 
      case ExifInterface.ORIENTATION_ROTATE_180: 
       orientation = 180; 
       break; 
      case ExifInterface.ORIENTATION_ROTATE_90: 
       orientation = 90; 
       break; 
      case ExifInterface.ORIENTATION_NORMAL: 
       orientation = 0; 
       break; 
      default: 
       break; 
     } 

     fos.close(); 
} 

Hier werden die EXIF-Ergebnisse sind ich für ein Arbeitsgerät erhalten:

  • Orientierung: 0

Und hier die Ergebnisse für die S4:

  • Orientierung: 0
+0

möglich Duplikat [setRotation (90) Bild im Hochformat nehmen funktioniert nicht auf Samsung-Geräten] (http://stackoverflow.com/questions/11023696/setrotation90-to-take-picture-in- portrait-mode-does-not-work-on-samsung-device) –

Antwort

4

Es liegt daran, dass das Telefon immer noch in der Landschaft speichern und die Metadaten als 90 Grad setzen. Sie können versuchen, das Exif zu überprüfen, drehen Sie die Bitmap, bevor Sie in der Bildansicht. Um zu überprüfen, exif, verwenden Sie so etwas wie unten:

int orientation = -1; 

    ExifInterface exif = new ExifInterface(imagePath); 

    int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 
      ExifInterface.ORIENTATION_NORMAL); 

    switch (exifOrientation) { 
     case ExifInterface.ORIENTATION_ROTATE_270: 
      orientation = 270; 

      break; 
     case ExifInterface.ORIENTATION_ROTATE_180: 
      orientation = 180; 

      break; 
     case ExifInterface.ORIENTATION_ROTATE_90: 
      orientation = 90; 

      break; 

     case ExifInterface.ORIENTATION_NORMAL: 
      orientation = 0; 

      break; 
     default: 
      break; 
    } 
+1

Danke für Ihre Antwort, ich habe Ihre Lösung implementiert (siehe Bearbeiten), aber die exifOrientation ist immer gleich 0 auf funktionierenden und nicht funktionierenden Geräten. – Aymeric

+0

Wie lautet die scaleType-Einstellung für ImageView? Wenn Sie es auf "center" setzen, was ist das Ergebnis? –

+0

Die Vorschau hat nichts mit dem Problem zu tun, das Problem ist, dass auf einigen Samsung-Geräten das Bild als Querformat statt als Porträt gespeichert wird, wie im Nexus 5. – Aymeric

2

Ich hatte ein ähnliches Problem in Bezug auf das gespeicherte Bild.

Ich habe etwas ähnliches wie hier https://github.com/googlesamples/android-vision/issues/124 von Benutzer kinghsumit (der Kommentar vom 15. September 2016) verwendet.

Ich werde es hier kopieren, nur für den Fall.

Unten Klasse wird verwendet, um Orientierung aus Byte [] Daten zu erhalten.

public class Exif { 
    private static final String TAG = "CameraExif"; 

    // Returns the degrees in clockwise. Values are 0, 90, 180, or 270. 
    public static int getOrientation(byte[] jpeg) { 
     if (jpeg == null) { 
      return 0; 
     } 

     int offset = 0; 
     int length = 0; 

     // ISO/IEC 10918-1:1993(E) 
     while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) { 
      int marker = jpeg[offset] & 0xFF; 

      // Check if the marker is a padding. 
      if (marker == 0xFF) { 
       continue; 
      } 
      offset++; 

      // Check if the marker is SOI or TEM. 
      if (marker == 0xD8 || marker == 0x01) { 
       continue; 
      } 
      // Check if the marker is EOI or SOS. 
      if (marker == 0xD9 || marker == 0xDA) { 
       break; 
      } 

      // Get the length and check if it is reasonable. 
      length = pack(jpeg, offset, 2, false); 
      if (length < 2 || offset + length > jpeg.length) { 
       Log.e(TAG, "Invalid length"); 
       return 0; 
      } 

      // Break if the marker is EXIF in APP1. 
      if (marker == 0xE1 && length >= 8 && 
        pack(jpeg, offset + 2, 4, false) == 0x45786966 && 
        pack(jpeg, offset + 6, 2, false) == 0) { 
       offset += 8; 
       length -= 8; 
       break; 
      } 

      // Skip other markers. 
      offset += length; 
      length = 0; 
     } 

     // JEITA CP-3451 Exif Version 2.2 
     if (length > 8) { 
      // Identify the byte order. 
      int tag = pack(jpeg, offset, 4, false); 
      if (tag != 0x49492A00 && tag != 0x4D4D002A) { 
       Log.e(TAG, "Invalid byte order"); 
       return 0; 
      } 
      boolean littleEndian = (tag == 0x49492A00); 

      // Get the offset and check if it is reasonable. 
      int count = pack(jpeg, offset + 4, 4, littleEndian) + 2; 
      if (count < 10 || count > length) { 
       Log.e(TAG, "Invalid offset"); 
       return 0; 
      } 
      offset += count; 
      length -= count; 

      // Get the count and go through all the elements. 
      count = pack(jpeg, offset - 2, 2, littleEndian); 
      while (count-- > 0 && length >= 12) { 
       // Get the tag and check if it is orientation. 
       tag = pack(jpeg, offset, 2, littleEndian); 
       if (tag == 0x0112) { 
        // We do not really care about type and count, do we? 
        int orientation = pack(jpeg, offset + 8, 2, littleEndian); 
        switch (orientation) { 
         case 1: 
          return 0; 
         case 3: 
          return 180; 
         case 6: 
          return 90; 
         case 8: 
          return 270; 
        } 
        Log.i(TAG, "Unsupported orientation"); 
        return 0; 
       } 
       offset += 12; 
       length -= 12; 
      } 
     } 

     Log.i(TAG, "Orientation not found"); 
     return 0; 
    } 

    private static int pack(byte[] bytes, int offset, int length, boolean littleEndian) { 
     int step = 1; 
     if (littleEndian) { 
      offset += length - 1; 
      step = -1; 
     } 

     int value = 0; 
     while (length-- > 0) { 
      value = (value << 8) | (bytes[offset] & 0xFF); 
      offset += step; 
     } 
     return value; 
    } 
} 

Er arbeitete für mich, mit Ausnahme des Nexus 5-fach, aber das ist, weil das Gerät ein eigentümliches Problem aufgrund seiner Konstruktion hat.

Ich hoffe, das hilft Ihnen!

+0

Eigentlich müssen Sie das EXIF-Byte nicht aus JPEG lesen - Sie können genauso gut die Ausrichtung des Geräts verfolgen, während das Bild aufgenommen wird. Ja, ein böswilliger Benutzer könnte das Gerät schnell in die Landschaft und zurück drehen, und Sie melden eine falsche Ausrichtung. Aber die Kamera setzt EXIF-Flag (wenn es das tut) basierend auf genau derselben Logik und kann so einfach getäuscht werden. –

+0

ist es nicht effizienter, es nur einmal zu überprüfen (wenn das Bild aufgenommen wird)? – Mel

+0

Um welche Art von Effizienz machen Sie sich Sorgen? Wenn der Benutzer nach dem Drücken der Taste * capture * die Geräteausrichtung schnell ändert, kann ein falsches Ergebnis angezeigt werden. Doppelprüfung kann Ihre Wahl etwas zuverlässiger machen. Wenn der angezeigte Winkel 45 ° beträgt, wählen Sie dann Querformat oder Hochformat? Eigentlich war der TS-Zweck, ein * Portrait * -Bild zu machen. In diesem Fall kann er einfach 90 wählen. –

0

Sie können versuchen, Kamera-Parameter zu verwenden, um das Rotationsproblem zu beheben.

Camera.Parameters parameters = camera.getParameters(); 
parameters.set("orientation", "portrait"); 
parameters.setRotation(90); 
camera.setParameters(parameters); 
Verwandte Themen