学习完百度人脸API的调用,现在即可开发自己的人脸识别签到系统,下面作者先贴上部分功能源码来给大家参考和学习
(一)百度人脸库的人脸验证
1° 获取待识别的照片
既然是人脸认证 那么当然首先得向百度人脸库添加你的人脸
然后再把你需要进行人脸识别的照片与百度人脸库的人脸进行校对,如果校对成功,即签到打卡成功
关于获取带人脸识别的照片,作者采取了两种方式获取(即时拍照、从相册导入)
即时拍照:
Camera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure(); //Android7.0拍照必加 且需要在方法类前加@SuppressLint("NewApi")
File outputImage = new File(Environment.getExternalStorageDirectory() + File.separator + "face.jpg"); //临时照片存储地
try {
if (outputImage.exists()) {
outputImage.delete(); //若临时存储地已有照片则delete
}
outputImage.createNewFile(); //临时存储地创建新文件
} catch (IOException e) {
e.printStackTrace();
}
imageUri = Uri.fromFile(outputImage); //获取临时存储地Uri
ImagePath = outputImage.getAbsolutePath(); //得到绝对路径
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //跳转相机
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //相片输出路径
startActivityForResult(intent, CAMERA); //返回照片路径
}
});
从相册直接获取:
getImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent in = new Intent(Intent.ACTION_PICK); //选择数据
in.setType("image/*"); //选择的数据为图片
startActivityForResult(in, Photo_ALBUM);
}
});
获取照片之后需要把照片显示到手机APP界面上,给用户做一个预览 也就是方法startActivityForResult的作用
(https://www.jianshu.com/p/6179f16907dc)
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 相册选择图片
if (requestCode == Photo_ALBUM) {
if (data != null) {
Uri uri = data.getData(); //获取图片uri
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToNext();
ImagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)); //获得图片的绝对路径
cursor.close();
resizePhoto(); //将图片变成缩小的bitmap 方法后贴
int degree = getPicRotate(ImagePath); //获取拍照图片的旋转角度 可以查一下我给出的地址 getPicRotate方法在下面
Matrix m = new Matrix(); //对图形处理
m.setRotate(degree); //旋转
lastp = Bitmap.createBitmap(myBitmapImage, 0, 0, myBitmapImage.getWidth(), myBitmapImage.getHeight(), m, true); //获取缩小且旋转好的图片
myPhoto.setImageBitmap(lastp); //显示图片
Log.i("图片路径", ImagePath);
}
} else if (requestCode == CAMERA) {
try {
resizePhoto();
int degree = getPicRotate(ImagePath); //获取旋转角度
Matrix m = new Matrix(); //对图形处理的类
m.setRotate(degree); //旋转
lastp = Bitmap.createBitmap(myBitmapImage, 0, 0, myBitmapImage.getWidth(), myBitmapImage.getHeight(), m, true);
myPhoto.setImageBitmap(lastp); //显示图片
} catch (Exception e) {
e.printStackTrace();
}
}
}
获取旋转角度
1 public int getPicRotate(String path) { //旋转图片
2 int degree = 0;
3 try {
4 ExifInterface exifInterface = new ExifInterface(path);
5 int orientation = exifInterface.getAttributeInt(
6 ExifInterface.TAG_ORIENTATION,
7 ExifInterface.ORIENTATION_NORMAL); //命名空间 命名空间所属属性
8 switch (orientation) {
9 case ExifInterface.ORIENTATION_ROTATE_90:
10 degree = 90;
11 break;
12 case ExifInterface.ORIENTATION_ROTATE_180:
13 degree = 180;
14 break;
15 case ExifInterface.ORIENTATION_ROTATE_270:
16 degree = 270;
17 break;
18 }
19 } catch (IOException e) {
20 e.printStackTrace();
21 }
22 return degree;
23 }
2° 上传待查照片到百度开发平台,与百度人脸库里已存的照片进行匹配
既然已经获取了待查照片,那么现在即把照片与人脸库的已有照片进行匹配,若百度人脸库存在此人,那么就匹配成功(即签到成功)
1.当点击识别按钮后,将待查照片上传,并创建一个线程用于处理放回的结果
detect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
res = null;
tip.setVisibility(View.VISIBLE); //识别中提示 在Android界面显示
if (myBitmapImage == null) {
Toast.makeText(MainActivity.this, "请添加图片!!!", Toast.LENGTH_LONG).show();
tip.setVisibility(View.GONE); //隐藏 识别中提示
}
else if(Class.getText().toString().trim().equals("请输入查课节次")==true){
Toast.makeText(MainActivity.this, "请输入查课节次!!!", Toast.LENGTH_LONG).show();
tip.setVisibility(View.GONE);
}
else {
//对于上传的图片进行处理
int degree = getPicRotate(ImagePath);
Matrix m = new Matrix(); //对图形处理
m.setRotate(degree); //旋转
bitmapSmall = Bitmap.createBitmap(myBitmapImage, 0, 0, myBitmapImage.getWidth(), myBitmapImage.getHeight(), m, true);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//图片转数据流
bitmapSmall.compress(Bitmap.CompressFormat.JPEG, 100, stream); //图片压缩格式,压缩率,文件输出流对象
final byte[] arrays = stream.toByteArray(); //转成二进制数组
final String pic = android.util.Base64.encodeToString(arrays, Base64.DEFAULT); //获取图片 Base64格式的String
new Thread(new Runnable() {
@Override
public void run() {
HashMap<String, String> options = new HashMap<>();
options.put("quality_control", "NORMAL"); //质量控制
options.put("liveness_control", "LOW"); //活体控制
options.put("max_user_num", "3"); //返回结果的最大个数
String groupId = "ruan1,ruan2"; //查询的人脸组
String imageType = "BASE64"; //上传的图片格式
AipFace client = new AipFace("15119543", "lwxkzZOqjm4bcN2DmHoe8giy", "skYUhhZAfsUCFsBud7VQPIdWPvMt7tOM");
client.setConnectionTimeoutInMillis(2000); //超时设置
client.setSocketTimeoutInMillis(6000);
res = client.search(pic, imageType, groupId, options); //获取查询结果集
try {
Message message = Message.obtain();
message.what = 1;
message.obj = res;
handler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
Message message = Message.obtain();
message.what = 2;
handler.sendMessage(message);
}
}
}).start();
}
}
});
}
2.当上传完图片后,即可获取查询结果集,那么将结果集交由线程进行处理
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
JSONObject jsonObject=null;
String classes = null;
String Record_class = null;
String PId = null;
String name = null;
if (msg.what == 1) { //放回有效的结果集
JSONObject res = (JSONObject) msg.obj;
System.out.println(res);
error_code = res.optString("error_code"); //看是否查询成功
System.out.println(error_code);
if (error_code.equals("0") == true) {
JSONArray TEMP = res.optJSONObject("result").optJSONArray("user_list"); //获取查询结果集的 用户列表
try {
jsonObject = TEMP.getJSONObject(0);
String Tscore = jsonObject.getString("score");
classes = jsonObject.getString("group_id"); //这里获取classes到获取name 是为了后面实现将查询结果同步到服务器数据库做准备
Record_class = Class.getText().toString().trim();
PId = jsonObject.getString("user_id");
name = jsonObject.getString("user_info");
System.out.println(classes+Record_class+ PId+ name);
System.out.println(jsonObject);
System.out.println(Tscore);
score = Double.parseDouble(Tscore); //获取最相似人脸分数 因为最相似人脸会直接放到查询的user_list的第一个
System.out.println(score);
// score=Math.ceil(Double.parseDouble(Tscore));
} catch (JSONException SE) {
SE.printStackTrace();
}
if (score >= 75) { //若相似度大于75
addrecord(classes,Record_class,PId,name); //服务器数据库添加查询成功的结果
} else
Toast.makeText(MainActivity.this, "打卡失败,请重新导入照片", Toast.LENGTH_LONG).show();
myBitmapImage = null;
InitBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.show, null);
tip.setVisibility(View.GONE);
myPhoto.setImageBitmap(InitBitmap);
}
else
Toast.makeText(MainActivity.this, "打卡失败,请重新导入照片", Toast.LENGTH_LONG).show();
tip.setVisibility(View.GONE);
}
}
};
关于addrecord方法,我用了Android的volley框架来进行与自己服务器后台的交互
public void addrecord(final String classes,final String Record_class,final String PId,final String name){
Log.d("addrecord",classes+","+PId+","+name);
//请求地址,需要换接口
String url="http://47.106.10.15:8080/FtoFserver/FtoFserver/addRecord"; //服务器后台的接口 我接口使用Servelet写的
String tag = "addrecord";
//取得请求队列
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this); //传参为当前的context
//防止重复请求,所以先取消tag标识的请求队列
requestQueue.cancelAll(tag);
//创建StringRequest,定义字符串请求的请求方式为POST(省略第一个参数会默认为GET方式)
StringRequest request = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("response",response);
try {
JSONObject jsonObject = new JSONObject(response); //获取的到的后台回应
temp = jsonObject.getString("canLogin");
if(temp.equals("true")){
//等待接口
System.out.println("发布成功");
Toast.makeText(MainActivity.this, "打卡成功!!!", Toast.LENGTH_LONG).show();
}else {
System.out.println("发布失败");
Toast.makeText(MainActivity.this, "网络延迟,请重新上传!!!", Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
//做自己的请求异常操作,如Toast提示(“无网络连接”等);
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//做自己的响应错误操作,如Toast提示(“请稍后重试”等)
Log.d("error",error.toString());
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>(); //将打卡成功的人的信息同步到自己的服务器端
params.put("classes", classes); //传参到服务器接口
params.put("Record_class",Record_class);
params.put("PId",PId);
params.put("name",name);
System.out.println(name);
return params;
}
};
//设置Tag标签
request.setTag(tag);
request.setRetryPolicy(new DefaultRetryPolicy(20*1000,1,1.0f));
//将请求添加到队列中
requestQueue.add(request);
}
3°当验证完所有人脸,又把验证结果同步到自己事先部署的服务器端后,即完成了所以打卡操作
接下来 就可以用普通的servelet来获取服务器中存储的所有总的签到结果
即可导出已签到的人和未签到的人
补充 :
当然百度人脸库的更新、添加、删除也与打卡操作大同小异 也可以编辑类似上面的代码 来直接在安卓端操作
例如:
你可以事先在百度人脸库创建分组,然后往分组添加人脸的时候,同时完成添加人脸和添加自己服务端的人脸花名册信息操作
这样既可以抛去对直接去操作百度控制台的的操作
查询已经签到人:
可以直接查看自己服务器端的签到记录获取
查看未签到人:
可以通过服务器端的 班级花名册 减去签到记录已有的人
既可以获取未签到的人
整个项目我已经上传到github 欢迎大家学习
https://github.com/MrBling/FacetoFace(FacetoFace用Android studio导入项目,FtoFServer是我服务器端的代码 想看的话用IDEA导入项目即可)