1 Camera预览角度处理

开发Camera过程中会遇到Camera拍照,获取照片后可以上传照片或者展示给用户。
Camera的图像数据来源于摄像头硬件的图像传感器,这个图像传感器被固定到手机上后会有一个默认的方向,一般默认方向是当手机左侧横放时(手机横放并且手机顶部在左侧)。由于默认图片传感器为横向,大部分手机拍照则是竖向,所以得到的数据依然会是横向的,这时就需要对图片进行旋转。
图像传感器的取景方向与手机正常方向成90读夹角。

由于Camera默认是横向的,竖向拍照时得到的照片和预览的照片会有所不同,因为预览可以利用setDisplayOrientation设置预览角度调节预览图片,但是setDisplayOrientation只是改变了预览的角度,对于拍摄生成的图片依然会拿到原来的未被旋转和默认图片传感器方向相同的数据。而对于前置摄像头预览得到的图片会比后置摄像头多一个镜面效果,两者都需要对拍摄生成的图片进行旋转处理才能得到正常的符合眼睛所看到的预览图片的样式。

解决预览方向问题:setDisplayOrientation(int rotateDegree),默认情况下该方法的值为0,与图像传感器取景方向一致,竖向使用手机时只需要设置setDisplayOrientation(90)一般就可以解决预览问题
对于后置摄像头想要正常预览旋转角度应该设置为Camera默认角度减去屏幕朝向角度,这个值可能为负,角度值不能为负故需要加上360求正然后取余;对于前置摄像头拍照时可以明显发现预览图片是左右翻转的,也就是前置摄像头的预览成像是沿图像的中央垂直线翻转过来,形成镜面效果。所以前置摄像头求旋转角度为摄像头的默认角度加上手机屏幕旋转角度,之后进行镜面操作利用360度减去上面求到的值(翻转),然后加上360度(防止出现负数)之后对360度取余(预览是镜面的,但是最终拿到的data数据不是镜面翻转的,为保持和预览的一致性需要对图片进行镜面操作)。

如何计算setDisplayOrientation需要翻转的角度,每次重新预览或者SurfaceView变化都需要重新求角度:

public static int calculateCameraPreviewOrientation(Activity activity) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraID, 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;} else {result = (info.orientation - degrees + 360) % 360;}mOrientation = result;return result;
}

setDisplayOrientation 只对预览有作用,不会影响其他数据。
info.orientation默认为90度,屏幕竖屏默认为0。

如果不设置预览角度:

设置了预览角度

2 拍照生成图片角度处理

利用Camera拍照时,读取图片数据存储为图片,取到的数据直接来源于图像传感器采集到的图像数据,预览由于setDisplayOrientation 正常但原始处于依然没有变化,所以保存的图片不与预览时看到的画面方向一致,而是与图像传感器的方向一致。竖着拍照得到的照片看起来是横向的,当横着拍照时,得到的照片看起来才是正常的。前置摄像头的预览有镜面效果,但从图像传感器采集到的数据却没有镜面效果,所以生成的图片需要镜面操作。
后置摄像头得到的图片,未经任何处理:

前置摄像头得到的图片未经任何处理:

上面得到的图片是Activity设置了竖向显示,然后手机屏幕0度(手机顶部在上)状态进行拍照。如果拍照过程中旋转手机,此时可以发现预览图片还是正常的但是保存的图片却发生了旋转。上一部分文章解决了预览图片的旋转角度问题,当拍照过程中旋转,预览可以很好地处理的,但拍摄生成的图片还是从原始图像传感器获取,它的方向会随手机位置的变化(特别是方向)而变化,所以需要对手机屏幕方向进行实时监听,然后对保存图片做相应操作。

手机顶部朝右拍摄:

利用sensor就可以实现对手机方向的判断,由于手机可能在任意角度,但是传感器返回的数值多种多样,但是最终利用传感器获取到的屏幕方向只能是0,90,180,270四种,所以需要有个阈值来确定手机方向。

判断手机旋转角度:

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

