1. 程式人生 > >face++人臉識別介面實現原理(一)

face++人臉識別介面實現原理(一)

背景:

市面上,能夠提供人臉識別解決方案的公司主要有,百度,科大訊飛,曠世face++,還有已經被facebook收購的face.com。這裡涉及到的是曠世科技的人臉識別技術。曠世科技人臉識別技術,在國內得到了很廣泛運用,其中最有名是,支付寶的刷臉驗證登入技術,以及螞蟻金服的刷臉驗證技術。

需求:

在安卓平臺上,開發一app,這個app可以:

(1)提取人臉特徵,包括這個人表情(微笑,驚訝,平靜,生氣),年齡,是否戴眼鏡,是否睜眼,膚色,性別,眼睛,鼻子,眉毛在臉部的位置(座標)。

(2)實現登入驗證

(3)將一張合影中的所有人的身份都識別出來。

原理:

實際上,原理並不是很難理解。首先,你將一張圖片,上傳到伺服器,伺服器會提取你的面部特徵,寫進一個檔案中,這檔案會有一個唯一標識嗎,叫做face_token,代表你的身份,然後伺服器會把這些特徵以及標識通過json資料響應給你。響應給你的json資料裡包含面部特徵,以及你的face_token。

登入驗證就是對比或者說匹配的過程,你通過終端拍攝或者從相簿選擇一張照片,然後上傳到伺服器,伺服器首先提取你的面部特徵,然後和你註冊賬號時提取的面部特徵進行對比,如果相識度達到一定高度,就會認為是同一個人,然後伺服器響應驗證通過,進入賬號。

將一張合影中的所有人的身份都識別出來,原理和登入驗證是一樣,只不過多了一個工序。首先,伺服器會探測一張圖片中有多人,並且把能探測到人的臉部特徵以及標識碼以json陣列形式都反饋給你,我們可以通過遍歷標識碼的方式,一個一個匹配,最後把結果彙總,反饋個使用者即可。可能你會有疑惑,為什麼要遍歷標識碼來驗證,因為標識碼是代表一個人的身份,如果用其他引數,比如是否戴眼鏡,那麼你會分不清反饋的結果到底指向得是那個人,進而看不出圖片中的某個人和伺服器反饋的那個人是否是同一個人。

準備
1.註冊face++ api_key Secret:

登入官網,註冊賬號,進入開發者中心,註冊免費體驗版(正式版web api收費標準:一小時30元,一個月5000元,一年600000元,離線版sdk收費標準:每年300,000元起),然後建立應用程式,獲取祕鑰以及密碼。

2.通讀官方文獻,熟悉介面是怎麼用的。

進入官網api文件,瞭解相關介面,這裡要注意的是需要下載java版的呼叫介面工具,這個工具不是必須,但是它可以省去你很長時間,裡面把封裝好了許多函式,方面我們呼叫。

需要用到技術:

javaSE語法,android平臺相關知識(頁面佈局,網路通訊,調取攝像頭),解析json,影象壓縮技術(上傳大小有限制),手機許可權獲取。

實現:

1.開啟AS,建立新工程,將目錄結構調整為project模式,lib資料夾新增java介面呼叫工具。如圖(1)所示,右擊工具包,點選add as lib..

2.寫頁面佈局(這不是重點,我直接給原始碼),初始化控制元件,繫結按鈕,效果如圖:

3.註冊人臉(此程式碼可以不斷完善)

1)調取相機,拍照。

   private void take_photo() {
        // 設定相機拍照後照片儲存路徑
        mPictureFile = new File(Environment.getExternalStorageDirectory(),
                "picture" + System.currentTimeMillis()/1000 + ".jpg");
        // 啟動拍照,並儲存到臨時檔案
        Intent mIntent = new Intent();
        mIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
        mIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mPictureFile));
        mIntent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
        startActivityForResult(mIntent, REQUEST_CAMERA_IMAGE);
    }

2)將圖片壓縮,顯示在ImageView中

