Android Camera开发如何适配旋转

2,008 阅读5分钟

相机捕获的画面方向和手机的方向不一致,比如手机是竖着拿的,但是画面是横的,因为摄像头默认捕获的画面byte[]是根据横向来的,而你的应用是竖向的,并且捕获画面的默认方向是依赖于相机的安装方向的。

不同的android设备相机安装的方向存在差异,这里需要引入一个相机默认方向的概念

相机默认方向

image.png

/**
 * <p>The orientation of the camera image. The value is the angle that the
 * camera image needs to be rotated clockwise so it shows correctly on
 * the display in its natural orientation. It should be 0, 90, 180, or 270.</p>
 *
 * <p>For example, suppose a device has a naturally tall screen. The
 * back-facing camera sensor is mounted in landscape. You are looking at
 * the screen. If the top side of the camera sensor is aligned with the
 * right edge of the screen in natural orientation, the value should be
 * 90. If the top side of a front-facing camera sensor is aligned with
 * the right of the screen, the value should be 270.</p>
 *
 * @see #setDisplayOrientation(int)
 * @see Parameters#setRotation(int)
 * @see Parameters#setPreviewSize(int, int)
 * @see Parameters#setPictureSize(int, int)
 * @see Parameters#setJpegThumbnailSize(int, int)
 */
public int orientation;

android.hardware.Camera.CameraInfo.orientaion

  • 是指相机图像需要顺时针旋转的角度,以便在其自然方向上正确显示
  • 它的值应该是0 、90、180 或 270其中之一。
  • 举例说明:假设设备具有自然高屏幕,设备背面的相机横向安装,查看屏幕,如果相机传感器的顶侧与屏幕的右边缘对齐,则该值应为90;如果正面相机传感器的顶侧与屏幕右侧对齐,则值应 是270。

预览显示适配

GOOGLE建议的方法:

public static void setCameraDisplayOrientation(Activity activity,
        int cameraId, android.hardware.Camera camera) {
    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    int rotation = activity.getWindowManager().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 result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;  // compensate the mirror
    } else {  // back-facing
        result = (info.orientation - degrees + 360) % 360;
    }
    camera.setDisplayOrientation(result);
}

android.view.Display.getRotation

这个方法返回屏幕在 "自然 "方向(我理解为设备默认使用朝向)上的旋转情况。

  • 返回的值可能是 :Surface#ROTATION_0(不旋转)、Surface#ROTATION_90 Surface#ROTATION_180 、Surface#ROTATION_270

  • 例如,如果一个设备有一个竖向的屏幕,用户将横向旋转,那么这里返回的值可能是Surface#ROTATION_270也可能是Surface#ROTATION_90,这取决于它被转动的方向。

  • 这个角度是指屏幕上所画图形的旋转,它与设备的物理旋转方向相反。例如,如果设备被逆时针旋转了90度,为了补偿,渲染将被顺时针旋转90度,因此这里的返回值将是90度,即Surface.ROTATION_90

android.hardware.Camera.setDisplayOrientation

以度为单位设置预览显示的顺时针旋转。不影响图片的保存数据

这个方法一定要在camera.setPreviewDisplay(surfaceHolder)这个之后,启动相机预览之前调用。

拍照保存图片适配

一般有两种方法: 1、使用setRotation直接旋转拍照帧 2、读取拍照帧的exif中的orientation信息进行旋转

android.hardware.Camera.Parameters.setRotation

这个方法用于设置相机保存图像的旋转角度,他不影响预览画面的旋转角度

官方推荐的写法:

public void onOrientationChanged(int orientation) {
    if (orientation == ORIENTATION_UNKNOWN) {
        return;
    }
    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    orientation = (orientation + 45) / 90 * 90;
    int rotation = 0;
    if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
        rotation = (info.orientation - orientation + 360) % 360;
    } else {  // back-facing camera
        rotation = (info.orientation + orientation) % 360;
    }
    mParameters.setRotation(rotation);
}

ExifInterface.TAG_ORIENTATION

一些android机型Camera拍摄帧本身不进行旋转,而将旋转角度写入exif中。

EXIF:可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif), 是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。

只有JPEG格式的图片才会携带exif数据,像PNG,WebP这类的图片就不会有这些数据。

ExifTool Version Number         : 10.80
File Name                       : test.jpg
File Size                       : 2.6 MB
File Modification Date/Time     : 2021:07:28 11:41:45+08:00
File Access Date/Time           : 2021:07:28 11:41:45+08:00
File Inode Change Date/Time     : 2021:07:28 11:41:45+08:00
File Permissions                : rw-------
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Big-endian (Motorola, MM)
Make                            : XXX
Camera Model Name               : XXX
Orientation                     : Rotate 90 CW
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Modify Date                     : 2021:07:28 11:41:45
Y Cb Cr Positioning             : Centered
Exposure Time                   : 1/50
F Number                        : 2.2
Exposure Program                : Not Defined
ISO                             : 129
Exif Version                    : 0220
Date/Time Original              : 2021:07:28 11:41:45
Create Date                     : 2021:07:28 11:41:45
Components Configuration        : Y, Cb, Cr, -
Shutter Speed Value             : 1/50
Aperture Value                  : 2.2
Brightness Value                : 0
Exposure Compensation           : 0
Max Aperture Value              : 2.2
Metering Mode                   : Average
Light Source                    : D65
Flash                           : Off, Did not fire
Focal Length                    : 3.8 mm
Sub Sec Time                    : 790011
Sub Sec Time Original           : 790011
Sub Sec Time Digitized          : 790011
Flashpix Version                : 0100
Color Space                     : sRGB
Exif Image Width                : 4208
Exif Image Height               : 3120
Interoperability Index          : R98 - DCF basic file (sRGB)
Interoperability Version        : 0100
Sensing Method                  : Unknown (0)
Scene Type                      : Unknown (0)
Exposure Mode                   : Auto
White Balance                   : Auto
Focal Length In 35mm Format     : 0 mm
Scene Capture Type              : Standard
Compression                     : JPEG (old-style)
Thumbnail Offset                : 898
Thumbnail Length                : 15963
Image Width                     : 4208
Image Height                    : 3120
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Aperture                        : 2.2
Image Size                      : 4208x3120
Megapixels                      : 13.1
Shutter Speed                   : 1/50
Create Date                     : 2021:07:28 11:41:45.790011
Date/Time Original              : 2021:07:28 11:41:45.790011
Modify Date                     : 2021:07:28 11:41:45.790011
Thumbnail Image                 : (Binary data 15963 bytes, use -b option to extract)
Focal Length                    : 3.8 mm
Light Value                     : 7.6

这样就可以通过从exif中读取orientation信息,再对图片进行旋转:

Matrix mat = new Matrix();
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
ExifInterface ei = new ExifInterface(path);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
    case ExifInterface.ORIENTATION_ROTATE_90:
        mat.postRotate(90);
        break;
    case ExifInterface.ORIENTATION_ROTATE_180:
        mat.postRotate(180);
        break;
}
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mat, true);