如果不是CameraX不能使用,建议使用CameraX《基于CameraX实现人脸》
不推荐Camera2,适配难!还巨麻烦,要写的代码配置太多了!
一代我是直接新建的Class继承了Texture.SurfaceTextureListener,非Activity
一、初始化
public class FaceAnalyzeByOne implements TextureView.SurfaceTextureListener {
private static final String TAG = "一代Camera";
private Camera mCamera;
private Camera.CameraInfo mCameraInfo=new Camera.CameraInfo();
private int cameraID=0;
private Activity that;
private TextureView mTextureView;
private FaceEngine faceObj;
private SurfaceTexture mSurface;
private int afCode = -1; // sdk状态
public boolean needPreview=true; // 是否需要预览框?
public int previewRotation=Surface.ROTATION_0; // 预览的旋转角度?
private boolean isFrontCamera = true; // 用前置摄像头?
private int lastFaceID = -1; // 上次识别到的人脸id,每次镜头里的同一人脸会是固定id,同一人离镜再入镜会是新ID
public CameraCallback faceEmit;
public Point screenPoint= new Point();
public Point previewPoint; // 最终相机预览的的一个分辨率,不是预览容器的宽高
// 同名方法直接初始化构造
public FaceAnalyzeByOne(Activity context,CameraCallback callback){
that = context;
that.getWindowManager().getDefaultDisplay().getSize(screenPoint);
initEngine();// 初始化SDK
faceEmit=callback;
//动态创建预览的view
RelativeLayout layout=that.findViewById(.camerax_preview);
mTextureView =new TextureView(that);
layout.addView(mTextureView);
mTextureView.setSurfaceTextureListener(this);//监听这个viewOK等
}
}二、SDK相关
//注销arcsoft 引擎
public void unInitEngine() {
if (afCode == 0) {
afCode = faceObj.unInit();
Log.i(TAG, "unInitEngine: " + afCode);
}
}
//初始化arcsoft引擎
public void initEngine(){
faceObj = new FaceEngine();
afCode = faceObj.init(that.getApplicationContext(),
DetectMode.ASF_DETECT_MODE_VIDEO,
DetectFaceOrientPriority.ASF_OP_ALL_OUT,// 全角度检测
16, 3,faceObj.ASF_FACE_DETECT | faceObj.ASF_AGE
| faceObj.ASF_FACE3DANGLE | faceObj.ASF_GENDER
| faceObj.ASF_LIVENESS| faceObj.ASF_FACE_RECOGNITION
);
if (afCode != ErrorInfo.MOK) {
if(afCode == ErrorInfo.MERR_ASF_NOT_ACTIVATED){
Log.e(Tag,"设备SDK未激活");
}else{
Log.e(Tag,"初始化失败:"+afCode);
}
}
}三、因为引入了TextureView.SurfaceTextureListener所以有四个方法重写一下,第一个是关键
// 监听view可用了
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// Log.i(TAG, "onSurfaceTextureAvailable: " + width + ", " + height);
mSurface=surface;
startCamera();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
closeCamera();
return false;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}四、预览的view初始化OK,初始化相机
// 初始化相机
public void startCamera(){
mCamera = createCamera();
setOrientation();
try {
mCamera.setPreviewTexture(mSurface);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
private long lastDrawTime = 0;
private int timerSpace = 300; // 识别间隔
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
long start = System.currentTimeMillis();
if (start - lastDrawTime < timerSpace) {
return;
}
lastDrawTime = System.currentTimeMillis();
getFaceInfo(data);// 获取到的直接是NV21数据,不需要像其他两代相机一样转换
}
});
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
//创建相机的方法
private Camera createCamera() {
cameraID=isFrontCamera?Camera.CameraInfo.CAMERA_FACING_FRONT:Camera.CameraInfo.CAMERA_FACING_BACK;
Camera camera = Camera.open(cameraID);
Camera.Parameters mParameters = camera.getParameters();
Camera.getCameraInfo(cameraID, mCameraInfo);
List<String> supportedFocusModes = mParameters.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // 设置聚焦模式(自动)
}
mParameters.setPreviewFormat(ImageFormat.NV21); //预览格式
mParameters.setExposureCompensation(0); // 设置曝光强度
Camera.Size bestSize = getBestPreview(mParameters.getSupportedPreviewSizes(),screenPoint);
Log.e(TAG, "最佳预览尺寸: " + bestSize.width + ", " + bestSize.height);
previewPoint = new Point(bestSize.width,bestSize.height);
mParameters.setPreviewSize(bestSize.width, bestSize.height); // 设置预览大小
camera.setParameters(mParameters);
return camera;
}
// 确认画面的方向
public void setOrientation(){
int rotation=that.getWindowManager().getDefaultDisplay().getRotation();
int degrees = rotation * 90;
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 (isFrontCamera) {
result = (mCameraInfo.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (mCameraInfo.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}五、分析图像获取特征
private void getFaceInfo(byte[] nv21) {
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();//装获取到的人脸列表
FaceFeature faceFeature = new FaceFeature(); //装特征数据
//第一步是送数据给arcsoft sdk,检查是否有人脸信息。
int code = faceObj.detectFaces(nv21, previewPoint.x,
previewPoint.y,
faceObj.CP_PAF_NV21, faceInfoList);
// Log.e("人脸数据长度", nv21.length+"人脸"+code);
//数据检查正常,并且含有人脸信息,则进行下一步的人脸识别。
if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {
int newFaceID = faceInfoList.get(0).getFaceId();
if (lastFaceID == newFaceID) { // 相同脸则return
Log.e("人脸ID相同", "旧" + lastFaceID);// + "、新" + newFaceID
return;
}
lastFaceID = newFaceID;
code = faceObj.extractFaceFeature(nv21, previewPoint.x,
previewPoint.y,
faceObj.CP_PAF_NV21, faceInfoList.get(0), faceFeature);
if (code != ErrorInfo.MOK) {
return;
}
byte[] featureData=faceFeature.getFeatureData();//最终的特征数据!!!
post(featureData); //把数据给后端去查信息
}
}六、其他
//销毁创建的相机
public void closeCamera(){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
}
/**
* 回调接口定义
*/
public interface CameraCallback {
void onFind(JSONObject response) throws JSONException;
void onUnFind(JSONObject response) throws JSONException;
}
// 切换前后置摄像头
public void switchCamera(){
isFrontCamera=!isFrontCamera;
closeCamera();
startCamera();
}
/**
* 获取最佳预览分辨率,解决预览拉伸问题
*
* @param supportedSize 相机支持的预览分辨率
* @param screenResolution 控件宽高或屏幕宽高
* @return 与预览控件比例最接近预览分辨率
*/
public static Size getBestPreview(List<Size> supportedSize, Point screenResolution) {
// 如果x<y,交换x,y的值
if (screenResolution.x < screenResolution.y) {
int temp = screenResolution.x;
screenResolution.x = screenResolution.y;
screenResolution.y = temp;
}
// 计算控件宽高比
float referRatio = screenResolution.x * 1.0f / screenResolution.y;
float lastDiff = 0.5f;
Size lastSize = null;
for (Size size : supportedSize) {
// Log.e("可用尺寸", size.width+"*"+size.height);
// 只找出预览高度>=720 分辨率
if (size.height < 720) {
continue;
}
// 计算分辨率宽高比
float supportRatio = size.width * 1.0f / size.height;
// 计算差值的绝对值
float currentDiff = Math.abs(referRatio - supportRatio);
// 循环取出差值最小的分辨率
if (currentDiff < lastDiff) {
lastDiff = currentDiff;
lastSize = size;
}
}
// return new Point(lastSize.getWidth(), lastSize.getHeight());
return lastSize;
}七、其他页面使用
//在activity页面
FaceAnalyzeByOneObj = new FaceAnalyzeByOne(this,
new FaceAnalyzeByOne.CameraCallback() {
@Override
public void onFind(JSONObject response) throws JSONException {
// Log.e("找到人脸", response.toString());
}
@Override
public void onUnFind(JSONObject response) throws JSONException {
// Log.e("未找到", response.toString());
}
});
切换镜头
FaceAnalyzeByOneObj.switchCamera();
















