學習完百度人臉API的調用,如今便可開發本身的人臉識別簽到系統,下面做者先貼上部分功能源碼來給你們參考和學習
java
(一)百度人臉庫的人臉驗證android
1° 獲取待識別的照片git
既然是人臉認證 那麼固然首先得向百度人臉庫添加你的人臉github
而後再把你須要進行人臉識別的照片與百度人臉庫的人臉進行校對,若是校對成功,即簽到打卡成功數據庫
關於獲取帶人臉識別的照片,做者採起了兩種方式獲取(即時拍照、從相冊導入)json
即時拍照:數組
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導入項目便可)