1 first register your own account in Face + +
https://console.faceplusplus.com.cn/dashboard
Face
In Face recognition technology, Face refers to the Face found in the image. When Face detection is performed on a picture, the detected Face will be recorded, including the position of the Face in the picture, and a system will be used to identify the face_token. Note: multiple Face detection for the same image will get different faces for the same person's face_token.
Face set
Face set is a storage object used to store the detected face. A FaceSet inner face_ Tokens are not repeated
Face comparison / face search
After the computer detects a person's face in the picture, the process of judging the person's identity through the face is called face comparison / face search.
Face comparison refers to collecting a new face and comparing it with the face of a known user to determine whether the new face belongs to the known user. Face comparison requires calling the Compare API.
Face search is to collect a user's new face and search in the face set of multiple known users to find out which known user the new face belongs to.
Face search needs to call the Search API.
Create API Key
To call API, you need to create an API key (API key), which is the certificate for using API and SDK. After the registration verification is successful, you can click "create my first application" on the welcome page, and a free API key will be automatically generated, which you can use directly. (Note: the free API key can call the API according to the free rules. If you want to use the paid version service, please follow the steps below to create a formal API key.)
After creating the API Key, you can see the statistics of your account balance and API call amount on the console.
postman calls API interface
Face detection
API document for face detection
After performing the following steps, look at the returned results
You can see the meaning of the response field by comparing the API parameters
Face contrast
Pass two different photos and see the confidence
Create faceSet
API documentation
A faceset set is generated
Add face to faceset
api documentation
Successfully added to face collection
According to outer_id get faceset
You can see a face
Continue to add a picture to faceset. You can detect that the number has changed to 2
Delete face
api documentation
You can see that the number has decreased after deletion
Face recognition to achieve face brushing login
Input face information and get face through face detection interface_ Token, stored in outer_ id=travel_ In faceset of faceset, when the user logs in, the user's current login image is obtained, and then the face comparison interface is used to compare the current login image with the face information previously saved in faceset.
The returned information after comparison has the thresholds set by us. You can select one of the thresholds according to your own needs. When the confidence is higher than this threshold, it can be regarded as the same person, that is, you can log in successfully. On the contrary, you can't log in successfully.
This project is based on the springboot project. So the next configuration is to perform face login on one of my existing spring boot projects
Needs to be added in the configuration file
face: config: api-key: JtGRv7lXpLK3wkip5KKIdDaOOKe1J-Cx api-secret: 1zZ-eiAQiw7x-yG0gQ6ta0tmIB9GqLDg outer-id: faceset1
Configure restTemplate in startup class
@SpringBootApplication public class TravelApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(TravelApplication.class, args); } }
The following is the business of DAO layer, which is the same as the request in postman above
DAO layer
package com.xxq.mapper; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.Getter; import lombok.Setter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.MultipartBodyBuilder; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; @ConfigurationProperties("face.config") @Component @Getter @Setter public class FaceDao { @Autowired private RestTemplate restTemplate; private String apiKey; private String apiSecret; private String outerId; /** * Face detection * @param filePath * @return * / * */ public DetectResponseEntity detect(String filePath) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // Multi part form body MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder(); // -----------------Form part general field multipartBodyBuilder.part("api_key", apiKey); multipartBodyBuilder.part("api_secret", apiSecret); // -----------------File domain part // Read file from disk multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG); // build complete message body MultiValueMap<String, HttpEntity<?>> multipartBody =multipartBodyBuilder.build(); ResponseEntity<DetectResponseEntity> responseEntity = restTemplate.postForEntity("https://api-cn.faceplusplus.com/facepp/v3/detect", multipartBody, DetectResponseEntity.class); return responseEntity.getBody(); } /** * Create face faseSet for login */ public void faceSetCreate() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);//Normal form request MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); map.add("api_key", apiKey); map.add("api_secret", apiSecret); map.add("outer_id", outerId); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<> (map, headers); restTemplate.postForEntity("https://apicn.faceplusplus.com/facepp/v3/faceset/create", request, String.class); } /** * Get login face set details */ public FaceSetResponseEntity getFaceSetDetail() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); map.add("api_key", apiKey); map.add("api_secret", apiSecret); map.add("outer_id", outerId); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<> (map, headers); ResponseEntity<FaceSetResponseEntity> responseEntity = restTemplate.postForEntity("https://api-cn.faceplusplus.com/facepp/v3/faceset/getdetail", request, FaceSetResponseEntity.class); return responseEntity.getBody(); } /** * Add faceToken to FaceSet * The string composed of face tokens can be one or more, separated by commas. Up to 5 face_token * @param faceTokens */ public void addFaceToFaceSet(String faceTokens) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); map.add("api_key", apiKey); map.add("api_secret", apiSecret); map.add("outer_id", outerId); map.add("face_tokens", faceTokens); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<> (map, headers); restTemplate.postForEntity("https://api-cn.faceplusplus.com/facepp/v3/faceset/addface", request, String.class); } /** * Face comparison * @param faceToken1 * @param faceToken2 * @return */ public boolean compareFace(String faceToken1, String faceToken2) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // Multi part form body MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder(); // -----------------Form part multipartBodyBuilder.part("api_key", apiKey); multipartBodyBuilder.part("api_secret", apiSecret); multipartBodyBuilder.part("face_token1", faceToken1); multipartBodyBuilder.part("face_token2", faceToken2); // -----------------File part // Read file from disk //multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG); MultiValueMap<String, HttpEntity<?>> multipartBody = multipartBodyBuilder.build(); ResponseEntity<CompareResponseEntity> responseEntity = restTemplate.postForEntity("https://api-cn.faceplusplus.com/facepp/v3/compare", multipartBody, CompareResponseEntity.class); System.out.println(responseEntity); CompareResponseEntity e = responseEntity.getBody(); if (e.getConfidence() >= e.getThresholds().e5) { return true; } else { return false; } } /** * Face comparison returns entity class */ @Data public static class CompareResponseEntity { private Double confidence; private ThresholdsResponseEntity thresholds; } /** * Face contrast confidence threshold returns entity class */ @Data public static class ThresholdsResponseEntity { @JsonProperty("1e-5") private Double e5; } /** * FaceSet Return entity class */ @Data public static class FaceSetResponseEntity{ private String faceset_token; private String outer_id; private Integer face_count; private List<String> face_tokens; } @Data /** * Face detection return data entity class */ public static class DetectResponseEntity { private String request_id; private Integer face_num; private List<FaceEntity> faces; } @Data /** * Face entity class */ public static class FaceEntity { private String face_token; } }
Test class
package com.xxq.mapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest @RunWith(SpringRunner.class) public class FaceDaoTest { @Autowired private FaceDao faceDao; @Test public void detect() { FaceDao.DetectResponseEntity entity=faceDao.detect("C:\\Users\\ly\\Desktop\\photo\\towards.jpg"); System.out.println(entity); } @Test public void getFaceSetDetail() { FaceDao.FaceSetResponseEntity e = faceDao.getFaceSetDetail(); System.out.println(e); } // This method is only applicable to faceset s without outerID. In some cases, it cannot be created and an error is reported. @Test public void createFaceSet() { faceDao.faceSetCreate(); } @Test public void addFaceToFaceSet() { faceDao.addFaceToFaceSet("af1f0bd2054dfe2aa1ce06c3be7116fa"); } @Test public void campareFace() { boolean b = faceDao.compareFace("04aeead26262cea830b63ec0c0a92507", "af1f0bd2054dfe2aa1ce06c3be7116fa"); System.out.println(b); } }
**
Error reporting java.lang.NoClassDefFoundError: org/reactivestreams/Publisher
Add the following components
**
<dependency> <groupId>org.reactivestreams</groupId> <artifactId>reactive-streams</artifactId> <version>1.0.3</version> </dependency>
Service layer
Interface layer
public interface FaceService { /* * Enter face * */ public void addFace(String filePath); /* * Login face comparison * */ public boolean loginByFace(String filePath); }
Implementation layer
package com.xxq.service.impl; import com.xxq.service.FaceService; import com.xxq.mapper.FaceDao; import com.xxq.service.FaceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.File; @Service public class FaceServiceImpl implements FaceService { @Autowired private FaceDao faceDao; @Override public void addFace(String filePath) { FaceDao.FaceSetResponseEntity fs = null; try { fs = faceDao.getFaceSetDetail(); } catch (Exception e) { //Simply capture it without any processing } if (fs == null) { //faceset does not exist faceDao.faceSetCreate(); } FaceDao.DetectResponseEntity dr = faceDao.detect(filePath); //View face for (FaceDao.FaceEntity f : dr.getFaces()) { faceDao.addFaceToFaceSet(f.getFace_token()); } } @Override public boolean loginByFace(String filePath) { boolean result = false; FaceDao.FaceSetResponseEntity fs = null; try { fs = faceDao.getFaceSetDetail(); } catch (Exception e) { } if (fs == null) { //faceset does not exist faceDao.faceSetCreate(); fs = faceDao.getFaceSetDetail(); } FaceDao.DetectResponseEntity dr = faceDao.detect(filePath); //View face String ft1 = null; if (dr.getFace_num() >=1) { ft1 = dr.getFaces().get(0).getFace_token(); } else { return false; } for (String ft2: fs.getFace_tokens()) { if (faceDao.compareFace(ft1, ft2)) { result = true; } } new File(filePath).delete(); //Delete login face to save server resources return result; } }
Controller layer
package com.xxq.controller; import com.xxq.service.FaceService; import com.xxq.util.ImageUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.UUID; @Controller @RequestMapping("/admin/face") public class FaceController { @Autowired private FaceService faceService; /** * Transfer to face entry page * @return */ @RequestMapping("/toinput") public String toInput() { return "/face/input"; } @RequestMapping("/tologin") public String toLogin() { return "/face/login"; } /** * Enter face * @param imgData * @param request * @return * @throws IOException */ @ResponseBody @RequestMapping("/upload") public ResponseEntity doAdd(@RequestParam("imgData") String imgData, HttpServletRequest request) throws IOException { String savePath = request.getServletContext().getRealPath("img/face/"); String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".png"; System.out.println(savePath); ImageUtils.generateImage(imgData.substring(22), savePath, fileName); faceService.addFace(savePath + fileName); return ResponseEntity.ok("{\"success\": \"true\"}"); } /** * Face recognition login * @param imgData * @param request * @return * @throws IOException */ @ResponseBody @RequestMapping("/login") public ResponseEntity login(@RequestParam("imgData") String imgData, HttpServletRequest request) throws IOException { String savePath = request.getServletContext().getRealPath("img/face/login/"); String fileName = UUID.randomUUID().toString().replaceAll("-", "") +".png"; System.out.println(savePath); ImageUtils.generateImage(imgData.substring(22), savePath, fileName); boolean b = faceService.loginByFace(savePath + fileName); if (b) { System.out.println("Login succeeded"); return ResponseEntity.ok("{\"success\": true}"); } else { System.out.println("Login failed"); return ResponseEntity.ok("{\"success\": false}"); } } }
Tool class for BASE64 transcoding
package com.xxq.util; import sun.misc.BASE64Decoder; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class ImageUtils { public static boolean generateImage(String imgStr, String filePath, String fileName) { try { if (imgStr == null) { return false; } BASE64Decoder decoder = new BASE64Decoder(); byte[] b = decoder.decodeBuffer(imgStr); File file = new File(filePath); if (!file.exists()) { file.mkdirs(); } OutputStream out = new FileOutputStream(filePath+fileName); out.write(b); out.flush(); out.close(); return true; } catch (Exception e) { return false; } } }
Front page:
input.jtml
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta charset="utf-8"/> <title>user management </title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/> <!-- bootstrap & fontawesome --> <link rel="stylesheet" href="/assets/css/bootstrap.min.css"/> <link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css"/> <!-- page specific plugin styles --> <!-- text fonts --> <link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css"/> <!-- ace styles --> <link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style"/> <!--[if lte IE 9]> <link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet"/> <![endif]--> <link rel="stylesheet" href="/assets/css/ace-skins.min.css"/> <link rel="stylesheet" href="/assets/css/ace-rtl.min.css"/> <!--[if lte IE 9]> <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/> <![endif]--> <!-- inline styles related to this page --> <!-- ace settings handler --> <script src="/assets/js/ace-extra.min.js"></script> <!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries --> <!--[if lte IE 8]> <script src="/assets/js/html5shiv.min.js"></script> <script src="/assets/js/respond.min.js"></script> <![endif]--> <!--[if !IE]> --> <script src="/assets/js/jquery-2.1.4.min.js"></script> <!-- <![endif]--> <!--[if IE]> <script src="/assets/js/jquery-1.11.3.min.js"></script> <![endif]--> <script src="/assets/js/bootstrap.min.js"></script> <!-- page specific plugin scripts --> <script src="/assets/js/jquery.dataTables.min.js"></script> <script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script> <script src="/assets/js/dataTables.buttons.min.js"></script> <script src="/assets/js/buttons.flash.min.js"></script> <script src="/assets/js/buttons.html5.min.js"></script> <script src="/assets/js/buttons.print.min.js"></script> <script src="/assets/js/buttons.colVis.min.js"></script> <script src="/assets/js/dataTables.select.min.js"></script> <!-- ace scripts --> <script src="/assets/js/ace-elements.min.js"></script> <script src="/assets/js/ace.min.js"></script> </head> <body class="no-skin"> <div th:replace="header :: navbar"></div> <div class="main-container ace-save-state" id="main-container"> <script type="text/javascript"> try { ace.settings.loadState('main-container') } catch (e) { } </script> <div th:replace="left :: sidebar"></div> <div class="main-content"> <div class="main-content-inner"> <div class="breadcrumbs ace-save-state" id="breadcrumbs"> <ul class="breadcrumb"> <li> <i class="ace-icon fa fa-home home-icon"></i> <a href="#"> Home Page</a> </li> <li> <a href="#"> User</a> </li> <li class="active">user management </li> </ul><!-- /.breadcrumb --> </div> <div class="page-content"> <div style="padding: 10px;"> <tr> <td colspan="2"> <button class="btn btn-sm btn-default" onclick="openMedia()">Turn on the camera</button> <button class="btn btn-sm btn-default" onclick="closeMedia()">Turn off the camera</button> <button class="btn btn-sm btn-default" onclick="takePhoto()">Enter face/button> </td> </tr> <table> <tr> <td> <video id="video" width="500px" height="500px" autoplay="autoplay"></video> <canvas id="canvas" width="500px" height="500px" style="display: none"></canvas> </td> <td> <img id="imgTag" src="" alt="..." width="500px" height="500px"><br> </td> </tr> </table> <script> let mediaStreamTrack=null; // Video object (global) let video ; function openMedia() { let constraints = { video: { width: 500, height: 500 }, audio: false }; //Get video camera video = document.getElementById('video'); let promise = navigator.mediaDevices.getUserMedia(constraints); promise.then((mediaStream) => { // mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[1]; mediaStreamTrack=mediaStream.getVideoTracks() video.srcObject = mediaStream; video.play(); }); } // photograph function takePhoto() { //Get Canvas object let video = document.getElementById('video'); let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, 500, 500); // toDataURL - -- can pass in 'image/png' -- default, 'image/jpeg' let img = document.getElementById('canvas').toDataURL(); // img here is the picture console.log('img-----', img); document.getElementById('imgTag').src=img; //upload $.ajax({ url:"/admin/face/upload", type:"POST", data:{"imgData":img}, success:function(data){ alert("Entered successfully") } ,error:function(){ alert("Input failed") } }); } // Turn off the camera function closeMedia() { let stream = document.getElementById('video').srcObject; let tracks = stream.getTracks(); tracks.forEach(function(track) { track.stop(); }); document.getElementById('video').srcObject = null; } </script> </div> </div><!-- /.page-content --> </div> </div><!-- /.main-content --> </div><!-- /.main-container --> </body> </html>
login.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta charset="utf-8"/> <title>user management </title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/> <!-- bootstrap & fontawesome --> <link rel="stylesheet" href="/assets/css/bootstrap.min.css"/> <link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css"/> <!-- page specific plugin styles --> <!-- text fonts --> <link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css"/> <!-- ace styles --> <link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style"/> <!--[if lte IE 9]> <link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet"/> <![endif]--> <link rel="stylesheet" href="/assets/css/ace-skins.min.css"/> <link rel="stylesheet" href="/assets/css/ace-rtl.min.css"/> <!--[if lte IE 9]> <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/> <![endif]--> <!-- inline styles related to this page --> <!-- ace settings handler --> <script src="/assets/js/ace-extra.min.js"></script> <!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries --> <!--[if lte IE 8]> <script src="/assets/js/html5shiv.min.js"></script> <script src="/assets/js/respond.min.js"></script> <![endif]--> <!--[if !IE]> --> <script src="/assets/js/jquery-2.1.4.min.js"></script> <!-- <![endif]--> <!--[if IE]> <script src="/assets/js/jquery-1.11.3.min.js"></script> <![endif]--> <script src="/assets/js/bootstrap.min.js"></script> <!-- page specific plugin scripts --> <script src="/assets/js/jquery.dataTables.min.js"></script> <script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script> <script src="/assets/js/dataTables.buttons.min.js"></script> <script src="/assets/js/buttons.flash.min.js"></script> <script src="/assets/js/buttons.html5.min.js"></script> <script src="/assets/js/buttons.print.min.js"></script> <script src="/assets/js/buttons.colVis.min.js"></script> <script src="/assets/js/dataTables.select.min.js"></script> <!-- ace scripts --> <script src="/assets/js/ace-elements.min.js"></script> <script src="/assets/js/ace.min.js"></script> </head> <body class="no-skin"> <div class="main-container ace-save-state" id="main-container"> <div class="main-content"> <div class="main-content-inner"> <div class="page-content"> <div style="padding: 10px;"> <tr> <td colspan="2"> <button class="btn btn-sm btn-default" onclick="openMedia()">Turn on the camera</button> <button class="btn btn-sm btn-default" onclick="closeMedia()">Turn off the camera</button> <button class="btn btn-sm btn-default" onclick="takePhoto()">Sign in</button> </td> </tr> <table> <tr> <td> <video id="video" width="500px" height="500px" autoplay="autoplay"></video> <canvas id="canvas" width="500px" height="500px" style="display: none"></canvas> </td> <td> <img id="imgTag" src="" alt="..." width="500px" height="500px"><br> </td> </tr> </table> <script> let mediaStreamTrack=null; // Video object (global) let video ; function openMedia() { let constraints = { video: { width: 500, height: 500 }, audio: false }; //Get video camera video = document.getElementById('video'); let promise = navigator.mediaDevices.getUserMedia(constraints); promise.then((mediaStream) => { // mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[1]; mediaStreamTrack=mediaStream.getVideoTracks() video.srcObject = mediaStream; video.play(); }); } // photograph function takePhoto() { //Get Canvas object let video = document.getElementById('video'); let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, 500, 500); // toDataURL - -- can pass in 'image/png' -- default, 'image/jpeg' let img = document.getElementById('canvas').toDataURL(); // img here is the picture console.log('img-----', img); document.getElementById('imgTag').src=img; //upload $.ajax({ url:"/admin/face/login", type:"POST", data:{"imgData":img}, dataType: "json", success:function(data){ var b = data.success; alert(b); if (b) { alert("Login succeeded"); } else { alert("Login failed"); } } ,error:function(){ alert("Login failed") } }); } // Turn off the camera function closeMedia() { let stream = document.getElementById('video').srcObject; let tracks = stream.getTracks(); tracks.forEach(function(track) { track.stop(); }); document.getElementById('video').srcObject = null; } </script> </div> </div><!-- /.page-content --> </div> </div><!-- /.main-content --> </div><!-- /.main-container --> </body> </html>