Camera開發(fā)相關(guān)知識(shí)點(diǎn)整理

目錄

  1. 相機(jī)開發(fā)的基本步驟
  2. 相機(jī)開發(fā)中的一些配置
  3. 相機(jī)功能設(shè)置
  4. 使用FaceDetector識(shí)別人臉
  5. 幾個(gè)重要的方法

Camera開發(fā)github地址

1. 相機(jī)開發(fā)的基本步驟

  1. 聲明硬件特性
  2. 聲明需要的權(quán)限和動(dòng)態(tài)請(qǐng)求權(quán)限
  3. 在布局中添加預(yù)覽類和設(shè)置監(jiān)聽器
  4. 打開相機(jī)和預(yù)覽
  5. 拍照
  6. 釋放相機(jī)

1.1 聲明硬件特性

在清單文件中聲明硬件特性

<uses-feature android:name="android.hardware.camera" />

1.2 聲明需要的權(quán)限和動(dòng)態(tài)請(qǐng)求權(quán)限

聲明相機(jī)和讀寫外部存儲(chǔ)的權(quán)限

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

動(dòng)態(tài)申請(qǐng)權(quán)限

private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
  

 private void checkPerission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 檢查該權(quán)限是否已經(jīng)獲取
            for (int i = 0; i < permissions.length; i++) {
                int permission = ContextCompat.checkSelfPermission(getApplicationContext(), permissions[i]);
                // 權(quán)限是否已經(jīng) 授權(quán) GRANTED---授權(quán)  DINIED---拒絕
                if (permission != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(this, permissions, 321);
                    break;
                }
            }
        }
    }  

1.3 在布局中添加預(yù)覽類和設(shè)置監(jiān)聽器

在布局中添加SurfaceView作為相機(jī)的預(yù)覽類

<SurfaceView
    android:id="@+id/surfaceView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

設(shè)置SurfaceView的狀態(tài)回調(diào)

private void initSurfaceView() {
        mHolder = mBinding.surfaceView.getHolder();
        mHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder holder) {
            //surface創(chuàng)建的時(shí)候回調(diào)
                //打開相機(jī)
                mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;
                mCamera = Camera.open(mCameraID);
                setCallback();
            }
        
            @Override
            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
            //surface改變的時(shí)候回調(diào)
                mCamera.stopPreview();
                setCamera();
            }
        
            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
            //surface銷毀的時(shí)候回調(diào)
                releaseCamera();
            }
        });
        }

一般在surface創(chuàng)建的時(shí)候打開相機(jī)預(yù)覽,在surface被銷毀的時(shí)候釋放相機(jī),當(dāng)surface發(fā)生變化的時(shí)候需要關(guān)閉預(yù)覽,重新設(shè)置相機(jī)并打開預(yù)覽

1.4 打開相機(jī)和預(yù)覽

打開前置攝像頭,并開啟預(yù)覽

mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;
mCamera = Camera.open(mCameraID);
//啟動(dòng)預(yù)覽
mCamera.startPreview();                

1.5 拍照

mCamera.takePicture(new Camera.ShutterCallback() {
                @Override
                public void onShutter() {
                    //快門回調(diào)發(fā)生在圖像捕獲之后
                    Log.d("testtakepicture", "onShutter");
                }
            }, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    //當(dāng)原始圖像數(shù)據(jù)可用時(shí),會(huì)發(fā)生原始回調(diào)
                    Log.d("testtakepicture", "onPictureTaken1:" + data);
                }
            }, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    //當(dāng)一個(gè)縮放的、完全處理過的postview圖像可用時(shí),會(huì)發(fā)生postview回調(diào)
                    Log.d("testtakepicture", "onPictureTaken2:" + data);
                }
            }, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    //當(dāng)壓縮后的圖像可用時(shí),會(huì)發(fā)生jpeg回調(diào)。
                    Log.d("testtakepicture", "onPictureTaken3:" + data);
                }
            });

1.6 釋放相機(jī)

  private void releaseCamera() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);
            mCamera.setFaceDetectionListener(null);
            mCamera.release();
            mCamera = null;
        }
    }

2. 相機(jī)開發(fā)中的一些配置

  1. 預(yù)覽旋轉(zhuǎn)角度設(shè)置
  2. 設(shè)置合適的預(yù)覽大小
  3. 設(shè)置合適的圖片大小
  4. 保存圖片

