Android Camera1参数设置

       谷歌推荐开发者不使用Camera1 API,使用Camera2作为相机开发的接口。但是我在一些老的项目中遇到Camera1,且还需在其基础上进行扩展。
       出于好奇心,且抱着一种学习的态度,我还是总结了一些Camera1的知识。
       本文主要介绍,打开相机后,如何设置相机相关的参数,如预览方向、照片方向等。在自己开发的过程中,这一部分耗时最多,因此,记录下来以便以后温习和使用。

目录

  • 1 基本知识
    • 1.1 设备方向
    • 1.2 相机方向
    • 1.3 获取当前设备方向
  • 2 设置预览方向
  • 3 设置照片方向
  • 4 设置最佳的预览尺寸
    • 4.1 获取设备支持的预览尺寸序列
    • 4.2 选取最优的预览尺寸
    • 4.3 设置预览尺寸
  • 5 设置最佳的照片尺寸
  • 6 设置对焦模式
  • 7 完成参数设置
  • 8 拍照后,部分机型的照片方向适配
    • 8.1 前置摄像头需要左右翻转
    • 8.2 部分机型需要再旋转
    • 8.3 整合代码

1 基本知识

1.1 设备方向

  当手机竖直时为默认的设备方向,此时设备方向为0°;
  若手机顺时针分别旋转90、180、270,此时的设备方向依次为90°、180°、270°。

1.2 相机方向

  当手机由默认方向顺时针旋转270°成横屏,后置相机的顶端在设备的右边缘,此时的状态即为相机的默认状态,方向为0°;
  相机默认方向与设备默认方向间的夹角为90°。

1.3 获取当前设备方向

  设备竖直时为默认的方向,旋转角度为0°;
  可以通过如下方法获取设备当前方向与默认方向间的夹角。

WindowManager wm = (WindowManager) ComponentContext.getContext()
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        int result = 0;
        try {
            Method m = display.getClass().getDeclaredMethod("getRotation");
            switch ((Integer) m.invoke(display)) {
                case Surface.ROTATION_0:
                    result = 0;
                    break;
                case Surface.ROTATION_90:
                    result = 90;
                    break;
                case Surface.ROTATION_180:
                    result = 180;
                    break;
                case Surface.ROTATION_270:
                    result = 270;
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            return result;
        }
        return result;

2 设置预览方向

  为了让相机预览画面与我们拿着手机时的朝向一致,需要将预览旋转一个角度;
  通过如下方法获取这个角度;

int degrees = 0;
  switch (screenRotation) {
    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 = 0;
  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;
  }
  return result;

 screenRotation是在1.3中获取的设备方向,info是与当前相机相关的CameraInfo对象,其获取方式如下:

Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);

  最后返回的结果result,将赋值给Camera,从而设置预览方向。

mCamera.setDisplayOrientation(result);

3 设置照片方向

为了让拍出来的照片与我们手持设备预览时看到的效果一致,我们需要设置,让照片旋转一个角度。
通过如下方法获取照片待设置的方向:

if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
      return (info.orientation - camRotation + 360) % 360;
  } else {
      return (info.orientation + camRotation) % 360;
  }

 其中info为上述所述的CameraInfo对象;camRotation即为1.3中获得的设备方向。
 得到了结果后,将其设置给相机:

Camera.Parameters parameters = mCamera.getParameters();
  //.....
parameters.setRotation(pictureRotation);

4 设置最佳的预览尺寸

4.1 获取设备支持的预览尺寸序列

通过如下方式即可获得:

List supportedPreviewSizes = parameters.getSupportedPreviewSizes(); 

通过debug发现,该List中的每个Size,其width都是大于height的。这也正好印证了,相机的默认方向是横屏的。

4.2 选取最优的预览尺寸

这里直接提供方法,并在注释中解释了方法的大致逻辑:

