5

我正在使用视觉 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 中发现了这个问题,有什么解决方案吗?

4

4 回答 4

6

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

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;
}
 }
于 2016-09-15T12:09:41.490 回答
1

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

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

我如何在项目中使用 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
}
于 2016-09-12T05:45:55.847 回答
1

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

查看此链接:Glide 源

getOrientation从那里开始的方法似乎大部分时间都可以完成这项工作。

于 2016-09-12T09:49:12.983 回答
0

在许多情况下,pictureCallback()接收一个带有未定义方向标签的 Jpeg。但是您可以通过查看显示旋转或运行方向侦听器来计算实际的设备方向,因为Camera.takePicture 返回一个旋转的 byteArray

于 2020-08-16T19:13:09.707 回答