1. 程式人生 > >Android課程設計 人臉識別簽到(二)

Android課程設計 人臉識別簽到(二)

得到 bject color stc capture tab string default contex

學習完百度人臉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來獲取服務器中存儲的所有總的簽到結果

即可導出已簽到的人和未簽到的人

補充 :

當然百度人臉庫的更新、添加、刪除也與打卡操作大同小異 也可以編輯類似上面的代碼 來直接在安卓端操作

例如:

你可以事先在百度人臉庫創建分組,然後往分組添加人臉的時候,同時完成添加人臉和添加自己服務端的人臉花名冊信息操作

這樣既可以拋去對直接去操作百度控制臺的的操作

查詢已經簽到人:

可以直接查看自己服務器端的簽到記錄獲取

查看未簽到人:

可以通過服務器端的 班級花名冊 減去簽到記錄已有的人

既可以獲取未簽到的人

Android課程設計 人臉識別簽到(二)