Android开发 Camera2开发_2_预览分辨率或拍照分辨率的计算

前言

  不管在Camera1或者Camera2在适配不同手机/不同使用场景的情况下都需要计算摄像头里提供的分辨率列表中最合适的那一个分辨率.所以在需要大量机型适配的app,是不建议不经过计算直接自定义分辨率设置到预览或者拍照照片中,有概率会因为摄像头不支持你输入的自定义分辨率导致报错或者打不开摄像头.

  如果你的确有需求要自定义分辨率,那么使用场景只有一个那就是你是在开发Android设备,并且你输入的自定义分辨率确定在这个设备上不会报错.

  目前本人总结的2个分辨率计算方法有2个:

  •   求最佳比例正方形分辨率
  •   求最满足宽度的情况下,在找到最接近高度的分辨率.

  下面我就来解释这个2个计算方法.

求最佳比例正方形分辨率

  较为歪门邪道的方法,核心就是TextureView的宽高比与摄像头的高宽比做差值比较,注意这里一个是宽高一个是高宽,求出来的结果就是在指定指定比例最接近正方形的分辨率

  优点:因为是正方形的分辨率,所以在预览的时候不管是什么尺寸的TextureView的都能显示的不会变形.所以比较适合在小尺寸TextureView上

  缺点:在预览的时候其实无法完全显示完整(正方形不管怎么样都有可能上下或者左右超出View的大小),所以TextureView会自动忽略四周部分,只显示最中间的部分.这样拍照的时候就会发现预览与实际照片显示范围不一致.

  /**
     * 获取匹配的大小 这里是Camera2获取分辨率数组的方式,Camera1获取不同,计算一样
     * @return
     */
    private Size getMatchingSize(){
            Size selectSize = null;
            float selectProportion = 0;
        try {
            float viewProportion = (float)mTextureView.getWidth() / (float)mTextureView.getHeight();//计算View的宽高比
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId);
            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
            for (int i = 0; i < sizes.length; i++){
                Size itemSize = sizes[i];
                float itemSizeProportion = (float)itemSize.getHeight() / (float)itemSize.getWidth();//计算当前分辨率的高宽比
                float differenceProportion = Math.abs(viewProportion - itemSizeProportion);//求绝对值
                Log.e(TAG, "相减差值比例="+differenceProportion );
                if (i == 0){
                    selectSize = itemSize;
                    selectProportion = differenceProportion;
                    continue;
                }
                if (differenceProportion <= selectProportion){ //判断差值是不是比之前的选择的差值更小
                    if (differenceProportion == selectProportion){ //如果差值与之前选择的差值一样
                        if (selectSize.getWidth() + selectSize.getHeight() < itemSize.getWidth() + itemSize.getHeight()){//选择分辨率更大的Size
                            selectSize = itemSize;
                            selectProportion = differenceProportion;
                        }

                    }else { 
                        selectSize = itemSize;
                        selectProportion = differenceProportion;
                    }
                }
            }

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "getMatchingSize: 选择的比例是="+selectProportion);
        Log.e(TAG, "getMatchingSize: 选择的尺寸是 宽度="+selectSize.getWidth()+"高度="+selectSize.getHeight());
        return selectSize;
    }

求最满足宽度的情况下,在找到最接近高度的分辨率

  这个是最正常的算法了,核心就是找到与屏幕宽度最接近的分辨率,然后在找最接近屏幕高度的分辨率.这里是屏幕宽度是最高优先级的,其次在满足高度.

  优点:预览图像与拍照照片的效果完全一致.

  缺点:

  1.   只能满足全屏幕预览的拍照情况下,各种奇葩自定义大小的TextureView你基本上不可能找到满足的宽度的分辨率.
  2.   因为需要让TextureView的高度跟随分辨率高度,所以预览的上面或者下面可能会有需要留出空白区域的情况.(可以用黑色背景View填充),全屏预览的时候可以忽略这个情况,因为基本上手机的摄像头都会有一个分辨率刚好与屏幕分辨率一致.但是不排除个别奇葩手机

  在看代码前这里说明一个重要知识!摄像头分辨率的宽度和高度与屏幕分辨率的宽度和高度的对应

  1.摄像头分辨率的宽度和高度其实是手机横屏下的才是正确方向.如下图所示

  

  2.屏幕的分辨率的宽度和高度依然是手机竖屏下的高度和宽度.

  

代码部分

    private Size getMatchingSize2(){
        Size selectSize = null;
        try {
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId);
            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
            DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); //因为我这里是将预览铺满屏幕,所以直接获取屏幕分辨率
            int deviceWidth = displayMetrics.widthPixels; //屏幕分辨率宽
            int deviceHeigh = displayMetrics.heightPixels; //屏幕分辨率高
            Log.e(TAG, "getMatchingSize2: 屏幕密度宽度="+deviceWidth);
            Log.e(TAG, "getMatchingSize2: 屏幕密度高度="+deviceHeigh );
            /**
             * 循环40次,让宽度范围从最小逐步增加,找到最符合屏幕宽度的分辨率,
             * 你要是不放心那就增加循环,肯定会找到一个分辨率,不会出现此方法返回一个null的Size的情况
             * ,但是循环越大后获取的分辨率就越不匹配
             */
            for (int j = 1; j < 41; j++) {
                for (int i = 0; i < sizes.length; i++) { //遍历所有Size
                    Size itemSize = sizes[i];
                    Log.e(TAG,"当前itemSize 宽="+itemSize.getWidth()+"高="+itemSize.getHeight());
                    //判断当前Size高度小于屏幕宽度+j*5  &&  判断当前Size高度大于屏幕宽度-j*5
                    if (itemSize.getHeight() < (deviceWidth + j*5) && itemSize.getHeight() > (deviceWidth - j*5)) {
                        if (selectSize != null){ //如果之前已经找到一个匹配的宽度
                            if (Math.abs(deviceHeigh-itemSize.getWidth()) < Math.abs(deviceHeigh - selectSize.getWidth())){ //求绝对值算出最接近设备高度的尺寸
                                selectSize = itemSize;
                                continue;
                            }
                        }else {
                            selectSize = itemSize;
                        }

                    }
                }
                /**
                 * 因为需要满足宽度,所以原则上如果没有必要,首次找到最接近宽度屏幕的摄像头高度后就跳出循环
                 */
                if(selectSize != null){
             Log.e(TAG, "getMatchingSize2: 选择的分辨率宽度="+selectSize.getWidth());
             Log.e(TAG, "getMatchingSize2: 选择的分辨率高度="+selectSize.getHeight());
return selectSize; } } } catch (CameraAccessException e) { e.printStackTrace(); } Log.e(TAG, "getMatchingSize2: 选择的分辨率宽度="+selectSize.getWidth()); Log.e(TAG, "getMatchingSize2: 选择的分辨率高度="+selectSize.getHeight()); return selectSize; }

 

原文地址:https://www.cnblogs.com/guanxinjing/p/10943966.html