/**
   * 选取与width、height比例最接近的、设置支持的size
   * @param context
   * @param sizes 设置支持的size序列
   * @param w 相机预览视图的width
   * @param h 相机预览视图的height
   * @return
  */
  public static Camera.Size getOptimalSize(Context context, List sizes, int w, int h) {
      final double ASPECT_TOLERANCE = 0.1;//阈值,用于选取最优
      double targetRatio = -1;
      int orientation = context.getResources().getConfiguration().orientation;
      //保证targetRatio始终大于1,因为size.width/size.height始终大于1
      if (orientation == Configuration.ORIENTATION_PORTRAIT) {
          targetRatio = (double) h / w;
      } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
        targetRatio = (double) w / h;
      }
      Camera.Size optimalSize = null;
      double minDiff = Double.MAX_VALUE;
      int targetHeight = Math.min(w, h);
      for (Camera.Size size : sizes) {
          double ratio = (double) size.width / size.height;
          //若大于了阈值,则继续筛选
          if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
              continue;
          }
          if (Math.abs(size.height - targetHeight) < minDiff) {
              optimalSize = size;
              minDiff = Math.abs(size.height - targetHeight);
          }
      }
      //若通过比例没有获得最优,则通过最小差值获取最优,保证至少能得到值
      if (optimalSize == null) {
          minDiff = Double.MAX_VALUE;
          for (Camera.Size size : sizes) {
              if (Math.abs(size.height - targetHeight) < minDiff) {
                  optimalSize = size;
                  minDiff = Math.abs(size.height - targetHeight);
              }
          }
      }
      return optimalSize;
  }

4.3 设置预览尺寸 

parameters.setPreviewSize(optimizePreviewSize.width, optimizePreviewSize.height);

需要注意的是,预览view的宽高必须和设备支持的预览宽高相近或相等,否则会造成过度的拉伸、缩放;
如果view宽高很大,而预览尺寸很小,分辨率很低,预览质量会不好。
设置参数时,必须按照默认相机的方向,以Size.width和Size.height的秩序赋予数值。

5 设置最佳的照片尺寸

首先仍是获得设备支持的照片尺寸序列:

List supportedPictureSizes = parameters.getSupportedPictureSizes();

  然后通过#4.2中的方法,获得最佳的照片尺寸;
  最后设置到参数中:

parameters.setPictureSize(optimizePictureSize.width, optimizePictureSize.height);

6 设置对焦模式

首先获取设备支持的对焦模式:

  List supportedFocusMode = parameters.getSupportedFocusModes();

  遍历这些对焦模式,设置需要的模式:

for (String mode : supportedFocusMode) {
      if (mode.equals(Camera.Parameters.FOCUS_MODE_AUTO)) {
          parameters.setFocusMode(mode);
          break;
      }
  }

7 完成参数设置

  综上几步,完成了参数的设置;
  当然,若要实现更多的功能,还需要进行更多的设置。
  当你的参数设置完成后,需要调用如下方法,将参数写给相机:

  mCamera.setParameters(parameters);

若参数有设置不正确,或设备不支持该参数,会报出set parameter failed的提示。

8 拍照后,部分机型的照片方向适配 

拍照后,通过回调得到byte[]数组,存放着拍摄照片的字节流数据data;
此时,还不能直接存储照片,还需要做进一步的处理。

8.1 前置摄像头需要左右翻转

  Matrix m = new Matrix();
  if (mPreview.isFrontFace()) {//前置摄像头要水平翻转
      m.postScale(-1, 1);
  }

8.2 部分机型需要再旋转 

 String manufacturer = Build.MANUFACTURER;
  if (manufacturer != null && (manufacturer.equalsIgnoreCase("xiaomi") 
        || manufacturer.equals("samsung")
        || manufacturer.equals("sony"))) {//部分机型进行照片方向适配
          m.postRotate(90);
  }

8.3 整合代码

String path = "xxx";
  Matrix m = new Matrix();
  if (mPreview.isFrontFace()) {//前置摄像头要水平翻转
      m.postScale(-1, 1);
  }
  String manufacturer = Build.MANUFACTURER;
  if (manufacturer != null && (manufacturer.equalsIgnoreCase("xiaomi")
        || manufacturer.equals("samsung")
        || manufacturer.equals("sony"))) {//部分机型进行照片方向适配
          m.postRotate(90);
  }
  if (!m.isIdentity()) {//若设置了矩阵,要使用矩阵
       Bitmap tmp = ImageHelper.byteArray2Bitmap(data);
       Bitmap revisedBmp = Bitmap.createBitmap(tmp, 0, 0, tmp.getWidth(), tmp.getHeight(), m, true);
       data = ImageHelper.bitmap2ByteArray(revisedBmp);
  }
  boolean isSuccess = ImageHelper.saveJPGToSdcard(path, data);

 上述即为我整理的Camera1参数设置相关笔记,若有不正确的地方,希望大家多沟通交流。 

 

你可能感兴趣的:(Android)