保存图片时做的变换:

 {final Bitmap result;if (data != null && data.length > 0) {Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);Matrix matrix = new Matrix();
//利用传感器获取当前屏幕方向对应角度 加上 开始预览是角度int rotation = (calculateCameraPreviewOrientation(Main23Activity.this) + mSensorRotation) % 360 ;if (mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK) {//如果是后置摄像头因为没有镜面效果直接旋转特定角度matrix.setRotate(rotation);} else {//如果是前置摄像头需要做镜面操作,然后对图片做镜面postScale(-1, 1)//因为镜面效果需要360-rotation,才是前置摄像头真正的旋转角度rotation = (360 - rotation) % 360;matrix.setRotate(rotation);matrix.postScale(-1, 1);}result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);saveBitmap(path+ "focusdemo2.jpg",result);} else {result = null;}}
}/*** 设置预览角度,setDisplayOrientation本身只能改变预览的角度* previewFrameCallback以及拍摄出来的照片是不会发生改变的,拍摄出来的照片角度依旧不正常的* 拍摄的照片需要自行处理*/
public  int calculateCameraPreviewOrientation(Activity activity) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraID, 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;} else {result = (info.orientation - degrees + 360) % 360;}System.out.println("=======info.orientation==========="+info.orientation + degrees);mOrientation = result;return result;
}

PS:处理保存图片的旋转也有人利用图片的ExifInterface 信息,首先读取图片被旋转的角度,然后再进行相应旋转。
但测试发现很多手机获取到的图片的ExifInterface的旋转信息为0(因为只要我们对图片进行了处理,类似压缩,图片的旋转角度就会恢复到默认0度),所以还是要利用上面的方式处理。

3 利用上面的公式的实例代码