2.1 預(yù)覽旋轉(zhuǎn)角度設(shè)置

相機(jī)默認(rèn)的預(yù)覽方向和我們期待的預(yù)覽方向不一致,需要經(jīng)過一個(gè)轉(zhuǎn)換之后才能得到正確的預(yù)覽效果。不過這個(gè)預(yù)覽旋轉(zhuǎn)角度的值,官網(wǎng)給出了計(jì)算公式Camera.setDisplayOrientation(int orientation)

 public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, 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;  // compensate the mirror
     } else {  // back-facing
         result = (info.orientation - degrees + 360) % 360;
     }
     camera.setDisplayOrientation(result);
 }
 

2.2 設(shè)置合適的預(yù)覽大小

每一個(gè)相機(jī)有一個(gè)它支持的預(yù)覽大小的列表,我們給相機(jī)設(shè)計(jì)預(yù)覽大小的時(shí)候,只能從這個(gè)列表中選擇。一般選擇和屏幕寬高比較為接近的分辨率適當(dāng)?shù)念A(yù)覽大小,然后再對(duì)SurfaceView的大小做一個(gè)調(diào)整,以保證預(yù)覽不變形。

2.3 設(shè)置合適的圖片大小

一般根據(jù)預(yù)覽的大小,選擇一個(gè)和預(yù)覽比例一致的,分辨率最大的的大小作為圖片的大小。

2.4 保存圖片

拍照中的jpeg圖片、預(yù)覽回調(diào)圖片的方向和相機(jī)傳感器的視角一致,不受Camera.setDisplayOrientation(int orientation)的影響。預(yù)覽回調(diào)的方向和傳感器一致,不過在前置攝像頭下,默認(rèn)做了水平翻轉(zhuǎn),以便讓前置攝像頭看起來成鏡像。所以我們?cè)诒4鎴D片的時(shí)候,后置攝像頭的圖片我們直接按照“預(yù)覽旋轉(zhuǎn)角度設(shè)置”處理即可,而前置攝像頭的圖片先做一個(gè)水平鏡像,再按照“預(yù)覽旋轉(zhuǎn)角度設(shè)置”處理即可。

public static Matrix getPicMatrix(Activity activity, int cameraId) {
        Matrix matrix = new Matrix();
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotate = getCameraDisplayOrientation(activity, cameraId);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //預(yù)覽回調(diào)圖片和預(yù)覽圖像之間左右鏡像,所以可以先做鏡像,再按預(yù)覽圖像的旋轉(zhuǎn)角度旋轉(zhuǎn)
            matrix.postScale(-1f, 1f);
            matrix.postRotate(rotate);
        } else {
            matrix.postRotate(rotate);
        }
        return matrix;
    }

應(yīng)用矩陣,轉(zhuǎn)換bitmap

Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = CameraUtils.getPicMatrix(CameraActivity3.this, mCameraID);
Bitmap destBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

3. 相機(jī)功能設(shè)置

  1. 設(shè)置對(duì)焦模式
  2. 啟動(dòng)人臉檢測(cè)
  3. 手動(dòng)設(shè)置測(cè)光區(qū)域和對(duì)焦區(qū)域
  4. 曝光設(shè)置

3.1 設(shè)置對(duì)焦模式

拍照的時(shí)候,我們優(yōu)先使用Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE。設(shè)置之前需要先判斷相機(jī)支不支持這個(gè)對(duì)焦模式,根據(jù)parameters.getSupportedFocusModes()中是否包含要設(shè)置的模式來判斷相機(jī)是否支持這個(gè)對(duì)焦模式。設(shè)置對(duì)焦模式:

parameters.setFocusMode(mode)

常用的對(duì)焦模式

  1. Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:用于拍照的連續(xù)自動(dòng)對(duì)焦模式
  2. Camera.Parameters.FOCUS_MODE_AUTO:需要配合Camera.autoFocus(android.hardware.Camera.AutoFocusCallback)一起使用,調(diào)用一次,對(duì)焦一次
  3. Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:用于錄像的連續(xù)自動(dòng)對(duì)焦模式,焦點(diǎn)變化比較平穩(wěn),沒有FOCUS_MODE_CONTINUOUS_PICTURE快

