Face++ 人脸检测、注册以及训练(1)
@Author : Dolphix.J Qing
1 引言
检测给定图片(Image)中的所有人脸(Face)的位置和相应的面部属性
若结果的face_id没有被加入任何faceset/person之中,则在72小时之后过期被自动清除。
针对verify功能对一个person进行训练。请注意:
2 系统摄像头操作
调用Android系统摄像头,对人脸进行采集。其中有以下几个地方需要注意:
其中,系统摄像头使用如下,主要包括调用摄像头并获取结果全过程(absPath为存储路径)。
- 开设前置摄像头
- 设置指定存储位置
- 对图片进行压缩(这样可以避免不必要的流量消耗,同时也提高响应速度)
/**
* 开启系统相机
*/
private void openSysCamera(){
if (savePrepare()){
//开启系统相机
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
camera.putExtra("camerasensortype", 2); // 调用前置摄像头
camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(absPath,"tmp.jpg")));
startActivityForResult(camera, EventUtil.SYS_CAMERA);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == EventUtil.SYS_CAMERA && resultCode == Activity.RESULT_OK){
Log.i(TAG,"Camera finish!");
//保持原图比例压缩,并保存为tmp2.jpg
b = FileUtil.compressBySize(jpegName,500,500);
face.setImageBitmap(b);
FileUtil.saveBitmap(b,absPath+"tmp2.jpg");
if (CommonUtil.isNetWorkConnected(getApplicationContext())){
//人脸检测
detectFace();
}else{
Toast.makeText(getApplicationContext(),"无法检测,网络可能存在问题!",Toast.LENGTH_SHORT).show();
}
}else if (requestCode == EventUtil.SYS_CAMERA && resultCode == Activity.RESULT_CANCELED){
finish();
}
}
图片压缩,压缩时主要放缩比例,同时留意API平台对图片的尺寸要求。
/**
* 压缩图片尺寸
* @param pathName
* @param targetWidth
* @param targetHeight
* @return
*/
public static Bitmap compressBySize(String pathName, int targetWidth,int targetHeight) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;// 不去真的解析图片,只是获取图片的头部信息,包含宽高等;
Bitmap bitmap = BitmapFactory.decodeFile(pathName, opts);
// 得到图片的宽度、高度;
float imgWidth = opts.outWidth;
float imgHeight = opts.outHeight;
// 分别计算图片宽度、高度与目标宽度、高度的比例;取大于等于该比例的最小整数;
int widthRatio = (int) Math.ceil(imgWidth / (float) targetWidth);
int heightRatio = (int) Math.ceil(imgHeight / (float) targetHeight);
opts.inSampleSize = 1;
if (widthRatio > 1 || heightRatio > 1) {
if (widthRatio > heightRatio) {
opts.inSampleSize = widthRatio;
} else {
opts.inSampleSize = heightRatio;
}
}
//设置好缩放比例后,加载图片进内容;
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(pathName, opts);
return bitmap;
}
图片保存至SD卡,此操作时注意添加相应权限。
/**
* 保存Bitmap到sdcard
* @param b
* @return 图片绝对路径
*/
public static String saveBitmap(Bitmap b, String jpegName){
File tmp = new File(jpegName);
if (tmp.exists()){
tmp.delete();
}
try {
FileOutputStream fout = new FileOutputStream(jpegName);
BufferedOutputStream bos = new BufferedOutputStream(fout);
//压缩50%
b.compress(Bitmap.CompressFormat.JPEG, 50, bos);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return jpegName;
}
主要流程代码如下,其中缺失代码为GET/POST请求,格式构造可以参考
Face++ 人脸检测之POST[img](0)
/**
* 检测人脸
*/
private void detectFace(){
new Thread(new Runnable(){
@Override
public void run() {
PlatformDetect.detect(new File(absPath+"tmp2.jpg"));
}
}).start();
}
/**
* 人脸检测,回调
*/
private class DetectCallback implements PlatformDetect.DetectCallback{
@Override
public void detectResult(JSONObject rst) {
if (null == rst){
return;
}
Log.i(TAG, "detectResult: "+rst.toString());
try {
if (!rst.has("error")){
//找出所有人脸
final int count = rst.getJSONArray("face").length();
if (count > 0){
Log.i(TAG, "detectResult: 存在人脸");
jsonObject = rst;
//取得face_id
FaceInfo.face_id = rst.getJSONArray("face").getJSONObject(0).getString("face_id");
if (CommonUtil.isNetWorkConnected(getApplicationContext())) {
//人脸识别
addFace();
}
//开始根据数据绘制位置、坐标
handler.sendEmptyMessage(EventUtil.FACE_DRAW_DETECT_DATA);
return;
}
}
//不存在人脸
handler.sendEmptyMessage(EventUtil.NOT_EXIST_FACE);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
/**
* 添加人脸
*/
private void addFace(){
new Thread(new Runnable(){
@Override
public void run() {
requestPlatform();
}
}).start();
}
/**
* 请求Face++平台服务
*/
private void requestPlatform(){
Log.i(TAG, "requestPlatform: ");
try {
boolean b = true;
int t = 0;
if (null == user){
Log.i(TAG, "requestPlatform: null == user");
return;
}
while (b && t < 3) {
if (!user.equals("vcrobot") && CommonUtil.isNetWorkConnected(getApplicationContext())) {
//添加人脸
String result = NetTask.doGet(FacePerson.addFace(FaceInfo.face_id, user));
Log.i(TAG, "run: addFace "+result);
JSONObject jsonObject = new JSONObject(result);
if (jsonObject.has("error") || !jsonObject.getBoolean("success")) {
//添加失败,创建用户
result = NetTask.doGet(FacePerson.create("vcrobot",user,"vcr"));
Log.i(TAG, "run: create person "+result);
jsonObject = new JSONObject(result);
if (jsonObject.has("error") || jsonObject.getInt("added_group") <= 0){
//group不存在,创建group
result = NetTask.doGet(FaceGroup.create("vcrobot","vcr"));
jsonObject = new JSONObject(result);
Log.i(TAG, "run: create group "+result);
if (!jsonObject.has("error")){
Log.i(TAG, "requestPlatform: Group create success!");
}
}else{
Log.i(TAG, "requestPlatform: Person create success!");
}
}else{
b = false;
handler.sendEmptyMessage(EventUtil.FACE_ADD_SUCCESS);
Log.i(TAG, "requestPlatform: Add face success!");
}
}
++t;
}
if (b){
handler.sendEmptyMessage(EventUtil.FACE_ADD_FAIL);
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 针对verify功能对一个person进行训练。
*/
private void trainFace(){
new Thread(new Runnable(){
@Override
public void run() {
if (CommonUtil.isNetWorkConnected(getApplicationContext())){
PlatformTrain.train(user);
}
}
}).start();
}
/**
* 人脸训练,回调
*/
private class TrainCallback implements PlatformTrain.TrainCallback{
@Override
public void trainResult(JSONObject rst) {
Log.i(TAG, "trainResult: "+rst);
try{
if (!rst.has("error")){
//相应请求的session标识符,可用于结果查询
sessionID = rst.getString("session_id");
handler.sendEmptyMessage(EventUtil.FACE_TRAIN_FINISH);
}else{
//人脸训练出错
handler.sendEmptyMessage(EventUtil.FACE_TRAIN_ERR);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
最后,给出GET函数。具体URL自行构造。
/**
* doGet发送网络请求
* @param url
* @return
*/
public static String doGet(String url) {
Log.i(TAG, "doGet: "+url);
try{
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
// if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity httpEntity = httpResponse.getEntity();
InputStream inputStream = httpEntity.getContent();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
StringBuffer stringBuffer = new StringBuffer();
while (null != (line = bufferedReader.readLine())) {
stringBuffer.append(line);
}
return stringBuffer.toString();
// }
}catch (Exception e){e.printStackTrace();}
return null;
}