public class Main23Activity extends AppCompatActivity implements SurfaceHolder.Callback ,SensorEventListener{private static int mOrientation = 0;private static int mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;private CustomSurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private Camera mCamera;private boolean havePermission = false;private Button btnFocus;private Button btnTakePic;private Button btnRestar;private Button btnChange;private LinearLayout mContainer;private int useWidth;private int useHeight;private SensorManager mSensorManager;private Sensor mSensor;private int mSensorRotation = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main23);mContainer = findViewById(R.id.container);btnFocus = findViewById(R.id.focus);btnTakePic = findViewById(R.id.takepic);btnRestar = findViewById(R.id.restar);btnChange = findViewById(R.id.change);btnChange.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {switchCamera();}});btnRestar.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {/* if(mCamera != null){mCamera.startPreview();}*/// mSurfaceView.scrollBy(10,10);mContainer.animate().scaleX(0.4f).scaleY(0.7f);// mContainer.animate().rotation(50);}});btnFocus.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mCamera != null && mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK){mCamera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {if(success){Toast.makeText(Main23Activity.this,"对焦成功",Toast.LENGTH_SHORT).show();}else{}}});}}});btnTakePic.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(mCamera!= null){mCamera.takePicture(null, null, new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {// 获取Jpeg图片,并保存在sd卡上String path = Environment.getExternalStorageDirectory().getPath()  +"/focus/";File pathDir = new File(path);if (!pathDir.exists()){pathDir.mkdir();}File pictureFile = new File(path+ "focusdemo.jpg");File pictureFile2 = new File(path+ "focusdemo2.jpg");if (pictureFile.exists()){pictureFile.delete();}if (pictureFile2.exists()){pictureFile2.delete();}try {FileOutputStream fos = new FileOutputStream(pictureFile);fos.write(data);fos.close();} catch (Exception e) {}{final Bitmap result;if (data != null && data.length > 0) {Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);Matrix matrix = new Matrix();int rotation = (calculateCameraPreviewOrientation(Main23Activity.this) + mSensorRotation) % 360 ;if (mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK) {matrix.setRotate(rotation);} else {rotation = (360 - rotation) % 360;matrix.setRotate(rotation);matrix.postScale(-1, 1);}result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);saveBitmap(path+ "focusdemo2.jpg",result);} else {result = null;}}}});}}});mSensorManager = (SensorManager) Main23Activity.this.getSystemService(Activity.SENSOR_SERVICE);mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// 加速度// Android 6.0相机动态权限检查,省略了if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)== PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED) {havePermission = true;init();} else {havePermission = false;ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);}}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;}@Overrideprotected void onStop() {super.onStop();mSensorManager.unregisterListener(this,mSensor);}public void releaseCamera(){if (mCamera != null) {mSurfaceHolder.removeCallback(this);mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.lock();mCamera.release();mCamera = null;}}public void switchCamera(){if(mCameraID ==  Camera.CameraInfo.CAMERA_FACING_BACK){mCameraID =  Camera.CameraInfo.CAMERA_FACING_FRONT;}else{mCameraID =  Camera.CameraInfo.CAMERA_FACING_BACK;}try {initCamera();} catch (Exception e) {e.printStackTrace();}}public void init(){if(mSurfaceView == null){mSurfaceView = findViewById(R.id.surfaceview);mSurfaceView.setCustomEvent(new CustomSurfaceView.ONTouchEvent() {@Overridepublic void onTouchEvent(MotionEvent event) {handleFocus(event, mCamera);}});mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.addCallback(this);WindowManager wm = (WindowManager) Main23Activity.this.getSystemService(Context.WINDOW_SERVICE);int width = wm.getDefaultDisplay().getWidth();LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mSurfaceView.getLayoutParams();layoutParams.width = width;layoutParams.height = width*4/3;useWidth = width;useHeight = width*4/3;mSurfaceView.setLayoutParams(layoutParams);}}private void initCamera() {if (mCamera != null){releaseCamera();System.out.println("===================releaseCamera=============");}mCamera = Camera.open(mCameraID);System.out.println("===================openCamera=============");if (mCamera != null){try {mCamera.setPreviewDisplay(mSurfaceHolder);} catch (IOException e) {e.printStackTrace();}Camera.Parameters parameters = mCamera.getParameters();parameters.setRecordingHint(true);{//设置获取数据parameters.setPreviewFormat(ImageFormat.NV21);//parameters.setPreviewFormat(ImageFormat.YUV_420_888);//通过setPreviewCallback方法监听预览的回调:mCamera.setPreviewCallback(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] bytes, Camera camera) {//这里面的Bytes的数据就是NV21格式的数据,或者YUV_420_888的数据}});}if(mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK){parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);}mCamera.setParameters(parameters);calculateCameraPreviewOrientation(this);Camera.Size tempSize = setPreviewSize(mCamera, useHeight,useWidth);{//此处可以处理,获取到tempSize,如果tempSize和设置的SurfaceView的宽高冲突,重新设置SurfaceView的宽高}setPictureSize(mCamera,  useHeight,useWidth);mCamera.setDisplayOrientation(mOrientation);int degree = calculateCameraPreviewOrientation(Main23Activity.this);mCamera.setDisplayOrientation(degree);mCamera.startPreview();}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//当SurfaceView变化时也需要做相应操作,这里未做相应操作if (havePermission){initCamera();}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {mCamera.stopPreview();}private void setPictureSize(Camera camera ,int expectWidth,int expectHeight){Camera.Parameters parameters = camera.getParameters();Point point = new Point(expectWidth, expectHeight);Camera.Size size = findProperSize(point,parameters.getSupportedPreviewSizes());parameters.setPictureSize(size.width, size.height);camera.setParameters(parameters);}private Camera.Size setPreviewSize(Camera camera, int expectWidth, int expectHeight) {Camera.Parameters parameters = camera.getParameters();Point point = new Point(expectWidth, expectHeight);Camera.Size size = findProperSize(point,parameters.getSupportedPictureSizes());parameters.setPictureSize(size.width, size.height);camera.setParameters(parameters);return size;}/*** 找出最合适的尺寸,规则如下:* 1.将尺寸按比例分组,找出比例最接近屏幕比例的尺寸组* 2.在比例最接近的尺寸组中找出最接近屏幕尺寸且大于屏幕尺寸的尺寸* 3.如果没有找到,则忽略2中第二个条件再找一遍,应该是最合适的尺寸了*/private static Camera.Size findProperSize(Point surfaceSize, List<Camera.Size> sizeList) {if (surfaceSize.x <= 0 || surfaceSize.y <= 0 || sizeList == null) {return null;}int surfaceWidth = surfaceSize.x;int surfaceHeight = surfaceSize.y;List<List<Camera.Size>> ratioListList = new ArrayList<>();for (Camera.Size size : sizeList) {addRatioList(ratioListList, size);}final float surfaceRatio = (float) surfaceWidth / surfaceHeight;List<Camera.Size> bestRatioList = null;float ratioDiff = Float.MAX_VALUE;for (List<Camera.Size> ratioList : ratioListList) {float ratio = (float) ratioList.get(0).width / ratioList.get(0).height;float newRatioDiff = Math.abs(ratio - surfaceRatio);if (newRatioDiff < ratioDiff) {bestRatioList = ratioList;ratioDiff = newRatioDiff;}}Camera.Size bestSize = null;int diff = Integer.MAX_VALUE;assert bestRatioList != null;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (size.height >= surfaceHeight && newDiff < diff) {bestSize = size;diff = newDiff;}}if (bestSize != null) {return bestSize;}diff = Integer.MAX_VALUE;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (newDiff < diff) {bestSize = size;diff = newDiff;}}return bestSize;}private static void addRatioList(List<List<Camera.Size>> ratioListList, Camera.Size size) {float ratio = (float) size.width / size.height;for (List<Camera.Size> ratioList : ratioListList) {float mine = (float) ratioList.get(0).width / ratioList.get(0).height;if (ratio == mine) {ratioList.add(size);return;}}List<Camera.Size> ratioList = new ArrayList<>();ratioList.add(size);ratioListList.add(ratioList);}/*** 排序* @param list*/private static void sortList(List<Camera.Size> list) {Collections.sort(list, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size pre, Camera.Size after) {if (pre.width > after.width) {return 1;} else if (pre.width < after.width) {return -1;}return 0;}});}/*** 设置预览角度,setDisplayOrientation本身只能改变预览的角度* previewFrameCallback以及拍摄出来的照片是不会发生改变的,拍摄出来的照片角度依旧不正常的* 拍摄的照片需要自行处理*/public  int calculateCameraPreviewOrientation(Activity activity) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraID, 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;} else {result = (info.orientation - degrees + 360) % 360;}System.out.println("=======info.orientation==========="+info.orientation + degrees);mOrientation = result;return result;}@Overrideprotected void onResume() {super.onResume();mSensorManager.registerListener(this, mSensor,SensorManager.SENSOR_DELAY_NORMAL);if (havePermission && mCamera != null)mCamera.startPreview();}@Overrideprotected void onPause() {super.onPause();if (havePermission && mCamera != null)mCamera.stopPreview();}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode) {// 相机权限case 100:havePermission = true;init();break;}}/*** 转换对焦区域* 范围(-1000, -1000, 1000, 1000)*/private  Rect calculateTapArea(float x, float y,  int width, int height, float coefficient) {float focusAreaSize = 200;int areaSize = (int) (focusAreaSize * coefficient);int surfaceWidth = width;int surfaceHeight = height;int centerX = (int) (x / surfaceHeight * 2000 - 1000);int centerY = (int) (y / surfaceWidth * 2000 - 1000);int left = clamp(centerX - (areaSize / 2), -1000, 1000);int top = clamp(centerY - (areaSize / 2), -1000, 1000);int right = clamp(left + areaSize, -1000, 1000);int bottom = clamp(top + areaSize, -1000, 1000);return new Rect(left, top, right, bottom);}//不大于最大值,不小于最小值private  int clamp(int x, int min, int max) {if (x > max) {return max;}if (x < min) {return min;}return x;}private  void handleFocus(MotionEvent event, Camera camera) {int viewWidth = useWidth;int viewHeight = useHeight;Rect focusRect = calculateTapArea(event.getX(), event.getY(),  viewWidth, viewHeight,1.0f);//一定要首先取消camera.cancelAutoFocus();Camera.Parameters params = camera.getParameters();if (params.getMaxNumFocusAreas() > 0) {List<Camera.Area> focusAreas = new ArrayList<>();focusAreas.add(new Camera.Area(focusRect, 800));params.setFocusAreas(focusAreas);} else {//focus areas not supported}//首先保存原来的对焦模式,然后设置为macro,对焦回调后设置为保存的对焦模式final String currentFocusMode = params.getFocusMode();params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);camera.setParameters(params);camera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {//回调后 还原模式Camera.Parameters params = camera.getParameters();params.setFocusMode(currentFocusMode);camera.setParameters(params);if(success){Toast.makeText(Main23Activity.this,"对焦区域对焦成功",Toast.LENGTH_SHORT).show();}}});}@Overridepublic 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]);}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}public  void saveBitmap(String filePath ,Bitmap mBitmap) {File f = new File(filePath);FileOutputStream fOut = null;try {fOut = new FileOutputStream(f);} catch (FileNotFoundException e) {e.printStackTrace();}mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);try {fOut.flush();} catch (IOException e) {e.printStackTrace();}try {fOut.close();} catch (IOException e) {e.printStackTrace();}}
}