3.2 啟動(dòng)人臉檢測(cè)

  1. 檢查是否支持人臉檢測(cè):根據(jù)parameters.getMaxNumDetectedFaces()是否大于0來表示是否支持人臉檢測(cè)
  2. 創(chuàng)建并添加人臉檢測(cè)監(jiān)聽器到相機(jī)對(duì)象
mCamera.setFaceDetectionListener((faces, camera) -> {
    
});
  1. 在預(yù)覽后開啟人臉檢測(cè)
if (parameters.getMaxNumDetectedFaces() > 0) {
                mCamera.startFaceDetection();
}

3.2.1 人臉檢測(cè)中回調(diào)的人臉信息Face

Face

 public static class Face {
        public int id = -1;
        public Point leftEye;
        public Point mouth;
        public Rect rect;
        public Point rightEye;
        public int score;

    }

這里返回的坐標(biāo)是傳感器視角下的坐標(biāo),左上點(diǎn)為(-1000,-1000),右下點(diǎn)未(1000,1000)。到SurfaceView坐標(biāo)的轉(zhuǎn)換官網(wǎng)已經(jīng)給出。Camera.FAce.rect字段說明

/**
     * 獲取人臉坐標(biāo)轉(zhuǎn)換矩陣(https://developer.android.google.cn/reference/android/hardware/Camera.Face#mouth)
     *
     * @param activity
     * @param cameraId
     * @param view
     * @return
     */
    public static Matrix getViewLoactionMatrix(Activity activity, int cameraId, View view) {
        Matrix matrix = new Matrix();//矩陣的轉(zhuǎn)換結(jié)果和轉(zhuǎn)換的設(shè)置順序有關(guān)
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //前置需要鏡像
            matrix.setScale(-1f, 1f);
        } else {
            matrix.setScale(1f, 1f);
        }
        //后乘旋轉(zhuǎn)角度
        matrix.postRotate(CameraUtils.getCameraDisplayOrientation(activity, cameraId));
        //后乘縮放
        matrix.postScale(view.getWidth() / 2000f, view.getHeight() / 2000f);
        //再進(jìn)行位移
        matrix.postTranslate(view.getWidth() / 2f, view.getHeight() / 2f);
        return matrix;
    }

3.3 設(shè)置測(cè)光區(qū)域

  1. 根據(jù)params.getMaxNumMeteringAreas()是否大于0來判斷是否支持設(shè)置測(cè)光區(qū)域
  2. 計(jì)算測(cè)光區(qū)域:先在view上計(jì)算一個(gè)矩形區(qū)域作為測(cè)光區(qū)域
public static RectF getMeteringRect(int x, int y, int radius) {
    return new RectF(x - radius, y - radius, x + radius, y + radius);
}
  1. 轉(zhuǎn)換測(cè)光區(qū)域坐標(biāo):測(cè)光區(qū)域的坐標(biāo)是相機(jī)傳感器視角下的坐標(biāo),我們可以根據(jù)上面人臉檢測(cè)的坐標(biāo)來計(jì)算。人臉檢測(cè)的坐標(biāo)是將相機(jī)傳感器視角下的坐標(biāo)轉(zhuǎn)換成SurfaceView上的坐標(biāo),現(xiàn)在我們是將SurfaceView上的坐標(biāo)轉(zhuǎn)換成傳感器上的坐標(biāo),轉(zhuǎn)換公式如下
/**
     * 獲取屏幕坐標(biāo)轉(zhuǎn)換到相機(jī)傳感器坐標(biāo)的矩陣
     *
     * @param activity
     * @param cameraId
     * @return
     */
    public static Matrix getCameraSensorLocationMatrix(Activity activity, int cameraId, int width, int height) {
        Matrix matrix = new Matrix();
        //縮放
        matrix.postScale(2000f / width, 2000f / height);
        //平移
        matrix.postTranslate(-1000f, -1000f);
        //旋轉(zhuǎn)
        matrix.postRotate((360 - getCameraDisplayOrientation(activity, cameraId)) % 360);
        //前置攝像頭需要左右對(duì)調(diào)
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //前置需要鏡像
            matrix.postScale(-1f, 1f);
        } else {
            matrix.postScale(1f, 1f);
        }
        return matrix;
    }
  1. 設(shè)置測(cè)光區(qū)域:應(yīng)用上面的轉(zhuǎn)換公式將view上的測(cè)光區(qū)域轉(zhuǎn)換成相機(jī)傳感器視角下的測(cè)光區(qū)域
