相机捕获的画面方向和手机的方向不一致,比如手机是竖着拿的,但是画面是横的,因为摄像头默认捕获的画面byte[]是根据横向来的,而你的应用是竖向的,并且捕获画面的默认方向是依赖于相机的安装方向的。
不同的android设备相机安装的方向存在差异,这里需要引入一个相机默认方向的概念
相机默认方向
/**
* <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);