java使用face++簡單實現人臉識別注冊登錄
前言
人臉識別,好高大上!!!
理解之后很簡單。
支付寶使用的就是face++,
至於face++賬號信息,apikey…..,本文不做講述,網上很多.
一.設計思想
1. 先想一想,如果讓你實現人臉識別,你會怎么做?
尋找圖片上的關鍵點,制作一套算法,分析臉部信息,將得到的數據存入數據庫,
登錄的時候,通過同樣的算法,分析數據,和數據庫中存入的信息進行比對……
工作量好大!!!
face++有着它獨有的非常優秀的算法,我們可以將我們的圖片上傳到face++服務器來獲取對應的圖片數據,剩下的事情就很簡單了
2. 四個face++ api簡介:
這里只介紹用到的api,
2.1 使用api肯定需要先注冊一下信息,獲取api_key和api_secret,可以注冊試用的進行獲取
2.2 create
作用: 創建一個FaceSet,創建一個臉部信息集合,引用官網的描述:
url:https://api-cn.faceplusplus.com/facepp/v3/faceset/create
參數:api_key,api_secret,……
返回值: faceset_token, outer_id……,這里寫的兩個返回值需要記住,是這個臉部信息集合的唯一標識,具體返回值信息如下圖:
2.3 addFace
作用:向臉部信息集合faceSet添加一條或多條臉部信息,便於后期搜索
url: https://api-cn.faceplusplus.com/facepp/v3/faceset/addface
參數: 不用說,肯定需要,api_key,api_secret,faceSet_token或outer_id(臉部信息唯一標識),還有圖片信息,官網截圖:
返回值:可以獲取插入的結果信息
.
2.4 Search
作用: 傳入一張圖片信息到face++服務器,會返回最相似的face_token
url: https://api-cn.faceplusplus.com/facepp/v3/search
參數:api_key,api_secret,image_url或image_file或image_base64或face_token,詳細參數列表如下
是否必選 |
參數名 |
類型 |
參數說明 |
必選 |
api_key |
String |
調用此 API 的 API Key |
必選 |
api_secret |
String |
調用此 API 的 API Secret |
必選(四選一) |
face_token |
String |
進行搜索的目標人臉的 face_token,優先使用該參數 |
image_url |
String |
目標人臉所在的圖片的 URL |
|
image_file |
File |
目標人臉所在的圖片,二進制文件,需要用 post multipart/form-data 的方式上傳。 |
|
image_base64 |
String |
base64 編碼的二進制圖片數據 如果同時傳入了 image_url、image_file 和 image_base64 參數,本 API 使用順序為 image_file 優先,image_url 最低。 |
|
必選(二選一) |
faceset_token |
String |
用來搜索的 FaceSet 的標識 |
outer_id |
String |
用戶自定義的 FaceSet 標識 |
|
可選 |
return_result_count |
Int |
控制返回比對置信度最高的結果的數量。合法值為一個范圍 [1,5] 的整數。默認值為 1 |
可選(僅正式 API Key 可以使用) |
face_rectangle |
String |
當傳入圖片進行人臉檢測時,是否指定人臉框位置進行檢測。 如果此參數傳入值為空,或不傳入此參數,則不使用此功能。本 API 會自動檢測圖片內所有區域的所有人臉。 如果使用正式 API Key 對此參數傳入符合格式要求的值,則使用此功能。需要傳入一個字符串代表人臉框位置,系統會根據此坐標對框內的圖像進行人臉檢測,以及人臉關鍵點和人臉屬性等后續操作。系統返回的人臉矩形框位置會與傳入的 face_rectangle 完全一致。對於此人臉框之外的區域,系統不會進行人臉檢測,也不會返回任何其他的人臉信息。 參數規格:四個正整數,用逗號分隔,依次代表人臉框左上角縱坐標(top),左上角橫坐標(left),人臉框寬度(width),人臉框高度(height)。例如:70,80,100,100 注:只有在傳入 image_url、image_file 和 image_base64 三個參數中任意一個時,本參數才生效。 |
返回值:返回值包含和你傳入圖片信息最像的圖片的face_token,(可以再和數據庫中對應的信息進行比較)
2.4 Detect
作用:
傳入一張圖片信息,獲取這張圖片的face_token,注意,一張相同圖片獲取多次的face_token不同
url: https://api-cn.faceplusplus.com/facepp/v3/detect
參數:api_key,api_secret, image_url或image_file或image_base64,
是否必選 |
參數名 |
類型 |
參數說明 |
||||||
必選 |
api_key |
String |
調用此API的API Key |
||||||
必選 |
api_secret |
String |
調用此API的API Secret |
||||||
必選(三選一) |
image_url |
String |
圖片的 URL。 注:在下載圖片時可能由於網絡等原因導致下載圖片時間過長,建議使用 image_file 或 image_base64 參數直接上傳圖片。 |
||||||
image_file |
File |
一個圖片,二進制文件,需要用post multipart/form-data的方式上傳。 |
|||||||
image_base64 |
String |
base64 編碼的二進制圖片數據 如果同時傳入了 image_url、image_file 和 image_base64 參數,本API使用順序為 image_file 優先,image_url 最低。 |
|||||||
可選 |
return_landmark |
Int |
是否檢測並返回人臉關鍵點。合法值為:
注:本參數默認值為 0 |
||||||
可選 |
return_attributes |
String |
是否檢測並返回根據人臉特征判斷出的年齡、性別、情緒等屬性。合法值為:
注:本參數默認值為 none |
||||||
可選(僅正式 API Key 可以使用) |
calculate_all |
Int |
是否檢測並返回所有人臉的人臉關鍵點和人臉屬性。如果不使用此功能,則本 API 只會對人臉面積最大的五個人臉分析人臉關鍵點和人臉屬性。合法值為:
注:本參數默認值為 0 |
||||||
可選(僅正式 API Key 可以使用) |
face_rectangle |
String |
是否指定人臉框位置進行人臉檢測。 如果此參數傳入值為空,或不傳入此參數,則不使用此功能。本 API 會自動檢測圖片內所有區域的所有人臉。 如果使用正式 API Key 對此參數傳入符合格式要求的值,則使用此功能。需要傳入一個字符串代表人臉框位置,系統會根據此坐標對框內的圖像進行人臉檢測,以及人臉關鍵點和人臉屬性等后續操作。系統返回的人臉矩形框位置會與傳入的 face_rectangle 完全一致。對於此人臉框之外的區域,系統不會進行人臉檢測,也不會返回任何其他的人臉信息。 參數規格:四個正整數,用逗號分隔,依次代表人臉框左上角縱坐標(top),左上角橫坐標(left),人臉框寬度(width),人臉框高度(height)。例如:70,80,100,100 |
||||||
可選 |
beauty_score_min |
Int |
顏值評分分數區間的最小值。默認為0 注:默認顏值評分分數區間為0-100.可通過beauty_score_min和beauty_score_max來調節分數區間,滿足您的場景需求。 |
||||||
可選 |
beauty_score_max |
Int |
顏值評分分數區間的最大值。默認為100 |
返回值:圖片對應的face_token
字段 |
類型 |
說明 |
request_id |
String |
用於區分每一次請求的唯一的字符串。 |
faces |
Array |
被檢測出的人臉數組,具體包含內容見下文。 注:如果沒有檢測出人臉則為空數組 |
image_id |
String |
被檢測的圖片在系統中的標識。 |
time_used |
Int |
整個請求所花費的時間,單位為毫秒。 |
error_message |
String |
當請求失敗時才會返回此字符串,具體返回內容見后續錯誤信息章節。否則此字段不存在。 |
在faces中包含face_token
3. 設計分析
- 創建調用create api創建faceSet,取得faceSet_token,對應你的一張用戶信息表
- 注冊時:調用detect api傳入用戶注冊的圖片信息,獲取face_token,
將face_token存入faceSet,(調用addFace api存入)
將face_token存入數據庫
- 登錄: 從前端獲取用戶圖片,將圖片編碼為base64作為參數image_base64調用search api
返回值為在faceSet中,和傳入圖片相似度高的face_token
通過返回的face_token,在數據庫中進行查詢,實現登陸
二.用到的技術
有了上面的分析,即使使用javaweb也能實現了
本案例使用
maven
java的ssm框架
配上Druid連接池
前端使用了jquery,(不懂前端,通過參考和自己設計寫的很low)
三.實現
3.1前端界面:
實體類:
User{
String username;
String password;
String other; //在本案例中沒有作用
String faceToken;
}
技術不高,自己寫的一個簡單的界面
注冊界面:register.html
<!DOCTYPE html>
|
登錄界面:login.html
<!DOCTYPE html>
|
3.2后端界面
face相關類,通過face++官網查到一個demo,本案例修改demo並封裝了自己的信息,打成實現功能
獲取到的demo:
import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Random; import javax.net.ssl.SSLException; public class FaceTest { public static void main(String[] args) throws Exception{ File file = new File("你的本地圖片路徑"); byte[] buff = getBytesFromFile(file); String url = "https://api-cn.faceplusplus.com/facepp/v3/detect"; HashMap<String, String> map = new HashMap<>(); HashMap<String, byte[]> byteMap = new HashMap<>(); map.put("api_key", "你的KEY"); map.put("api_secret", "你的SECRET"); map.put("return_landmark", "1"); map.put("return_attributes", "gender,age,smiling,headpose,facequality,blur,eyestatus,emotion,ethnicity,beauty,mouthstatus,eyegaze,skinstatus"); byteMap.put("image_file", buff); try{ byte[] bacd = post(url, map, byteMap); String str = new String(bacd); System.out.println(str); }catch (Exception e) { e.printStackTrace(); } } private final static int CONNECT_TIME_OUT = 30000; private final static int READ_OUT_TIME = 50000; private static String boundaryString = getBoundary(); protected static byte[] post(String url, HashMap<String, String> map, HashMap<String, byte[]> fileMap) throws Exception { HttpURLConnection conne; URL url1 = new URL(url); conne = (HttpURLConnection) url1.openConnection(); conne.setDoOutput(true); conne.setUseCaches(false); conne.setRequestMethod("POST"); conne.setConnectTimeout(CONNECT_TIME_OUT); conne.setReadTimeout(READ_OUT_TIME); conne.setRequestProperty("accept", "*/*"); conne.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString); conne.setRequestProperty("connection", "Keep-Alive"); conne.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)"); DataOutputStream obos = new DataOutputStream(conne.getOutputStream()); Iterator iter = map.entrySet().iterator(); while(iter.hasNext()){ Map.Entry<String, String> entry = (Map.Entry) iter.next(); String key = entry.getKey(); String value = entry.getValue(); obos.writeBytes("--" + boundaryString + "\r\n"); obos.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"\r\n"); obos.writeBytes("\r\n"); obos.writeBytes(value + "\r\n"); } if(fileMap != null && fileMap.size() > 0){ Iterator fileIter = fileMap.entrySet().iterator(); while(fileIter.hasNext()){ Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIter.next(); obos.writeBytes("--" + boundaryString + "\r\n"); obos.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey() + "\"; filename=\"" + encode(" ") + "\"\r\n"); obos.writeBytes("\r\n"); obos.write(fileEntry.getValue()); obos.writeBytes("\r\n"); } } obos.writeBytes("--" + boundaryString + "--" + "\r\n"); obos.writeBytes("\r\n"); obos.flush(); obos.close(); InputStream ins = null; int code = conne.getResponseCode(); try{ if(code == 200){ ins = conne.getInputStream(); }else{ ins = conne.getErrorStream(); } }catch (SSLException e){ e.printStackTrace(); return new byte[0]; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buff = new byte[4096]; int len; while((len = ins.read(buff)) != -1){ baos.write(buff, 0, len); } byte[] bytes = baos.toByteArray(); ins.close(); return bytes; } private static String getBoundary() { StringBuilder sb = new StringBuilder(); Random random = new Random(); for(int i = 0; i < 32; ++i) { sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".charAt(random.nextInt("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".length()))); } return sb.toString(); } private static String encode(String value) throws Exception{ return URLEncoder.encode(value, "UTF-8"); } public static byte[] getBytesFromFile(File f) { if (f == null) { return null; } try { FileInputStream stream = new FileInputStream(f); ByteArrayOutputStream out = new ByteArrayOutputStream(1000); byte[] b = new byte[1000]; int n; while ((n = stream.read(b)) != -1) out.write(b, 0, n); stream.close(); out.close(); return out.toByteArray(); } catch (IOException e) { } return null; } }
|
哪里需要改?
四.總結
人無我有,人有我優
思路很清晰,具體實現很難!!!
實現后感覺很簡單