![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iN4EDZlNzYkZjM4cTNiRTNyMjM5M2NhVzYiZmNlljMm9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
å¼è¨
è¿ç¨å¨å®¶åå ¬ç第N天ï¼å¿«è¦é²åºå±äºï¼ä»å¤©çªç¶æ个å°å¦å¼å æVX说è¦å¨è¯¢æç¹ææ¯é®é¢ï¼ç»äºå¯ä»¥è£ Xäºï¼ã çäºä»çéæ±æè¿°ï¼å¤§æ¦æ¯è¦åä¸ä¸ªJava webçæ¬ç人è¸è¯å«åè½ï¼ç¶ååå¨äººç©çç¹å¾ï¼åæ«è¸æ¯å¯¹ãå¯æ¯æä¸ä¼åããã
ä¸è¿ï¼ä½ä¸ºä¸ä¸ªå® ç²çæç·ï¼å«è¯´æå°é¾å°±æ¯æ²¡å°é¾å¶é å°é¾ä¹è¦ä¸ï¼æ¢ç¶äººå®¶è¿ä¹çè¯çå¨è¯¢ï¼è¯´ææè¿æ¯æ被éè¦çä»·å¼ï¼ä¸ä¼é£å°±å¸®çæ¥æ¥èµæå§ï¼æ²¡æ³å°è¿ææå¤çæ¶è·~
çå®ä»çå¢éï¼å¿½ç¶æ³èµ·èªå·±å½å¹´åæ¯è®¾æ¶é£æ å©çæ ·åï¼æ¯ä½ççç¸ä¼¼ãæ¯æ¯çå°æè¿æ ·çå¨è¯¢ï¼è½å¸®çæé½å°½èªå·±æ大åªå帮ï¼æ¯ç«é½æ¯è¿ä¹èµ°è¿æ¥çã
人è¸è¯å«SDK
人è¸è¯å«
ææ¯æ¯å¾å¤æçï¼èªå·±ç¨
Java
ææä¸ä¸ªè¯å«ç®æ³æç¹ä¸åå®é ï¼æ¯ç«å®åä¸å 许æè¿ä¹å£å¼ ï¼è¿æ¯åå©ä¸æ¹çSDKå§ï¼
æ¾äºä¸ååç°ä¸ä¸ªå è´¹ç人è¸è¯å«SDKï¼
ArcSoft
:ï¼å°åï¼
https://ai.arcsoft.com.cn
ã
å®ç½é¦é¡µ -> å³ä¸è§å¼åè ä¸å¿ -> éæ©â人è¸è¯å«â -> æ·»å SDKï¼ä¼çæ
APPID
ã
SDK KEY
åç»ä¼ç¨å°ï¼æ ¹æ®éè¦éæ©ä¸åçç¯å¢ï¼
æ¬æåºäºwindowsç¯å¢
ï¼ï¼ç¶åä¸è½½
SDK
æ¯ä¸ä¸ªå缩å ã
Java项ç®æ建
ç»äºå¨æçè¦è¦æ寻ä¹ä¸ç»äºï¼æ¾å°ä¸ä¸ª
ArcSoft
ç
Javaçæ¬
Demoï¼å¼æºçæ¯ä¸ä»¶ç¾å¥½çäºæ ï¼è¯ä¸å¤è¯´å¼å¹²ï¼
githubå°åï¼
https://github.com/xinzhfiu/ArcSoftFaceDemo
ï¼æ¬å°æ建æ°æ®åºï¼å建表:
user_face_info
ãè¿ä¸ªè¡¨ä¸»è¦ç¨æ¥å人åç¹å¾ï¼å ¶ä¸ä¸»è¦çå段
face_feature
ç¨äºè¿å¶ç±»å
blob
åæ¾äººè¸ç¹å¾ã
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user_face_info
-- ----------------------------
DROP TABLE IF EXISTS `user_face_info`;
CREATE TABLE `user_face_info` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主é®',
`group_id` int(11) DEFAULT NULL COMMENT 'åç»id',
`face_id` varchar(31) DEFAULT NULL COMMENT '人è¸å¯ä¸Id',
`name` varchar(63) DEFAULT NULL COMMENT 'åå',
`age` int(3) DEFAULT NULL COMMENT '年纪',
`email` varchar(255) DEFAULT NULL COMMENT 'é®ç®±å°å',
`gender` smallint(1) DEFAULT NULL COMMENT 'æ§å«ï¼1=ç·ï¼2=女',
`phone_number` varchar(11) DEFAULT NULL COMMENT 'çµè¯å·ç ',
`face_feature` blob COMMENT '人è¸ç¹å¾',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´',
`fpath` varchar(255) COMMENT 'ç
§çè·¯å¾',
PRIMARY KEY (`id`) USING BTREE,
KEY `GROUP_ID` (`group_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
2ãä¿®æ¹ application.properties
æ件 æ´ä¸ªé¡¹ç®è¿æ¯æ¯è¾å®æ´çï¼åªéæ¹ä¸äºé ç½®å³å¯å¯å¨ï¼ä½æå ç¹æ³¨æçå°æ¹ï¼åè¾¹ä¼éç¹è¯´æã
config.arcface-sdk.sdk-lib-path
ï¼ åæ¾
SDK
å缩å ä¸çä¸ä¸ª.
dll
æ件çè·¯å¾
config.arcface-sdk.app-id
ï¼ å¼åè ä¸å¿ç
APPID
config.arcface-sdk.sdk-key
ï¼å¼åè ä¸å¿ç
SDK Key
config.arcface-sdk.sdk-lib-path=d:/arcsoft_lib
config.arcface-sdk.app-id=8XMHMu71Dmb5UtAEBpPTB1E9ZPNTw2nrvQ5bXxBobUA8
config.arcface-sdk.sdk-key=BA8TLA9vVwK7G6btJh2A2FCa8ZrC6VWZLNbBBFctCz5R
# druid æ¬å°çæ°æ®åºå°å
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/xin-master?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.druid.username=junkang
spring.datasource.druid.password=junkang
3ãæ ¹ç®å½å建 lib
æ件夹 å¨é¡¹ç®æ ¹ç®å½å建æ件夹
lib
,å°ä¸è½½çSDKå缩å ä¸çarcsoft-sdk-face-2.2.0.1.jaræ¾å ¥é¡¹ç®
æ ¹ç®å½
arcsoft
ä¾èµå
<dependency>
<groupId>com.arcsoft.face</groupId>
<artifactId>arcsoft-sdk-face</artifactId>
<version>2.2.0.1</version>
<scope>system</scope>
<systemPath>${basedir}/lib/arcsoft-sdk-face-2.2.0.1.jar</systemPath>
</dependency>
pom.xml
æ件è¦é ç½®
includeSystemScope
å±æ§ï¼å¦åå¯è½ä¼å¯¼è´
arcsoft-sdk-face-2.2.0.1.jar
å¼ç¨ä¸å°
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
5ãå¯å¨é¡¹ç® å°æ¤ä¸ºæ¢é ç½®å®æï¼
run
Application
æ件å¯å¨
æµè¯ä¸ä¸ï¼
http://127.0.0.1:8089/demo
ï¼å¦ä¸é¡µé¢å³å¯å¨æå
æä½
1ãå½å ¥äººè¸å¾å页é¢è¾å ¥å称ï¼ç¹å»
æå头注å
è°èµ·æ¬å°æå头ï¼æ交åå°å½åå¾åä¼ å ¥åå°ï¼è¯å«æåå½å人è¸ä½å¾ï¼ä¿åè³æ°æ®åºã
å½å ¥å®äººè¸å¾ååæµè¯ä¸ä¸è½å¦è¯å«æåï¼æ交å½åçå¾åï¼åç°è¯å«æå
ç¸ä¼¼åº¦92%
ãä½æ¯ä½ä¸ºç¨åºå对ä»ä¹äºæ é½è¦ææççæ度ï¼
è¿ç»æä¸æ¯èéå¨é¡µé¢åæ»çå§ï¼ä¸ºäºè¿ä¸æ¥éªè¯ï¼è¿åæè¸æ¡ä½åè¯ä¸ä¸ï¼åç°æ示â人è¸ä¸å¹é âï¼è¯æççæè¿è¡æ¯å¯¹ã
æºç åæ
ç®åçäºä¸ä¸é¡¹ç®æºç ï¼åæä¸ä¸å®ç°çè¿ç¨ï¼
页é¢åJSä¸çå°±æ¯å端ç¨åºååçï¼ä¸è¦é®æé®ä¸ºä»ä¹ï¼æçèªç¶æï¼ååå ~ ï¼
1ãJSè°èµ·æ¬å°æå头æç §ï¼ä¸ä¼ å¾çæ件å符串function getMedia() {
$("#mainDiv").empty();
let videoComp = " <video id='video' width='500px' height='500px' autoplay='autoplay' style='margin-top: 20px'></video><canvas id='canvas' width='500px' height='500px' style='display: none'></canvas>";
$("#mainDiv").append(videoComp);
let constraints = {
video: {width: 500, height: 500},
audio: true
};
//è·å¾videoæå头åºå
let video = document.getElementById("video");
//è¿éä»ç»æ°çæ¹æ³ï¼è¿åä¸ä¸ª Promise对象
// è¿ä¸ªPromise对象è¿åæååçåè°å½æ°å¸¦ä¸ä¸ª MediaStream 对象ä½ä¸ºå
¶åæ°
// then()æ¯Promise对象éçæ¹æ³
// then()æ¹æ³æ¯å¼æ¥æ§è¡ï¼å½then()åçæ¹æ³æ§è¡å®ååæ§è¡then()å
é¨çç¨åº
// é¿å
æ°æ®æ²¡æè·åå°
let promise = navigator.mediaDevices.getUserMedia(constraints);
promise.then(function (MediaStream) {
video.srcObject = MediaStream;
video.play();
});
// var t1 = window.setTimeout(function() {
// takePhoto();
// },2000)
}
//æç
§äºä»¶
function takePhoto() {
let mainComp = $("#mainDiv");
if(mainComp.has('video').length)
{
let userNameInput = $("#userName").val();
if(userNameInput == "")
{
alert("å§åä¸è½ä¸ºç©º!");
return false;
}
//è·å¾Canvas对象
let video = document.getElementById("video");
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, 500, 500);
var formData = new FormData();
var base64File = canvas.toDataURL();
var userName = $("#userName").val();
formData.append("file", base64File);
formData.append("name", userName);
formData.append("groupId", "101");
$.ajax({
type: "post",
url: "/faceAdd",
data: formData,
contentType: false,
processData: false,
async: false,
success: function (text) {
var res = JSON.stringify(text)
if (text.code == 0) {
alert("注åæå")
} else {
alert(text.message)
}
},
error: function (error) {
alert(JSON.stringify(error))
}
});
}
else{
var formData = new FormData();
let userName = $("#userName").val();
formData.append("groupId", "101");
var file = $("#file0")[0].files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
var base64 = reader.result;
formData.append("file", base64);
formData.append("name",userName);
$.ajax({
type: "post",
url: "/faceAdd",
data: formData,
contentType: false,
processData: false,
async: false,
success: function (text) {
var res = JSON.stringify(text)
if (text.code == 0) {
alert("注åæå")
} else {
alert(text.message)
}
},
error: function (error) {
alert(JSON.stringify(error))
}
});
location.reload();
}
}
}
2ãåå°è§£æå¾çï¼æå人åç¹å¾ åå°è§£æåç«¯ä¼ è¿æ¥çå¾çï¼æå人åç¹å¾åå ¥æ°æ®åºï¼äººåç¹å¾çæå主è¦æ¯é
FaceEngine
å¼æï¼é¡ºçæºç ä¸è·¯çä¸å»ï¼èªå·±æçå¦æµ å®å¨æ¯æ²¡æå ·ä½æ¯ä¸ªä»ä¹æ ·çç®æ³ã
/*
人è¸æ·»å
*/
@RequestMapping(value = "/faceAdd", method = RequestMethod.POST)
@ResponseBody
public Result<Object> faceAdd(@RequestParam("file") String file, @RequestParam("groupId") Integer groupId, @RequestParam("name") String name) {
try {
//解æå¾ç
byte[] decode = Base64.decode(base64Process(file));
ImageInfo imageInfo = ImageFactory.getRGBData(decode);
//人è¸ç¹å¾è·å
byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
if (bytes == null) {
return Results.newFailedResult(ErrorCodeEnum.NO_FACE_DETECTED);
}
UserFaceInfo userFaceInfo = new UserFaceInfo();
userFaceInfo.setName(name);
userFaceInfo.setGroupId(groupId);
userFaceInfo.setFaceFeature(bytes);
userFaceInfo.setFaceId(RandomUtil.randomString(10));
//人è¸ç¹å¾æå
¥å°æ°æ®åº
userFaceInfoService.insertSelective(userFaceInfo);
logger.info("faceAdd:" + name);
return Results.newSuccessResult("");
} catch (Exception e) {
logger.error("", e);
}
return Results.newFailedResult(ErrorCodeEnum.UNKNOWN);
}
3ã人åç¹å¾å¯¹æ¯ 人è¸è¯å«ï¼å°åç«¯ä¼ å ¥çå¾åç»è¿äººåç¹å¾æååï¼ååºä¸å·²åå¨ç人åä¿¡æ¯å¯¹æ¯åæ
/*
人è¸è¯å«
*/
@RequestMapping(value = "/faceSearch", method = RequestMethod.POST)
@ResponseBody
public Result<FaceSearchResDto> faceSearch(String file, Integer groupId) throws Exception {
byte[] decode = Base64.decode(base64Process(file));
BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode));
ImageInfo imageInfo = ImageFactory.bufferedImage2ImageInfo(bufImage);
//人è¸ç¹å¾è·å
byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
if (bytes == null) {
return Results.newFailedResult(ErrorCodeEnum.NO_FACE_DETECTED);
}
//人è¸æ¯å¯¹ï¼è·åæ¯å¯¹ç»æ
List<FaceUserInfo> userFaceInfoList = faceEngineService.compareFaceFeature(bytes, groupId);
if (CollectionUtil.isNotEmpty(userFaceInfoList)) {
FaceUserInfo faceUserInfo = userFaceInfoList.get(0);
FaceSearchResDto faceSearchResDto = new FaceSearchResDto();
BeanUtil.copyProperties(faceUserInfo, faceSearchResDto);
List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
if (CollectionUtil.isNotEmpty(processInfoList)) {
//人è¸æ£æµ
List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
int left = faceInfoList.get(0).getRect().getLeft();
int top = faceInfoList.get(0).getRect().getTop();
int width = faceInfoList.get(0).getRect().getRight() - left;
int height = faceInfoList.get(0).getRect().getBottom() - top;
Graphics2D graphics2D = bufImage.createGraphics();
graphics2D.setColor(Color.RED);//红è²
BasicStroke stroke = new BasicStroke(5f);
graphics2D.setStroke(stroke);
graphics2D.drawRect(left, top, width, height);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(bufImage, "jpg", outputStream);
byte[] bytes1 = outputStream.toByteArray();
faceSearchResDto.setImage("data:image/jpeg;base64," + Base64Utils.encodeToString(bytes1));
faceSearchResDto.setAge(processInfoList.get(0).getAge());
faceSearchResDto.setGender(processInfoList.get(0).getGender().equals(1) ? "女" : "ç·");
}
return Results.newSuccessResult(faceSearchResDto);
}
return Results.newFailedResult(ErrorCodeEnum.FACE_DOES_NOT_MATCH);
}
æ´ä¸ªäººè¸è¯å«åè½ç大è´æµç¨å¾å¦ä¸ï¼
æ»ç»
æ´ä¸ªé¡¹ç®ç设计æè·¯æ¯è¾æ¸ æ°ï¼é¾ç¹å¨äº
人è¸è¯å«å¼æ
å
å端JS
é¨å代ç ï¼å ¶ä»çåè½æ¯è¾å¹³å¸¸ã
æºç å°åï¼https://github.com/xinzhfiu/ArcSoftFaceDemo/ï¼æä»»ä½ææ¯é®é¢ï¼æ¬¢è¿éæ¶æ²é