【问题标题】:camerasource.takePicture() save rotated images in some devicecamerasource.takePicture() 在某些设备中保存旋转的图像
【发布时间】:2016-09-12 05:08:33
【问题描述】:

我正在使用视觉 api 来跟踪面部。我根据面部位置应用了蒙版。当我从前置摄像头拍照时,我调用 camerasource.takePicture() 来保存图像。我在三星等设备中面临图像旋转问题,并且捕获的图像显示蒙版和面部不同的位置。我使用 Exif 类来获取图像的方向,但它总是返回 0,所以我无法旋转图像。 我正在使用以下类来获取方向和旋转图像。

 public class ExifUtils {
 public Bitmap rotateBitmap(String src, Bitmap bitmap) {
    try {
        int orientation = getExifOrientation(src);

        if (orientation == 1) {
            return bitmap;
        }

        Matrix matrix = new Matrix();
        switch (orientation) {
            case 2:
                matrix.setScale(-1, 1);
                break;
            case 3:
                matrix.setRotate(180);
                break;
            case 4:
                matrix.setRotate(180);
                matrix.postScale(-1, 1);
                break;
            case 5:
                matrix.setRotate(90);
                matrix.postScale(-1, 1);
                break;
            case 6:
                matrix.setRotate(90);
                break;
            case 7:
                matrix.setRotate(-90);
                matrix.postScale(-1, 1);
                break;
            case 8:
                matrix.setRotate(-90);
                break;
            default:
                return bitmap;
        }

        try {
            Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0,
                    bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            bitmap.recycle();
            return oriented;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return bitmap;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    return bitmap;
}

private int getExifOrientation(String src) throws IOException {
    int orientation = 1;

    try {

        if (Build.VERSION.SDK_INT >= 5) {
            Class<?> exifClass = Class
                    .forName("android.media.ExifInterface");
            Constructor<?> exifConstructor = exifClass
                    .getConstructor(new Class[]{String.class});
            Object exifInstance = exifConstructor
                    .newInstance(new Object[]{src});
            Method getAttributeInt = exifClass.getMethod("getAttributeInt",
                    new Class[]{String.class, int.class});
            Field tagOrientationField = exifClass
                    .getField("TAG_ORIENTATION");
            String tagOrientation = (String) tagOrientationField.get(null);
            orientation = (Integer) getAttributeInt.invoke(exifInstance,
                    new Object[]{tagOrientation, 1});

        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (Fragment.InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (java.lang.InstantiationException e) {
        e.printStackTrace();
    }

    return orientation;
}

}

我在视觉 api 中找到了这个 issue 有什么解决方案吗。

【问题讨论】:

  • 查看我在视觉 api 问题上添加的评论。可以从帧元数据中获取方向。
  • Thanks@pm0733464 你能举个例子吗?
  • 我解决了我的问题,请参阅我在视觉 api 问题上添加的评论。github.com/googlesamples/android-vision/issues/124

标签: android google-vision


【解决方案1】:

我自己解决我的问题。我从字节数据中获取方向,然后根据方向旋转我的图像。

private CameraSource.PictureCallback mPicture = new CameraSource.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] bytes) {
       int orientation = Exif.getOrientation(bytes);
       Bitmap   bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        switch(orientation) {
            case 90:
                bitmapPicture= rotateImage(bitmap, 90);

                break;
            case 180:
                bitmapPicture= rotateImage(bitmap, 180);

                break;
            case 270:
                bitmapPicture= rotateImage(bitmap, 270);

                break;
            case 0:
           // if orientation is zero we don't need to rotate this 

            default:
                break;
        }
          //write your code here to save bitmap 
        }

    }

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

以下类用于从 byte[] 数据中获取方向。

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

【讨论】:

    【解决方案2】:

    我在三星设备上遇到过类似的问题,ExifInterface 似乎无法正常处理它们保存的图像。为了解决我使用 Glide 图像库中的代码的问题,它似乎可以正确处理检查原始图像旋转。

    查看此链接:Glide source

    getOrientation 方法似乎大部分时间都可以完成这项工作。

    【讨论】:

      【解决方案3】:

      对我来说,这听起来像是 Exif 标签的问题。基本上,现代相机以相同的方向保存图像,但也会保存一个标签,告诉您原始方向是什么。

      您可以使用与 java api 捆绑在一起的Exif Interface。我更喜欢Alessandro Crugnola's Android-Exif-Interface library,它不需要你保留文件路径

      我如何在项目中使用 Android-Exif-Interface:

      ExifInterface exif = new ExifInterface();
      Matrix matrix = new Matrix();
      try {
          exif.readExif(context.getContentResolver().openInputStream(fileUri), ExifInterface.Options.OPTION_ALL);
          ExifTag tag = exif.getTag(ExifInterface.TAG_ORIENTATION);
          int orientation = tag.getValueAsInt(1);
          switch (orientation) {
              case 3: /* 180° */
                  matrix.postRotate(180);
                  break;
              case 6: /*  90° */
                  matrix.postRotate(90);
                  break;
              case 8: /* 270° */
                  matrix.postRotate(-90);
                  break;
          }
      } catch (IOException e) {
          Log.i("INFO","expected behaviour: IOException");
          //not every picture comes from the phone, should that be the case,
          // we can't get exif tags anyway, since those aren't being transmitted
          // via http (atleast I think so. I'd need to save the picture on the SD card to
          // confirm that and I don't want to do that)
      } catch(NullPointerException e){
          Log.i("INFO","expected behaviour: NullPointerException");
          //same as above, not every picture comes from the phone
      }
      

      【讨论】:

      • 我尝试了更多代码,但每次我得到方向 0,所以我无法旋转图像,我在三星设备中遇到了这个问题。
      • 你能发布你用来获取方向的代码吗?没有它很难说太多
      • 我只能猜测问题出在 android.media.ExifInterface 上。我上面提到过,试试图书馆。当 android.media.ExifInterface 没有时,该库对我有用。我将稍微修改一下我的帖子以提供一个示例。
      • NullPointerException 发生在文件不存在(可能是错误的 Uri?)或提供的图像没有 exif 标签时。如果它没有 exif 标签,您可以简单地忽略 exif 标签并假设它具有标准旋转
      【解决方案4】:

      在许多情况下,pictureCallback() 会收到带有未定义方向标记的 Jpeg。但是您可以通过查看display rotation 或运行一个方向监听器来计算实际的设备方向,如Camera.takePicture returns a rotated byteArray

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多