前言
与常规的Python + OpenCV方式不一样,本文为了使系统运行于手机中,
采用了Android版本的OpenCV,过程十分坎坷,但最终还是成功了,
特放出来,大家一起学习,参考。
结合OpenCV470版本,
使用其人脸检测模型,
采集并存储人脸特征,
检测人脸对比特征,
最终得出结果。
效果展示
核心代码
初始化
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register_face);
//创建本地模型目录
FilesUtils.createFile(getExternalFilesDir(null).getAbsolutePath() + "/onnx");
FilesUtils.createFile(getExternalFilesDir(null).getAbsolutePath() + "/faceImg");
Log.e("【开始复制】", System.currentTimeMillis()+"");
AssetsUtils.copyFolderFromAssetsToSD(this,"onnx", getExternalFilesDir(null).getAbsolutePath() + "/onnx");
Log.e("【结束复制】", System.currentTimeMillis()+"");
//核心线程1, 最大线程数1, 存活60秒,数组队列(确保至少有一个待执行任务), 默认线程工厂
//丢弃队列中最旧的一个任务(即将被执行的一个任务),并执行最新的任务,如果执行器被关闭则任务被丢弃。–喜新弃旧型
threadPool=new ThreadPoolExecutor(1, 1,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());
appDatabase = FaceRoomDatabase.getInstance(this.getApplication());
faceDao = appDatabase.userDao();
initView();
}
人脸检测
faceDetectorYN.detect(rgba, faces);
ArrayList<Detection> detections = new ArrayList<>();
if(FaceConstant.IS_SHOW_CHECK_BOX) {
// 检测输出人脸是一个 CV_32F 类型的二维数组,其行是检测到的人脸实例,列是人脸的位置和 5 个人脸地标。 每行的格式如下:
// x1, y1, w, h, x_re, y_re, x_le, y_le, x_nt, y_nt, x_rcm, y_rcm, x_lcm, y_lcm
for (int i = 0; i < faces.rows(); i++) {
int x1 = (int) Math.round(faces.get(i, 0)[0]);
int y1 = (int) Math.round(faces.get(i, 1)[0]);
int w = (int) Math.round(faces.get(i, 2)[0]);
int h = (int) Math.round(faces.get(i, 3)[0]);
int x_re = (int) Math.round(faces.get(i, 4)[0]);
int y_re = (int) Math.round(faces.get(i, 5)[0]);
int x_le = (int) Math.round(faces.get(i, 6)[0]);
int y_le = (int) Math.round(faces.get(i, 7)[0]);
int x_nt = (int) Math.round(faces.get(i, 8)[0]);
int y_nt = (int) Math.round(faces.get(i, 9)[0]);
int x_rcm = (int) Math.round(faces.get(i, 10)[0]);
int y_rcm = (int) Math.round(faces.get(i, 11)[0]);
int x_lcm = (int) Math.round(faces.get(i, 12)[0]);
int y_lcm = (int) Math.round(faces.get(i, 13)[0]);
Detection detection = new Detection();
// 画框框
detection.setBoundingBox(new RectF(x1, y1, x1 + w, y1 + h));
// 画文字
Category category = new Category();
category.setLabel("id:" + i);
detection.setCategory(category);
ArrayList<PointF> points = new ArrayList<>();
// 画右眼点
PointF point1 = new PointF(x_re, y_re);
points.add(point1);
// 画左眼点
PointF point2 = new PointF(x_le, y_le);
points.add(point2);
// 画特鼻子点
PointF point3 = new PointF(x_nt, y_nt);
points.add(point3);
// 画右嘴点
PointF point4 = new PointF(x_rcm, y_rcm);
points.add(point4);
// 画左嘴点
PointF point5 = new PointF(x_lcm, y_lcm);
points.add(point5);
detection.setPoint(points);
detections.add(detection);
}
}
人脸注册
//首次开始要定好本次刷脸编号(即记录同一个人)
if(successGetCnt == 0) {
mFaceNo = UUID.randomUUID().toString();
}
successGetCnt++;
if(successGetCnt < FaceConstant.FACE_INFO_GET_CNT) {
runOnUiThread(new Runnable() {
@Override
public void run() {
imgFaceTipsBg.setVisibility(View.VISIBLE);
tvFaceTips.setText("采集中..." + Math.round(successGetCnt*1f/FaceConstant.FACE_INFO_GET_CNT*100f) + "%");
}
});
//存储数据
if(successGetCnt >= (FaceConstant.FACE_INFO_GET_CNT - FaceConstant.FACE_INFO_SAVE_CNT))
saveFaceInfo(faces, rgba, mFaceNo);
} else if(successGetCnt >= FaceConstant.FACE_INFO_GET_CNT){
runOnUiThread(new Runnable() {
@Override
public void run() {
imgFaceTipsBg.setVisibility(View.VISIBLE);
imgFaceTipsBg.setImageResource(R.mipmap.ic_tips_gate_success);
tvFaceTips.setText("采集完成");
}
});
}
人脸识别
private FaceEntity faceRecognizer(Mat faces, Mat img) {
// 人脸识别(对比)
// 人脸对齐
Mat aligned_face1 = new Mat();
faceRecognizerSF.alignCrop(img, faces.row(0), aligned_face1);
// 人脸特征提取
Mat feature1 = new Mat();
faceRecognizerSF.feature(aligned_face1, feature1);
//todo 不清楚为啥要克隆一下,如果不克隆,2个人脸会一直显示同一个人
feature1 = feature1.clone();
double maxScore_FR_COSINE = -10.0;
double minScore_FR_NORM_L2 = 10.0;
int maxScore_FR_COSINE_id = -1;
int minScore_FR_NORM_L2_id = -1;
Log.d(Thread.currentThread().getId()+ "【faceRecognizer2】", System.currentTimeMillis()+"");
Gson gosn = new Gson();
FaceEntity faceEntitys = null;
double topScore = -10.0;
for(int i=0; i<mFaceInfos.size(); i++) {
Mat feature2 = new Mat(1, 128, CvType.CV_32FC1);
double[] faceFeature2 = gosn.fromJson(mFaceInfos.get(i).getFaceFeature(), double[].class);
feature2.put(0,0, faceFeature2);
// 人脸对比
double score_FR_COSINE = faceRecognizerSF.match(feature1, feature2, FaceRecognizerSF.FR_COSINE);
double score_FR_NORM_L2 = faceRecognizerSF.match(feature1, feature2, FaceRecognizerSF.FR_NORM_L2);
if(score_FR_COSINE > 0.363 && score_FR_COSINE > maxScore_FR_COSINE) {
maxScore_FR_COSINE = score_FR_COSINE;
maxScore_FR_COSINE_id = i;
}
if(score_FR_NORM_L2 < 1.128 && score_FR_COSINE < minScore_FR_NORM_L2) {
minScore_FR_NORM_L2 = score_FR_NORM_L2;
minScore_FR_NORM_L2_id = i;
}
}
feature1.release();
if(maxScore_FR_COSINE_id > -1 || maxScore_FR_COSINE_id > -1) {
int id = maxScore_FR_COSINE_id > -1 ? maxScore_FR_COSINE_id : minScore_FR_NORM_L2_id;
return mFaceInfos.get(id);
} else {
return null;
}
}
完整代码可以联系我获取!!!