Android 人脸识别签到(二)

学习完百度人脸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导入项目即可)

 

posted @ 2018-12-22 14:57  饼先生  阅读(1339)  评论(0编辑  收藏