Camera.Parameters parameters = mCamera.getParameters();
        if (parameters.getMaxNumMeteringAreas() > 0) {
            Matrix matrix = CameraUtils.getCameraSensorLocationMatrix(CameraActivity3.this, mCameraID, width, height);
            RectF rect = CameraUtils.getMeteringRect(x, y, Math.min(width, height) / 10);
            RectF destRect = new RectF();
            matrix.mapRect(destRect, rect);
            List<Camera.Area> areas = new ArrayList<>();
            Camera.Area area = CameraUtils.getMeteringArea(destRect);
            if (area != null) {
                Log.d("testtouch", "area:" + area.rect.toString());
                areas.add(area);
            }
            try {
                parameters.setMeteringAreas(areas);
                mCamera.setParameters(parameters);
            } catch (RuntimeException e) {
                e.printStackTrace();
            }
        }

如果要設(shè)置對(duì)焦區(qū)域,方式和設(shè)置測(cè)光區(qū)域類似

3.4 曝光設(shè)置

曝光的默認(rèn)模式是自動(dòng)曝光。可設(shè)置停止自動(dòng)曝光和設(shè)置曝光等級(jí)

  1. 設(shè)置是否自動(dòng)曝光
  2. 設(shè)置曝光補(bǔ)償

3.4.1 設(shè)置是否自動(dòng)曝光

接口:Camera.Parameters.setAutoExposureLock (boolean toggle)

public void setAutoExposureLock (boolean toggle)

True表示自動(dòng)曝光被鎖定,false表示自動(dòng)曝光例程可以正常運(yùn)行。自動(dòng)曝光鎖定之后,要直到調(diào)用Camera.release()或者重新設(shè)置為false之后才會(huì)失效,鎖定之后依然可以調(diào)用setExposureCompensation (int value)設(shè)置曝光補(bǔ)償。使用

  1. 先要調(diào)用isAutoExposureLockSupported()判斷相機(jī)是否支持鎖定功能
  2. 然后在調(diào)用setAutoExposureLock (boolean toggle)設(shè)置示范法鎖定自動(dòng)曝光

3.4.2 設(shè)置曝光補(bǔ)償

什么事曝光補(bǔ)償?曝光補(bǔ)償是指,通過增加或者減少光線的攝入量,來提高或降低照片的亮度。

曝光補(bǔ)償存在一定范圍,比如-2至2,或者-3至3,單位為EV,可以理解為exposure value。

每增加或者降低1EV,光線的攝入量便增加或降低1倍。

android camera的曝光補(bǔ)償為階段性變化,每次變化1/2或1/3,該數(shù)量級(jí)被稱為step。

android camera可以通過以下五個(gè)API,分別獲取step,最大曝光補(bǔ)償級(jí)數(shù),最小曝光補(bǔ)償級(jí)數(shù),當(dāng)前曝光補(bǔ)償級(jí)數(shù),設(shè)置曝光補(bǔ)償級(jí)數(shù)。曝光補(bǔ)償值=step * 曝光補(bǔ)償級(jí)數(shù)。

  1. public int getMaxExposureCompensation ():最大補(bǔ)償指數(shù)
  2. public int getMinExposureCompensation ():最小補(bǔ)償指數(shù)
  3. public float getExposureCompensationStep ():補(bǔ)償?shù)牟介L(zhǎng)
  4. public void setExposureCompensation (int value):設(shè)置補(bǔ)償指數(shù)
  5. public int getExposureCompensation ():獲取補(bǔ)償指數(shù)

4. 使用FaceDetector識(shí)別人臉

使用FaceDetector來識(shí)別Bitmap中的人臉。FaceDetector對(duì)識(shí)別的Bitmap有要求,格式必須是Bitmap.Config.RGB_565的,且只能識(shí)別豎著的人臉。

FaceDetector faceDetector = new FaceDetector(destBitmap.getWidth(), destBitmap.getHeight(), 3);
FaceDetector.Face[] faces = new FaceDetector.Face[3];
faceDetector.findFaces(destBitmap, faces);

