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都反饋我們,這樣我們可以利用這兩個標識來完成我們想要的功能。