后置摄像头拍摄的原图和 处理后的图


前置摄像头拍摄的原图和处理后的图:

Android Camera预览角度和拍照保存图片角度学习相关推荐

  1. Android 相机预览方向和拍照方向

    我们知道手机 Camera 的图像数据都是来自于摄像头硬件的图像传感器(Image Sensor),这个 Sensor 被固定到手机之后是有一个默认的取景方向的,这个方向如下图所示,坐标原点位于手机横 ...

  2. Android camera预览流程

    前面已经简单介绍了,在Android系统中open camera的流程,但是,它又是怎么预览.怎么配置流,如何最终操作到camera HAL的呢.接下来以android原生相机应用,android9, ...

  3. Android Camera 预览、拍照、保存照片 实现

    和你一起终身学习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一.Camera2架构概述 二.Camera2中比较重要的类及方法 三.具体实现步骤 四.初始化相关参数 ...

  4. Android Camera 预览拉伸

    现象: 可以看到,相机预览拉伸严重,水杯都被拉伸很细长. 原因: 因为预览的控件(SurfaceView) 和相机输出的图像,宽高比不一致导致.我们需要统一控件的宽高比和相机输出的宽高比. 解决方法: ...

  5. Android Camera 预览拉伸问题

    预览会出现拉伸的问题代码: 预览拉伸Demo 预览画面拉伸问题改正的 gitee 代码在文章最后给出 预览拉伸的原因: 每颗Camera都支持一些分辨率,Camera的这些图像如果和预览画面的宽高比例 ...

  6. android camera预览帧,android camera2处理每一帧并显示其预览

    我使用 https://github.com/googlesamples/android-Camera2Basic,但尝试修改它,我可以访问每个帧,然后在surfaceview上绘制. 我明白了,我应 ...

  7. android camera 预览 数据流,camera,SurfaceView实时预览获取帧数据,在手机上总是闪退...

    请教各位关于用camera类做一个关于预览SurfaceView,然后通过 public void onPreviewFrame(byte[] data, Camera camera)获取data转换 ...

  8. android camera 预览 数据流,Android Camera预览过程数据流浅析

    硬件平台:Atmel SAMA5D3 SoC + OV2640 Camera Sensor Android版本:4.2.2 mediaserver进程是Camera Service的容器进程,它会动态 ...

  9. Android相机预览页面被压缩和拉伸问题

    最近公司要求在原有的项目中添加一个扫码登录的功能,在调试好相机之后,发现相机返回到Surfaceview页面上的预览图片,与我们现实中物品的比例并不相同,在一块正方形的Surfaceview中,预览界 ...