Face中信息

 public class Face {
        public static final float CONFIDENCE_THRESHOLD = 0.4F;
        public static final int EULER_X = 0;
        public static final int EULER_Y = 1;
        public static final int EULER_Z = 2;

        Face() {
            throw new RuntimeException("Stub!");
        }

        public float confidence() {
            throw new RuntimeException("Stub!");
        }

        public void getMidPoint(PointF point) {
            throw new RuntimeException("Stub!");
        }

        public float eyesDistance() {
            throw new RuntimeException("Stub!");
        }

        public float pose(int euler) {
            throw new RuntimeException("Stub!");
        }
    }
  1. confidence():可信度,值的范圍為0-1,一般超過0.3則認(rèn)為可信了
  2. eyesDistance():眼間距,值相對(duì)于bitmap的大小
  3. getMidPoint():兩眼中點(diǎn),值相對(duì)于bitmap的大小

4.1 設(shè)置人臉測(cè)光區(qū)域

使用FaceDetector識(shí)別到的人臉區(qū)域設(shè)置測(cè)光區(qū)域(設(shè)置與點(diǎn)擊屏幕設(shè)置測(cè)光區(qū)域一致)

  1. 先相對(duì)于bitmap的基礎(chǔ)上計(jì)算一個(gè)測(cè)光區(qū)域
 PointF pointF = new PointF();
face.getMidPoint(pointF);
//根據(jù)eyemid和eyedistance計(jì)算一個(gè)矩形
RectF rect = new RectF(pointF.x - face.eyesDistance(), pointF.y - face.eyesDistance() * 0.5f, pointF.x + face.eyesDistance(), pointF.y + face.eyesDistance() * 1.5f);
  1. 將SurfaceView的測(cè)光區(qū)域轉(zhuǎn)換成相機(jī)傳感器的測(cè)光區(qū)域
//將矩形坐標(biāo)轉(zhuǎn)成camera坐標(biāo)
Matrix matrix = getCameraSensorLocationMatrix(activity, cameraId, bitmapWidth, bitmapHeight);
RectF cameraRect = new RectF(0, 0, 0, 0);
matrix.mapRect(cameraRect, rect);

完整計(jì)算代碼

/**
     * 根據(jù)預(yù)覽回調(diào)中的圖片(調(diào)整后的豎著的人臉),經(jīng)過人臉檢測(cè)之后得到的Face信息,計(jì)算測(cè)光區(qū)域
     *
     * @param activity
     * @param cameraId
     * @param face
     * @param bitmapWidth
     * @param bitmapHeight
     * @return
     */
    public static Camera.Area getCameraArea(Activity activity, int cameraId, FaceDetector.Face face, int bitmapWidth, int bitmapHeight) {
        if (face != null) {
            PointF pointF = new PointF();
            face.getMidPoint(pointF);
            //根據(jù)eyemid和eyedistance計(jì)算一個(gè)矩形
            RectF rect = new RectF(pointF.x - face.eyesDistance(), pointF.y - face.eyesDistance() * 0.5f, pointF.x + face.eyesDistance(), pointF.y + face.eyesDistance() * 1.5f);
            //將矩形坐標(biāo)轉(zhuǎn)成camera坐標(biāo)
            Matrix matrix = getCameraSensorLocationMatrix(activity, cameraId, bitmapWidth, bitmapHeight);
            RectF cameraRect = new RectF(0, 0, 0, 0);
            matrix.mapRect(cameraRect, rect);
            Rect checkedRect = checkAreaRect(new Rect((int) cameraRect.left, (int) cameraRect.top, (int) cameraRect.right, (int) cameraRect.bottom));
            if (checkedRect != null) {
                return new Camera.Area(checkedRect, 1000);
            }
        }
        return null;
    }

設(shè)置

Camera.Parameters parameters = mCamera.getParameters();
if (parameters.getMaxNumMeteringAreas() > 0) {
    try {
        List<Camera.Area> areas = new ArrayList<>();
        for (FaceDetector.Face face : faces) {
            if (face != null && face.confidence() > 0.3) {
                Camera.Area area = CameraUtils.getCameraArea(this,
                        mCameraID,
                        face,
                        destBitmap.getWidth(),
                        destBitmap.getHeight()
                );
                if (area != null) {
                    Log.d("testfacearea", "pic:" + area.rect);
                    areas.add(area);
                }
            }
        }
        parameters.setMeteringAreas(areas);
        mCamera.setParameters(parameters);
    } catch (RuntimeException e) {
        e.printStackTrace();
        Log.d("testsetparametererror", "" + e.getMessage());
    }
}

