目錄
- 相機(jī)開發(fā)的基本步驟
- 相機(jī)開發(fā)中的一些配置
- 相機(jī)功能設(shè)置
- 使用FaceDetector識(shí)別人臉
- 幾個(gè)重要的方法
1. 相機(jī)開發(fā)的基本步驟
- 聲明硬件特性
- 聲明需要的權(quán)限和動(dòng)態(tài)請(qǐng)求權(quán)限
- 在布局中添加預(yù)覽類和設(shè)置監(jiān)聽器
- 打開相機(jī)和預(yù)覽
- 拍照
- 釋放相機(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ā)中的一些配置
- 預(yù)覽旋轉(zhuǎn)角度設(shè)置
- 設(shè)置合適的預(yù)覽大小
- 設(shè)置合適的圖片大小
- 保存圖片
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è)置
- 設(shè)置對(duì)焦模式
- 啟動(dòng)人臉檢測(cè)
- 手動(dòng)設(shè)置測(cè)光區(qū)域和對(duì)焦區(qū)域
- 曝光設(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ì)焦模式
- Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:用于拍照的連續(xù)自動(dòng)對(duì)焦模式
- Camera.Parameters.FOCUS_MODE_AUTO:需要配合Camera.autoFocus(android.hardware.Camera.AutoFocusCallback)一起使用,調(diào)用一次,對(duì)焦一次
- Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:用于錄像的連續(xù)自動(dòng)對(duì)焦模式,焦點(diǎn)變化比較平穩(wěn),沒有FOCUS_MODE_CONTINUOUS_PICTURE快
3.2 啟動(dòng)人臉檢測(cè)
- 檢查是否支持人臉檢測(cè):根據(jù)parameters.getMaxNumDetectedFaces()是否大于0來表示是否支持人臉檢測(cè)
- 創(chuàng)建并添加人臉檢測(cè)監(jiān)聽器到相機(jī)對(duì)象
mCamera.setFaceDetectionListener((faces, camera) -> {
});
- 在預(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ū)域
- 根據(jù)params.getMaxNumMeteringAreas()是否大于0來判斷是否支持設(shè)置測(cè)光區(qū)域
- 計(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);
}
- 轉(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;
}
- 設(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í)
- 設(shè)置是否自動(dòng)曝光
- 設(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ǔ)償。使用
- 先要調(diào)用isAutoExposureLockSupported()判斷相機(jī)是否支持鎖定功能
- 然后在調(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ù)。
- public int getMaxExposureCompensation ():最大補(bǔ)償指數(shù)
- public int getMinExposureCompensation ():最小補(bǔ)償指數(shù)
- public float getExposureCompensationStep ():補(bǔ)償?shù)牟介L(zhǎng)
- public void setExposureCompensation (int value):設(shè)置補(bǔ)償指數(shù)
- 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!");
}
}
- confidence():可信度,值的范圍為0-1,一般超過0.3則認(rèn)為可信了
- eyesDistance():眼間距,值相對(duì)于bitmap的大小
- getMidPoint():兩眼中點(diǎn),值相對(duì)于bitmap的大小
4.1 設(shè)置人臉測(cè)光區(qū)域
使用FaceDetector識(shí)別到的人臉區(qū)域設(shè)置測(cè)光區(qū)域(設(shè)置與點(diǎn)擊屏幕設(shè)置測(cè)光區(qū)域一致)
- 先相對(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);
- 將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è)重要的方法
- 計(jì)算預(yù)覽圖像的旋轉(zhuǎn)角度
- 計(jì)算預(yù)覽大小
- 計(jì)算圖片大小
- 相機(jī)傳感器坐標(biāo)轉(zhuǎn)View的坐標(biāo)
- View的坐標(biāo)轉(zhuǎn)相機(jī)傳感器坐標(biāo)
- 圖片旋轉(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;
}