11

I am developing a camera application for Android API 16 to 21 which main and only purpose is to take portrait photo. I am able to take picture with several devices (Nexus 4, Nexus 5, HTC...) and have them correctly oriented (meaning that my preview equals the taken picture both in size and orientation).

However I have tested my application on several other devices and some of them are giving me alot of trouble: Samsung Galaxy S3/S4/S5.

On these three devices, the preview is correctly displayed, however the pictures returned by the method onPictureTaken(final byte[] jpeg, Camera camera) are always sideways.

This is the Bitmap created from byte[] jpeg and displayed in the ImageView to my user just before saving it to the disk:

preview

And here is the image once saved on the disk:

disk

As you can see the image is completly stretched in the preview and wrongly rotated once saved on the disk.

Here is my CameraPreview class (I obfuscated other methods since they had nothing to do with camera parameters):

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

How come this exact piece of code works for other devices except Samsung's one ?

I tried to find answers on the following SO posts but nothing could help me so far: this one and this other one.

EDIT

Implementing Joey Chong's answer does not changes anything:

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

Here are the EXIF results I get for a working device:

  • Orientation: 0

And here the results for the S4:

  • Orientation: 0
4

4 回答 4

5

这是因为手机仍然以横向保存并将元数据放置为90度。您可以尝试检查 exif,在放入图像视图之前旋转位图。要检查 exif,请使用以下内容:

    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;
    }
于 2014-12-17T02:26:21.773 回答
3

我对保存的图像有类似的问题。

我使用了类似于用户 kinghsmit 在这里描述的内容https://github.com/googlesamples/android-vision/issues/124(2016 年 9 月 15 日的评论)。

我复制到这里,以防万一。

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

它对我有用,除了 Nexus 5x,但那是因为该设备由于其结构而存在一个特殊问题。

我希望这可以帮助你!

于 2017-12-28T15:18:01.523 回答
1

我使用了这个AndroidCameraUtil。它在这个问题上帮助了我很多。

于 2015-03-11T10:40:54.627 回答
0

您可以尝试使用相机参数来解决旋转问题。

Camera.Parameters parameters = camera.getParameters();
parameters.set("orientation", "portrait");
parameters.setRotation(90);
camera.setParameters(parameters);
于 2018-01-05T15:53:42.500 回答