5. 幾個(gè)重要的方法

  1. 計(jì)算預(yù)覽圖像的旋轉(zhuǎn)角度
  2. 計(jì)算預(yù)覽大小
  3. 計(jì)算圖片大小
  4. 相機(jī)傳感器坐標(biāo)轉(zhuǎn)View的坐標(biāo)
  5. View的坐標(biāo)轉(zhuǎn)相機(jī)傳感器坐標(biāo)
  6. 圖片旋轉(zhuǎn)

5.1 計(jì)算預(yù)覽圖像的旋轉(zhuǎn)角度

 /**
     * 獲取預(yù)覽圖像需要的旋轉(zhuǎn)角度(保證預(yù)覽方向正確)
     *
     * @param activity
     * @param cameraId
     */
    public static int getCameraDisplayOrientation(Activity activity,
                                                  int cameraId) {
        Camera.CameraInfo info =
                new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        //rotation是預(yù)覽Window的旋轉(zhuǎn)方向,對(duì)于手機(jī)而言,當(dāng)在清單文件設(shè)置Activity的screenOrientation="portait"時(shí),
        //rotation=0,這時(shí)候沒有旋轉(zhuǎn),當(dāng)screenOrientation="landScape"時(shí),rotation=1。
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        Log.d("DisplayOrientation", "roation:" + rotation + ";info.orientation:" + info.orientation);
        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;
        }
        //info.orientation:前置-270;后置-90

        int result;
        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;
    }

5.2 計(jì)算預(yù)覽大小

/**
     * 計(jì)算合適的預(yù)覽大小
     *
     * @param activity
     * @param view
     * @param parameters
     * @return
     */
    public static Camera.Size getPreviewSize(Activity activity, View view, Camera.Parameters parameters) {
        boolean isPortrait = isPortrait(activity);
        List<Camera.Size> localSizes = parameters.getSupportedPreviewSizes();
        if (localSizes != null && localSizes.size() > 0) {
            //過濾分辨率比較小的
            List<Camera.Size> filterList = new ArrayList<>();
            float viewSize = view.getWidth() * view.getHeight();
            for (int i = 0; i < localSizes.size(); i++) {
                Camera.Size size = localSizes.get(i);
                //過濾分辨率比較小的(不同設(shè)備上預(yù)覽的分辨率和屏幕的分辨率差距較大,這個(gè)閥值應(yīng)該根據(jù)具體設(shè)備調(diào)整)
                if (size.width * size.height / viewSize > 0.3) {
                    filterList.add(size);
                }
            }
            int height = isPortrait ? view.getHeight() : view.getWidth();
            int width = isPortrait ? view.getWidth() : view.getHeight();

            if (filterList.size() > 0) {
                return getPreviewSize(filterList, height, width);
            }
            return localSizes.get(0);
        }
        return null;
    }
    
private static Camera.Size getPreviewSize(List<Camera.Size> filterList, int height, int width) {
        Camera.Size fitSize = getPreviewSize1(filterList, height, width);
        if (fitSize != null) {
            return fitSize;
        }
        return getPreviewSize2(filterList, height, width);
    }

    /**
     * 獲取高寬比大于view的高寬比的值最小的Size
     *
     * @param filterList
     * @param height
     * @param width
     * @return
     */
    @Nullable
    private static Camera.Size getPreviewSize1(List<Camera.Size> filterList, int height, int width) {
        Camera.Size fitSize1 = null;
        float lastRatio = 0;
        float viewRatio = height * 1.0f / width;
        for (int i = 0; i < filterList.size(); i++) {
            Camera.Size temp = filterList.get(i);
            float ratio = temp.width * 1.0f / temp.height;
            if (ratio <= viewRatio && ratio > lastRatio) {
                lastRatio = ratio;
                fitSize1 = temp;
            }
        }
        return fitSize1;
    }

    /**
     * 獲取高寬比比view的高寬比小的最大值
     *
     * @param filterList
     * @param height
     * @param width
     * @return
     */
    @Nullable
    private static Camera.Size getPreviewSize2(List<Camera.Size> filterList, int height, int width) {
        Camera.Size fitSize = null;
        float lastRatio = 100;
        float viewRatio = height * 1.0f / width;
        for (int i = 0; i < filterList.size(); i++) {
            Camera.Size temp = filterList.get(i);
            float ratio = temp.width * 1.0f / temp.height;
            if (ratio >= viewRatio && ratio < lastRatio) {
                lastRatio = ratio;
                fitSize = temp;
            }
        }
        return fitSize;
    }

