Android開發 Camera2開發_2_預覽分辨率或拍照分辨率的計算

前言

  無論在Camera1或者Camera2在適配不一樣手機/不一樣使用場景的狀況下都須要計算攝像頭裏提供的分辨率列表中最合適的那一個分辨率.因此在須要大量機型適配的app,是不建議不通過計算直接自定義分辨率設置到預覽或者拍照照片中,有機率會由於攝像頭不支持你輸入的自定義分辨率致使報錯或者打不開攝像頭.算法

  若是你的確有需求要自定義分辨率,那麼使用場景只有一個那就是你是在開發Android設備,而且你輸入的自定義分辨率肯定在這個設備上不會報錯.數組

  目前本人總結的2個分辨率計算方法有2個:app

  •   求最佳比例正方形分辨率
  •   求最知足寬度的狀況下,在找到最接近高度的分辨率.

  下面我就來解釋這個2個計算方法.spa

求最佳比例正方形分辨率

  較爲歪門邪道的方法,核心就是TextureView的寬高比與攝像頭的高寬比作差值比較,注意這裏一個是寬高一個是高寬,求出來的結果就是在指定指定比例最接近正方形的分辨率code

  優勢:由於是正方形的分辨率,因此在預覽的時候無論是什麼尺寸的TextureView的都能顯示的不會變形.因此比較適合在小尺寸TextureView上orm

  缺點:在預覽的時候其實沒法徹底顯示完整(正方形無論怎麼樣都有可能上下或者左右超出View的大小),因此TextureView會自動忽略四周部分,只顯示最中間的部分.這樣拍照的時候就會發現預覽與實際照片顯示範圍不一致.blog

  /**
     * 獲取匹配的大小 這裏是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;
    }

求最知足寬度的狀況下,在找到最接近高度的分辨率

  這個是最正常的算法了,核心就是找到與屏幕寬度最接近的分辨率,而後在找最接近屏幕高度的分辨率.這裏是屏幕寬度是最高優先級的,其次在知足高度.開發

  優勢:預覽圖像與拍照照片的效果徹底一致.get

  缺點:it

  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  &&  判斷當前Size寬度小於當前屏幕高度
                    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){ //若是不等於null 說明已經找到了 跳出循環
                    break;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "getMatchingSize2: 選擇的分辨率寬度="+selectSize.getWidth());
        Log.e(TAG, "getMatchingSize2: 選擇的分辨率高度="+selectSize.getHeight());
        return selectSize;
    }
相關文章
相關標籤/搜索