最新文章

  1. C# 类型实例化的语法糖--unity下诡异结果
  2. Ubuntu14.04 + Text-Detection-with-FRCN(CPU)
  3. 简单快速导出word文档
  4. python第k序列元素查找_Python寻找第k小的元素
  5. Python Selenium 基本配置
  6. 干得漂亮!法院在微信朋友圈精准投放“老赖”广告 网友:建议全国推广
  7. 数据:以太坊2.0合约余额新增1.16万ETH
  8. CASIA WebFace | face recognition data | 人脸识别数据集 | 云盘分享 |
  9. C++设计模式从入门到精通——实例说明
  10. 二叉树叶子结点个数统计
  11. python多线程爬取音标
  12. 一名大二废柴今后目标
  13. 分别用抽象类和接口实现四个动物类(鱼类、鸟类、爬行类、昆虫类)的类别和天赋
  14. 诈金花游戏单机版 附开源地址
  15. Kubernetes 管理员认证(CKA)考试笔记(一)
  16. mysql 数据表的复制
  17. 内涵TV段子,价值500元的dz内涵笑话商业源码
  18. 「医疗行业」DevExpress助力上海一院HIS系统稳定升级
  19. DWF整合spring异常:**ScopedProxyUtils.isScopedTarget
  20. 511遇见易语言API模块进程ID取窗口句柄

热门文章

  1. Python 脚本部署到服务器端每天定时自动给女票发送暖心情话和贴心提醒
  2. 5.8 2021年5月9日,是第108个母亲节
  3. 使用Subjects
  4. PyTorch深度学习60分钟入门与实战(四)训练分类器
  5. 三星S7edge 8.0最新版G9350ZCS5CTA1刷机线刷
  6. Java如何读取文件文本内容的几种方式汇总
  7. 1688、淘宝、京东多平台根据关键词取商品列表 API 返回值说明
  8. twitch 无法载入抽搐_抽搐数据科学家访谈
  9. Photoshop出现无法完成,因为内存不够(RAM),如何解决?
  10. 【测试表征】你想要的表征,这里全都有!(二)