5.3 計(jì)算圖片大小

/**
     * 計(jì)算合適的圖片大?。ㄈ『皖A(yù)覽寬高比一致的,沒有則取分辨率最大的)
     *
     * @param parameters
     * @return
     */
    public static Camera.Size getPitureSize(Camera.Parameters parameters) {
        Camera.Size previewSize = parameters.getPreviewSize();
        List<Camera.Size> sizeList = parameters.getSupportedPictureSizes();
        if (previewSize != null && sizeList != null && sizeList.size() > 0) {
            //尋找寬高比一致的,從中選圖片最大的
            Camera.Size temp = null;
            for (Camera.Size size : sizeList) {
                if (size.height * previewSize.width == size.width * previewSize.height) {
                    if (temp == null) {
                        temp = size;
                    } else {
                        if (size.width * size.height > temp.width * temp.height) {
                            temp = size;
                        }
                    }
                }
            }
            //找分辨率最大的
            if (temp == null) {
                for (Camera.Size size : sizeList) {
                    if (temp == null) {
                        temp = size;
                    } else {
                        if (size.width * size.height > temp.width * temp.height) {
                            temp = size;
                        }
                    }
                }
            }
            return temp;
        }
        return null;
    }

5.4 相機(jī)傳感器坐標(biāo)轉(zhuǎn)View的坐標(biāo)

/**
     * 獲取人臉坐標(biāo)轉(zhuǎn)換矩陣(https://developer.android.google.cn/reference/android/hardware/Camera.Face#mouth)
     *
     * @param activity
     * @param cameraId
     * @param view
     * @return
     */
    public static Matrix getViewLoactionMatrix(Activity activity, int cameraId, View view) {
        Matrix matrix = new Matrix();//矩陣的轉(zhuǎn)換結(jié)果和轉(zhuǎn)換的設(shè)置順序有關(guān)
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //前置需要鏡像
            matrix.setScale(-1f, 1f);
        } else {
            matrix.setScale(1f, 1f);
        }
        //后乘旋轉(zhuǎn)角度
        matrix.postRotate(CameraUtils.getCameraDisplayOrientation(activity, cameraId));
        //后乘縮放
        matrix.postScale(view.getWidth() / 2000f, view.getHeight() / 2000f);
        //再進(jìn)行位移
        matrix.postTranslate(view.getWidth() / 2f, view.getHeight() / 2f);
        return matrix;
    }

5.5 View的坐標(biāo)轉(zhuǎn)相機(jī)傳感器坐標(biāo)

/**
     * 獲取屏幕坐標(biāo)轉(zhuǎn)換到相機(jī)傳感器坐標(biāo)的矩陣
     *
     * @param activity
     * @param cameraId
     * @return
     */
    public static Matrix getCameraSensorLocationMatrix(Activity activity, int cameraId, int width, int height) {
        Matrix matrix = new Matrix();
        //縮放
        matrix.postScale(2000f / width, 2000f / height);
        //平移
        matrix.postTranslate(-1000f, -1000f);
        //旋轉(zhuǎn)
        matrix.postRotate((360 - getCameraDisplayOrientation(activity, cameraId)) % 360);
        //前置攝像頭需要左右對(duì)調(diào)
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //前置需要鏡像
            matrix.postScale(-1f, 1f);
        } else {
            matrix.postScale(1f, 1f);
        }
        return matrix;
    }

5.6 圖片旋轉(zhuǎn)

public static Matrix getPicMatrix(Activity activity, int cameraId) {
        Matrix matrix = new Matrix();
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotate = getCameraDisplayOrientation(activity, cameraId);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //預(yù)覽回調(diào)圖片和預(yù)覽圖像之間左右鏡像,所以可以先做鏡像,再按預(yù)覽圖像的旋轉(zhuǎn)角度旋轉(zhuǎn)
            matrix.postScale(-1f, 1f);
            matrix.postRotate(rotate);
        } else {
            matrix.postRotate(rotate);
        }
        return matrix;
    }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容