开发前准备:
开发使用到的软件和工具:
Jdk8(64位,Oracle版本)、mysql5.73、spring boot、mybatis
{libarcsoft_face.dll(so)、libarcsoft_face_engine.dll(so)、libarcsoft_face_engine_jni.dll(so)}这些可以去官网下载。
其中里面的配置文件路径,io文件路径需要修改
数据表的配置
部分代码展示
- sprinh boot启动器
package com.facedemo;
@SpringBootApplication
@MapperScan({"com.gxd.facedemo.dao.mapper","com.gxd.facedemo.mapper"})
@EnableTransactionManagement
@EnableSwagger2Doc
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 配置文件设置
config.arcface-sdk.app-id=91BifrT9RHbUENePMKBKpCVv8TivpeUsViUwcREygaw1
# win
# config.arcface-sdk.sdk-lib-path=C:/facelib
# config.arcface-sdk.sdk-# key=7HSLAwHDNQy8XyVaqjjBxERtmfZA5btoqhmNGQEYSo8Y
# linux
config.arcface-sdk.sdk-lib-path=/usr/lib64
config.arcface-sdk.sdk-key=7HSLAwHDNQy8XyVaqjjBxERtdYz3QzutFqyRXsdMHRrj
config.arcface-sdk.thread-pool-size=5
注意 :此处建议修改为自己的文件路径,此处仅从参考!
核心类
人脸添加
package com.gxd.facedemo.controller;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.gxd.facedemo.base.Result;
import com.gxd.facedemo.base.Results;
import com.gxd.facedemo.domain.UserFaceInfo;
import com.gxd.facedemo.dto.FaceCompareResDto;
import com.gxd.facedemo.dto.FaceSearchResDto;
import com.gxd.facedemo.dto.FaceUserInfo;
import com.gxd.facedemo.dto.ProcessInfo;
import com.gxd.facedemo.enums.ErrorCodeEnum;
import com.gxd.facedemo.request.FaceCompareRequest;
import com.gxd.facedemo.service.UserFaceInfoService;
import com.gxd.facedemo.service.FaceEngineService;
import com.arcsoft.face.FaceInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;
import java.util.UUID;
import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;
@Api(tags={"人脸相关"})
@Controller
public class FaceController {
public final static Logger logger = LoggerFactory.getLogger(FaceController.class);
@Autowired
FaceEngineService faceEngineService;
@Autowired
UserFaceInfoService userFaceInfoService;
@ApiIgnore
@RequestMapping(value = "/demo")
public String demo() {
return "demo";
}
/**
* 人脸添加
*/
@ApiOperation(value="人脸添加", notes="添加人脸,人脸图片为base64格式字符串")
@RequestMapping(value = "/faceAdd", method = RequestMethod.POST)
@ResponseBody
public Result<Object> faceAdd(@ApiParam(value = "人脸图像数据", required = true) @RequestParam("file") String file, @ApiParam(value = "分组id", required = true)@RequestParam("groupId") Integer groupId, @ApiParam(value = "人脸姓名", required = true)@RequestParam("name") String name) {
try {
if (file == null) {
return Results.newFailedResult("人脸照片不能为空");
}
if (groupId == null) {
return Results.newFailedResult("分组id不能为空");
}
if (name == null) {
return Results.newFailedResult("姓名不能为空");
}
byte[] decode = Base64.decode(base64Process(file));
ImageInfo imageInfo = getRGBData(decode);
//人脸特征获取
byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
if (bytes == null) {
return Results.newFailedResult(ErrorCodeEnum.NO_FACE_DETECTED);
}
UserFaceInfo userFaceInfo = new UserFaceInfo();
userFaceInfo.setFaceId(UUID.randomUUID().toString().replace("-","").substring(0,10));
userFaceInfo.setName(name);
userFaceInfo.setGroupId(groupId);
userFaceInfo.setFaceFeature(bytes);
//人脸特征插入到数据库
userFaceInfoService.insertSelective(userFaceInfo);
logger.info("faceAdd:" + name);
return Results.newSuccessResult("");
} catch (Exception e) {
logger.error("", e);
}
return Results.newFailedResult(ErrorCodeEnum.UNKNOWN);
}
/**
* 人脸识别
*/
@ApiOperation(value="人脸识别", notes="待识别图像为base64格式字符串、必须指定人脸分组id")
@RequestMapping(value = "/faceSearch", method = RequestMethod.POST)
@ResponseBody
public Result<FaceSearchResDto> faceSearch(@ApiParam(value = "人脸图像数据", required = true)@RequestParam("file") String file, @ApiParam(value = "分组id", required = true) @RequestParam("groupId") Integer groupId) throws Exception {
if (groupId == null) {
return Results.newFailedResult("groupId is null");
}
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);
}
/**
* 人脸对比
*/
@RequestMapping(value = "/faceCompare", method = RequestMethod.POST)
@ResponseBody
@ApiOperation(value="人脸对比", notes="待识别图像为base64格式字符串")
public Result<FaceCompareResDto> faceCompare(@RequestBody FaceCompareRequest request) throws Exception{
// 已知人脸
byte[] decode1 = Base64.decode(base64Process(request.getFileKnown()));
BufferedImage bufImage1 = ImageIO.read(new ByteArrayInputStream(decode1));
ImageInfo imageInfo1 = ImageFactory.bufferedImage2ImageInfo(bufImage1);
//人脸1特征获取
byte[] bytes1 = faceEngineService.extractFaceFeature(imageInfo1);
// 未知人脸
byte[] decode2 = Base64.decode(base64Process(request.getFileUnknown()));
BufferedImage bufImage2 = ImageIO.read(new ByteArrayInputStream(decode2));
ImageInfo imageInfo2 = ImageFactory.bufferedImage2ImageInfo(bufImage2);
//人脸2特征获取
byte[] bytes2 = faceEngineService.extractFaceFeature(imageInfo2);
// 人脸对比
FaceCompareResDto res = faceEngineService.compareFaces(bytes1,bytes2);
return Results.newSuccessResult(res);
}
private String base64Process(String base64Str) {
if (!StringUtils.isEmpty(base64Str)) {
String photoBase64 = base64Str.substring(0, 30).toLowerCase();
int indexOf = photoBase64.indexOf("base64,");
if (indexOf > 0) {
base64Str = base64Str.substring(indexOf + 7);
}
return base64Str;
} else {
return "";
}
}
}
人脸删除
package com.gxd.facedemo.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gxd.facedemo.base.Result;
import com.gxd.facedemo.base.Results;
import com.gxd.facedemo.domain.UserFaceInfo;
import com.gxd.facedemo.mapper.MybatisUserFaceInfoMapper;
import com.gxd.facedemo.request.DeleteFaceRequest;
import com.gxd.facedemo.service.UserFaceInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@Api(tags={"数据维护"})
@RestController
public class UserListController {
@Autowired
MybatisUserFaceInfoMapper userFaceInfoMapper;
@Autowired
private UserFaceInfoService userFaceInfoService;
@ApiOperation(value="获取所有人脸数据")
@GetMapping("/userInfo")
public List<UserFaceInfo> getUserInfo()
{
List<UserFaceInfo> list = new ArrayList<UserFaceInfo>();
list = userFaceInfoMapper.findUserFaceInfoList();
return list;
}
/**
* 根据分组id和名称删除人脸
*/
@ResponseBody
@PostMapping("/deleteFaceByName")
@ApiOperation(value="根据分组id和名称删除人脸")
public Result<Object> deleteFaceByName(@RequestBody DeleteFaceRequest request) {
if ("".equals(request.getName())) {
return Results.newFailedResult("姓名不能为空");
}
if (request.getGroupId() == null) {
return Results.newFailedResult("分组id不能为空");
}
List<UserFaceInfo> userList = userFaceInfoService.list(new QueryWrapper<UserFaceInfo>().eq("group_id", request.getGroupId()).eq("name", request.getName()));
if (userList==null||userList.isEmpty()){
return Results.newFailedResult("人脸数据不存在,删除失败");
}
List<Integer> userIdList = new ArrayList<>();
for (UserFaceInfo userFaceInfo : userList) {
userIdList.add(userFaceInfo.getId());
}
userFaceInfoService.removeByIds(userIdList);
return Results.newSuccessResult("删除人脸数据成功");
}
/**
* 删除人脸集合
*/
@ResponseBody
@PostMapping("/deleteFaceSet/{groupId}")
@ApiOperation(value="删除人脸集合")
public Result<Object> deleteFaceSet(@PathVariable("groupId") Integer groupId) {
if (groupId == null) {
return Results.newFailedResult("分组id不能为空");
}
int num = userFaceInfoService.count(new QueryWrapper<UserFaceInfo>().eq("group_id",groupId));
if (num == 0){
return Results.newFailedResult("当前集合不存在");
}
userFaceInfoService.remove(new QueryWrapper<UserFaceInfo>().eq("group_id",groupId));
return Results.newSuccessResult("删除人脸集合成功");
}
}