if (requestCode == REQUEST_CAMERA_IMAGE) {
            if (null == mPictureFile) {
                showTip("拍照失敗,請重試");
                return;
            }


            fileSrc = mPictureFile.getAbsolutePath();
            updateGallery(fileSrc);
            // 跳轉到圖片裁剪頁面
            FaceUtil.cropPicture(this,Uri.fromFile(new File(fileSrc)));
        } else if (requestCode == FaceUtil.REQUEST_CROP_IMAGE) {
            // 獲取返回資料
            Bitmap bmp = data.getParcelableExtra("data");
            // 若返回資料不為null,儲存至本地,防止裁剪時未能正常儲存
            if(null != bmp){
                FaceUtil.saveBitmapToFile(MainActivity.this, bmp);
            }
            // 獲取圖片儲存路徑
            fileSrc = FaceUtil.getImagePath(MainActivity.this);
            // 獲取圖片的寬和高
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            mImage = BitmapFactory.decodeFile(fileSrc, options);

            // 壓縮圖片
            options.inSampleSize = Math.max(1, (int) Math.ceil(Math.max(
                    (double) options.outWidth / 1024f,
                    (double) options.outHeight / 1024f)));
            options.inJustDecodeBounds = false;
            mImage = BitmapFactory.decodeFile(fileSrc, options);


            // 若mImageBitmap為空則圖片資訊不能正常獲取
            if(null == mImage) {
                showTip("圖片資訊無法正常獲取!");
                return;
            }

            // 部分手機會對圖片做旋轉,這裡檢測旋轉角度
            int degree = FaceUtil.readPictureDegree(fileSrc);
            if (degree != 0) {
                // 把圖片旋轉為正的方向
                mImage = FaceUtil.rotateImage(degree, mImage);
            }

            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            //可根據流量及網路狀況對圖片進行壓縮
            mImage.compress(Bitmap.CompressFormat.JPEG, 80, baos);
            mImageData = baos.toByteArray();

            imageView.setImageBitmap(mImage);
        }

3)將圖片以二進位制陣列的形式傳給伺服器

   //圖片轉成二進位制陣列
    public  byte[] Bitmap2Bytes(Bitmap bm){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
        return baos.toByteArray();
    }


  //控制元件事件呼叫
  CommonOperate commonOperate = new CommonOperate(key, secret, false);//建立連線
  Response response = commonOperate.detectByte(Bitmap2Bytes(mImage), 0, attributes);//以本地而形式探測

4)接收反饋資料,解析json

   if(response.getStatus() != 200){//連線不成功
            return new String(response.getContent());
        }
        String res = new String(response.getContent());//連線成功
        Log.e("response", res);//將返回的資料打印出來
        JSONObject json = new JSONObject(res);//將返回的資料轉化為json資料格式

5)提取人臉face_token,將face_token加入臉集,用於後續匹配。

       String faceToken = json.optJSONArray("faces").optJSONObject(0).optString("face_token");//將face_token提取出來

       faceToken = getFaceToken(response);
       FaceSetOperate faceSetOperate=new FaceSetOperate(key, secret, false);
                    Response response1=faceSetOperate.addFaceByFaceToken(faceToken,"c0f50247c7d194f0998d555de208f0e6");

6)一提示框的形式,反饋使用者註冊完成


showtip("註冊完成");
private void showTip(final String str) {
        mToast.setText(str);
        mToast.show();
    } 

7)效果圖

8)在Androidmanifest.xml中新增許可權


<!-- 讀取網路資訊狀態 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <!-- 如需使用人臉識別,還要新增: 攝相頭許可權,拍照需要用到 -->
    <uses-permission android:name="android.permission.CAMERA" />

9)總結:從結果上看,伺服器把這個人的基本特性通過json資料反饋給我們,年齡22,女性,不戴眼鏡,表情中立,並且這個人的face_token,和臉集的face_taken都反饋我們,這樣我們可以利用這兩個標識來完成我們想要的功能。