照片显示的角度需要转换
开发Camera过程中会遇到Camera拍照,获取照片后可以上传照片或者展示给用户。
Camera的图像数据来源于摄像头硬件的图像传感器,这个图像传感器被固定到手机上后会有一个默认的方向,一般默认方向是当手机左侧横放时(手机横放并且手机顶部在左侧)。由于默认图片传感器为横向,大部分手机拍照则是竖向,所以得到的数据依然会是横向的,这时就需要对图片进行旋转。
图像传感器的取景方向与手机正常方向成90读夹角。
由于Camera默认是横向的,竖向拍照时得到的照片和预览的照片会有所不同,因为预览可以利用setDisplayOrientation设置预览角度调节预览图片,但是setDisplayOrientation只是改变了预览的角度,对于拍摄生成的图片依然会拿到原来的未被旋转和默认图片传感器方向相同的数据。而对于前置摄像头预览得到的图片会比后置摄像头多一个镜面效果,两者都需要对拍摄生成的图片进行旋转处理才能得到正常的符合眼睛所看到的预览图片的样式。
0. 获取当前设备的旋转角度
public Classs Activity{
private OrientationEventListener mOrientationListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mOrientationListener = new CameraOrientationEventListener(getApplicationContext());
}
@Override
protected void onResume() {
super.onResume();
mOrientationListener.enable();
}
@Override
protected void onPause() {
super.onPause();
mOrientationListener.disable();
}
private class CameraOrientationEventListener extends OrientationEventListener {
CameraOrientationEventListener(Context context) {
super(context);
}
@Override
public void onOrientationChanged(int orientation) {
// orientation ( 0- 360)
// newOrientation (0, 90, 180, 270)
int newOrientation = roundOrientation(orientation, mOrientation);
if (newOrientation == mOrientation) {
return;
}
mOrientation = newOrientation;
int deviceRotation = getWindowManager().getDefaultDisplay().getRotation();
Log.d(TAG, "onOrientationChanged newOrientation:" + orientation + ", nowOrientation: " +
mOrientation + ", deviceRotation:" + deviceRotation);
}
}
//将 当前设备旋转角度 转换 ( 0- 360) ---> (0, 90, 180, 270)
public int roundOrientation(int orientation, int orientationHistory) {
boolean changeOrientation ;
if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
changeOrientation = true;
} else {
int dist = Math.abs(orientation - orientationHistory);
dist = Math.min(dist, 360 - dist);
changeOrientation = (dist >= 45 + 5);
}
if (changeOrientation) {
return ((orientation + 45) / 90 * 90) % 360;
}
return orientationHistory;
}
}
//获取显示角度
int getDisplayOrientation(){
return deviceSensorToDisplayRotation();
}
// 将 设置当前角度 与 sensor 角度计算转换,生成最终display角度
public int deviceSensorToDisplayRotation(CameraCharacteristics mCharacteristics) {
int orientation ;
sensorOrientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
if (mCharacteristics.get(CameraCharacteristics.LENS_FACING)
== CameraCharacteristics.LENS_FACING_FRONT) {
orientation = (sensorOrientation - mOrientation + 360) % 360;
isBackCamera = false;
} else {
orientation = (sensorOrientation + mOrientation) % 360;
isBackCamera = true;
}
int windowRotation = getWindowManager().getDefaultDisplay().getRotation();
int windowOrientation = ORIENTATIONS.get(windowRotation);
Log.d(TAG, "deviceSensorToDisplayRotation "
+ " windowRotation:" + windowRotation
+ ", windowOrientation:" + windowOrientation
+ ", deviceOrientation:" + mOrientation
+ ", sensorOrientation" + sensorOrientation
+ ", displayOrientation:" + orientation);
return orientation;
}
------ 另外一种获取当前设备旋转角度方式(不是很推荐,建议用 0)-----------------------
@Override
public void onSensorChanged(SensorEvent event) {
//手机移动一段时间后静止,然后静止一段时间后进行对焦
// 读取加速度传感器数值,values数组0,1,2分别对应x,y,z轴的加速度
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
int x = (int) event.values[0];
int y = (int) event.values[1];
int z = (int) event.values[2];
}
mSensorRotation = calculateSensorRotation(event.values[0],event.values[1]);
}
public int calculateSensorRotation(float x, float y) {
//x是values[0]的值,X轴方向加速度,从左侧向右侧移动,values[0]为负值;从右向左移动,values[0]为正值
//y是values[1]的值,Y轴方向加速度,从上到下移动,values[1]为负值;从下往上移动,values[1]为正值
//不考虑Z轴上的数据,
if (Math.abs(x) > 6 && Math.abs(y) < 4) {
if (x > 6) {
return 270;
} else {
return 90;
}
} else if (Math.abs(y) > 6 && Math.abs(x) < 4) {
if (y > 6) {
return 0;
} else {
return 180;
}
}
return -1;
}
*********************** 官方提供的JPEG图片方向算法 ***************
private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
if (deviceOrientation == OrientationEventListener.ORIENTATION_UNKNOWN){
return 0;
}
int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);//获取传感器方向
// Round device orientation to a multiple of 90
deviceOrientation = (deviceOrientation + 45) / 90 * 90;
// Reverse device orientation for front-facing cameras
boolean facingFront = c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;//判断摄像头面向
if (facingFront) {
deviceOrientation = -deviceOrientation;
}
// Calculate desired JPEG orientation relative to camera orientation to make
// the image upright relative to the device orientation
int jpegOrientation = (sensorOrientation + deviceOrientation + 360) % 360;
return jpegOrientation;
}
1. JPEG 拍照的角度设置, 部分手机可能不管用
final CaptureRequest.Builder snapShotCaptureBuilder =
mOneCamera.getDisplayOrientation(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
getSupportedThumbnailSizes();
//设置 JPEG_ORIENTATION
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
getDisplayOrientation(mOrientation));
2. 视频的角度设置
int orientation = getDisplayOrientation(mOrientation);
mMediaRecorder.setOrientationHint(orientation);
3. af ae 角度设置
4. 保存照片的角度Exif 设置
int orientation = mOneCamera.getDisplayOrientation(mOrientation);
ExifInterface exif = onProcessCaptureExifTags(bytes, location, orientation, mTotalCaptureResult, false);
ExifInterface onProcessCaptureExifTags(byte[] bytes, Location location, int orientation, TotalCaptureResult result, Boolean mForceFlashOn) {
ExifInterface exif;
if(bytes == null){
exif = new ExifInterface();
}else{
exif = Exif.getExif(bytes);
}
exif.addMakeAndModelTag();
//add to exif
exif.addOrientationTag(orientation);
}
4.1 可能有些图片经过变化(裁剪,缩放等,exif中的值会变化), 可以在保存图片时,对图片本身数据进行旋转。
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix()
matrix.setRotate(rotation); //rotation 和 exif中写入的一样
matrix.postScale(-1,1);
result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
5. 设置媒体库 Media中的角度
public static ContentValues getContentValuesForData(String title,
long date, Location location, int orientation, int jpegLength,
String path, int width, int height, String mimeType,long burstShotId, int fileType) {
// Insert into MediaStore.
ContentValues values = new ContentValues();
values.put(ImageColumns.TITLE, title);
if (mimeType.equalsIgnoreCase("jpeg") ||
mimeType.equalsIgnoreCase("image/jpeg")) {
values.put(ImageColumns.DISPLAY_NAME, title + ".jpg");
} else {
values.put(ImageColumns.DISPLAY_NAME, title + ".raw");
}
values.put(ImageColumns.DATE_TAKEN, date);
values.put(ImageColumns.MIME_TYPE, "image/jpeg");
// Clockwise rotation in degrees. 0, 90, 180, or 270.
values.put(ImageColumns.ORIENTATION, orientation);
values.put(ImageColumns.DATA, path);
values.put(ImageColumns.SIZE, jpegLength);
}
6.算法设置角度
设置
setSensorOrientation(mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) )
使用
@Override
public void setSensorOrientation(int orientation) {
mOrientation = orientation;
}
BeaurifyJniSdk.preViewInstance().nativeReset(mTextureWidth,mTextureHeight,mOrientation);